1 /*
2 * AutomatableModel.cpp - some implementations of AutomatableModel-class
3 *
4 * Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5 *
6 * This file is part of LMMS - https://lmms.io
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program (see COPYING); if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
22 *
23 */
24
25 #include "AutomatableModel.h"
26
27 #include "lmms_math.h"
28
29 #include "AutomationPattern.h"
30 #include "ControllerConnection.h"
31 #include "LocaleHelper.h"
32 #include "Mixer.h"
33 #include "ProjectJournal.h"
34
35 float AutomatableModel::s_copiedValue = 0;
36 long AutomatableModel::s_periodCounter = 0;
37
38
39
AutomatableModel(DataType type,const float val,const float min,const float max,const float step,Model * parent,const QString & displayName,bool defaultConstructed)40 AutomatableModel::AutomatableModel( DataType type,
41 const float val, const float min, const float max, const float step,
42 Model* parent, const QString & displayName, bool defaultConstructed ) :
43 Model( parent, displayName, defaultConstructed ),
44 m_dataType( type ),
45 m_scaleType( Linear ),
46 m_minValue( min ),
47 m_maxValue( max ),
48 m_step( step ),
49 m_range( max - min ),
50 m_centerValue( m_minValue ),
51 m_valueChanged( false ),
52 m_setValueDepth( 0 ),
53 m_hasStrictStepSize( false ),
54 m_hasLinkedModels( false ),
55 m_controllerConnection( NULL ),
56 m_valueBuffer( static_cast<int>( Engine::mixer()->framesPerPeriod() ) ),
57 m_lastUpdatedPeriod( -1 ),
58 m_hasSampleExactData( false )
59
60 {
61 m_value = fittedValue( val );
62 setInitValue( val );
63 }
64
65
66
67
~AutomatableModel()68 AutomatableModel::~AutomatableModel()
69 {
70 while( m_linkedModels.empty() == false )
71 {
72 m_linkedModels.last()->unlinkModel( this );
73 m_linkedModels.erase( m_linkedModels.end() - 1 );
74 }
75
76 if( m_controllerConnection )
77 {
78 delete m_controllerConnection;
79 }
80
81 m_valueBuffer.clear();
82
83 emit destroyed( id() );
84 }
85
86
87
88
isAutomated() const89 bool AutomatableModel::isAutomated() const
90 {
91 return AutomationPattern::isAutomated( this );
92 }
93
94
saveSettings(QDomDocument & doc,QDomElement & element,const QString & name)95 void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, const QString& name )
96 {
97 if( isAutomated() || m_scaleType != Linear )
98 {
99 // automation needs tuple of data (name, id, value)
100 // scale type also needs an extra value
101 // => it must be appended as a node
102 QDomElement me = doc.createElement( name );
103 me.setAttribute( "id", ProjectJournal::idToSave( id() ) );
104 me.setAttribute( "value", m_value );
105 me.setAttribute( "scale_type", m_scaleType == Logarithmic ? "log" : "linear" );
106 element.appendChild( me );
107 }
108 else
109 {
110 // non automation, linear scale (default), can be saved as attribute
111 element.setAttribute( name, m_value );
112 }
113
114 if( m_controllerConnection && m_controllerConnection->getController()->type()
115 != Controller::DummyController )
116 {
117 QDomElement controllerElement;
118
119 // get "connection" element (and create it if needed)
120 QDomNode node = element.namedItem( "connection" );
121 if( node.isElement() )
122 {
123 controllerElement = node.toElement();
124 }
125 else
126 {
127 controllerElement = doc.createElement( "connection" );
128 element.appendChild( controllerElement );
129 }
130
131 QDomElement element = doc.createElement( name );
132 m_controllerConnection->saveSettings( doc, element );
133
134 controllerElement.appendChild( element );
135 }
136 }
137
138
139
140
loadSettings(const QDomElement & element,const QString & name)141 void AutomatableModel::loadSettings( const QDomElement& element, const QString& name )
142 {
143 // compat code
144 QDomNode node = element.namedItem( AutomationPattern::classNodeName() );
145 if( node.isElement() )
146 {
147 node = node.namedItem( name );
148 if( node.isElement() )
149 {
150 AutomationPattern * p = AutomationPattern::globalAutomationPattern( this );
151 p->loadSettings( node.toElement() );
152 setValue( p->valueAt( 0 ) );
153 // in older projects we sometimes have odd automations
154 // with just one value in - eliminate if necessary
155 if( !p->hasAutomation() )
156 {
157 delete p;
158 }
159 return;
160 }
161 // logscales were not existing at this point of time
162 // so they can be ignored
163 }
164
165 QDomNode connectionNode = element.namedItem( "connection" );
166 // reads controller connection
167 if( connectionNode.isElement() )
168 {
169 QDomNode thisConnection = connectionNode.toElement().namedItem( name );
170 if( thisConnection.isElement() )
171 {
172 setControllerConnection( new ControllerConnection( (Controller*)NULL ) );
173 m_controllerConnection->loadSettings( thisConnection.toElement() );
174 //m_controllerConnection->setTargetName( displayName() );
175 }
176 }
177
178 // models can be stored as elements (port00) or attributes (port10):
179 // <ladspacontrols port10="4.41">
180 // <port00 value="4.41" id="4249278"/>
181 // </ladspacontrols>
182 // element => there is automation data, or scaletype information
183 node = element.namedItem( name );
184 if( node.isElement() )
185 {
186 changeID( node.toElement().attribute( "id" ).toInt() );
187 setValue( LocaleHelper::toFloat( node.toElement().attribute( "value" ) ) );
188 if( node.toElement().hasAttribute( "scale_type" ) )
189 {
190 if( node.toElement().attribute( "scale_type" ) == "linear" )
191 {
192 setScaleType( Linear );
193 }
194 else if( node.toElement().attribute( "scale_type" ) == "log" )
195 {
196 setScaleType( Logarithmic );
197 }
198 }
199 }
200 else
201 {
202
203 setScaleType( Linear );
204
205 if( element.hasAttribute( name ) )
206 // attribute => read the element's value from the attribute list
207 {
208 setInitValue( LocaleHelper::toFloat( element.attribute( name ) ) );
209 }
210 else
211 {
212 reset();
213 }
214 }
215 }
216
217
218
219
setValue(const float value)220 void AutomatableModel::setValue( const float value )
221 {
222 m_oldValue = m_value;
223 ++m_setValueDepth;
224 const float old_val = m_value;
225
226 m_value = fittedValue( value );
227 if( old_val != m_value )
228 {
229 // add changes to history so user can undo it
230 addJournalCheckPoint();
231
232 // notify linked models
233 for( AutoModelVector::Iterator it = m_linkedModels.begin(); it != m_linkedModels.end(); ++it )
234 {
235 if( (*it)->m_setValueDepth < 1 && (*it)->fittedValue( value ) != (*it)->m_value )
236 {
237 bool journalling = (*it)->testAndSetJournalling( isJournalling() );
238 (*it)->setValue( value );
239 (*it)->setJournalling( journalling );
240 }
241 }
242 m_valueChanged = true;
243 emit dataChanged();
244 }
245 else
246 {
247 emit dataUnchanged();
248 }
249 --m_setValueDepth;
250 }
251
252
253
254
logToLinearScale(T value) const255 template<class T> T AutomatableModel::logToLinearScale( T value ) const
256 {
257 return castValue<T>( ::logToLinearScale( minValue<float>(), maxValue<float>(), static_cast<float>( value ) ) );
258 }
259
260
scaledValue(float value) const261 float AutomatableModel::scaledValue( float value ) const
262 {
263 return m_scaleType == Linear
264 ? value
265 : logToLinearScale<float>( ( value - minValue<float>() ) / m_range );
266 }
267
268
inverseScaledValue(float value) const269 float AutomatableModel::inverseScaledValue( float value ) const
270 {
271 return m_scaleType == Linear
272 ? value
273 : ::linearToLogScale( minValue<float>(), maxValue<float>(), value );
274 }
275
276
277
displayValue(const float val) const278 QString AutomatableModel::displayValue( const float val ) const
279 {
280 switch( m_dataType )
281 {
282 case Float: return QString::number( castValue<float>( scaledValue( val ) ) );
283 case Integer: return QString::number( castValue<int>( scaledValue( val ) ) );
284 case Bool: return QString::number( castValue<bool>( scaledValue( val ) ) );
285 }
286 return "0";
287 }
288
289
290
291 //! @todo: this should be moved into a maths header
292 template<class T>
roundAt(T & value,const T & where,const T & step_size)293 void roundAt( T& value, const T& where, const T& step_size )
294 {
295 if( qAbs<float>( value - where )
296 < typeInfo<float>::minEps() * qAbs<float>( step_size ) )
297 {
298 value = where;
299 }
300 }
301
302
303
304
305 template<class T>
roundAt(T & value,const T & where) const306 void AutomatableModel::roundAt( T& value, const T& where ) const
307 {
308 ::roundAt(value, where, m_step);
309 }
310
311
312
313
setAutomatedValue(const float value)314 void AutomatableModel::setAutomatedValue( const float value )
315 {
316 m_oldValue = m_value;
317 ++m_setValueDepth;
318 const float oldValue = m_value;
319
320 const float scaled_value = scaledValue( value );
321
322 m_value = fittedValue( scaled_value );
323
324 if( oldValue != m_value )
325 {
326 // notify linked models
327 for( AutoModelVector::Iterator it = m_linkedModels.begin();
328 it != m_linkedModels.end(); ++it )
329 {
330 if( (*it)->m_setValueDepth < 1 &&
331 (*it)->fittedValue( m_value ) != (*it)->m_value )
332 {
333 (*it)->setAutomatedValue( value );
334 }
335 }
336 m_valueChanged = true;
337 emit dataChanged();
338 }
339 --m_setValueDepth;
340 }
341
342
343
344
setRange(const float min,const float max,const float step)345 void AutomatableModel::setRange( const float min, const float max,
346 const float step )
347 {
348 if( ( m_maxValue != max ) || ( m_minValue != min ) )
349 {
350 m_minValue = min;
351 m_maxValue = max;
352 if( m_minValue > m_maxValue )
353 {
354 qSwap<float>( m_minValue, m_maxValue );
355 }
356 m_range = m_maxValue - m_minValue;
357
358 setStep( step );
359
360 // re-adjust value
361 setValue( value<float>() );
362
363 emit propertiesChanged();
364 }
365 }
366
367
368
369
setStep(const float step)370 void AutomatableModel::setStep( const float step )
371 {
372 if( m_step != step )
373 {
374 m_step = step;
375 emit propertiesChanged();
376 }
377 }
378
379
380
381
fittedValue(float value) const382 float AutomatableModel::fittedValue( float value ) const
383 {
384 value = tLimit<float>( value, m_minValue, m_maxValue );
385
386 if( m_step != 0 && m_hasStrictStepSize )
387 {
388 value = nearbyintf( value / m_step ) * m_step;
389 }
390
391 roundAt( value, m_maxValue );
392 roundAt( value, m_minValue );
393 roundAt( value, 0.0f );
394
395 if( value < m_minValue )
396 {
397 return m_minValue;
398 }
399 else if( value > m_maxValue )
400 {
401 return m_maxValue;
402 }
403
404 return value;
405 }
406
407
408
409
410
linkModel(AutomatableModel * model)411 void AutomatableModel::linkModel( AutomatableModel* model )
412 {
413 if( !m_linkedModels.contains( model ) && model != this )
414 {
415 m_linkedModels.push_back( model );
416 m_hasLinkedModels = true;
417
418 if( !model->hasLinkedModels() )
419 {
420 QObject::connect( this, SIGNAL( dataChanged() ),
421 model, SIGNAL( dataChanged() ), Qt::DirectConnection );
422 }
423 }
424 }
425
426
427
428
unlinkModel(AutomatableModel * model)429 void AutomatableModel::unlinkModel( AutomatableModel* model )
430 {
431 AutoModelVector::Iterator it = qFind( m_linkedModels.begin(), m_linkedModels.end(), model );
432 if( it != m_linkedModels.end() )
433 {
434 m_linkedModels.erase( it );
435 }
436 m_hasLinkedModels = !m_linkedModels.isEmpty();
437 }
438
439
440
441
442
443
linkModels(AutomatableModel * model1,AutomatableModel * model2)444 void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 )
445 {
446 if (!model1->m_linkedModels.contains( model2 ) && model1 != model2)
447 {
448 // copy data
449 model1->m_value = model2->m_value;
450 if (model1->valueBuffer() && model2->valueBuffer())
451 {
452 std::copy_n(model2->valueBuffer()->data(),
453 model1->valueBuffer()->length(),
454 model1->valueBuffer()->data());
455 }
456 // send dataChanged() before linking (because linking will
457 // connect the two dataChanged() signals)
458 emit model1->dataChanged();
459 // finally: link the models
460 model1->linkModel( model2 );
461 model2->linkModel( model1 );
462 }
463 }
464
465
466
467
unlinkModels(AutomatableModel * model1,AutomatableModel * model2)468 void AutomatableModel::unlinkModels( AutomatableModel* model1, AutomatableModel* model2 )
469 {
470 model1->unlinkModel( model2 );
471 model2->unlinkModel( model1 );
472 }
473
474
475
476
unlinkAllModels()477 void AutomatableModel::unlinkAllModels()
478 {
479 for( AutomatableModel* model : m_linkedModels )
480 {
481 unlinkModels( this, model );
482 }
483
484 m_hasLinkedModels = false;
485 }
486
487
488
489
setControllerConnection(ControllerConnection * c)490 void AutomatableModel::setControllerConnection( ControllerConnection* c )
491 {
492 m_controllerConnection = c;
493 if( c )
494 {
495 QObject::connect( m_controllerConnection, SIGNAL( valueChanged() ),
496 this, SIGNAL( dataChanged() ), Qt::DirectConnection );
497 QObject::connect( m_controllerConnection, SIGNAL( destroyed() ), this, SLOT( unlinkControllerConnection() ) );
498 m_valueChanged = true;
499 emit dataChanged();
500 }
501 }
502
503
504
505
controllerValue(int frameOffset) const506 float AutomatableModel::controllerValue( int frameOffset ) const
507 {
508 if( m_controllerConnection )
509 {
510 float v = 0;
511 switch(m_scaleType)
512 {
513 case Linear:
514 v = minValue<float>() + ( range() * controllerConnection()->currentValue( frameOffset ) );
515 break;
516 case Logarithmic:
517 v = logToLinearScale(
518 controllerConnection()->currentValue( frameOffset ));
519 break;
520 default:
521 qFatal("AutomatableModel::controllerValue(int)"
522 "lacks implementation for a scale type");
523 break;
524 }
525 if( typeInfo<float>::isEqual( m_step, 1 ) && m_hasStrictStepSize )
526 {
527 return qRound( v );
528 }
529 return v;
530 }
531
532 AutomatableModel* lm = m_linkedModels.first();
533 if( lm->controllerConnection() )
534 {
535 return fittedValue( lm->controllerValue( frameOffset ) );
536 }
537
538 return fittedValue( lm->m_value );
539 }
540
541
valueBuffer()542 ValueBuffer * AutomatableModel::valueBuffer()
543 {
544 QMutexLocker m( &m_valueBufferMutex );
545 // if we've already calculated the valuebuffer this period, return the cached buffer
546 if( m_lastUpdatedPeriod == s_periodCounter )
547 {
548 return m_hasSampleExactData
549 ? &m_valueBuffer
550 : NULL;
551 }
552
553 float val = m_value; // make sure our m_value doesn't change midway
554
555 ValueBuffer * vb;
556 if( m_controllerConnection && m_controllerConnection->getController()->isSampleExact() )
557 {
558 vb = m_controllerConnection->valueBuffer();
559 if( vb )
560 {
561 float * values = vb->values();
562 float * nvalues = m_valueBuffer.values();
563 switch( m_scaleType )
564 {
565 case Linear:
566 for( int i = 0; i < m_valueBuffer.length(); i++ )
567 {
568 nvalues[i] = minValue<float>() + ( range() * values[i] );
569 }
570 break;
571 case Logarithmic:
572 for( int i = 0; i < m_valueBuffer.length(); i++ )
573 {
574 nvalues[i] = logToLinearScale( values[i] );
575 }
576 break;
577 default:
578 qFatal("AutomatableModel::valueBuffer() "
579 "lacks implementation for a scale type");
580 break;
581 }
582 m_lastUpdatedPeriod = s_periodCounter;
583 m_hasSampleExactData = true;
584 return &m_valueBuffer;
585 }
586 }
587 AutomatableModel* lm = NULL;
588 if( m_hasLinkedModels )
589 {
590 lm = m_linkedModels.first();
591 }
592 if( lm && lm->controllerConnection() && lm->controllerConnection()->getController()->isSampleExact() )
593 {
594 vb = lm->valueBuffer();
595 float * values = vb->values();
596 float * nvalues = m_valueBuffer.values();
597 for( int i = 0; i < vb->length(); i++ )
598 {
599 nvalues[i] = fittedValue( values[i] );
600 }
601 m_lastUpdatedPeriod = s_periodCounter;
602 m_hasSampleExactData = true;
603 return &m_valueBuffer;
604 }
605
606 if( m_oldValue != val )
607 {
608 m_valueBuffer.interpolate( m_oldValue, val );
609 m_oldValue = val;
610 m_lastUpdatedPeriod = s_periodCounter;
611 m_hasSampleExactData = true;
612 return &m_valueBuffer;
613 }
614
615 // if we have no sample-exact source for a ValueBuffer, return NULL to signify that no data is available at the moment
616 // in which case the recipient knows to use the static value() instead
617 m_lastUpdatedPeriod = s_periodCounter;
618 m_hasSampleExactData = false;
619 return NULL;
620 }
621
622
unlinkControllerConnection()623 void AutomatableModel::unlinkControllerConnection()
624 {
625 if( m_controllerConnection )
626 {
627 m_controllerConnection->disconnect( this );
628 }
629
630 m_controllerConnection = NULL;
631 }
632
633
634
635
setInitValue(const float value)636 void AutomatableModel::setInitValue( const float value )
637 {
638 m_initValue = fittedValue( value );
639 bool journalling = testAndSetJournalling( false );
640 setValue( value );
641 m_oldValue = m_value;
642 setJournalling( journalling );
643 emit initValueChanged( value );
644 }
645
646
647
648
reset()649 void AutomatableModel::reset()
650 {
651 setValue( initValue<float>() );
652 }
653
654
655
656
copyValue()657 void AutomatableModel::copyValue()
658 {
659 s_copiedValue = value<float>();
660 }
661
662
663
664
pasteValue()665 void AutomatableModel::pasteValue()
666 {
667 setValue( copiedValue() );
668 }
669
670
671
globalAutomationValueAt(const MidiTime & time)672 float AutomatableModel::globalAutomationValueAt( const MidiTime& time )
673 {
674 // get patterns that connect to this model
675 QVector<AutomationPattern *> patterns = AutomationPattern::patternsForModel( this );
676 if( patterns.isEmpty() )
677 {
678 // if no such patterns exist, return current value
679 return m_value;
680 }
681 else
682 {
683 // of those patterns:
684 // find the patterns which overlap with the miditime position
685 QVector<AutomationPattern *> patternsInRange;
686 for( QVector<AutomationPattern *>::ConstIterator it = patterns.begin(); it != patterns.end(); it++ )
687 {
688 int s = ( *it )->startPosition();
689 int e = ( *it )->endPosition();
690 if( s <= time && e >= time ) { patternsInRange += ( *it ); }
691 }
692
693 AutomationPattern * latestPattern = NULL;
694
695 if( ! patternsInRange.isEmpty() )
696 {
697 // if there are more than one overlapping patterns, just use the first one because
698 // multiple pattern behaviour is undefined anyway
699 latestPattern = patternsInRange[0];
700 }
701 else
702 // if we find no patterns at the exact miditime, we need to search for the last pattern before time and use that
703 {
704 int latestPosition = 0;
705
706 for( QVector<AutomationPattern *>::ConstIterator it = patterns.begin(); it != patterns.end(); it++ )
707 {
708 int e = ( *it )->endPosition();
709 if( e <= time && e > latestPosition )
710 {
711 latestPosition = e;
712 latestPattern = ( *it );
713 }
714 }
715 }
716
717 if( latestPattern )
718 {
719 // scale/fit the value appropriately and return it
720 const float value = latestPattern->valueAt( time - latestPattern->startPosition() );
721 const float scaled_value = scaledValue( value );
722 return fittedValue( scaled_value );
723 }
724 // if we still find no pattern, the value at that time is undefined so
725 // just return current value as the best we can do
726 else return m_value;
727 }
728 }
729
getRoundedValue() const730 float FloatModel::getRoundedValue() const
731 {
732 return qRound( value() / step<float>() ) * step<float>();
733 }
734
735
736
737
getDigitCount() const738 int FloatModel::getDigitCount() const
739 {
740 float steptemp = step<float>();
741 int digits = 0;
742 while ( steptemp < 1 )
743 {
744 steptemp = steptemp * 10.0f;
745 digits++;
746 }
747 return digits;
748 }
749
750