1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //    $Id: ctrlpanel.cpp,v 1.10.2.9 2009/06/14 05:24:45 terminator356 Exp $
5 //  (C) Copyright 1999-2004 Werner Schweer (ws@seh.de)
6 //  (C) Copyright 2012, 2017 Tim E. Real (terminator356 on users dot sourceforge dot net)
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; version 2 of
11 //  the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 //=========================================================
23 
24 #include <stdio.h>
25 #include <list>
26 
27 #include "ctrlpanel.h"
28 #include "ctrlcanvas.h"
29 
30 #include <QSizePolicy>
31 #include <QTimer>
32 #include <QColor>
33 #include <QStyle>
34 
35 #include "muse_math.h"
36 
37 #include "globaldefs.h"
38 #include "app.h"
39 #include "globals.h"
40 #include "midictrl.h"
41 #include "instruments/minstrument.h"
42 #include "mididev.h"
43 #include "icons.h"
44 #include "event.h"
45 #include "part.h"
46 #include "midiedit/drummap.h"
47 #include "gconfig.h"
48 #include "song.h"
49 #include "utils.h"
50 
51 #include "audio.h"
52 #include "midi_consts.h"
53 #include "menutitleitem.h"
54 #include "popupmenu.h"
55 #include "helper.h"
56 
57 // Forwards from header:
58 #include <QHBoxLayout>
59 #include <QVBoxLayout>
60 #include <QSpacerItem>
61 #include <QAction>
62 #include "midieditor.h"
63 #include "midi_controller.h"
64 #include "midiport.h"
65 #include "track.h"
66 #include "ctrlcanvas.h"
67 #include "compact_knob.h"
68 #include "compact_slider.h"
69 #include "lcd_widgets.h"
70 
71 namespace MusEGui {
72 
73 //---------------------------------------------------------
74 //   CtrlPanel
75 //---------------------------------------------------------
76 
CtrlPanel(QWidget * parent,MidiEditor * e,CtrlCanvas * c,const char * name)77 CtrlPanel::CtrlPanel(QWidget* parent, MidiEditor* e, CtrlCanvas* c, const char* name)
78    : QWidget(parent)
79       {
80       setObjectName(name);
81       inHeartBeat = true;
82 
83       //setFocusPolicy(Qt::NoFocus);
84 
85       _knob = nullptr;
86       _slider = nullptr;
87       _patchEdit = nullptr;
88       _veloPerNoteButton = nullptr;
89 
90       _preferKnobs = MusEGlobal::config.preferKnobsVsSliders;
91       _showval = MusEGlobal::config.showControlValues;
92       editor = e;
93       ctrlcanvas = c;
94       setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
95       vbox = new QVBoxLayout;
96       QHBoxLayout* bbox = new QHBoxLayout;
97       bbox->setSpacing (0);
98       vbox->addLayout(bbox);
99       vbox->addStretch();
100       kbox = new QHBoxLayout;
101       vbox->addLayout(kbox);
102       vbox->addStretch();
103       vbox->setContentsMargins(0, 0, 0, 0);
104       bbox->setContentsMargins(0, 0, 0, 0);
105       kbox->setContentsMargins(0, 0, 0, 0);
106       vbox->setSpacing (0);
107       kbox->setSpacing(0);
108       lspacer = nullptr;
109       rspacer = nullptr;
110 
111       //: Select controller
112       selCtrl = new CompactToolButton(this);
113       selCtrl->setIcon(*midiControllerSelectSVGIcon);
114       selCtrl->setIconSize(QSize(14, 14));
115       selCtrl->setHasFixedIconSize(true);
116       selCtrl->setContentsMargins(4, 4, 4, 4);
117       selCtrl->setFocusPolicy(Qt::NoFocus);
118       selCtrl->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
119       selCtrl->setToolTip(tr("Select controller"));
120 
121       //: Remove panel (destroy button)
122       CompactToolButton* destroy = new CompactToolButton(this);
123       destroy->setIcon(*midiControllerRemoveSVGIcon);
124       destroy->setIconSize(QSize(14, 14));
125       destroy->setHasFixedIconSize(true);
126       destroy->setContentsMargins(4, 4, 4, 4);
127       destroy->setFocusPolicy(Qt::NoFocus);
128       destroy->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
129       destroy->setToolTip(tr("Remove panel"));
130 
131       connect(selCtrl, SIGNAL(clicked()), SLOT(ctrlPopup()));
132       connect(destroy, SIGNAL(clicked()), SIGNAL(destroyPanel()));
133 
134       _track = nullptr;
135       _ctrl = nullptr;
136       _dnum = -1;
137 
138       bbox->addStretch();
139       bbox->addWidget(selCtrl);
140       bbox->addWidget(destroy);
141       bbox->addStretch();
142 
143       configChanged();
144 
145       connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t)));
146       connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged()));
147       connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat()));
148       inHeartBeat = false;
149       setLayout(vbox);
150       }
151 
buildPanel()152 void CtrlPanel::buildPanel()
153 {
154   if(!_track || !_ctrl)
155   {
156     if(_veloPerNoteButton)
157     { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; }
158 
159     if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; }
160 
161     if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; }
162 
163     if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; }
164 
165     if(lspacer) { kbox->removeItem(lspacer); delete lspacer; lspacer = nullptr; }
166 
167     if(rspacer) { kbox->removeItem(rspacer); delete rspacer; rspacer = nullptr; }
168 
169     return;
170   }
171 
172   if(_dnum == MusECore::CTRL_VELOCITY)
173   {
174     if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; }
175 
176     if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; }
177 
178     if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; }
179 
180     if(!lspacer)
181     {
182       lspacer = new QSpacerItem(0, 0);
183       kbox->addSpacerItem(lspacer);
184     }
185 
186     if(!_veloPerNoteButton)
187     {
188       _veloPerNoteButton = new CompactToolButton(this);
189       _veloPerNoteButton->setIcon(*velocityPerNoteSVGIcon);
190       _veloPerNoteButton->setIconSize(QSize(19, 19));
191       _veloPerNoteButton->setHasFixedIconSize(true);
192       _veloPerNoteButton->setContentsMargins(2, 2, 2, 2);
193       _veloPerNoteButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
194 
195       _veloPerNoteButton->setFocusPolicy(Qt::NoFocus);
196       _veloPerNoteButton->setCheckable(true);
197       _veloPerNoteButton->setToolTip(tr("All/Per-note velocity mode"));
198       if(ctrlcanvas)
199         _veloPerNoteButton->setChecked(ctrlcanvas->perNoteVeloMode());
200 
201       connect(_veloPerNoteButton, SIGNAL(clicked()), SLOT(velPerNoteClicked()));
202 
203       kbox->addWidget(_veloPerNoteButton);
204     }
205 
206     if(!rspacer)
207     {
208       rspacer = new QSpacerItem(0, 0);
209       kbox->addSpacerItem(rspacer);
210     }
211   }
212   else
213   {
214     if(lspacer) { kbox->removeItem(lspacer); delete lspacer; lspacer = nullptr; }
215 
216     if(rspacer) { kbox->removeItem(rspacer); delete rspacer; rspacer = nullptr; }
217 
218     if(_dnum == MusECore::CTRL_PROGRAM)
219     {
220       if(_veloPerNoteButton)
221       { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; }
222 
223       if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; }
224 
225       if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; }
226 
227       if(!_patchEdit)
228       {
229         _patchEdit = new LCDPatchEdit(this);
230         _patchEdit->setReadoutOrientation(LCDPatchEdit::PatchVertical);
231         _patchEdit->setValue(MusECore::CTRL_VAL_UNKNOWN);
232         _patchEdit->setFocusPolicy(Qt::NoFocus);
233         // Don't allow anything here, it interferes with the CompactPatchEdit which sets it's own controls' tooltips.
234         //control->setToolTip(d->_toolTipText);
235         _patchEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
236         _patchEdit->setContentsMargins(0, 0, 0, 0);
237 
238         _patchEdit->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
239         if(_patchEdit->font() != MusEGlobal::config.fonts[1])
240         {
241           _patchEdit->setFont(MusEGlobal::config.fonts[1]);
242           _patchEdit->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]));
243         }
244 
245         connect(_patchEdit, SIGNAL(valueChanged(int,int)), SLOT(patchCtrlChanged(int)));
246         connect(_patchEdit, SIGNAL(rightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int)));
247 
248         kbox->addWidget(_patchEdit);
249       }
250     }
251     else
252     {
253       if(_preferKnobs)
254       {
255         if(_veloPerNoteButton)
256         { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; }
257 
258         if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; }
259 
260         if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; }
261 
262         if(!_knob)
263         {
264           _knob = new CompactKnob(this, "CtrlPanelKnob", CompactKnob::Bottom);
265           _knob->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
266           _knob->setToolTip(tr("Manual adjust (Ctrl-double-click on/off)"));
267           //_knob->setFocusPolicy(Qt::NoFocus);
268           _knob->setRange(0.0, 127.0, 1.0);
269           _knob->setValue(0.0);
270           _knob->setHasOffMode(true);
271           _knob->setOff(true);
272           _knob->setValueDecimals(0);
273           _knob->setFaceColor(MusEGlobal::config.midiControllerSliderColor);
274           _knob->setStep(1.0);
275           _knob->setShowLabel(false);
276           _knob->setShowValue(true);
277           _knob->setEnableValueToolTips(false);      // FIXME: Tooltip just gets in the way!
278           _knob->setShowValueToolTipsOnHover(false); //
279 
280           if(_knob->font() != MusEGlobal::config.fonts[1])
281           {
282             _knob->setFont(MusEGlobal::config.fonts[1]);
283             _knob->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]));
284           }
285 
286           connect(_knob, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(ctrlChanged(double,bool,int,int)));
287           connect(_knob, SIGNAL(sliderRightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int)));
288 
289           kbox->addWidget(_knob);
290         }
291       }
292       else
293       {
294         if(_veloPerNoteButton)
295         { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; }
296 
297         if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; }
298 
299         if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; }
300 
301         if(!_slider)
302         {
303           _slider = new CompactSlider(this, "CtrlPanelSlider");
304           _slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
305           _slider->setToolTip(tr("Manual adjust (Ctrl-double-click on/off)"));
306           //_slider->setFocusPolicy(Qt::NoFocus);
307           _slider->setRange(0.0, 127.0, 1.0);
308           _slider->setValue(0.0);
309           _slider->setHasOffMode(true);
310           _slider->setOff(true);
311           _slider->setValueDecimals(0);
312           _slider->setBarColor(MusEGlobal::config.sliderBackgroundColor);
313           _slider->setStep(1.0);
314           _slider->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
315           _slider->setEnableValueToolTips(false);      // FIXME: Tooltip just gets in the way!
316           _slider->setShowValueToolTipsOnHover(false); //
317 
318           if(_slider->font() != MusEGlobal::config.fonts[1])
319           {
320             _slider->setFont(MusEGlobal::config.fonts[1]);
321             _slider->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]));
322           }
323 
324           connect(_slider, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(ctrlChanged(double,bool,int,int)));
325           connect(_slider, SIGNAL(sliderRightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int)));
326 
327           kbox->addWidget(_slider);
328         }
329       }
330     }
331   }
332 }
333 
334 //---------------------------------------------------------
335 //   heartBeat
336 //---------------------------------------------------------
337 
heartBeat()338 void CtrlPanel::heartBeat()
339 {
340   if(editor && editor->deleting())  // Ignore while while deleting to prevent crash.
341     return;
342 
343   inHeartBeat = true;
344 
345   if(_track && _ctrl && _dnum != -1)
346   {
347     if(_dnum != MusECore::CTRL_VELOCITY)
348     {
349       int outport = _track->outPort();
350       int chan = _track->outChannel();
351       int cdp = ctrlcanvas->getCurDrumPitch();
352       if(_ctrl->isPerNoteController() && cdp >= 0)
353       {
354         if(_track->type() == MusECore::Track::DRUM)
355         {
356           // Default to track port if -1 and track channel if -1.
357           outport = _track->drummap()[cdp].port;
358           if(outport == -1)
359             outport = _track->outPort();
360           chan = _track->drummap()[cdp].channel;
361           if(chan == -1)
362             chan = _track->outChannel();
363         }
364       }
365 
366       MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport];
367       MusECore::MidiCtrlValListList* mcvll = mp->controller();
368 
369       MusECore::ciMidiCtrlValList imcvl = mcvll->find(chan, _dnum);
370       const bool enable = imcvl != mcvll->end() && !_track->off();
371 
372 // REMOVE Tim. midi. Added.
373 // Although we do this for the midi strip control racks (and probably shouldn't),
374 //  we need the control to be enabled - user would expect to be able to adjust it
375 //  even if controller was not found - our code will create the controller.
376 //       if(_knob)
377 //       {
378 //         if(_knob->isEnabled() != enable)
379 //           _knob->setEnabled(enable);
380 //       }
381 //       else if(_slider)
382 //       {
383 //         if(_slider->isEnabled() != enable)
384 //           _slider->setEnabled(enable);
385 //       }
386 
387       if(enable)
388       {
389         MusECore::MidiCtrlValList* mcvl = imcvl->second;
390 
391         int hwVal = mcvl->hwVal();
392 
393         if(_patchEdit && _dnum == MusECore::CTRL_PROGRAM)
394         {
395           // Special for new LCD patch edit control: Need to give both current and last values.
396           // Keeping a local last value with the control won't work.
397           _patchEdit->blockSignals(true);
398           _patchEdit->setLastValidPatch(mcvl->lastValidHWVal());
399           _patchEdit->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0());
400           _patchEdit->setValue(hwVal);
401           _patchEdit->blockSignals(false);
402         }
403         else
404         {
405           int min = 0;
406           int max = 127;
407           int bias = 0;
408           int initval = 0;
409           MusECore::MidiController* mc = mp->midiController(_dnum, chan);
410           if(mc)
411           {
412             bias = mc->bias();
413             min = mc->minVal();
414             max = mc->maxVal();
415             initval = mc->initVal();
416             if(initval == MusECore::CTRL_VAL_UNKNOWN)
417               initval = 0;
418           }
419 
420           const double dmin = (double)min;
421           const double dmax = (double)max;
422 
423           if(_knob)
424           {
425             const double c_dmin = _knob->minValue();
426             const double c_dmax = _knob->maxValue();
427             if(c_dmin != min && c_dmax != max)
428             {
429               _knob->blockSignals(true);
430               _knob->setRange(dmin, dmax, 1.0);
431               _knob->blockSignals(false);
432             }
433             else if(c_dmin != min)
434             {
435               _knob->blockSignals(true);
436               _knob->setMinValue(min);
437               _knob->blockSignals(false);
438             }
439             else if(c_dmax != max)
440             {
441               _knob->blockSignals(true);
442               _knob->setMaxValue(max);
443               _knob->blockSignals(false);
444             }
445 
446             if(hwVal == MusECore::CTRL_VAL_UNKNOWN)
447             {
448               hwVal = mcvl->lastValidHWVal();
449               if(hwVal == MusECore::CTRL_VAL_UNKNOWN)
450               {
451                 hwVal = initval;
452                 if(!_knob->isOff() || hwVal != _knob->value())
453                 {
454                   _knob->blockSignals(true);
455                   _knob->setValueState(hwVal, true);
456                   _knob->blockSignals(false);
457                 }
458               }
459               else
460               {
461                 hwVal -= bias;
462                 if(!_knob->isOff() || hwVal != _knob->value())
463                 {
464                   _knob->blockSignals(true);
465                   _knob->setValueState(hwVal, true);
466                   _knob->blockSignals(false);
467                 }
468               }
469             }
470             else
471             {
472               hwVal -= bias;
473               if(_knob->isOff() || hwVal != _knob->value())
474               {
475                 _knob->blockSignals(true);
476                 _knob->setValueState(hwVal, false);
477                 _knob->blockSignals(false);
478               }
479             }
480           }
481           else if(_slider)
482           {
483             const double c_dmin = _slider->minValue();
484             const double c_dmax = _slider->maxValue();
485             if(c_dmin != min && c_dmax != max)
486             {
487               _slider->blockSignals(true);
488               _slider->setRange(dmin, dmax, 1.0);
489               _slider->blockSignals(false);
490             }
491             else if(c_dmin != min)
492             {
493               _slider->blockSignals(true);
494               _slider->setMinValue(min);
495               _slider->blockSignals(false);
496             }
497             else if(c_dmax != max)
498             {
499               _slider->blockSignals(true);
500               _slider->setMaxValue(max);
501               _slider->blockSignals(false);
502             }
503 
504             if(hwVal == MusECore::CTRL_VAL_UNKNOWN)
505             {
506               hwVal = mcvl->lastValidHWVal();
507               if(hwVal == MusECore::CTRL_VAL_UNKNOWN)
508               {
509                 hwVal = initval;
510                 if(!_slider->isOff() || hwVal != _slider->value())
511                 {
512                   _slider->blockSignals(true);
513                   _slider->setValueState(hwVal, true);
514                   _slider->blockSignals(false);
515                 }
516               }
517               else
518               {
519                 hwVal -= bias;
520                 if(!_slider->isOff() || hwVal != _slider->value())
521                 {
522                   _slider->blockSignals(true);
523                   _slider->setValueState(hwVal, true);
524                   _slider->blockSignals(false);
525                 }
526               }
527             }
528             else
529             {
530               hwVal -= bias;
531               if(_slider->isOff() || hwVal != _slider->value())
532               {
533                 _slider->blockSignals(true);
534                 _slider->setValueState(hwVal, false);
535                 _slider->blockSignals(false);
536               }
537             }
538           }
539         }
540       }
541     }
542   }
543 
544   inHeartBeat = false;
545 }
546 
547 //---------------------------------------------------------
548 //   configChanged
549 //---------------------------------------------------------
550 
configChanged()551 void CtrlPanel::configChanged()
552 {
553   songChanged(SC_CONFIG);
554 
555   // Detect when knobs are preferred and rebuild.
556   if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders)
557   {
558     _preferKnobs = MusEGlobal::config.preferKnobsVsSliders;
559     setController();
560   }
561 
562   if(_patchEdit)
563   {
564     if(_patchEdit->font() != MusEGlobal::config.fonts[1])
565     {
566       _patchEdit->setFont(MusEGlobal::config.fonts[1]);
567       _patchEdit->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]));
568     }
569     _patchEdit->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
570   }
571 
572   if(_knob)
573   {
574     if(_knob->font() != MusEGlobal::config.fonts[1])
575     {
576       _knob->setFont(MusEGlobal::config.fonts[1]);
577       _knob->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]));
578     }
579 
580     // Whether to show values along with labels for certain controls.
581     //_knob->setShowValue(MusEGlobal::config.showControlValues);
582   }
583 
584   if(_slider)
585   {
586     if(_slider->font() != MusEGlobal::config.fonts[1])
587     {
588       _slider->setFont(MusEGlobal::config.fonts[1]);
589       _slider->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]));
590     }
591 
592     _slider->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize);
593     // Whether to show values along with labels for certain controls.
594     //_slider->setShowValue(MusEGlobal::config.showControlValues);
595   }
596 
597   setControlColor();
598 }
599 
600 //---------------------------------------------------------
601 //   songChanged
602 //---------------------------------------------------------
603 
songChanged(MusECore::SongChangedStruct_t)604 void CtrlPanel::songChanged(MusECore::SongChangedStruct_t /*flags*/)
605 {
606   if(editor && editor->deleting())  // Ignore while while deleting to prevent crash.
607     return;
608 }
609 
patchCtrlChanged(int val)610 void CtrlPanel::patchCtrlChanged(int val)
611 {
612   ctrlChanged(double(val), false, _dnum, 0);
613 }
614 
615 //---------------------------------------------------------
616 //   ctrlChanged
617 //---------------------------------------------------------
618 
ctrlChanged(double val,bool off,int,int)619 void CtrlPanel::ctrlChanged(double val, bool off, int /*id*/, int /*scrollMode*/)
620     {
621       if (inHeartBeat)
622             return;
623       if(!_track || !_ctrl || _dnum == -1)
624           return;
625 
626       int ival = lrint(val);
627       int outport = _track->outPort();
628       int chan = _track->outChannel();
629       if(chan < 0 || chan >= MusECore::MUSE_MIDI_CHANNELS || outport < 0 || outport >= MusECore::MIDI_PORTS)
630           return;
631       int cdp = ctrlcanvas->getCurDrumPitch();
632       if(_ctrl->isPerNoteController() && cdp >= 0)
633       {
634         if(_track->type() == MusECore::Track::DRUM)
635         {
636           // Default to track port if -1 and track channel if -1.
637           outport = _track->drummap()[cdp].port;
638           if(outport == -1)
639             outport = _track->outPort();
640           chan = _track->drummap()[cdp].channel;
641           if(chan == -1)
642             chan = _track->outChannel();
643         }
644       }
645 
646       MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport];
647       // Shouldn't happen, but...
648       if(off || ival < _ctrl->minVal() || ival > _ctrl->maxVal())
649         ival = MusECore::CTRL_VAL_UNKNOWN;
650 
651       if(ival != MusECore::CTRL_VAL_UNKNOWN)
652         // Auto bias...
653         ival += _ctrl->bias();
654 
655       MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), outport, chan, MusECore::ME_CONTROLLER, _dnum, ival);
656       mp->putEvent(ev);
657     }
658 
659 //---------------------------------------------------------
660 //   setController
661 //---------------------------------------------------------
662 
setController()663 void CtrlPanel::setController()
664 {
665   if(!_track || !_ctrl)
666   {
667     buildPanel();
668     inHeartBeat = false;
669     return;
670   }
671 
672   MusECore::MidiPort* mp = &MusEGlobal::midiPorts[_track->outPort()];
673   int ch = _track->outChannel();
674   int cdp = ctrlcanvas->getCurDrumPitch();
675   _dnum = _ctrl->num();
676   if(_ctrl->isPerNoteController() && cdp >= 0)
677   {
678     if(_track->type() == MusECore::Track::DRUM)
679     {
680       _dnum = (_dnum & ~0xff) | _track->drummap()[cdp].anote;
681       int mport = _track->drummap()[cdp].port;
682       // Default to track port if -1 and track channel if -1.
683       if(mport == -1)
684         mport = _track->outPort();
685       mp = &MusEGlobal::midiPorts[mport];
686       ch = _track->drummap()[cdp].channel;
687       if(ch == -1)
688         ch = _track->outChannel();
689     }
690     else if(_track->type() == MusECore::Track::MIDI)
691     {
692       _dnum = (_dnum & ~0xff) | cdp; //FINDMICHJETZT does that work?
693     }
694   }
695 
696   buildPanel();
697 
698   if(_dnum == MusECore::CTRL_VELOCITY)
699   {
700   }
701   else
702   {
703     MusECore::MidiCtrlValListList* mcvll = mp->controller();
704 
705     int mn; int mx; int v;
706     if(_dnum == MusECore::CTRL_PROGRAM)
707     {
708       if(_patchEdit)
709       {
710         MusECore::ciMidiCtrlValList imcvl = mcvll->find(ch, _dnum);
711         if(imcvl != mcvll->end())
712         {
713           MusECore::MidiCtrlValList* mcvl = imcvl->second;
714           int hwVal = mcvl->hwVal();
715           // Special for new LCD patch edit control: Need to give both current and last values.
716           // Keeping a local last value with the control won't work.
717           _patchEdit->blockSignals(true);
718           _patchEdit->setLastValidPatch(mcvl->lastValidHWVal());
719           _patchEdit->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0());
720           _patchEdit->setValue(hwVal);
721           _patchEdit->blockSignals(false);
722         }
723       }
724       else
725       {
726         mn = 1;
727         mx = 128;
728         v = mp->hwCtrlState(ch, _dnum);
729 
730         if(_knob)
731           _knob->setRange(double(mn), double(mx), 1.0);
732         else if(_slider)
733           _slider->setRange(double(mn), double(mx), 1.0);
734         if(v == MusECore::CTRL_VAL_UNKNOWN || ((v & 0xffffff) == 0xffffff))
735         {
736           int lastv = mp->lastValidHWCtrlState(ch, _dnum);
737           if(lastv == MusECore::CTRL_VAL_UNKNOWN || ((lastv & 0xffffff) == 0xffffff))
738           {
739             int initv = _ctrl->initVal();
740             if(initv == MusECore::CTRL_VAL_UNKNOWN || ((initv & 0xffffff) == 0xffffff))
741               v = 1;
742             else
743               v = (initv + 1) & 0xff;
744           }
745           else
746             v = (lastv + 1) & 0xff;
747 
748           if(v > 128)
749             v = 128;
750         }
751         else
752         {
753           v = (v + 1) & 0xff;
754           if(v > 128)
755             v = 128;
756         }
757 
758         if(_knob)
759         {
760           _knob->setValue(double(v));
761         }
762         else if(_slider)
763         {
764           _slider->setValue(double(v));
765         }
766       }
767     }
768     else
769     {
770       mn = _ctrl->minVal();
771       mx = _ctrl->maxVal();
772       v = mp->hwCtrlState(ch, _dnum);
773       if(_knob)
774         _knob->setRange(double(mn), double(mx), 1.0);
775       else if(_slider)
776         _slider->setRange(double(mn), double(mx), 1.0);
777       if(v == MusECore::CTRL_VAL_UNKNOWN)
778       {
779         int lastv = mp->lastValidHWCtrlState(ch, _dnum);
780         if(lastv == MusECore::CTRL_VAL_UNKNOWN)
781         {
782           if(_ctrl->initVal() == MusECore::CTRL_VAL_UNKNOWN)
783             v = 0;
784           else
785             v = _ctrl->initVal();
786         }
787         else
788           v = lastv - _ctrl->bias();
789       }
790       else
791       {
792         // Auto bias...
793         v -= _ctrl->bias();
794       }
795 
796       if(_knob)
797       {
798         _knob->setValue(double(v));
799       }
800       else if(_slider)
801       {
802         _slider->setValue(double(v));
803       }
804     }
805   }
806 
807   setControlColor();
808 }
809 
setControlColor()810 void CtrlPanel::setControlColor()
811 {
812   if(_dnum == -1)
813     return;
814 
815   QColor color = MusEGlobal::config.sliderBackgroundColor;
816 
817   switch(_dnum)
818   {
819     case MusECore::CTRL_PANPOT:
820       color = MusEGlobal::config.panSliderColor;
821     break;
822 
823     case MusECore::CTRL_PROGRAM:
824       color = MusEGlobal::config.midiPatchReadoutColor;
825     break;
826 
827     default:
828       color = MusEGlobal::config.midiControllerSliderColor;
829     break;
830   }
831 
832   if(_patchEdit)
833   {
834     _patchEdit->setReadoutColor(color);
835     // Colours in these sections were not updating with stylesheet colours,
836     //  because normally that's only done at creation, and we create these
837     //  things once and keep them around waiting to be shown.
838     // Anyway, this trick recommended by help. Tested OK.
839     // Good demonstration of how to apply a stylesheet after a property has been changed.
840     style()->unpolish(_patchEdit);
841     style()->polish(_patchEdit);
842   }
843 
844   if(_knob)
845   {
846     _knob->setFaceColor(color);
847     style()->unpolish(_knob);
848     style()->polish(_knob);
849   }
850 
851   if(_slider)
852   {
853     _slider->setBorderColor(color);
854     _slider->setBarColor(MusEGlobal::config.sliderBarColor);
855     style()->unpolish(_slider);
856     style()->polish(_slider);
857   }
858 }
859 
860 //---------------------------------------------------------
861 //   setHWController
862 //---------------------------------------------------------
863 
setHWController(MusECore::MidiTrack * t,MusECore::MidiController * ctrl)864 void CtrlPanel::setHWController(MusECore::MidiTrack* t, MusECore::MidiController* ctrl)
865 {
866   inHeartBeat = true;
867 
868   _track = t; _ctrl = ctrl;
869 
870   setController();
871 
872   inHeartBeat = false;
873 }
874 
875 //---------------------------------------------------------
876 //   setHeight
877 //---------------------------------------------------------
878 
setHeight(int h)879 void CtrlPanel::setHeight(int h)
880       {
881       setFixedHeight(h);
882       }
883 
884 //---------------------------------------------------
885 // ctrlPopup
886 //---------------------------------------------------
887 
ctrlPopup()888 void CtrlPanel::ctrlPopup()
889       {
890       MusECore::PartList* parts  = editor->parts();
891       MusECore::Part* part       = editor->curCanvasPart();
892       int curDrumPitch           = ctrlcanvas->getCurDrumPitch();
893 
894       PopupMenu* pup = new PopupMenu(true);  // true = enable stay open. Don't bother with parent.
895       int est_width = populateMidiCtrlMenu(pup, parts, part, curDrumPitch);
896       QPoint ep = mapToGlobal(QPoint(0,0));
897       //int newx = ep.x() - ctrlMainPop->width();  // Too much! Width says 640. Maybe because it hasn't been shown yet  .
898       int newx = ep.x() - est_width;
899       if(newx < 0)
900         newx = 0;
901       ep.setX(newx);
902       connect(pup, SIGNAL(triggered(QAction*)), SLOT(ctrlPopupTriggered(QAction*)));
903       pup->exec(ep);
904       delete pup;
905       selCtrl->setDown(false);
906       }
907 
908 //---------------------------------------------------------
909 //   ctrlPopupTriggered
910 //---------------------------------------------------------
911 
ctrlPopupTriggered(QAction * act)912 void CtrlPanel::ctrlPopupTriggered(QAction* act)
913 {
914   if(!act || (act->data().toInt() == -1))
915     return;
916 
917   MusECore::Part* part       = editor->curCanvasPart();
918   MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track());
919   int channel      = track->outChannel();
920   MusECore::MidiPort* port   = &MusEGlobal::midiPorts[track->outPort()];
921   MusECore::MidiCtrlValListList* cll = port->controller();
922   const int min = channel << 24;
923   const int max = min + 0x1000000;
924   const int edit_ins = max + 3;
925   const int velo = max + 0x101;
926   int rv = act->data().toInt();
927 
928   if (rv == velo) {    // special case velocity
929         emit controllerChanged(MusECore::CTRL_VELOCITY);
930         }
931   else if (rv == edit_ins) {            // edit instrument
932         MusECore::MidiInstrument* instr = port->instrument();
933         MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrumentControllers);
934         }
935   else {                           // Select a control
936         MusECore::iMidiCtrlValList i = cll->find(channel, rv);
937         if(i == cll->end())
938         {
939           MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(rv);
940           cll->add(channel, vl);
941         }
942         int num = rv;
943         if(port->drumController(rv))
944           num |= 0xff;
945         emit controllerChanged(num);
946       }
947 }
948 
949 //---------------------------------------------------------
950 //   ctrlRightClicked
951 //---------------------------------------------------------
952 
ctrlRightClicked(const QPoint & p,int)953 void CtrlPanel::ctrlRightClicked(const QPoint& p, int /*id*/)
954 {
955   if(!editor->curCanvasPart() || !_ctrl)
956     return;
957 
958   int cdp = ctrlcanvas->getCurDrumPitch();
959   int ctlnum = _ctrl->num();
960   if(_track->isDrumTrack() &&
961      _ctrl->isPerNoteController() && cdp >= 0)
962     //ctlnum = (ctlnum & ~0xff) | MusEGlobal::drumMap[cdp].enote; DELETETHIS or which of them is correct?
963     ctlnum = (ctlnum & ~0xff) | cdp;
964 
965   MusECore::MidiPart* part = dynamic_cast<MusECore::MidiPart*>(editor->curCanvasPart());
966   MusEGlobal::song->execMidiAutomationCtlPopup(0, part, p, ctlnum);
967 }
968 
969 //---------------------------------------------------------
970 //   velPerNoteClicked
971 //---------------------------------------------------------
972 
velPerNoteClicked()973 void CtrlPanel::velPerNoteClicked()
974 {
975   if(ctrlcanvas && _veloPerNoteButton && _veloPerNoteButton->isChecked() != ctrlcanvas->perNoteVeloMode())
976     ctrlcanvas->setPerNoteVeloMode(_veloPerNoteButton->isChecked());
977 }
978 
979 //---------------------------------------------------------
980 //   setVeloPerNoteMode
981 //---------------------------------------------------------
982 
setVeloPerNoteMode(bool v)983 void CtrlPanel::setVeloPerNoteMode(bool v)
984 {
985   if(_veloPerNoteButton && v != _veloPerNoteButton->isChecked())
986     _veloPerNoteButton->setChecked(v);
987 }
988 
989 } // namespace MusEGui
990 
991