diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 10ee252e9..206cdafa8 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -29,6 +29,7 @@ #include "JournallingObject.h" #include "Model.h" +#include "MidiTime.h" // simple way to map a property of a view to a model @@ -239,6 +240,7 @@ public: return m_hasLinkedModels; } + float globalAutomationValueAt( const MidiTime& time ); public slots: virtual void reset(); diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index aea64947e..8b9cc6d14 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -152,6 +152,7 @@ public: static bool isAutomated( const AutomatableModel * _m ); + static QVector patternsForModel( const AutomatableModel * _m ); static AutomationPattern * globalAutomationPattern( AutomatableModel * _m ); static void resolveAllIDs(); diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index a647b3451..7b962252b 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -555,6 +555,70 @@ void AutomatableModel::pasteValue() +float AutomatableModel::globalAutomationValueAt( const MidiTime& time ) +{ + // get patterns that connect to this model + QVector patterns = AutomationPattern::patternsForModel( this ); + if( patterns.isEmpty() ) + { + // if no such patterns exist, return current value + return m_value; + } + else + { + // of those patterns: + // find the patterns which overlap with the miditime position + QVector patterns_in_range; + for( QVector::ConstIterator it = patterns.begin(); it != patterns.end(); it++ ) + { + int s = ( *it )->startPosition(); + int e = ( *it )->endPosition(); + if( s <= time && e >= time ) { patterns_in_range += ( *it ); } + } + + AutomationPattern * latest_pattern = NULL; + + if( ! patterns_in_range.isEmpty() ) + { + // if there are more than one overlapping patterns, just use the first one because + // multiple pattern behaviour is undefined anyway + latest_pattern = patterns_in_range[0]; + } + else + // if we find no patterns at the exact miditime, we need to search for the last pattern before time and use that + { + int latest_position = 0; + + for( QVector::ConstIterator it = patterns.begin(); it != patterns.end(); it++ ) + { + int e = ( *it )->endPosition(); + if( e <= time && e > latest_position ) + { + latest_position = e; + latest_pattern = ( *it ); + } + } + } + + if( latest_pattern ) + { + // scale/fit the value appropriately and return it + const float value = latest_pattern->valueAt( time ); + const float scaled_value = + ( m_scaleType == Linear ) + ? value + : logToLinearScale( + // fits value into [0,1]: + (value - minValue()) / maxValue() + ); + return fittedValue( scaled_value ); + } + // if we still find no pattern, the value at that time is undefined so + // just return current value as the best we can do + else return m_value; + } +} + #include "moc_AutomatableModel.cxx" diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 49f154732..94025a389 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -551,6 +551,51 @@ bool AutomationPattern::isAutomated( const AutomatableModel * _m ) } +/*! \brief returns a list of all the automation patterns everywhere that are connected to a specific model + * \param _m the model we want to look for + */ +QVector AutomationPattern::patternsForModel( const AutomatableModel * _m ) +{ + QVector patterns; + TrackContainer::TrackList l; + l += engine::getSong()->tracks(); + l += engine::getBBTrackContainer()->tracks(); + l += engine::getSong()->globalAutomationTrack(); + + // go through all tracks... + for( TrackContainer::TrackList::ConstIterator it = l.begin(); it != l.end(); ++it ) + { + // we want only automation tracks... + if( ( *it )->type() == track::AutomationTrack || + ( *it )->type() == track::HiddenAutomationTrack ) + { + // get patterns in those tracks.... + const track::tcoVector & v = ( *it )->getTCOs(); + // go through all the patterns... + for( track::tcoVector::ConstIterator j = v.begin(); j != v.end(); ++j ) + { + AutomationPattern * a = dynamic_cast( *j ); + // check that the pattern has automation + if( a && a->hasAutomation() ) + { + // now check is the pattern is connected to the model we want by going through all the connections + // of the pattern + bool has_object = false; + for( objectVector::const_iterator k = a->m_objects.begin(); k != a->m_objects.end(); ++k ) + { + if( *k == _m ) + { + has_object = true; + } + } + // if the patterns is connected to the model, add it to the list + if( has_object ) { patterns += a; } + } + } + } + } + return patterns; +}