diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 2091b3f9a86..55f9b0567b1 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -34,6 +34,7 @@ #include "ValueBuffer.h" #include "MemoryManager.h" #include "ModelVisitor.h" +#include "ControllerConnection.h" // simple way to map a property of a view to a model #define mapPropertyFromModelPtr(type,getfunc,setfunc,modelname) \ @@ -148,7 +149,18 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject template inline T value( int frameOffset = 0 ) const { - if( hasLinkedModels() || m_controllerConnection != NULL ) + if (m_controllerConnection) + { + if (!m_useControllerValue) + { + return castValue(m_value); + } + else + { + return castValue(controllerValue(frameOffset)); + } + } + else if (hasLinkedModels()) { return castValue( controllerValue( frameOffset ) ); } @@ -298,9 +310,15 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject s_periodCounter = 0; } + bool useControllerValue() + { + return m_useControllerValue; + } + public slots: virtual void reset(); void unlinkControllerConnection(); + void setUseControllerValue(bool b = true); protected: @@ -395,6 +413,8 @@ public slots: // prevent several threads from attempting to write the same vb at the same time QMutex m_valueBufferMutex; + bool m_useControllerValue; + signals: void initValueChanged( float val ); void destroyed( jo_id_t id ); diff --git a/include/ControllerConnection.h b/include/ControllerConnection.h index e57cd4db2f5..322f22419d4 100644 --- a/include/ControllerConnection.h +++ b/include/ControllerConnection.h @@ -33,6 +33,7 @@ #include #include +#include "AutomatableModel.h" #include "Controller.h" #include "JournallingObject.h" #include "ValueBuffer.h" @@ -47,7 +48,7 @@ class LMMS_EXPORT ControllerConnection : public QObject, public JournallingObjec Q_OBJECT public: - ControllerConnection( Controller * _controller ); + ControllerConnection(Controller * _controller, AutomatableModel * contmod); ControllerConnection( int _controllerId ); virtual ~ControllerConnection(); @@ -98,7 +99,6 @@ class LMMS_EXPORT ControllerConnection : public QObject, public JournallingObjec return classNodeName(); } - public slots: void deleteConnection(); @@ -112,6 +112,8 @@ public slots: static ControllerConnectionVector s_connections; + AutomatableModel * m_controlledModel; + signals: // The value changed while the mixer isn't running (i.e: MIDI CC) void valueChanged(); diff --git a/include/Song.h b/include/Song.h index 3e3405d22da..09ca35f6b9c 100644 --- a/include/Song.h +++ b/include/Song.h @@ -470,6 +470,8 @@ private slots: TimePos m_exportSongEnd; TimePos m_exportEffectiveLength; + AutomatedValueMap m_oldAutomatedValues; + friend class LmmsCore; friend class SongEditor; friend class mainWindow; diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index f66706a3b94..9e6cb3cef3f 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -53,7 +53,8 @@ AutomatableModel::AutomatableModel( m_controllerConnection( NULL ), m_valueBuffer( static_cast( Engine::mixer()->framesPerPeriod() ) ), m_lastUpdatedPeriod( -1 ), - m_hasSampleExactData( false ) + m_hasSampleExactData(false), + m_useControllerValue(true) { m_value = fittedValue( val ); @@ -214,7 +215,7 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString& } if( thisConnection.isElement() ) { - setControllerConnection( new ControllerConnection( (Controller*)NULL ) ); + setControllerConnection(new ControllerConnection((Controller*)NULL, this)); m_controllerConnection->loadSettings( thisConnection.toElement() ); //m_controllerConnection->setTargetName( displayName() ); } @@ -371,6 +372,8 @@ void AutomatableModel::roundAt( T& value, const T& where ) const void AutomatableModel::setAutomatedValue( const float value ) { + setUseControllerValue(false); + m_oldValue = m_value; ++m_setValueDepth; const float oldValue = m_value; @@ -382,13 +385,13 @@ void AutomatableModel::setAutomatedValue( const float value ) if( oldValue != m_value ) { // notify linked models - for( AutoModelVector::Iterator it = m_linkedModels.begin(); - it != m_linkedModels.end(); ++it ) + for (AutoModelVector::Iterator it = m_linkedModels.begin(); + it != m_linkedModels.end(); ++it) { - if( (*it)->m_setValueDepth < 1 && - (*it)->fittedValue( m_value ) != (*it)->m_value ) + if (!((*it)->controllerConnection()) && (*it)->m_setValueDepth < 1 && + (*it)->fittedValue(m_value) != (*it)->m_value) { - (*it)->setAutomatedValue( value ); + (*it)->setAutomatedValue(value); } } m_valueChanged = true; @@ -584,7 +587,7 @@ float AutomatableModel::controllerValue( int frameOffset ) const } AutomatableModel* lm = m_linkedModels.first(); - if( lm->controllerConnection() ) + if (lm->controllerConnection() && lm->useControllerValue()) { return fittedValue( lm->controllerValue( frameOffset ) ); } @@ -607,7 +610,7 @@ ValueBuffer * AutomatableModel::valueBuffer() float val = m_value; // make sure our m_value doesn't change midway ValueBuffer * vb; - if( m_controllerConnection && m_controllerConnection->getController()->isSampleExact() ) + if (m_controllerConnection && m_useControllerValue && m_controllerConnection->getController()->isSampleExact()) { vb = m_controllerConnection->valueBuffer(); if( vb ) @@ -638,23 +641,28 @@ ValueBuffer * AutomatableModel::valueBuffer() return &m_valueBuffer; } } - AutomatableModel* lm = NULL; - if( hasLinkedModels() ) - { - lm = m_linkedModels.first(); - } - if( lm && lm->controllerConnection() && lm->controllerConnection()->getController()->isSampleExact() ) + + if (!m_controllerConnection) { - vb = lm->valueBuffer(); - float * values = vb->values(); - float * nvalues = m_valueBuffer.values(); - for( int i = 0; i < vb->length(); i++ ) + AutomatableModel* lm = NULL; + if (hasLinkedModels()) { - nvalues[i] = fittedValue( values[i] ); + lm = m_linkedModels.first(); + } + if (lm && lm->controllerConnection() && lm->useControllerValue() && + lm->controllerConnection()->getController()->isSampleExact()) + { + vb = lm->valueBuffer(); + float * values = vb->values(); + float * nvalues = m_valueBuffer.values(); + for (int i = 0; i < vb->length(); i++) + { + nvalues[i] = fittedValue(values[i]); + } + m_lastUpdatedPeriod = s_periodCounter; + m_hasSampleExactData = true; + return &m_valueBuffer; } - m_lastUpdatedPeriod = s_periodCounter; - m_hasSampleExactData = true; - return &m_valueBuffer; } if( m_oldValue != val ) @@ -766,6 +774,20 @@ float AutomatableModel::globalAutomationValueAt( const TimePos& time ) } } +void AutomatableModel::setUseControllerValue(bool b) +{ + if (b) + { + m_useControllerValue = true; + emit dataChanged(); + } + else if (m_controllerConnection && m_useControllerValue) + { + m_useControllerValue = false; + emit dataChanged(); + } +} + float FloatModel::getRoundedValue() const { return qRound( value() / step() ) * step(); diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index e08f15b6348..b8fe4b30244 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -36,10 +36,11 @@ ControllerConnectionVector ControllerConnection::s_connections; -ControllerConnection::ControllerConnection( Controller * _controller ) : +ControllerConnection::ControllerConnection(Controller * _controller, AutomatableModel * contmod) : m_controller( NULL ), m_controllerId( -1 ), - m_ownsController( false ) + m_ownsController(false), + m_controlledModel(contmod) { if( _controller != NULL ) { @@ -121,7 +122,13 @@ void ControllerConnection::setController( Controller * _controller ) } m_ownsController = - ( _controller->type() == Controller::MidiController ); + (_controller->type() == Controller::MidiController); + + connect(Engine::getSong(), SIGNAL(stopped()), + m_controlledModel, SLOT(setUseControllerValue()), + Qt::UniqueConnection); + + m_controlledModel->setUseControllerValue(true); // If we don't own the controller, allow deletion of controller // to delete the connection diff --git a/src/core/Song.cpp b/src/core/Song.cpp index be3d2745ae1..6a17fff2b05 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -94,7 +94,8 @@ Song::Song() : m_elapsedTicks( 0 ), m_elapsedBars( 0 ), m_loopRenderCount(1), - m_loopRenderRemaining(1) + m_loopRenderRemaining(1), + m_oldAutomatedValues() { for(int i = 0; i < Mode_Count; ++i) m_elapsedMilliSeconds[i] = 0; connect( &m_tempoModel, SIGNAL( dataChanged() ), @@ -404,6 +405,21 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp } } + // Checks if an automated model stopped being automated by automation patterns + // so we can move the control back to any connected controller again + if (!m_oldAutomatedValues.isEmpty()) + { + for (auto it = m_oldAutomatedValues.begin(); it != m_oldAutomatedValues.end(); it++) + { + AutomatableModel * am = it.key(); + if (am->controllerConnection() && !values.contains(am)) + { + am->setUseControllerValue(true); + } + } + } + m_oldAutomatedValues = values; + // Apply values for (auto it = values.begin(); it != values.end(); it++) { @@ -411,6 +427,10 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp { it.key()->setAutomatedValue(it.value()); } + else if (!it.key()->useControllerValue()) + { + it.key()->setUseControllerValue(true); + } } } diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp index efd6967cf18..1f26580a408 100644 --- a/src/gui/AutomatableModelView.cpp +++ b/src/gui/AutomatableModelView.cpp @@ -33,6 +33,7 @@ #include "embed.h" #include "GuiApplication.h" #include "MainWindow.h" +#include "Song.h" #include "StringPairDrag.h" #include "Clipboard.h" @@ -228,7 +229,7 @@ void AutomatableModelViewSlots::execConnectionDialog() // New else { - ControllerConnection* cc = new ControllerConnection( d.chosenController() ); + ControllerConnection* cc = new ControllerConnection(d.chosenController(), m); m->setControllerConnection( cc ); //cc->setTargetName( m->displayName() ); } @@ -250,8 +251,12 @@ void AutomatableModelViewSlots::removeConnection() if( m->controllerConnection() ) { + disconnect(Engine::getSong(), SIGNAL(stopped()), + m, SLOT(setUseControllerValue())); + delete m->controllerConnection(); m->setControllerConnection( NULL ); + emit m->dataChanged(); } } @@ -295,7 +300,6 @@ void AutomatableModelViewSlots::pasteFromClipboard() } } - /// Attempt to parse a float from the clipboard static float floatFromClipboard(bool* ok) {