Merge branch 'stable-1.1'

Conflicts:
	include/AutomatableModel.h
	include/FxMixer.h
	src/core/FxMixer.cpp
	src/gui/widgets/caption_menu.cpp
	src/tracks/InstrumentTrack.cpp
This commit is contained in:
Vesa
2014-11-16 15:16:40 +02:00
21 changed files with 184 additions and 133 deletions

View File

@@ -83,13 +83,11 @@ QMenu::item:selected {
color: white;
font-weight:bold;
background-color: #747474;
margin:3px;
}
QMenu::item:disabled {
color: #747474;
background-color: #c9c9c9;
margin:0px;
font-size:12px;
font-weight: normal;
padding: 4px 32px 4px 20px;

View File

@@ -25,7 +25,7 @@
#ifndef AUTOMATABLE_MODEL_H
#define AUTOMATABLE_MODEL_H
#include <math.h>
#include "lmms_math.h"
#include <QtCore/QMutex>
#include "JournallingObject.h"
@@ -72,7 +72,8 @@ public:
enum ScaleType
{
Linear,
Logarithmic
Logarithmic,
Decibel
};
enum DataType
@@ -249,6 +250,19 @@ public:
return m_hasLinkedModels;
}
// a way to track changed values in the model and avoid using signals/slots - useful for speed-critical code.
// note that this method should only be called once per period since it resets the state of the variable - so if your model
// has to be accessed by more than one object, then this function shouldn't be used.
bool isValueChanged()
{
if( m_valueChanged )
{
m_valueChanged = false;
return true;
}
return false;
}
float globalAutomationValueAt( const MidiTime& time );
bool hasStrictStepSize() const
@@ -318,6 +332,8 @@ private:
float m_step;
float m_range;
float m_centerValue;
bool m_valueChanged;
// currently unused?
float m_oldValue;

View File

@@ -59,6 +59,7 @@ class FxChannel : public ThreadableJob
QMutex m_lock;
int m_channelIndex; // what channel index are we
bool m_queued; // are we queued up for rendering yet?
bool m_muted; // are we muted? updated per period so we don't have to call m_muteModel.value() twice
// pointers to other channels that this one sends to
FxRouteVector m_sends;
@@ -69,6 +70,11 @@ class FxChannel : public ThreadableJob
virtual bool requiresProcessing() const { return true; }
void unmuteForSolo();
QAtomicInt m_dependenciesMet;
void incrementDeps();
void processed();
private:
virtual void doProcessing( sampleFrame * _working_buffer );
};
@@ -196,10 +202,13 @@ private:
void allocateChannelsTo(int num);
QMutex m_sendsMutex;
<<<<<<< HEAD
void addChannelLeaf( FxChannel * ch, sampleFrame * buf );
int m_lastSoloed;
=======
>>>>>>> stable-1.1
friend class MixerWorkerThread;
friend class FxMixerView;

View File

@@ -61,6 +61,11 @@ public:
{
m_state = Queued;
}
inline void done()
{
m_state = Done;
}
void process( sampleFrame* workingBuffer = NULL )
{

View File

@@ -38,6 +38,12 @@ public:
captionMenu( const QString & _title, QWidget * _parent = 0 );
virtual ~captionMenu();
///
/// \brief Adds a "Help" action displaying the Menu's parent's WhatsThis
/// text when selected.
///
void addHelpAction();
} ;

View File

