Renamed all automation related files and classes to match new coding style
[lmms.git] / src / core / AutomationPattern.cpp
blobf6ee0c0963c86b4992109fc837a74180b86d16d7
1 /*
2 * AutomationPattern.cpp - implementation of class AutomationPattern which
3 * holds dynamic values
5 * Copyright (c) 2008-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
6 * Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
8 * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public
21 * License along with this program (see COPYING); if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301 USA.
27 #include <QtXml/QDomElement>
28 #include <QtGui/QMouseEvent>
29 #include <QtGui/QPainter>
31 #include "AutomationPattern.h"
32 #include "AutomationPatternView.h"
33 #include "AutomationEditor.h"
34 #include "AutomationTrack.h"
35 #include "ProjectJournal.h"
36 #include "bb_track_container.h"
37 #include "song.h"
41 AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) :
42 trackContentObject( _auto_track ),
43 m_autoTrack( _auto_track ),
44 m_objects(),
45 m_hasAutomation( false )
47 changeLength( midiTime( 1, 0 ) );
48 m_timeMap[0] = 0;
54 AutomationPattern::AutomationPattern( const AutomationPattern & _pat_to_copy ) :
55 trackContentObject( _pat_to_copy.m_autoTrack ),
56 m_autoTrack( _pat_to_copy.m_autoTrack ),
57 m_objects( _pat_to_copy.m_objects ),
58 m_hasAutomation( _pat_to_copy.m_hasAutomation )
60 for( timeMap::const_iterator it = _pat_to_copy.m_timeMap.begin();
61 it != _pat_to_copy.m_timeMap.end(); ++it )
63 m_timeMap[it.key()] = it.value();
70 AutomationPattern::~AutomationPattern()
72 if( engine::automationEditor() &&
73 engine::automationEditor()->currentPattern() == this )
75 engine::automationEditor()->setCurrentPattern( NULL );
82 void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup )
84 bool addIt = true;
86 if( _search_dup )
88 for( objectVector::iterator it = m_objects.begin();
89 it != m_objects.end(); ++it )
91 if( *it == _obj )
93 // Already exists
94 // TODO: Maybe let the user know in some non-annoying way
95 addIt = false;
96 break;
101 if( addIt )
103 m_objects += _obj;
104 // been empty before?
105 if( m_objects.size() == 1 && !hasAutomation() )
107 // then initialize default-value
108 putValue( 0, _obj->value<float>(), false );
110 connect( _obj, SIGNAL( destroyed( jo_id_t ) ),
111 this, SLOT( objectDestroyed( jo_id_t ) ),
112 Qt::DirectConnection );
119 const AutomatableModel * AutomationPattern::firstObject() const
121 AutomatableModel * m;
122 if( !m_objects.isEmpty() && ( m = m_objects.first() ) != NULL )
124 return m;
127 static FloatModel _fm( 0, 0, 1, 0.001 );
128 return &_fm;
135 //TODO: Improve this
136 midiTime AutomationPattern::length() const
138 tick_t max_length = 0;
140 for( timeMap::const_iterator it = m_timeMap.begin();
141 it != m_timeMap.end(); ++it )
143 max_length = qMax<tick_t>( max_length, it.key() );
145 return midiTime( qMax( midiTime( max_length ).getTact() + 1, 1 ), 0 );
151 midiTime AutomationPattern::putValue( const midiTime & _time,
152 const float _value,
153 const bool _quant_pos )
155 midiTime newTime = _quant_pos && engine::automationEditor() ?
156 note::quantized( _time,
157 engine::automationEditor()->quantization() ) :
158 _time;
160 m_timeMap[newTime] = _value;
162 if( newTime == 0 )
164 for( objectVector::iterator it = m_objects.begin();
165 it != m_objects.end(); )
167 if( *it )
169 ( *it )->setValue( _value );
170 ++it;
172 else
174 it = m_objects.erase( it );
179 // just one automation value?
180 if( m_timeMap.size() == 1 )
182 m_hasAutomation = m_objects.isEmpty(); // usually false
183 for( objectVector::iterator it = m_objects.begin();
184 it != m_objects.end(); ++it )
186 // default value differs from current value?
187 if( *it && _value != ( *it )->initValue<float>() )
189 // then enable automating this object
190 m_hasAutomation = true;
194 else
196 // in all other cases assume we have automation
197 m_hasAutomation = true;
200 // we need to maximize our length in case we're part of a hidden
201 // automation track as the user can't resize this pattern
202 if( getTrack() && getTrack()->type() == track::HiddenAutomationTrack )
204 changeLength( length() );
207 emit dataChanged();
209 return newTime;
215 void AutomationPattern::removeValue( const midiTime & _time )
217 if( _time != 0 )
219 m_timeMap.remove( _time );
221 if( m_timeMap.size() == 1 )
223 const float val = m_timeMap[0];
224 m_hasAutomation = false;
225 for( objectVector::iterator it = m_objects.begin();
226 it != m_objects.end(); )
228 if( *it )
230 ( *it )->setValue( val );
231 if( ( *it )->initValue<float>() != val )
233 m_hasAutomation = true;
235 ++it;
237 else
239 it = m_objects.erase( it );
244 if( getTrack() &&
245 getTrack()->type() == track::HiddenAutomationTrack )
247 changeLength( length() );
250 emit dataChanged();
257 float AutomationPattern::valueAt( const midiTime & _time ) const
259 if( m_timeMap.isEmpty() )
261 return 0;
263 timeMap::const_iterator v = m_timeMap.lowerBound( _time );
264 // lowerBound returns next value with greater key, therefore we take
265 // the previous element to get the current value
266 return ( v != m_timeMap.begin() ) ? (v-1).value() : v.value();
272 void AutomationPattern::saveSettings( QDomDocument & _doc, QDomElement & _this )
274 _this.setAttribute( "pos", startPosition() );
275 _this.setAttribute( "len", trackContentObject::length() );
276 _this.setAttribute( "name", name() );
278 for( timeMap::const_iterator it = m_timeMap.begin();
279 it != m_timeMap.end(); ++it )
281 QDomElement element = _doc.createElement( "time" );
282 element.setAttribute( "pos", it.key() );
283 element.setAttribute( "value", it.value() );
284 _this.appendChild( element );
287 for( objectVector::const_iterator it = m_objects.begin();
288 it != m_objects.end(); ++it )
290 if( *it )
292 QDomElement element = _doc.createElement( "object" );
293 element.setAttribute( "id", ( *it )->id() );
294 _this.appendChild( element );
302 void AutomationPattern::loadSettings( const QDomElement & _this )
304 clear();
306 movePosition( _this.attribute( "pos" ).toInt() );
307 setName( _this.attribute( "name" ) );
309 for( QDomNode node = _this.firstChild(); !node.isNull();
310 node = node.nextSibling() )
312 QDomElement element = node.toElement();
313 if( element.isNull() )
315 continue;
317 if( element.tagName() == "time" )
319 m_timeMap[element.attribute( "pos" ).toInt()]
320 = element.attribute( "value" ).toFloat();
322 else if( element.tagName() == "object" )
324 m_idsToResolve << element.attribute( "id" ).toInt();
328 m_hasAutomation = m_timeMap.size() > 1;
329 if( m_hasAutomation == false )
331 for( objectVector::iterator it = m_objects.begin();
332 it != m_objects.end(); ++it )
334 if( *it )
336 ( *it )->setValue( m_timeMap[0] );
340 int len = _this.attribute( "len" ).toInt();
341 if( len <= 0 )
343 len = length();
345 changeLength( len );
351 const QString AutomationPattern::name() const
353 if( !trackContentObject::name().isEmpty() )
355 return trackContentObject::name();
357 if( !m_objects.isEmpty() && m_objects.first() != NULL )
359 return m_objects.first()->fullDisplayName();
361 return tr( "Drag a control while pressing <Ctrl>" );
367 void AutomationPattern::processMidiTime( const midiTime & _time )
369 if( _time >= 0 && m_hasAutomation )
371 const float val = valueAt( _time );
372 for( objectVector::iterator it = m_objects.begin();
373 it != m_objects.end(); ++it )
375 if( *it )
377 ( *it )->setAutomatedValue( val );
388 trackContentObjectView * AutomationPattern::createView( trackView * _tv )
390 return new AutomationPatternView( this, _tv );
397 bool AutomationPattern::isAutomated( const AutomatableModel * _m )
399 trackContainer::trackList l = engine::getSong()->tracks() +
400 engine::getBBTrackContainer()->tracks();
401 l += engine::getSong()->globalAutomationTrack();
402 for( trackContainer::trackList::const_iterator it = l.begin();
403 it != l.end(); ++it )
405 if( ( *it )->type() == track::AutomationTrack ||
406 ( *it )->type() == track::HiddenAutomationTrack )
408 const track::tcoVector & v = ( *it )->getTCOs();
409 for( track::tcoVector::const_iterator j = v.begin();
410 j != v.end(); ++j )
412 const AutomationPattern * a =
413 dynamic_cast<const AutomationPattern *>( *j );
414 if( a && a->m_hasAutomation )
416 for( objectVector::const_iterator k = a->m_objects.begin();
417 k != a->m_objects.end(); ++k )
419 if( *k == _m )
421 return true;
428 return false;
435 AutomationPattern * AutomationPattern::globalAutomationPattern(
436 AutomatableModel * _m )
438 AutomationTrack * t = engine::getSong()->globalAutomationTrack();
439 track::tcoVector v = t->getTCOs();
440 for( track::tcoVector::const_iterator j = v.begin(); j != v.end(); ++j )
442 AutomationPattern * a = dynamic_cast<AutomationPattern *>( *j );
443 if( a )
445 for( objectVector::const_iterator k = a->m_objects.begin();
446 k != a->m_objects.end(); ++k )
448 if( *k == _m )
450 return a;
456 AutomationPattern * a = new AutomationPattern( t );
457 a->addObject( _m, false );
458 return a;
464 void AutomationPattern::resolveAllIDs()
466 trackContainer::trackList l = engine::getSong()->tracks() +
467 engine::getBBTrackContainer()->tracks();
468 l += engine::getSong()->globalAutomationTrack();
469 for( trackContainer::trackList::iterator it = l.begin();
470 it != l.end(); ++it )
472 if( ( *it )->type() == track::AutomationTrack ||
473 ( *it )->type() == track::HiddenAutomationTrack )
475 track::tcoVector v = ( *it )->getTCOs();
476 for( track::tcoVector::iterator j = v.begin();
477 j != v.end(); ++j )
479 AutomationPattern * a = dynamic_cast<AutomationPattern *>( *j );
480 if( a )
482 for( QVector<jo_id_t>::Iterator k = a->m_idsToResolve.begin();
483 k != a->m_idsToResolve.end(); ++k )
485 JournallingObject * o = engine::projectJournal()->
486 journallingObject( *k );
487 if( o && dynamic_cast<AutomatableModel *>( o ) )
489 a->addObject( dynamic_cast<AutomatableModel *>( o ), false );
492 a->m_idsToResolve.clear();
493 a->dataChanged();
503 void AutomationPattern::clear()
505 const float val = firstObject()->value<float>();
506 m_timeMap.clear();
507 putValue( 0, val );
509 if( engine::automationEditor() &&
510 engine::automationEditor()->currentPattern() == this )
512 engine::automationEditor()->update();
519 void AutomationPattern::openInAutomationEditor()
521 engine::automationEditor()->setCurrentPattern( this );
522 engine::automationEditor()->parentWidget()->show();
523 engine::automationEditor()->setFocus();
529 void AutomationPattern::objectDestroyed( jo_id_t _id )
531 // TODO: distict between temporary removal (e.g. LADSPA controls
532 // when switching samplerate) and real deletions because in the latter
533 // case we had to remove ourselves if we're the global automation
534 // pattern of the destroyed object
535 m_idsToResolve += _id;
541 #include "moc_AutomationPattern.cxx"