2 * AutomationPattern.cpp - implementation of class AutomationPattern which
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"
41 AutomationPattern::AutomationPattern( AutomationTrack
* _auto_track
) :
42 trackContentObject( _auto_track
),
43 m_autoTrack( _auto_track
),
45 m_hasAutomation( false )
47 changeLength( midiTime( 1, 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
)
88 for( objectVector::iterator it
= m_objects
.begin();
89 it
!= m_objects
.end(); ++it
)
94 // TODO: Maybe let the user know in some non-annoying way
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
)
127 static FloatModel
_fm( 0, 0, 1, 0.001 );
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
,
153 const bool _quant_pos
)
155 midiTime newTime
= _quant_pos
&& engine::automationEditor() ?
156 note::quantized( _time
,
157 engine::automationEditor()->quantization() ) :
160 m_timeMap
[newTime
] = _value
;
164 for( objectVector::iterator it
= m_objects
.begin();
165 it
!= m_objects
.end(); )
169 ( *it
)->setValue( _value
);
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;
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() );
215 void AutomationPattern::removeValue( const midiTime
& _time
)
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(); )
230 ( *it
)->setValue( val
);
231 if( ( *it
)->initValue
<float>() != val
)
233 m_hasAutomation
= true;
239 it
= m_objects
.erase( it
);
245 getTrack()->type() == track::HiddenAutomationTrack
)
247 changeLength( length() );
257 float AutomationPattern::valueAt( const midiTime
& _time
) const
259 if( m_timeMap
.isEmpty() )
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
)
292 QDomElement element
= _doc
.createElement( "object" );
293 element
.setAttribute( "id", ( *it
)->id() );
294 _this
.appendChild( element
);
302 void AutomationPattern::loadSettings( const QDomElement
& _this
)
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() )
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
)
336 ( *it
)->setValue( m_timeMap
[0] );
340 int len
= _this
.attribute( "len" ).toInt();
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
)
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();
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
)
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
);
445 for( objectVector::const_iterator k
= a
->m_objects
.begin();
446 k
!= a
->m_objects
.end(); ++k
)
456 AutomationPattern
* a
= new AutomationPattern( t
);
457 a
->addObject( _m
, false );
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();
479 AutomationPattern
* a
= dynamic_cast<AutomationPattern
*>( *j
);
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();
503 void AutomationPattern::clear()
505 const float val
= firstObject()->value
<float>();
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"