Skip to content

Commit

Permalink
Play automation pattern when midi controller connected (LMMS#5657)
Browse files Browse the repository at this point in the history
* Play automation pattern when midi controller connected

* empty line removed

* Improved for clarity

* removed midi specific controller logic

* formatting changes

* comments added

Co-authored-by: IanCaio <[email protected]>

Co-authored-by: IanCaio <[email protected]>
  • Loading branch information
serdnab and IanCaio authored Mar 26, 2021
1 parent 28fbfa9 commit 9fb6295
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 32 deletions.
22 changes: 21 additions & 1 deletion include/AutomatableModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -148,7 +149,18 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
template<class T>
inline T value( int frameOffset = 0 ) const
{
if( hasLinkedModels() || m_controllerConnection != NULL )
if (m_controllerConnection)
{
if (!m_useControllerValue)
{
return castValue<T>(m_value);
}
else
{
return castValue<T>(controllerValue(frameOffset));
}
}
else if (hasLinkedModels())
{
return castValue<T>( controllerValue( frameOffset ) );
}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 );
Expand Down
6 changes: 4 additions & 2 deletions include/ControllerConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <QtCore/QObject>
#include <QtCore/QVector>

#include "AutomatableModel.h"
#include "Controller.h"
#include "JournallingObject.h"
#include "ValueBuffer.h"
Expand All @@ -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();
Expand Down Expand Up @@ -98,7 +99,6 @@ class LMMS_EXPORT ControllerConnection : public QObject, public JournallingObjec
return classNodeName();
}


public slots:
void deleteConnection();

Expand All @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions include/Song.h
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,8 @@ private slots:
TimePos m_exportSongEnd;
TimePos m_exportEffectiveLength;

AutomatedValueMap m_oldAutomatedValues;

friend class LmmsCore;
friend class SongEditor;
friend class mainWindow;
Expand Down
68 changes: 45 additions & 23 deletions src/core/AutomatableModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ AutomatableModel::AutomatableModel(
m_controllerConnection( NULL ),
m_valueBuffer( static_cast<int>( Engine::mixer()->framesPerPeriod() ) ),
m_lastUpdatedPeriod( -1 ),
m_hasSampleExactData( false )
m_hasSampleExactData(false),
m_useControllerValue(true)

{
m_value = fittedValue( val );
Expand Down Expand Up @@ -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() );
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 ) );
}
Expand All @@ -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 )
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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<float>() ) * step<float>();
Expand Down
13 changes: 10 additions & 3 deletions src/core/ControllerConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
{
Expand Down Expand Up @@ -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
Expand Down
22 changes: 21 additions & 1 deletion src/core/Song.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() ),
Expand Down Expand Up @@ -404,13 +405,32 @@ 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++)
{
if (! recordedModels.contains(it.key()))
{
it.key()->setAutomatedValue(it.value());
}
else if (!it.key()->useControllerValue())
{
it.key()->setUseControllerValue(true);
}
}
}

Expand Down
8 changes: 6 additions & 2 deletions src/gui/AutomatableModelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "embed.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "Song.h"
#include "StringPairDrag.h"
#include "Clipboard.h"

Expand Down Expand Up @@ -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() );
}
Expand All @@ -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();
}
}

Expand Down Expand Up @@ -295,7 +300,6 @@ void AutomatableModelViewSlots::pasteFromClipboard()
}
}


/// Attempt to parse a float from the clipboard
static float floatFromClipboard(bool* ok)
{
Expand Down

0 comments on commit 9fb6295

Please sign in to comment.