1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //  $Id: strip.cpp,v 1.6.2.5 2009/11/14 03:37:48 terminator356 Exp $
5 //
6 //  (C) Copyright 2000-2004 Werner Schweer (ws@seh.de)
7 //  (C) Copyright 2011 - 2016 Tim E. Real (terminator356 on sourceforge)
8 //
9 //  This program is free software; you can redistribute it and/or
10 //  modify it under the terms of the GNU General Public License
11 //  as published by the Free Software Foundation; version 2 of
12 //  the License, or (at your option) any later version.
13 //
14 //  This program is distributed in the hope that it will be useful,
15 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 //  GNU General Public License for more details.
18 //
19 //  You should have received a copy of the GNU General Public License
20 //  along with this program; if not, write to the Free Software
21 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 //
23 //=========================================================
24 
25 #include "muse_math.h"
26 
27 #include <QPalette>
28 #include <QColor>
29 #include <QMenu>
30 #include <QString>
31 #include <QPainter>
32 #include <QList>
33 #include <QInputDialog>
34 #include <QMessageBox>
35 #include <QStyle>
36 #include <QStyleOption>
37 #include <QApplication>
38 
39 #include "globals.h"
40 #include "gconfig.h"
41 #include "app.h"
42 #include "midiport.h"
43 #include "audio.h"
44 #include "song.h"
45 #include "strip.h"
46 #include "utils.h"
47 #include "muse_math.h"
48 #include "ctrl.h"
49 #include "midi_consts.h"
50 #include "midictrl.h"
51 #include "icons.h"
52 #include "undo.h"
53 #include "operations.h"
54 #include "amixer.h"
55 #include "menutitleitem.h"
56 #include "shortcuts.h"
57 
58 // Forwards from header:
59 #include <QMouseEvent>
60 #include <QResizeEvent>
61 #include <QGridLayout>
62 #include <QLayout>
63 #include <QPushButton>
64 #include "track.h"
65 #include "combobox.h"
66 #include "compact_knob.h"
67 #include "compact_slider.h"
68 #include "pixmap_button.h"
69 #include "meter.h"
70 
71 // For debugging output: Uncomment the fprintf section.
72 #define DEBUG_STRIP(dev, format, args...) // fprintf(dev, format, ##args);
73 
74 using MusECore::UndoOp;
75 
76 namespace MusEGui {
77 
78 
79 //---------------------------------------------------------
80 //   ComponentRack
81 //---------------------------------------------------------
82 
ComponentRack(int id,QWidget * parent,Qt::WindowFlags f)83 ComponentRack::ComponentRack(int id, QWidget* parent, Qt::WindowFlags f)
84   : QFrame(parent, f), _id(id)
85 {
86   _layout = new ComponentRackLayout(this); // Install a layout manager.
87   _layout->setSpacing(0);
88   _layout->setContentsMargins(0, 0, 0, 0);
89 }
90 
findComponent(ComponentWidget::ComponentType componentType,int componentWidgetType,int index,QWidget * widget)91 ComponentWidget* ComponentRack::findComponent(
92       ComponentWidget::ComponentType componentType,
93       int componentWidgetType,
94       int index,
95       QWidget* widget)
96 {
97   iComponentWidget icw = _components.find(componentType, componentWidgetType, index, widget);
98   if(icw != _components.end())
99     return &(*icw);
100   return 0;
101 }
102 
clearDelete()103 void ComponentRack::clearDelete()
104 {
105   for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic)
106   {
107     ComponentWidget& cw = *ic;
108     if(cw._widget)
109       cw._widget->deleteLater();
110   }
111   _components.clear();
112 
113 // layout items must be removed too to avoid artefacts! (kybos)
114   while(_layout->takeAt(0));
115 }
116 
addComponentWidget(const ComponentWidget & cw,const ComponentWidget & before)117 void ComponentRack::addComponentWidget( const ComponentWidget& cw, const ComponentWidget& before )
118 {
119   if(cw._widget)
120   {
121     int idx = -1;
122     if(before.isValid())
123     {
124       iComponentWidget ibcw = _components.find(before);
125       if(ibcw == _components.end())
126       {
127         DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' item not found. Pushing back.\n");
128         _components.push_back(cw);
129       }
130       else
131       {
132         idx = _layout->indexOf(before._widget);
133         if(idx == -1)
134         {
135           DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' widget not found. Pushing back.\n");
136           _components.push_back(cw);
137         }
138         else
139         {
140           DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' widget found. Inserting. Layout idx:%d.\n", idx);
141           _components.insert(ibcw, cw);
142         }
143       }
144     }
145     else
146     {
147       DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' item not valid. Pushing back.\n");
148       _components.push_back(cw);
149     }
150 
151     if(idx == -1)
152       _layout->addWidget(cw._widget);
153     else
154       _layout->insertWidget(idx, cw._widget);
155   }
156 }
157 
newComponentWidget(ComponentDescriptor * desc,const ComponentWidget & before)158 void ComponentRack::newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before )
159 {
160   QPalette pal(palette());
161   ComponentWidget cw;
162   switch(desc->_widgetType)
163   {
164     case CompactKnobComponentWidget:
165     {
166       CompactKnobComponentDescriptor* d = static_cast<CompactKnobComponentDescriptor*>(desc);
167       if(!d->_compactKnob)
168       {
169         CompactKnob* control = new CompactKnob(nullptr,
170                                                d->_objName,
171                                                CompactKnob::Right,
172                                                d->_label);
173         d->_compactKnob = control;
174         control->setId(d->_index);
175         control->setRange(d->_min, d->_max, d->_step);
176         control->setValueDecimals(d->_precision);
177         control->setSpecialValueText(d->_specialValueText);
178         control->setHasOffMode(d->_hasOffMode);
179         control->setValueState(d->_initVal, d->_isOff);
180         control->setValPrefix(d->_prefix);
181         control->setValSuffix(d->_suffix);
182         control->setShowValue(d->_showValue);
183         // Do not set. Compact knob needs to manage it's own tooltips.
184         //control->setToolTip(d->_toolTipText);
185         control->setEnabled(d->_enabled);
186         control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
187         control->setContentsMargins(0, 0, 0, 0);
188         if(d->_color.isValid())
189           control->setFaceColor(d->_color);
190         //if(d->_rimColor.isValid())
191         //  control->setRimColor(d->_rimColor);
192         if(d->_faceColor.isValid())
193           control->setFaceColor(d->_faceColor);
194         if(d->_shinyColor.isValid())
195           control->setShinyColor(d->_shinyColor);
196 
197         //control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
198 
199         switch(d->_componentType)
200         {
201           case controllerComponent:
202               connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(controllerChanged(double,bool,int,int)));
203               connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(controllerMoved(double,int,bool)));
204               connect(control, SIGNAL(sliderPressed(double, int)), SLOT(controllerPressed(double, int)));
205               connect(control, SIGNAL(sliderReleased(double, int)), SLOT(controllerReleased(double, int)));
206               connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int)));
207           break;
208 
209           case propertyComponent:
210               connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(propertyChanged(double,bool,int,int)));
211               connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(propertyMoved(double,int,bool)));
212               connect(control, SIGNAL(sliderPressed(double, int)), SLOT(propertyPressed(double, int)));
213               connect(control, SIGNAL(sliderReleased(double, int)), SLOT(propertyReleased(double, int)));
214               connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(propertyRightClicked(QPoint,int)));
215           break;
216         }
217       }
218 
219       cw = ComponentWidget(
220                             d->_compactKnob,
221                             d->_widgetType,
222                             d->_componentType,
223                             d->_index
224                           );
225 
226     }
227     break;
228 
229     case CompactSliderComponentWidget:
230     {
231       CompactSliderComponentDescriptor* d = static_cast<CompactSliderComponentDescriptor*>(desc);
232       if(!d->_compactSlider)
233       {
234         CompactSlider* control = new CompactSlider(nullptr, d->_objName, Qt::Horizontal, CompactSlider::None, d->_label);
235         d->_compactSlider = control;
236         control->setId(d->_index);
237         control->setRange(d->_min, d->_max, d->_step);
238         control->setValueDecimals(d->_precision);
239         control->setSpecialValueText(d->_specialValueText);
240         control->setHasOffMode(d->_hasOffMode);
241         control->setValueState(d->_initVal, d->_isOff);
242         control->setValPrefix(d->_prefix);
243         control->setValSuffix(d->_suffix);
244         control->setShowValue(d->_showValue);
245         control->setActiveBorders(d->_activeBorders);
246         control->setToolTip(d->_toolTipText);
247         control->setEnabled(d->_enabled);
248         control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
249         control->setContentsMargins(0, 0, 0, 0);
250 
251         if(d->_color.isValid())
252           control->setBorderColor(d->_color);
253         if(d->_barColor.isValid())
254           control->setBarColor(d->_barColor);
255         if(d->_slotColor.isValid())
256           control->setSlotColor(d->_slotColor);
257         if(d->_thumbColor.isValid())
258           control->setThumbColor(d->_thumbColor);
259 
260         control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
261 
262         switch(d->_componentType)
263         {
264           case controllerComponent:
265               connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(controllerChanged(double,bool,int,int)));
266               connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(controllerMoved(double,int,bool)));
267               connect(control, SIGNAL(sliderPressed(double, int)), SLOT(controllerPressed(double, int)));
268               connect(control, SIGNAL(sliderReleased(double, int)), SLOT(controllerReleased(double, int)));
269               connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int)));
270           break;
271 
272           case propertyComponent:
273               connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(propertyChanged(double,bool,int,int)));
274               connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(propertyMoved(double,int,bool)));
275               connect(control, SIGNAL(sliderPressed(double, int)), SLOT(propertyPressed(double, int)));
276               connect(control, SIGNAL(sliderReleased(double, int)), SLOT(propertyReleased(double, int)));
277               connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(propertyRightClicked(QPoint,int)));
278           break;
279         }
280       }
281 
282       cw = ComponentWidget(
283                             d->_compactSlider,
284                             d->_widgetType,
285                             d->_componentType,
286                             d->_index
287                           );
288 
289     }
290     break;
291 
292     case ElidedLabelComponentWidget:
293     {
294       ElidedLabelComponentDescriptor* d = static_cast<ElidedLabelComponentDescriptor*>(desc);
295       if(!d->_elidedLabel)
296       {
297         ElidedLabel* control = new ElidedLabel(nullptr, d->_elideMode);
298         d->_elidedLabel = control;
299         control->setObjectName(d->_objName);
300 
301         control->setId(d->_index);
302         control->setToolTip(d->_toolTipText);
303         control->setEnabled(d->_enabled);
304         control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
305         control->setContentsMargins(0, 0, 0, 0);
306 
307 //        if(d->_color.isValid())
308 //        {
309 //          pal.setColor(QPalette::Active, QPalette::Button, d->_color); // Border
310 //          pal.setColor(QPalette::Inactive, QPalette::Button, d->_color); // Border
311 //          control->setPalette(pal);
312 //        }
313 
314         if(d->_bgColor.isValid())
315             control->setBgColor(d->_bgColor);
316         if(d->_bgActiveColor.isValid())
317             control->setBgActiveColor(d->_bgActiveColor);
318         if(d->_color.isValid())
319             control->setBorderColor(d->_color);
320         if(d->_fontColor.isValid())
321             control->setFontColor(d->_fontColor);
322         if(d->_fontActiveColor.isValid())
323             control->setFontActiveColor(d->_fontActiveColor);
324 
325         switch(d->_componentType)
326         {
327           case propertyComponent:
328               connect(control, SIGNAL(pressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)),
329                       SLOT(labelPropertyPressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)));
330               connect(control, SIGNAL(released(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)),
331                       SLOT(labelPropertyReleased(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)));
332               connect(control, SIGNAL(returnPressed(QPoint,int,Qt::KeyboardModifiers)),
333                       SLOT(labelPropertyReturnPressed(QPoint,int,Qt::KeyboardModifiers)));
334           break;
335         }
336       }
337 
338       cw = ComponentWidget(
339                             d->_elidedLabel,
340                             d->_widgetType,
341                             d->_componentType,
342                             d->_index
343                           );
344 
345     }
346     break;
347 
348     case ExternalComponentWidget:
349     {
350       WidgetComponentDescriptor* d = static_cast<WidgetComponentDescriptor*>(desc);
351 
352       QWidget* widget = d->_widget;
353       if(widget)
354       {
355         widget->setToolTip(d->_toolTipText);
356         widget->setEnabled(d->_enabled);
357         widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
358 
359         if(d->_color.isValid())
360         {
361           pal.setColor(QPalette::Active, QPalette::Button, d->_color); // Border
362           pal.setColor(QPalette::Inactive, QPalette::Button, d->_color); // Border
363           widget->setPalette(pal);
364         }
365 
366         cw = ComponentWidget(
367                               d->_widget,
368                               d->_widgetType,
369                               d->_componentType,
370                               d->_index
371                             );
372       }
373     }
374     break;
375   }
376 
377   if(cw._widget)
378     addComponentWidget(cw, before);
379 }
380 
setComponentMinValue(const ComponentWidget & cw,double min,bool updateOnly)381 void ComponentRack::setComponentMinValue(const ComponentWidget& cw, double min, bool updateOnly)
382 {
383   if(!cw._widget)
384     return;
385 
386   switch(cw._widgetType)
387   {
388     case CompactSliderComponentWidget:
389     {
390       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
391       if(min != w->minValue())
392       {
393         if(updateOnly)
394           w->blockSignals(true);
395         w->setMinValue(min);
396         if(updateOnly)
397           w->blockSignals(false);
398       }
399     }
400     break;
401 
402     case CompactKnobComponentWidget:
403     {
404       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
405       if(min != w->minValue())
406       {
407         if(updateOnly)
408           w->blockSignals(true);
409         w->setMinValue(min);
410         if(updateOnly)
411           w->blockSignals(false);
412       }
413     }
414     break;
415   }
416 }
417 
setComponentMaxValue(const ComponentWidget & cw,double max,bool updateOnly)418 void ComponentRack::setComponentMaxValue(const ComponentWidget& cw, double max, bool updateOnly)
419 {
420   if(!cw._widget)
421     return;
422 
423   switch(cw._widgetType)
424   {
425     case CompactSliderComponentWidget:
426     {
427       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
428       if(max != w->maxValue())
429       {
430         if(updateOnly)
431           w->blockSignals(true);
432         w->setMaxValue(max);
433         if(updateOnly)
434           w->blockSignals(false);
435       }
436     }
437     break;
438 
439     case CompactKnobComponentWidget:
440     {
441       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
442       if(max != w->maxValue())
443       {
444         if(updateOnly)
445           w->blockSignals(true);
446         w->setMaxValue(max);
447         if(updateOnly)
448           w->blockSignals(false);
449       }
450     }
451     break;
452   }
453 }
454 
componentMinValue(const ComponentWidget & cw) const455 double ComponentRack::componentMinValue(const ComponentWidget& cw) const
456 {
457   if(cw._widget)
458   {
459     switch(cw._widgetType)
460     {
461       case CompactSliderComponentWidget:
462         return static_cast<CompactSlider*>(cw._widget)->minValue();
463       break;
464 
465       case CompactKnobComponentWidget:
466         return static_cast<CompactKnob*>(cw._widget)->minValue();
467       break;
468     }
469   }
470 
471   return 0.0;
472 }
473 
componentMaxValue(const ComponentWidget & cw) const474 double ComponentRack::componentMaxValue(const ComponentWidget& cw) const
475 {
476   if(cw._widget)
477   {
478     switch(cw._widgetType)
479     {
480       case CompactSliderComponentWidget:
481         return static_cast<CompactSlider*>(cw._widget)->maxValue();
482       break;
483 
484       case CompactKnobComponentWidget:
485         return static_cast<CompactKnob*>(cw._widget)->maxValue();
486       break;
487     }
488   }
489 
490   return 0.0;
491 }
492 
setComponentRange(const ComponentWidget & cw,double min,double max,bool updateOnly,double step,int pageSize,DoubleRange::ConversionMode mode)493 void ComponentRack::setComponentRange(const ComponentWidget& cw, double min, double max, bool updateOnly,
494                                       double step, int pageSize,
495                                       DoubleRange::ConversionMode mode)
496 {
497   if(!cw._widget)
498     return;
499 
500   switch(cw._widgetType)
501   {
502     case CompactSliderComponentWidget:
503     {
504       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
505       if(min != w->minValue() || max != w->maxValue())
506       {
507         if(updateOnly)
508           w->blockSignals(true);
509         if(min != w->minValue() && max != w->maxValue())
510           w->setRange(min, max, step, pageSize, mode);
511         else if(min != w->minValue())
512           w->setMinValue(max);
513         else
514           w->setMaxValue(max);
515         if(updateOnly)
516           w->blockSignals(false);
517       }
518     }
519     break;
520 
521     case CompactKnobComponentWidget:
522     {
523       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
524       if(min != w->minValue() || max != w->maxValue())
525       {
526         if(updateOnly)
527           w->blockSignals(true);
528         if(min != w->minValue() && max != w->maxValue())
529           w->setRange(min, max, step, pageSize, mode);
530         else if(min != w->minValue())
531           w->setMinValue(max);
532         else
533           w->setMaxValue(max);
534         if(updateOnly)
535           w->blockSignals(false);
536       }
537     }
538     break;
539   }
540 }
541 
componentValue(const ComponentWidget & cw) const542 double ComponentRack::componentValue(const ComponentWidget& cw) const
543 {
544   if(cw._widget)
545   {
546     switch(cw._widgetType)
547     {
548       case CompactSliderComponentWidget:
549         return static_cast<CompactSlider*>(cw._widget)->value();
550       break;
551 
552       case CompactKnobComponentWidget:
553         return static_cast<CompactKnob*>(cw._widget)->value();
554       break;
555     }
556   }
557 
558   return 0.0;
559 }
560 
setComponentValue(const ComponentWidget & cw,double val,bool updateOnly)561 void ComponentRack::setComponentValue(const ComponentWidget& cw, double val, bool updateOnly)
562 {
563   if(!cw._widget)
564     return;
565 
566   switch(cw._widgetType)
567   {
568     case CompactSliderComponentWidget:
569     {
570       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
571       //if(val != cw->_currentValue) // TODO ?
572       if(val != w->value())
573       {
574         if(updateOnly)
575           w->blockSignals(true);
576         w->setValue(val);
577         if(updateOnly)
578           w->blockSignals(false);
579         //cw->_currentValue = val;  // TODO ?
580       }
581     }
582     break;
583 
584     case CompactKnobComponentWidget:
585     {
586       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
587       //if(val != cw->_currentValue) // TODO ?
588       if(val != w->value())
589       {
590         if(updateOnly)
591           w->blockSignals(true);
592         w->setValue(val);
593         if(updateOnly)
594           w->blockSignals(false);
595         //cw->_currentValue = val;  // TODO ?
596       }
597     }
598     break;
599   }
600 }
601 
fitComponentValue(const ComponentWidget & cw,double val,bool updateOnly)602 void ComponentRack::fitComponentValue(const ComponentWidget& cw, double val, bool updateOnly)
603 {
604   if(!cw._widget)
605     return;
606 
607   switch(cw._widgetType)
608   {
609     case CompactSliderComponentWidget:
610     {
611       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
612       //if(val != cw->_currentValue) // TODO ?
613       if(val != w->value())
614       {
615         if(updateOnly)
616           w->blockSignals(true);
617         w->fitValue(val);
618         if(updateOnly)
619           w->blockSignals(false);
620         //cw->_currentValue = val;  // TODO ?
621       }
622     }
623     break;
624 
625     case CompactKnobComponentWidget:
626     {
627       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
628       //if(val != cw->_currentValue) // TODO ?
629       if(val != w->value())
630       {
631         if(updateOnly)
632           w->blockSignals(true);
633         w->fitValue(val);
634         if(updateOnly)
635           w->blockSignals(false);
636         //cw->_currentValue = val;  // TODO ?
637       }
638     }
639     break;
640   }
641 }
642 
incComponentValue(const ComponentWidget & cw,int steps,bool updateOnly)643 void ComponentRack::incComponentValue(const ComponentWidget& cw, int steps, bool updateOnly)
644 {
645   if(!cw._widget)
646     return;
647 
648   switch(cw._widgetType)
649   {
650     case CompactSliderComponentWidget:
651     {
652       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
653       if(updateOnly)
654         w->blockSignals(true);
655       w->incValue(steps);
656       if(updateOnly)
657         w->blockSignals(false);
658     }
659     break;
660 
661     case CompactKnobComponentWidget:
662     {
663       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
664       if(updateOnly)
665         w->blockSignals(true);
666       w->incValue(steps);
667       if(updateOnly)
668         w->blockSignals(false);
669     }
670     break;
671   }
672 }
673 
setComponentText(const ComponentWidget & cw,const QString & text,bool updateOnly)674 void ComponentRack::setComponentText(const ComponentWidget& cw, const QString& text, bool updateOnly)
675 {
676   if(!cw._widget)
677     return;
678 
679   switch(cw._widgetType)
680   {
681     case ElidedLabelComponentWidget:
682     {
683       ElidedLabel* w = static_cast<ElidedLabel*>(cw._widget);
684       if(text != w->text())
685       {
686         if(updateOnly)
687           w->blockSignals(true);
688         w->setText(text);
689         if(updateOnly)
690           w->blockSignals(false);
691       }
692     }
693     break;
694 
695     case CompactKnobComponentWidget:
696     {
697       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
698       if(text != w->labelText())
699       {
700         if(updateOnly)
701           w->blockSignals(true);
702         w->setLabelText(text);
703         if(updateOnly)
704           w->blockSignals(false);
705       }
706     }
707     break;
708 
709     case CompactSliderComponentWidget:
710     {
711       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
712       if(text != w->labelText())
713       {
714         if(updateOnly)
715           w->blockSignals(true);
716         w->setLabelText(text);
717         if(updateOnly)
718           w->blockSignals(false);
719       }
720     }
721     break;
722   }
723 }
724 
setComponentEnabled(const ComponentWidget & cw,bool enable,bool)725 void ComponentRack::setComponentEnabled(const ComponentWidget& cw, bool enable, bool /*updateOnly*/)
726 {
727   if(!cw._widget)
728     return;
729 
730   // Nothing special for now. Just operate on the widget itself.
731   cw._widget->setEnabled(enable);
732 }
733 
setComponentShowValue(const ComponentWidget & cw,bool show,bool updateOnly)734 void ComponentRack::setComponentShowValue(const ComponentWidget& cw, bool show, bool updateOnly)
735 {
736   if(!cw._widget)
737     return;
738 
739   switch(cw._widgetType)
740   {
741     case CompactKnobComponentWidget:
742     {
743       CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
744       if(show != w->showValue())
745       {
746         if(updateOnly)
747           w->blockSignals(true);
748         w->setShowValue(show);
749         if(updateOnly)
750           w->blockSignals(false);
751       }
752     }
753     break;
754 
755     case CompactSliderComponentWidget:
756     {
757       CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
758       if(show != w->showValue())
759       {
760         if(updateOnly)
761           w->blockSignals(true);
762         w->setShowValue(show);
763         if(updateOnly)
764           w->blockSignals(false);
765       }
766     }
767     break;
768   }
769 }
770 
setupComponentTabbing(QWidget * previousWidget)771 QWidget* ComponentRack::setupComponentTabbing(QWidget* previousWidget)
772 {
773   QWidget* prev = previousWidget;
774   for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic)
775   {
776     const ComponentWidget& cw = *ic;
777     if(cw._widget)
778     {
779       if(prev)
780         QWidget::setTabOrder(prev, cw._widget);
781       prev = cw._widget;
782     }
783   }
784   return prev;
785 }
786 
787 //---------------------------------------------------------
788 //   configChanged
789 //   Catch when label font, or configuration min slider and meter values change, or viewable tracks etc.
790 //---------------------------------------------------------
791 
configChanged()792 void ComponentRack::configChanged()
793 {
794   // FIXME For some reason we have to set the font and stylesheet here as well as the strip.
795   // Strip font and stylesheet changes don't seem to be propagated to these racks.
796   // FIXME They also don't seem to be propagated to our CompactPatchEdit component on the midi strip.
797   if(font() != MusEGlobal::config.fonts[1])
798   {
799     setFont(MusEGlobal::config.fonts[1]); // should be redundant, overridden by style sheet
800     setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]));
801   }
802 
803   for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic)
804   {
805     const ComponentWidget& cw = *ic;
806     if(!cw._widget)
807       continue;
808 
809     switch(cw._widgetType)
810     {
811       case CompactKnobComponentWidget:
812       {
813         //CompactKnob* w = static_cast<CompactKnob*>(cw._widget);
814         //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
815       }
816       break;
817 
818       case CompactSliderComponentWidget:
819       {
820         CompactSlider* w = static_cast<CompactSlider*>(cw._widget);
821         w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
822       }
823       break;
824 
825 //       case ElidedLabelComponentWidget:
826 //       {
827 //         ElidedLabel* w = static_cast<ElidedLabel*>(cw._widget);
828 //         //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
829 //       }
830 //       break;
831 
832       default:
833       break;
834     }
835   }
836 }
837 
838 
839 //---------------------------------------------------------
840 //   TrackNameLabel
841 //---------------------------------------------------------
842 
843 const int TrackNameLabel::_expandIconWidth = 14;
844 
TrackNameLabel(QWidget * parent)845 TrackNameLabel::TrackNameLabel(QWidget* parent)
846  : QLabel(parent)
847 {
848     _style3d = true;
849     _hasExpandIcon = false;
850     _expandIconPressed = false;
851     _hovered = false;
852 }
853 
854 //TrackNameLabel::TrackNameLabel(const QString& text, QWidget* parent, const char* name, Qt::WindowFlags f)
855 // : QLabel(text, parent, name, f)
856 //{
857 //    _style3d = true;
858 //    _hasExpandIcon = false;
859 //    _expandIconPressed = false;
860 //}
861 
mouseDoubleClickEvent(QMouseEvent * ev)862 void TrackNameLabel::mouseDoubleClickEvent(QMouseEvent* ev)
863 {
864   ev->accept();
865   if (hasExpandIcon() && _hovered && (ev->pos().x() < _expandIconWidth))
866     return;
867 
868   emit doubleClicked();
869 }
870 
paintEvent(QPaintEvent * ev)871 void TrackNameLabel::paintEvent(QPaintEvent* ev)
872 {
873   ev->ignore();
874   QLabel::paintEvent(ev);
875 
876   if(!hasExpandIcon() || !_hovered)
877     return;
878 
879   if(rect().width() <= 0 || rect().height() <= 0)
880     return;
881 
882   QPainter p(this);
883 
884   p.save();
885 
886   const QRect r = rect();
887 
888   p.fillRect(r.x(), r.y(), _expandIconWidth, r.height(), palette().mid());
889 
890   //expandLeftRightSVGIcon->paint(&p, r.x() + r.width() - _expandIconWidth, r.y(), _expandIconWidth, r.height());
891   expandLeftRightSVGIcon->paint(&p, r.x(), r.y(), _expandIconWidth, r.height());
892 
893   p.restore();
894 }
895 
mousePressEvent(QMouseEvent * ev)896 void TrackNameLabel::mousePressEvent(QMouseEvent* ev)
897 {
898   if (hasExpandIcon() && _hovered && (ev->pos().x() < _expandIconWidth))
899   {
900     _expandIconPressed = true;
901     ev->accept();
902     emit expandClicked();
903   }
904   else
905   {
906     ev->ignore();
907     QLabel::mousePressEvent(ev);
908   }
909 }
910 
mouseReleaseEvent(QMouseEvent * ev)911 void TrackNameLabel::mouseReleaseEvent(QMouseEvent* ev)
912 {
913   if(_expandIconPressed)
914   {
915     _expandIconPressed = false;
916     ev->accept();
917   }
918   else
919   {
920     ev->ignore();
921     QLabel::mouseReleaseEvent(ev);
922   }
923 }
924 
mouseMoveEvent(QMouseEvent * ev)925 void TrackNameLabel::mouseMoveEvent(QMouseEvent* ev)
926 {
927     if(_expandIconPressed)
928     {
929         ev->accept();
930     }
931     else
932     {
933         ev->ignore();
934         QLabel::mouseMoveEvent(ev);
935     }
936 }
937 
leaveEvent(QEvent * e)938 void TrackNameLabel::leaveEvent(QEvent *e)
939 {
940     if(_hovered)
941     {
942         _hovered = false;
943         update();
944     }
945     e->ignore();
946     QLabel::leaveEvent(e);
947 }
948 
enterEvent(QEvent * e)949 void TrackNameLabel::enterEvent(QEvent *e)
950 {
951     if (!_hovered) {
952         _hovered = true;
953         update();
954     }
955     e->ignore();
956     QLabel::enterEvent(e);
957 }
958 
959 
960 
961 //---------------------------------------------------------
962 //   Strip
963 //---------------------------------------------------------
964 
965 //const int Strip::FIXED_METER_WIDTH = 7;
966 
967 //---------------------------------------------------------
968 //   setRecordFlag
969 //---------------------------------------------------------
970 
setRecordFlag(bool flag)971 void Strip::setRecordFlag(bool flag)
972 {
973     if (record) {
974         record->blockSignals(true);
975         record->setChecked(flag);
976         record->blockSignals(false);
977     }
978 
979     if (!flag) {
980         for (const auto& it : *MusEGlobal::song->tracks()) {
981             if (it->canRecord() && it->recordFlag())
982                 return;
983         }
984         MusEGlobal::song->setRecord(false);
985     }
986 }
987 
988 //---------------------------------------------------------
989 //   resetPeaks
990 //---------------------------------------------------------
991 
resetPeaks()992 void Strip::resetPeaks()
993       {
994       track->resetPeaks();
995       }
996 
997 //---------------------------------------------------------
998 //   recordToggled
999 //---------------------------------------------------------
1000 
recordToggled(bool val)1001 void Strip::recordToggled(bool val)
1002 {
1003   if (track->type() == MusECore::Track::AUDIO_OUTPUT)
1004   {
1005     if (val && !track->recordFlag())
1006     {
1007       MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)track);
1008 
1009       if (!((MusECore::AudioOutput*)track)->recFile())
1010       {
1011         if(record)
1012         {
1013           record->blockSignals(true);
1014           record->setChecked(false);
1015           record->blockSignals(false);
1016         }
1017       }
1018     }
1019     return;
1020   }
1021 
1022   MusEGlobal::song->setRecordFlag(track, val);
1023 }
1024 
1025 //---------------------------------------------------------
1026 //   returnPressed
1027 //---------------------------------------------------------
1028 
changeTrackName()1029 void Strip::changeTrackName()
1030 {
1031   if(!track)
1032     return;
1033 
1034   const QString oldname = track->name();
1035 
1036   QInputDialog dlg(this);
1037   dlg.setWindowTitle(tr("Track Name"));
1038   dlg.setLabelText(tr("Enter track name:"));
1039   dlg.setTextValue(oldname);
1040   // set standard font size explicitly, otherwise the font is too small (inherited from mixer strip)
1041   dlg.setStyleSheet("font-size:" + QString::number(MusEGlobal::config.fonts[0].pointSize()) + "pt");
1042 
1043   const int res = dlg.exec();
1044   if(res == QDialog::Rejected)
1045     return;
1046 
1047   const QString newname = dlg.textValue();
1048 
1049   if(newname == oldname)
1050     return;
1051 
1052   MusECore::TrackList* tl = MusEGlobal::song->tracks();
1053   for (MusECore::iTrack i = tl->begin(); i != tl->end(); ++i)
1054   {
1055     if ((*i)->name() == newname)
1056     {
1057       QMessageBox::critical(this,
1058         tr("MusE: Bad Trackname"),
1059         tr("Please choose a unique track name"),
1060         QMessageBox::Ok,
1061         Qt::NoButton,
1062         Qt::NoButton);
1063       return;
1064     }
1065   }
1066 
1067   MusEGlobal::song->applyOperation(
1068     MusECore::UndoOp(MusECore::UndoOp::ModifyTrackName, track, oldname, newname));
1069 }
1070 
1071 //---------------------------------------------------------
1072 //   trackNameLabelExpandClicked
1073 //---------------------------------------------------------
1074 
trackNameLabelExpandClicked()1075 void Strip::trackNameLabelExpandClicked()
1076 {
1077   setExpanded(!isExpanded());
1078 }
1079 
1080 //---------------------------------------------------------
1081 //   heartBeat
1082 //---------------------------------------------------------
1083 
heartBeat()1084 void Strip::heartBeat()
1085       {
1086       }
1087 
paintEvent(QPaintEvent * ev)1088 void Strip::paintEvent(QPaintEvent * ev)
1089 {
1090   QFrame::paintEvent(ev);
1091   QPainter p(this);
1092   if (_highlight) {
1093     QPen pen(Qt::yellow);
1094     pen.setWidth(1);
1095     p.setPen(pen);
1096     p.drawRect(0,0,width()-1,height()-1);
1097   }
1098   ev->accept();
1099 }
1100 
1101 //---------------------------------------------------------
1102 //   setLabelText
1103 //---------------------------------------------------------
1104 
setLabelText()1105 void Strip::setLabelText()
1106 {
1107     if(!track)
1108         return;
1109 
1110     QFont fnt(MusEGlobal::config.fonts[6]);
1111 
1112     const bool fit = MusECore::autoAdjustFontSize(label, track->name(), fnt,
1113                                                   false, true, fnt.pointSize(), 7);
1114     if (fit) {
1115         label->setText(track->name());
1116         label->setToolTip(QString());
1117     } else {
1118         QFontMetrics fm = QFontMetrics(fnt);
1119         QString elidedText = fm.elidedText(track->name(), Qt::ElideMiddle, label->width());
1120         label->setText(elidedText);
1121         label->setToolTip(track->name());
1122     }
1123 
1124     if (track->isSynthTrack()) {
1125         MusECore::SynthI *s = static_cast<MusECore::SynthI*>(track);
1126         if(!s->uri().isEmpty())
1127             label->setToolTip(QString(track->name() + "\n") + s->uri());
1128     }
1129 
1130     QString stxt;
1131 
1132     if (label->style3d()) {
1133         QColor c(track->labelColor());
1134         QColor c2(c.lighter());
1135         c.setAlpha(190);
1136         c2.setAlpha(190);
1137 
1138         stxt = QString("* { background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,"
1139                        "stop:0.263158 rgba(%1, %2, %3, %4), stop:0.7547368 rgba(%5, %6, %7, %8));")
1140                 .arg(c2.red()).arg(c2.green()).arg(c2.blue()).arg(c2.alpha()).arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
1141         stxt += QString("color: black;");
1142     } else {
1143         QColor c(track->color());
1144         if (!MusECore::isColorBright(c))
1145             c = c.lighter(130);
1146         stxt = "QLabel { background-color:" + c.name() + ";";
1147         if (MusECore::getPerceivedLuminance(c) < 64)
1148             stxt += QStringLiteral("color: white;");
1149         else
1150             stxt += QStringLiteral("color: black;");
1151     }
1152     stxt += MusECore::font2StyleSheet(fnt) + "}";
1153     stxt += "QToolTip {font-size:" + QString::number(qApp->font().pointSize()) + "pt}";
1154 
1155     label->setStyleSheet(stxt);
1156 }
1157 
1158 
1159 //---------------------------------------------------------
1160 //   muteToggled
1161 //---------------------------------------------------------
1162 
muteToggled(bool val)1163 void Strip::muteToggled(bool val)
1164       {
1165       if(track)
1166       {
1167         // This is a minor operation easily manually undoable. Let's not clog the undo list with it.
1168         MusECore::PendingOperationList operations;
1169         operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackMute));
1170         MusEGlobal::audio->msgExecutePendingOperations(operations, true);
1171       }
1172       updateMuteIcon();
1173       }
1174 
1175 //---------------------------------------------------------
1176 //   soloToggled
1177 //---------------------------------------------------------
1178 
soloToggled(bool val)1179 void Strip::soloToggled(bool val)
1180 {
1181     if (track && track->internalSolo()) {
1182         if (solo->isChecked())
1183             solo->setIcon(*soloAndProxyOnSVGIcon);
1184         else
1185             solo->setIcon(*soloProxyOnAloneSVGIcon);
1186     } else {
1187         solo->setIcon(*soloStateSVGIcon);
1188     }
1189     //    solo->setIconSetB(track && track->internalSolo());
1190 
1191     if (!track)
1192         return;
1193     // This is a minor operation easily manually undoable. Let's not clog the undo list with it.
1194     MusECore::PendingOperationList operations;
1195     operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackSolo));
1196     MusEGlobal::audio->msgExecutePendingOperations(operations, true);
1197 }
1198 
1199 //---------------------------------------------------------
1200 //   Strip
1201 //    create mixer strip
1202 //---------------------------------------------------------
1203 
Strip(QWidget * parent,MusECore::Track * t,bool hasHandle,bool isEmbedded)1204 Strip::Strip(QWidget* parent, MusECore::Track* t, bool hasHandle, bool isEmbedded)
1205    : QFrame(parent)
1206       {
1207       setObjectName("Strip");
1208       setMouseTracking(true);
1209       setAttribute(Qt::WA_DeleteOnClose);
1210       setFrameStyle(Panel | Raised);
1211       setLineWidth(1);
1212 
1213       // Set so that strip can redirect focus proxy to volume label in descendants.
1214       // Nope. Seemed like a good idea but no. Do not allow the strip to gain focus.
1215       // Just in case it does, which is possible, keep descendants' focus proxy code
1216       //  so that the slider label will be a sensible place for focus to land.
1217       setFocusPolicy(Qt::NoFocus);
1218 
1219       _focusYieldWidget = nullptr;
1220       _isEmbedded = isEmbedded;
1221       _broadcastChanges = false;
1222       _selected = false;
1223       _highlight = false;
1224 
1225       _curGridRow = 0;
1226       _userWidth = 0;
1227       _isExpanded = false;
1228       _visible = true;
1229       dragOn=false;
1230 
1231       sliderGrid    = nullptr;
1232       record        = nullptr;
1233       solo          = nullptr;
1234       mute          = nullptr;
1235       iR            = nullptr;
1236       oR            = nullptr;
1237       autoType      = nullptr;
1238 
1239       track    = t;
1240       meter[0] = nullptr;
1241       meter[1] = nullptr;
1242       setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
1243 
1244       grid = new QGridLayout();
1245       grid->setContentsMargins(0, 0, 0, 0);
1246       grid->setSpacing(0);
1247 
1248       _handle = nullptr;
1249       if(hasHandle)
1250       {
1251         _expanderWidth = 4;
1252         ensurePolished();
1253         _handle = new ExpanderHandle(nullptr, _expanderWidth);
1254         connect(_handle, &ExpanderHandle::moved, [this](int d) { changeUserWidth(d); } );
1255         QHBoxLayout* hlayout = new QHBoxLayout(this);
1256         hlayout->setContentsMargins(0, 0, 0, 0);
1257         hlayout->setSpacing(0);
1258         hlayout->addLayout(grid);
1259         hlayout->addWidget(_handle);
1260       }
1261       else
1262       {
1263         setLayout(grid);
1264       }
1265 
1266       //---------------------------------------------
1267       //    label
1268       //---------------------------------------------
1269 
1270       label = new TrackNameLabel(this);
1271       label->setFocusPolicy(Qt::NoFocus);
1272       label->setObjectName("TrackNameLabel");
1273       label->setContentsMargins(0, 0, 0, 0);
1274       label->setAlignment(Qt::AlignCenter);
1275       label->setAutoFillBackground(true);
1276       label->ensurePolished();
1277       if (label->style3d()) {
1278           label->setLineWidth(2);
1279           label->setFrameStyle(Sunken | StyledPanel);
1280           label->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum));
1281       } else {
1282           label->setFrameStyle(NoFrame);
1283           label->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
1284           label->setFixedHeight(16);
1285       }
1286       label->setHasExpandIcon(!_isEmbedded);
1287 
1288       setLabelText();
1289 
1290       grid->addWidget(label, _curGridRow++, 0, 1, 3);
1291 
1292       connect(label, &TrackNameLabel::doubleClicked, [this]() { changeTrackName(); } );
1293       connect(label, &TrackNameLabel::expandClicked, [this]() { trackNameLabelExpandClicked(); } );
1294       }
1295 
1296 //---------------------------------------------------------
1297 //   Strip
1298 //---------------------------------------------------------
1299 
~Strip()1300 Strip::~Strip()
1301       {
1302       }
1303 
setFocusYieldWidget(QWidget * w)1304 void Strip::setFocusYieldWidget(QWidget* w)
1305 {
1306   if(_focusYieldWidget == w)
1307     return;
1308   if(_focusYieldWidget)
1309     disconnect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*)));
1310   _focusYieldWidget = w;
1311   if(_focusYieldWidget)
1312     connect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*)));
1313 }
1314 
focusYieldWidgetDestroyed(QObject * obj)1315 void Strip::focusYieldWidgetDestroyed(QObject* obj)
1316 {
1317   if(obj != _focusYieldWidget)
1318     return;
1319   //disconnect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*)));
1320   _focusYieldWidget = nullptr;
1321 }
1322 
addGridWidget(QWidget * w,const GridPosStruct & pos,Qt::Alignment alignment)1323 void Strip::addGridWidget(QWidget* w, const GridPosStruct& pos, Qt::Alignment alignment)
1324 {
1325   grid->addWidget(w, pos._row, pos._col, pos._rowSpan, pos._colSpan, alignment);
1326 }
1327 
addGridLayout(QLayout * l,const GridPosStruct & pos,Qt::Alignment alignment)1328 void Strip::addGridLayout(QLayout* l, const GridPosStruct& pos, Qt::Alignment alignment)
1329 {
1330   grid->addLayout(l, pos._row, pos._col, pos._rowSpan, pos._colSpan, alignment);
1331 }
1332 
1333 //---------------------------------------------------------
1334 //   setAutomationType
1335 //---------------------------------------------------------
1336 
setAutomationType(int t)1337 void Strip::setAutomationType(int t)
1338 {
1339 // REMOVE Tim. mixer. Removed. TESTING...
1340 //   // If going to OFF mode, need to update current 'manual' values from the automation values at this time...
1341 //   if(t == AUTO_OFF && track->automationType() != AUTO_OFF) // && track->automationType() != AUTO_WRITE)
1342 //   {
1343 //     // May have a lot to do in updateCurValues, so try using idle.
1344 //     MusEGlobal::audio->msgIdle(true);
1345 //     track->setAutomationType(AutomationType(t));
1346 //     if(!track->isMidiTrack())
1347 //       (static_cast<MusECore::AudioTrack*>(track))->controller()->updateCurValues(MusEGlobal::audio->curFramePos());
1348 //     MusEGlobal::audio->msgIdle(false);
1349 //   }
1350 //   else
1351     // Try it within one message.
1352     MusEGlobal::audio->msgSetTrackAutomationType(track, t);
1353 
1354   MusEGlobal::song->update(SC_AUTOMATION);
1355 }
1356 
resizeEvent(QResizeEvent * ev)1357 void Strip::resizeEvent(QResizeEvent* ev)
1358 {
1359   DEBUG_STRIP(stderr, "Strip::resizeEvent\n");
1360   QFrame::resizeEvent(ev);
1361   setLabelText();
1362 }
1363 
updateRouteButtons()1364 void Strip::updateRouteButtons()
1365 {
1366     if (iR)
1367     {
1368         //      iR->setIconSetB(track->noInRoute());
1369         if (track->noInRoute()) {
1370             iR->setToolTip(MusEGlobal::noInputRoutingToolTipWarn);
1371             iR->setIcon(*routingInputUnconnectedSVGIcon);
1372         }  else {
1373             iR->setToolTip(MusEGlobal::inputRoutingToolTipBase);
1374             iR->setIcon(*routingInputSVGIcon);
1375         }
1376     }
1377 
1378     if (oR)
1379     {
1380         //    oR->setIconSetB(track->noOutRoute());
1381         if (track->noOutRoute()) {
1382             oR->setToolTip(MusEGlobal::noOutputRoutingToolTipWarn);
1383             oR->setIcon(*routingOutputUnconnectedSVGIcon);
1384         } else {
1385             oR->setToolTip(MusEGlobal::outputRoutingToolTipBase);
1386             oR->setIcon(*routingOutputSVGIcon);
1387         }
1388     }
1389 }
1390 
mousePressEvent(QMouseEvent * ev)1391 void Strip::mousePressEvent(QMouseEvent* ev)
1392 {
1393   ev->accept();
1394 
1395   // Only one button at a time.
1396   if(ev->buttons() ^ ev->button())
1397     return;
1398 
1399   QPoint mousePos = QCursor::pos();
1400   mouseWidgetOffset = pos() - mousePos;
1401 
1402   if (ev->button() == Qt::RightButton) {
1403     QMenu* menu = new QMenu;
1404 
1405     menu->addAction(new MenuTitleItem(tr("Configuration"), menu));
1406 
1407     QAction* act = menu->addAction(tr("Prefer Knobs, Not Sliders"));
1408     act->setData(int(2));
1409     act->setCheckable(true);
1410     act->setChecked(MusEGlobal::config.preferKnobsVsSliders);
1411 
1412     act = menu->addAction(tr("Show Values in Controls"));
1413     act->setData(int(3));
1414     act->setCheckable(true);
1415     act->setChecked(MusEGlobal::config.showControlValues);
1416 
1417     act = menu->addAction(tr("Prefer Midi Volume As Decibels"));
1418     act->setData(int(4));
1419     act->setCheckable(true);
1420     act->setChecked(MusEGlobal::config.preferMidiVolumeDb);
1421 
1422     menu->addSeparator();
1423 
1424     act = menu->addAction(tr("Monitor on Record-arm Automatically"));
1425     act->setData(int(5));
1426     act->setCheckable(true);
1427     act->setChecked(MusEGlobal::config.monitorOnRecord);
1428 
1429     QMenu* audioEffectsRackVisibleItemsMenu = new QMenu(tr("Visible Audio Effects"));
1430     QActionGroup* agroup = new QActionGroup(this);
1431     agroup->setExclusive(true);
1432     for(int i = 0; i <= MusECore::PipelineDepth; ++i)
1433     {
1434       act = audioEffectsRackVisibleItemsMenu->addAction(QString::number(i));
1435       act->setData(int(5000 + i));
1436       act->setCheckable(true);
1437       act->setActionGroup(agroup);
1438       if(i == MusEGlobal::config.audioEffectsRackVisibleItems)
1439         act->setChecked(MusEGlobal::config.monitorOnRecord);
1440     }
1441     menu->addMenu(audioEffectsRackVisibleItemsMenu);
1442 
1443     menu->addAction(new MenuTitleItem(tr("Actions"), menu));
1444 
1445     act = menu->addAction(tr("Change Track Name"));
1446     act->setData(int(1001));
1447 
1448     if(!_isEmbedded)
1449     {
1450 
1451 //      act = menu->addAction(tr("Remove track"));
1452 //      act->setData(int(0));
1453 //      menu->addSeparator();
1454       act = menu->addAction(tr("Hide Strip"));
1455       act->setData(int(1));
1456     }
1457 
1458     QPoint pt = QCursor::pos();
1459     act = menu->exec(pt, nullptr);
1460     if (!act)
1461     {
1462       delete menu;
1463       return;
1464     }
1465 
1466     DEBUG_STRIP("Menu finished, data returned %d\n", act->data().toInt());
1467 
1468     const int sel = act->data().toInt();
1469     const bool checked = act->isChecked();
1470     delete menu;
1471 
1472     switch(sel)
1473     {
1474       case 0:
1475         //  DEBUG_STRIP(stderr, "Strip:: delete track\n");
1476         //  MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(track), track));
1477       break;
1478 
1479       case 1:
1480         DEBUG_STRIP(stderr, "Strip:: setStripVisible false \n");
1481         setStripVisible(false);
1482         setVisible(false);
1483         emit visibleChanged(this, false);
1484       break;
1485 
1486       case 2:
1487         if(MusEGlobal::config.preferKnobsVsSliders != checked)
1488         {
1489           MusEGlobal::config.preferKnobsVsSliders = checked;
1490           MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version.
1491         }
1492       break;
1493 
1494       case 3:
1495         if(MusEGlobal::config.showControlValues != checked)
1496         {
1497           MusEGlobal::config.showControlValues = checked;
1498           MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version.
1499         }
1500       break;
1501 
1502       case 4:
1503         if(MusEGlobal::config.preferMidiVolumeDb != checked)
1504         {
1505           MusEGlobal::config.preferMidiVolumeDb = checked;
1506           MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version.
1507         }
1508       break;
1509 
1510       case 5:
1511         if(MusEGlobal::config.monitorOnRecord != checked)
1512         {
1513           MusEGlobal::config.monitorOnRecord = checked;
1514           MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version.
1515         }
1516       break;
1517 
1518       case 1001:
1519         changeTrackName();
1520       break;
1521 
1522       default:
1523         if(sel >= 5000 && sel <= 5000 + MusECore::PipelineDepth)
1524         {
1525           MusEGlobal::config.audioEffectsRackVisibleItems = sel - 5000;
1526           MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version.
1527         }
1528       break;
1529     }
1530 
1531     ev->accept();
1532     return;
1533   }
1534   else if (ev->button() == Qt::LeftButton)
1535   {
1536     if(!_isEmbedded)
1537     {
1538       if (ev->modifiers() & Qt::ControlModifier)
1539       {
1540         setSelected(!isSelected());
1541         track->setSelected(isSelected());
1542         MusEGlobal::song->update(SC_TRACK_SELECTION);
1543       }
1544       else
1545       {
1546         emit clearStripSelection();
1547         MusEGlobal::song->selectAllTracks(false);
1548         setSelected(true);
1549         track->setSelected(true);
1550         MusEGlobal::song->update(SC_TRACK_SELECTION);
1551       }
1552     }
1553   }
1554 }
1555 
sizeHint() const1556 QSize Strip::sizeHint() const
1557 {
1558   const QSize sz = QFrame::sizeHint();
1559 //  printf("*** Strip size hint width: %d ***\n", sz.width());
1560 //  const QSize szm = QFrame::minimumSizeHint();
1561 //  printf("*** Strip size minimum hint width: %d ***\n", szm.width());
1562   return QSize(sz.width() + (_isExpanded ? _userWidth : 0), sz.height());
1563 //   return QSize(_isExpanded ? _userWidth : 0, sz.height());
1564 }
1565 
setUserWidth(int w)1566 void Strip::setUserWidth(int w)
1567 {
1568   _userWidth = w;
1569   if(_userWidth < 0)
1570     _userWidth = 0;
1571 
1572   _isExpanded = _userWidth > 0;
1573 
1574 //   grid->invalidate();
1575 //   grid->activate();
1576 //   grid->update();
1577 //   adjustSize();
1578   updateGeometry();
1579 }
1580 
changeUserWidth(int delta)1581 void Strip::changeUserWidth(int delta)
1582 {
1583   if(!_isExpanded)
1584     _userWidth = 0;
1585   _userWidth += delta;
1586   if(_userWidth < 0)
1587     _userWidth = 0;
1588   _isExpanded = _userWidth > 0;
1589   updateGeometry();
1590   emit userWidthChanged(this, _userWidth);
1591 }
1592 
setExpanded(bool v)1593 void Strip::setExpanded(bool v)
1594 {
1595   if(_isExpanded == v)
1596     return;
1597   _isExpanded = v;
1598   if(_isExpanded && _userWidth <= 0)
1599     _userWidth = 60;
1600   updateGeometry();
1601 }
1602 
setEmbedded(bool embed)1603 void Strip::setEmbedded(bool embed)
1604 {
1605   _isEmbedded = embed;
1606   label->setHasExpandIcon(!embed);
1607 }
1608 
1609 
1610 //============================================================
1611 
1612 
1613 //---------------------------------------------------------
1614 //   ExpanderHandle
1615 //---------------------------------------------------------
1616 
ExpanderHandle(QWidget * parent,int handleWidth,Qt::WindowFlags f)1617 ExpanderHandle::ExpanderHandle(QWidget* parent, int handleWidth, Qt::WindowFlags f)
1618               : QFrame(parent, f), _handleWidth(handleWidth)
1619 {
1620   setObjectName("ExpanderHandle");
1621   setCursor(Qt::SplitHCursor);
1622   setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
1623   setFixedWidth(_handleWidth);
1624   setContentsMargins(0, 0, 0, 0);
1625  _resizeMode = ResizeModeNone;
1626 }
1627 
paintEvent(QPaintEvent * ev)1628 void ExpanderHandle::paintEvent(QPaintEvent * ev)
1629 {
1630   QPainter p(this);
1631 
1632   if(const QStyle* st = style())
1633   {
1634     st = st->proxy();
1635 
1636     QStyleOption o;
1637     o.initFrom(this);
1638     o.rect = rect();
1639     o.state = QStyle::State_Active | QStyle::State_Enabled;
1640     st->drawControl(QStyle::CE_Splitter, &o, &p);
1641   }
1642 
1643   ev->accept();
1644 }
1645 
mousePressEvent(QMouseEvent * e)1646 void ExpanderHandle::mousePressEvent(QMouseEvent* e)
1647 {
1648   // Only one button at a time.
1649 //   if(e->buttons() ^ e->button())
1650 //   {
1651 //     //_resizeMode = ResizeModeNone;
1652 //     //unsetCursor();
1653 //     e->accept();
1654 //     return;
1655 //   }
1656 
1657   //if(_resizeMode == ResizeModeHovering)
1658   //{
1659     // Switch to dragging mode.
1660     //_resizeMode = ResizeModeDragging;
1661     //ev->ignore();
1662     //return;
1663   //}
1664 
1665   switch(_resizeMode)
1666   {
1667     case ResizeModeNone:
1668     case ResizeModeHovering:
1669       _dragLastGlobPos = e->globalPos();
1670       _resizeMode = ResizeModeDragging;
1671       e->accept();
1672       return;
1673     break;
1674 
1675     case ResizeModeDragging:
1676       e->accept();
1677       return;
1678     break;
1679   }
1680 
1681   e->ignore();
1682   QFrame::mousePressEvent(e);
1683 }
1684 
mouseMoveEvent(QMouseEvent * e)1685 void ExpanderHandle::mouseMoveEvent(QMouseEvent* e)
1686 {
1687 //   const QPoint p = e->pos();
1688   switch(_resizeMode)
1689   {
1690     case ResizeModeNone:
1691     {
1692 //       if(p.x() >= (width() - frameWidth()) && p.x() < width() && p.y() >= 0 && p.y() < height())
1693 //       {
1694 //         _resizeMode = ResizeModeHovering;
1695 //         setCursor(Qt::SizeHorCursor);
1696 //       }
1697 //       e->accept();
1698 //       return;
1699     }
1700     break;
1701 
1702     case ResizeModeHovering:
1703     {
1704 //       if(p.x() < (width() - frameWidth()) || p.x() >= width() || p.y() < 0 || p.y() >= height())
1705 //       {
1706 //         _resizeMode = ResizeModeNone;
1707 //         unsetCursor();
1708 //       }
1709 //       e->accept();
1710 //       return;
1711     }
1712     break;
1713 
1714     case ResizeModeDragging:
1715     {
1716       const QPoint gp = e->globalPos();
1717       const QPoint delta = gp -_dragLastGlobPos;
1718       _dragLastGlobPos = gp;
1719       emit moved(delta.x());
1720       e->accept();
1721       return;
1722     }
1723     break;
1724   }
1725 
1726   e->ignore();
1727   QFrame::mouseMoveEvent(e);
1728 }
1729 
mouseReleaseEvent(QMouseEvent * e)1730 void ExpanderHandle::mouseReleaseEvent(QMouseEvent* e)
1731 {
1732 //   switch(_resizeMode)
1733 //   {
1734 //     case ResizeModeNone:
1735 //     case ResizeModeHovering:
1736 //     break;
1737 //
1738 //     case ResizeModeDragging:
1739 //     {
1740 //       const QPoint p = ev->pos();
1741 //       if(p.x() >= (width() - frameWidth()) && p.x() < width() && p.y() >= 0 && p.y() < height())
1742 //       {
1743 //         _resizeMode = ResizeModeHovering;
1744 //         setCursor(Qt::SizeHorCursor);
1745 //       }
1746 //       else
1747 //       {
1748 //         _resizeMode = ResizeModeNone;
1749 //         unsetCursor();
1750 //       }
1751 //       ev->ignore();
1752 //       return;
1753 //     }
1754 //     break;
1755 //   }
1756   _resizeMode = ResizeModeNone;
1757   e->ignore();
1758   QFrame::mouseReleaseEvent(e);
1759 }
1760 
1761 // void ExpanderHandle::leaveEvent(QEvent* e)
1762 // {
1763 //   e->ignore();
1764 //   QFrame::leaveEvent(e);
1765 //   switch(_resizeMode)
1766 //   {
1767 //     case ResizeModeDragging:
1768 //       return;
1769 //     break;
1770 //
1771 //     case ResizeModeHovering:
1772 //       _resizeMode = ResizeModeNone;
1773 //       Fall through...
1774 //     case ResizeModeNone:
1775 //       unsetCursor();
1776 //       return;
1777 //     break;
1778 //   }
1779 // }
1780 
sizeHint() const1781 QSize ExpanderHandle::sizeHint() const
1782 {
1783   QSize sz = QFrame::sizeHint();
1784   sz.setWidth(_handleWidth);
1785   return sz;
1786 }
1787 
1788 
mouseReleaseEvent(QMouseEvent * ev)1789 void Strip::mouseReleaseEvent(QMouseEvent* ev)
1790 {
1791   ev->accept();
1792   if (!_isEmbedded && dragOn) {
1793     emit moveStrip(this);
1794   }
1795   dragOn=false;
1796 }
1797 
mouseMoveEvent(QMouseEvent * e)1798 void Strip::mouseMoveEvent(QMouseEvent* e)
1799 {
1800   e->accept();
1801   if(e->buttons() == Qt::LeftButton)
1802   {
1803     if(!_isEmbedded)
1804     {
1805       if(dragOn)
1806       {
1807         QPoint mousePos = QCursor::pos();
1808         move(mousePos + mouseWidgetOffset);
1809       }
1810       else
1811       {
1812         raise();
1813         dragOn = true;
1814       }
1815     }
1816   }
1817 }
handleForwardedKeyPress(QKeyEvent * event)1818 bool Strip::handleForwardedKeyPress(QKeyEvent* event)
1819 {
1820   const int kb_code = event->key() | event->modifiers();
1821 
1822   if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_DOWN].key) {
1823         incVolume(-1);
1824         return true;
1825   }
1826   else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_UP].key) {
1827         incVolume(1);
1828         return true;
1829   }
1830   else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_LEFT].key) {
1831         incPan(-1);
1832         return true;
1833   }
1834   else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_RIGHT].key) {
1835         incPan(1);
1836         return true;
1837   }
1838   else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_DOWN_PAGE].key) {
1839         incVolume(-5);
1840         return true;
1841   }
1842   else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_UP_PAGE].key) {
1843         incVolume(5);
1844         return true;
1845   }
1846   else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_LEFT_PAGE].key) {
1847         incPan(-5);
1848         return true;
1849   }
1850   else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_RIGHT_PAGE].key) {
1851         incPan(5);
1852         return true;
1853   }
1854   else if (kb_code ==  MusEGui::shortcuts[MusEGui::SHRT_MUTE_CURRENT_TRACKS].key)
1855   {
1856       mute->setChecked(!mute->isChecked());
1857       return true;
1858   }
1859   else if (kb_code == MusEGui::shortcuts[MusEGui::SHRT_SOLO_CURRENT_TRACKS].key)
1860   {
1861       solo->setChecked(!solo->isChecked());
1862       return true;
1863   }
1864   return false;
1865 }
1866 
keyPressEvent(QKeyEvent * ev)1867 void Strip::keyPressEvent(QKeyEvent* ev)
1868 {
1869   // Set to accept by default.
1870   ev->accept();
1871 
1872   if (ev->key() == Qt::Key_Escape)
1873   {
1874     if(_focusYieldWidget)
1875     {
1876       // Yield the focus to the given widget.
1877       _focusYieldWidget->setFocus();
1878 
1879       // Activate the window.
1880       if(!_focusYieldWidget->isActiveWindow())
1881       {
1882         _focusYieldWidget->activateWindow();
1883       }
1884       // Yield the focus to the given widget.
1885       _focusYieldWidget->setFocus();
1886       // Activate the window.
1887       if(!_focusYieldWidget->isActiveWindow())
1888         _focusYieldWidget->activateWindow();
1889       return;
1890     }
1891   }
1892 
1893   bool handled = handleForwardedKeyPress(ev);
1894   if (handled)
1895       return;
1896 
1897   // Let mixer window or other higher up handle it.
1898   ev->ignore();
1899   QFrame::keyPressEvent(ev);
1900 }
1901 
setSelected(bool v)1902 void Strip::setSelected(bool v)
1903 {
1904     if(_selected == v)
1905         return;
1906 
1907     if(_isEmbedded)
1908     {
1909         _selected = false;
1910         return;
1911     }
1912     if (v) {
1913         if (label->style3d())
1914             label->setFrameStyle(Raised | StyledPanel);
1915         setHighLight(true);
1916         // First time selected? Set the focus.
1917         setFocus();
1918     }
1919     else {
1920         if (label->style3d())
1921             label->setFrameStyle(Sunken | StyledPanel);
1922         setHighLight(false);
1923     }
1924     _selected=v;
1925 }
1926 
setHighLight(bool highlight)1927 void Strip::setHighLight(bool highlight)
1928 {
1929   _highlight = highlight;
1930   update();
1931 }
1932 
getLabelText()1933 QString Strip::getLabelText()
1934 {
1935   return label->text();
1936 }
1937 
updateMuteIcon()1938 void Strip::updateMuteIcon()
1939 {
1940     if(!track)
1941         return;
1942 
1943     bool found = false;
1944     MusECore::TrackList* tl = MusEGlobal::song->tracks();
1945     for(MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it)
1946     {
1947         MusECore::Track* t = *it;
1948         // Ignore this track.
1949         if(t != track && (t->internalSolo() || t->solo()))
1950         {
1951             found = true;
1952             break;
1953         }
1954     }
1955     //  mute->setIconSetB(found && !track->internalSolo() && !track->solo());
1956     if (found && !track->internalSolo() && !track->solo()) {
1957         if (mute->isChecked())
1958             mute->setIcon(*muteAndProxyOnSVGIcon);
1959         else
1960             mute->setIcon(*muteProxyOnSVGIcon);
1961     } else {
1962         mute->setIcon(*muteStateSVGIcon);
1963     }
1964 }
1965 
1966 
1967 
1968 //--------------------------------
1969 //    Control ganging support.
1970 //--------------------------------
1971 
1972 struct MidiIncListStruct
1973 {
1974   int _port;
1975   int _chan;
MidiIncListStructMusEGui::MidiIncListStruct1976   MidiIncListStruct(int port, int chan) : _port(port), _chan(chan) { }
operator ==MusEGui::MidiIncListStruct1977   bool operator==(const MidiIncListStruct& other) { return other._port == _port && other._chan == _chan; }
1978 };
1979 
componentChanged(int type,double val,bool off,int id,int scrollMode)1980 void Strip::componentChanged(int type, double val, bool off, int id, int scrollMode)
1981 {
1982   // The track has already taken care of its own controllers.
1983   // Don't bother other tracks if the track is not selected. ie part of a selected group.
1984   // Don't bother if broadcasting changes is disabled.
1985   if(!track || !track->selected() || !_broadcastChanges)
1986     return;
1987 
1988   // TODO: Only controller components handled for now.
1989   if(type != ComponentRack::controllerComponent)
1990     return;
1991 
1992   QList<MidiIncListStruct> doneMidiTracks;
1993   QList<MusECore::Track*> doneAudioTracks;
1994 
1995   if(track->isMidiTrack())
1996   {
1997     // TODO: Only volume and pan handled for now.
1998     int a_ctlnum;
1999     switch(id)
2000     {
2001       case MusECore::CTRL_VOLUME:
2002         a_ctlnum = MusECore::AC_VOLUME;
2003       break;
2004       case MusECore::CTRL_PANPOT:
2005         a_ctlnum = MusECore::AC_PAN;
2006       break;
2007       default:
2008         return;
2009       break;
2010     }
2011 
2012     MusECore::MidiTrack* m_track = static_cast<MusECore::MidiTrack*>(track);
2013     const int m_port  = m_track->outPort();
2014     const int m_chan  = m_track->outChannel();
2015     MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port];
2016     MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan,  false);
2017     if(!m_mctl)
2018       return;
2019 
2020     int i_m_min = m_mctl->minVal();
2021     const int i_m_max = m_mctl->maxVal();
2022     const int i_m_bias = m_mctl->bias();
2023 
2024     //----------------------------------------------------------
2025     // For midi volume, the formula given by MMA is:
2026     //  volume dB = 40 * log(midivol / 127)  and the inverse is:
2027     //  midi volume = 127 * 10 ^ (volume dB / 40)
2028     // Unusual, it is a factor of 40 not 20. Since muse_db2val()
2029     //  does 10 ^ (volume dB / 20), just divide volume dB by 2.
2030     //----------------------------------------------------------
2031     double m_val = val;
2032     double ma_val = val;
2033     if(id == MusECore::CTRL_VOLUME)
2034     {
2035       if(MusEGlobal::config.preferMidiVolumeDb)
2036       {
2037         if(ma_val <= MusEGlobal::config.minSlider)
2038           m_val = ma_val = 0.0;
2039         else
2040         {
2041           m_val = double(i_m_max) * muse_db2val(m_val);
2042           ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0);
2043         }
2044       }
2045       else
2046       {
2047         // It's a linear scale. We need to convert to Db and
2048         //  scale the linear value by 2 since the midi scale is
2049         //  twice the usual Db per step. Then convert back to linear.
2050         m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0;
2051         m_val *= 2.0;
2052         m_val = double(i_m_max) * muse_db2val(m_val / 2.0);
2053       }
2054     }
2055 
2056     //--------------------------------------------------------------
2057     //   NOTE: Midi int to audio float conversion:
2058     //   If the control has a bias at all, it is supposed
2059     //    to define the 'middle', like pan.
2060     //   But if the control's range is odd (127), that
2061     //    makes an uneven, uncentered float conversion.
2062     //   So the policy here is to force an even range for
2063     //    symmetrical +/- float conversion. (0.0 in the middle.)
2064     //   Treat value '0' (-64 pan) and '1' (-63 pan) the same.
2065     //--------------------------------------------------------------
2066     if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1))
2067       ++i_m_min;
2068     const int i_m_range = i_m_max - i_m_min;
2069     if(i_m_range == 0) // Avoid divide by zero.
2070       return;
2071 
2072     if(m_val < i_m_min)
2073      m_val = i_m_min;
2074     if(m_val > i_m_max)
2075      m_val = i_m_max;
2076     const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range;
2077 
2078     // Make sure to include this track as already done.
2079     doneMidiTracks.append(MidiIncListStruct(m_port, m_chan));
2080 
2081     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2082     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2083     {
2084       MusECore::Track* t = *it;
2085       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2086       if(t == track || !t->selected())
2087         continue;
2088 
2089       if(t->isMidiTrack())
2090       {
2091         MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(t);
2092         const int port     = mt->outPort();
2093         const int chan  = mt->outChannel();
2094         // Make sure midi tracks on same port and channel are only done once.
2095         const MidiIncListStruct mils(port, chan);
2096         if(doneMidiTracks.contains(mils))
2097           continue;
2098         doneMidiTracks.append(mils);
2099 
2100         double d_val = ma_val;
2101         MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port];
2102         MusECore::MidiController* mctl = mp->midiController(id, chan, false);
2103         if(!mctl)
2104           continue;
2105         if(off || (d_val < double(mctl->minVal())) || (d_val > double(mctl->maxVal())))
2106         {
2107           if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN)
2108             mp->putHwCtrlEvent(MusECore::MidiPlayEvent(0, port, chan,
2109                                                       MusECore::ME_CONTROLLER,
2110                                                       id,
2111                                                       MusECore::CTRL_VAL_UNKNOWN));
2112         }
2113         else
2114         {
2115           d_val += double(mctl->bias());
2116           mp->putControllerValue(port, chan, id, d_val, false);
2117         }
2118       }
2119       else
2120       {
2121         // Make sure we're not doing the same track more than once.
2122         if(doneAudioTracks.contains(t))
2123           continue;
2124         doneAudioTracks.append(t);
2125 
2126         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
2127         MusECore::iCtrlList icl = at->controller()->find(a_ctlnum);
2128         if(icl == at->controller()->end())
2129           continue;
2130         MusECore::CtrlList* cl = icl->second;
2131 
2132         // The audio volume can go above 0dB (amplification) while
2133         //  the top midi value of 127 represents 0dB. Cut it off at 0dB.
2134         const double a_min = cl->minVal();
2135         const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal();
2136         const double a_range = a_max - a_min;
2137         const double a_val = (m_fact * a_range) + a_min;
2138 
2139         // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals.
2140         // ScrDirect mode is one-time only on press with modifier.
2141         if(scrollMode != SliderBase::ScrDirect)
2142           at->recordAutomation(a_ctlnum, a_val);
2143         at->setParam(a_ctlnum, a_val);  // Schedules a timed control change.
2144         at->enableController(a_ctlnum, false);
2145       }
2146     }
2147   }
2148   else
2149   {
2150     // TODO: Only volume and pan handled for now.
2151     int m_ctlnum;
2152     switch(id)
2153     {
2154       case MusECore::AC_VOLUME:
2155         m_ctlnum = MusECore::CTRL_VOLUME;
2156       break;
2157       case MusECore::AC_PAN:
2158         m_ctlnum = MusECore::CTRL_PANPOT;
2159       break;
2160       default:
2161         return;
2162       break;
2163     }
2164 
2165     MusECore::AudioTrack* a_track = static_cast<MusECore::AudioTrack*>(track);
2166     MusECore::iCtrlList icl = a_track->controller()->find(id);
2167     if(icl == a_track->controller()->end())
2168       return;
2169     MusECore::CtrlList* cl = icl->second;
2170 
2171     //----------------------------------------------------------
2172     // For midi volume, the formula given by MMA is:
2173     //  volume dB = 40 * log(midivol / 127)  and the inverse is:
2174     //  midi volume = 127 * 10 ^ (volume dB / 40)
2175     // Unusual, it is a factor of 40 not 20. Since muse_db2val()
2176     //  does 10 ^ (volume dB / 20), just divide volume dB by 2.
2177     //----------------------------------------------------------
2178     double a_val = val;
2179     double ma_val = val;
2180     if(id == MusECore::AC_VOLUME)
2181     {
2182       if(ma_val <= MusEGlobal::config.minSlider)
2183         a_val = ma_val = 0.0;
2184       else
2185       {
2186         a_val = muse_db2val(a_val);
2187         ma_val = muse_db2val(ma_val / 2.0);
2188       }
2189     }
2190 
2191     // The audio volume can go above 0dB (amplification) while
2192     //  the top midi value of 127 represents 0dB. Cut it off at 0dB.
2193     const double a_min = cl->minVal();
2194     const double a_max = (id == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal();
2195     const double a_range = a_max - a_min;
2196     if(a_range < 0.0001) // Avoid divide by zero.
2197       return;
2198     const double a_fact = (ma_val - a_min) / a_range;
2199 
2200     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2201     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2202     {
2203       MusECore::Track* t = *it;
2204       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2205       if(t == track || !t->selected())
2206         continue;
2207       if(t->isMidiTrack())
2208       {
2209         MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(t);
2210         const int port     = mt->outPort();
2211         const int chan  = mt->outChannel();
2212         // Make sure midi tracks on same port and channel are only done once.
2213         const MidiIncListStruct mils(port, chan);
2214         if(doneMidiTracks.contains(mils))
2215           continue;
2216         doneMidiTracks.append(mils);
2217 
2218         MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port];
2219         MusECore::MidiController* mctl = mp->midiController(m_ctlnum, chan, false);
2220         if(mctl)
2221         {
2222           int min = mctl->minVal();
2223           const int max = mctl->maxVal();
2224           const int bias = mctl->bias();
2225 
2226           //--------------------------------------------------------------
2227           //   NOTE: Midi int to audio float conversion:
2228           //   If the control has a bias at all, it is supposed
2229           //    to define the 'middle', like pan.
2230           //   But if the control's range is odd (127), that
2231           //    makes an uneven, uncentered float conversion.
2232           //   So the policy here is to force an even range for
2233           //    symmetrical +/- float conversion. (0.0 in the middle.)
2234           //   Treat value '0' (-64 pan) and '1' (-63 pan) the same.
2235           //--------------------------------------------------------------
2236           if(bias != 0 && ((max - min) & 0x1))
2237             ++min;
2238 
2239           const double d_min = (double)min;
2240           const double d_range = double(max - min);
2241           double d_val = a_fact * d_range + d_min;
2242 
2243           if(d_val < double(mctl->minVal()))
2244             d_val = mctl->minVal();
2245           if(d_val > double(mctl->maxVal()))
2246             d_val = mctl->maxVal();
2247           d_val += double(mctl->bias());
2248           mp->putControllerValue(port, chan, m_ctlnum, d_val, false);
2249         }
2250       }
2251       else
2252       {
2253         // Make sure we're not doing the same track more than once.
2254         if(doneAudioTracks.contains(t))
2255           continue;
2256         doneAudioTracks.append(t);
2257 
2258         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
2259         // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals.
2260         // ScrDirect mode is one-time only on press with modifier.
2261         if(scrollMode != SliderBase::ScrDirect)
2262           at->recordAutomation(id, a_val);
2263         at->setParam(id, a_val);  // Schedules a timed control change.
2264         at->enableController(id, false);
2265       }
2266     }
2267   }
2268 }
2269 
componentMoved(int,double,int,bool)2270 void Strip::componentMoved(int /*type*/, double /*val*/, int /*id*/, bool /*shift_pressed*/)
2271 {
2272   //emit componentMoved(this, type, val, id, shift_pressed);
2273 }
2274 
componentPressed(int type,double val,int id)2275 void Strip::componentPressed(int type, double val, int id)
2276 {
2277   // The track has already taken care of its own controllers.
2278   // Don't bother other tracks if the track is not selected. ie part of a selected group.
2279   // Don't bother if broadcasting changes is disabled.
2280   if(!track || !track->selected() || !_broadcastChanges)
2281     return;
2282 
2283   // TODO: Only controller components handled for now.
2284   if(type != ComponentRack::controllerComponent)
2285     return;
2286 
2287   QList<MidiIncListStruct> doneMidiTracks;
2288   QList<MusECore::Track*> doneAudioTracks;
2289 
2290   if(track->isMidiTrack())
2291   {
2292     // TODO: Only volume and pan handled for now.
2293     int a_ctlnum;
2294     switch(id)
2295     {
2296       case MusECore::CTRL_VOLUME:
2297         a_ctlnum = MusECore::AC_VOLUME;
2298       break;
2299       case MusECore::CTRL_PANPOT:
2300         a_ctlnum = MusECore::AC_PAN;
2301       break;
2302       default:
2303         return;
2304       break;
2305     }
2306 
2307     MusECore::MidiTrack* m_track = static_cast<MusECore::MidiTrack*>(track);
2308     const int m_port  = m_track->outPort();
2309     const int m_chan  = m_track->outChannel();
2310     MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port];
2311     MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan, false);
2312     if(!m_mctl)
2313       return;
2314 
2315     int i_m_min = m_mctl->minVal();
2316     const int i_m_max = m_mctl->maxVal();
2317     const int i_m_bias = m_mctl->bias();
2318 
2319     //----------------------------------------------------------
2320     // For midi volume, the formula given by MMA is:
2321     //  volume dB = 40 * log(midivol / 127)  and the inverse is:
2322     //  midi volume = 127 * 10 ^ (volume dB / 40)
2323     // Unusual, it is a factor of 40 not 20. Since muse_db2val()
2324     //  does 10 ^ (volume dB / 20), just divide volume dB by 2.
2325     //----------------------------------------------------------
2326     double m_val = val;
2327     double ma_val = val;
2328     if(id == MusECore::CTRL_VOLUME)
2329     {
2330       if(MusEGlobal::config.preferMidiVolumeDb)
2331       {
2332         if(ma_val <= MusEGlobal::config.minSlider)
2333           m_val = ma_val = 0.0;
2334         else
2335         {
2336           m_val = double(i_m_max) * muse_db2val(m_val);
2337           ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0);
2338         }
2339       }
2340       else
2341       {
2342         // It's a linear scale. We need to convert to Db and
2343         //  scale the linear value by 2 since the midi scale is
2344         //  twice the usual Db per step. Then convert back to linear.
2345         m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0;
2346         m_val *= 2.0;
2347         m_val = double(i_m_max) * muse_db2val(m_val / 2.0);
2348       }
2349     }
2350 
2351     //--------------------------------------------------------------
2352     //   NOTE: Midi int to audio float conversion:
2353     //   If the control has a bias at all, it is supposed
2354     //    to define the 'middle', like pan.
2355     //   But if the control's range is odd (127), that
2356     //    makes an uneven, uncentered float conversion.
2357     //   So the policy here is to force an even range for
2358     //    symmetrical +/- float conversion. (0.0 in the middle.)
2359     //   Treat value '0' (-64 pan) and '1' (-63 pan) the same.
2360     //--------------------------------------------------------------
2361     if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1))
2362       ++i_m_min;
2363     const int i_m_range = i_m_max - i_m_min;
2364     if(i_m_range == 0) // Avoid divide by zero.
2365       return;
2366 
2367     if(m_val < i_m_min)
2368      m_val = i_m_min;
2369     if(m_val > i_m_max)
2370      m_val = i_m_max;
2371     const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range;
2372 
2373     // Make sure to include this track as already done.
2374     doneMidiTracks.append(MidiIncListStruct(m_port, m_chan));
2375 
2376     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2377     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2378     {
2379       MusECore::Track* t = *it;
2380       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2381       if(t == track || !t->selected())
2382         continue;
2383 
2384       if(t->isMidiTrack())
2385       {
2386       }
2387       else
2388       {
2389         // Make sure we're not doing the same track more than once.
2390         if(doneAudioTracks.contains(t))
2391           continue;
2392         doneAudioTracks.append(t);
2393 
2394         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
2395         MusECore::iCtrlList icl = at->controller()->find(a_ctlnum);
2396         if(icl == at->controller()->end())
2397           continue;
2398         MusECore::CtrlList* cl = icl->second;
2399 
2400         // The audio volume can go above 0dB (amplification) while
2401         //  the top midi value of 127 represents 0dB. Cut it off at 0dB.
2402         const double a_min = cl->minVal();
2403         const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal();
2404         const double a_range = a_max - a_min;
2405         const double a_val = (m_fact * a_range) + a_min;
2406 
2407         at->startAutoRecord(a_ctlnum, a_val);
2408         at->setPluginCtrlVal(a_ctlnum, a_val);
2409         //at->setParam(a_ctlnum, val);   // Schedules a timed control change. // TODO Try this instead
2410         at->enableController(a_ctlnum, false);
2411       }
2412     }
2413   }
2414   else
2415   {
2416     // TODO: Only volume and pan handled for now.
2417     switch(id)
2418     {
2419       case MusECore::AC_VOLUME:
2420       break;
2421       case MusECore::AC_PAN:
2422       break;
2423 
2424       default:
2425         return;
2426       break;
2427     }
2428 
2429     double a_val = val;
2430     if(id == MusECore::AC_VOLUME)
2431     {
2432       if(a_val <= MusEGlobal::config.minSlider)
2433         a_val = 0.0;
2434       else
2435         a_val = muse_db2val(a_val);
2436     }
2437 
2438     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2439     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2440     {
2441       MusECore::Track* t = *it;
2442       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2443       if(t == track || !t->selected())
2444         continue;
2445       if(t->isMidiTrack())
2446       {
2447       }
2448       else
2449       {
2450         // Make sure we're not doing the same track more than once.
2451         if(doneAudioTracks.contains(t))
2452           continue;
2453         doneAudioTracks.append(t);
2454 
2455         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
2456         at->startAutoRecord(id, a_val);
2457         at->setPluginCtrlVal(id, a_val);
2458         //at->setParam(id, val);   // Schedules a timed control change. // TODO Try this instead
2459         at->enableController(id, false);
2460       }
2461     }
2462   }
2463 }
2464 
componentReleased(int type,double val,int id)2465 void Strip::componentReleased(int type, double val, int id)
2466 {
2467   // The track has already taken care of its own controllers.
2468   // Don't bother other tracks if the track is not selected. ie part of a selected group.
2469   // Don't bother if broadcasting changes is disabled.
2470   if(!track || !track->selected() || !_broadcastChanges)
2471     return;
2472 
2473   // TODO: Only controller components handled for now.
2474   if(type != ComponentRack::controllerComponent)
2475     return;
2476 
2477   QList<MidiIncListStruct> doneMidiTracks;
2478   QList<MusECore::Track*> doneAudioTracks;
2479 
2480   if(track->isMidiTrack())
2481   {
2482     // TODO: Only volume and pan handled for now.
2483     int a_ctlnum;
2484     switch(id)
2485     {
2486       case MusECore::CTRL_VOLUME:
2487         a_ctlnum = MusECore::AC_VOLUME;
2488       break;
2489       case MusECore::CTRL_PANPOT:
2490         a_ctlnum = MusECore::AC_PAN;
2491       break;
2492       default:
2493         return;
2494       break;
2495     }
2496 
2497     MusECore::MidiTrack* m_track = static_cast<MusECore::MidiTrack*>(track);
2498     const int m_port  = m_track->outPort();
2499     const int m_chan  = m_track->outChannel();
2500     MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port];
2501     MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan, false);
2502     if(!m_mctl)
2503       return;
2504 
2505     int i_m_min = m_mctl->minVal();
2506     const int i_m_max = m_mctl->maxVal();
2507     const int i_m_bias = m_mctl->bias();
2508 
2509     //----------------------------------------------------------
2510     // For midi volume, the formula given by MMA is:
2511     //  volume dB = 40 * log(midivol / 127)  and the inverse is:
2512     //  midi volume = 127 * 10 ^ (volume dB / 40)
2513     // Unusual, it is a factor of 40 not 20. Since muse_db2val()
2514     //  does 10 ^ (volume dB / 20), just divide volume dB by 2.
2515     //----------------------------------------------------------
2516     double m_val = val;
2517     double ma_val = val;
2518     if(id == MusECore::CTRL_VOLUME)
2519     {
2520       if(MusEGlobal::config.preferMidiVolumeDb)
2521       {
2522         if(ma_val <= MusEGlobal::config.minSlider)
2523           m_val = ma_val = 0.0;
2524         else
2525         {
2526           m_val = double(i_m_max) * muse_db2val(m_val);
2527           ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0);
2528         }
2529       }
2530       else
2531       {
2532         // It's a linear scale. We need to convert to Db and
2533         //  scale the linear value by 2 since the midi scale is
2534         //  twice the usual Db per step. Then convert back to linear.
2535         m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0;
2536         m_val *= 2.0;
2537         m_val = double(i_m_max) * muse_db2val(m_val / 2.0);
2538       }
2539     }
2540 
2541     //--------------------------------------------------------------
2542     //   NOTE: Midi int to audio float conversion:
2543     //   If the control has a bias at all, it is supposed
2544     //    to define the 'middle', like pan.
2545     //   But if the control's range is odd (127), that
2546     //    makes an uneven, uncentered float conversion.
2547     //   So the policy here is to force an even range for
2548     //    symmetrical +/- float conversion. (0.0 in the middle.)
2549     //   Treat value '0' (-64 pan) and '1' (-63 pan) the same.
2550     //--------------------------------------------------------------
2551     if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1))
2552       ++i_m_min;
2553     const int i_m_range = i_m_max - i_m_min;
2554     if(i_m_range == 0) // Avoid divide by zero.
2555       return;
2556 
2557     if(m_val < i_m_min)
2558      m_val = i_m_min;
2559     if(m_val > i_m_max)
2560      m_val = i_m_max;
2561     const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range;
2562 
2563     // Make sure to include this track as already done.
2564     doneMidiTracks.append(MidiIncListStruct(m_port, m_chan));
2565 
2566     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2567     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2568     {
2569       MusECore::Track* t = *it;
2570       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2571       if(t == track || !t->selected())
2572         continue;
2573 
2574       if(t->isMidiTrack())
2575       {
2576       }
2577       else
2578       {
2579         // Make sure we're not doing the same track more than once.
2580         if(doneAudioTracks.contains(t))
2581           continue;
2582         doneAudioTracks.append(t);
2583 
2584         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
2585         MusECore::iCtrlList icl = at->controller()->find(a_ctlnum);
2586         if(icl == at->controller()->end())
2587           continue;
2588         MusECore::CtrlList* cl = icl->second;
2589 
2590         // The audio volume can go above 0dB (amplification) while
2591         //  the top midi value of 127 represents 0dB. Cut it off at 0dB.
2592         const double a_min = cl->minVal();
2593         const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal();
2594         const double a_range = a_max - a_min;
2595         const double a_val = (m_fact * a_range) + a_min;
2596 
2597         MusECore::AutomationType atype = at->automationType();
2598         at->stopAutoRecord(a_ctlnum, a_val);
2599         if(atype == MusECore::AUTO_OFF || atype == MusECore::AUTO_TOUCH)
2600           at->enableController(a_ctlnum, true);
2601       }
2602     }
2603   }
2604   else
2605   {
2606     // TODO: Only volume and pan handled for now.
2607     switch(id)
2608     {
2609       case MusECore::AC_VOLUME:
2610       break;
2611       case MusECore::AC_PAN:
2612       break;
2613 
2614       default:
2615         return;
2616       break;
2617     }
2618 
2619     //----------------------------------------------------------
2620     // For midi volume, the formula given by MMA is:
2621     //  volume dB = 40 * log(midivol / 127)  and the inverse is:
2622     //  midi volume = 127 * 10 ^ (volume dB / 40)
2623     // Unusual, it is a factor of 40 not 20. Since muse_db2val()
2624     //  does 10 ^ (volume dB / 20), just divide volume dB by 2.
2625     //----------------------------------------------------------
2626     double a_val = val;
2627     if(id == MusECore::AC_VOLUME)
2628     {
2629       if(a_val <= MusEGlobal::config.minSlider)
2630         a_val = 0.0;
2631       else
2632         a_val = muse_db2val(a_val);
2633     }
2634 
2635     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2636     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2637     {
2638       MusECore::Track* t = *it;
2639       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2640       if(t == track || !t->selected())
2641         continue;
2642       if(t->isMidiTrack())
2643       {
2644       }
2645       else
2646       {
2647         // Make sure we're not doing the same track more than once.
2648         if(doneAudioTracks.contains(t))
2649           continue;
2650         doneAudioTracks.append(t);
2651 
2652         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
2653         MusECore::AutomationType atype = at->automationType();
2654         at->stopAutoRecord(id, a_val);
2655         if(atype == MusECore::AUTO_OFF || atype == MusECore::AUTO_TOUCH)
2656           at->enableController(id, true);
2657       }
2658     }
2659   }
2660 }
2661 
componentIncremented(int type,double oldCompVal,double newCompVal,bool off,int id,int)2662 void Strip::componentIncremented(int type, double oldCompVal, double newCompVal,
2663                                  bool off, int id, int /*scrollMode*/)
2664 {
2665   // The track has already taken care of its own controllers.
2666   // Don't bother other tracks if the track is not selected. ie part of a selected group.
2667   // Don't bother if broadcasting changes is disabled.
2668   if(!track || !track->selected() || !_broadcastChanges)
2669     return;
2670 
2671   // TODO: Only controller components handled for now.
2672   if(type != ComponentRack::controllerComponent)
2673     return;
2674 
2675   bool wait_required = false;
2676   QList<MidiIncListStruct> doneMidiTracks;
2677   QList<MusECore::Track*> doneAudioTracks;
2678 
2679   const double d_comp_val_delta = newCompVal - oldCompVal;
2680 
2681   if(track->isMidiTrack())
2682   {
2683     // TODO: Only volume and pan handled for now.
2684     int a_ctlnum;
2685     switch(id)
2686     {
2687       case MusECore::CTRL_VOLUME:
2688         a_ctlnum = MusECore::AC_VOLUME;
2689       break;
2690       case MusECore::CTRL_PANPOT:
2691         a_ctlnum = MusECore::AC_PAN;
2692       break;
2693       default:
2694         return;
2695       break;
2696     }
2697 
2698     MusECore::MidiTrack* m_track = static_cast<MusECore::MidiTrack*>(track);
2699     const int m_port  = m_track->outPort();
2700     const int m_chan  = m_track->outChannel();
2701     MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port];
2702     MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan, false);
2703     if(!m_mctl)
2704       return;
2705 
2706     const int i_m_min = m_mctl->minVal();
2707     const int i_m_max = m_mctl->maxVal();
2708     const int i_m_bias = m_mctl->bias();
2709     int i_ma_min = i_m_min;
2710     const int i_ma_max = i_m_max;
2711 
2712     //----------------------------------------------------------
2713     // For midi volume, the formula given by MMA is:
2714     //  volume dB = 40 * log(midivol / 127)  and the inverse is:
2715     //  midi volume = 127 * 10 ^ (volume dB / 40)
2716     // Unusual, it is a factor of 40 not 20. Since muse_db2val()
2717     //  does 10 ^ (volume dB / 20), just divide volume dB by 2.
2718     //----------------------------------------------------------
2719 
2720     double m_old_val = oldCompVal;
2721     double ma_old_val = oldCompVal;
2722     double m_new_val = newCompVal;
2723     double ma_new_val = newCompVal;
2724 
2725     if(id == MusECore::CTRL_VOLUME)
2726     {
2727       if(MusEGlobal::config.preferMidiVolumeDb)
2728       {
2729         if(ma_old_val <= MusEGlobal::config.minSlider)
2730           m_old_val = ma_old_val = 0.0;
2731         else
2732         {
2733           m_old_val = double(i_m_max) * muse_db2val(m_old_val / 2.0);
2734           ma_old_val = double(i_ma_max) * muse_db2val(ma_old_val);
2735         }
2736 
2737         if(ma_new_val <= MusEGlobal::config.minSlider)
2738           m_new_val = ma_new_val = 0.0;
2739         else
2740         {
2741           m_new_val = double(i_m_max) * muse_db2val(m_new_val / 2.0);
2742           ma_new_val = double(i_ma_max) * muse_db2val(ma_new_val);
2743         }
2744       }
2745       else
2746       {
2747         // It's a linear scale. We need to convert to Db and
2748         //  scale the linear value by 2 since the midi scale is
2749         //  twice the usual Db per step. Then convert back to linear.
2750         ma_old_val = muse_val2dbr(ma_old_val / double(i_ma_max)) * 2.0;
2751         ma_old_val *= 2.0;
2752         ma_old_val = double(i_ma_max) * muse_db2val(ma_old_val / 2.0);
2753 
2754         ma_new_val = muse_val2dbr(ma_new_val / double(i_ma_max)) * 2.0;
2755         ma_new_val *= 2.0;
2756         ma_new_val = double(i_ma_max) * muse_db2val(ma_new_val / 2.0);
2757       }
2758     }
2759 
2760     //--------------------------------------------------------------
2761     //   NOTE: Midi int to audio float conversion:
2762     //   If the control has a bias at all, it is supposed
2763     //    to define the 'middle', like pan.
2764     //   But if the control's range is odd (127), that
2765     //    makes an uneven, uncentered float conversion.
2766     //   So the policy here is to force an even range for
2767     //    symmetrical +/- float conversion. (0.0 in the middle.)
2768     //   Treat value '0' (-64 pan) and '1' (-63 pan) the same.
2769     //--------------------------------------------------------------
2770     if(i_m_bias != 0 && ((i_ma_max - i_ma_min) & 0x1))
2771       ++i_ma_min;
2772     const int i_m_range = i_m_max - i_m_min;
2773     const int i_ma_range = i_ma_max - i_ma_min;
2774 
2775     if(m_old_val < i_m_min)
2776      m_old_val = i_m_min;
2777     if(m_old_val > i_m_max)
2778      m_old_val = i_m_max;
2779 
2780     if(m_new_val < i_m_min)
2781      m_new_val = i_m_min;
2782     if(m_new_val > i_m_max)
2783      m_new_val = i_m_max;
2784 
2785     if(ma_old_val < i_ma_min)
2786      ma_old_val = i_ma_min;
2787     if(ma_old_val > i_ma_max)
2788      ma_old_val = i_ma_max;
2789 
2790     if(ma_new_val < i_ma_min)
2791      ma_new_val = i_ma_min;
2792     if(ma_new_val > i_ma_max)
2793      ma_new_val = i_ma_max;
2794 
2795     if(i_m_range == 0 || i_ma_range == 0) // Avoid divide by zero.
2796       return;
2797 
2798     // Get the current or last valid actual controller value.
2799     const double m_val_delta = m_new_val - m_old_val;
2800     const double ma_val_delta = ma_new_val - ma_old_val;
2801 
2802     const double ma_new_delta_fact = ma_val_delta / (double)i_ma_range;
2803 
2804     // Make sure to include this track as already done.
2805     doneMidiTracks.append(MidiIncListStruct(m_port, m_chan));
2806 
2807     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2808     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2809     {
2810       MusECore::Track* t = *it;
2811       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2812       if(t == track || !t->selected())
2813         continue;
2814 
2815       if(t->isMidiTrack())
2816       {
2817         MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(t);
2818         const int port     = mt->outPort();
2819         const int chan  = mt->outChannel();
2820         // Make sure midi tracks on same port and channel are only done once.
2821         const MidiIncListStruct mils(port, chan);
2822         if(doneMidiTracks.contains(mils))
2823           continue;
2824         doneMidiTracks.append(mils);
2825 
2826         MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port];
2827         MusECore::MidiController* mctl = mp->midiController(id, chan, false);
2828         if(!mctl)
2829           continue;
2830         int min = mctl->minVal();
2831         const int max = mctl->maxVal();
2832 
2833         if(off)
2834         {
2835           if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN)
2836             mp->putHwCtrlEvent(MusECore::MidiPlayEvent(0, port, chan,
2837                                                       MusECore::ME_CONTROLLER,
2838                                                       id,
2839                                                       MusECore::CTRL_VAL_UNKNOWN));
2840         }
2841         else
2842         {
2843           double d_fin_val = m_new_val;
2844           // Get the current or last valid value.
2845           double d_cur_val =  mp->hwDCtrlState(chan, id);
2846           if(MusECore::MidiController::dValIsUnknown(d_cur_val))
2847             d_cur_val = mp->lastValidHWDCtrlState(chan, id);
2848           if(MusECore::MidiController::dValIsUnknown(d_cur_val))
2849             d_fin_val = m_new_val;
2850           else
2851           {
2852             d_cur_val = d_cur_val - double(mctl->bias());
2853             if(id == MusECore::CTRL_VOLUME && MusEGlobal::config.preferMidiVolumeDb)
2854             {
2855               d_fin_val = muse_val2dbr(d_cur_val / double(max)) * 2.0;
2856               d_fin_val += d_comp_val_delta;
2857               d_fin_val = double(max) * muse_db2val(d_fin_val / 2.0);
2858             }
2859             else
2860               d_fin_val = d_cur_val + m_val_delta;
2861           }
2862 
2863           if(d_fin_val < double(min))
2864             d_fin_val = min;
2865           if(d_fin_val > double(max))
2866             d_fin_val = max;
2867 
2868           d_fin_val += double(mctl->bias());
2869 
2870           // False = linear not dB because we are doing the conversion here.
2871           mp->putControllerValue(port, chan, id, d_fin_val, false);
2872 
2873           // Trip the wait flag.
2874           wait_required = true;
2875         }
2876       }
2877       else
2878       {
2879         // Make sure we're not doing the same track more than once.
2880         if(doneAudioTracks.contains(t))
2881           continue;
2882         doneAudioTracks.append(t);
2883 
2884         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
2885         MusECore::iCtrlList icl = at->controller()->find(a_ctlnum);
2886         if(icl == at->controller()->end())
2887           continue;
2888         MusECore::CtrlList* cl = icl->second;
2889 
2890         double d_cur_val = cl->curVal();
2891         // The audio volume can go above 0dB (amplification) while
2892         //  the top midi value of 127 represents 0dB. Cut it off at 0dB.
2893         const double a_min = cl->minVal();
2894         const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal();
2895         const double a_range = a_max - a_min;
2896         const double a_val_delta = ma_new_delta_fact * a_range;
2897 
2898         if(id == MusECore::CTRL_VOLUME && MusEGlobal::config.preferMidiVolumeDb)
2899         {
2900           if(d_cur_val <= 0.0)
2901             d_cur_val = MusEGlobal::config.minSlider;
2902           else
2903             d_cur_val = muse_val2dbr(d_cur_val);
2904 
2905           d_cur_val += d_comp_val_delta;
2906           if(d_cur_val < MusEGlobal::config.minSlider)
2907             d_cur_val = MusEGlobal::config.minSlider;
2908           if(d_cur_val > 10.0)
2909             d_cur_val = 10.0;
2910 
2911           d_cur_val = muse_db2val(d_cur_val);
2912         }
2913         else
2914           d_cur_val += a_val_delta;
2915 
2916         if(d_cur_val < a_min)
2917           d_cur_val = a_min;
2918         if(d_cur_val > a_max)
2919           d_cur_val = a_max;
2920 
2921         at->recordAutomation(a_ctlnum, d_cur_val);
2922         at->setParam(a_ctlnum, d_cur_val);  // Schedules a timed control change.
2923         at->enableController(a_ctlnum, false);
2924 
2925         // Trip the wait flag.
2926         wait_required = true;
2927       }
2928     }
2929   }
2930   else
2931   {
2932     // TODO: Only volume and pan handled for now.
2933     int m_ctlnum;
2934     switch(id)
2935     {
2936       case MusECore::AC_VOLUME:
2937         m_ctlnum = MusECore::CTRL_VOLUME;
2938       break;
2939       case MusECore::AC_PAN:
2940         m_ctlnum = MusECore::CTRL_PANPOT;
2941       break;
2942       default:
2943         return;
2944       break;
2945     }
2946 
2947     MusECore::AudioTrack* a_track = static_cast<MusECore::AudioTrack*>(track);
2948     MusECore::iCtrlList icl = a_track->controller()->find(id);
2949     if(icl == a_track->controller()->end())
2950       return;
2951     MusECore::CtrlList* cl = icl->second;
2952 
2953     //----------------------------------------------------------
2954     // For midi volume, the formula given by MMA is:
2955     //  volume dB = 40 * log(midivol / 127)  and the inverse is:
2956     //  midi volume = 127 * 10 ^ (volume dB / 40)
2957     // Unusual, it is a factor of 40 not 20. Since muse_db2val()
2958     //  does 10 ^ (volume dB / 20), just divide volume dB by 2.
2959     //----------------------------------------------------------
2960     double a_val = newCompVal;
2961     double ma_val = newCompVal;
2962     if(id == MusECore::AC_VOLUME)
2963     {
2964       if(ma_val <= MusEGlobal::config.minSlider)
2965         a_val = ma_val = 0.0;
2966       else
2967       {
2968         a_val = muse_db2val(a_val);
2969         ma_val = muse_db2val(ma_val / 2.0);
2970       }
2971     }
2972 
2973     // The audio volume can go above 0dB (amplification) while
2974     //  the top midi value of 127 represents 0dB. Cut it off at 0dB.
2975     const double a_min = cl->minVal();
2976     const double a_max = cl->maxVal();
2977     const double a_range = a_max - a_min;
2978     if(a_range < 0.0001) // Avoid divide by zero.
2979       return;
2980     const double a_fact_delta = muse_round2micro((newCompVal - oldCompVal) / a_range);
2981 
2982     MusECore::TrackList* tracks = MusEGlobal::song->tracks();
2983     for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it)
2984     {
2985       MusECore::Track* t = *it;
2986       // Do selected tracks. Ignore this track, it has already taken care of its own controllers.
2987       if(t == track || !t->selected())
2988         continue;
2989       if(t->isMidiTrack())
2990       {
2991         MusECore::MidiTrack* mt = static_cast<MusECore::MidiTrack*>(t);
2992         const int port     = mt->outPort();
2993         const int chan  = mt->outChannel();
2994         // Make sure midi tracks on same port and channel are only done once.
2995         const MidiIncListStruct mils(port, chan);
2996         if(doneMidiTracks.contains(mils))
2997           continue;
2998         doneMidiTracks.append(mils);
2999 
3000         MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port];
3001         MusECore::MidiController* mctl = mp->midiController(m_ctlnum, chan, false);
3002         if(mctl)
3003         {
3004           int min = mctl->minVal();
3005           const int max = mctl->maxVal();
3006           const int bias = mctl->bias();
3007 
3008           //--------------------------------------------------------------
3009           //   NOTE: Midi int to audio float conversion:
3010           //   If the control has a bias at all, it is supposed
3011           //    to define the 'middle', like pan.
3012           //   But if the control's range is odd (127), that
3013           //    makes an uneven, uncentered float conversion.
3014           //   So the policy here is to force an even range for
3015           //    symmetrical +/- float conversion. (0.0 in the middle.)
3016           //   Treat value '0' (-64 pan) and '1' (-63 pan) the same.
3017           //--------------------------------------------------------------
3018           if(bias != 0 && ((max - min) & 0x1))
3019             ++min;
3020 
3021           const double d_min = (double)min;
3022           const double d_max = (double)max;
3023           const double d_range = double(max - min);
3024 
3025           const double m_val_delta = muse_round2micro(a_fact_delta * d_range);
3026 
3027           double d_fin_val = 0.0;
3028           // Get the current or last valid value.
3029           double d_cur_val =  mp->hwDCtrlState(chan, m_ctlnum);
3030           if(MusECore::MidiController::dValIsUnknown(d_cur_val))
3031             d_cur_val = mp->lastValidHWDCtrlState(chan, m_ctlnum);
3032           if(MusECore::MidiController::dValIsUnknown(d_cur_val))
3033           {
3034             if(!mctl->initValIsUnknown())
3035               d_cur_val = double(mctl->initVal()) + double(bias);
3036           }
3037           if(MusECore::MidiController::dValIsUnknown(d_cur_val))
3038             d_fin_val = 0.0;
3039           else
3040           {
3041             d_cur_val = d_cur_val - double(mctl->bias());
3042             if(m_ctlnum == MusECore::CTRL_VOLUME)
3043             {
3044               d_fin_val = muse_val2dbr(d_cur_val / d_max) * 2.0;
3045               d_fin_val += d_comp_val_delta;
3046               d_fin_val = d_max * muse_db2val(d_fin_val / 2.0);
3047             }
3048             else
3049               d_fin_val = d_cur_val + m_val_delta;
3050           }
3051 
3052           if(d_fin_val < d_min)
3053             d_fin_val = d_min;
3054           if(d_fin_val > d_max)
3055             d_fin_val = d_max;
3056 
3057           d_fin_val += double(mctl->bias());
3058 
3059           // False = linear not dB because we are doing the conversion here.
3060           mp->putControllerValue(port, chan, m_ctlnum, d_fin_val, false);
3061 
3062           // Trip the wait flag.
3063           wait_required = true;
3064         }
3065       }
3066       else
3067       {
3068         // Make sure we're not doing the same track more than once.
3069         if(doneAudioTracks.contains(t))
3070           continue;
3071         doneAudioTracks.append(t);
3072 
3073         MusECore::AudioTrack* at = static_cast<MusECore::AudioTrack*>(t);
3074         MusECore::iCtrlList icl = at->controller()->find(id);
3075         if(icl == at->controller()->end())
3076           continue;
3077         MusECore::CtrlList* cl = icl->second;
3078 
3079         double d_cur_val = cl->curVal();
3080         // The audio volume can go above 0dB (amplification) while
3081         //  the top midi value of 127 represents 0dB. Cut it off at 0dB.
3082         const double d_min = cl->minVal();
3083         const double d_max = cl->maxVal();
3084         const double d_range = d_max - d_min;
3085         const double d_val_delta = a_fact_delta * d_range;
3086 
3087         if(id == MusECore::AC_VOLUME)
3088         {
3089           if(d_cur_val <= 0.0)
3090             d_cur_val = MusEGlobal::config.minSlider;
3091           else
3092             d_cur_val = muse_val2dbr(d_cur_val);
3093 
3094           d_cur_val += d_comp_val_delta;
3095           if(d_cur_val < MusEGlobal::config.minSlider)
3096             d_cur_val = MusEGlobal::config.minSlider;
3097           if(d_cur_val > 10.0)
3098             d_cur_val = 10.0;
3099 
3100           d_cur_val = muse_db2val(d_cur_val);
3101         }
3102         else
3103           d_cur_val += d_val_delta;
3104 
3105         if(d_cur_val < d_min)
3106           d_cur_val = d_min;
3107         if(d_cur_val > d_max)
3108           d_cur_val = d_max;
3109 
3110         // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals.
3111         // ScrDirect mode is one-time only on press with modifier.
3112         //if(scrollMode != SliderBase::ScrDirect)
3113         at->recordAutomation(id, d_cur_val);
3114         at->setParam(id, d_cur_val);  // Schedules a timed control change.
3115         at->enableController(id, false);
3116 
3117         // Trip the wait flag.
3118         wait_required = true;
3119       }
3120     }
3121   }
3122 
3123   // This is a DELTA operation. Unfortunately we may need to WAIT for the hw controls to update
3124   //  in the audio thread before we can apply ANOTHER delta to the soon-to-be 'current' value.
3125   if(wait_required)
3126     MusEGlobal::audio->msgAudioWait();
3127 }
3128 
3129 } // namespace MusEGui
3130