@@ -52,6 +52,9 @@ BassBoosterEffect::BassBoosterEffect( Model* parent, const Descriptor::SubPlugin
m_bbFX( DspEffectLibrary::FastBassBoost( 70.0f, 1.0f, 2.8f ) ),
m_bbControls( this )
{
changeFrequency();
changeGain();
changeRatio();
}
@@ -70,6 +73,10 @@ bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames
{
return( false );
}
// check out changed controls
if( m_bbControls.m_freqModel.isValueChanged() ) { changeFrequency(); }
if( m_bbControls.m_gainModel.isValueChanged() ) { changeGain(); }
if( m_bbControls.m_ratioModel.isValueChanged() ) { changeRatio(); }
double outSum = 0.0;
const float d = dryLevel();
@@ -91,6 +98,32 @@ bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames
}
inline void BassBoosterEffect::changeFrequency()
{
const sample_t fac = engine::mixer()->processingSampleRate() / 44100.0f;
m_bbFX.leftFX().setFrequency( m_bbControls.m_freqModel.value() * fac );
m_bbFX.rightFX().setFrequency( m_bbControls.m_freqModel.value() * fac );
}
inline void BassBoosterEffect::changeGain()
{
m_bbFX.leftFX().setGain( m_bbControls.m_gainModel.value() );
m_bbFX.rightFX().setGain( m_bbControls.m_gainModel.value() );
}
inline void BassBoosterEffect::changeRatio()
{
m_bbFX.leftFX().setRatio( m_bbControls.m_ratioModel.value() );
m_bbFX.rightFX().setRatio( m_bbControls.m_ratioModel.value() );
}

View File

@@ -23,8 +23,8 @@
*/
#ifndef _BASS_BOOSTER_H
#define _BASS_BOOSTER_H
#ifndef BASS_BOOSTER_H
#define BASS_BOOSTER_H
#include "Effect.h"
#include "DspEffectLibrary.h"
@@ -45,6 +45,10 @@ public:
private:
void changeFrequency();
void changeGain();
void changeRatio();
DspEffectLibrary::MonoToStereoAdaptor<DspEffectLibrary::FastBassBoost> m_bbFX;
BassBoosterControls m_bbControls;

View File

@@ -37,48 +37,17 @@ 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( &m_freqModel, SIGNAL( dataChanged() ), this, SLOT( changeFrequency() ) );
connect( &m_gainModel, SIGNAL( dataChanged() ), this, SLOT( changeGain() ) );
connect( &m_ratioModel, SIGNAL( dataChanged() ), this, SLOT( changeRatio() ) );
connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changeFrequency() ) );
changeFrequency();
changeGain();
changeRatio();
}
void BassBoosterControls::changeFrequency()
{
const sample_t fac = engine::mixer()->processingSampleRate() / 44100.0f;
m_effect->m_bbFX.leftFX().setFrequency( m_freqModel.value() * fac );
m_effect->m_bbFX.rightFX().setFrequency( m_freqModel.value() * fac );
m_effect->changeFrequency();
}
void BassBoosterControls::changeGain()
{
m_effect->m_bbFX.leftFX().setGain( m_gainModel.value() );
m_effect->m_bbFX.rightFX().setGain( m_gainModel.value() );
}
void BassBoosterControls::changeRatio()
{
m_effect->m_bbFX.leftFX().setRatio( m_ratioModel.value() );
m_effect->m_bbFX.rightFX().setRatio( m_ratioModel.value() );
}
void BassBoosterControls::loadSettings( const QDomElement& _this )
{
m_freqModel.loadSettings( _this, "freq" );

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef _BASSBOOSTER_CONTROLS_H
#define _BASSBOOSTER_CONTROLS_H
#ifndef BASSBOOSTER_CONTROLS_H
#define BASSBOOSTER_CONTROLS_H
#include "EffectControls.h"
#include "BassBoosterControlDialog.h"
@@ -62,9 +62,6 @@ public:
private slots:
void changeFrequency();
void changeGain();
void changeRatio();
private:
BassBoosterEffect* m_effect;
@@ -73,7 +70,7 @@ private:
FloatModel m_ratioModel;
friend class BassBoosterControlDialog;
friend class BassBoosterEffect;
} ;
#endif

View File

@@ -251,9 +251,8 @@ void nineButtonSelector::updateButton( int _new_button )
void nineButtonSelector::contextMenuEvent( QContextMenuEvent * )
{
captionMenu contextMenu( windowTitle() );
contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ),
this, SLOT( displayHelp() ) );
captionMenu contextMenu( windowTitle(), this );
contextMenu.addHelpAction();
contextMenu.exec( QCursor::pos() );
}

View File

