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