Merge branch 'master' into dynamic-effect-dialog

This commit is contained in:
Hyunjin Song
2022-11-11 11:33:17 +09:00
2534 changed files with 532640 additions and 215432 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
INCLUDE(BuildPlugin)
BUILD_PLUGIN(audiofileprocessor AudioFileProcessor.cpp AudioFileProcessor.h MOCFILES AudioFileProcessor.h EMBEDDED_RESOURCES *.png)

View File

Before

Width:  |  Height:  |  Size: 245 KiB

After

Width:  |  Height:  |  Size: 245 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
INCLUDE(BuildPlugin)
BUILD_PLUGIN(bitinvader BitInvader.cpp BitInvader.h MOCFILES BitInvader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png")

View File

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 434 B

View File

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 352 B

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View 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

File diff suppressed because it is too large Load Diff

384
plugins/CarlaBase/Carla.h Normal file
View 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

View 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

View 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; }

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -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()

View File

@@ -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

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -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()

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View 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
View 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
View 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

View 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

View 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

View 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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
plugins/Compressor/blend_sel.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

View File

Before

Width:  |  Height:  |  Size: 774 B

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
plugins/Compressor/peak_sel.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
plugins/Compressor/peak_unsel.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
plugins/Compressor/rms_sel.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
plugins/Compressor/rms_unsel.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Some files were not shown because too many files have changed in this diff Show More