@@ -756,9 +756,8 @@ void vibedView::normalizeClicked()
void vibedView::contextMenuEvent( QContextMenuEvent * )
{
captionMenu contextMenu( model()->displayName() );
contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ),
this, SLOT( displayHelp() ) );
captionMenu contextMenu( model()->displayName(), this );
contextMenu.addHelpAction();
contextMenu.exec( QCursor::pos() );
}

View File

@@ -47,6 +47,7 @@ AutomatableModel::AutomatableModel( DataType type,
m_step( step ),
m_range( max - min ),
m_centerValue( m_minValue ),
m_valueChanged( false ),
m_setValueDepth( 0 ),
m_hasStrictStepSize( false ),
m_hasLinkedModels( false ),
@@ -240,6 +241,7 @@ void AutomatableModel::setValue( const float value )
(*it)->setJournalling( journalling );
}
}
m_valueChanged = true;
emit dataChanged();
}
else
@@ -334,6 +336,7 @@ void AutomatableModel::setAutomatedValue( const float value )
(*it)->setAutomatedValue( value );
}
}
m_valueChanged = true;
emit dataChanged();
}
--m_setValueDepth;
@@ -478,6 +481,7 @@ void AutomatableModel::setControllerConnection( ControllerConnection* c )
{
QObject::connect( m_controllerConnection, SIGNAL( valueChanged() ), this, SIGNAL( dataChanged() ) );
QObject::connect( m_controllerConnection, SIGNAL( destroyed() ), this, SLOT( unlinkControllerConnection() ) );
m_valueChanged = true;
emit dataChanged();
}
}

View File

