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