float AutomatableModel::globalAutomationValueAt( const MidiTime& time ) { // get patterns that connect to this model QVector<AutomationPattern *> 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<AutomationPattern *> patternsInRange; for( QVector<AutomationPattern *>::ConstIterator it = patterns.begin(); it != patterns.end(); it++ ) { int s = ( *it )->startPosition(); int e = ( *it )->endPosition(); if( s <= time && e >= time ) { patternsInRange += ( *it ); } } AutomationPattern * latestPattern = NULL; if( ! patternsInRange.isEmpty() ) { // if there are more than one overlapping patterns, just use the first one because // multiple pattern behaviour is undefined anyway latestPattern = patternsInRange[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 latestPosition = 0; for( QVector<AutomationPattern *>::ConstIterator it = patterns.begin(); it != patterns.end(); it++ ) { int e = ( *it )->endPosition(); if( e <= time && e > latestPosition ) { latestPosition = e; latestPattern = ( *it ); } } } if( latestPattern ) { // scale/fit the value appropriately and return it const float value = latestPattern->valueAt( time - latestPattern->startPosition() ); const float scaled_value = scaledValue( value ); 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; } }
smfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value ) { if( !ap || time > lastPos + DefaultTicksPerTact ) { MidiTime pPos = MidiTime( time.getTact(), 0 ); ap = dynamic_cast<AutomationPattern*>( at->createTCO(0) ); ap->movePosition( pPos ); ap->addObject( objModel ); } lastPos = time; time = time - ap->startPosition(); ap->putValue( time, value, false ); ap->changeLength( MidiTime( time.getTact() + 1, 0 ) ); return *this; }
bool AutomationTrack::play( const MidiTime & _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num ) { if( isMuted() ) { return false; } tcoVector tcos; if( _tco_num >= 0 ) { TrackContentObject * tco = getTCO( _tco_num ); tcos.push_back( tco ); } else { getTCOsInRange( tcos, _start, _start + static_cast<int>( _frames / Engine::framesPerTick()) ); } for( tcoVector::iterator it = tcos.begin(); it != tcos.end(); ++it ) { AutomationPattern * p = dynamic_cast<AutomationPattern *>( *it ); if( p == NULL || ( *it )->isMuted() ) { continue; } MidiTime cur_start = _start; if( _tco_num < 0 ) { cur_start -= p->startPosition(); } p->processMidiTime( cur_start ); } return false; }