@@ -70,7 +70,8 @@ FxChannel::FxChannel( int idx, Model * _parent ) :
m_name(),
m_lock(),
m_channelIndex( idx ),
m_queued( false )
m_queued( false ),
m_dependenciesMet( 0 )
{
engine::mixer()->clearAudioBuffer( m_buffer,
engine::mixer()->framesPerPeriod() );
@@ -85,6 +86,27 @@ FxChannel::~FxChannel()
}
inline void FxChannel::processed()
{
foreach( FxRoute * receiverRoute, m_sends )
{
if( receiverRoute->receiver()->m_muted == false )
{
receiverRoute->receiver()->incrementDeps();
}
}
}
void FxChannel::incrementDeps()
{
m_dependenciesMet.ref();
if( m_dependenciesMet >= m_receives.size() )
{
m_queued = true;
MixerWorkerThread::addJob( this );
m_dependenciesMet = 0;
}
}
void FxChannel::unmuteForSolo()
{
@@ -107,25 +129,14 @@ void FxChannel::doProcessing( sampleFrame * _buf )
// <tobydox> this improves cache hit rate
_buf = m_buffer;
if( m_muteModel.value() == false )
if( m_muted == false )
{
// OK, we are not muted, so we go recursively through all the channels
// which send to us (our children)...
foreach( FxRoute * senderRoute, m_receives )
{
FxChannel * sender = senderRoute->sender();
FloatModel * sendModel = senderRoute->amount();
if( ! sendModel ) qFatal( "Error: no send model found from %d to %d", senderRoute->senderIndex(), m_channelIndex );
// wait for the sender job - either it's just been queued yet,
// then ThreadableJob::process() will process it now within this
// thread - otherwise it has been is is being processed by another
// thread and we just have to wait for it to finish
while( sender->state() != ThreadableJob::Done )
{
sender->process();
}
if( sender->m_hasInput || sender->m_stillRunning )
{
// figure out if we're getting sample-exact input
@@ -158,20 +169,28 @@ void FxChannel::doProcessing( sampleFrame * _buf )
m_hasInput = true;
}
}
const float v = m_volumeModel.value();
if( m_hasInput )
{
// only start fxchain when we have input...
m_fxChain.startRunning();
}
m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp, m_hasInput );
m_peakLeft = qMax( m_peakLeft, engine::mixer()->peakValueLeft( _buf, fpp ) * v );
m_peakRight = qMax( m_peakRight, engine::mixer()->peakValueRight( _buf, fpp ) * v );
}
const float v = m_volumeModel.value();
if( m_hasInput )
else
{
// only start fxchain when we have input...
m_fxChain.startRunning();
m_peakLeft = m_peakRight = 0.0f;
}
m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp, m_hasInput );
m_peakLeft = qMax( m_peakLeft, engine::mixer()->peakValueLeft( _buf, fpp ) * v );
m_peakRight = qMax( m_peakRight, engine::mixer()->peakValueRight( _buf, fpp ) * v );
// increment dependency counter of all receivers
processed();
}
@@ -456,11 +475,9 @@ void FxMixer::deleteChannelSend( FxRoute * route )
bool FxMixer::isInfiniteLoop( fx_ch_t sendFrom, fx_ch_t sendTo )
{
if( sendFrom == sendTo ) return true;
//m_sendsMutex.lock();
FxChannel * from = m_fxChannels[sendFrom];
FxChannel * to = m_fxChannels[sendTo];
bool b = checkInfiniteLoop( from, to );
//m_sendsMutex.unlock();
return b;
}
@@ -538,42 +555,38 @@ void FxMixer::prepareMasterMix()
void FxMixer::addChannelLeaf( FxChannel * ch, sampleFrame * buf )
{
// if we're muted or this channel is seen already, discount it
if( ch->m_queued )
{
return;
}
foreach( FxRoute * senderRoute, ch->m_receives )
{
addChannelLeaf( senderRoute->sender(), buf );
}
// add this channel to job list
ch->m_queued = true;
MixerWorkerThread::addJob( ch );
}
void FxMixer::masterMix( sampleFrame * _buf )
{
const int fpp = engine::mixer()->framesPerPeriod();
// recursively loop through channel dependency chain
// and add all channels to job list that have no dependencies
// when the channel completes it will check its parent to see if it needs
// to be processed.
//m_sendsMutex.lock();
MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
addChannelLeaf( m_fxChannels[0], _buf );
while( m_fxChannels[0]->state() != ThreadableJob::Done )
if( m_sendsMutex.tryLock() )
{
MixerWorkerThread::startAndWaitForJobs();
// add the channels that have no dependencies (no incoming senders, ie. no receives)
// to the jobqueue. The channels that have receives get added when their senders get processed, which
// is detected by dependency counting.
// also instantly add all muted channels as they don't need to care about their senders, and can just increment the deps of
// their recipients right away.
MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
foreach( FxChannel * ch, m_fxChannels )
{
ch->m_muted = ch->m_muteModel.value();
if( ch->m_muted ) // instantly "process" muted channels
{
ch->processed();
ch->done();
}
else if( ch->m_receives.size() == 0 )
{
ch->m_queued = true;
MixerWorkerThread::addJob( ch );
}
}
while( m_fxChannels[0]->state() != ThreadableJob::Done )
{
MixerWorkerThread::startAndWaitForJobs();
}
m_sendsMutex.unlock();
}
//m_sendsMutex.unlock();
// handle sample-exact data in master volume fader
ValueBuffer * volBuf = m_fxChannels[0]->m_volumeModel.valueBuffer();

View File

@@ -1044,7 +1044,7 @@ void trackContentWidget::updateBackground()
// draw lines
pmp.setPen( QPen( QColor( 0, 0, 0, 160 ), 1 ) );
// horizontal line
pmp.drawLine( 0, 0, w*2, 0 );
pmp.drawLine( 0, h-1, w*2, h-1 );
// vertical lines
for( float x = 0; x < w * 2; x += ppt )

View File

@@ -176,14 +176,12 @@ void ControllerView::modelChanged()
void ControllerView::contextMenuEvent( QContextMenuEvent * )
{
QPointer<captionMenu> contextMenu = new captionMenu( model()->displayName() );
QPointer<captionMenu> contextMenu = new captionMenu( model()->displayName(), this );
contextMenu->addAction( embed::getIconPixmap( "cancel" ),
tr( "&Remove this plugin" ),
this, SLOT( deleteController() ) );
contextMenu->addSeparator();
contextMenu->addAction( embed::getIconPixmap( "help" ),
tr( "&Help" ),
this, SLOT( displayHelp() ) );
contextMenu->addHelpAction();
contextMenu->exec( QCursor::pos() );
delete contextMenu;
}

View File

@@ -245,7 +245,7 @@ void EffectView::closeEffects()
void EffectView::contextMenuEvent( QContextMenuEvent * )
{
QPointer<captionMenu> contextMenu = new captionMenu( model()->displayName() );
QPointer<captionMenu> contextMenu = new captionMenu( model()->displayName(), this );
contextMenu->addAction( embed::getIconPixmap( "arp_up" ),
tr( "Move &up" ),
this, SLOT( moveUp() ) );
@@ -257,9 +257,7 @@ void EffectView::contextMenuEvent( QContextMenuEvent * )
tr( "&Remove this plugin" ),
this, SLOT( deletePlugin() ) );
contextMenu->addSeparator();
contextMenu->addAction( embed::getIconPixmap( "help" ),
tr( "&Help" ),
this, SLOT( displayHelp() ) );
contextMenu->addHelpAction();
contextMenu->exec( QCursor::pos() );
delete contextMenu;
}

