Merge branch 'master' into dynamic-effect-dialog
@@ -28,20 +28,23 @@
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT amplifier_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
LMMS_STRINGIFY( PLUGIN_NAME ),
|
||||
"Amplifier",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser", "A native amplifier plugin" ),
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser", "A native amplifier plugin" ),
|
||||
"Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>",
|
||||
0x0100,
|
||||
Plugin::Effect,
|
||||
new PluginPixmapLoader("logo"),
|
||||
NULL,
|
||||
NULL
|
||||
nullptr,
|
||||
nullptr,
|
||||
} ;
|
||||
|
||||
}
|
||||
@@ -57,9 +60,6 @@ AmplifierEffect::AmplifierEffect( Model* parent, const Descriptor::SubPluginFeat
|
||||
|
||||
|
||||
|
||||
AmplifierEffect::~AmplifierEffect()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ bool AmplifierEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames )
|
||||
for( fpp_t f = 0; f < frames; ++f )
|
||||
{
|
||||
// qDebug( "offset %d, value %f", f, m_ampControls.m_volumeModel.value( f ) );
|
||||
outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1];
|
||||
|
||||
sample_t s[2] = { buf[f][0], buf[f][1] };
|
||||
|
||||
@@ -123,6 +122,7 @@ bool AmplifierEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames )
|
||||
|
||||
buf[f][0] = d * buf[f][0] + w * s[0];
|
||||
buf[f][1] = d * buf[f][1] + w * s[1];
|
||||
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
|
||||
}
|
||||
|
||||
checkGate( outSum / frames );
|
||||
@@ -145,3 +145,4 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data )
|
||||
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
@@ -29,16 +29,18 @@
|
||||
|
||||
#include "Effect.h"
|
||||
#include "AmplifierControls.h"
|
||||
#include "ValueBuffer.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class AmplifierEffect : public Effect
|
||||
{
|
||||
public:
|
||||
AmplifierEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
|
||||
virtual ~AmplifierEffect();
|
||||
virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames );
|
||||
~AmplifierEffect() override = default;
|
||||
bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override;
|
||||
|
||||
virtual EffectControls* controls()
|
||||
EffectControls* controls() override
|
||||
{
|
||||
return &m_ampControls;
|
||||
}
|
||||
@@ -51,4 +53,7 @@ private:
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,13 +23,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QLayout>
|
||||
|
||||
#include "AmplifierControlDialog.h"
|
||||
#include "AmplifierControls.h"
|
||||
#include "embed.h"
|
||||
#include "Knob.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
AmplifierControlDialog::AmplifierControlDialog( AmplifierControls* controls ) :
|
||||
EffectControlDialog( controls )
|
||||
@@ -40,30 +43,33 @@ AmplifierControlDialog::AmplifierControlDialog( AmplifierControls* controls ) :
|
||||
setPalette( pal );
|
||||
setFixedSize( 100, 110 );
|
||||
|
||||
Knob * volumeKnob = new Knob( knobBright_26, this);
|
||||
auto volumeKnob = new Knob(knobBright_26, this);
|
||||
volumeKnob -> move( 16, 10 );
|
||||
volumeKnob -> setVolumeKnob( true );
|
||||
volumeKnob->setModel( &controls->m_volumeModel );
|
||||
volumeKnob->setLabel( tr( "VOL" ) );
|
||||
volumeKnob->setHintText( tr( "Volume:" ) , "%" );
|
||||
|
||||
Knob * panKnob = new Knob( knobBright_26, this);
|
||||
auto panKnob = new Knob(knobBright_26, this);
|
||||
panKnob -> move( 57, 10 );
|
||||
panKnob->setModel( &controls->m_panModel );
|
||||
panKnob->setLabel( tr( "PAN" ) );
|
||||
panKnob->setHintText( tr( "Panning:" ) , "" );
|
||||
|
||||
Knob * leftKnob = new Knob( knobBright_26, this);
|
||||
auto leftKnob = new Knob(knobBright_26, this);
|
||||
leftKnob -> move( 16, 65 );
|
||||
leftKnob -> setVolumeKnob( true );
|
||||
leftKnob->setModel( &controls->m_leftModel );
|
||||
leftKnob->setLabel( tr( "LEFT" ) );
|
||||
leftKnob->setHintText( tr( "Left gain:" ) , "%" );
|
||||
|
||||
Knob * rightKnob = new Knob( knobBright_26, this);
|
||||
auto rightKnob = new Knob(knobBright_26, this);
|
||||
rightKnob -> move( 57, 65 );
|
||||
rightKnob -> setVolumeKnob( true );
|
||||
rightKnob->setModel( &controls->m_rightModel );
|
||||
rightKnob->setLabel( tr( "RIGHT" ) );
|
||||
rightKnob->setHintText( tr( "Right gain:" ) , "%" );
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
@@ -28,19 +28,27 @@
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class AmplifierControls;
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class AmplifierControlDialog : public EffectControlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AmplifierControlDialog( AmplifierControls* controls );
|
||||
virtual ~AmplifierControlDialog()
|
||||
{
|
||||
}
|
||||
~AmplifierControlDialog() override = default;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
|
||||
#include "AmplifierControls.h"
|
||||
#include "Amplifier.h"
|
||||
#include "Engine.h"
|
||||
#include "Song.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
AmplifierControls::AmplifierControls( AmplifierEffect* effect ) :
|
||||
EffectControls( effect ),
|
||||
@@ -77,6 +77,6 @@ void AmplifierControls::saveSettings( QDomDocument& doc, QDomElement& _this )
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
|
||||
|
||||
@@ -28,36 +28,40 @@
|
||||
|
||||
#include "EffectControls.h"
|
||||
#include "AmplifierControlDialog.h"
|
||||
#include "Knob.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class AmplifierEffect;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class AmplifierControlDialog;
|
||||
}
|
||||
|
||||
|
||||
class AmplifierControls : public EffectControls
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AmplifierControls( AmplifierEffect* effect );
|
||||
virtual ~AmplifierControls()
|
||||
{
|
||||
}
|
||||
~AmplifierControls() override = default;
|
||||
|
||||
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
inline virtual QString nodeName() const
|
||||
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
|
||||
void loadSettings( const QDomElement & _this ) override;
|
||||
inline QString nodeName() const override
|
||||
{
|
||||
return "AmplifierControls";
|
||||
}
|
||||
|
||||
virtual int controlCount()
|
||||
int controlCount() override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
virtual EffectControlDialog* createView()
|
||||
gui::EffectControlDialog* createView() override
|
||||
{
|
||||
return new AmplifierControlDialog( this );
|
||||
return new gui::AmplifierControlDialog( this );
|
||||
}
|
||||
|
||||
|
||||
@@ -71,9 +75,12 @@ private:
|
||||
FloatModel m_leftModel;
|
||||
FloatModel m_rightModel;
|
||||
|
||||
friend class AmplifierControlDialog;
|
||||
friend class gui::AmplifierControlDialog;
|
||||
friend class AmplifierEffect;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* audio_file_processor.cpp - instrument for using audio-files
|
||||
* AudioFileProcessor.cpp - instrument for using audio-files
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
@@ -22,39 +22,44 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioFileProcessor.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QBitmap>
|
||||
#include <QDomDocument>
|
||||
#include <QFileInfo>
|
||||
#include <QDropEvent>
|
||||
|
||||
#include <samplerate.h>
|
||||
|
||||
#include "audio_file_processor.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "ComboBox.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "Engine.h"
|
||||
#include "Song.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "Mixer.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "interpolation.h"
|
||||
#include "gui_templates.h"
|
||||
#include "ToolTip.h"
|
||||
#include "StringPairDrag.h"
|
||||
#include "DataFile.h"
|
||||
#include "Engine.h"
|
||||
#include "gui_templates.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "PathUtil.h"
|
||||
#include "PixmapButton.h"
|
||||
#include "Song.h"
|
||||
#include "StringPairDrag.h"
|
||||
#include "Clipboard.h"
|
||||
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
LMMS_STRINGIFY( PLUGIN_NAME ),
|
||||
"AudioFileProcessor",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser",
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser",
|
||||
"Simple sampler with various settings for "
|
||||
"using samples (e.g. drums) in an "
|
||||
"instrument-track" ),
|
||||
@@ -63,7 +68,7 @@ Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor =
|
||||
Plugin::Instrument,
|
||||
new PluginPixmapLoader( "logo" ),
|
||||
"wav,ogg,ds,spx,au,voc,aif,aiff,flac,raw",
|
||||
NULL
|
||||
nullptr,
|
||||
} ;
|
||||
|
||||
}
|
||||
@@ -71,7 +76,7 @@ Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor =
|
||||
|
||||
|
||||
|
||||
audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) :
|
||||
AudioFileProcessor::AudioFileProcessor( InstrumentTrack * _instrument_track ) :
|
||||
Instrument( _instrument_track, &audiofileprocessor_plugin_descriptor ),
|
||||
m_sampleBuffer(),
|
||||
m_ampModel( 100, 0, 500, 1, this, tr( "Amplify" ) ),
|
||||
@@ -86,38 +91,31 @@ audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) :
|
||||
m_nextPlayBackwards( false )
|
||||
{
|
||||
connect( &m_reverseModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( reverseModelChanged() ) );
|
||||
this, SLOT( reverseModelChanged() ), Qt::DirectConnection );
|
||||
connect( &m_ampModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( ampModelChanged() ) );
|
||||
this, SLOT( ampModelChanged() ), Qt::DirectConnection );
|
||||
connect( &m_startPointModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( startPointChanged() ) );
|
||||
this, SLOT( startPointChanged() ), Qt::DirectConnection );
|
||||
connect( &m_endPointModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( endPointChanged() ) );
|
||||
this, SLOT( endPointChanged() ), Qt::DirectConnection );
|
||||
connect( &m_loopPointModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( loopPointChanged() ) );
|
||||
this, SLOT( loopPointChanged() ), Qt::DirectConnection );
|
||||
connect( &m_stutterModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( stutterModelChanged() ) );
|
||||
|
||||
this, SLOT( stutterModelChanged() ), Qt::DirectConnection );
|
||||
|
||||
//interpolation modes
|
||||
m_interpolationModel.addItem( tr( "None" ) );
|
||||
m_interpolationModel.addItem( tr( "Linear" ) );
|
||||
m_interpolationModel.addItem( tr( "Sinc" ) );
|
||||
m_interpolationModel.setValue( 1 );
|
||||
|
||||
|
||||
pointChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
audioFileProcessor::~audioFileProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::playNote( NotePlayHandle * _n,
|
||||
void AudioFileProcessor::playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
@@ -199,7 +197,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n,
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::deleteNotePluginData( NotePlayHandle * _n )
|
||||
void AudioFileProcessor::deleteNotePluginData( NotePlayHandle * _n )
|
||||
{
|
||||
delete (handleState *)_n->m_pluginData;
|
||||
}
|
||||
@@ -207,7 +205,7 @@ void audioFileProcessor::deleteNotePluginData( NotePlayHandle * _n )
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::saveSettings( QDomDocument & _doc,
|
||||
void AudioFileProcessor::saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _this )
|
||||
{
|
||||
_this.setAttribute( "src", m_sampleBuffer.audioFile() );
|
||||
@@ -231,13 +229,13 @@ void audioFileProcessor::saveSettings( QDomDocument & _doc,
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::loadSettings( const QDomElement & _this )
|
||||
void AudioFileProcessor::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
if( _this.attribute( "src" ) != "" )
|
||||
{
|
||||
setAudioFile( _this.attribute( "src" ), false );
|
||||
|
||||
QString absolutePath = m_sampleBuffer.tryToMakeAbsolute( m_sampleBuffer.audioFile() );
|
||||
QString absolutePath = PathUtil::toAbsolute( m_sampleBuffer.audioFile() );
|
||||
if ( !QFileInfo( absolutePath ).exists() )
|
||||
{
|
||||
QString message = tr( "Sample not found: %1" ).arg( m_sampleBuffer.audioFile() );
|
||||
@@ -253,17 +251,16 @@ void audioFileProcessor::loadSettings( const QDomElement & _this )
|
||||
m_loopModel.loadSettings( _this, "looped" );
|
||||
m_ampModel.loadSettings( _this, "amp" );
|
||||
m_endPointModel.loadSettings( _this, "eframe" );
|
||||
m_startPointModel.loadSettings( _this, "sframe" );
|
||||
|
||||
// compat code for not having a separate loopback point
|
||||
if( _this.hasAttribute( "lframe" ) )
|
||||
if (_this.hasAttribute("lframe") || !(_this.firstChildElement("lframe").isNull()))
|
||||
{
|
||||
m_loopPointModel.loadSettings( _this, "lframe" );
|
||||
m_startPointModel.loadSettings( _this, "sframe" );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_loopPointModel.loadSettings( _this, "sframe" );
|
||||
m_startPointModel.setValue( m_loopPointModel.value() );
|
||||
}
|
||||
|
||||
m_reverseModel.loadSettings( _this, "reversed" );
|
||||
@@ -284,7 +281,7 @@ void audioFileProcessor::loadSettings( const QDomElement & _this )
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::loadFile( const QString & _file )
|
||||
void AudioFileProcessor::loadFile( const QString & _file )
|
||||
{
|
||||
setAudioFile( _file );
|
||||
}
|
||||
@@ -292,7 +289,7 @@ void audioFileProcessor::loadFile( const QString & _file )
|
||||
|
||||
|
||||
|
||||
QString audioFileProcessor::nodeName( void ) const
|
||||
QString AudioFileProcessor::nodeName() const
|
||||
{
|
||||
return audiofileprocessor_plugin_descriptor.name;
|
||||
}
|
||||
@@ -300,10 +297,11 @@ QString audioFileProcessor::nodeName( void ) const
|
||||
|
||||
|
||||
|
||||
int audioFileProcessor::getBeatLen( NotePlayHandle * _n ) const
|
||||
int AudioFileProcessor::getBeatLen( NotePlayHandle * _n ) const
|
||||
{
|
||||
const float freq_factor = BaseFreq / _n->frequency() *
|
||||
Engine::mixer()->processingSampleRate() / Engine::mixer()->baseSampleRate();
|
||||
const auto baseFreq = instrumentTrack()->baseFreq();
|
||||
const float freq_factor = baseFreq / _n->frequency() *
|
||||
Engine::audioEngine()->processingSampleRate() / Engine::audioEngine()->baseSampleRate();
|
||||
|
||||
return static_cast<int>( floorf( ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * freq_factor ) );
|
||||
}
|
||||
@@ -312,15 +310,15 @@ int audioFileProcessor::getBeatLen( NotePlayHandle * _n ) const
|
||||
|
||||
|
||||
|
||||
PluginView * audioFileProcessor::instantiateView( QWidget * _parent )
|
||||
gui::PluginView* AudioFileProcessor::instantiateView( QWidget * _parent )
|
||||
{
|
||||
return new AudioFileProcessorView( this, _parent );
|
||||
return new gui::AudioFileProcessorView( this, _parent );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::setAudioFile( const QString & _audio_file,
|
||||
void AudioFileProcessor::setAudioFile( const QString & _audio_file,
|
||||
bool _rename )
|
||||
{
|
||||
// is current channel-name equal to previous-filename??
|
||||
@@ -330,7 +328,7 @@ void audioFileProcessor::setAudioFile( const QString & _audio_file,
|
||||
m_sampleBuffer.audioFile().isEmpty() ) )
|
||||
{
|
||||
// then set it to new one
|
||||
instrumentTrack()->setName( QFileInfo( _audio_file).fileName() );
|
||||
instrumentTrack()->setName( PathUtil::cleanName( _audio_file ) );
|
||||
}
|
||||
// else we don't touch the track-name, because the user named it self
|
||||
|
||||
@@ -341,7 +339,7 @@ void audioFileProcessor::setAudioFile( const QString & _audio_file,
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::reverseModelChanged( void )
|
||||
void AudioFileProcessor::reverseModelChanged()
|
||||
{
|
||||
m_sampleBuffer.setReversed( m_reverseModel.value() );
|
||||
m_nextPlayStartPoint = m_sampleBuffer.startFrame();
|
||||
@@ -351,20 +349,20 @@ void audioFileProcessor::reverseModelChanged( void )
|
||||
|
||||
|
||||
|
||||
void audioFileProcessor::ampModelChanged( void )
|
||||
void AudioFileProcessor::ampModelChanged()
|
||||
{
|
||||
m_sampleBuffer.setAmplification( m_ampModel.value() / 100.0f );
|
||||
}
|
||||
|
||||
|
||||
void audioFileProcessor::stutterModelChanged()
|
||||
void AudioFileProcessor::stutterModelChanged()
|
||||
{
|
||||
m_nextPlayStartPoint = m_sampleBuffer.startFrame();
|
||||
m_nextPlayBackwards = false;
|
||||
}
|
||||
|
||||
|
||||
void audioFileProcessor::startPointChanged( void )
|
||||
void AudioFileProcessor::startPointChanged()
|
||||
{
|
||||
// check if start is over end and swap values if so
|
||||
if( m_startPointModel.value() > m_endPointModel.value() )
|
||||
@@ -391,19 +389,19 @@ void audioFileProcessor::startPointChanged( void )
|
||||
{
|
||||
m_endPointModel.setValue( qMin( m_endPointModel.value() + 0.001f, 1.0f ) );
|
||||
}
|
||||
|
||||
|
||||
pointChanged();
|
||||
|
||||
}
|
||||
|
||||
void audioFileProcessor::endPointChanged( void )
|
||||
void AudioFileProcessor::endPointChanged()
|
||||
{
|
||||
// same as start, for now
|
||||
startPointChanged();
|
||||
|
||||
}
|
||||
|
||||
void audioFileProcessor::loopPointChanged( void )
|
||||
void AudioFileProcessor::loopPointChanged()
|
||||
{
|
||||
|
||||
// check that loop point is between start-end points and not overlapping with endpoint
|
||||
@@ -426,11 +424,11 @@ void audioFileProcessor::loopPointChanged( void )
|
||||
pointChanged();
|
||||
}
|
||||
|
||||
void audioFileProcessor::pointChanged( void )
|
||||
void AudioFileProcessor::pointChanged()
|
||||
{
|
||||
const f_cnt_t f_start = static_cast<f_cnt_t>( m_startPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
|
||||
const f_cnt_t f_end = static_cast<f_cnt_t>( m_endPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
|
||||
const f_cnt_t f_loop = static_cast<f_cnt_t>( m_loopPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
|
||||
const auto f_start = static_cast<f_cnt_t>(m_startPointModel.value() * m_sampleBuffer.frames());
|
||||
const auto f_end = static_cast<f_cnt_t>(m_endPointModel.value() * m_sampleBuffer.frames());
|
||||
const auto f_loop = static_cast<f_cnt_t>(m_loopPointModel.value() * m_sampleBuffer.frames());
|
||||
|
||||
m_nextPlayStartPoint = f_start;
|
||||
m_nextPlayBackwards = false;
|
||||
@@ -442,16 +440,18 @@ void audioFileProcessor::pointChanged( void )
|
||||
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
|
||||
QPixmap * AudioFileProcessorView::s_artwork = NULL;
|
||||
QPixmap * AudioFileProcessorView::s_artwork = nullptr;
|
||||
|
||||
|
||||
AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
|
||||
QWidget * _parent ) :
|
||||
InstrumentViewFixedSize( _instrument, _parent )
|
||||
{
|
||||
if( s_artwork == NULL )
|
||||
if( s_artwork == nullptr )
|
||||
{
|
||||
s_artwork = new QPixmap( PLUGIN_NAME::getIconPixmap(
|
||||
"artwork" ) );
|
||||
@@ -466,7 +466,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
|
||||
"select_file" ) );
|
||||
connect( m_openAudioFileButton, SIGNAL( clicked() ),
|
||||
this, SLOT( openAudioFile() ) );
|
||||
ToolTip::add( m_openAudioFileButton, tr( "Open sample" ) );
|
||||
m_openAudioFileButton->setToolTip(tr("Open sample"));
|
||||
|
||||
m_reverseButton = new PixmapButton( this );
|
||||
m_reverseButton->setCheckable( true );
|
||||
@@ -475,37 +475,36 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
|
||||
"reverse_on" ) );
|
||||
m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"reverse_off" ) );
|
||||
ToolTip::add( m_reverseButton, tr( "Reverse sample" ) );
|
||||
m_reverseButton->setToolTip(tr("Reverse sample"));
|
||||
|
||||
// loop button group
|
||||
|
||||
PixmapButton * m_loopOffButton = new PixmapButton( this );
|
||||
auto m_loopOffButton = new PixmapButton(this);
|
||||
m_loopOffButton->setCheckable( true );
|
||||
m_loopOffButton->move( 190, 105 );
|
||||
m_loopOffButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"loop_off_on" ) );
|
||||
m_loopOffButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"loop_off_off" ) );
|
||||
ToolTip::add( m_loopOffButton, tr( "Disable loop" ) );
|
||||
m_loopOffButton->setToolTip(tr("Disable loop"));
|
||||
|
||||
|
||||
PixmapButton * m_loopOnButton = new PixmapButton( this );
|
||||
auto m_loopOnButton = new PixmapButton(this);
|
||||
m_loopOnButton->setCheckable( true );
|
||||
m_loopOnButton->move( 190, 124 );
|
||||
m_loopOnButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"loop_on_on" ) );
|
||||
m_loopOnButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"loop_on_off" ) );
|
||||
ToolTip::add( m_loopOnButton, tr( "Enable loop" ) );
|
||||
m_loopOnButton->setToolTip(tr("Enable loop"));
|
||||
|
||||
PixmapButton * m_loopPingPongButton = new PixmapButton( this );
|
||||
auto m_loopPingPongButton = new PixmapButton(this);
|
||||
m_loopPingPongButton->setCheckable( true );
|
||||
m_loopPingPongButton->move( 216, 124 );
|
||||
m_loopPingPongButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"loop_pingpong_on" ) );
|
||||
m_loopPingPongButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"loop_pingpong_off" ) );
|
||||
ToolTip::add( m_loopPingPongButton, tr( "Enable ping-pong loop" ) );
|
||||
m_loopPingPongButton->setToolTip(tr("Enable ping-pong loop"));
|
||||
|
||||
m_loopGroup = new automatableButtonGroup( this );
|
||||
m_loopGroup->addButton( m_loopOffButton );
|
||||
@@ -519,7 +518,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
|
||||
"stutter_on" ) );
|
||||
m_stutterButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"stutter_off" ) );
|
||||
ToolTip::add( m_stutterButton,
|
||||
m_stutterButton->setToolTip(
|
||||
tr( "Continue sample playback across notes" ) );
|
||||
|
||||
m_ampKnob = new Knob( knobBright_26, this );
|
||||
@@ -541,15 +540,15 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
|
||||
|
||||
// interpolation selector
|
||||
m_interpBox = new ComboBox( this );
|
||||
m_interpBox->setGeometry( 142, 62, 82, 22 );
|
||||
m_interpBox->setGeometry( 142, 62, 82, ComboBox::DEFAULT_HEIGHT );
|
||||
m_interpBox->setFont( pointSize<8>( m_interpBox->font() ) );
|
||||
|
||||
// wavegraph
|
||||
m_waveView = 0;
|
||||
newWaveView();
|
||||
|
||||
connect( castModel<audioFileProcessor>(), SIGNAL( isPlaying( f_cnt_t ) ),
|
||||
m_waveView, SLOT( isPlaying( f_cnt_t ) ) );
|
||||
connect( castModel<AudioFileProcessor>(), SIGNAL( isPlaying( lmms::f_cnt_t ) ),
|
||||
m_waveView, SLOT( isPlaying( lmms::f_cnt_t ) ) );
|
||||
|
||||
qRegisterMetaType<f_cnt_t>( "f_cnt_t" );
|
||||
|
||||
@@ -559,20 +558,20 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
|
||||
|
||||
|
||||
|
||||
AudioFileProcessorView::~AudioFileProcessorView()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioFileProcessorView::dragEnterEvent( QDragEnterEvent * _dee )
|
||||
{
|
||||
if( _dee->mimeData()->hasFormat( StringPairDrag::mimeType() ) )
|
||||
// For mimeType() and MimeType enum class
|
||||
using namespace Clipboard;
|
||||
|
||||
if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) )
|
||||
{
|
||||
QString txt = _dee->mimeData()->data(
|
||||
StringPairDrag::mimeType() );
|
||||
if( txt.section( ':', 0, 0 ) == QString( "tco_%1" ).arg(
|
||||
mimeType( MimeType::StringPair ) );
|
||||
if( txt.section( ':', 0, 0 ) == QString( "clip_%1" ).arg(
|
||||
Track::SampleTrack ) )
|
||||
{
|
||||
_dee->acceptProposedAction();
|
||||
@@ -602,7 +601,7 @@ void AudioFileProcessorView::newWaveView()
|
||||
delete m_waveView;
|
||||
m_waveView = 0;
|
||||
}
|
||||
m_waveView = new AudioFileProcessorWaveView( this, 245, 75, castModel<audioFileProcessor>()->m_sampleBuffer );
|
||||
m_waveView = new AudioFileProcessorWaveView( this, 245, 75, castModel<AudioFileProcessor>()->m_sampleBuffer );
|
||||
m_waveView->move( 2, 172 );
|
||||
m_waveView->setKnobs(
|
||||
dynamic_cast<AudioFileProcessorWaveView::knob *>( m_startKnob ),
|
||||
@@ -620,15 +619,15 @@ void AudioFileProcessorView::dropEvent( QDropEvent * _de )
|
||||
QString value = StringPairDrag::decodeValue( _de );
|
||||
if( type == "samplefile" )
|
||||
{
|
||||
castModel<audioFileProcessor>()->setAudioFile( value );
|
||||
castModel<AudioFileProcessor>()->setAudioFile( value );
|
||||
_de->accept();
|
||||
newWaveView();
|
||||
return;
|
||||
}
|
||||
else if( type == QString( "tco_%1" ).arg( Track::SampleTrack ) )
|
||||
else if( type == QString( "clip_%1" ).arg( Track::SampleTrack ) )
|
||||
{
|
||||
DataFile dataFile( value.toUtf8() );
|
||||
castModel<audioFileProcessor>()->setAudioFile( dataFile.content().firstChild().toElement().attribute( "src" ) );
|
||||
castModel<AudioFileProcessor>()->setAudioFile( dataFile.content().firstChild().toElement().attribute( "src" ) );
|
||||
_de->accept();
|
||||
return;
|
||||
}
|
||||
@@ -645,9 +644,9 @@ void AudioFileProcessorView::paintEvent( QPaintEvent * )
|
||||
|
||||
p.drawPixmap( 0, 0, *s_artwork );
|
||||
|
||||
audioFileProcessor * a = castModel<audioFileProcessor>();
|
||||
auto a = castModel<AudioFileProcessor>();
|
||||
|
||||
QString file_name = "";
|
||||
QString file_name = "";
|
||||
int idx = a->m_sampleBuffer.audioFile().length();
|
||||
|
||||
p.setFont( pointSize<8>( font() ) );
|
||||
@@ -674,7 +673,7 @@ void AudioFileProcessorView::paintEvent( QPaintEvent * )
|
||||
|
||||
|
||||
|
||||
void AudioFileProcessorView::sampleUpdated( void )
|
||||
void AudioFileProcessorView::sampleUpdated()
|
||||
{
|
||||
m_waveView->updateSampleRange();
|
||||
m_waveView->update();
|
||||
@@ -685,13 +684,13 @@ void AudioFileProcessorView::sampleUpdated( void )
|
||||
|
||||
|
||||
|
||||
void AudioFileProcessorView::openAudioFile( void )
|
||||
void AudioFileProcessorView::openAudioFile()
|
||||
{
|
||||
QString af = castModel<audioFileProcessor>()->m_sampleBuffer.
|
||||
QString af = castModel<AudioFileProcessor>()->m_sampleBuffer.
|
||||
openAudioFile();
|
||||
if( af != "" )
|
||||
{
|
||||
castModel<audioFileProcessor>()->setAudioFile( af );
|
||||
castModel<AudioFileProcessor>()->setAudioFile( af );
|
||||
Engine::getSong()->setModified();
|
||||
m_waveView->updateSampleRange();
|
||||
}
|
||||
@@ -700,9 +699,9 @@ void AudioFileProcessorView::openAudioFile( void )
|
||||
|
||||
|
||||
|
||||
void AudioFileProcessorView::modelChanged( void )
|
||||
void AudioFileProcessorView::modelChanged()
|
||||
{
|
||||
audioFileProcessor * a = castModel<audioFileProcessor>();
|
||||
auto a = castModel<AudioFileProcessor>();
|
||||
connect( &a->m_sampleBuffer, SIGNAL( sampleUpdated() ),
|
||||
this, SLOT( sampleUpdated() ) );
|
||||
m_ampKnob->setModel( &a->m_ampModel );
|
||||
@@ -867,7 +866,7 @@ void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me )
|
||||
|
||||
void AudioFileProcessorWaveView::wheelEvent( QWheelEvent * _we )
|
||||
{
|
||||
zoom( _we->delta() > 0 );
|
||||
zoom( _we->angleDelta().y() > 0 );
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -1143,7 +1142,7 @@ void AudioFileProcessorWaveView::slideSamplePointByFrames( knobType _point, f_cn
|
||||
case start:
|
||||
break;
|
||||
}
|
||||
if( a_knob == NULL )
|
||||
if( a_knob == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1239,7 +1238,7 @@ float AudioFileProcessorWaveView::knob::getValue( const QPoint & _p )
|
||||
const double dec_fact = ! m_waveView ? 1 :
|
||||
double( m_waveView->m_to - m_waveView->m_from )
|
||||
/ m_waveView->m_sampleBuffer.frames();
|
||||
const float inc = ::Knob::getValue( _p ) * dec_fact;
|
||||
const float inc = Knob::getValue( _p ) * dec_fact;
|
||||
|
||||
return inc;
|
||||
}
|
||||
@@ -1270,6 +1269,7 @@ bool AudioFileProcessorWaveView::knob::checkBound( double _v ) const
|
||||
}
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
|
||||
|
||||
@@ -1280,12 +1280,11 @@ extern "C"
|
||||
// necessary for getting instance out of shared lib
|
||||
PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * model, void *)
|
||||
{
|
||||
return new audioFileProcessor(static_cast<InstrumentTrack *>(model));
|
||||
return new AudioFileProcessor(static_cast<InstrumentTrack *>(model));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* audio_file_processor.h - declaration of class audioFileProcessor
|
||||
* AudioFileProcessor.h - declaration of class AudioFileProcessor
|
||||
* (instrument-plugin for using audio-files)
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
@@ -29,42 +29,54 @@
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
#include "ComboBoxModel.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "Knob.h"
|
||||
#include "PixmapButton.h"
|
||||
#include "AutomatableButton.h"
|
||||
#include "ComboBox.h"
|
||||
|
||||
|
||||
class audioFileProcessor : public Instrument
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class automatableButtonGroup;
|
||||
class PluginView;
|
||||
class InstrumentViewFixedSize;
|
||||
class Knob;
|
||||
class PixmapButton;
|
||||
class ComboBox;
|
||||
class AudioFileProcessorView;
|
||||
}
|
||||
|
||||
|
||||
class AudioFileProcessor : public Instrument
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
audioFileProcessor( InstrumentTrack * _instrument_track );
|
||||
virtual ~audioFileProcessor();
|
||||
AudioFileProcessor( InstrumentTrack * _instrument_track );
|
||||
|
||||
virtual void playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer );
|
||||
virtual void deleteNotePluginData( NotePlayHandle * _n );
|
||||
void playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer ) override;
|
||||
void deleteNotePluginData( NotePlayHandle * _n ) override;
|
||||
|
||||
virtual void saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _parent );
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
void saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _parent ) override;
|
||||
void loadSettings( const QDomElement & _this ) override;
|
||||
|
||||
virtual void loadFile( const QString & _file );
|
||||
void loadFile( const QString & _file ) override;
|
||||
|
||||
virtual QString nodeName() const;
|
||||
QString nodeName() const override;
|
||||
|
||||
virtual int getBeatLen( NotePlayHandle * _n ) const;
|
||||
|
||||
virtual f_cnt_t desiredReleaseFrames() const
|
||||
f_cnt_t desiredReleaseFrames() const override
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
|
||||
virtual PluginView * instantiateView( QWidget * _parent );
|
||||
gui::PluginView* instantiateView( QWidget * _parent ) override;
|
||||
|
||||
|
||||
public slots:
|
||||
@@ -82,11 +94,11 @@ private slots:
|
||||
|
||||
|
||||
signals:
|
||||
void isPlaying( f_cnt_t _current_frame );
|
||||
void isPlaying( lmms::f_cnt_t _current_frame );
|
||||
|
||||
|
||||
private:
|
||||
typedef SampleBuffer::handleState handleState;
|
||||
using handleState = SampleBuffer::handleState;
|
||||
|
||||
SampleBuffer m_sampleBuffer;
|
||||
|
||||
@@ -102,21 +114,23 @@ private:
|
||||
f_cnt_t m_nextPlayStartPoint;
|
||||
bool m_nextPlayBackwards;
|
||||
|
||||
friend class AudioFileProcessorView;
|
||||
friend class gui::AudioFileProcessorView;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class AudioFileProcessorWaveView;
|
||||
|
||||
|
||||
class AudioFileProcessorView : public InstrumentViewFixedSize
|
||||
class AudioFileProcessorView : public gui::InstrumentViewFixedSize
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioFileProcessorView( Instrument * _instrument, QWidget * _parent );
|
||||
virtual ~AudioFileProcessorView();
|
||||
virtual ~AudioFileProcessorView() = default;
|
||||
|
||||
void newWaveView();
|
||||
protected slots:
|
||||
@@ -141,7 +155,7 @@ private:
|
||||
Knob * m_endKnob;
|
||||
Knob * m_loopKnob;
|
||||
|
||||
PixmapButton * m_openAudioFileButton;
|
||||
gui::PixmapButton * m_openAudioFileButton;
|
||||
PixmapButton * m_reverseButton;
|
||||
automatableButtonGroup * m_loopGroup;
|
||||
PixmapButton * m_stutterButton;
|
||||
@@ -172,7 +186,7 @@ public:
|
||||
loop
|
||||
} ;
|
||||
|
||||
class knob : public ::Knob
|
||||
class knob : public Knob
|
||||
{
|
||||
const AudioFileProcessorWaveView * m_waveView;
|
||||
const Knob * m_relatedKnob;
|
||||
@@ -180,7 +194,7 @@ public:
|
||||
|
||||
public:
|
||||
knob( QWidget * _parent ) :
|
||||
::Knob( knobBright_26, _parent ),
|
||||
Knob( knobBright_26, _parent ),
|
||||
m_waveView( 0 ),
|
||||
m_relatedKnob( 0 )
|
||||
{
|
||||
@@ -221,7 +235,7 @@ public slots:
|
||||
QWidget::update();
|
||||
}
|
||||
|
||||
void isPlaying( f_cnt_t _current_frame );
|
||||
void isPlaying( lmms::f_cnt_t _current_frame );
|
||||
|
||||
|
||||
private:
|
||||
@@ -285,6 +299,8 @@ private:
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
3
plugins/AudioFileProcessor/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(audiofileprocessor AudioFileProcessor.cpp AudioFileProcessor.h MOCFILES AudioFileProcessor.h EMBEDDED_RESOURCES *.png)
|
||||
|
Before Width: | Height: | Size: 245 KiB After Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -27,20 +27,24 @@
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT bassbooster_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
LMMS_STRINGIFY( PLUGIN_NAME ),
|
||||
"BassBooster",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser", "Boost your bass the fast and simple way" ),
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser", "Boost your bass the fast and simple way" ),
|
||||
"Tobias Doerffel <tobydox/at/users.sf.net>",
|
||||
0x0100,
|
||||
Plugin::Effect,
|
||||
new PluginPixmapLoader("logo"),
|
||||
NULL,
|
||||
NULL
|
||||
nullptr,
|
||||
nullptr,
|
||||
} ;
|
||||
|
||||
}
|
||||
@@ -61,9 +65,6 @@ BassBoosterEffect::BassBoosterEffect( Model* parent, const Descriptor::SubPlugin
|
||||
|
||||
|
||||
|
||||
BassBoosterEffect::~BassBoosterEffect()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -100,13 +101,13 @@ bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames
|
||||
//float gain = gainBuffer ? gainBuffer[f] : gain;
|
||||
m_bbFX.leftFX().setGain( gain );
|
||||
m_bbFX.rightFX().setGain( gain);
|
||||
outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1];
|
||||
|
||||
sample_t s[2] = { buf[f][0], buf[f][1] };
|
||||
m_bbFX.nextSample( s[0], s[1] );
|
||||
|
||||
buf[f][0] = d * buf[f][0] + w * s[0];
|
||||
buf[f][1] = d * buf[f][1] + w * s[1];
|
||||
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
|
||||
}
|
||||
|
||||
checkGate( outSum / frames );
|
||||
@@ -117,7 +118,7 @@ bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames
|
||||
|
||||
inline void BassBoosterEffect::changeFrequency()
|
||||
{
|
||||
const sample_t fac = Engine::mixer()->processingSampleRate() / 44100.0f;
|
||||
const sample_t fac = Engine::audioEngine()->processingSampleRate() / 44100.0f;
|
||||
|
||||
m_bbFX.leftFX().setFrequency( m_bbControls.m_freqModel.value() * fac );
|
||||
m_bbFX.rightFX().setFrequency( m_bbControls.m_freqModel.value() * fac );
|
||||
@@ -155,3 +156,5 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data )
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
@@ -30,15 +30,17 @@
|
||||
#include "DspEffectLibrary.h"
|
||||
#include "BassBoosterControls.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class BassBoosterEffect : public Effect
|
||||
{
|
||||
public:
|
||||
BassBoosterEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
|
||||
virtual ~BassBoosterEffect();
|
||||
virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames );
|
||||
~BassBoosterEffect() override = default;
|
||||
bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override;
|
||||
|
||||
virtual EffectControls* controls()
|
||||
EffectControls* controls() override
|
||||
{
|
||||
return &m_bbControls;
|
||||
}
|
||||
@@ -60,4 +62,7 @@ private:
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,13 +22,19 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QLayout>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "BassBoosterControlDialog.h"
|
||||
#include "BassBoosterControls.h"
|
||||
#include "embed.h"
|
||||
#include "Knob.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
BassBoosterControlDialog::BassBoosterControlDialog( BassBoosterControls* controls ) :
|
||||
EffectControlDialog( controls )
|
||||
@@ -39,22 +45,22 @@ BassBoosterControlDialog::BassBoosterControlDialog( BassBoosterControls* control
|
||||
setPalette( pal );
|
||||
setFixedSize( 120, 60 );
|
||||
|
||||
QVBoxLayout * tl = new QVBoxLayout( this );
|
||||
auto tl = new QVBoxLayout(this);
|
||||
tl->addSpacing( 4 );
|
||||
|
||||
QHBoxLayout * l = new QHBoxLayout;
|
||||
auto l = new QHBoxLayout;
|
||||
|
||||
Knob * freqKnob = new Knob( knobBright_26, this);
|
||||
auto freqKnob = new Knob(knobBright_26, this);
|
||||
freqKnob->setModel( &controls->m_freqModel );
|
||||
freqKnob->setLabel( tr( "FREQ" ) );
|
||||
freqKnob->setHintText( tr( "Frequency:" ) , "Hz" );
|
||||
|
||||
Knob * gainKnob = new Knob( knobBright_26, this );
|
||||
auto gainKnob = new Knob(knobBright_26, this);
|
||||
gainKnob->setModel( &controls->m_gainModel );
|
||||
gainKnob->setLabel( tr( "GAIN" ) );
|
||||
gainKnob->setHintText( tr( "Gain:" ) , "" );
|
||||
|
||||
Knob * ratioKnob = new Knob( knobBright_26, this );
|
||||
auto ratioKnob = new Knob(knobBright_26, this);
|
||||
ratioKnob->setModel( &controls->m_ratioModel );
|
||||
ratioKnob->setLabel( tr( "RATIO" ) );
|
||||
ratioKnob->setHintText( tr( "Ratio:" ) , "" );
|
||||
@@ -66,3 +72,6 @@ BassBoosterControlDialog::BassBoosterControlDialog( BassBoosterControls* control
|
||||
tl->addLayout( l );
|
||||
setLayout( tl );
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
@@ -27,19 +27,26 @@
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class BassBoosterControls;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class BassBoosterControlDialog : public EffectControlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BassBoosterControlDialog( BassBoosterControls* controls );
|
||||
virtual ~BassBoosterControlDialog()
|
||||
{
|
||||
}
|
||||
~BassBoosterControlDialog() override = default;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "BassBoosterControls.h"
|
||||
#include "BassBooster.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
BassBoosterControls::BassBoosterControls( BassBoosterEffect* effect ) :
|
||||
@@ -37,7 +39,7 @@ BassBoosterControls::BassBoosterControls( BassBoosterEffect* effect ) :
|
||||
m_gainModel( 1.0f, 0.1f, 5.0f, 0.05f, this, tr( "Gain" ) ),
|
||||
m_ratioModel( 2.0f, 0.1f, 10.0f, 0.1f, this, tr( "Ratio" ) )
|
||||
{
|
||||
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changeFrequency() ) );
|
||||
connect( Engine::audioEngine(), SIGNAL( sampleRateChanged() ), this, SLOT( changeFrequency() ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +68,4 @@ void BassBoosterControls::saveSettings( QDomDocument& doc, QDomElement& _this )
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
@@ -27,8 +27,9 @@
|
||||
|
||||
#include "EffectControls.h"
|
||||
#include "BassBoosterControlDialog.h"
|
||||
#include "Knob.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class BassBoosterEffect;
|
||||
|
||||
@@ -38,25 +39,23 @@ class BassBoosterControls : public EffectControls
|
||||
Q_OBJECT
|
||||
public:
|
||||
BassBoosterControls( BassBoosterEffect* effect );
|
||||
virtual ~BassBoosterControls()
|
||||
{
|
||||
}
|
||||
~BassBoosterControls() override = default;
|
||||
|
||||
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
inline virtual QString nodeName() const
|
||||
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
|
||||
void loadSettings( const QDomElement & _this ) override;
|
||||
inline QString nodeName() const override
|
||||
{
|
||||
return "bassboostercontrols";
|
||||
}
|
||||
|
||||
virtual int controlCount()
|
||||
int controlCount() override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
virtual EffectControlDialog* createView()
|
||||
gui::EffectControlDialog* createView() override
|
||||
{
|
||||
return new BassBoosterControlDialog( this );
|
||||
return new gui::BassBoosterControlDialog( this );
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +68,11 @@ private:
|
||||
FloatModel m_gainModel;
|
||||
FloatModel m_ratioModel;
|
||||
|
||||
friend class BassBoosterControlDialog;
|
||||
friend class gui::BassBoosterControlDialog;
|
||||
friend class BassBoosterEffect;
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* bit_invader.cpp - instrument which uses a usereditable wavetable
|
||||
* BitInvader.cpp - instrument which uses a usereditable wavetable
|
||||
*
|
||||
* Copyright (c) 2006-2008 Andreas Brandmaier <andy/at/brandmaier/dot/de>
|
||||
*
|
||||
@@ -25,18 +25,16 @@
|
||||
|
||||
#include <QDomElement>
|
||||
|
||||
#include "bit_invader.h"
|
||||
#include "BitInvader.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "base64.h"
|
||||
#include "Engine.h"
|
||||
#include "Graph.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "Knob.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "Mixer.h"
|
||||
#include "LedCheckBox.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "Oscillator.h"
|
||||
#include "PixmapButton.h"
|
||||
#include "ToolTip.h"
|
||||
#include "Song.h"
|
||||
#include "interpolation.h"
|
||||
|
||||
@@ -44,55 +42,69 @@
|
||||
|
||||
#include "plugin_export.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
static const int wavetableSize = 200;
|
||||
static const float defaultNormalizationFactor = 1.0f;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT bitinvader_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
LMMS_STRINGIFY( PLUGIN_NAME ),
|
||||
"BitInvader",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser",
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser",
|
||||
"Customizable wavetable synthesizer" ),
|
||||
"Andreas Brandmaier <andreas/at/brandmaier/dot/de>",
|
||||
0x0100,
|
||||
Plugin::Instrument,
|
||||
new PluginPixmapLoader( "logo" ),
|
||||
NULL,
|
||||
NULL
|
||||
nullptr,
|
||||
nullptr,
|
||||
} ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bSynth::bSynth( float * _shape, int _length, NotePlayHandle * _nph, bool _interpolation,
|
||||
BSynth::BSynth( float * _shape, NotePlayHandle * _nph, bool _interpolation,
|
||||
float _factor, const sample_rate_t _sample_rate ) :
|
||||
sample_index( 0 ),
|
||||
sample_realindex( 0 ),
|
||||
nph( _nph ),
|
||||
sample_length( _length ),
|
||||
sample_rate( _sample_rate ),
|
||||
interpolation( _interpolation)
|
||||
{
|
||||
sample_shape = new float[sample_length];
|
||||
for (int i=0; i < _length; ++i)
|
||||
sample_shape = new float[wavetableSize];
|
||||
for (int i=0; i < wavetableSize; ++i)
|
||||
{
|
||||
sample_shape[i] = _shape[i] * _factor;
|
||||
float buf = _shape[i] * _factor;
|
||||
|
||||
/* Double check that normalization has been performed correctly,
|
||||
i.e., the absolute value of all samples is <= 1.0 if _factor
|
||||
is different to the default normalization factor. If there is
|
||||
a value > 1.0, clip the sample to 1.0 to limit the range. */
|
||||
if ((_factor != defaultNormalizationFactor) && (fabsf(buf) > 1.0f))
|
||||
{
|
||||
buf = (buf < 0) ? -1.0f : 1.0f;
|
||||
}
|
||||
sample_shape[i] = buf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bSynth::~bSynth()
|
||||
BSynth::~BSynth()
|
||||
{
|
||||
delete[] sample_shape;
|
||||
}
|
||||
|
||||
|
||||
sample_t bSynth::nextStringSample()
|
||||
sample_t BSynth::nextStringSample( float sample_length )
|
||||
{
|
||||
float sample_step =
|
||||
static_cast<float>( sample_length / ( sample_rate / nph->frequency() ) );
|
||||
auto sample_step = static_cast<float>(sample_length / (sample_rate / nph->frequency()));
|
||||
|
||||
|
||||
// check overflow
|
||||
while (sample_realindex >= sample_length) {
|
||||
sample_realindex -= sample_length;
|
||||
@@ -137,35 +149,30 @@ sample_t bSynth::nextStringSample()
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
bitInvader::bitInvader( InstrumentTrack * _instrument_track ) :
|
||||
BitInvader::BitInvader( InstrumentTrack * _instrument_track ) :
|
||||
Instrument( _instrument_track, &bitinvader_plugin_descriptor ),
|
||||
m_sampleLength( 128, 4, 200, 1, this, tr( "Sample length" ) ),
|
||||
m_graph( -1.0f, 1.0f, 128, this ),
|
||||
m_sampleLength(wavetableSize, 4, wavetableSize, 1, this, tr("Sample length")),
|
||||
m_graph(-1.0f, 1.0f, wavetableSize, this),
|
||||
m_interpolation( false, this ),
|
||||
m_normalize( false, this )
|
||||
{
|
||||
|
||||
m_graph.setWaveToSine();
|
||||
lengthChanged();
|
||||
|
||||
connect( &m_sampleLength, SIGNAL( dataChanged( ) ),
|
||||
this, SLOT( lengthChanged( ) ) );
|
||||
connect( &m_sampleLength, SIGNAL( dataChanged() ),
|
||||
this, SLOT( lengthChanged() ), Qt::DirectConnection );
|
||||
|
||||
connect( &m_graph, SIGNAL( samplesChanged( int, int ) ),
|
||||
this, SLOT( samplesChanged( int, int ) ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bitInvader::~bitInvader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void bitInvader::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
void BitInvader::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
{
|
||||
|
||||
// Save plugin version
|
||||
@@ -176,8 +183,8 @@ void bitInvader::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
|
||||
// Save sample shape base64-encoded
|
||||
QString sampleString;
|
||||
base64::encode( (const char *)m_graph.samples(),
|
||||
m_graph.length() * sizeof(float), sampleString );
|
||||
base64::encode((const char *)m_graph.samples(),
|
||||
wavetableSize * sizeof(float), sampleString);
|
||||
_this.setAttribute( "sampleShape", sampleString );
|
||||
|
||||
|
||||
@@ -191,8 +198,11 @@ void bitInvader::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
|
||||
|
||||
|
||||
void bitInvader::loadSettings( const QDomElement & _this )
|
||||
void BitInvader::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
// Clear wavetable before loading a new
|
||||
m_graph.clear();
|
||||
|
||||
// Load sample length
|
||||
m_sampleLength.loadSettings( _this, "sampleLength" );
|
||||
|
||||
@@ -203,8 +213,9 @@ void bitInvader::loadSettings( const QDomElement & _this )
|
||||
char * dst = 0;
|
||||
base64::decode( _this.attribute( "sampleShape"), &dst, &size );
|
||||
|
||||
m_graph.setLength( sampleLength );
|
||||
m_graph.setSamples( (float*) dst );
|
||||
m_graph.setLength(size / sizeof(float));
|
||||
m_graph.setSamples(reinterpret_cast<float*>(dst));
|
||||
m_graph.setLength(sampleLength);
|
||||
delete[] dst;
|
||||
|
||||
// Load LED normalize
|
||||
@@ -217,7 +228,7 @@ void bitInvader::loadSettings( const QDomElement & _this )
|
||||
|
||||
|
||||
|
||||
void bitInvader::lengthChanged()
|
||||
void BitInvader::lengthChanged()
|
||||
{
|
||||
m_graph.setLength( (int) m_sampleLength.value() );
|
||||
|
||||
@@ -227,7 +238,7 @@ void bitInvader::lengthChanged()
|
||||
|
||||
|
||||
|
||||
void bitInvader::samplesChanged( int _begin, int _end )
|
||||
void BitInvader::samplesChanged( int _begin, int _end )
|
||||
{
|
||||
normalize();
|
||||
//engine::getSongEditor()->setModified();
|
||||
@@ -236,10 +247,10 @@ void bitInvader::samplesChanged( int _begin, int _end )
|
||||
|
||||
|
||||
|
||||
void bitInvader::normalize()
|
||||
void BitInvader::normalize()
|
||||
{
|
||||
// analyze
|
||||
float max = 0;
|
||||
float max = std::numeric_limits<float>::epsilon();
|
||||
const float* samples = m_graph.samples();
|
||||
for(int i=0; i < m_graph.length(); i++)
|
||||
{
|
||||
@@ -252,7 +263,7 @@ void bitInvader::normalize()
|
||||
|
||||
|
||||
|
||||
QString bitInvader::nodeName() const
|
||||
QString BitInvader::nodeName() const
|
||||
{
|
||||
return( bitinvader_plugin_descriptor.name );
|
||||
}
|
||||
@@ -260,37 +271,36 @@ QString bitInvader::nodeName() const
|
||||
|
||||
|
||||
|
||||
void bitInvader::playNote( NotePlayHandle * _n,
|
||||
void BitInvader::playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
|
||||
if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr )
|
||||
{
|
||||
|
||||
float factor;
|
||||
if( !m_normalize.value() )
|
||||
{
|
||||
factor = 1.0f;
|
||||
factor = defaultNormalizationFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
factor = m_normalizeFactor;
|
||||
}
|
||||
|
||||
_n->m_pluginData = new bSynth(
|
||||
_n->m_pluginData = new BSynth(
|
||||
const_cast<float*>( m_graph.samples() ),
|
||||
m_graph.length(),
|
||||
_n,
|
||||
m_interpolation.value(), factor,
|
||||
Engine::mixer()->processingSampleRate() );
|
||||
Engine::audioEngine()->processingSampleRate() );
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
bSynth * ps = static_cast<bSynth *>( _n->m_pluginData );
|
||||
auto ps = static_cast<BSynth*>(_n->m_pluginData);
|
||||
for( fpp_t frame = offset; frame < frames + offset; ++frame )
|
||||
{
|
||||
const sample_t cur = ps->nextStringSample();
|
||||
const sample_t cur = ps->nextStringSample( m_graph.length() );
|
||||
for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl )
|
||||
{
|
||||
_working_buffer[frame][chnl] = cur;
|
||||
@@ -305,26 +315,27 @@ void bitInvader::playNote( NotePlayHandle * _n,
|
||||
|
||||
|
||||
|
||||
void bitInvader::deleteNotePluginData( NotePlayHandle * _n )
|
||||
void BitInvader::deleteNotePluginData( NotePlayHandle * _n )
|
||||
{
|
||||
delete static_cast<bSynth *>( _n->m_pluginData );
|
||||
delete static_cast<BSynth *>( _n->m_pluginData );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PluginView * bitInvader::instantiateView( QWidget * _parent )
|
||||
gui::PluginView * BitInvader::instantiateView( QWidget * _parent )
|
||||
{
|
||||
return( new bitInvaderView( this, _parent ) );
|
||||
return( new gui::BitInvaderView( this, _parent ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
|
||||
|
||||
bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
BitInvaderView::BitInvaderView( Instrument * _instrument,
|
||||
QWidget * _parent ) :
|
||||
InstrumentViewFixedSize( _instrument, _parent )
|
||||
{
|
||||
@@ -344,7 +355,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
m_graph->setAutoFillBackground( true );
|
||||
m_graph->setGraphColor( QColor( 255, 255, 255 ) );
|
||||
|
||||
ToolTip::add( m_graph, tr ( "Draw your own waveform here "
|
||||
m_graph->setToolTip(tr("Draw your own waveform here "
|
||||
"by dragging your mouse on this graph."
|
||||
));
|
||||
|
||||
@@ -361,7 +372,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
"sin_wave_active" ) );
|
||||
m_sinWaveBtn->setInactiveGraphic( embed::getIconPixmap(
|
||||
"sin_wave_inactive" ) );
|
||||
ToolTip::add( m_sinWaveBtn,
|
||||
m_sinWaveBtn->setToolTip(
|
||||
tr( "Sine wave" ) );
|
||||
|
||||
m_triangleWaveBtn = new PixmapButton( this, tr( "Triangle wave" ) );
|
||||
@@ -370,7 +381,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
embed::getIconPixmap( "triangle_wave_active" ) );
|
||||
m_triangleWaveBtn->setInactiveGraphic(
|
||||
embed::getIconPixmap( "triangle_wave_inactive" ) );
|
||||
ToolTip::add( m_triangleWaveBtn,
|
||||
m_triangleWaveBtn->setToolTip(
|
||||
tr( "Triangle wave" ) );
|
||||
|
||||
m_sawWaveBtn = new PixmapButton( this, tr( "Saw wave" ) );
|
||||
@@ -379,7 +390,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
"saw_wave_active" ) );
|
||||
m_sawWaveBtn->setInactiveGraphic( embed::getIconPixmap(
|
||||
"saw_wave_inactive" ) );
|
||||
ToolTip::add( m_sawWaveBtn,
|
||||
m_sawWaveBtn->setToolTip(
|
||||
tr( "Saw wave" ) );
|
||||
|
||||
m_sqrWaveBtn = new PixmapButton( this, tr( "Square wave" ) );
|
||||
@@ -388,7 +399,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
"square_wave_active" ) );
|
||||
m_sqrWaveBtn->setInactiveGraphic( embed::getIconPixmap(
|
||||
"square_wave_inactive" ) );
|
||||
ToolTip::add( m_sqrWaveBtn,
|
||||
m_sqrWaveBtn->setToolTip(
|
||||
tr( "Square wave" ) );
|
||||
|
||||
m_whiteNoiseWaveBtn = new PixmapButton( this,
|
||||
@@ -398,7 +409,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
embed::getIconPixmap( "white_noise_wave_active" ) );
|
||||
m_whiteNoiseWaveBtn->setInactiveGraphic(
|
||||
embed::getIconPixmap( "white_noise_wave_inactive" ) );
|
||||
ToolTip::add( m_whiteNoiseWaveBtn,
|
||||
m_whiteNoiseWaveBtn->setToolTip(
|
||||
tr( "White noise" ) );
|
||||
|
||||
m_usrWaveBtn = new PixmapButton( this, tr( "User-defined wave" ) );
|
||||
@@ -407,7 +418,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
"usr_wave_active" ) );
|
||||
m_usrWaveBtn->setInactiveGraphic( embed::getIconPixmap(
|
||||
"usr_wave_inactive" ) );
|
||||
ToolTip::add( m_usrWaveBtn,
|
||||
m_usrWaveBtn->setToolTip(
|
||||
tr( "User-defined wave" ) );
|
||||
|
||||
m_smoothBtn = new PixmapButton( this, tr( "Smooth waveform" ) );
|
||||
@@ -416,7 +427,7 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
"smooth_active" ) );
|
||||
m_smoothBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
|
||||
"smooth_inactive" ) );
|
||||
ToolTip::add( m_smoothBtn,
|
||||
m_smoothBtn->setToolTip(
|
||||
tr( "Smooth waveform" ) );
|
||||
|
||||
|
||||
@@ -457,9 +468,9 @@ bitInvaderView::bitInvaderView( Instrument * _instrument,
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::modelChanged()
|
||||
void BitInvaderView::modelChanged()
|
||||
{
|
||||
bitInvader * b = castModel<bitInvader>();
|
||||
auto b = castModel<BitInvader>();
|
||||
|
||||
m_graph->setModel( &b->m_graph );
|
||||
m_sampleLengthKnob->setModel( &b->m_sampleLength );
|
||||
@@ -471,7 +482,7 @@ void bitInvaderView::modelChanged()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::sinWaveClicked()
|
||||
void BitInvaderView::sinWaveClicked()
|
||||
{
|
||||
m_graph->model()->clearInvisible();
|
||||
m_graph->model()->setWaveToSine();
|
||||
@@ -481,7 +492,7 @@ void bitInvaderView::sinWaveClicked()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::triangleWaveClicked()
|
||||
void BitInvaderView::triangleWaveClicked()
|
||||
{
|
||||
m_graph->model()->clearInvisible();
|
||||
m_graph->model()->setWaveToTriangle();
|
||||
@@ -491,7 +502,7 @@ void bitInvaderView::triangleWaveClicked()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::sawWaveClicked()
|
||||
void BitInvaderView::sawWaveClicked()
|
||||
{
|
||||
m_graph->model()->clearInvisible();
|
||||
m_graph->model()->setWaveToSaw();
|
||||
@@ -501,7 +512,7 @@ void bitInvaderView::sawWaveClicked()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::sqrWaveClicked()
|
||||
void BitInvaderView::sqrWaveClicked()
|
||||
{
|
||||
m_graph->model()->clearInvisible();
|
||||
m_graph->model()->setWaveToSquare();
|
||||
@@ -511,7 +522,7 @@ void bitInvaderView::sqrWaveClicked()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::noiseWaveClicked()
|
||||
void BitInvaderView::noiseWaveClicked()
|
||||
{
|
||||
m_graph->model()->clearInvisible();
|
||||
m_graph->model()->setWaveToNoise();
|
||||
@@ -521,12 +532,12 @@ void bitInvaderView::noiseWaveClicked()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::usrWaveClicked()
|
||||
void BitInvaderView::usrWaveClicked()
|
||||
{
|
||||
QString fileName = m_graph->model()->setWaveToUser();
|
||||
if (!fileName.isEmpty())
|
||||
{
|
||||
ToolTip::add(m_usrWaveBtn, fileName);
|
||||
m_usrWaveBtn->setToolTip(fileName);
|
||||
m_graph->model()->clearInvisible();
|
||||
Engine::getSong()->setModified();
|
||||
}
|
||||
@@ -535,7 +546,7 @@ void bitInvaderView::usrWaveClicked()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::smoothClicked()
|
||||
void BitInvaderView::smoothClicked()
|
||||
{
|
||||
m_graph->model()->smooth();
|
||||
Engine::getSong()->setModified();
|
||||
@@ -544,7 +555,7 @@ void bitInvaderView::smoothClicked()
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::interpolationToggled( bool value )
|
||||
void BitInvaderView::interpolationToggled( bool value )
|
||||
{
|
||||
m_graph->setGraphStyle( value ? Graph::LinearStyle : Graph::NearestStyle);
|
||||
Engine::getSong()->setModified();
|
||||
@@ -553,12 +564,13 @@ void bitInvaderView::interpolationToggled( bool value )
|
||||
|
||||
|
||||
|
||||
void bitInvaderView::normalizeToggled( bool value )
|
||||
void BitInvaderView::normalizeToggled( bool value )
|
||||
{
|
||||
Engine::getSong()->setModified();
|
||||
}
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
|
||||
extern "C"
|
||||
@@ -567,12 +579,11 @@ extern "C"
|
||||
// necessary for getting instance out of shared lib
|
||||
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * )
|
||||
{
|
||||
return( new bitInvader( static_cast<InstrumentTrack *>( m ) ) );
|
||||
return( new BitInvader( static_cast<InstrumentTrack *>( m ) ) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* bit_invader.h - declaration of class bitInvader and bSynth which
|
||||
* BitInvader.h - declaration of class BitInvader and BSynth which
|
||||
* are a wavetable synthesizer
|
||||
*
|
||||
* Copyright (c) 2006-2008 Andreas Brandmaier <andy/at/brandmaier/dot/de>
|
||||
@@ -27,27 +27,35 @@
|
||||
#ifndef BIT_INVADER_H
|
||||
#define BIT_INVADER_H
|
||||
|
||||
#include "AutomatableModel.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
#include "Graph.h"
|
||||
#include "Knob.h"
|
||||
#include "PixmapButton.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class oscillator;
|
||||
class bitInvaderView;
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class bSynth
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class BitInvaderView;
|
||||
class Knob;
|
||||
class LedCheckBox;
|
||||
class PixmapButton;
|
||||
}
|
||||
|
||||
|
||||
class BSynth
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
bSynth( float * sample, int length, NotePlayHandle * _nph,
|
||||
BSynth( float * sample, NotePlayHandle * _nph,
|
||||
bool _interpolation, float factor,
|
||||
const sample_rate_t _sample_rate );
|
||||
virtual ~bSynth();
|
||||
virtual ~BSynth();
|
||||
|
||||
sample_t nextStringSample();
|
||||
sample_t nextStringSample( float sample_length );
|
||||
|
||||
|
||||
private:
|
||||
@@ -55,37 +63,36 @@ private:
|
||||
float sample_realindex;
|
||||
float* sample_shape;
|
||||
NotePlayHandle* nph;
|
||||
const int sample_length;
|
||||
const sample_rate_t sample_rate;
|
||||
|
||||
bool interpolation;
|
||||
|
||||
} ;
|
||||
|
||||
class bitInvader : public Instrument
|
||||
class BitInvader : public Instrument
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
bitInvader(InstrumentTrack * _instrument_track );
|
||||
virtual ~bitInvader();
|
||||
BitInvader(InstrumentTrack * _instrument_track );
|
||||
~BitInvader() override = default;
|
||||
|
||||
virtual void playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer );
|
||||
virtual void deleteNotePluginData( NotePlayHandle * _n );
|
||||
void playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer ) override;
|
||||
void deleteNotePluginData( NotePlayHandle * _n ) override;
|
||||
|
||||
|
||||
virtual void saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _parent );
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
void saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _parent ) override;
|
||||
void loadSettings( const QDomElement & _this ) override;
|
||||
|
||||
virtual QString nodeName() const;
|
||||
QString nodeName() const override;
|
||||
|
||||
virtual f_cnt_t desiredReleaseFrames() const
|
||||
f_cnt_t desiredReleaseFrames() const override
|
||||
{
|
||||
return( 64 );
|
||||
}
|
||||
|
||||
virtual PluginView * instantiateView( QWidget * _parent );
|
||||
gui::PluginView * instantiateView( QWidget * _parent ) override;
|
||||
|
||||
protected slots:
|
||||
void lengthChanged();
|
||||
@@ -103,19 +110,21 @@ private:
|
||||
|
||||
float m_normalizeFactor;
|
||||
|
||||
friend class bitInvaderView;
|
||||
friend class gui::BitInvaderView;
|
||||
} ;
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class bitInvaderView : public InstrumentViewFixedSize
|
||||
class BitInvaderView : public InstrumentViewFixedSize
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
bitInvaderView( Instrument * _instrument,
|
||||
BitInvaderView( Instrument * _instrument,
|
||||
QWidget * _parent );
|
||||
|
||||
virtual ~bitInvaderView() {};
|
||||
~BitInvaderView() override = default;
|
||||
|
||||
protected slots:
|
||||
//void sampleSizeChanged( float _new_sample_length );
|
||||
@@ -130,10 +139,10 @@ protected slots:
|
||||
void noiseWaveClicked();
|
||||
void usrWaveClicked();
|
||||
|
||||
void smoothClicked( void );
|
||||
void smoothClicked( );
|
||||
|
||||
private:
|
||||
virtual void modelChanged();
|
||||
void modelChanged() override;
|
||||
|
||||
Knob * m_sampleLengthKnob;
|
||||
PixmapButton * m_sinWaveBtn;
|
||||
@@ -153,5 +162,8 @@ private:
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
3
plugins/BitInvader/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(bitinvader BitInvader.cpp BitInvader.h MOCFILES BitInvader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png")
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 434 B |
|
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 352 B |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@@ -27,6 +27,10 @@
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
const int OS_RATE = 5;
|
||||
const float OS_RATIO = 1.0f / OS_RATE;
|
||||
const float CUTOFF_RATIO = 0.353553391f;
|
||||
@@ -39,15 +43,15 @@ extern "C"
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT bitcrush_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
LMMS_STRINGIFY( PLUGIN_NAME ),
|
||||
"Bitcrush",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser", "An oversampling bitcrusher" ),
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser", "An oversampling bitcrusher" ),
|
||||
"Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>",
|
||||
0x0100,
|
||||
Plugin::Effect,
|
||||
new PluginPixmapLoader( "logo" ),
|
||||
NULL,
|
||||
NULL
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -55,10 +59,10 @@ Plugin::Descriptor PLUGIN_EXPORT bitcrush_plugin_descriptor =
|
||||
BitcrushEffect::BitcrushEffect( Model * parent, const Descriptor::SubPluginFeatures::Key * key ) :
|
||||
Effect( &bitcrush_plugin_descriptor, parent, key ),
|
||||
m_controls( this ),
|
||||
m_sampleRate( Engine::mixer()->processingSampleRate() ),
|
||||
m_sampleRate( Engine::audioEngine()->processingSampleRate() ),
|
||||
m_filter( m_sampleRate )
|
||||
{
|
||||
m_buffer = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() * OS_RATE );
|
||||
m_buffer = MM_ALLOC<sampleFrame>( Engine::audioEngine()->framesPerPeriod() * OS_RATE );
|
||||
m_filter.setLowpass( m_sampleRate * ( CUTOFF_RATIO * OS_RATIO ) );
|
||||
m_needsUpdate = true;
|
||||
|
||||
@@ -79,7 +83,7 @@ BitcrushEffect::~BitcrushEffect()
|
||||
|
||||
void BitcrushEffect::sampleRateChanged()
|
||||
{
|
||||
m_sampleRate = Engine::mixer()->processingSampleRate();
|
||||
m_sampleRate = Engine::audioEngine()->processingSampleRate();
|
||||
m_filter.setSampleRate( m_sampleRate );
|
||||
m_filter.setLowpass( m_sampleRate * ( CUTOFF_RATIO * OS_RATIO ) );
|
||||
m_needsUpdate = true;
|
||||
@@ -251,3 +255,6 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
@@ -29,18 +29,21 @@
|
||||
|
||||
#include "Effect.h"
|
||||
#include "BitcrushControls.h"
|
||||
#include "ValueBuffer.h"
|
||||
#include "lmms_math.h"
|
||||
#include "BasicFilters.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
class BitcrushEffect : public Effect
|
||||
{
|
||||
public:
|
||||
BitcrushEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
|
||||
virtual ~BitcrushEffect();
|
||||
virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames );
|
||||
~BitcrushEffect() override;
|
||||
bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override;
|
||||
|
||||
virtual EffectControls* controls()
|
||||
EffectControls* controls() override
|
||||
{
|
||||
return &m_controls;
|
||||
}
|
||||
@@ -80,4 +83,7 @@ private:
|
||||
friend class BitcrushControls;
|
||||
};
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
52
plugins/Bitcrush/BitcrushControlDialog.cpp
Executable file → Normal file
@@ -24,16 +24,18 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "embed.h"
|
||||
#include "BitcrushControlDialog.h"
|
||||
#include "BitcrushControls.h"
|
||||
#include "embed.h"
|
||||
#include "ToolTip.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "LedCheckBox.h"
|
||||
#include "Knob.h"
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) :
|
||||
EffectControlDialog( controls )
|
||||
{
|
||||
@@ -44,20 +46,20 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) :
|
||||
setFixedSize( 181, 128 );
|
||||
|
||||
// labels
|
||||
QLabel * inLabel = new QLabel( tr( "IN" ), this );
|
||||
auto inLabel = new QLabel(tr("IN"), this);
|
||||
inLabel->move( 24, 15 );
|
||||
|
||||
QLabel * outLabel = new QLabel( tr( "OUT" ), this );
|
||||
|
||||
auto outLabel = new QLabel(tr("OUT"), this);
|
||||
outLabel->move( 139, 15 );
|
||||
|
||||
// input knobs
|
||||
Knob * inGain = new Knob( knobBright_26, this );
|
||||
auto inGain = new Knob(knobBright_26, this);
|
||||
inGain->move( 16, 32 );
|
||||
inGain->setModel( & controls->m_inGain );
|
||||
inGain->setLabel( tr( "GAIN" ) );
|
||||
inGain->setHintText( tr( "Input gain:" ) , " dBFS" );
|
||||
|
||||
Knob * inNoise = new Knob( knobBright_26, this );
|
||||
|
||||
auto inNoise = new Knob(knobBright_26, this);
|
||||
inNoise->move( 14, 76 );
|
||||
inNoise->setModel( & controls->m_inNoise );
|
||||
inNoise->setLabel( tr( "NOISE" ) );
|
||||
@@ -65,39 +67,40 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) :
|
||||
|
||||
|
||||
// output knobs
|
||||
Knob * outGain = new Knob( knobBright_26, this );
|
||||
auto outGain = new Knob(knobBright_26, this);
|
||||
outGain->move( 138, 32 );
|
||||
outGain->setModel( & controls->m_outGain );
|
||||
outGain->setLabel( tr( "GAIN" ) );
|
||||
outGain->setHintText( tr( "Output gain:" ) , " dBFS" );
|
||||
|
||||
Knob * outClip = new Knob( knobBright_26, this );
|
||||
|
||||
auto outClip = new Knob(knobBright_26, this);
|
||||
outClip->move( 138, 76 );
|
||||
outClip->setModel( & controls->m_outClip );
|
||||
outClip->setLabel( tr( "CLIP" ) );
|
||||
outClip->setHintText( tr( "Output clip:" ) , "%" );
|
||||
outClip->setHintText( tr( "Output clip:" ) , " dBFS");
|
||||
|
||||
|
||||
|
||||
// leds
|
||||
LedCheckBox * rateEnabled = new LedCheckBox( "", this, tr( "Rate enabled" ), LedCheckBox::Green );
|
||||
auto rateEnabled = new LedCheckBox("", this, tr("Rate enabled"), LedCheckBox::Green);
|
||||
rateEnabled->move( 64, 14 );
|
||||
rateEnabled->setModel( & controls->m_rateEnabled );
|
||||
ToolTip::add( rateEnabled, tr( "Enable sample-rate crushing" ) );
|
||||
|
||||
LedCheckBox * depthEnabled = new LedCheckBox( "", this, tr( "Depth enabled" ), LedCheckBox::Green );
|
||||
rateEnabled->setToolTip(tr("Enable sample-rate crushing"));
|
||||
|
||||
auto depthEnabled = new LedCheckBox("", this, tr("Depth enabled"), LedCheckBox::Green);
|
||||
depthEnabled->move( 101, 14 );
|
||||
depthEnabled->setModel( & controls->m_depthEnabled );
|
||||
ToolTip::add( depthEnabled, tr( "Enable bit-depth crushing" ) );
|
||||
depthEnabled->setToolTip(tr("Enable bit-depth crushing"));
|
||||
|
||||
|
||||
// rate crushing knobs
|
||||
Knob * rate = new Knob( knobBright_26, this );
|
||||
auto rate = new Knob(knobBright_26, this);
|
||||
rate->move( 59, 32 );
|
||||
rate->setModel( & controls->m_rate );
|
||||
rate->setLabel( tr( "FREQ" ) );
|
||||
rate->setHintText( tr( "Sample rate:" ) , " Hz" );
|
||||
|
||||
Knob * stereoDiff = new Knob( knobBright_26, this );
|
||||
|
||||
auto stereoDiff = new Knob(knobBright_26, this);
|
||||
stereoDiff->move( 72, 76 );
|
||||
stereoDiff->setModel( & controls->m_stereoDiff );
|
||||
stereoDiff->setLabel( tr( "STEREO" ) );
|
||||
@@ -105,9 +108,12 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) :
|
||||
|
||||
|
||||
// depth crushing knob
|
||||
Knob * levels = new Knob( knobBright_26, this );
|
||||
auto levels = new Knob(knobBright_26, this);
|
||||
levels->move( 92, 32 );
|
||||
levels->setModel( & controls->m_levels );
|
||||
levels->setLabel( tr( "QUANT" ) );
|
||||
levels->setHintText( tr( "Levels:" ) , "" );
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
@@ -29,16 +29,26 @@
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class BitcrushControls;
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class BitcrushControlDialog : public EffectControlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BitcrushControlDialog( BitcrushControls * controls );
|
||||
virtual ~BitcrushControlDialog()
|
||||
{
|
||||
}
|
||||
~BitcrushControlDialog() override = default;
|
||||
};
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,7 +28,10 @@
|
||||
|
||||
#include "BitcrushControls.h"
|
||||
#include "Bitcrush.h"
|
||||
#include "lmms_math.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
|
||||
BitcrushControls::BitcrushControls( BitcrushEffect * eff ) :
|
||||
@@ -39,20 +42,17 @@ BitcrushControls::BitcrushControls( BitcrushEffect * eff ) :
|
||||
m_outGain( 0.0f, -20.0f, 20.0f, 0.1f, this, tr( "Output gain" ) ),
|
||||
m_outClip( 0.0f, -20.0f, 20.0f, 0.1f, this, tr( "Output clip" ) ),
|
||||
m_rate( 44100.f, 20.f, 44100.f, 1.0f, this, tr( "Sample rate" ) ),
|
||||
m_stereoDiff( 0.f, 0.f, 50.f, 0.1f, this, tr( "Stereo difference" ) ),
|
||||
m_levels( 256.f, 1.f, 256.f, 1.0f, this, tr( "Levels" ) ),
|
||||
m_stereoDiff( 0.f, -50.f, 50.f, 0.1f, this, tr( "Stereo difference" ) ),
|
||||
m_levels( 256.f, 1.f, 256.f, 0.01f, this, tr( "Levels" ) ),
|
||||
m_rateEnabled( true, this, tr( "Rate enabled" ) ),
|
||||
m_depthEnabled( true, this, tr( "Depth enabled" ) )
|
||||
{
|
||||
m_rate.setStrictStepSize( true );
|
||||
m_levels.setStrictStepSize( true );
|
||||
|
||||
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) );
|
||||
connect( Engine::audioEngine(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) );
|
||||
}
|
||||
|
||||
BitcrushControls::~BitcrushControls()
|
||||
{
|
||||
}
|
||||
|
||||
void BitcrushControls::saveSettings( QDomDocument & doc, QDomElement & elem )
|
||||
{
|
||||
@@ -87,3 +87,6 @@ void BitcrushControls::sampleRateChanged()
|
||||
{
|
||||
m_effect->sampleRateChanged();
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
@@ -29,6 +29,10 @@
|
||||
#include "EffectControls.h"
|
||||
#include "BitcrushControlDialog.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
class BitcrushEffect;
|
||||
|
||||
class BitcrushControls : public EffectControls
|
||||
@@ -36,23 +40,23 @@ class BitcrushControls : public EffectControls
|
||||
Q_OBJECT
|
||||
public:
|
||||
BitcrushControls( BitcrushEffect * eff );
|
||||
virtual ~BitcrushControls();
|
||||
~BitcrushControls() override = default;
|
||||
|
||||
virtual void saveSettings( QDomDocument & doc, QDomElement & elem );
|
||||
virtual void loadSettings( const QDomElement & elem );
|
||||
inline virtual QString nodeName() const
|
||||
void saveSettings( QDomDocument & doc, QDomElement & elem ) override;
|
||||
void loadSettings( const QDomElement & elem ) override;
|
||||
inline QString nodeName() const override
|
||||
{
|
||||
return( "bitcrushcontrols" );
|
||||
}
|
||||
|
||||
virtual int controlCount()
|
||||
int controlCount() override
|
||||
{
|
||||
return( 9 );
|
||||
}
|
||||
|
||||
virtual EffectControlDialog * createView()
|
||||
gui::EffectControlDialog * createView() override
|
||||
{
|
||||
return( new BitcrushControlDialog( this ) );
|
||||
return( new gui::BitcrushControlDialog( this ) );
|
||||
}
|
||||
|
||||
private slots:
|
||||
@@ -75,8 +79,11 @@ private:
|
||||
BoolModel m_rateEnabled;
|
||||
BoolModel m_depthEnabled;
|
||||
|
||||
friend class BitcrushControlDialog;
|
||||
friend class gui::BitcrushControlDialog;
|
||||
friend class BitcrushEffect;
|
||||
};
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,8 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
SET(CMAKE_DEBUG_POSTFIX "")
|
||||
|
||||
# Enable C++11
|
||||
SET(CMAKE_CXX_STANDARD 11)
|
||||
# Enable C++17
|
||||
SET(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
IF(LMMS_BUILD_APPLE)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
@@ -14,89 +14,7 @@ INCLUDE_DIRECTORIES(
|
||||
"${CMAKE_BINARY_DIR}/src"
|
||||
)
|
||||
|
||||
SET(PLUGIN_LIST "" CACHE STRING "List of plug-ins to build")
|
||||
|
||||
STRING(REPLACE " " ";" PLUGIN_LIST "${PLUGIN_LIST}")
|
||||
|
||||
OPTION(LMMS_MINIMAL "Build a minimal list of plug-ins" OFF)
|
||||
|
||||
SET(MINIMAL_LIST
|
||||
audio_file_processor
|
||||
kicker
|
||||
triple_oscillator
|
||||
)
|
||||
|
||||
IF(LMMS_MINIMAL)
|
||||
IF("${PLUGIN_LIST}" STREQUAL "")
|
||||
STRING(REPLACE ";" " " MINIMAL_LIST_STRING "${MINIMAL_LIST}")
|
||||
MESSAGE(
|
||||
"-- Using minimal plug-ins: ${MINIMAL_LIST_STRING}\n"
|
||||
" Note: You can specify specific plug-ins using -DPLUGIN_LIST=\"foo bar\""
|
||||
)
|
||||
ENDIF()
|
||||
SET(PLUGIN_LIST ${MINIMAL_LIST} ${PLUGIN_LIST})
|
||||
ENDIF()
|
||||
|
||||
IF("${PLUGIN_LIST}" STREQUAL "")
|
||||
SET(PLUGIN_LIST
|
||||
${MINIMAL_LIST}
|
||||
Amplifier
|
||||
BassBooster
|
||||
bit_invader
|
||||
Bitcrush
|
||||
carlabase
|
||||
carlapatchbay
|
||||
carlarack
|
||||
CrossoverEQ
|
||||
Delay
|
||||
DualFilter
|
||||
dynamics_processor
|
||||
Eq
|
||||
Flanger
|
||||
HydrogenImport
|
||||
ladspa_browser
|
||||
LadspaEffect
|
||||
lb302
|
||||
MidiImport
|
||||
MidiExport
|
||||
MultitapEcho
|
||||
monstro
|
||||
nes
|
||||
OpulenZ
|
||||
organic
|
||||
FreeBoy
|
||||
patman
|
||||
peak_controller_effect
|
||||
GigPlayer
|
||||
ReverbSC
|
||||
sf2_player
|
||||
sfxr
|
||||
sid
|
||||
SpectrumAnalyzer
|
||||
stereo_enhancer
|
||||
stereo_matrix
|
||||
stk
|
||||
vst_base
|
||||
vestige
|
||||
VstEffect
|
||||
watsyn
|
||||
waveshaper
|
||||
vibed
|
||||
Xpressive
|
||||
zynaddsubfx
|
||||
)
|
||||
|
||||
ENDIF("${PLUGIN_LIST}" STREQUAL "")
|
||||
|
||||
IF(MSVC)
|
||||
SET(MSVC_INCOMPATIBLE_PLUGINS
|
||||
LadspaEffect
|
||||
zynaddsubfx
|
||||
)
|
||||
message(WARNING "Compiling with MSVC. The following plugins are not available: ${MSVC_INCOMPATIBLE_PLUGINS}")
|
||||
LIST(REMOVE_ITEM PLUGIN_LIST ${MSVC_INCOMPATIBLE_PLUGINS})
|
||||
ENDIF()
|
||||
|
||||
# See cmake/modules/PluginList.cmake
|
||||
FOREACH(PLUGIN ${PLUGIN_LIST})
|
||||
ADD_SUBDIRECTORY(${PLUGIN})
|
||||
ENDFOREACH()
|
||||
|
||||
59
plugins/CarlaBase/CMakeLists.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
# For MacOS, use "OLD" RPATH install_name behavior
|
||||
# This can be changed to "NEW" safely if install_apple.sh.in
|
||||
# is updated to relink libcarlabase.dylib. MacOS 10.8 uses
|
||||
# cmake 3.9.6, so this can be done at any time.
|
||||
IF(NOT CMAKE_VERSION VERSION_LESS 3.9)
|
||||
CMAKE_POLICY(SET CMP0068 OLD)
|
||||
ENDIF()
|
||||
|
||||
# If Carla was not provided by the system, make a dummy library instead
|
||||
if(LMMS_HAVE_WEAKCARLA)
|
||||
SET(CARLA_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source/includes
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source/utils
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/carla/source/backend
|
||||
)
|
||||
|
||||
IF(LMMS_BUILD_WIN32)
|
||||
# use carla.dll
|
||||
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
SET(CARLA_NATIVE_LIB carla)
|
||||
ELSE()
|
||||
# use libcarla_native-plugin
|
||||
SET(CARLA_NATIVE_LIB carla_native-plugin)
|
||||
ENDIF()
|
||||
ADD_LIBRARY(${CARLA_NATIVE_LIB} SHARED DummyCarla.cpp)
|
||||
TARGET_INCLUDE_DIRECTORIES(${CARLA_NATIVE_LIB} PUBLIC ${CARLA_INCLUDE_DIRS})
|
||||
INSTALL(TARGETS ${CARLA_NATIVE_LIB}
|
||||
LIBRARY DESTINATION "${PLUGIN_DIR}/optional"
|
||||
RUNTIME DESTINATION "${PLUGIN_DIR}/optional"
|
||||
)
|
||||
SET(CARLA_LIBRARIES ${CARLA_NATIVE_LIB})
|
||||
# Set parent scope variables so carlarack and carlapatchbay can see them
|
||||
SET(CARLA_LIBRARIES ${CARLA_LIBRARIES} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA)
|
||||
# Mimic the missing "config.h"
|
||||
SET(CARLA_INCLUDE_DIRS ${CARLA_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/CarlaConfig)
|
||||
SET(CARLA_INCLUDE_DIRS ${CARLA_INCLUDE_DIRS} PARENT_SCOPE)
|
||||
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS})
|
||||
LINK_DIRECTORIES(${CARLA_LIBRARY_DIRS})
|
||||
LINK_LIBRARIES(${CARLA_LIBRARIES})
|
||||
BUILD_PLUGIN(carlabase Carla.cpp Carla.h
|
||||
MOCFILES Carla.h
|
||||
EMBEDDED_RESOURCES artwork-patchbay.png artwork-rack.png
|
||||
EXPORT_BASE_NAME carlabase
|
||||
LINK SHARED)
|
||||
SET_TARGET_PROPERTIES(carlabase
|
||||
PROPERTIES SKIP_BUILD_RPATH TRUE
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
INSTALL_RPATH_USE_LINK_PATH TRUE
|
||||
INSTALL_RPATH "${CARLA_RPATH}")
|
||||
IF(LMMS_HAVE_WEAKCARLA)
|
||||
ADD_DEPENDENCIES(carlabase ${CARLA_NATIVE_LIB})
|
||||
ENDIF()
|
||||
endif()
|
||||
1147
plugins/CarlaBase/Carla.cpp
Normal file
384
plugins/CarlaBase/Carla.h
Normal file
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* carla.h - Carla for LMMS
|
||||
*
|
||||
* Copyright (C) 2014-2018 Filipe Coelho <falktx@falktx.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CARLA_H
|
||||
#define CARLA_H
|
||||
|
||||
#define CARLA_SETTING_PREFIX "PARAM_KNOB_"
|
||||
#define CARLA_MIN_PARAM_VERSION 0x020090
|
||||
#define CARLA_VERSION_HEX_3 0x30000
|
||||
|
||||
// qt
|
||||
#include <QCloseEvent>
|
||||
#include <QDomElement>
|
||||
#include <QMutex>
|
||||
|
||||
// carla/source/includes
|
||||
#include "carlabase_export.h"
|
||||
#include "CarlaDefines.h"
|
||||
#if CARLA_VERSION_HEX >= 0x010911
|
||||
#include "CarlaNativePlugin.h"
|
||||
#else
|
||||
#include "CarlaBackend.h"
|
||||
#include "CarlaNative.h"
|
||||
#include "CarlaUtils.h"
|
||||
CARLA_EXPORT
|
||||
const NativePluginDescriptor* carla_get_native_patchbay_plugin();
|
||||
|
||||
CARLA_EXPORT
|
||||
const NativePluginDescriptor* carla_get_native_rack_plugin();
|
||||
#endif
|
||||
|
||||
// lmms/include/
|
||||
#include "AutomatableModel.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
#include "SubWindow.h"
|
||||
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
class QCompleter;
|
||||
class QGridLayout;
|
||||
class QHBoxLayout;
|
||||
class QLineEdit;
|
||||
class QStringListModel;
|
||||
class QScrollArea;
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class CarlaParamsView;
|
||||
class CarlaInstrumentView;
|
||||
class Knob;
|
||||
}
|
||||
|
||||
class CarlaParamFloatModel : public FloatModel
|
||||
{
|
||||
public:
|
||||
CarlaParamFloatModel(Model * parent):
|
||||
FloatModel(0.0, 0.0, 1.0, 0.001, parent, "Unused"),
|
||||
m_isOutput(false),
|
||||
m_isEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
// From AutomatableModel.h, it's private there.
|
||||
inline static bool mustQuoteName(const QString &name)
|
||||
{
|
||||
QRegExp reg("^[A-Za-z0-9._-]+$");
|
||||
return !reg.exactMatch(name);
|
||||
}
|
||||
|
||||
inline void loadSettings(const QDomElement& element, const QString& name = QString("value")) override
|
||||
{
|
||||
AutomatableModel::loadSettings(element, name);
|
||||
bool mustQuote = mustQuoteName(name);
|
||||
QDomElement me = element.firstChildElement(mustQuote ? QString("automatablemodel") : name);
|
||||
if (!me.isNull()) {
|
||||
m_isOutput = (bool)me.attribute("output", "0").toInt();
|
||||
m_groupName = QString(me.attribute("groupName", ""));
|
||||
}
|
||||
}
|
||||
|
||||
inline void saveSettings(QDomDocument& doc, QDomElement& element,
|
||||
const QString& name = QString( "value" )) override
|
||||
{
|
||||
if (m_isEnabled)
|
||||
{
|
||||
AutomatableModel::saveSettings(doc, element, name);
|
||||
bool mustQuote = mustQuoteName(name);
|
||||
QDomElement me = element.firstChildElement(mustQuote ? QString("automatablemodel") : name);
|
||||
if (!me.isNull())
|
||||
{
|
||||
me.setAttribute("output", m_isOutput);
|
||||
me.setAttribute("groupName", m_groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline const bool enabled()
|
||||
{
|
||||
return m_isEnabled;
|
||||
}
|
||||
|
||||
inline const bool isOutput()
|
||||
{
|
||||
return m_isOutput;
|
||||
}
|
||||
|
||||
inline void setOutput(bool state = true)
|
||||
{
|
||||
m_isOutput = state;
|
||||
}
|
||||
|
||||
inline void setEnabled(bool state = true)
|
||||
{
|
||||
m_isEnabled = state;
|
||||
}
|
||||
|
||||
inline void setGroupName(QString groupName)
|
||||
{
|
||||
m_groupName = groupName;
|
||||
}
|
||||
|
||||
inline void setGroupId(uint8_t groupId)
|
||||
{
|
||||
m_groupId = groupId;
|
||||
}
|
||||
|
||||
virtual QString groupName() const
|
||||
{
|
||||
return m_groupName;
|
||||
}
|
||||
|
||||
virtual uint8_t groupId() const
|
||||
{
|
||||
return m_groupId;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isOutput;
|
||||
bool m_isEnabled;
|
||||
uint8_t m_groupId;
|
||||
QString m_groupName;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class CARLABASE_EXPORT CarlaInstrument : public Instrument
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const uint32_t kMaxMidiEvents = 512;
|
||||
|
||||
CarlaInstrument(InstrumentTrack* const instrumentTrack, const Descriptor* const descriptor, const bool isPatchbay);
|
||||
~CarlaInstrument() override;
|
||||
|
||||
// Carla NativeHostDescriptor functions
|
||||
uint32_t handleGetBufferSize() const;
|
||||
double handleGetSampleRate() const;
|
||||
bool handleIsOffline() const;
|
||||
const NativeTimeInfo* handleGetTimeInfo() const;
|
||||
void handleUiParameterChanged(const uint32_t index, const float value) const;
|
||||
void handleUiClosed();
|
||||
intptr_t handleDispatcher(const NativeHostDispatcherOpcode opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt);
|
||||
|
||||
// LMMS functions
|
||||
Flags flags() const override;
|
||||
QString nodeName() const override;
|
||||
void saveSettings(QDomDocument& doc, QDomElement& parent) override;
|
||||
void loadSettings(const QDomElement& elem) override;
|
||||
void play(sampleFrame* workingBuffer) override;
|
||||
bool handleMidiEvent(const MidiEvent& event, const TimePos& time, f_cnt_t offset) override;
|
||||
gui::PluginView* instantiateView(QWidget* parent) override;
|
||||
|
||||
signals:
|
||||
void uiClosed();
|
||||
void paramsUpdated();
|
||||
|
||||
private slots:
|
||||
void sampleRateChanged();
|
||||
void refreshParams(bool init = false);
|
||||
void clearParamModels();
|
||||
void paramModelChanged(uint32_t index);
|
||||
void updateParamModel(uint32_t index);
|
||||
|
||||
private:
|
||||
const bool kIsPatchbay;
|
||||
|
||||
NativePluginHandle fHandle;
|
||||
NativeHostDescriptor fHost;
|
||||
const NativePluginDescriptor* fDescriptor;
|
||||
|
||||
uint32_t fMidiEventCount;
|
||||
NativeMidiEvent fMidiEvents[kMaxMidiEvents];
|
||||
NativeTimeInfo fTimeInfo;
|
||||
|
||||
// this is only needed because note-offs are being sent during play
|
||||
QMutex fMutex;
|
||||
|
||||
uint8_t m_paramGroupCount;
|
||||
QList<CarlaParamFloatModel*> m_paramModels;
|
||||
QDomElement m_settingsElem;
|
||||
|
||||
QCompleter* m_paramsCompleter;
|
||||
QStringListModel* m_completerModel;
|
||||
|
||||
friend class gui::CarlaInstrumentView;
|
||||
friend class gui::CarlaParamsView;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class CarlaParamsSubWindow : public SubWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void uiClosed();
|
||||
void resized();
|
||||
|
||||
public:
|
||||
CarlaParamsSubWindow(QWidget * _parent, Qt::WindowFlags windowFlags) :
|
||||
SubWindow(_parent)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
setWindowFlags(windowFlags);
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent * event) override
|
||||
{
|
||||
if (mousePress) {
|
||||
resizing = true;
|
||||
}
|
||||
SubWindow::resizeEvent(event);
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent * event) override
|
||||
{
|
||||
mousePress = true;
|
||||
SubWindow::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent * event) override
|
||||
{
|
||||
if (resizing) {
|
||||
resizing = false;
|
||||
mousePress = false;
|
||||
emit resized();
|
||||
}
|
||||
SubWindow::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void closeEvent(QCloseEvent * event) override
|
||||
{
|
||||
emit uiClosed();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
private:
|
||||
bool resizing = false;
|
||||
bool mousePress = false;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class CarlaInstrumentView : public InstrumentViewFixedSize
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CarlaInstrumentView(CarlaInstrument* const instrument, QWidget* const parent);
|
||||
~CarlaInstrumentView() override;
|
||||
|
||||
private slots:
|
||||
void toggleUI(bool);
|
||||
void uiClosed();
|
||||
void toggleParamsWindow();
|
||||
void paramsUiClosed();
|
||||
|
||||
private:
|
||||
void modelChanged() override;
|
||||
void timerEvent(QTimerEvent*) override;
|
||||
|
||||
NativePluginHandle fHandle;
|
||||
const NativePluginDescriptor* fDescriptor;
|
||||
int fTimerId;
|
||||
|
||||
CarlaInstrument* const m_carlaInstrument;
|
||||
QWidget* const m_parent;
|
||||
|
||||
QMdiSubWindow* m_paramsSubWindow;
|
||||
CarlaParamsView* m_paramsView;
|
||||
|
||||
QPushButton* m_toggleUIButton;
|
||||
QPushButton* m_toggleParamsWindowButton;
|
||||
|
||||
friend class CarlaParamsView;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class CarlaParamsView : public InstrumentView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CarlaParamsView(CarlaInstrumentView* const instrumentView, QWidget* const parent);
|
||||
~CarlaParamsView() override;
|
||||
|
||||
signals:
|
||||
void uiClosed();
|
||||
|
||||
private slots:
|
||||
void refreshKnobs();
|
||||
void filterKnobs();
|
||||
void clearFilterText();
|
||||
void windowResized();
|
||||
|
||||
private:
|
||||
void adjustWindowWidth();
|
||||
void addKnob(uint32_t index);
|
||||
void clearKnobs();
|
||||
|
||||
CarlaInstrument* const m_carlaInstrument;
|
||||
CarlaInstrumentView* const m_carlaInstrumentView;
|
||||
QList<Knob*> m_knobs;
|
||||
|
||||
// Keep track of the biggest knob width per group
|
||||
QList<uint16_t> m_maxKnobWidthPerGroup;
|
||||
|
||||
uint32_t m_maxColumns;
|
||||
uint32_t m_curColumn;
|
||||
uint32_t m_curRow;
|
||||
uint32_t m_curOutColumn;
|
||||
uint32_t m_curOutRow;
|
||||
|
||||
QScrollArea* m_inputScrollArea;
|
||||
QGridLayout* m_inputScrollAreaLayout;
|
||||
QWidget* m_inputScrollAreaWidgetContent;
|
||||
QScrollArea* m_outputScrollArea;
|
||||
QGridLayout* m_outputScrollAreaLayout;
|
||||
QWidget* m_outputScrollAreaWidgetContent;
|
||||
QHBoxLayout* m_toolBarLayout;
|
||||
QLineEdit* m_paramsFilterLineEdit;
|
||||
QPushButton* m_clearFilterButton;
|
||||
QPushButton* m_automatedOnlyButton;
|
||||
QComboBox* m_groupFilterCombo;
|
||||
QStringListModel* m_groupFilterModel;
|
||||
};
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
9
plugins/CarlaBase/CarlaConfig/config.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// config.h for Carla
|
||||
#ifndef FOR_CARLA_CONFIG_H
|
||||
#define FOR_CARLA_CONFIG_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define HAVE_CPP11_SUPPORT 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
13
plugins/CarlaBase/DummyCarla.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
// A dummy Carla interface
|
||||
#define BUILDING_CARLA
|
||||
#include "CarlaNativePlugin.h"
|
||||
|
||||
CARLA_EXPORT const char* carla_get_library_filename() { return nullptr; }
|
||||
CARLA_EXPORT const char* carla_get_library_folder() { return nullptr; }
|
||||
CARLA_EXPORT const NativePluginDescriptor* carla_get_native_rack_plugin() { return nullptr; }
|
||||
CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_plugin() { return nullptr; }
|
||||
CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay16_plugin() { return nullptr; }
|
||||
CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay32_plugin() { return nullptr; }
|
||||
CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay64_plugin() { return nullptr; }
|
||||
CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_cv_plugin() { return nullptr; }
|
||||
CARLA_EXPORT CarlaBackend::CarlaEngine* carla_get_native_plugin_engine(const NativePluginDescriptor* desc, NativePluginHandle handle) { return nullptr; }
|
||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
1
plugins/CarlaBase/carla
Submodule
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -1,9 +1,9 @@
|
||||
if(LMMS_HAVE_CARLA)
|
||||
if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA)
|
||||
ADD_DEFINITIONS(-DCARLA_PLUGIN_PATCHBAY -DCARLA_PLUGIN_SYNTH)
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../carlabase")
|
||||
LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/../carlabase"
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../CarlaBase")
|
||||
LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/../CarlaBase"
|
||||
${CARLA_LIBRARY_DIRS})
|
||||
LINK_LIBRARIES(carlabase)
|
||||
BUILD_PLUGIN(carlapatchbay carlapatchbay.cpp EMBEDDED_RESOURCES logo.png)
|
||||
endif(LMMS_HAVE_CARLA)
|
||||
BUILD_PLUGIN(carlapatchbay CarlaPatchbay.cpp EMBEDDED_RESOURCES logo.png)
|
||||
endif()
|
||||
@@ -22,26 +22,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "carla.h"
|
||||
#include "Carla.h"
|
||||
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
#include "InstrumentTrack.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT carlapatchbay_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
LMMS_STRINGIFY( PLUGIN_NAME ),
|
||||
"Carla Patchbay",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser",
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser",
|
||||
"Carla Patchbay Instrument" ),
|
||||
"falkTX <falktx/at/falktx.com>",
|
||||
CARLA_VERSION_HEX,
|
||||
Plugin::Instrument,
|
||||
new PluginPixmapLoader( "logo" ),
|
||||
NULL,
|
||||
NULL
|
||||
nullptr,
|
||||
nullptr,
|
||||
} ;
|
||||
|
||||
PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*)
|
||||
@@ -50,3 +55,6 @@ PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -1,9 +1,9 @@
|
||||
if(LMMS_HAVE_CARLA)
|
||||
if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA)
|
||||
ADD_DEFINITIONS(-DCARLA_PLUGIN_RACK -DCARLA_PLUGIN_SYNTH)
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../carlabase")
|
||||
LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/../carlabase"
|
||||
INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../CarlaBase")
|
||||
LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/../CarlaBase"
|
||||
${CARLA_LIBRARY_DIRS})
|
||||
LINK_LIBRARIES(carlabase)
|
||||
BUILD_PLUGIN(carlarack carlarack.cpp EMBEDDED_RESOURCES logo.png)
|
||||
endif(LMMS_HAVE_CARLA)
|
||||
BUILD_PLUGIN(carlarack CarlaRack.cpp EMBEDDED_RESOURCES logo.png)
|
||||
endif()
|
||||
@@ -22,26 +22,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "carla.h"
|
||||
#include "Carla.h"
|
||||
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
#include "InstrumentTrack.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT carlarack_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
LMMS_STRINGIFY( PLUGIN_NAME ),
|
||||
"Carla Rack",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser",
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser",
|
||||
"Carla Rack Instrument" ),
|
||||
"falkTX <falktx/at/falktx.com>",
|
||||
CARLA_VERSION_HEX,
|
||||
Plugin::Instrument,
|
||||
new PluginPixmapLoader( "logo" ),
|
||||
NULL,
|
||||
NULL
|
||||
nullptr,
|
||||
nullptr,
|
||||
} ;
|
||||
|
||||
PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*)
|
||||
@@ -50,3 +55,6 @@ PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
BIN
plugins/CarlaRack/logo.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
3
plugins/Compressor/CMakeLists.txt
Executable file
@@ -0,0 +1,3 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(compressor Compressor.cpp CompressorControls.cpp CompressorControlDialog.cpp MOCFILES Compressor.h CompressorControls.h CompressorControlDialog.h ../Eq/EqFader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png")
|
||||
665
plugins/Compressor/Compressor.cpp
Executable file
@@ -0,0 +1,665 @@
|
||||
/*
|
||||
* Compressor.cpp
|
||||
*
|
||||
* Copyright (c) 2020 Lost Robot <r94231@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Compressor.h"
|
||||
|
||||
#include "embed.h"
|
||||
#include "interpolation.h"
|
||||
#include "lmms_math.h"
|
||||
#include "plugin_export.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT compressor_plugin_descriptor =
|
||||
{
|
||||
LMMS_STRINGIFY(PLUGIN_NAME),
|
||||
"Compressor",
|
||||
QT_TRANSLATE_NOOP("PluginBrowser", "A dynamic range compressor."),
|
||||
"Lost Robot <r94231@gmail.com>",
|
||||
0x0100,
|
||||
Plugin::Effect,
|
||||
new PluginPixmapLoader("logo"),
|
||||
nullptr,
|
||||
nullptr,
|
||||
} ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
CompressorEffect::CompressorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) :
|
||||
Effect(&compressor_plugin_descriptor, parent, key),
|
||||
m_compressorControls(this)
|
||||
{
|
||||
m_sampleRate = Engine::audioEngine()->processingSampleRate();
|
||||
|
||||
m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR;
|
||||
|
||||
m_maxLookaheadVal[0] = 0;
|
||||
m_maxLookaheadVal[1] = 0;
|
||||
|
||||
// 200 ms
|
||||
m_crestTimeConst = exp(-1.f / (0.2f * m_sampleRate));
|
||||
|
||||
connect(&m_compressorControls.m_attackModel, SIGNAL(dataChanged()), this, SLOT(calcAttack()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_releaseModel, SIGNAL(dataChanged()), this, SLOT(calcRelease()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_holdModel, SIGNAL(dataChanged()), this, SLOT(calcHold()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_ratioModel, SIGNAL(dataChanged()), this, SLOT(calcRatio()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_rangeModel, SIGNAL(dataChanged()), this, SLOT(calcRange()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_rmsModel, SIGNAL(dataChanged()), this, SLOT(resizeRMS()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_lookaheadLengthModel, SIGNAL(dataChanged()), this, SLOT(calcLookaheadLength()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_thresholdModel, SIGNAL(dataChanged()), this, SLOT(calcThreshold()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_kneeModel, SIGNAL(dataChanged()), this, SLOT(calcKnee()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_outGainModel, SIGNAL(dataChanged()), this, SLOT(calcOutGain()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_inGainModel, SIGNAL(dataChanged()), this, SLOT(calcInGain()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_tiltModel, SIGNAL(dataChanged()), this, SLOT(calcTiltCoeffs()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_tiltFreqModel, SIGNAL(dataChanged()), this, SLOT(calcTiltCoeffs()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_limiterModel, SIGNAL(dataChanged()), this, SLOT(redrawKnee()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_mixModel, SIGNAL(dataChanged()), this, SLOT(calcMix()), Qt::DirectConnection);
|
||||
|
||||
connect(&m_compressorControls.m_autoAttackModel, SIGNAL(dataChanged()), this, SLOT(calcAutoAttack()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_autoReleaseModel, SIGNAL(dataChanged()), this, SLOT(calcAutoRelease()), Qt::DirectConnection);
|
||||
|
||||
connect(&m_compressorControls.m_thresholdModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_ratioModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_kneeModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
|
||||
connect(&m_compressorControls.m_autoMakeupModel, SIGNAL(dataChanged()), this, SLOT(calcAutoMakeup()), Qt::DirectConnection);
|
||||
|
||||
connect(Engine::audioEngine(), SIGNAL(sampleRateChanged()), this, SLOT(changeSampleRate()));
|
||||
changeSampleRate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
float CompressorEffect::msToCoeff(float ms)
|
||||
{
|
||||
// Convert time in milliseconds to applicable lowpass coefficient
|
||||
return exp(m_coeffPrecalc / ms);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CompressorEffect::calcAutoMakeup()
|
||||
{
|
||||
// Formulas using the compressor's Threshold, Ratio, and Knee values to estimate a good makeup gain value
|
||||
|
||||
float tempGainResult;
|
||||
if (-m_thresholdVal < m_kneeVal)
|
||||
{
|
||||
const float temp = -m_thresholdVal + m_kneeVal;
|
||||
tempGainResult = ((m_compressorControls.m_limiterModel.value() ? 0 : m_ratioVal) - 1) * temp * temp / (4 * m_kneeVal);
|
||||
}
|
||||
else// Above knee
|
||||
{
|
||||
tempGainResult = m_compressorControls.m_limiterModel.value()
|
||||
? m_thresholdVal
|
||||
: m_thresholdVal - m_thresholdVal * m_ratioVal;
|
||||
}
|
||||
|
||||
m_autoMakeupVal = 1.f / dbfsToAmp(tempGainResult);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CompressorEffect::calcAttack()
|
||||
{
|
||||
m_attCoeff = msToCoeff(m_compressorControls.m_attackModel.value());
|
||||
}
|
||||
|
||||
void CompressorEffect::calcRelease()
|
||||
{
|
||||
m_relCoeff = msToCoeff(m_compressorControls.m_releaseModel.value());
|
||||
}
|
||||
|
||||
void CompressorEffect::calcAutoAttack()
|
||||
{
|
||||
m_autoAttVal = m_compressorControls.m_autoAttackModel.value() * 0.01f;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcAutoRelease()
|
||||
{
|
||||
m_autoRelVal = m_compressorControls.m_autoReleaseModel.value() * 0.01f;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcHold()
|
||||
{
|
||||
m_holdLength = m_compressorControls.m_holdModel.value() * 0.001f * m_sampleRate;
|
||||
m_holdTimer[0] = 0;
|
||||
m_holdTimer[1] = 0;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcOutGain()
|
||||
{
|
||||
// 0.999 is needed to keep the values from crossing the threshold all the time
|
||||
// (most commonly for limiters specifically), and is kept across all modes for consistency.
|
||||
m_outGainVal = dbfsToAmp(m_compressorControls.m_outGainModel.value()) * 0.999;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcRatio()
|
||||
{
|
||||
m_ratioVal = 1.f / m_compressorControls.m_ratioModel.value();
|
||||
m_redrawKnee = true;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcRange()
|
||||
{
|
||||
// Range is inactive when turned all the way down
|
||||
m_rangeVal = (m_compressorControls.m_rangeModel.value() > m_compressorControls.m_rangeModel.minValue())
|
||||
? dbfsToAmp(m_compressorControls.m_rangeModel.value())
|
||||
: 0;
|
||||
}
|
||||
|
||||
void CompressorEffect::resizeRMS()
|
||||
{
|
||||
m_rmsTimeConst = exp(-1.f / (m_compressorControls.m_rmsModel.value() * 0.001f * m_sampleRate));
|
||||
}
|
||||
|
||||
void CompressorEffect::calcLookaheadLength()
|
||||
{
|
||||
m_lookaheadLength = qMax(m_compressorControls.m_lookaheadLengthModel.value() * 0.001f * m_sampleRate, 1.f);
|
||||
|
||||
m_preLookaheadLength = ceil(m_lookaheadDelayLength - m_lookaheadLength);
|
||||
}
|
||||
|
||||
void CompressorEffect::calcThreshold()
|
||||
{
|
||||
m_thresholdVal = m_compressorControls.m_thresholdModel.value();
|
||||
m_thresholdAmpVal = dbfsToAmp(m_thresholdVal);
|
||||
m_redrawKnee = true;
|
||||
m_redrawThreshold = true;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcKnee()
|
||||
{
|
||||
m_kneeVal = m_compressorControls.m_kneeModel.value() * 0.5f;
|
||||
m_redrawKnee = true;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcInGain()
|
||||
{
|
||||
m_inGainVal = dbfsToAmp(m_compressorControls.m_inGainModel.value());
|
||||
}
|
||||
|
||||
void CompressorEffect::redrawKnee()
|
||||
{
|
||||
m_redrawKnee = true;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcTiltCoeffs()
|
||||
{
|
||||
m_tiltVal = m_compressorControls.m_tiltModel.value();
|
||||
|
||||
const float amp = 6 / log(2);
|
||||
|
||||
const float gfactor = 5;
|
||||
const float g1 = m_tiltVal > 0 ? -gfactor * m_tiltVal : -m_tiltVal;
|
||||
const float g2 = m_tiltVal > 0 ? m_tiltVal : gfactor * m_tiltVal;
|
||||
|
||||
m_lgain = exp(g1 / amp) - 1;
|
||||
m_hgain = exp(g2 / amp) - 1;
|
||||
|
||||
const float omega = 2 * F_PI * m_compressorControls.m_tiltFreqModel.value();
|
||||
const float n = 1 / (m_sampleRate * 3 + omega);
|
||||
m_a0 = 2 * omega * n;
|
||||
m_b1 = (m_sampleRate * 3 - omega) * n;
|
||||
}
|
||||
|
||||
void CompressorEffect::calcMix()
|
||||
{
|
||||
m_mixVal = m_compressorControls.m_mixModel.value() * 0.01;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
|
||||
{
|
||||
if (!isEnabled() || !isRunning())
|
||||
{
|
||||
// Clear lookahead buffers and other values when needed
|
||||
if (!m_cleanedBuffers)
|
||||
{
|
||||
m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR;
|
||||
m_gainResult[0] = m_gainResult[1] = 1;
|
||||
m_displayPeak[0] = m_displayPeak[1] = COMP_NOISE_FLOOR;
|
||||
m_displayGain[0] = m_displayGain[1] = COMP_NOISE_FLOOR;
|
||||
std::fill(std::begin(m_lookaheadBuf[0]), std::end(m_lookaheadBuf[0]), 0);
|
||||
std::fill(std::begin(m_lookaheadBuf[1]), std::end(m_lookaheadBuf[1]), 0);
|
||||
m_lookaheadBufLoc[0] = 0;
|
||||
m_lookaheadBufLoc[1] = 0;
|
||||
std::fill(std::begin(m_preLookaheadBuf[0]), std::end(m_preLookaheadBuf[0]), 0);
|
||||
std::fill(std::begin(m_preLookaheadBuf[1]), std::end(m_preLookaheadBuf[1]), 0);
|
||||
m_preLookaheadBufLoc[0] = 0;
|
||||
m_preLookaheadBufLoc[1] = 0;
|
||||
std::fill(std::begin(m_inputBuf[0]), std::end(m_inputBuf[0]), 0);
|
||||
std::fill(std::begin(m_inputBuf[1]), std::end(m_inputBuf[1]), 0);
|
||||
m_inputBufLoc = 0;
|
||||
m_cleanedBuffers = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cleanedBuffers = false;
|
||||
}
|
||||
|
||||
float outSum = 0.0;
|
||||
const float d = dryLevel();
|
||||
const float w = wetLevel();
|
||||
|
||||
float lOutPeak = 0.0;
|
||||
float rOutPeak = 0.0;
|
||||
float lInPeak = 0.0;
|
||||
float rInPeak = 0.0;
|
||||
|
||||
const bool midside = m_compressorControls.m_midsideModel.value();
|
||||
const bool peakmode = m_compressorControls.m_peakmodeModel.value();
|
||||
const float inBalance = m_compressorControls.m_inBalanceModel.value();
|
||||
const float outBalance = m_compressorControls.m_outBalanceModel.value();
|
||||
const bool limiter = m_compressorControls.m_limiterModel.value();
|
||||
const float blend = m_compressorControls.m_blendModel.value();
|
||||
const float stereoBalance = m_compressorControls.m_stereoBalanceModel.value();
|
||||
const bool autoMakeup = m_compressorControls.m_autoMakeupModel.value();
|
||||
const int stereoLink = m_compressorControls.m_stereoLinkModel.value();
|
||||
const bool audition = m_compressorControls.m_auditionModel.value();
|
||||
const bool feedback = m_compressorControls.m_feedbackModel.value();
|
||||
const bool lookahead = m_compressorControls.m_lookaheadModel.value();
|
||||
|
||||
for(fpp_t f = 0; f < frames; ++f)
|
||||
{
|
||||
sample_t drySignal[2] = {buf[f][0], buf[f][1]};
|
||||
sample_t s[2] = {drySignal[0] * m_inGainVal, drySignal[1] * m_inGainVal};
|
||||
|
||||
// Calculate tilt filters, to bias the sidechain to the low or high frequencies
|
||||
if (m_tiltVal)
|
||||
{
|
||||
calcTiltFilter(s[0], s[0], 0);
|
||||
calcTiltFilter(s[1], s[1], 1);
|
||||
}
|
||||
|
||||
if (midside)// Convert left/right to mid/side
|
||||
{
|
||||
const float temp = s[0];
|
||||
s[0] = (temp + s[1]) * 0.5;
|
||||
s[1] = temp - s[1];
|
||||
}
|
||||
|
||||
s[0] *= inBalance > 0 ? 1 - inBalance : 1;
|
||||
s[1] *= inBalance < 0 ? 1 + inBalance : 1;
|
||||
|
||||
m_gainResult[0] = 0;
|
||||
m_gainResult[1] = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
float inputValue = feedback ? m_prevOut[i] : s[i];
|
||||
|
||||
// Calculate the crest factor of the audio by diving the peak by the RMS
|
||||
m_crestPeakVal[i] = qMax(inputValue * inputValue, m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inputValue * inputValue));
|
||||
m_crestRmsVal[i] = m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inputValue * inputValue));
|
||||
m_crestFactorVal[i] = m_crestPeakVal[i] / m_crestRmsVal[i];
|
||||
|
||||
m_rmsVal[i] = m_rmsTimeConst * m_rmsVal[i] + ((1 - m_rmsTimeConst) * (inputValue * inputValue));
|
||||
|
||||
// Grab the peak or RMS value
|
||||
inputValue = qMax(COMP_NOISE_FLOOR, peakmode ? std::abs(inputValue) : std::sqrt(m_rmsVal[i]));
|
||||
|
||||
// The following code uses math magic to semi-efficiently
|
||||
// find the largest value in the lookahead buffer.
|
||||
// This can probably be improved.
|
||||
if (lookahead)
|
||||
{
|
||||
// Pre-lookahead delay, so the total delay always matches 20 ms
|
||||
++m_preLookaheadBufLoc[i];
|
||||
if (m_preLookaheadBufLoc[i] >= m_preLookaheadLength)
|
||||
{
|
||||
m_preLookaheadBufLoc[i] = 0;
|
||||
}
|
||||
const float tempInputValue = inputValue;
|
||||
inputValue = m_preLookaheadBuf[i][m_preLookaheadBufLoc[i]];
|
||||
m_preLookaheadBuf[i][m_preLookaheadBufLoc[i]] = tempInputValue;
|
||||
|
||||
|
||||
// Increment ring buffer location
|
||||
++m_lookaheadBufLoc[i];
|
||||
if (m_lookaheadBufLoc[i] >= m_lookaheadLength)
|
||||
{
|
||||
m_lookaheadBufLoc[i] = 0;
|
||||
}
|
||||
|
||||
m_lookaheadBuf[i][m_lookaheadBufLoc[i]] = inputValue;
|
||||
|
||||
// If the new input value is larger than the stored maximum,
|
||||
// store that as the maximum
|
||||
if (inputValue >= m_maxLookaheadVal[i])
|
||||
{
|
||||
m_maxLookaheadVal[i] = inputValue;
|
||||
m_maxLookaheadTimer[i] = m_lookaheadLength;
|
||||
}
|
||||
|
||||
// Decrement timer. When the timer reaches 0, that means the
|
||||
// stored maximum value has left the buffer and a new
|
||||
// maximum value must be found.
|
||||
if (--m_maxLookaheadTimer[i] <= 0)
|
||||
{
|
||||
m_maxLookaheadTimer[i] = std::distance(std::begin(m_lookaheadBuf[i]),
|
||||
std::max_element(std::begin(m_lookaheadBuf[i]), std::begin(m_lookaheadBuf[i]) + m_lookaheadLength));
|
||||
m_maxLookaheadVal[i] = m_lookaheadBuf[i][m_maxLookaheadTimer[i]];
|
||||
m_maxLookaheadTimer[i] = realmod(m_maxLookaheadTimer[i] - m_lookaheadBufLoc[i], m_lookaheadLength);
|
||||
}
|
||||
|
||||
inputValue = m_maxLookaheadVal[i];
|
||||
}
|
||||
|
||||
float t = inputValue;
|
||||
|
||||
if (t > m_yL[i])// Attack phase
|
||||
{
|
||||
// We want the "resting value" of our crest factor to be with a sine wave,
|
||||
// which with this variable has a value of 2.
|
||||
// So, we pull this value down to 0, and multiply it by the percentage of
|
||||
// automatic attack control that is applied. We then add 2 back to it.
|
||||
float crestFactorValTemp = ((m_crestFactorVal[i] - 2.f) * m_autoAttVal) + 2.f;
|
||||
|
||||
// Calculate attack value depending on crest factor
|
||||
const float att = m_autoAttVal
|
||||
? msToCoeff(2.f * m_compressorControls.m_attackModel.value() / (crestFactorValTemp))
|
||||
: m_attCoeff;
|
||||
|
||||
m_yL[i] = m_yL[i] * att + (1 - att) * t;
|
||||
m_holdTimer[i] = m_holdLength;// Reset hold timer
|
||||
}
|
||||
else// Release phase
|
||||
{
|
||||
float crestFactorValTemp = ((m_crestFactorVal[i] - 2.f) * m_autoRelVal) + 2.f;
|
||||
|
||||
const float rel = m_autoRelVal
|
||||
? msToCoeff(2.f * m_compressorControls.m_releaseModel.value() / (crestFactorValTemp))
|
||||
: m_relCoeff;
|
||||
|
||||
if (m_holdTimer[i])// Don't change peak if hold is being applied
|
||||
{
|
||||
--m_holdTimer[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_yL[i] = m_yL[i] * rel + (1 - rel) * t;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep it above the noise floor
|
||||
m_yL[i] = qMax(COMP_NOISE_FLOOR, m_yL[i]);
|
||||
|
||||
// For the visualizer
|
||||
m_displayPeak[i] = qMax(m_yL[i], m_displayPeak[i]);
|
||||
|
||||
const float currentPeakDbfs = ampToDbfs(m_yL[i]);
|
||||
|
||||
// Now find the gain change that should be applied,
|
||||
// depending on the measured input value.
|
||||
if (currentPeakDbfs - m_thresholdVal < -m_kneeVal)// Below knee
|
||||
{
|
||||
m_gainResult[i] = currentPeakDbfs;
|
||||
}
|
||||
else if (currentPeakDbfs - m_thresholdVal < m_kneeVal)// Within knee
|
||||
{
|
||||
const float temp = currentPeakDbfs - m_thresholdVal + m_kneeVal;
|
||||
m_gainResult[i] = currentPeakDbfs + ((limiter ? 0 : m_ratioVal) - 1) * temp * temp / (4 * m_kneeVal);
|
||||
}
|
||||
else// Above knee
|
||||
{
|
||||
m_gainResult[i] = limiter
|
||||
? m_thresholdVal
|
||||
: m_thresholdVal + (currentPeakDbfs - m_thresholdVal) * m_ratioVal;
|
||||
}
|
||||
|
||||
m_gainResult[i] = dbfsToAmp(m_gainResult[i]) / m_yL[i];
|
||||
m_gainResult[i] = qMax(m_rangeVal, m_gainResult[i]);
|
||||
}
|
||||
|
||||
switch (stereoLink)
|
||||
{
|
||||
case Unlinked:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Maximum:
|
||||
{
|
||||
m_gainResult[0] = m_gainResult[1] = qMin(m_gainResult[0], m_gainResult[1]);
|
||||
break;
|
||||
}
|
||||
case Average:
|
||||
{
|
||||
m_gainResult[0] = m_gainResult[1] = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case Minimum:
|
||||
{
|
||||
m_gainResult[0] = m_gainResult[1] = qMax(m_gainResult[0], m_gainResult[1]);
|
||||
break;
|
||||
}
|
||||
case Blend:
|
||||
{
|
||||
if (blend > 0)// 0 is unlinked
|
||||
{
|
||||
if (blend <= 1)// Blend to minimum volume
|
||||
{
|
||||
const float temp1 = qMin(m_gainResult[0], m_gainResult[1]);
|
||||
m_gainResult[0] = linearInterpolate(m_gainResult[0], temp1, blend);
|
||||
m_gainResult[1] = linearInterpolate(m_gainResult[1], temp1, blend);
|
||||
}
|
||||
else if (blend <= 2)// Blend to average volume
|
||||
{
|
||||
const float temp1 = qMin(m_gainResult[0], m_gainResult[1]);
|
||||
const float temp2 = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
|
||||
m_gainResult[0] = linearInterpolate(temp1, temp2, blend - 1);
|
||||
m_gainResult[1] = m_gainResult[0];
|
||||
}
|
||||
else// Blend to maximum volume
|
||||
{
|
||||
const float temp1 = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
|
||||
const float temp2 = qMax(m_gainResult[0], m_gainResult[1]);
|
||||
m_gainResult[0] = linearInterpolate(temp1, temp2, blend - 2);
|
||||
m_gainResult[1] = m_gainResult[0];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Bias compression to the left or right (or mid or side)
|
||||
if (stereoBalance != 0)
|
||||
{
|
||||
m_gainResult[0] = 1 - ((1 - m_gainResult[0]) * (stereoBalance > 0 ? 1 - stereoBalance : 1));
|
||||
m_gainResult[1] = 1 - ((1 - m_gainResult[1]) * (stereoBalance < 0 ? 1 + stereoBalance : 1));
|
||||
}
|
||||
|
||||
// For visualizer
|
||||
m_displayGain[0] = qMax(m_gainResult[0], m_displayGain[0]);
|
||||
m_displayGain[1] = qMax(m_gainResult[1], m_displayGain[1]);
|
||||
|
||||
// Delay the signal by 20 ms via ring buffer if lookahead is enabled
|
||||
if (lookahead)
|
||||
{
|
||||
++m_inputBufLoc;
|
||||
if (m_inputBufLoc >= m_lookaheadDelayLength)
|
||||
{
|
||||
m_inputBufLoc = 0;
|
||||
}
|
||||
|
||||
const float temp[2] = {drySignal[0], drySignal[1]};
|
||||
s[0] = m_inputBuf[0][m_inputBufLoc];
|
||||
s[1] = m_inputBuf[1][m_inputBufLoc];
|
||||
|
||||
m_inputBuf[0][m_inputBufLoc] = temp[0];
|
||||
m_inputBuf[1][m_inputBufLoc] = temp[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
s[0] = drySignal[0];
|
||||
s[1] = drySignal[1];
|
||||
}
|
||||
|
||||
float delayedDrySignal[2] = {s[0], s[1]};
|
||||
|
||||
if (midside)// Convert left/right to mid/side
|
||||
{
|
||||
const float temp = s[0];
|
||||
s[0] = (temp + s[1]) * 0.5;
|
||||
s[1] = temp - s[1];
|
||||
}
|
||||
|
||||
s[0] *= inBalance > 0 ? 1 - inBalance : 1;
|
||||
s[1] *= inBalance < 0 ? 1 + inBalance : 1;
|
||||
|
||||
s[0] *= m_gainResult[0] * m_inGainVal * m_outGainVal * (outBalance > 0 ? 1 - outBalance : 1);
|
||||
s[1] *= m_gainResult[1] * m_inGainVal * m_outGainVal * (outBalance < 0 ? 1 + outBalance : 1);
|
||||
|
||||
if (midside)// Convert mid/side back to left/right
|
||||
{
|
||||
const float temp1 = s[0];
|
||||
const float temp2 = s[1] * 0.5;
|
||||
s[0] = temp1 + temp2;
|
||||
s[1] = temp1 - temp2;
|
||||
}
|
||||
|
||||
m_prevOut[0] = s[0];
|
||||
m_prevOut[1] = s[1];
|
||||
|
||||
// Negate wet signal from dry signal
|
||||
if (audition)
|
||||
{
|
||||
s[0] = (-s[0] + delayedDrySignal[0] * m_outGainVal * m_inGainVal);
|
||||
s[1] = (-s[1] + delayedDrySignal[1] * m_outGainVal * m_inGainVal);
|
||||
}
|
||||
else if (autoMakeup)
|
||||
{
|
||||
s[0] *= m_autoMakeupVal;
|
||||
s[1] *= m_autoMakeupVal;
|
||||
}
|
||||
|
||||
// Calculate wet/dry value results
|
||||
const float temp1 = delayedDrySignal[0];
|
||||
const float temp2 = delayedDrySignal[1];
|
||||
buf[f][0] = d * temp1 + w * s[0];
|
||||
buf[f][1] = d * temp2 + w * s[1];
|
||||
buf[f][0] = (1 - m_mixVal) * temp1 + m_mixVal * buf[f][0];
|
||||
buf[f][1] = (1 - m_mixVal) * temp2 + m_mixVal * buf[f][1];
|
||||
|
||||
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
|
||||
|
||||
lInPeak = drySignal[0] > lInPeak ? drySignal[0] : lInPeak;
|
||||
rInPeak = drySignal[1] > rInPeak ? drySignal[1] : rInPeak;
|
||||
lOutPeak = s[0] > lOutPeak ? s[0] : lOutPeak;
|
||||
rOutPeak = s[1] > rOutPeak ? s[1] : rOutPeak;
|
||||
}
|
||||
|
||||
checkGate(outSum / frames);
|
||||
m_compressorControls.m_outPeakL = lOutPeak;
|
||||
m_compressorControls.m_outPeakR = rOutPeak;
|
||||
m_compressorControls.m_inPeakL = lInPeak;
|
||||
m_compressorControls.m_inPeakR = rInPeak;
|
||||
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
|
||||
// Regular modulo doesn't handle negative numbers correctly. This does.
|
||||
inline int CompressorEffect::realmod(int k, int n)
|
||||
{
|
||||
return (k %= n) < 0 ? k+n : k;
|
||||
}
|
||||
|
||||
// Regular fmod doesn't handle negative numbers correctly. This does.
|
||||
inline float CompressorEffect::realfmod(float k, float n)
|
||||
{
|
||||
return (k = fmod(k, n)) < 0 ? k+n : k;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void CompressorEffect::calcTiltFilter(sample_t inputSample, sample_t &outputSample, int filtNum)
|
||||
{
|
||||
m_tiltOut[filtNum] = m_a0 * inputSample + m_b1 * m_tiltOut[filtNum];
|
||||
outputSample = inputSample + m_lgain * m_tiltOut[filtNum] + m_hgain * (inputSample - m_tiltOut[filtNum]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CompressorEffect::changeSampleRate()
|
||||
{
|
||||
m_sampleRate = Engine::audioEngine()->processingSampleRate();
|
||||
|
||||
m_coeffPrecalc = COMP_LOG / (m_sampleRate * 0.001f);
|
||||
|
||||
// 200 ms
|
||||
m_crestTimeConst = exp(-1.f / (0.2f * m_sampleRate));
|
||||
|
||||
// 20 ms
|
||||
m_lookaheadDelayLength = 0.02 * m_sampleRate;
|
||||
m_inputBuf[0].resize(m_lookaheadDelayLength);
|
||||
m_inputBuf[1].resize(m_lookaheadDelayLength);
|
||||
|
||||
m_lookaheadBuf[0].resize(m_lookaheadDelayLength);
|
||||
m_lookaheadBuf[1].resize(m_lookaheadDelayLength);
|
||||
|
||||
m_preLookaheadBuf[0].resize(m_lookaheadDelayLength);
|
||||
m_preLookaheadBuf[1].resize(m_lookaheadDelayLength);
|
||||
|
||||
calcThreshold();
|
||||
calcKnee();
|
||||
calcRatio();
|
||||
calcAutoMakeup();// This should be after Threshold, Knee, and Ratio
|
||||
|
||||
calcAttack();
|
||||
calcRelease();
|
||||
calcRange();
|
||||
calcLookaheadLength();
|
||||
calcHold();
|
||||
resizeRMS();
|
||||
calcOutGain();
|
||||
calcInGain();
|
||||
calcTiltCoeffs();
|
||||
calcMix();
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// necessary for getting instance out of shared lib
|
||||
PLUGIN_EXPORT Plugin * lmms_plugin_main(Model* parent, void* data)
|
||||
{
|
||||
return new CompressorEffect(parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(data));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
158
plugins/Compressor/Compressor.h
Executable file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Compressor.h
|
||||
*
|
||||
* Copyright (c) 2020 Lost Robot <r94231@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef COMPRESSOR_H
|
||||
#define COMPRESSOR_H
|
||||
|
||||
#include "CompressorControls.h"
|
||||
|
||||
#include "Effect.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
constexpr float COMP_LOG = -2.2;
|
||||
|
||||
class CompressorEffect : public Effect
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CompressorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
|
||||
~CompressorEffect() override = default;
|
||||
bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override;
|
||||
|
||||
EffectControls* controls() override
|
||||
{
|
||||
return &m_compressorControls;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void calcAutoMakeup();
|
||||
void calcAttack();
|
||||
void calcRelease();
|
||||
void calcAutoAttack();
|
||||
void calcAutoRelease();
|
||||
void calcHold();
|
||||
void calcOutGain();
|
||||
void calcRatio();
|
||||
void calcRange();
|
||||
void resizeRMS();
|
||||
void calcLookaheadLength();
|
||||
void calcThreshold();
|
||||
void calcKnee();
|
||||
void calcInGain();
|
||||
void calcTiltCoeffs();
|
||||
void calcMix();
|
||||
void changeSampleRate();
|
||||
void redrawKnee();
|
||||
|
||||
private:
|
||||
CompressorControls m_compressorControls;
|
||||
|
||||
float msToCoeff(float ms);
|
||||
|
||||
inline void calcTiltFilter(sample_t inputSample, sample_t &outputSample, int filtNum);
|
||||
inline int realmod(int k, int n);
|
||||
inline float realfmod(float k, float n);
|
||||
|
||||
enum StereoLinkModes { Unlinked, Maximum, Average, Minimum, Blend };
|
||||
|
||||
std::vector<float> m_preLookaheadBuf[2];
|
||||
int m_preLookaheadBufLoc[2] = {0};
|
||||
|
||||
std::vector<float> m_lookaheadBuf[2];
|
||||
int m_lookaheadBufLoc[2] = {0};
|
||||
|
||||
std::vector<float> m_inputBuf[2];
|
||||
int m_inputBufLoc = 0;
|
||||
|
||||
float m_attCoeff;
|
||||
float m_relCoeff;
|
||||
float m_autoAttVal;
|
||||
float m_autoRelVal;
|
||||
|
||||
int m_holdLength = 0;
|
||||
int m_holdTimer[2] = {0, 0};
|
||||
|
||||
int m_lookaheadLength;
|
||||
int m_lookaheadDelayLength;
|
||||
int m_preLookaheadLength;
|
||||
float m_thresholdAmpVal;
|
||||
float m_autoMakeupVal;
|
||||
float m_outGainVal;
|
||||
float m_inGainVal;
|
||||
float m_rangeVal;
|
||||
float m_tiltVal;
|
||||
float m_mixVal;
|
||||
|
||||
float m_coeffPrecalc;
|
||||
|
||||
sampleFrame m_maxLookaheadVal;
|
||||
|
||||
int m_maxLookaheadTimer[2] = {1, 1};
|
||||
|
||||
float m_rmsTimeConst;
|
||||
float m_rmsVal[2] = {0, 0};
|
||||
|
||||
float m_crestPeakVal[2] = {0, 0};
|
||||
float m_crestRmsVal[2] = {0, 0};
|
||||
float m_crestFactorVal[2] = {0, 0};
|
||||
float m_crestTimeConst;
|
||||
|
||||
float m_tiltOut[2] = {0};
|
||||
|
||||
bool m_cleanedBuffers = false;
|
||||
|
||||
float m_sampleRate;
|
||||
|
||||
float m_lgain;
|
||||
float m_hgain;
|
||||
float m_a0;
|
||||
float m_b1;
|
||||
|
||||
float m_prevOut[2] = {0};
|
||||
|
||||
float m_yL[2];
|
||||
float m_gainResult[2];
|
||||
float m_displayPeak[2];
|
||||
float m_displayGain[2];
|
||||
|
||||
float m_kneeVal;
|
||||
float m_thresholdVal;
|
||||
float m_ratioVal;
|
||||
|
||||
bool m_redrawKnee = true;
|
||||
bool m_redrawThreshold = true;
|
||||
|
||||
friend class CompressorControls;
|
||||
friend class gui::CompressorControlDialog;
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
771
plugins/Compressor/CompressorControlDialog.cpp
Executable file
@@ -0,0 +1,771 @@
|
||||
/*
|
||||
* CompressorControlDialog.cpp
|
||||
*
|
||||
* Copyright (c) 2020 Lost Robot <r94231@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Compressor.h"
|
||||
#include "CompressorControlDialog.h"
|
||||
#include "CompressorControls.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QWheelEvent>
|
||||
|
||||
#include "AutomatableButton.h"
|
||||
#include "embed.h"
|
||||
#include "../Eq/EqFader.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "interpolation.h"
|
||||
#include "Knob.h"
|
||||
#include "MainWindow.h"
|
||||
#include "PixmapButton.h"
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
CompressorControlDialog::CompressorControlDialog(CompressorControls* controls) :
|
||||
EffectControlDialog(controls),
|
||||
m_controls(controls),
|
||||
m_inVolAreaColor(209, 216, 228, 17),
|
||||
m_inVolColor(209, 216, 228, 100),
|
||||
m_outVolAreaColor(209, 216, 228, 30),
|
||||
m_outVolColor(209, 216, 228, 240),
|
||||
m_gainReductionColor(180, 100, 100, 210),
|
||||
m_kneeColor(39, 171, 95, 255),
|
||||
m_kneeColor2(9, 171, 160, 255),
|
||||
m_threshColor(39, 171, 95, 100),
|
||||
m_textColor(209, 216, 228, 50),
|
||||
m_graphColor(209, 216, 228, 50),
|
||||
m_resetColor(200, 100, 15, 200)
|
||||
{
|
||||
setAutoFillBackground(true);
|
||||
QPalette pal;
|
||||
pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork"));
|
||||
setPalette(pal);
|
||||
setMinimumSize(MIN_COMP_SCREEN_X, MIN_COMP_SCREEN_Y);
|
||||
resize(COMP_SCREEN_X, COMP_SCREEN_Y);
|
||||
|
||||
m_graphPixmap.fill(QColor("transparent"));
|
||||
|
||||
m_visPixmap.fill(QColor("transparent"));
|
||||
|
||||
m_kneePixmap.fill(QColor("transparent"));
|
||||
|
||||
m_kneePixmap2.fill(QColor("transparent"));
|
||||
|
||||
m_miscPixmap.fill(QColor("transparent"));
|
||||
|
||||
m_controlsBoxLabel = new QLabel(this);
|
||||
m_controlsBoxLabel->setPixmap(PLUGIN_NAME::getIconPixmap("controlsBox"));
|
||||
m_controlsBoxLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
m_rmsEnabledLabel = new QLabel(this);
|
||||
m_rmsEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled"));
|
||||
m_rmsEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
m_blendEnabledLabel = new QLabel(this);
|
||||
m_blendEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled"));
|
||||
m_blendEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
m_lookaheadEnabledLabel = new QLabel(this);
|
||||
m_lookaheadEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled"));
|
||||
m_lookaheadEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
m_ratioEnabledLabel = new QLabel(this);
|
||||
m_ratioEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled_large"));
|
||||
m_ratioEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
m_thresholdKnob = new Knob(knobStyled, this);
|
||||
makeLargeKnob(m_thresholdKnob, tr("Threshold:") , " dBFS");
|
||||
m_thresholdKnob->setModel(&controls->m_thresholdModel);
|
||||
m_thresholdKnob->setToolTip(tr("Volume at which the compression begins to take place"));
|
||||
|
||||
m_ratioKnob = new Knob(knobStyled, this);
|
||||
makeLargeKnob(m_ratioKnob, tr("Ratio:") , ":1");
|
||||
m_ratioKnob->setModel(&controls->m_ratioModel);
|
||||
m_ratioKnob->setToolTip(tr("How far the compressor must turn the volume down after crossing the threshold"));
|
||||
|
||||
m_attackKnob = new Knob(knobStyled, this);
|
||||
makeLargeKnob(m_attackKnob, tr("Attack:") , " ms");
|
||||
m_attackKnob->setModel(&controls->m_attackModel);
|
||||
m_attackKnob->setToolTip(tr("Speed at which the compressor starts to compress the audio"));
|
||||
|
||||
m_releaseKnob = new Knob(knobStyled, this);
|
||||
makeLargeKnob(m_releaseKnob, tr("Release:") , " ms");
|
||||
m_releaseKnob->setModel(&controls->m_releaseModel);
|
||||
m_releaseKnob->setToolTip(tr("Speed at which the compressor ceases to compress the audio"));
|
||||
|
||||
m_kneeKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_kneeKnob, tr("Knee:") , " dB");
|
||||
m_kneeKnob->setModel(&controls->m_kneeModel);
|
||||
m_kneeKnob->setToolTip(tr("Smooth out the gain reduction curve around the threshold"));
|
||||
|
||||
m_rangeKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_rangeKnob, tr("Range:") , " dBFS");
|
||||
m_rangeKnob->setModel(&controls->m_rangeModel);
|
||||
m_rangeKnob->setToolTip(tr("Maximum gain reduction"));
|
||||
|
||||
m_lookaheadLengthKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_lookaheadLengthKnob, tr("Lookahead Length:") , " ms");
|
||||
m_lookaheadLengthKnob->setModel(&controls->m_lookaheadLengthModel);
|
||||
m_lookaheadLengthKnob->setToolTip(tr("How long the compressor has to react to the sidechain signal ahead of time"));
|
||||
|
||||
m_holdKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_holdKnob, tr("Hold:") , " ms");
|
||||
m_holdKnob->setModel(&controls->m_holdModel);
|
||||
m_holdKnob->setToolTip(tr("Delay between attack and release stages"));
|
||||
|
||||
m_rmsKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_rmsKnob, tr("RMS Size:") , "");
|
||||
m_rmsKnob->setModel(&controls->m_rmsModel);
|
||||
m_rmsKnob->setToolTip(tr("Size of the RMS buffer"));
|
||||
|
||||
m_inBalanceKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_inBalanceKnob, tr("Input Balance:") , "");
|
||||
m_inBalanceKnob->setModel(&controls->m_inBalanceModel);
|
||||
m_inBalanceKnob->setToolTip(tr("Bias the input audio to the left/right or mid/side"));
|
||||
|
||||
m_outBalanceKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_outBalanceKnob, tr("Output Balance:") , "");
|
||||
m_outBalanceKnob->setModel(&controls->m_outBalanceModel);
|
||||
m_outBalanceKnob->setToolTip(tr("Bias the output audio to the left/right or mid/side"));
|
||||
|
||||
m_stereoBalanceKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_stereoBalanceKnob, tr("Stereo Balance:") , "");
|
||||
m_stereoBalanceKnob->setModel(&controls->m_stereoBalanceModel);
|
||||
m_stereoBalanceKnob->setToolTip(tr("Bias the sidechain signal to the left/right or mid/side"));
|
||||
|
||||
m_blendKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_blendKnob, tr("Stereo Link Blend:") , "");
|
||||
m_blendKnob->setModel(&controls->m_blendModel);
|
||||
m_blendKnob->setToolTip(tr("Blend between unlinked/maximum/average/minimum stereo linking modes"));
|
||||
|
||||
m_tiltKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_tiltKnob, tr("Tilt Gain:") , " dB");
|
||||
m_tiltKnob->setModel(&controls->m_tiltModel);
|
||||
m_tiltKnob->setToolTip(tr("Bias the sidechain signal to the low or high frequencies. -6 db is lowpass, 6 db is highpass."));
|
||||
|
||||
m_tiltFreqKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_tiltFreqKnob, tr("Tilt Frequency:") , " Hz");
|
||||
m_tiltFreqKnob->setModel(&controls->m_tiltFreqModel);
|
||||
m_tiltFreqKnob->setToolTip(tr("Center frequency of sidechain tilt filter"));
|
||||
|
||||
m_mixKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_mixKnob, tr("Mix:") , "%");
|
||||
m_mixKnob->setModel(&controls->m_mixModel);
|
||||
m_mixKnob->setToolTip(tr("Balance between wet and dry signals"));
|
||||
|
||||
m_autoAttackKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_autoAttackKnob, tr("Auto Attack:") , "%");
|
||||
m_autoAttackKnob->setModel(&controls->m_autoAttackModel);
|
||||
m_autoAttackKnob->setToolTip(tr("Automatically control attack value depending on crest factor"));
|
||||
|
||||
m_autoReleaseKnob = new Knob(knobStyled, this);
|
||||
makeSmallKnob(m_autoReleaseKnob, tr("Auto Release:") , "%");
|
||||
m_autoReleaseKnob->setModel(&controls->m_autoReleaseModel);
|
||||
m_autoReleaseKnob->setToolTip(tr("Automatically control release value depending on crest factor"));
|
||||
|
||||
m_outFader = new EqFader(&controls->m_outGainModel,tr("Output gain"),
|
||||
this, &controls->m_outPeakL, &controls->m_outPeakR);
|
||||
m_outFader->setDisplayConversion(false);
|
||||
m_outFader->setHintText(tr("Gain"), "dBFS");
|
||||
m_outFader->setToolTip(tr("Output volume"));
|
||||
|
||||
m_inFader = new EqFader(&controls->m_inGainModel,tr("Input gain"),
|
||||
this, &controls->m_inPeakL, &controls->m_inPeakR);
|
||||
m_inFader->setDisplayConversion(false);
|
||||
m_inFader->setHintText(tr("Gain"), "dBFS");
|
||||
m_inFader->setToolTip(tr("Input volume"));
|
||||
|
||||
rmsButton = new PixmapButton(this, tr("Root Mean Square"));
|
||||
rmsButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("rms_sel"));
|
||||
rmsButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("rms_unsel"));
|
||||
rmsButton->setToolTip(tr("Use RMS of the input"));
|
||||
|
||||
peakButton = new PixmapButton(this, tr("Peak"));
|
||||
peakButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("peak_sel"));
|
||||
peakButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("peak_unsel"));
|
||||
peakButton->setToolTip(tr("Use absolute value of the input"));
|
||||
|
||||
rmsPeakGroup = new automatableButtonGroup(this);
|
||||
rmsPeakGroup->addButton(rmsButton);
|
||||
rmsPeakGroup->addButton(peakButton);
|
||||
rmsPeakGroup->setModel(&controls->m_peakmodeModel);
|
||||
|
||||
leftRightButton = new PixmapButton(this, tr("Left/Right"));
|
||||
leftRightButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("leftright_sel"));
|
||||
leftRightButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("leftright_unsel"));
|
||||
leftRightButton->setToolTip(tr("Compress left and right audio"));
|
||||
|
||||
midSideButton = new PixmapButton(this, tr("Mid/Side"));
|
||||
midSideButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("midside_sel"));
|
||||
midSideButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("midside_unsel"));
|
||||
midSideButton->setToolTip(tr("Compress mid and side audio"));
|
||||
|
||||
leftRightMidSideGroup = new automatableButtonGroup(this);
|
||||
leftRightMidSideGroup->addButton(leftRightButton);
|
||||
leftRightMidSideGroup->addButton(midSideButton);
|
||||
leftRightMidSideGroup->setModel(&controls->m_midsideModel);
|
||||
|
||||
compressButton = new PixmapButton(this, tr("Compressor"));
|
||||
compressButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("compressor_sel"));
|
||||
compressButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("compressor_unsel"));
|
||||
compressButton->setToolTip(tr("Compress the audio"));
|
||||
|
||||
limitButton = new PixmapButton(this, tr("Limiter"));
|
||||
limitButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("limiter_sel"));
|
||||
limitButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("limiter_unsel"));
|
||||
limitButton->setToolTip(tr("Set Ratio to infinity (is not guaranteed to limit audio volume)"));
|
||||
|
||||
compressLimitGroup = new automatableButtonGroup(this);
|
||||
compressLimitGroup->addButton(compressButton);
|
||||
compressLimitGroup->addButton(limitButton);
|
||||
compressLimitGroup->setModel(&controls->m_limiterModel);
|
||||
|
||||
unlinkedButton = new PixmapButton(this, tr("Unlinked"));
|
||||
unlinkedButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("unlinked_sel"));
|
||||
unlinkedButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("unlinked_unsel"));
|
||||
unlinkedButton->setToolTip(tr("Compress each channel separately"));
|
||||
|
||||
maximumButton = new PixmapButton(this, tr("Maximum"));
|
||||
maximumButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("maximum_sel"));
|
||||
maximumButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("maximum_unsel"));
|
||||
maximumButton->setToolTip(tr("Compress based on the loudest channel"));
|
||||
|
||||
averageButton = new PixmapButton(this, tr("Average"));
|
||||
averageButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("average_sel"));
|
||||
averageButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("average_unsel"));
|
||||
averageButton->setToolTip(tr("Compress based on the averaged channel volume"));
|
||||
|
||||
minimumButton = new PixmapButton(this, tr("Minimum"));
|
||||
minimumButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("minimum_sel"));
|
||||
minimumButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("minimum_unsel"));
|
||||
minimumButton->setToolTip(tr("Compress based on the quietest channel"));
|
||||
|
||||
blendButton = new PixmapButton(this, tr("Blend"));
|
||||
blendButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("blend_sel"));
|
||||
blendButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("blend_unsel"));
|
||||
blendButton->setToolTip(tr("Blend between stereo linking modes"));
|
||||
|
||||
stereoLinkGroup = new automatableButtonGroup(this);
|
||||
stereoLinkGroup->addButton(unlinkedButton);
|
||||
stereoLinkGroup->addButton(maximumButton);
|
||||
stereoLinkGroup->addButton(averageButton);
|
||||
stereoLinkGroup->addButton(minimumButton);
|
||||
stereoLinkGroup->addButton(blendButton);
|
||||
stereoLinkGroup->setModel(&controls->m_stereoLinkModel);
|
||||
|
||||
autoMakeupButton = new PixmapButton(this, tr("Auto Makeup Gain"));
|
||||
autoMakeupButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("autogain_sel"));
|
||||
autoMakeupButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("autogain_unsel"));
|
||||
autoMakeupButton->setToolTip(tr("Automatically change makeup gain depending on threshold, knee, and ratio settings"));
|
||||
autoMakeupButton->setCheckable(true);
|
||||
autoMakeupButton->setModel(&controls->m_autoMakeupModel);
|
||||
|
||||
auditionButton = new PixmapButton(this, tr("Soft Clip"));
|
||||
auditionButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("audition_sel"));
|
||||
auditionButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("audition_unsel"));
|
||||
auditionButton->setToolTip(tr("Play the delta signal"));
|
||||
auditionButton->setCheckable(true);
|
||||
auditionButton->setModel(&controls->m_auditionModel);
|
||||
|
||||
feedbackButton = new PixmapButton(this, tr("Soft Clip"));
|
||||
feedbackButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("feedback_sel"));
|
||||
feedbackButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("feedback_unsel"));
|
||||
feedbackButton->setToolTip(tr("Use the compressor's output as the sidechain input"));
|
||||
feedbackButton->setCheckable(true);
|
||||
feedbackButton->setModel(&controls->m_feedbackModel);
|
||||
|
||||
lookaheadButton = new PixmapButton(this, tr("Lookahead Enabled"));
|
||||
lookaheadButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("lookahead_sel"));
|
||||
lookaheadButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("lookahead_unsel"));
|
||||
lookaheadButton->setToolTip(tr("Enable Lookahead, which introduces 20 milliseconds of latency"));
|
||||
lookaheadButton->setCheckable(true);
|
||||
lookaheadButton->setModel(&controls->m_lookaheadModel);
|
||||
|
||||
connect(getGUI()->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(updateDisplay()));
|
||||
|
||||
connect(&m_controls->m_peakmodeModel, SIGNAL(dataChanged()), this, SLOT(peakmodeChanged()));
|
||||
connect(&m_controls->m_stereoLinkModel, SIGNAL(dataChanged()), this, SLOT(stereoLinkChanged()));
|
||||
connect(&m_controls->m_lookaheadModel, SIGNAL(dataChanged()), this, SLOT(lookaheadChanged()));
|
||||
connect(&m_controls->m_limiterModel, SIGNAL(dataChanged()), this, SLOT(limiterChanged()));
|
||||
|
||||
m_timeElapsed.start();
|
||||
|
||||
peakmodeChanged();
|
||||
stereoLinkChanged();
|
||||
lookaheadChanged();
|
||||
limiterChanged();
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::makeLargeKnob(Knob * knob, QString hint, QString unit)
|
||||
{
|
||||
knob->setHintText(hint, unit);
|
||||
knob->setFixedSize(56, 56);
|
||||
knob->setOuterRadius(23);
|
||||
knob->setInnerRadius(15);
|
||||
knob->setCenterPointX(28);
|
||||
knob->setCenterPointY(28);
|
||||
}
|
||||
|
||||
void CompressorControlDialog::makeSmallKnob(Knob * knob, QString hint, QString unit)
|
||||
{
|
||||
knob->setHintText(hint, unit);
|
||||
knob->setFixedSize(30, 30);
|
||||
knob->setOuterRadius(10);
|
||||
knob->setInnerRadius(4);
|
||||
knob->setCenterPointX(15);
|
||||
knob->setCenterPointY(15);
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::peakmodeChanged()
|
||||
{
|
||||
m_rmsKnob->setVisible(!m_controls->m_peakmodeModel.value());
|
||||
m_rmsEnabledLabel->setVisible(!m_controls->m_peakmodeModel.value());
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::stereoLinkChanged()
|
||||
{
|
||||
m_blendKnob->setVisible(m_controls->m_stereoLinkModel.value() == 4);
|
||||
m_blendEnabledLabel->setVisible(m_controls->m_stereoLinkModel.value() == 4);
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::lookaheadChanged()
|
||||
{
|
||||
m_lookaheadLengthKnob->setVisible(m_controls->m_lookaheadModel.value());
|
||||
m_lookaheadEnabledLabel->setVisible(m_controls->m_lookaheadModel.value());
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::limiterChanged()
|
||||
{
|
||||
m_ratioKnob->setVisible(!m_controls->m_limiterModel.value());
|
||||
m_ratioEnabledLabel->setVisible(!m_controls->m_limiterModel.value());
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::updateDisplay()
|
||||
{
|
||||
if (!isVisible())
|
||||
{
|
||||
m_timeElapsed.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
int elapsedMil = m_timeElapsed.elapsed();
|
||||
m_timeElapsed.restart();
|
||||
m_timeSinceLastUpdate += elapsedMil;
|
||||
m_compPixelMovement = int(m_timeSinceLastUpdate / COMP_MILLI_PER_PIXEL);
|
||||
m_timeSinceLastUpdate %= COMP_MILLI_PER_PIXEL;
|
||||
|
||||
// Time Change / Daylight Savings Time protection
|
||||
if (!m_compPixelMovement || m_compPixelMovement <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_controls->m_effect->isEnabled() || !m_controls->m_effect->isRunning())
|
||||
{
|
||||
m_controls->m_effect->m_displayPeak[0] = COMP_NOISE_FLOOR;
|
||||
m_controls->m_effect->m_displayPeak[1] = COMP_NOISE_FLOOR;
|
||||
m_controls->m_effect->m_displayGain[0] = 1;
|
||||
m_controls->m_effect->m_displayGain[1] = 1;
|
||||
}
|
||||
|
||||
m_peakAvg = (m_controls->m_effect->m_displayPeak[0] + m_controls->m_effect->m_displayPeak[1]) * 0.5f;
|
||||
m_gainAvg = (m_controls->m_effect->m_displayGain[0] + m_controls->m_effect->m_displayGain[1]) * 0.5f;
|
||||
|
||||
m_controls->m_effect->m_displayPeak[0] = m_controls->m_effect->m_yL[0];
|
||||
m_controls->m_effect->m_displayPeak[1] = m_controls->m_effect->m_yL[1];
|
||||
m_controls->m_effect->m_displayGain[0] = m_controls->m_effect->m_gainResult[0];
|
||||
m_controls->m_effect->m_displayGain[1] = m_controls->m_effect->m_gainResult[1];
|
||||
|
||||
m_yPoint = dbfsToYPoint(ampToDbfs(m_peakAvg));
|
||||
m_yGainPoint = dbfsToYPoint(ampToDbfs(m_gainAvg));
|
||||
|
||||
m_threshYPoint = dbfsToYPoint(m_controls->m_effect->m_thresholdVal);
|
||||
m_threshXPoint = m_kneeWindowSizeY - m_threshYPoint;
|
||||
|
||||
drawVisPixmap();
|
||||
|
||||
if (m_controls->m_effect->m_redrawKnee)
|
||||
{
|
||||
redrawKnee();
|
||||
}
|
||||
|
||||
drawKneePixmap2();
|
||||
|
||||
if (m_controls->m_effect->m_redrawThreshold)
|
||||
{
|
||||
drawMiscPixmap();
|
||||
}
|
||||
|
||||
m_lastPoint = m_yPoint;
|
||||
m_lastGainPoint = m_yGainPoint;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::drawVisPixmap()
|
||||
{
|
||||
m_p.begin(&m_visPixmap);
|
||||
|
||||
// Move entire display to the left
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.drawPixmap(-m_compPixelMovement, 0, m_visPixmap);
|
||||
m_p.fillRect(m_windowSizeX-m_compPixelMovement, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
m_p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// Draw translucent portion of input volume line
|
||||
m_p.setPen(QPen(m_inVolAreaColor, 1));
|
||||
for (int i = 0; i < m_compPixelMovement; ++i)
|
||||
{
|
||||
const int temp = linearInterpolate(m_lastPoint, m_yPoint, float(i) / float(m_compPixelMovement));
|
||||
m_p.drawLine(m_windowSizeX-m_compPixelMovement+i, temp, m_windowSizeX-m_compPixelMovement+i, m_windowSizeY);
|
||||
}
|
||||
|
||||
// Draw input volume line
|
||||
m_p.setPen(QPen(m_inVolColor, 1));
|
||||
m_p.drawLine(m_windowSizeX-m_compPixelMovement-1, m_lastPoint, m_windowSizeX, m_yPoint);
|
||||
|
||||
// Draw translucent portion of output volume line
|
||||
m_p.setPen(QPen(m_outVolAreaColor, 1));
|
||||
for (int i = 0; i < m_compPixelMovement; ++i)
|
||||
{
|
||||
const int temp = linearInterpolate(m_lastPoint+m_lastGainPoint, m_yPoint+m_yGainPoint, float(i) / float(m_compPixelMovement));
|
||||
m_p.drawLine(m_windowSizeX-m_compPixelMovement+i, temp, m_windowSizeX-m_compPixelMovement+i, m_windowSizeY);
|
||||
}
|
||||
|
||||
// Draw output volume line
|
||||
m_p.setPen(QPen(m_outVolColor, 1));
|
||||
m_p.drawLine(m_windowSizeX-m_compPixelMovement-1, m_lastPoint+m_lastGainPoint, m_windowSizeX, m_yPoint+m_yGainPoint);
|
||||
|
||||
// Draw gain reduction line
|
||||
m_p.setPen(QPen(m_gainReductionColor, 2));
|
||||
m_p.drawLine(m_windowSizeX-m_compPixelMovement-1, m_lastGainPoint, m_windowSizeX, m_yGainPoint);
|
||||
|
||||
m_p.end();
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::redrawKnee()
|
||||
{
|
||||
m_controls->m_effect->m_redrawKnee = false;
|
||||
|
||||
// Start drawing knee visualizer
|
||||
m_p.begin(&m_kneePixmap);
|
||||
|
||||
m_p.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
// Clear display
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(0, 0, m_windowSizeX, m_kneeWindowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
m_p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
m_p.setPen(QPen(m_kneeColor, 3));
|
||||
|
||||
// Limiter = infinite ratio
|
||||
float actualRatio = m_controls->m_limiterModel.value() ? 0 : m_controls->m_effect->m_ratioVal;
|
||||
|
||||
// Calculate endpoints for the two straight lines
|
||||
float kneePoint1 = m_controls->m_effect->m_thresholdVal - m_controls->m_effect->m_kneeVal;
|
||||
float kneePoint2X = m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal;
|
||||
float kneePoint2Y = (m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * (actualRatio * (m_controls->m_effect->m_kneeVal / -m_controls->m_effect->m_thresholdVal))));
|
||||
float ratioPoint = m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * actualRatio);
|
||||
|
||||
// Draw two straight lines
|
||||
m_p.drawLine(0, m_kneeWindowSizeY, dbfsToXPoint(kneePoint1), dbfsToYPoint(kneePoint1));
|
||||
if (dbfsToXPoint(kneePoint2X) < m_kneeWindowSizeY)
|
||||
{
|
||||
m_p.drawLine(dbfsToXPoint(kneePoint2X), dbfsToYPoint(kneePoint2Y), m_kneeWindowSizeY, dbfsToYPoint(ratioPoint));
|
||||
}
|
||||
|
||||
// Draw knee section
|
||||
if (m_controls->m_effect->m_kneeVal)
|
||||
{
|
||||
m_p.setPen(QPen(m_kneeColor2, 3));
|
||||
|
||||
float prevPoint[2] = {kneePoint1, kneePoint1};
|
||||
float newPoint[2] = {0, 0};
|
||||
|
||||
// Draw knee curve using many straight lines.
|
||||
for (int i = 0; i < COMP_KNEE_LINES; ++i)
|
||||
{
|
||||
newPoint[0] = linearInterpolate(kneePoint1, kneePoint2X, (i + 1) / (float)COMP_KNEE_LINES);
|
||||
|
||||
const float temp = newPoint[0] - m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal;
|
||||
newPoint[1] = (newPoint[0] + (actualRatio - 1) * temp * temp / (4 * m_controls->m_effect->m_kneeVal));
|
||||
|
||||
m_p.drawLine(dbfsToXPoint(prevPoint[0]), dbfsToYPoint(prevPoint[1]), dbfsToXPoint(newPoint[0]), dbfsToYPoint(newPoint[1]));
|
||||
|
||||
prevPoint[0] = newPoint[0];
|
||||
prevPoint[1] = newPoint[1];
|
||||
}
|
||||
}
|
||||
|
||||
m_p.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
// Erase right portion
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(m_kneeWindowSizeX + 1, 0, m_windowSizeX, m_kneeWindowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
m_p.end();
|
||||
|
||||
m_p.begin(&m_kneePixmap2);
|
||||
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(0, 0, m_windowSizeX, m_kneeWindowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
m_p.end();
|
||||
|
||||
m_lastKneePoint = 0;
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::drawKneePixmap2()
|
||||
{
|
||||
m_p.begin(&m_kneePixmap2);
|
||||
|
||||
m_p.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
int kneePoint = dbfsToXPoint(ampToDbfs(m_peakAvg));
|
||||
if (kneePoint > m_lastKneePoint)
|
||||
{
|
||||
QRectF knee2Rect = QRect(m_lastKneePoint, 0, kneePoint - m_lastKneePoint, m_kneeWindowSizeY);
|
||||
m_p.drawPixmap(knee2Rect, m_kneePixmap, knee2Rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(kneePoint, 0, m_lastKneePoint, m_kneeWindowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
}
|
||||
m_lastKneePoint = kneePoint;
|
||||
|
||||
m_p.end();
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::drawMiscPixmap()
|
||||
{
|
||||
m_p.begin(&m_miscPixmap);
|
||||
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
m_p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// Draw threshold lines
|
||||
m_p.setPen(QPen(m_threshColor, 2, Qt::DotLine));
|
||||
m_p.drawLine(0, m_threshYPoint, m_windowSizeX, m_threshYPoint);
|
||||
m_p.drawLine(m_threshXPoint, 0, m_threshXPoint, m_kneeWindowSizeY);
|
||||
|
||||
m_p.end();
|
||||
|
||||
m_controls->m_effect->m_redrawThreshold = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CompressorControlDialog::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
if (!isVisible())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_p.begin(this);
|
||||
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
m_p.drawPixmap(0, 0, m_graphPixmap);
|
||||
m_p.drawPixmap(0, 0, m_visPixmap);
|
||||
m_p.setOpacity(0.25);
|
||||
m_p.drawPixmap(0, 0, m_kneePixmap);
|
||||
m_p.setOpacity(1);
|
||||
if (m_controls->m_effect->isEnabled() && m_controls->m_effect->isRunning())
|
||||
{
|
||||
m_p.drawPixmap(0, 0, m_kneePixmap2);
|
||||
}
|
||||
m_p.drawPixmap(0, 0, m_miscPixmap);
|
||||
|
||||
m_p.end();
|
||||
}
|
||||
|
||||
|
||||
inline int CompressorControlDialog::dbfsToYPoint(float inDbfs)
|
||||
{
|
||||
return (-((inDbfs + m_dbRange) / m_dbRange) + 1) * m_windowSizeY;
|
||||
}
|
||||
|
||||
inline int CompressorControlDialog::dbfsToXPoint(float inDbfs)
|
||||
{
|
||||
return m_kneeWindowSizeY - dbfsToYPoint(inDbfs);
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
resetCompressorView();
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::wheelEvent(QWheelEvent * event)
|
||||
{
|
||||
const float temp = m_dbRange;
|
||||
const float dbRangeNew = m_dbRange - copysignf(COMP_GRID_SPACING, event->angleDelta().y());
|
||||
m_dbRange = round(qBound(COMP_GRID_SPACING, dbRangeNew, COMP_GRID_MAX) / COMP_GRID_SPACING) * COMP_GRID_SPACING;
|
||||
|
||||
// Only reset view if the scolling had an effect
|
||||
if (m_dbRange != temp)
|
||||
{
|
||||
drawGraph();
|
||||
m_controls->m_effect->m_redrawKnee = true;
|
||||
m_controls->m_effect->m_redrawThreshold = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::drawGraph()
|
||||
{
|
||||
m_p.begin(&m_graphPixmap);
|
||||
|
||||
m_p.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
m_p.setPen(QPen(m_textColor, 1));
|
||||
|
||||
// Arbitrary formula for increasing font size when window size increases
|
||||
m_p.setFont(QFont("Arial", qMax(int(m_windowSizeY / 1080.f * 24), 12)));
|
||||
|
||||
// Redraw graph
|
||||
m_p.setPen(QPen(m_graphColor, 1));
|
||||
for (int i = 1; i < m_dbRange / COMP_GRID_SPACING + 1; ++i)
|
||||
{
|
||||
m_p.drawLine(0, dbfsToYPoint(-COMP_GRID_SPACING * i), m_windowSizeX, dbfsToYPoint(-COMP_GRID_SPACING * i));
|
||||
m_p.drawLine(dbfsToXPoint(-COMP_GRID_SPACING * i), 0, dbfsToXPoint(-COMP_GRID_SPACING * i), m_kneeWindowSizeY);
|
||||
m_p.drawText(QRectF(m_windowSizeX - 50, dbfsToYPoint(-COMP_GRID_SPACING * i), 50, 50), Qt::AlignRight | Qt::AlignTop, QString::number(i * -COMP_GRID_SPACING));
|
||||
}
|
||||
|
||||
m_p.end();
|
||||
}
|
||||
|
||||
|
||||
void CompressorControlDialog::resetCompressorView()
|
||||
{
|
||||
m_windowSizeX = size().width();
|
||||
m_windowSizeY = size().height();
|
||||
m_kneeWindowSizeX = m_windowSizeY;
|
||||
m_kneeWindowSizeY = m_windowSizeY;
|
||||
m_controlsBoxX = (m_windowSizeX - COMP_BOX_X) * 0.5;
|
||||
m_controlsBoxY = m_windowSizeY - 40 - COMP_BOX_Y;
|
||||
|
||||
m_controls->m_effect->m_redrawKnee = true;
|
||||
m_controls->m_effect->m_redrawThreshold = true;
|
||||
m_lastKneePoint = 0;
|
||||
|
||||
drawGraph();
|
||||
|
||||
m_p.begin(&m_visPixmap);
|
||||
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent"));
|
||||
m_p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
// Draw line at right side, so the sudden
|
||||
// content that the visualizer will display
|
||||
// later on won't look too ugly
|
||||
m_p.setPen(QPen(m_resetColor, 3));
|
||||
m_p.drawLine(m_windowSizeX, 0, m_windowSizeX, m_windowSizeY);
|
||||
|
||||
m_p.end();
|
||||
|
||||
m_controlsBoxLabel->move(m_controlsBoxX, m_controlsBoxY);
|
||||
m_rmsEnabledLabel->move(m_controlsBoxX + 429, m_controlsBoxY + 209);
|
||||
m_blendEnabledLabel->move(m_controlsBoxX + 587, m_controlsBoxY + 197);
|
||||
m_lookaheadEnabledLabel->move(m_controlsBoxX + 221, m_controlsBoxY + 135);
|
||||
m_ratioEnabledLabel->move(m_controlsBoxX + 267, m_controlsBoxY + 21);
|
||||
|
||||
m_thresholdKnob->move(m_controlsBoxX + 137, m_controlsBoxY + 21);
|
||||
m_ratioKnob->move(m_controlsBoxX + 267, m_controlsBoxY + 21);
|
||||
m_attackKnob->move(m_controlsBoxX + 397, m_controlsBoxY + 21);
|
||||
m_releaseKnob->move(m_controlsBoxX + 527, m_controlsBoxY + 21);
|
||||
m_kneeKnob->move(m_controlsBoxX + 97, m_controlsBoxY + 135);
|
||||
m_rangeKnob->move(m_controlsBoxX + 159, m_controlsBoxY + 135);
|
||||
m_lookaheadLengthKnob->move(m_controlsBoxX + 221, m_controlsBoxY + 135);
|
||||
m_holdKnob->move(m_controlsBoxX + 283, m_controlsBoxY + 135);
|
||||
|
||||
m_rmsKnob->move(m_controlsBoxX + 429, m_controlsBoxY + 209);
|
||||
m_inBalanceKnob->move(m_controlsBoxX + 27, m_controlsBoxY + 219);
|
||||
m_outBalanceKnob->move(m_controlsBoxX + 662, m_controlsBoxY + 219);
|
||||
m_stereoBalanceKnob->move(m_controlsBoxX + 522, m_controlsBoxY + 137);
|
||||
m_blendKnob->move(m_controlsBoxX + 587, m_controlsBoxY + 197);
|
||||
m_tiltKnob->move(m_controlsBoxX + 364, m_controlsBoxY + 138);
|
||||
m_tiltFreqKnob->move(m_controlsBoxX + 415, m_controlsBoxY + 138);
|
||||
m_mixKnob->move(m_controlsBoxX + 27, m_controlsBoxY + 13);
|
||||
|
||||
m_outFader->move(m_controlsBoxX + 666, m_controlsBoxY + 91);
|
||||
m_inFader->move(m_controlsBoxX + 31, m_controlsBoxY + 91);
|
||||
|
||||
rmsButton->move(m_controlsBoxX + 337, m_controlsBoxY + 231);
|
||||
peakButton->move(m_controlsBoxX + 337, m_controlsBoxY + 248);
|
||||
|
||||
leftRightButton->move(m_controlsBoxX + 220, m_controlsBoxY + 231);
|
||||
midSideButton->move(m_controlsBoxX + 220, m_controlsBoxY + 248);
|
||||
|
||||
compressButton->move(m_controlsBoxX + 98, m_controlsBoxY + 231);
|
||||
limitButton->move(m_controlsBoxX + 98, m_controlsBoxY + 248);
|
||||
|
||||
unlinkedButton->move(m_controlsBoxX + 495, m_controlsBoxY + 180);
|
||||
maximumButton->move(m_controlsBoxX + 495, m_controlsBoxY + 197);
|
||||
averageButton->move(m_controlsBoxX + 495, m_controlsBoxY + 214);
|
||||
minimumButton->move(m_controlsBoxX + 495, m_controlsBoxY + 231);
|
||||
blendButton->move(m_controlsBoxX + 495, m_controlsBoxY + 248);
|
||||
|
||||
autoMakeupButton->move(m_controlsBoxX + 220, m_controlsBoxY + 206);
|
||||
auditionButton->move(m_controlsBoxX + 658, m_controlsBoxY + 14);
|
||||
feedbackButton->move(m_controlsBoxX + 98, m_controlsBoxY + 206);
|
||||
m_autoAttackKnob->move(m_controlsBoxX + 460, m_controlsBoxY + 38);
|
||||
m_autoReleaseKnob->move(m_controlsBoxX + 590, m_controlsBoxY + 38);
|
||||
lookaheadButton->move(m_controlsBoxX + 202, m_controlsBoxY + 171);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
229
plugins/Compressor/CompressorControlDialog.h
Executable file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* CompressorControlDialog.h
|
||||
*
|
||||
* Copyright (c) 2020 Lost Robot <r94231@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPRESSOR_CONTROL_DIALOG_H
|
||||
#define COMPRESSOR_CONTROL_DIALOG_H
|
||||
|
||||
#include <QBasicTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QPainter>
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
|
||||
class QLabel;
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
constexpr float COMP_NOISE_FLOOR = 0.000001;// -120 dbFs
|
||||
|
||||
class CompressorControls;
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
constexpr int COMP_MILLI_PER_PIXEL = 6;
|
||||
constexpr int MIN_COMP_SCREEN_X = 800;
|
||||
constexpr int MIN_COMP_SCREEN_Y = 360;
|
||||
constexpr int MAX_COMP_SCREEN_X = 1920;
|
||||
constexpr int MAX_COMP_SCREEN_Y = 1080;
|
||||
constexpr int COMP_SCREEN_X = 800;
|
||||
constexpr int COMP_SCREEN_Y = 560;
|
||||
constexpr int KNEE_SCREEN_X = COMP_SCREEN_Y;
|
||||
constexpr int KNEE_SCREEN_Y = COMP_SCREEN_Y;
|
||||
constexpr int COMP_KNEE_LINES = 20;
|
||||
constexpr int COMP_BOX_X = 720;
|
||||
constexpr int COMP_BOX_Y = 280;
|
||||
constexpr float COMP_GRID_SPACING = 3.f;// 3 db per grid line
|
||||
constexpr float COMP_GRID_MAX = 96.f;// Can't zoom out past 96 db
|
||||
|
||||
class automatableButtonGroup;
|
||||
class Knob;
|
||||
class PixmapButton;
|
||||
class EqFader;
|
||||
|
||||
|
||||
class CompressorControlDialog : public EffectControlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CompressorControlDialog(CompressorControls* controls);
|
||||
|
||||
bool isResizable() const override {return true;}
|
||||
QSize sizeHint() const override {return QSize(COMP_SCREEN_X, COMP_SCREEN_Y);}
|
||||
|
||||
// For theming purposes
|
||||
Q_PROPERTY(QColor inVolAreaColor MEMBER m_inVolAreaColor)
|
||||
Q_PROPERTY(QColor inVolColor MEMBER m_inVolColor)
|
||||
Q_PROPERTY(QColor outVolAreaColor MEMBER m_outVolAreaColor)
|
||||
Q_PROPERTY(QColor outVolColor MEMBER m_outVolColor)
|
||||
Q_PROPERTY(QColor gainReductionColor MEMBER m_gainReductionColor)
|
||||
Q_PROPERTY(QColor kneeColor MEMBER m_kneeColor)
|
||||
Q_PROPERTY(QColor kneeColor2 MEMBER m_kneeColor2)
|
||||
Q_PROPERTY(QColor threshColor MEMBER m_threshColor)
|
||||
Q_PROPERTY(QColor textColor MEMBER m_textColor)
|
||||
Q_PROPERTY(QColor graphColor MEMBER m_graphColor)
|
||||
Q_PROPERTY(QColor resetColor MEMBER m_resetColor)
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void updateDisplay();
|
||||
void peakmodeChanged();
|
||||
void stereoLinkChanged();
|
||||
void lookaheadChanged();
|
||||
void limiterChanged();
|
||||
|
||||
private:
|
||||
void makeLargeKnob(Knob * knob, QString hint, QString unit);
|
||||
void makeSmallKnob(Knob * knob, QString hint, QString unit);
|
||||
void resetCompressorView();
|
||||
void drawVisPixmap();
|
||||
void redrawKnee();
|
||||
void drawKneePixmap2();
|
||||
void drawMiscPixmap();
|
||||
void drawGraph();
|
||||
|
||||
QPainter m_p;
|
||||
|
||||
QBasicTimer m_updateTimer;
|
||||
|
||||
CompressorControls * m_controls;
|
||||
|
||||
inline int dbfsToYPoint(float inDbfs);
|
||||
inline int dbfsToXPoint(float inDbfs);
|
||||
|
||||
QPixmap m_visPixmap = QPixmap(MAX_COMP_SCREEN_X, MAX_COMP_SCREEN_Y);
|
||||
QPixmap m_kneePixmap = QPixmap(MAX_COMP_SCREEN_X, MAX_COMP_SCREEN_Y);
|
||||
QPixmap m_kneePixmap2 = QPixmap(MAX_COMP_SCREEN_X, MAX_COMP_SCREEN_Y);
|
||||
QPixmap m_miscPixmap = QPixmap(MAX_COMP_SCREEN_X, MAX_COMP_SCREEN_Y);
|
||||
QPixmap m_graphPixmap = QPixmap(MAX_COMP_SCREEN_X, MAX_COMP_SCREEN_Y);
|
||||
|
||||
int m_lastPoint;
|
||||
int m_lastGainPoint;
|
||||
int m_lastKneePoint = 0;
|
||||
|
||||
int m_windowSizeX = size().width();
|
||||
int m_windowSizeY = size().height();
|
||||
int m_kneeWindowSizeX = m_windowSizeY;
|
||||
int m_kneeWindowSizeY = m_windowSizeY;
|
||||
int m_controlsBoxX = 0;
|
||||
int m_controlsBoxY = 0;
|
||||
|
||||
float m_dbRange = 36;
|
||||
|
||||
QColor m_inVolAreaColor = QColor(209, 216, 228, 17);
|
||||
QColor m_inVolColor = QColor(209, 216, 228, 100);
|
||||
QColor m_outVolAreaColor = QColor(209, 216, 228, 30);
|
||||
QColor m_outVolColor = QColor(209, 216, 228, 240);
|
||||
QColor m_gainReductionColor = QColor(180, 100, 100, 210);
|
||||
QColor m_kneeColor = QColor(39, 171, 95, 255);
|
||||
QColor m_kneeColor2 = QColor(9, 171, 160, 255);
|
||||
QColor m_threshColor = QColor(39, 171, 95, 100);
|
||||
QColor m_textColor = QColor(209, 216, 228, 50);
|
||||
QColor m_graphColor = QColor(209, 216, 228, 50);
|
||||
QColor m_resetColor = QColor(200, 100, 15, 200);
|
||||
|
||||
float m_peakAvg;
|
||||
float m_gainAvg;
|
||||
|
||||
float m_yPoint;
|
||||
float m_yGainPoint;
|
||||
|
||||
int m_threshYPoint;
|
||||
int m_threshXPoint;
|
||||
|
||||
int m_compPixelMovement;
|
||||
|
||||
QLabel * m_controlsBoxLabel;
|
||||
QLabel * m_rmsEnabledLabel;
|
||||
QLabel * m_blendEnabledLabel;
|
||||
QLabel * m_lookaheadEnabledLabel;
|
||||
QLabel * m_ratioEnabledLabel;
|
||||
|
||||
Knob * m_thresholdKnob;
|
||||
Knob * m_ratioKnob;
|
||||
Knob * m_attackKnob;
|
||||
Knob * m_releaseKnob;
|
||||
Knob * m_kneeKnob;
|
||||
Knob * m_rangeKnob;
|
||||
Knob * m_lookaheadLengthKnob;
|
||||
Knob * m_holdKnob;
|
||||
|
||||
Knob * m_rmsKnob;
|
||||
Knob * m_inBalanceKnob;
|
||||
Knob * m_outBalanceKnob;
|
||||
Knob * m_stereoBalanceKnob;
|
||||
Knob * m_blendKnob;
|
||||
Knob * m_tiltKnob;
|
||||
Knob * m_tiltFreqKnob;
|
||||
Knob * m_mixKnob;
|
||||
|
||||
Knob * m_autoAttackKnob;
|
||||
Knob * m_autoReleaseKnob;
|
||||
|
||||
EqFader * m_outFader;
|
||||
EqFader * m_inFader;
|
||||
|
||||
PixmapButton * rmsButton;
|
||||
PixmapButton * peakButton;
|
||||
automatableButtonGroup * rmsPeakGroup;
|
||||
|
||||
PixmapButton * leftRightButton;
|
||||
PixmapButton * midSideButton;
|
||||
automatableButtonGroup * leftRightMidSideGroup;
|
||||
|
||||
PixmapButton * compressButton;
|
||||
PixmapButton * limitButton;
|
||||
automatableButtonGroup * compressLimitGroup;
|
||||
|
||||
PixmapButton * unlinkedButton;
|
||||
PixmapButton * maximumButton;
|
||||
PixmapButton * averageButton;
|
||||
PixmapButton * minimumButton;
|
||||
PixmapButton * blendButton;
|
||||
automatableButtonGroup * stereoLinkGroup;
|
||||
|
||||
PixmapButton * autoMakeupButton;
|
||||
PixmapButton * auditionButton;
|
||||
PixmapButton * feedbackButton;
|
||||
PixmapButton * lookaheadButton;
|
||||
|
||||
QElapsedTimer m_timeElapsed;
|
||||
int m_timeSinceLastUpdate = 0;
|
||||
|
||||
friend class CompressorControls;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
148
plugins/Compressor/CompressorControls.cpp
Executable file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* CompressorControls.cpp
|
||||
*
|
||||
* Copyright (c) 2020 Lost Robot <r94231@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "CompressorControls.h"
|
||||
#include "Compressor.h"
|
||||
|
||||
#include <QDomElement>
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
CompressorControls::CompressorControls(CompressorEffect* effect) :
|
||||
EffectControls(effect),
|
||||
m_effect(effect),
|
||||
m_thresholdModel(-8.0f, -60.0f, 0.0f, 0.001f, this, tr("Threshold")),
|
||||
m_ratioModel(1.8f, 1.0f, 20.0f, 0.001f, this, tr("Ratio")),
|
||||
m_attackModel(10.0f, 0.005f, 250.f, 0.001f, this, tr("Attack")),
|
||||
m_releaseModel(100.0f, 1.f, 2500.f, 0.001f, this, tr("Release")),
|
||||
m_kneeModel(12.0f, 0.0f, 96.0f, 0.01f, this, tr("Knee")),
|
||||
m_holdModel(0.0f, 0.0f, 500.0f, 0.01f, this, tr("Hold")),
|
||||
m_rangeModel(-240.0f, -240.0f, 0.0f, 0.01f, this, tr("Range")),
|
||||
m_rmsModel(1.0f, 0.0f, 250.0f, 0.01f, this, tr("RMS Size")),
|
||||
m_midsideModel(0.0f, 0.0f, 1.0f, this, tr("Mid/Side")),
|
||||
m_peakmodeModel(0.0f, 0.0f, 1.0f, this, tr("Peak Mode")),
|
||||
m_lookaheadLengthModel(0.0f, 0.0f, 20.0f, 0.0001f, this, tr("Lookahead Length")),
|
||||
m_inBalanceModel(0.0f, -1.0f, 1.0f, 0.0001f, this, tr("Input Balance")),
|
||||
m_outBalanceModel(0.0f, -1.0f, 1.0f, 0.0001f, this, tr("Output Balance")),
|
||||
m_limiterModel(0.f, 0.f, 1.0f, this, tr("Limiter")),
|
||||
m_outGainModel(0.f, -60.f, 30.f, 0.01f, this, tr("Output Gain")),
|
||||
m_inGainModel(0.f, -60.f, 30.f, 0.01f, this, tr("Input Gain")),
|
||||
m_blendModel(1.f, 0.f, 3.f, 0.0001f, this, tr("Blend")),
|
||||
m_stereoBalanceModel(0.0f, -1.0f, 1.0f, 0.0001f, this, tr("Stereo Balance")),
|
||||
m_autoMakeupModel(false, this, tr("Auto Makeup Gain")),
|
||||
m_auditionModel(false, this, tr("Audition")),
|
||||
m_feedbackModel(false, this, tr("Feedback")),
|
||||
m_autoAttackModel(0.0f, 0.f, 100.0f, 0.01f, this, tr("Auto Attack")),
|
||||
m_autoReleaseModel(0.0f, 0.f, 100.0f, 0.01f, this, tr("Auto Release")),
|
||||
m_lookaheadModel(false, this, tr("Lookahead")),
|
||||
m_tiltModel(0.0f, -6.0f, 6.0f, 0.0001f, this, tr("Tilt")),
|
||||
m_tiltFreqModel(150.0f, 20.0f, 20000.0f, 0.1f, this, tr("Tilt Frequency")),
|
||||
m_stereoLinkModel(1.0f, 0.0f, 4.0f, this, tr("Stereo Link")),
|
||||
m_mixModel(100.0f, 0.f, 100.0f, 0.01f, this, tr("Mix"))
|
||||
{
|
||||
m_ratioModel.setScaleLogarithmic(true);
|
||||
m_holdModel.setScaleLogarithmic(true);
|
||||
m_attackModel.setScaleLogarithmic(true);
|
||||
m_releaseModel.setScaleLogarithmic(true);
|
||||
m_thresholdModel.setScaleLogarithmic(true);
|
||||
m_rangeModel.setScaleLogarithmic(true);
|
||||
m_lookaheadLengthModel.setScaleLogarithmic(true);
|
||||
m_rmsModel.setScaleLogarithmic(true);
|
||||
m_kneeModel.setScaleLogarithmic(true);
|
||||
m_tiltFreqModel.setScaleLogarithmic(true);
|
||||
m_rangeModel.setScaleLogarithmic(true);
|
||||
}
|
||||
|
||||
|
||||
void CompressorControls::saveSettings(QDomDocument& doc, QDomElement& _this)
|
||||
{
|
||||
m_thresholdModel.saveSettings(doc, _this, "threshold");
|
||||
m_ratioModel.saveSettings(doc, _this, "ratio");
|
||||
m_attackModel.saveSettings(doc, _this, "attack");
|
||||
m_releaseModel.saveSettings(doc, _this, "release");
|
||||
m_kneeModel.saveSettings(doc, _this, "knee");
|
||||
m_holdModel.saveSettings(doc, _this, "hold");
|
||||
m_rangeModel.saveSettings(doc, _this, "range");
|
||||
m_rmsModel.saveSettings(doc, _this, "rms");
|
||||
m_midsideModel.saveSettings(doc, _this, "midside");
|
||||
m_peakmodeModel.saveSettings(doc, _this, "peakmode");
|
||||
m_lookaheadLengthModel.saveSettings(doc, _this, "lookaheadLength");
|
||||
m_inBalanceModel.saveSettings(doc, _this, "inBalance");
|
||||
m_outBalanceModel.saveSettings(doc, _this, "outBalance");
|
||||
m_limiterModel.saveSettings(doc, _this, "limiter");
|
||||
m_outGainModel.saveSettings(doc, _this, "outGain");
|
||||
m_inGainModel.saveSettings(doc, _this, "inGain");
|
||||
m_blendModel.saveSettings(doc, _this, "blend");
|
||||
m_stereoBalanceModel.saveSettings(doc, _this, "stereoBalance");
|
||||
m_autoMakeupModel.saveSettings(doc, _this, "autoMakeup");
|
||||
m_auditionModel.saveSettings(doc, _this, "audition");
|
||||
m_feedbackModel.saveSettings(doc, _this, "feedback");
|
||||
m_autoAttackModel.saveSettings(doc, _this, "autoAttack");
|
||||
m_autoReleaseModel.saveSettings(doc, _this, "autoRelease");
|
||||
m_lookaheadModel.saveSettings(doc, _this, "lookahead");
|
||||
m_tiltModel.saveSettings(doc, _this, "tilt");
|
||||
m_tiltFreqModel.saveSettings(doc, _this, "tiltFreq");
|
||||
m_stereoLinkModel.saveSettings(doc, _this, "stereoLink");
|
||||
m_mixModel.saveSettings(doc, _this, "mix");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CompressorControls::loadSettings(const QDomElement& _this)
|
||||
{
|
||||
m_thresholdModel.loadSettings(_this, "threshold");
|
||||
m_ratioModel.loadSettings(_this, "ratio");
|
||||
m_attackModel.loadSettings(_this, "attack");
|
||||
m_releaseModel.loadSettings(_this, "release");
|
||||
m_kneeModel.loadSettings(_this, "knee");
|
||||
m_holdModel.loadSettings(_this, "hold");
|
||||
m_rangeModel.loadSettings(_this, "range");
|
||||
m_rmsModel.loadSettings(_this, "rms");
|
||||
m_midsideModel.loadSettings(_this, "midside");
|
||||
m_peakmodeModel.loadSettings(_this, "peakmode");
|
||||
m_lookaheadLengthModel.loadSettings(_this, "lookaheadLength");
|
||||
m_inBalanceModel.loadSettings(_this, "inBalance");
|
||||
m_outBalanceModel.loadSettings(_this, "outBalance");
|
||||
m_limiterModel.loadSettings(_this, "limiter");
|
||||
m_outGainModel.loadSettings(_this, "outGain");
|
||||
m_inGainModel.loadSettings(_this, "inGain");
|
||||
m_blendModel.loadSettings(_this, "blend");
|
||||
m_stereoBalanceModel.loadSettings(_this, "stereoBalance");
|
||||
m_autoMakeupModel.loadSettings(_this, "autoMakeup");
|
||||
m_auditionModel.loadSettings(_this, "audition");
|
||||
m_feedbackModel.loadSettings(_this, "feedback");
|
||||
m_autoAttackModel.loadSettings(_this, "autoAttack");
|
||||
m_autoReleaseModel.loadSettings(_this, "autoRelease");
|
||||
m_lookaheadModel.loadSettings(_this, "lookahead");
|
||||
m_tiltModel.loadSettings(_this, "tilt");
|
||||
m_tiltFreqModel.loadSettings(_this, "tiltFreq");
|
||||
m_stereoLinkModel.loadSettings(_this, "stereoLink");
|
||||
m_mixModel.loadSettings(_this, "mix");
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
106
plugins/Compressor/CompressorControls.h
Executable file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* CompressorControls.h
|
||||
*
|
||||
* Copyright (c) 2020 Lost Robot <r94231@gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPRESSOR_CONTROLS_H
|
||||
#define COMPRESSOR_CONTROLS_H
|
||||
|
||||
#include "CompressorControlDialog.h"
|
||||
|
||||
#include "EffectControls.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class CompressorEffect;
|
||||
|
||||
|
||||
class CompressorControls : public EffectControls
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CompressorControls(CompressorEffect* effect);
|
||||
|
||||
void saveSettings(QDomDocument & _doc, QDomElement & _parent) override;
|
||||
void loadSettings(const QDomElement & _this) override;
|
||||
inline QString nodeName() const override
|
||||
{
|
||||
return "CompressorControls";
|
||||
}
|
||||
|
||||
int controlCount() override
|
||||
{
|
||||
return 28;
|
||||
}
|
||||
|
||||
gui::EffectControlDialog* createView() override
|
||||
{
|
||||
return new gui::CompressorControlDialog(this);
|
||||
}
|
||||
|
||||
private:
|
||||
CompressorEffect * m_effect;
|
||||
|
||||
FloatModel m_thresholdModel;
|
||||
FloatModel m_ratioModel;
|
||||
FloatModel m_attackModel;
|
||||
FloatModel m_releaseModel;
|
||||
FloatModel m_kneeModel;
|
||||
FloatModel m_holdModel;
|
||||
FloatModel m_rangeModel;
|
||||
FloatModel m_rmsModel;
|
||||
IntModel m_midsideModel;
|
||||
IntModel m_peakmodeModel;
|
||||
FloatModel m_lookaheadLengthModel;
|
||||
FloatModel m_inBalanceModel;
|
||||
FloatModel m_outBalanceModel;
|
||||
IntModel m_limiterModel;
|
||||
FloatModel m_outGainModel;
|
||||
FloatModel m_inGainModel;
|
||||
FloatModel m_blendModel;
|
||||
FloatModel m_stereoBalanceModel;
|
||||
BoolModel m_autoMakeupModel;
|
||||
BoolModel m_auditionModel;
|
||||
BoolModel m_feedbackModel;
|
||||
FloatModel m_autoAttackModel;
|
||||
FloatModel m_autoReleaseModel;
|
||||
BoolModel m_lookaheadModel;
|
||||
FloatModel m_tiltModel;
|
||||
FloatModel m_tiltFreqModel;
|
||||
IntModel m_stereoLinkModel;
|
||||
FloatModel m_mixModel;
|
||||
|
||||
float m_inPeakL;
|
||||
float m_inPeakR;
|
||||
float m_outPeakL;
|
||||
float m_outPeakR;
|
||||
|
||||
friend class gui::CompressorControlDialog;
|
||||
friend class CompressorEffect;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
BIN
plugins/Compressor/artwork.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
plugins/Compressor/audition_sel.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
plugins/Compressor/audition_unsel.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
plugins/Compressor/autogain_sel.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
plugins/Compressor/autogain_unsel.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
plugins/Compressor/average_sel.png
Executable file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
plugins/Compressor/average_unsel.png
Executable file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
plugins/Compressor/blend_sel.png
Executable file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
plugins/Compressor/blend_unsel.png
Executable file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
plugins/Compressor/compressor_sel.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
plugins/Compressor/compressor_unsel.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
plugins/Compressor/controlsBox.png
Executable file
|
After Width: | Height: | Size: 42 KiB |
BIN
plugins/Compressor/feedback_sel.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
plugins/Compressor/feedback_unsel.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
plugins/Compressor/knob_enabled.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
plugins/Compressor/knob_enabled_large.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
plugins/Compressor/leftright_sel.png
Executable file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
plugins/Compressor/leftright_unsel.png
Executable file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
plugins/Compressor/limiter_sel.png
Executable file
|
After Width: | Height: | Size: 771 B |
BIN
plugins/Compressor/limiter_unsel.png
Executable file
|
After Width: | Height: | Size: 871 B |
0
plugins/dynamics_processor/logo.png → plugins/Compressor/logo.png
Normal file → Executable file
|
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 774 B |
BIN
plugins/Compressor/lookahead_sel.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
plugins/Compressor/lookahead_unsel.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
plugins/Compressor/maximum_sel.png
Executable file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
plugins/Compressor/maximum_unsel.png
Executable file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
plugins/Compressor/midside_sel.png
Executable file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
plugins/Compressor/midside_unsel.png
Executable file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
plugins/Compressor/minimum_sel.png
Executable file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
plugins/Compressor/minimum_unsel.png
Executable file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
plugins/Compressor/peak_sel.png
Executable file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
plugins/Compressor/peak_unsel.png
Executable file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
plugins/Compressor/rms_sel.png
Executable file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
plugins/Compressor/rms_unsel.png
Executable file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
plugins/Compressor/unlinked_sel.png
Executable file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
plugins/Compressor/unlinked_unsel.png
Executable file
|
After Width: | Height: | Size: 8.3 KiB |