View File

@@ -183,7 +183,7 @@ void FxLine::mouseDoubleClickEvent( QMouseEvent * )
void FxLine::contextMenuEvent( QContextMenuEvent * )
{
FxMixer * mix = engine::fxMixer();
QPointer<captionMenu> contextMenu = new captionMenu( mix->effectChannel( m_channelIndex )->m_name );
QPointer<captionMenu> contextMenu = new captionMenu( mix->effectChannel( m_channelIndex )->m_name, this );
if( m_channelIndex != 0 ) // no move-options in master
{
contextMenu->addAction( tr( "Move &left" ), this, SLOT( moveChannelLeft() ) );
@@ -199,9 +199,7 @@ void FxLine::contextMenuEvent( QContextMenuEvent * )
contextMenu->addSeparator();
}
contextMenu->addAction( embed::getIconPixmap( "help" ),
tr( "&Help" ),
this, SLOT( displayHelp() ) );
contextMenu->addHelpAction();
contextMenu->exec( QCursor::pos() );
delete contextMenu;
}

View File

@@ -24,6 +24,7 @@
#include "caption_menu.h"
#include "embed.h"
@@ -45,6 +46,19 @@ captionMenu::~captionMenu()
void captionMenu::addHelpAction()
{
QWidget* parent = (QWidget*) this->parent();
if (parent == NULL)
return;
if (! parent->whatsThis().isEmpty()) {
addAction( embed::getIconPixmap( "help" ), tr( "&Help" ),
parent, SLOT( displayHelp() ) );
}
else {
QAction* helpAction = addAction( embed::getIconPixmap("help"), tr("Help (not available)") );
helpAction->setDisabled(true);
}
}

View File

@@ -481,11 +481,10 @@ void knob::contextMenuEvent( QContextMenuEvent * )
// an QApplication::restoreOverrideCursor()-call...
mouseReleaseEvent( NULL );
captionMenu contextMenu( model()->displayName() );
captionMenu contextMenu( model()->displayName(), this );
addDefaultActions( &contextMenu );
contextMenu.addSeparator();
contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ),
this, SLOT( displayHelp() ) );
contextMenu.addHelpAction();
contextMenu.exec( QCursor::pos() );
}

View File

@@ -83,7 +83,7 @@ void TempoSyncKnob::contextMenuEvent( QContextMenuEvent * )
{
mouseReleaseEvent( NULL );
captionMenu contextMenu( model()->displayName() );
captionMenu contextMenu( model()->displayName(), this );
addDefaultActions( &contextMenu );
contextMenu.addSeparator();
@@ -147,9 +147,7 @@ void TempoSyncKnob::contextMenuEvent( QContextMenuEvent * )
}
contextMenu.addSeparator();
contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ),
this, SLOT( displayHelp() ) );
contextMenu.addHelpAction();
contextMenu.exec( QCursor::pos() );
delete syncMenu;

View File

@@ -806,6 +806,7 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement
}
node = node.nextSibling();
}
updatePitchRange();
unlock();
}
@@ -1585,10 +1586,3 @@ void InstrumentTrackWindow::loadSettings( const QDomElement& thisElement )
m_itv->m_tlb->setChecked( true );
}
}