1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: plugin.cpp,v 1.21.2.23 2009/12/15 22:07:12 spamatica Exp $
5 //
6 // (C) Copyright 2000 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 <stdio.h>
26 #include <stdlib.h>
27 #include <dlfcn.h>
28 #include <string>
29 #include "muse_math.h"
30 #include <sys/stat.h>
31
32 #include <QGridLayout>
33 #include <QLabel>
34 #include <QWhatsThis>
35 #include <QToolBar>
36 #include <QMessageBox>
37 #include <QByteArray>
38 #include <QToolButton>
39 #include <QComboBox>
40 #include <QGroupBox>
41
42 #include "globals.h"
43 #include "gconfig.h"
44 #include "filedialog.h"
45 #include "slider.h"
46 #include "midictrl_consts.h"
47 #include "plugin.h"
48 #include "controlfifo.h"
49 #include "icons.h"
50 #include "song.h"
51 #include "doublelabel.h"
52 #include "fastlog.h"
53 #include "checkbox.h"
54 #include "comboboxpi.h"
55 #include "meter.h"
56 #include "utils.h"
57 #include "pluglist.h"
58 #include "gui.h"
59 #include "pluginsettings.h"
60
61 #ifdef LV2_SUPPORT
62 #include "lv2host.h"
63 #endif
64
65 #ifdef VST_NATIVE_SUPPORT
66 #include "vst_native.h"
67 #endif
68
69 #include "audio.h"
70 #include "al/dsp.h"
71
72 #include "muse_math.h"
73
74 // Forwards from header:
75 #include <QScrollArea>
76 #include <QShowEvent>
77 #include <QHideEvent>
78 #include <QAction>
79 #include <QSpinBox>
80 #include "xml.h"
81 #include "plugin_list.h"
82 #include "track.h"
83
84 #ifdef _WIN32
85 #define S_ISLNK(X) 0
86 #endif
87
88 // Turn on debugging messages.
89 //#define PLUGIN_DEBUGIN
90
91 // Turn on constant stream of debugging messages.
92 //#define PLUGIN_DEBUGIN_PROCESS
93
94 namespace MusEGlobal {
95 MusECore::PluginList plugins;
96 MusECore::PluginGroups plugin_groups;
97 QList<QString> plugin_group_names;
98
99 }
100
101 namespace MusECore {
102
103 //---------------------------------------------------------
104 // ladspa2MidiControlValues
105 //---------------------------------------------------------
106
ladspa2MidiControlValues(const LADSPA_Descriptor * plugin,unsigned long port,int ctlnum,int * min,int * max,int * def)107 bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum, int* min, int* max, int* def)
108 {
109 LADSPA_PortRangeHint range = plugin->PortRangeHints[port];
110 LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor;
111
112 float fmin, fmax, fdef;
113 int imin, imax;
114 float frng;
115
116 bool hasdef = ladspaDefaultValue(plugin, port, &fdef);
117 MidiController::ControllerType t = midiControllerType(ctlnum);
118
119 #ifdef PLUGIN_DEBUGIN
120 printf("ladspa2MidiControlValues: ctlnum:%d ladspa port:%lu has default?:%d default:%f\n", ctlnum, port, hasdef, fdef);
121 #endif
122
123 if(desc & LADSPA_HINT_TOGGLED)
124 {
125 #ifdef PLUGIN_DEBUGIN
126 printf("ladspa2MidiControlValues: has LADSPA_HINT_TOGGLED\n");
127 #endif
128
129 *min = 0;
130 *max = 1;
131 *def = (int)lrintf(fdef);
132 return hasdef;
133 }
134
135 float m = 1.0;
136 if(desc & LADSPA_HINT_SAMPLE_RATE)
137 {
138 #ifdef PLUGIN_DEBUGIN
139 printf("ladspa2MidiControlValues: has LADSPA_HINT_SAMPLE_RATE\n");
140 #endif
141
142 m = float(MusEGlobal::sampleRate);
143 }
144
145 if(desc & LADSPA_HINT_BOUNDED_BELOW)
146 {
147 #ifdef PLUGIN_DEBUGIN
148 printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_BELOW\n");
149 #endif
150
151 fmin = range.LowerBound * m;
152 }
153 else
154 fmin = 0.0;
155
156 if(desc & LADSPA_HINT_BOUNDED_ABOVE)
157 {
158 #ifdef PLUGIN_DEBUGIN
159 printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_ABOVE\n");
160 #endif
161
162 fmax = range.UpperBound * m;
163 }
164 else
165 fmax = 1.0;
166
167 frng = fmax - fmin;
168 imin = lrintf(fmin);
169 imax = lrintf(fmax);
170
171 int ctlmn = 0;
172 int ctlmx = 127;
173
174 #ifdef PLUGIN_DEBUGIN
175 printf("ladspa2MidiControlValues: port min:%f max:%f \n", fmin, fmax);
176 #endif
177
178 bool isneg = (imin < 0);
179 int bias = 0;
180 switch(t)
181 {
182 case MidiController::RPN:
183 case MidiController::NRPN:
184 case MidiController::Controller7:
185 if(isneg)
186 {
187 ctlmn = -64;
188 ctlmx = 63;
189 bias = -64;
190 }
191 else
192 {
193 ctlmn = 0;
194 ctlmx = 127;
195 }
196 break;
197 case MidiController::Controller14:
198 case MidiController::RPN14:
199 case MidiController::NRPN14:
200 if(isneg)
201 {
202 ctlmn = -8192;
203 ctlmx = 8191;
204 bias = -8192;
205 }
206 else
207 {
208 ctlmn = 0;
209 ctlmx = 16383;
210 }
211 break;
212 case MidiController::Program:
213 ctlmn = 0;
214 ctlmx = 0x3fff; // FIXME: Really should not happen or be allowed. What to do here...
215 break;
216 case MidiController::Pitch:
217 ctlmn = -8192;
218 ctlmx = 8191;
219 break;
220 case MidiController::Velo: // cannot happen
221 default:
222 break;
223 }
224 float fctlrng = float(ctlmx - ctlmn);
225
226 // Is it an integer control?
227 if(desc & LADSPA_HINT_INTEGER)
228 {
229 #ifdef PLUGIN_DEBUGIN
230 printf("ladspa2MidiControlValues: has LADSPA_HINT_INTEGER\n");
231 #endif
232
233 // just clip the limits instead of scaling the whole range. ie fit the range into clipped space.
234 if(imin < ctlmn)
235 imin = ctlmn;
236 if(imax > ctlmx)
237 imax = ctlmx;
238
239 *min = imin;
240 *max = imax;
241
242 *def = (int)lrintf(fdef);
243
244 return hasdef;
245 }
246
247 // It's a floating point control, just use wide open maximum range.
248 *min = ctlmn;
249 *max = ctlmx;
250
251 float normdef = fdef / frng;
252 fdef = normdef * fctlrng;
253
254 // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later....
255
256 *def = (int)lrintf(fdef) + bias;
257
258 #ifdef PLUGIN_DEBUGIN
259 printf("ladspa2MidiControlValues: setting default:%d\n", *def);
260 #endif
261
262 return hasdef;
263 }
264
265 //---------------------------------------------------------
266 // midi2LadspaValue
267 //---------------------------------------------------------
268
midi2LadspaValue(const LADSPA_Descriptor * plugin,unsigned long port,int ctlnum,int val)269 float midi2LadspaValue(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum, int val)
270 {
271 LADSPA_PortRangeHint range = plugin->PortRangeHints[port];
272 LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor;
273
274 float fmin, fmax;
275 int imin;
276 float frng;
277
278 MidiController::ControllerType t = midiControllerType(ctlnum);
279
280 #ifdef PLUGIN_DEBUGIN
281 printf("midi2LadspaValue: ctlnum:%d ladspa port:%lu val:%d\n", ctlnum, port, val);
282 #endif
283
284 float m = 1.0;
285 if(desc & LADSPA_HINT_SAMPLE_RATE)
286 {
287 #ifdef PLUGIN_DEBUGIN
288 printf("midi2LadspaValue: has LADSPA_HINT_SAMPLE_RATE\n");
289 #endif
290
291 m = float(MusEGlobal::sampleRate);
292 }
293
294 if(desc & LADSPA_HINT_BOUNDED_BELOW)
295 {
296 #ifdef PLUGIN_DEBUGIN
297 printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_BELOW\n");
298 #endif
299
300 fmin = range.LowerBound * m;
301 }
302 else
303 fmin = 0.0;
304
305 if(desc & LADSPA_HINT_BOUNDED_ABOVE)
306 {
307 #ifdef PLUGIN_DEBUGIN
308 printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_ABOVE\n");
309 #endif
310
311 fmax = range.UpperBound * m;
312 }
313 else
314 fmax = 1.0;
315
316 frng = fmax - fmin;
317 imin = lrintf(fmin);
318
319 if(desc & LADSPA_HINT_TOGGLED)
320 {
321 #ifdef PLUGIN_DEBUGIN
322 printf("midi2LadspaValue: has LADSPA_HINT_TOGGLED\n");
323 #endif
324
325 if(val > 0)
326 return fmax;
327 else
328 return fmin;
329 }
330
331 int ctlmn = 0;
332 int ctlmx = 127;
333
334 #ifdef PLUGIN_DEBUGIN
335 printf("midi2LadspaValue: port min:%f max:%f \n", fmin, fmax);
336 #endif
337
338 bool isneg = (imin < 0);
339 int bval = val;
340 int cval = val;
341 switch(t)
342 {
343 case MidiController::RPN:
344 case MidiController::NRPN:
345 case MidiController::Controller7:
346 if(isneg)
347 {
348 ctlmn = -64;
349 ctlmx = 63;
350 bval -= 64;
351 cval -= 64;
352 }
353 else
354 {
355 ctlmn = 0;
356 ctlmx = 127;
357 cval -= 64;
358 }
359 break;
360 case MidiController::Controller14:
361 case MidiController::RPN14:
362 case MidiController::NRPN14:
363 if(isneg)
364 {
365 ctlmn = -8192;
366 ctlmx = 8191;
367 bval -= 8192;
368 cval -= 8192;
369 }
370 else
371 {
372 ctlmn = 0;
373 ctlmx = 16383;
374 cval -= 8192;
375 }
376 break;
377 case MidiController::Program:
378 ctlmn = 0;
379 ctlmx = 0xffffff;
380 break;
381 case MidiController::Pitch:
382 ctlmn = -8192;
383 ctlmx = 8191;
384 break;
385 case MidiController::Velo: // cannot happen
386 default:
387 break;
388 }
389 int ctlrng = ctlmx - ctlmn;
390 float fctlrng = float(ctlmx - ctlmn);
391
392 // Is it an integer control?
393 if(desc & LADSPA_HINT_INTEGER)
394 {
395 float ret = float(cval);
396 if(ret < fmin)
397 ret = fmin;
398 if(ret > fmax)
399 ret = fmax;
400 #ifdef PLUGIN_DEBUGIN
401 printf("midi2LadspaValue: has LADSPA_HINT_INTEGER returning:%f\n", ret);
402 #endif
403
404 return ret;
405 }
406
407 // Avoid divide-by-zero error below.
408 if(ctlrng == 0)
409 return 0.0;
410
411 // It's a floating point control, just use wide open maximum range.
412 float normval = float(bval) / fctlrng;
413 float ret = normval * frng + fmin;
414
415 #ifdef PLUGIN_DEBUGIN
416 printf("midi2LadspaValue: float returning:%f\n", ret);
417 #endif
418
419 return ret;
420 }
421
422 //---------------------------------------------------------
423 // ladspaCtrlValueType
424 //---------------------------------------------------------
425
ladspaCtrlValueType(const LADSPA_Descriptor * plugin,int port)426 CtrlValueType ladspaCtrlValueType(const LADSPA_Descriptor* plugin, int port)
427 {
428 LADSPA_PortRangeHint range = plugin->PortRangeHints[port];
429 LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor;
430
431 if(desc & LADSPA_HINT_INTEGER)
432 return VAL_INT;
433 else if(desc & LADSPA_HINT_LOGARITHMIC)
434 return VAL_LOG;
435 else if(desc & LADSPA_HINT_TOGGLED)
436 return VAL_BOOL;
437 else
438 return VAL_LINEAR;
439 }
440
441 //---------------------------------------------------------
442 // ladspaCtrlMode
443 //---------------------------------------------------------
444
ladspaCtrlMode(const LADSPA_Descriptor * plugin,int port)445 CtrlList::Mode ladspaCtrlMode(const LADSPA_Descriptor* plugin, int port)
446 {
447 LADSPA_PortRangeHint range = plugin->PortRangeHints[port];
448 LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor;
449
450 if(desc & LADSPA_HINT_INTEGER)
451 return CtrlList::DISCRETE;
452 else if(desc & LADSPA_HINT_LOGARITHMIC)
453 return CtrlList::INTERPOLATE;
454 else if(desc & LADSPA_HINT_TOGGLED)
455 return CtrlList::DISCRETE;
456 else
457 return CtrlList::INTERPOLATE;
458 }
459
460 // DELETETHIS 20
461 // Works but not needed.
462 /*
463 //---------------------------------------------------------
464 // ladspa2MidiController
465 //---------------------------------------------------------
466
467 MidiController* ladspa2MidiController(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum)
468 {
469 int min, max, def;
470
471 if(!ladspa2MidiControlValues(plugin, port, ctlnum, &min, &max, &def))
472 return 0;
473
474 MidiController* mc = new MidiController(QString(plugin->PortNames[port]), ctlnum, min, max, def);
475
476 return mc;
477 }
478 */
479
480 //----------------------------------------------------------------------------------
481 // defaultValue
482 // If no default ladspa value found, still sets *def to 1.0, but returns false.
483 //---------------------------------------------------------------------------------
484
ladspaDefaultValue(const LADSPA_Descriptor * plugin,unsigned long port,float * val)485 bool ladspaDefaultValue(const LADSPA_Descriptor* plugin, unsigned long port, float* val)
486 {
487 if(port < plugin->PortCount)
488 {
489 LADSPA_PortRangeHint range = plugin->PortRangeHints[port];
490 LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor;
491 float m = (rh & LADSPA_HINT_SAMPLE_RATE) ? float(MusEGlobal::sampleRate) : 1.0f;
492
493 if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh))
494 {
495 *val = range.LowerBound * m;
496 return true;
497 }
498 else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh))
499 {
500 *val = range.UpperBound*m;
501 return true;
502 }
503 else if (LADSPA_IS_HINT_DEFAULT_LOW(rh))
504 {
505 if (LADSPA_IS_HINT_LOGARITHMIC(rh))
506 {
507 *val = expf(logf(range.LowerBound * m) * .75 +
508 logf(range.UpperBound * m) * .25);
509 return true;
510 }
511 else
512 {
513 *val = range.LowerBound*.75*m + range.UpperBound*.25*m;
514 return true;
515 }
516 }
517 else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh))
518 {
519 if (LADSPA_IS_HINT_LOGARITHMIC(rh))
520 {
521 *val = expf(logf(range.LowerBound * m) * .5 +
522 logf(range.UpperBound * m) * .5);
523 return true;
524 }
525 else
526 {
527 *val = range.LowerBound*.5*m + range.UpperBound*.5*m;
528 return true;
529 }
530 }
531 else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh))
532 {
533 if (LADSPA_IS_HINT_LOGARITHMIC(rh))
534 {
535 *val = expf(logf(range.LowerBound * m) * .25 +
536 logf(range.UpperBound * m) * .75);
537 return true;
538 }
539 else
540 {
541 *val = range.LowerBound*.25*m + range.UpperBound*.75*m;
542 return true;
543 }
544 }
545 else if (LADSPA_IS_HINT_DEFAULT_0(rh))
546 {
547 *val = 0.0;
548 return true;
549 }
550 else if (LADSPA_IS_HINT_DEFAULT_1(rh))
551 {
552 *val = 1.0;
553 return true;
554 }
555 else if (LADSPA_IS_HINT_DEFAULT_100(rh))
556 {
557 *val = 100.0;
558 return true;
559 }
560 else if (LADSPA_IS_HINT_DEFAULT_440(rh))
561 {
562 *val = 440.0;
563 return true;
564 }
565
566 // No default found. Make one up...
567 else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh) && LADSPA_IS_HINT_BOUNDED_ABOVE(rh))
568 {
569 if (LADSPA_IS_HINT_LOGARITHMIC(rh))
570 {
571 *val = expf(logf(range.LowerBound * m) * .5 +
572 logf(range.UpperBound * m) * .5);
573 return true;
574 }
575 else
576 {
577 *val = range.LowerBound*.5*m + range.UpperBound*.5*m;
578 return true;
579 }
580 }
581 else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh))
582 {
583 *val = range.LowerBound;
584 return true;
585 }
586 else if (LADSPA_IS_HINT_BOUNDED_ABOVE(rh))
587 {
588 // Hm. What to do here... Just try 0.0 or the upper bound if less than zero.
589 //if(range.UpperBound > 0.0)
590 // *val = 0.0;
591 //else
592 // *val = range.UpperBound * m;
593 // Instead try this: Adopt an 'attenuator-like' policy, where upper is the default.
594 *val = range.UpperBound * m;
595 return true;
596 }
597 }
598
599 // No default found. Set return value to 0.0, but return false.
600 *val = 0.0;
601 return false;
602 }
603
604 //---------------------------------------------------------
605 // ladspaControlRange
606 //---------------------------------------------------------
607
ladspaControlRange(const LADSPA_Descriptor * plugin,unsigned long port,float * min,float * max)608 void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, float* min, float* max)
609 {
610 LADSPA_PortRangeHint range = plugin->PortRangeHints[port];
611 LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor;
612 if (desc & LADSPA_HINT_TOGGLED) {
613 *min = 0.0;
614 *max = 1.0;
615 return;
616 }
617 float m = 1.0;
618 if (desc & LADSPA_HINT_SAMPLE_RATE)
619 m = float(MusEGlobal::sampleRate);
620
621 if (desc & LADSPA_HINT_BOUNDED_BELOW)
622 *min = range.LowerBound * m;
623 else
624 *min = 0.0;
625 if (desc & LADSPA_HINT_BOUNDED_ABOVE)
626 *max = range.UpperBound * m;
627 else
628 *max = 1.0;
629 }
630
631
632 //==============================================================
633 // BEGIN PluginQuirks
634 //==============================================================
635
636 //---------------------------------------------------------
637 // write
638 //---------------------------------------------------------
639
write(int level,Xml & xml) const640 void PluginQuirks::write(int level, Xml& xml) const
641 {
642 // Defaults? Nothing to save.
643 if(!_fixedSpeed && !_transportAffectsAudioLatency && !_overrideReportedLatency
644 && _latencyOverrideValue == 0 && _fixNativeUIScaling == NatUISCaling::GLOBAL)
645 return;
646
647 xml.tag(level++, "quirks");
648
649 if(_fixedSpeed)
650 xml.intTag(level, "fixedSpeed", _fixedSpeed);
651
652 if(_transportAffectsAudioLatency)
653 xml.intTag(level, "trnspAffAudLat", _transportAffectsAudioLatency);
654
655 if(_overrideReportedLatency)
656 xml.intTag(level, "ovrRepAudLat", _overrideReportedLatency);
657
658 if(_latencyOverrideValue != 0)
659 xml.intTag(level, "latOvrVal", _latencyOverrideValue);
660
661 if(_fixNativeUIScaling != NatUISCaling::GLOBAL)
662 xml.intTag(level, "fixNatUIScal", _fixNativeUIScaling);
663
664 xml.etag(--level, "quirks");
665 }
666
667 //---------------------------------------------------------
668 // read
669 // return true on error
670 //---------------------------------------------------------
671
read(Xml & xml)672 bool PluginQuirks::read(Xml& xml)
673 {
674 for (;;) {
675 Xml::Token token(xml.parse());
676 const QString& tag(xml.s1());
677 switch (token) {
678 case Xml::Error:
679 case Xml::End:
680 return true;
681 case Xml::TagStart:
682 if (tag == "fixedSpeed")
683 _fixedSpeed = xml.parseInt();
684 else if (tag == "trnspAffAudLat")
685 _transportAffectsAudioLatency = xml.parseInt();
686 else if (tag == "ovrRepAudLat")
687 _overrideReportedLatency = xml.parseInt();
688 else if (tag == "latOvrVal")
689 _latencyOverrideValue = xml.parseInt();
690 else if (tag == "fixNatUIScal")
691 _fixNativeUIScaling = (NatUISCaling)xml.parseInt();
692 else
693 xml.unknown("PluginQuirks");
694 break;
695 case Xml::Attribut:
696 break;
697 case Xml::TagEnd:
698 if (tag == "quirks") {
699 return false;
700 }
701 return true;
702 default:
703 break;
704 }
705 }
706 return true;
707 }
708
fixNativeUIScaling() const709 bool PluginQuirks::fixNativeUIScaling() const {
710 if (qApp->devicePixelRatio() <= 1.0)
711 return false; // no hidpi monitor in use, no need to fix anything
712
713 return ((_fixNativeUIScaling == NatUISCaling::GLOBAL && MusEGlobal::config.noPluginScaling)
714 || _fixNativeUIScaling == NatUISCaling::ON);
715 }
716
717 //==============================================================
718 // END PluginQuirks
719 //==============================================================
720
721
722
723 //---------------------------------------------------------
724 // Plugin
725 //---------------------------------------------------------
726
Plugin(QFileInfo * f,const LADSPA_Descriptor * d,const QString & uri,bool isDssi,bool isDssiSynth,bool isDssiVst,PluginFeatures_t reqFeatures)727 Plugin::Plugin(QFileInfo* f, const LADSPA_Descriptor* d, const QString& uri,
728 bool isDssi, bool isDssiSynth, bool isDssiVst, PluginFeatures_t reqFeatures)
729 {
730 _isDssi = isDssi;
731 _isDssiSynth = isDssiSynth;
732 _isDssiVst = isDssiVst;
733 _isLV2Plugin = false;
734 _isLV2Synth = false;
735 _isVstNativePlugin = false;
736 _isVstNativeSynth = false;
737 _requiredFeatures = reqFeatures;
738 _usesTimePosition = false;
739
740
741 #ifdef DSSI_SUPPORT
742 dssi_descr = nullptr;
743 #endif
744
745 if(f)
746 fi = *f;
747 _uri = uri;
748
749 plugin = nullptr;
750 ladspa = nullptr;
751 _handle = nullptr;
752 _references = 0;
753 _instNo = 0;
754 _label = QString(d->Label);
755 _name = QString(d->Name);
756 _uniqueID = d->UniqueID;
757 _maker = QString(d->Maker);
758 _copyright = QString(d->Copyright);
759
760 _portCount = d->PortCount;
761
762 _inports = 0;
763 _outports = 0;
764 _controlInPorts = 0;
765 _controlOutPorts = 0;
766 for(unsigned long k = 0; k < _portCount; ++k)
767 {
768 LADSPA_PortDescriptor pd = d->PortDescriptors[k];
769 if(pd & LADSPA_PORT_AUDIO)
770 {
771 if(pd & LADSPA_PORT_INPUT)
772 ++_inports;
773 else
774 if(pd & LADSPA_PORT_OUTPUT)
775 ++_outports;
776 }
777 else
778 if(pd & LADSPA_PORT_CONTROL)
779 {
780 if(pd & LADSPA_PORT_INPUT)
781 ++_controlInPorts;
782 else
783 if(pd & LADSPA_PORT_OUTPUT)
784 ++_controlOutPorts;
785 }
786 }
787
788 // Hack: Blacklist vst plugins in-place, configurable for now.
789 if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace))
790 _requiredFeatures |= PluginNoInPlaceProcessing;
791 }
792
Plugin(const MusEPlugin::PluginScanInfoStruct & info)793 Plugin::Plugin(const MusEPlugin::PluginScanInfoStruct& info)
794 {
795 _isDssi = false;
796 _isDssiSynth = false;
797 _isDssiVst = false;
798 _isLV2Plugin = false;
799 _isLV2Synth = false;
800 _isVstNativePlugin = false;
801 _isVstNativeSynth = false;
802 _requiredFeatures = info._requiredFeatures;
803
804 switch(info._type)
805 {
806 case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA:
807 break;
808
809 case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI:
810 {
811 _isDssi = true;
812 if(info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument)
813 _isDssiSynth = true;
814 }
815 break;
816
817 case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST:
818 {
819 _isDssi = true;
820 _isDssiVst = true;
821 if(info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument)
822 _isDssiSynth = true;
823 }
824 break;
825
826 case MusEPlugin::PluginScanInfoStruct::PluginTypeVST:
827 case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2:
828 case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST:
829 case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS:
830 case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown:
831 case MusEPlugin::PluginScanInfoStruct::PluginTypeNone:
832 case MusEPlugin::PluginScanInfoStruct::PluginTypeAll:
833 break;
834 }
835
836 #ifdef DSSI_SUPPORT
837 dssi_descr = nullptr;
838 #endif
839
840 // fi = info._fi;
841 fi = QFileInfo(PLUGIN_GET_QSTRING(info.filePath()));
842 _uri = PLUGIN_GET_QSTRING(info._uri);
843
844 plugin = nullptr;
845 ladspa = nullptr;
846 _handle = nullptr;
847 _references = 0;
848 _instNo = 0;
849
850 _label = PLUGIN_GET_QSTRING(info._label);
851 _name = PLUGIN_GET_QSTRING(info._name);
852 _uniqueID = info._uniqueID;
853 _maker = PLUGIN_GET_QSTRING(info._maker);
854 _copyright = PLUGIN_GET_QSTRING(info._copyright);
855
856 _portCount = info._portCount;
857
858 _inports = info._inports;
859 _outports = info._outports;
860 _controlInPorts = info._controlInPorts;
861 _controlOutPorts = info._controlOutPorts;
862
863 // Hack: Blacklist vst plugins in-place, configurable for now.
864 if(_isDssiVst && !MusEGlobal::config.vstInPlace)
865 _requiredFeatures |= PluginNoInPlaceProcessing;
866
867 _usesTimePosition = info._pluginFlags & MusEPlugin::PluginScanInfoStruct::SupportsTimePosition;
868 }
869
~Plugin()870 Plugin::~Plugin()
871 {
872 if(plugin && !isLV2Plugin() && !isVstNativePlugin())
873 // delete plugin;
874 printf("Plugin::~Plugin Error: plugin is not NULL\n");
875 }
876
877 //---------------------------------------------------------
878 // incReferences
879 //---------------------------------------------------------
880
incReferences(int val)881 int Plugin::incReferences(int val)
882 {
883 #ifdef PLUGIN_DEBUGIN
884 fprintf(stderr, "Plugin::incReferences _references:%d val:%d\n", _references, val);
885 #endif
886
887 int newref = _references + val;
888
889 if(newref <= 0)
890 {
891 _references = 0;
892 if(_handle)
893 {
894 #ifdef PLUGIN_DEBUGIN
895 fprintf(stderr, "Plugin::incReferences no more instances, closing library\n");
896 #endif
897
898 dlclose(_handle);
899 }
900
901 _handle = nullptr;
902 ladspa = nullptr;
903 plugin = nullptr;
904 rpIdx.clear();
905
906 #ifdef DSSI_SUPPORT
907 dssi_descr = nullptr;
908 #endif
909
910 return 0;
911 }
912
913 if(_handle == nullptr)
914 {
915 _handle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW);
916
917 if(_handle == 0)
918 {
919 fprintf(stderr, "Plugin::incReferences dlopen(%s) failed: %s\n",
920 fi.filePath().toLatin1().constData(), dlerror());
921 return 0;
922 }
923
924 #ifdef DSSI_SUPPORT
925 DSSI_Descriptor_Function dssi = (DSSI_Descriptor_Function)dlsym(_handle, "dssi_descriptor");
926 if(dssi)
927 {
928 const DSSI_Descriptor* descr;
929 for(unsigned long i = 0;; ++i)
930 {
931 descr = dssi(i);
932 if(descr == nullptr)
933 break;
934
935 QString label(descr->LADSPA_Plugin->Label);
936 if(label == _label)
937 {
938 _isDssi = true;
939 ladspa = nullptr;
940 dssi_descr = descr;
941 plugin = descr->LADSPA_Plugin;
942 break;
943 }
944 }
945 }
946 else
947 #endif // DSSI_SUPPORT
948 {
949 LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_handle, "ladspa_descriptor");
950 if(ladspadf)
951 {
952 const LADSPA_Descriptor* descr;
953 for(unsigned long i = 0;; ++i)
954 {
955 descr = ladspadf(i);
956 if(descr == nullptr)
957 break;
958
959 QString label(descr->Label);
960 if(label == _label)
961 {
962 _isDssi = false;
963 ladspa = ladspadf;
964 plugin = descr;
965
966 #ifdef DSSI_SUPPORT
967 dssi_descr = nullptr;
968 #endif
969
970 break;
971 }
972 }
973 }
974 }
975
976 if(plugin != nullptr)
977 {
978 _name = QString(plugin->Name);
979 _uniqueID = plugin->UniqueID;
980 _maker = QString(plugin->Maker);
981 _copyright = QString(plugin->Copyright);
982
983 _portCount = plugin->PortCount;
984
985 _inports = 0;
986 _outports = 0;
987 _controlInPorts = 0;
988 _controlOutPorts = 0;
989 for(unsigned long k = 0; k < _portCount; ++k)
990 {
991 LADSPA_PortDescriptor pd = plugin->PortDescriptors[k];
992 if(pd & LADSPA_PORT_AUDIO)
993 {
994 if(pd & LADSPA_PORT_INPUT)
995 ++_inports;
996 else
997 if(pd & LADSPA_PORT_OUTPUT)
998 ++_outports;
999
1000 rpIdx.push_back((unsigned long)-1);
1001 }
1002 else
1003 if(pd & LADSPA_PORT_CONTROL)
1004 {
1005 if(pd & LADSPA_PORT_INPUT)
1006 {
1007 rpIdx.push_back(_controlInPorts);
1008 ++_controlInPorts;
1009 }
1010 else
1011 if(pd & LADSPA_PORT_OUTPUT)
1012 {
1013 rpIdx.push_back((unsigned long)-1);
1014 ++_controlOutPorts;
1015 }
1016 }
1017 }
1018
1019 // Hack: Blacklist vst plugins in-place, configurable for now.
1020 if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace))
1021 _requiredFeatures |= PluginNoInPlaceProcessing;
1022 }
1023 }
1024
1025 if(plugin == nullptr)
1026 {
1027 dlclose(_handle);
1028 _handle = 0;
1029 _references = 0;
1030 fprintf(stderr, "Plugin::incReferences Error: %s no plugin!\n", fi.filePath().toLatin1().constData());
1031 return 0;
1032 }
1033
1034 _references = newref;
1035
1036 return _references;
1037 }
1038
1039 //---------------------------------------------------------
1040 // range
1041 //---------------------------------------------------------
1042
range(unsigned long i,float * min,float * max) const1043 void Plugin::range(unsigned long i, float* min, float* max) const
1044 {
1045 ladspaControlRange(plugin, i, min, max);
1046 }
1047
1048 //---------------------------------------------------------
1049 // defaultValue
1050 //---------------------------------------------------------
1051
defaultValue(unsigned long port) const1052 double Plugin::defaultValue(unsigned long port) const
1053 {
1054 float val;
1055 ladspaDefaultValue(plugin, port, &val);
1056 return val;
1057 }
1058
1059 //---------------------------------------------------------
1060 // ctrlValueType
1061 //---------------------------------------------------------
1062
ctrlValueType(unsigned long i) const1063 CtrlValueType Plugin::ctrlValueType(unsigned long i) const
1064 {
1065 return ladspaCtrlValueType(plugin, i);
1066 }
1067
1068 //---------------------------------------------------------
1069 // ctrlMode
1070 //---------------------------------------------------------
1071
ctrlMode(unsigned long i) const1072 CtrlList::Mode Plugin::ctrlMode(unsigned long i) const
1073 {
1074 return ladspaCtrlMode(plugin, i);
1075 }
1076
ctrlEnumValues(unsigned long) const1077 CtrlEnumValues* Plugin::ctrlEnumValues ( unsigned long ) const
1078 {
1079 return nullptr;
1080 }
1081
shift_left(int first,int last)1082 void PluginGroups::shift_left(int first, int last)
1083 {
1084 for (int i=first; i<=last; i++)
1085 replace_group(i, i-1);
1086 }
1087
shift_right(int first,int last)1088 void PluginGroups::shift_right(int first, int last)
1089 {
1090 for (int i=last; i>=first; i--)
1091 replace_group(i,i+1);
1092 }
1093
erase(int index)1094 void PluginGroups::erase(int index)
1095 {
1096 for (PluginGroups::iterator it=begin(); it!=end(); it++)
1097 {
1098 it->remove(index);
1099 }
1100 }
1101
replace_group(int old,int now)1102 void PluginGroups::replace_group(int old, int now)
1103 {
1104 for (PluginGroups::iterator it=begin(); it!=end(); it++)
1105 {
1106 if (it->contains(old))
1107 {
1108 it->remove(old);
1109 it->insert(now);
1110 }
1111 }
1112 }
1113
1114 //---------------------------------------------------------
1115 // initPlugins
1116 //---------------------------------------------------------
1117
initPlugins()1118 void initPlugins()
1119 {
1120 const char* message = "Plugins: loadPluginLib: ";
1121 const MusEPlugin::PluginScanList& scan_list = MusEPlugin::pluginList;
1122 for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl)
1123 {
1124 const MusEPlugin::PluginScanInfoRef inforef = *isl;
1125 const MusEPlugin::PluginScanInfoStruct& info = inforef->info();
1126 switch(info._type)
1127 {
1128 case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA:
1129 {
1130 if(MusEGlobal::loadPlugins)
1131 {
1132 // Make sure it doesn't already exist.
1133 if(const Plugin* pl = MusEGlobal::plugins.find(
1134 PLUGIN_GET_QSTRING(info._completeBaseName),
1135 PLUGIN_GET_QSTRING(info._uri),
1136 PLUGIN_GET_QSTRING(info._label)))
1137 {
1138 fprintf(stderr, "Ignoring LADSPA effect label:%s uri:%s path:%s duplicate of path:%s\n",
1139 PLUGIN_GET_CSTRING(info._label),
1140 PLUGIN_GET_CSTRING(info._uri),
1141 PLUGIN_GET_CSTRING(info.filePath()),
1142 pl->filePath().toLatin1().constData());
1143 }
1144 else
1145 {
1146 if(MusEGlobal::debugMsg)
1147 info.dump(message);
1148 MusEGlobal::plugins.add(info);
1149 }
1150 }
1151 }
1152 break;
1153
1154 case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI:
1155 case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST:
1156 {
1157 #ifdef DSSI_SUPPORT
1158 if(MusEGlobal::loadDSSI)
1159 {
1160 // Allow both effects and instruments for now.
1161 if(info._class & MusEPlugin::PluginScanInfoStruct::PluginClassEffect ||
1162 info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument)
1163 {
1164 // Make sure it doesn't already exist.
1165 if(const Plugin* pl = MusEGlobal::plugins.find(
1166 PLUGIN_GET_QSTRING(info._completeBaseName),
1167 PLUGIN_GET_QSTRING(info._uri),
1168 PLUGIN_GET_QSTRING(info._label)))
1169 {
1170 fprintf(stderr, "Ignoring DSSI effect label:%s uri:%s path:%s duplicate of path:%s\n",
1171 PLUGIN_GET_CSTRING(info._label),
1172 PLUGIN_GET_CSTRING(info._uri),
1173 PLUGIN_GET_CSTRING(info.filePath()),
1174 pl->filePath().toLatin1().constData());
1175 }
1176 else
1177 {
1178 if(MusEGlobal::debugMsg)
1179 info.dump(message);
1180 MusEGlobal::plugins.add(info);
1181 }
1182 }
1183 }
1184 #endif
1185 }
1186 break;
1187
1188 case MusEPlugin::PluginScanInfoStruct::PluginTypeVST:
1189 case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2:
1190 case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST:
1191 case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS:
1192 case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown:
1193 case MusEPlugin::PluginScanInfoStruct::PluginTypeNone:
1194 case MusEPlugin::PluginScanInfoStruct::PluginTypeAll:
1195 break;
1196 }
1197 }
1198 }
1199
1200 //---------------------------------------------------------
1201 // find
1202 //---------------------------------------------------------
1203
find(const QString & file,const QString & uri,const QString & label) const1204 Plugin* PluginList::find(const QString& file, const QString& uri, const QString& label) const
1205 {
1206 const bool f_empty = file.isEmpty();
1207 const bool u_empty = uri.isEmpty();
1208 const bool l_empty = label.isEmpty();
1209 for (ciPlugin i = begin(); i != end(); ++i) {
1210 if ( (!u_empty || f_empty || file == (*i)->lib()) &&
1211 (u_empty || uri == (*i)->uri()) &&
1212 (!u_empty || l_empty || label == (*i)->label()))
1213 return *i;
1214 }
1215
1216 return 0;
1217 }
1218
1219 //---------------------------------------------------------
1220 // Pipeline
1221 //---------------------------------------------------------
1222
Pipeline()1223 Pipeline::Pipeline()
1224 : std::vector<PluginI*>()
1225 {
1226 for(int i = 0; i < MusECore::MAX_CHANNELS; ++i)
1227 buffer[i] = nullptr;
1228 initBuffers();
1229
1230 for (int i = 0; i < MusECore::PipelineDepth; ++i)
1231 push_back(nullptr);
1232 }
1233
1234 //---------------------------------------------------------
1235 // Pipeline copy constructor
1236 //---------------------------------------------------------
1237
Pipeline(const Pipeline & p,AudioTrack * t)1238 Pipeline::Pipeline(const Pipeline& p, AudioTrack* t)
1239 : std::vector<PluginI*>()
1240 {
1241 for(int i = 0; i < MusECore::MAX_CHANNELS; ++i)
1242 buffer[i] = nullptr;
1243 initBuffers();
1244
1245 for(int i = 0; i < MusECore::PipelineDepth; ++i)
1246 {
1247 PluginI* pli = p[i];
1248 if(pli)
1249 {
1250 Plugin* pl = pli->plugin();
1251 if(pl)
1252 {
1253 PluginI* new_pl = new PluginI();
1254 if(new_pl->initPluginInstance(pl, t->channels())) {
1255 fprintf(stderr, "cannot instantiate plugin <%s>\n",
1256 pl->name().toLatin1().constData());
1257 delete new_pl;
1258 }
1259 else
1260 {
1261 // Assigns valid ID and track to plugin, and creates controllers for plugin.
1262 t->setupPlugin(new_pl, i);
1263 push_back(new_pl);
1264 continue;
1265 }
1266 }
1267 }
1268 push_back(nullptr); // No plugin. Initialize with NULL.
1269 }
1270 }
1271
1272 //---------------------------------------------------------
1273 // ~Pipeline
1274 //---------------------------------------------------------
1275
~Pipeline()1276 Pipeline::~Pipeline()
1277 {
1278 removeAll();
1279 for (int i = 0; i < MusECore::MAX_CHANNELS; ++i)
1280 if(buffer[i])
1281 ::free(buffer[i]);
1282 }
1283
initBuffers()1284 void Pipeline::initBuffers()
1285 {
1286 for(int i = 0; i < MusECore::MAX_CHANNELS; ++i)
1287 {
1288 if(!buffer[i])
1289 {
1290 #ifdef _WIN32
1291 buffer[i] = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize);
1292 if(buffer[i] == NULL)
1293 {
1294 fprintf(stderr, "ERROR: Pipeline ctor: _aligned_malloc returned error: NULL. Aborting!\n");
1295 abort();
1296 }
1297 #else
1298 int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize);
1299 if(rv != 0)
1300 {
1301 fprintf(stderr, "ERROR: Pipeline ctor: posix_memalign returned error:%d. Aborting!\n", rv);
1302 abort();
1303 }
1304 #endif
1305 }
1306 }
1307
1308 for(int i = 0; i < MusECore::MAX_CHANNELS; ++i)
1309 {
1310 if(MusEGlobal::config.useDenormalBias)
1311 {
1312 for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q)
1313 buffer[i][q] = MusEGlobal::denormalBias;
1314 }
1315 else
1316 memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize);
1317 }
1318 }
1319
1320 //---------------------------------------------------------
1321 // latency
1322 //---------------------------------------------------------
1323
latency() const1324 float Pipeline::latency() const
1325 {
1326 float l = 0.0f;
1327 const PluginI* p;
1328 for(int i = 0; i < MusECore::PipelineDepth; ++i)
1329 {
1330 p = (*this)[i];
1331 if(p)
1332 {
1333 // REMOVE Tim. lv2. Added. TESTING. Do we need to leave this alone for reporting?
1334 // I think so... It seemed we do that with tracks but then those are wave and midi tracks...
1335 #if 0
1336 // If the transport affects audio latency, it means we can completely correct
1337 // for the latency by adjusting the transport, therefore meaning zero
1338 // resulting audio latency. As far as the rest of the app knows, the plugin
1339 // in this rack position has zero audio latency. Yet we still retain the
1340 // original latency value in each plugin so we can use it.
1341 if(!p->cquirks()._transportAffectsAudioLatency)
1342 #endif
1343 l+= p->latency();
1344 }
1345 }
1346 return l;
1347 }
1348
1349 //---------------------------------------------------------
1350 // addScheduledControlEvent
1351 // track_ctrl_id is the fully qualified track audio controller number
1352 // Returns true if event cannot be delivered
1353 //---------------------------------------------------------
1354
addScheduledControlEvent(int track_ctrl_id,double val,unsigned frame)1355 bool Pipeline::addScheduledControlEvent(int track_ctrl_id, double val, unsigned frame)
1356 {
1357 // If a track controller, or the special dssi synth controller block, just return.
1358 if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MusECore::MAX_PLUGINS, 0))
1359 return true;
1360 int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW;
1361 for (int i = 0; i < MusECore::PipelineDepth; ++i)
1362 {
1363 PluginI* p = (*this)[i];
1364 if(p && p->id() == rack_idx)
1365 return p->addScheduledControlEvent(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, val, frame);
1366 }
1367 return true;
1368 }
1369
1370 //---------------------------------------------------------
1371 // controllerEnabled
1372 // Returns whether automation control stream is enabled or disabled.
1373 // Used during automation recording to inhibit gui controls
1374 //---------------------------------------------------------
1375
controllerEnabled(int track_ctrl_id)1376 bool Pipeline::controllerEnabled(int track_ctrl_id)
1377 {
1378 // If a track controller, or the special dssi synth controller block, just return.
1379 if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MusECore::MAX_PLUGINS, 0))
1380 return false;
1381 int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW;
1382 for (int i = 0; i < MusECore::PipelineDepth; ++i)
1383 {
1384 PluginI* p = (*this)[i];
1385 if(p && p->id() == rack_idx)
1386 return p->controllerEnabled(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK);
1387 }
1388 return false;
1389 }
1390
1391 //---------------------------------------------------------
1392 // enableController
1393 // Enable or disable gui automation control stream.
1394 // Used during automation recording to inhibit gui controls
1395 //---------------------------------------------------------
1396
enableController(int track_ctrl_id,bool en)1397 void Pipeline::enableController(int track_ctrl_id, bool en)
1398 {
1399 // If a track controller, or the special dssi synth controller block, just return.
1400 if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MusECore::MAX_PLUGINS, 0))
1401 return;
1402 int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW;
1403 for (int i = 0; i < MusECore::PipelineDepth; ++i)
1404 {
1405 PluginI* p = (*this)[i];
1406 if(p && p->id() == rack_idx)
1407 {
1408 p->enableController(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, en);
1409 return;
1410 }
1411 }
1412 }
1413
1414 //---------------------------------------------------------
1415 // setChannels
1416 //---------------------------------------------------------
1417
setChannels(int n)1418 void Pipeline::setChannels(int n)
1419 {
1420 for (int i = 0; i < MusECore::PipelineDepth; ++i)
1421 if ((*this)[i])
1422 (*this)[i]->setChannels(n);
1423 }
1424
1425 //---------------------------------------------------------
1426 // insert
1427 // give ownership of object plugin to Pipeline
1428 //---------------------------------------------------------
1429
insert(PluginI * plugin,int index)1430 void Pipeline::insert(PluginI* plugin, int index)
1431 {
1432 remove(index);
1433 (*this)[index] = plugin;
1434 }
1435
1436 //---------------------------------------------------------
1437 // remove
1438 //---------------------------------------------------------
1439
remove(int index)1440 void Pipeline::remove(int index)
1441 {
1442 PluginI* plugin = (*this)[index];
1443 if (plugin)
1444 delete plugin;
1445 (*this)[index] = 0;
1446 }
1447
1448 //---------------------------------------------------------
1449 // removeAll
1450 //---------------------------------------------------------
1451
removeAll()1452 void Pipeline::removeAll()
1453 {
1454 for (int i = 0; i < MusECore::PipelineDepth; ++i)
1455 remove(i);
1456 }
1457
1458 //---------------------------------------------------------
1459 // isOn
1460 //---------------------------------------------------------
1461
isOn(int idx) const1462 bool Pipeline::isOn(int idx) const
1463 {
1464 PluginI* p = (*this)[idx];
1465 if (p)
1466 return p->on();
1467 return false;
1468 }
1469
1470 //---------------------------------------------------------
1471 // setOn
1472 //---------------------------------------------------------
1473
setOn(int idx,bool flag)1474 void Pipeline::setOn(int idx, bool flag)
1475 {
1476 PluginI* p = (*this)[idx];
1477 if (p) {
1478 p->setOn(flag);
1479 if (p->gui())
1480 p->gui()->setOn(flag);
1481 }
1482 }
1483
1484 //---------------------------------------------------------
1485 // label
1486 //---------------------------------------------------------
1487
label(int idx) const1488 QString Pipeline::label(int idx) const
1489 {
1490 PluginI* p = (*this)[idx];
1491 if (p)
1492 return p->label();
1493 return QString("");
1494 }
1495
1496 //---------------------------------------------------------
1497 // name
1498 //---------------------------------------------------------
1499
name(int idx) const1500 QString Pipeline::name(int idx) const
1501 {
1502 PluginI* p = (*this)[idx];
1503 if (p)
1504 return p->name();
1505
1506 return "<" + QObject::tr("FX slot") + " " + QString::number(idx + 1) + ">";
1507 }
1508
1509 //---------------------------------------------------------
1510 // uri
1511 //---------------------------------------------------------
1512
uri(int idx) const1513 QString Pipeline::uri(int idx) const
1514 {
1515 PluginI* p = (*this)[idx];
1516 if (p)
1517 return p->uri();
1518 return QString();
1519 }
1520
1521
1522 //---------------------------------------------------------
1523 // empty
1524 //---------------------------------------------------------
1525
empty(int idx) const1526 bool Pipeline::empty(int idx) const
1527 {
1528 PluginI* p = (*this)[idx];
1529 return p == 0;
1530 }
1531
1532 //---------------------------------------------------------
1533 // move
1534 //---------------------------------------------------------
1535
move(int idx,bool up)1536 void Pipeline::move(int idx, bool up)
1537 {
1538 PluginI* p1 = (*this)[idx];
1539 if (up)
1540 {
1541 (*this)[idx] = (*this)[idx-1];
1542
1543 if((*this)[idx]) {
1544 (*this)[idx]->setID(idx);
1545 }
1546
1547 (*this)[idx-1] = p1;
1548
1549 if(p1)
1550 {
1551 p1->setID(idx - 1);
1552 if(p1->track())
1553 MusEGlobal::audio->msgSwapControllerIDX(p1->track(), idx, idx - 1);
1554 }
1555 }
1556 else
1557 {
1558 (*this)[idx] = (*this)[idx+1];
1559
1560 if((*this)[idx]) {
1561 (*this)[idx]->setID(idx);
1562 }
1563
1564 (*this)[idx+1] = p1;
1565
1566 if(p1)
1567 {
1568 p1->setID(idx + 1);
1569 if(p1->track())
1570 MusEGlobal::audio->msgSwapControllerIDX(p1->track(), idx, idx + 1);
1571 }
1572 }
1573 }
1574
1575 //---------------------------------------------------------
1576 // isDssiPlugin
1577 //---------------------------------------------------------
1578
isDssiPlugin(int idx) const1579 bool Pipeline::isDssiPlugin(int idx) const
1580 {
1581 PluginI* p = (*this)[idx];
1582 if(p)
1583 return p->isDssiPlugin();
1584
1585 return false;
1586 }
1587
isLV2Plugin(int idx) const1588 bool Pipeline::isLV2Plugin(int idx) const
1589 {
1590 PluginI* p = (*this)[idx];
1591 if(p)
1592 return p->isLV2Plugin();
1593
1594 return false;
1595 }
1596
isVstNativePlugin(int idx) const1597 bool Pipeline::isVstNativePlugin(int idx) const
1598 {
1599 PluginI* p = (*this)[idx];
1600 if(p)
1601 return p->isVstNativePlugin();
1602
1603 return false;
1604
1605 }
1606
1607 //---------------------------------------------------------
1608 // has_dssi_ui
1609 //---------------------------------------------------------
1610
has_dssi_ui(int idx) const1611 bool Pipeline::has_dssi_ui(int idx) const
1612 {
1613 PluginI* p = (*this)[idx];
1614 if(p)
1615 {
1616 #ifdef LV2_SUPPORT
1617 if(p->plugin() && p->plugin()->isLV2Plugin())
1618 return ((LV2PluginWrapper *)p->plugin())->hasNativeGui();
1619 #endif
1620
1621 #ifdef VST_NATIVE_SUPPORT
1622 if(p->plugin() && p->plugin()->isVstNativePlugin())
1623 return ((VstNativePluginWrapper *)p->plugin())->hasNativeGui();
1624 #endif
1625
1626
1627 return !p->dssi_ui_filename().isEmpty();
1628 }
1629
1630 return false;
1631 }
1632 //---------------------------------------------------------
1633 // showGui
1634 //---------------------------------------------------------
1635
showGui(int idx,bool flag)1636 void Pipeline::showGui(int idx, bool flag)
1637 {
1638 PluginI* p = (*this)[idx];
1639 if (p)
1640 p->showGui(flag);
1641 }
1642
1643 //---------------------------------------------------------
1644 // showNativeGui
1645 //---------------------------------------------------------
1646
1647 #if defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT)
showNativeGui(int idx,bool flag)1648 void Pipeline::showNativeGui(int idx, bool flag)
1649 {
1650 PluginI* p = (*this)[idx];
1651 #ifdef LV2_SUPPORT
1652 if(p && p->plugin()->isLV2Plugin())
1653 {
1654 ((LV2PluginWrapper *)p->plugin())->showNativeGui(p, flag);
1655 return;
1656 }
1657
1658 #endif
1659
1660 #ifdef VST_NATIVE_SUPPORT
1661 if(p && p->plugin()->isVstNativePlugin())
1662 {
1663 ((VstNativePluginWrapper *)p->plugin())->showNativeGui(p, flag);
1664 return;
1665 }
1666
1667 #endif
1668 #ifdef OSC_SUPPORT
1669 if (p)
1670 p->oscIF().oscShowGui(flag);
1671 #endif
1672 }
1673 #else // defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT)
showNativeGui(int,bool)1674 void Pipeline::showNativeGui(int /*idx*/, bool /*flag*/)
1675 {
1676 }
1677 #endif // defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT)
1678
1679 //---------------------------------------------------------
1680 // deleteGui
1681 //---------------------------------------------------------
1682
deleteGui(int idx)1683 void Pipeline::deleteGui(int idx)
1684 {
1685 if(idx >= MusECore::PipelineDepth)
1686 return;
1687 PluginI* p = (*this)[idx];
1688 if(p)
1689 p->deleteGui();
1690 #ifdef LV2_SUPPORT
1691 if(p && p->plugin()->isLV2Plugin())
1692 {
1693 ((LV2PluginWrapper *)p->plugin())->showNativeGui(p, false);
1694 }
1695
1696 #endif
1697
1698 #ifdef VST_NATIVE_SUPPORT
1699 if(p && p->plugin()->isVstNativePlugin())
1700 {
1701 ((VstNativePluginWrapper *)p->plugin())->showNativeGui(p, false);
1702 }
1703
1704 #endif
1705 }
1706
1707 //---------------------------------------------------------
1708 // deleteAllGuis
1709 //---------------------------------------------------------
1710
deleteAllGuis()1711 void Pipeline::deleteAllGuis()
1712 {
1713 for(int i = 0; i < MusECore::PipelineDepth; i++)
1714 deleteGui(i);
1715 }
1716
1717 //---------------------------------------------------------
1718 // guiVisible
1719 //---------------------------------------------------------
1720
guiVisible(int idx)1721 bool Pipeline::guiVisible(int idx)
1722 {
1723 PluginI* p = (*this)[idx];
1724 if (p)
1725 return p->guiVisible();
1726 return false;
1727 }
1728
1729 //---------------------------------------------------------
1730 // nativeGuiVisible
1731 //---------------------------------------------------------
1732
nativeGuiVisible(int idx)1733 bool Pipeline::nativeGuiVisible(int idx)
1734 {
1735 PluginI* p = (*this)[idx];
1736 if (p)
1737 {
1738 #ifdef LV2_SUPPORT
1739 if(p->plugin()->isLV2Plugin())
1740 return ((LV2PluginWrapper *)p->plugin())->nativeGuiVisible(p);
1741 #endif
1742
1743 #ifdef VST_NATIVE_SUPPORT
1744 if(p->plugin()->isVstNativePlugin())
1745 return ((VstNativePluginWrapper *)p->plugin())->nativeGuiVisible(p);
1746 #endif
1747
1748 return p->nativeGuiVisible();
1749
1750 }
1751 return false;
1752 }
1753
1754 //---------------------------------------------------------
1755 // apply
1756 // If ports is 0, just process controllers only, not audio (do not 'run').
1757 //---------------------------------------------------------
1758
apply(unsigned pos,unsigned long ports,unsigned long nframes,float ** buffer1)1759 void Pipeline::apply(unsigned pos, unsigned long ports, unsigned long nframes, float** buffer1)
1760 {
1761 bool swap = false;
1762
1763 // Divide up the total pipeline latency to distribute latency correction
1764 // among the plugins according to the latency of each plugin. Each has
1765 // more correction than the next. The values are negative, meaning 'correction'.
1766 const int sz = size();
1767 float latency_corr_offsets[sz];
1768 float latency_corr_offset = 0.0f;
1769 for(int i = sz - 1; i >= 0; --i)
1770 {
1771 const PluginI* p = (*this)[i];
1772 if(!p)
1773 continue;
1774 const float lat = p->latency();
1775 // If the transport affects audio latency, it means we can completely correct
1776 // for the latency by adjusting the transport, therefore meaning zero
1777 // resulting audio latency. As far as the rest of the app knows, the plugin
1778 // in this rack position has zero audio latency. Yet we still retain the
1779 // original latency value in each plugin so we can use it.
1780 // Here we use a neat trick to conditionally subtract as we go, yet still
1781 // set the right transport correction offset value for each plugin.
1782 latency_corr_offsets[i] = latency_corr_offset - lat;
1783 if(!p->cquirks()._transportAffectsAudioLatency)
1784 latency_corr_offset -= lat;
1785 }
1786
1787 for (int i = 0; i < sz; ++i) {
1788 PluginI* p = (*this)[i];
1789 if(!p)
1790 continue;
1791
1792 const float corr_offset = latency_corr_offsets[i];
1793 if (p->on())
1794 {
1795 if (!(p->requiredFeatures() & PluginNoInPlaceProcessing))
1796 {
1797 if (swap)
1798 p->apply(pos, nframes, ports, buffer, buffer, corr_offset);
1799 else
1800 p->apply(pos, nframes, ports, buffer1, buffer1, corr_offset);
1801 }
1802 else
1803 {
1804 if (swap)
1805 p->apply(pos, nframes, ports, buffer, buffer1, corr_offset);
1806 else
1807 p->apply(pos, nframes, ports, buffer1, buffer, corr_offset);
1808 swap = !swap;
1809 }
1810 }
1811 else
1812 {
1813 p->apply(pos, nframes, 0, 0, 0, corr_offset); // Do not process (run) audio, process controllers only.
1814 }
1815 }
1816 if (ports != 0 && swap)
1817 {
1818 for (unsigned long i = 0; i < ports; ++i)
1819 AL::dsp->cpy(buffer1[i], buffer[i], nframes);
1820 }
1821 }
1822
1823 //---------------------------------------------------------
1824 // PluginIBase
1825 //---------------------------------------------------------
1826
PluginIBase()1827 PluginIBase::PluginIBase()
1828 {
1829 _gui = 0;
1830 }
1831
~PluginIBase()1832 PluginIBase::~PluginIBase()
1833 {
1834 if(_gui)
1835 delete _gui;
1836 }
1837
1838 //---------------------------------------------------------
1839 // showGui
1840 //---------------------------------------------------------
1841
showGui()1842 void PluginIBase::showGui()
1843 {
1844 if(_gui == 0)
1845 makeGui();
1846 _gui->updateWindowTitle();
1847 if(_gui->isVisible())
1848 _gui->hide();
1849 else
1850 _gui->show();
1851 }
1852
showGui(bool flag)1853 void PluginIBase::showGui(bool flag)
1854 {
1855 if(flag)
1856 {
1857 if(_gui == 0)
1858 makeGui();
1859 _gui->show();
1860 }
1861 else
1862 {
1863 if(_gui)
1864 _gui->hide();
1865 }
1866 }
1867
1868 //---------------------------------------------------------
1869 // guiVisible
1870 //---------------------------------------------------------
1871
guiVisible() const1872 bool PluginIBase::guiVisible() const
1873 {
1874 return _gui && _gui->isVisible();
1875 }
1876
setGeometry(int x,int y,int w,int h)1877 void PluginIBase::setGeometry(int x, int y, int w, int h)
1878 {
1879 _guiGeometry = QRect(x, y, w, h);
1880 if(_gui)
1881 {
1882
1883 #ifdef QT_SHOW_POS_BUG_WORKAROUND
1884 // Because of the bug, no matter what we must supply a position,
1885 // even upon first showing...
1886
1887 // Check sane size.
1888 if(w == 0)
1889 w = _gui->sizeHint().width();
1890 if(h == 0)
1891 h = _gui->sizeHint().height();
1892
1893 // No size hint? Try minimum size.
1894 if(w == 0)
1895 w = _gui->minimumSize().width();
1896 if(h == 0)
1897 h = _gui->minimumSize().height();
1898
1899 // Fallback.
1900 if(w == 0)
1901 w = 200;
1902 if(h == 0)
1903 h = 200;
1904
1905 _gui->setGeometry(x, y, w, h);
1906
1907 #else
1908
1909 // If the saved geometry is valid, use it.
1910 // Otherwise this is probably the first time showing,
1911 // so do not set a geometry - let Qt pick one
1912 // (using auto-placement and sizeHint).
1913 if(!(x == 0 && y == 0 && w == 0 && h == 0))
1914 {
1915 // Check sane size.
1916 if(w == 0)
1917 w = _gui->sizeHint().width();
1918 if(h == 0)
1919 h = _gui->sizeHint().height();
1920
1921 // No size hint? Try minimum size.
1922 if(w == 0)
1923 w = _gui->minimumSize().width();
1924 if(h == 0)
1925 h = _gui->minimumSize().height();
1926
1927 // Fallback.
1928 if(w == 0)
1929 w = 200;
1930 if(h == 0)
1931 h = 200;
1932
1933 _gui->setGeometry(x, y, w, h);
1934 }
1935 #endif
1936
1937 }
1938 }
1939
1940 // Returns the current geometry of the gui, or if the gui does not exist,
1941 // the saved gui geometry.
getGeometry(int * x,int * y,int * w,int * h) const1942 void PluginIBase::getGeometry(int *x, int *y, int *w, int *h) const
1943 {
1944 // If gui does not exist return the saved geometry.
1945 if(!_gui)
1946 {
1947 if(x) *x = _guiGeometry.x();
1948 if(y) *y = _guiGeometry.y();
1949 if(w) *w = _guiGeometry.width();
1950 if(h) *h = _guiGeometry.height();
1951 return;
1952 }
1953
1954 // Return the actual gui geometry.
1955 if(x) *x = _gui->geometry().x();
1956 if(y) *y = _gui->geometry().y();
1957 if(w) *w = _gui->geometry().width();
1958 if(h) *h = _gui->geometry().height();
1959 }
1960
1961 // Saves the current gui geometry.
saveGeometry(int x,int y,int w,int h)1962 void PluginIBase::saveGeometry(int x, int y, int w, int h)
1963 {
1964 _guiGeometry = QRect(x, y, w, h);
1965 }
1966
1967 // Returns the saved gui geometry.
savedGeometry(int * x,int * y,int * w,int * h) const1968 void PluginIBase::savedGeometry(int *x, int *y, int *w, int *h) const
1969 {
1970 if(x) *x = _guiGeometry.x();
1971 if(y) *y = _guiGeometry.y();
1972 if(w) *w = _guiGeometry.width();
1973 if(h) *h = _guiGeometry.height();
1974 }
1975
1976
1977 // Sets the gui's geometry. Also updates the saved geometry.
setNativeGeometry(int x,int y,int w,int h)1978 void PluginIBase::setNativeGeometry(int x, int y, int w, int h)
1979 {
1980 _nativeGuiGeometry = QRect(x, y, w, h);
1981 }
1982
1983 // Returns the current geometry of the gui, or if the gui does not exist,
1984 // the saved gui geometry.
getNativeGeometry(int * x,int * y,int * w,int * h) const1985 void PluginIBase::getNativeGeometry(int *x, int *y, int *w, int *h) const
1986 {
1987 if(x) *x = 0;
1988 if(y) *y = 0;
1989 if(w) *w = 0;
1990 if(h) *h = 0;
1991 }
1992
1993 // Saves the current gui geometry.
saveNativeGeometry(int x,int y,int w,int h)1994 void PluginIBase::saveNativeGeometry(int x, int y, int w, int h)
1995 {
1996 _nativeGuiGeometry = QRect(x, y, w, h);
1997 }
1998
1999 // Returns the saved gui geometry.
savedNativeGeometry(int * x,int * y,int * w,int * h) const2000 void PluginIBase::savedNativeGeometry(int *x, int *y, int *w, int *h) const
2001 {
2002 if(x) *x = _nativeGuiGeometry.x();
2003 if(y) *y = _nativeGuiGeometry.y();
2004 if(w) *w = _nativeGuiGeometry.width();
2005 if(h) *h = _nativeGuiGeometry.height();
2006 }
2007
2008 //---------------------------------------------------------
2009 // addScheduledControlEvent
2010 // i is the specific index of the control input port
2011 // Returns true if event cannot be delivered
2012 //---------------------------------------------------------
2013
addScheduledControlEvent(unsigned long i,double val,unsigned frame)2014 bool PluginIBase::addScheduledControlEvent(unsigned long i, double val, unsigned frame)
2015 {
2016 if(i >= parameters())
2017 {
2018 printf("PluginIBase::addScheduledControlEvent param number %lu out of range of ports:%lu\n", i, parameters());
2019 return true;
2020 }
2021 ControlEvent ce;
2022 ce.unique = false;
2023 ce.fromGui = false;
2024 ce.idx = i;
2025 ce.value = val;
2026 // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp().
2027 // timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events
2028 // are treated when they arrive in our ALSA driver.)
2029 //ce.frame = MusEGlobal::audio->timestamp();
2030 // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which
2031 // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead:
2032 ce.frame = frame;
2033
2034 if(_controlFifo.put(ce))
2035 {
2036 fprintf(stderr, "PluginIBase::addScheduledControlEvent: fifo overflow: in control number:%lu\n", i);
2037 return true;
2038 }
2039 return false;
2040 }
2041
ctrlEnumValues(unsigned long i) const2042 CtrlEnumValues* PluginIBase::ctrlEnumValues(unsigned long i) const {
2043 Q_UNUSED(i)
2044 return nullptr;
2045 }
2046
portGroup(long unsigned int i) const2047 QString PluginIBase::portGroup(long unsigned int i) const {
2048 Q_UNUSED(i)
2049 return QString();
2050 }
2051
ctrlIsTrigger(long unsigned int i) const2052 bool PluginIBase::ctrlIsTrigger(long unsigned int i) const {
2053 Q_UNUSED(i)
2054 return false;
2055 }
2056
ctrlNotOnGui(long unsigned int i) const2057 bool PluginIBase::ctrlNotOnGui(long unsigned int i) const {
2058 Q_UNUSED(i)
2059 return false;
2060 }
2061
2062
dssi_ui_filename() const2063 QString PluginIBase::dssi_ui_filename() const
2064 {
2065 QString libr(lib());
2066 if(dirPath().isEmpty() || libr.isEmpty())
2067 return QString();
2068
2069 QString guiPath(dirPath() + "/" + libr);
2070
2071 QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files);
2072 if(!guiDir.exists())
2073 return QString();
2074
2075 QStringList list = guiDir.entryList();
2076
2077 QString plug(pluginLabel());
2078 QString lib_qt_ui;
2079 QString lib_any_ui;
2080 QString plug_qt_ui;
2081 QString plug_any_ui;
2082
2083 for(int i = 0; i < list.count(); ++i)
2084 {
2085 QFileInfo fi(guiPath + QString("/") + list[i]);
2086 QString gui(fi.filePath());
2087 struct stat buf;
2088 if(stat(gui.toLatin1().constData(), &buf))
2089 continue;
2090 if(!((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) &&
2091 (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
2092 continue;
2093
2094 // FIXME: Qt::CaseInsensitive - a quick and dirty way to accept any suffix. Should be case sensitive...
2095 if(!libr.isEmpty())
2096 {
2097 if(lib_qt_ui.isEmpty() && list[i].contains(libr + QString("_qt"), Qt::CaseInsensitive))
2098 lib_qt_ui = gui;
2099 if(lib_any_ui.isEmpty() && list[i].contains(libr + QString('_') /*, Qt::CaseInsensitive*/))
2100 lib_any_ui = gui;
2101 }
2102 if(!plug.isEmpty())
2103 {
2104 if(plug_qt_ui.isEmpty() && list[i].contains(plug + QString("_qt"), Qt::CaseInsensitive))
2105 plug_qt_ui = gui;
2106 if(plug_any_ui.isEmpty() && list[i].contains(plug + QString('_') /*, Qt::CaseInsensitive*/))
2107 plug_any_ui = gui;
2108 }
2109 }
2110
2111 // Prefer qt plugin ui
2112 if(!plug_qt_ui.isEmpty())
2113 return plug_qt_ui;
2114 // Prefer any plugin ui
2115 if(!plug_any_ui.isEmpty())
2116 return plug_any_ui;
2117 // Prefer qt lib ui
2118 if(!lib_qt_ui.isEmpty())
2119 return lib_qt_ui;
2120 // Prefer any lib ui
2121 if(!lib_any_ui.isEmpty())
2122 return lib_any_ui;
2123
2124 // No suitable UI file found
2125 return QString();
2126 };
2127
2128 //---------------------------------------------------------
2129 // PluginI
2130 //---------------------------------------------------------
2131
init()2132 void PluginI::init()
2133 {
2134 _plugin = 0;
2135 instances = 0;
2136 handle = 0;
2137 controls = 0;
2138 controlsOut = 0;
2139 controlsOutDummy = 0;
2140 controlPorts = 0;
2141 controlOutPorts = 0;
2142 _audioInSilenceBuf = 0;
2143 _audioOutDummyBuf = 0;
2144 _hasLatencyOutPort = false;
2145 _latencyOutPort = 0;
2146 _on = true;
2147 initControlValues = false;
2148 _showNativeGuiPending = false;
2149 }
2150
PluginI()2151 PluginI::PluginI()
2152 {
2153 _id = -1;
2154 _track = 0;
2155
2156 init();
2157 }
2158
2159 //---------------------------------------------------------
2160 // PluginI
2161 //---------------------------------------------------------
2162
~PluginI()2163 PluginI::~PluginI()
2164 {
2165 #ifdef OSC_SUPPORT
2166 _oscif.oscSetPluginI(nullptr);
2167 #endif
2168
2169 if (_plugin) {
2170 deactivate();
2171 _plugin->incReferences(-1);
2172 }
2173
2174 if(_audioInSilenceBuf)
2175 free(_audioInSilenceBuf);
2176 if(_audioOutDummyBuf)
2177 free(_audioOutDummyBuf);
2178
2179 if (controlsOutDummy)
2180 delete[] controlsOutDummy;
2181 if (controlsOut)
2182 delete[] controlsOut;
2183 if (controls)
2184 delete[] controls;
2185 if (handle)
2186 delete[] handle;
2187 }
2188
2189 //---------------------------------------------------------
2190 // setID
2191 //---------------------------------------------------------
2192
setID(int i)2193 void PluginI::setID(int i)
2194 {
2195 _id = i;
2196 }
2197
2198 //---------------------------------------------------------
2199 // updateControllers
2200 //---------------------------------------------------------
2201
updateControllers()2202 void PluginI::updateControllers()
2203 {
2204 if(!_track)
2205 return;
2206
2207 for(unsigned long i = 0; i < controlPorts; ++i)
2208 _track->setPluginCtrlVal(genACnum(_id, i), controls[i].val); // TODO A faster bulk message
2209 }
2210
2211 //---------------------------------------------------------
2212 // setChannel
2213 //---------------------------------------------------------
2214
setChannels(int c)2215 void PluginI::setChannels(int c)
2216 {
2217 channel = c;
2218
2219 unsigned long ins = _plugin->inports();
2220 unsigned long outs = _plugin->outports();
2221 int ni = 1;
2222 if(outs)
2223 ni = c / outs;
2224 else
2225 if(ins)
2226 ni = c / ins;
2227
2228 if(ni < 1)
2229 ni = 1;
2230
2231 if (ni == instances)
2232 return;
2233
2234 LADSPA_Handle* handles = new LADSPA_Handle[ni];
2235
2236 if(ni > instances)
2237 {
2238 for(int i = 0; i < ni; ++i)
2239 {
2240 if(i < instances)
2241 // Transfer existing handle from old array to new array.
2242 handles[i] = handle[i];
2243 else
2244 {
2245 // Create a new plugin instance with handle.
2246 handles[i] = _plugin->instantiate(this);
2247 if(handles[i] == nullptr)
2248 {
2249 fprintf(stderr, "PluginI::setChannels: cannot instantiate instance %d\n", i);
2250
2251 // Although this is a messed up state not easy to get out of (final # of channels?), try not to assert().
2252 // Whoever uses these will have to check instance count or null handle, and try to gracefully fix it and allow a song save.
2253 for(int k = i; k < ni; ++k)
2254 handles[i] = nullptr;
2255 ni = i + 1;
2256 //channel = ?;
2257 break;
2258 }
2259 }
2260 }
2261 }
2262 else
2263 {
2264 for(int i = 0; i < instances; ++i)
2265 {
2266 if(i < ni)
2267 // Transfer existing handle from old array to new array.
2268 handles[i] = handle[i];
2269 else
2270 {
2271 // Delete existing plugin instance.
2272 // Previously we deleted all the instances and rebuilt from scratch.
2273 // One side effect of this: Since a GUI is constructed only on the first handle,
2274 // previously the native GUI would close when changing channels. Now it doesn't, which is good.
2275 _plugin->deactivate(handle[i]);
2276 _plugin->cleanup(handle[i]);
2277 }
2278 }
2279 }
2280
2281 // Delete the old array, and set the new array.
2282 delete[] handle;
2283 handle = handles;
2284
2285 // Connect ports:
2286 unsigned long curPort = 0;
2287 unsigned long curOutPort = 0;
2288 unsigned long ports = _plugin->ports();
2289 for(unsigned long k = 0; k < ports; ++k)
2290 {
2291 LADSPA_PortDescriptor pd = _plugin->portd(k);
2292 if(pd & LADSPA_PORT_CONTROL)
2293 {
2294 if(pd & LADSPA_PORT_INPUT)
2295 {
2296 for(int i = instances; i < ni; ++i)
2297 _plugin->connectPort(handle[i], k, &controls[curPort].val);
2298 controls[curPort].idx = k;
2299 ++curPort;
2300 }
2301 else if(pd & LADSPA_PORT_OUTPUT)
2302 {
2303 // Connect only the first instance's output controls.
2304 // We don't have a mechanism to display the other instances' outputs.
2305 _plugin->connectPort(handle[0], k, &controlsOut[curOutPort].val);
2306 // Connect the rest to dummy ports.
2307 for(int i = 1; i < ni; ++i)
2308 _plugin->connectPort(handle[i], k, &controlsOutDummy[curOutPort].val);
2309 controlsOut[curOutPort].idx = k;
2310 ++curOutPort;
2311 }
2312 }
2313 }
2314
2315 // Activate new instances.
2316 for(int i = instances; i < ni; ++i)
2317 _plugin->activate(handle[i]);
2318
2319 // Initialize control values.
2320 if(initControlValues)
2321 {
2322 for(unsigned long i = 0; i < controlPorts; ++i)
2323 controls[i].val = controls[i].tmpVal;
2324 }
2325 else
2326 {
2327 // get initial control values from plugin
2328 for(unsigned long i = 0; i < controlPorts; ++i)
2329 controls[i].tmpVal = controls[i].val;
2330 }
2331
2332 // Finally, set the new number of instances.
2333 instances = ni;
2334 }
2335
2336 //---------------------------------------------------------
2337 // setParam
2338 //---------------------------------------------------------
2339
setParam(unsigned long i,double val)2340 void PluginI::setParam(unsigned long i, double val)
2341 {
2342 addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame());
2343 }
2344
2345 //---------------------------------------------------------
2346 // defaultValue
2347 //---------------------------------------------------------
2348
defaultValue(unsigned long param) const2349 double PluginI::defaultValue(unsigned long param) const
2350 {
2351 if(param >= controlPorts)
2352 return 0.0;
2353
2354 return _plugin->defaultValue(controls[param].idx);
2355 }
2356
setCustomData(const std::vector<QString> & customParams)2357 void PluginI::setCustomData(const std::vector<QString>&
2358 #if defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT)
2359 customParams
2360 #endif
2361 )
2362 {
2363 if(_plugin == nullptr)
2364 return;
2365
2366 #ifdef LV2_SUPPORT
2367 if(_plugin->isLV2Plugin()) //now only do it for lv2 plugs
2368 {
2369
2370 LV2PluginWrapper *lv2Plug = static_cast<LV2PluginWrapper *>(_plugin);
2371 for(int i = 0; i < instances; ++i)
2372 {
2373 lv2Plug->setCustomData(handle [i], customParams);
2374 }
2375 }
2376 #endif
2377
2378 #ifdef VST_NATIVE_SUPPORT
2379 if(_plugin->isVstNativePlugin()) //now only do it for lv2 plugs
2380 {
2381
2382 VstNativePluginWrapper *vstPlug = static_cast<VstNativePluginWrapper *>(_plugin);
2383 for(int i = 0; i < instances; ++i)
2384 {
2385 vstPlug->setCustomData(handle [i], customParams);
2386 }
2387 }
2388 #endif
2389 }
2390
instantiate(PluginI *)2391 LADSPA_Handle Plugin::instantiate(PluginI *)
2392 {
2393 LADSPA_Handle h = plugin->instantiate(plugin, MusEGlobal::sampleRate);
2394 if(h == nullptr)
2395 {
2396 fprintf(stderr, "Plugin::instantiate() Error: plugin:%s instantiate failed!\n", plugin->Label);
2397 return nullptr;
2398 }
2399
2400 return h;
2401 }
2402
2403
2404
2405 //---------------------------------------------------------
2406 // initPluginInstance
2407 // return true on error
2408 //---------------------------------------------------------
2409
initPluginInstance(Plugin * plug,int c)2410 bool PluginI::initPluginInstance(Plugin* plug, int c)
2411 {
2412 channel = c;
2413 if(plug == nullptr)
2414 {
2415 printf("initPluginInstance: zero plugin\n");
2416 return true;
2417 }
2418 _plugin = plug;
2419
2420 if (_plugin->incReferences(1)==0)
2421 return true;
2422
2423 #ifdef OSC_SUPPORT
2424 _oscif.oscSetPluginI(this);
2425 #endif
2426
2427 QString inst("-" + QString::number(_plugin->instNo()));
2428 _name = _plugin->name() + inst;
2429 _label = _plugin->label() + inst;
2430
2431 unsigned long ins = plug->inports();
2432 unsigned long outs = plug->outports();
2433 if(outs)
2434 {
2435 instances = channel / outs;
2436 if(instances < 1)
2437 instances = 1;
2438 }
2439 else
2440 if(ins)
2441 {
2442 instances = channel / ins;
2443 if(instances < 1)
2444 instances = 1;
2445 }
2446 else
2447 instances = 1;
2448
2449 handle = new LADSPA_Handle[instances];
2450 for(int i = 0; i < instances; ++i)
2451 handle[i]=nullptr;
2452
2453 for(int i = 0; i < instances; ++i)
2454 {
2455 #ifdef PLUGIN_DEBUGIN
2456 fprintf(stderr, "PluginI::initPluginInstance instance:%d\n", i);
2457 #endif
2458
2459 handle[i] = _plugin->instantiate(this);
2460 if(handle[i] == nullptr)
2461 return true;
2462 }
2463
2464 unsigned long ports = _plugin->ports();
2465
2466 controlPorts = 0;
2467 controlOutPorts = 0;
2468
2469 for(unsigned long k = 0; k < ports; ++k)
2470 {
2471 LADSPA_PortDescriptor pd = _plugin->portd(k);
2472 if(pd & LADSPA_PORT_CONTROL)
2473 {
2474 if(pd & LADSPA_PORT_INPUT)
2475 ++controlPorts;
2476 else
2477 if(pd & LADSPA_PORT_OUTPUT)
2478 ++controlOutPorts;
2479 }
2480 }
2481
2482 controls = new Port[controlPorts];
2483 controlsOut = new Port[controlOutPorts];
2484 controlsOutDummy = new Port[controlOutPorts];
2485
2486 unsigned long curPort = 0;
2487 unsigned long curOutPort = 0;
2488 for(unsigned long k = 0; k < ports; ++k)
2489 {
2490 LADSPA_PortDescriptor pd = _plugin->portd(k);
2491 if(pd & LADSPA_PORT_CONTROL)
2492 {
2493 if(pd & LADSPA_PORT_INPUT)
2494 {
2495 controls[curPort].idx = k;
2496 double val = _plugin->defaultValue(k);
2497 controls[curPort].val = val;
2498 controls[curPort].tmpVal = val;
2499 controls[curPort].enCtrl = true;
2500 for(int i = 0; i < instances; ++i)
2501 _plugin->connectPort(handle[i], k, &controls[curPort].val);
2502 ++curPort;
2503 }
2504 else
2505 if(pd & LADSPA_PORT_OUTPUT)
2506 {
2507 const char* pname = _plugin->portName(k);
2508 if(pname == QString("latency") || pname == QString("_latency"))
2509 {
2510 _hasLatencyOutPort = true;
2511 _latencyOutPort = curOutPort;
2512 }
2513 controlsOut[curOutPort].idx = k;
2514 controlsOut[curOutPort].val = 0.0;
2515 controlsOut[curOutPort].tmpVal = 0.0;
2516 controlsOut[curOutPort].enCtrl = false;
2517 // Connect only the first instance's output controls.
2518 // We don't have a mechanism to display the other instances' outputs.
2519 _plugin->connectPort(handle[0], k, &controlsOut[curOutPort].val);
2520 // Connect the rest to dummy ports.
2521 for(int i = 1; i < instances; ++i)
2522 _plugin->connectPort(handle[i], k, &controlsOutDummy[curOutPort].val);
2523 ++curOutPort;
2524 }
2525 }
2526 }
2527
2528 #ifdef _WIN32
2529 _audioInSilenceBuf = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize);
2530 if(_audioInSilenceBuf == NULL)
2531 {
2532 fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioInSilenceBuf _aligned_malloc returned error: NULL. Aborting!\n");
2533 abort();
2534 }
2535 #else
2536 int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize);
2537 if(rv != 0)
2538 {
2539 fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioInSilenceBuf posix_memalign returned error:%d. Aborting!\n", rv);
2540 abort();
2541 }
2542 #endif
2543 if(MusEGlobal::config.useDenormalBias)
2544 {
2545 for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q)
2546 {
2547 _audioInSilenceBuf[q] = MusEGlobal::denormalBias;
2548 }
2549 }
2550 else
2551 {
2552 memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize);
2553 }
2554 #ifdef _WIN32
2555 _audioOutDummyBuf = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize);
2556 if(_audioOutDummyBuf == NULL)
2557 {
2558 fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioOutDummyBuf _aligned_malloc returned error: NULL. Aborting!\n");
2559 abort();
2560 }
2561 #else
2562 rv = posix_memalign((void **)&_audioOutDummyBuf, 16, sizeof(float) * MusEGlobal::segmentSize);
2563 if(rv != 0)
2564 {
2565 fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioOutDummyBuf posix_memalign returned error:%d. Aborting!\n", rv);
2566 abort();
2567 }
2568 #endif
2569 activate();
2570 return false;
2571 }
2572
2573 //---------------------------------------------------------
2574 // connect
2575 //---------------------------------------------------------
2576
connect(unsigned long ports,unsigned long offset,float ** src,float ** dst)2577 void PluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst)
2578 {
2579 unsigned long port = 0;
2580 for (int i = 0; i < instances; ++i) {
2581 for (unsigned long k = 0; k < _plugin->ports(); ++k) {
2582 if (isAudioIn(k)) {
2583 if(port < ports)
2584 _plugin->connectPort(handle[i], k, src[port] + offset);
2585 else
2586 // Connect to an input silence buffer.
2587 _plugin->connectPort(handle[i], k, _audioInSilenceBuf + offset);
2588 ++port;
2589 }
2590 }
2591 }
2592 port = 0;
2593 for (int i = 0; i < instances; ++i) {
2594 for (unsigned long k = 0; k < _plugin->ports(); ++k) {
2595 if (isAudioOut(k)) {
2596 if(port < ports)
2597 _plugin->connectPort(handle[i], k, dst[port] + offset);
2598 else
2599 // Connect to a dummy buffer.
2600 _plugin->connectPort(handle[i], k, _audioOutDummyBuf + offset);
2601 ++port;
2602 }
2603 }
2604 }
2605 }
2606
2607 //---------------------------------------------------------
2608 // deactivate
2609 //---------------------------------------------------------
2610
deactivate()2611 void PluginI::deactivate()
2612 {
2613 for (int i = 0; i < instances; ++i) {
2614 _plugin->deactivate(handle[i]);
2615 _plugin->cleanup(handle[i]);
2616 }
2617 }
2618
2619 //---------------------------------------------------------
2620 // activate
2621 //---------------------------------------------------------
2622
activate()2623 void PluginI::activate()
2624 {
2625 for (int i = 0; i < instances; ++i)
2626 _plugin->activate(handle[i]);
2627 if (initControlValues) {
2628 for (unsigned long i = 0; i < controlPorts; ++i) {
2629 controls[i].val = controls[i].tmpVal;
2630 }
2631 }
2632 else {
2633 // get initial control values from plugin
2634 for (unsigned long i = 0; i < controlPorts; ++i) {
2635 controls[i].tmpVal = controls[i].val;
2636 }
2637 }
2638 }
2639
2640 //---------------------------------------------------------
2641 // latency
2642 //---------------------------------------------------------
2643
latency() const2644 float PluginI::latency() const
2645 {
2646 // Do not report any latency if the plugin is not on.
2647 if(!on())
2648 return 0.0;
2649 if(cquirks()._overrideReportedLatency)
2650 return cquirks()._latencyOverrideValue;
2651 if(!hasLatencyOutPort())
2652 return 0.0;
2653 return controlsOut[latencyOutPortIndex()].val;
2654 }
2655
2656
2657 //---------------------------------------------------------
2658 // setControl
2659 // set plugin instance controller value by name
2660 //---------------------------------------------------------
2661
setControl(const QString & s,double val)2662 bool PluginI::setControl(const QString& s, double val)
2663 {
2664 for (unsigned long i = 0; i < controlPorts; ++i) {
2665 if (_plugin->portName(controls[i].idx) == s) {
2666 setParam(i, val);
2667 return false;
2668 }
2669 }
2670 printf("PluginI:setControl(%s, %f) controller not found\n",
2671 s.toLatin1().constData(), val);
2672 return true;
2673 }
2674
2675 //---------------------------------------------------------
2676 // saveConfiguration
2677 //---------------------------------------------------------
2678
writeConfiguration(int level,Xml & xml)2679 void PluginI::writeConfiguration(int level, Xml& xml)
2680 {
2681 if(_plugin->uri().isEmpty())
2682 {
2683 xml.tag(level++, "plugin file=\"%s\" label=\"%s\" channel=\"%d\"",
2684 Xml::xmlString(_plugin->lib()).toLatin1().constData(),
2685 Xml::xmlString(_plugin->label()).toLatin1().constData(), channel);
2686 }
2687 else
2688 {
2689 xml.tag(level++, "plugin uri=\"%s\" label=\"%s\" channel=\"%d\"",
2690 Xml::xmlString(_plugin->uri()).toLatin1().constData(),
2691 Xml::xmlString(_plugin->label()).toLatin1().constData(), channel);
2692 }
2693
2694 #ifdef LV2_SUPPORT
2695 if(_plugin != nullptr && _plugin->isLV2Plugin())//save lv2 plugin state custom data before controls
2696 {
2697 LV2PluginWrapper *lv2Plug = static_cast<LV2PluginWrapper *>(_plugin);
2698 //for multi-instance plugins write only first instance's state
2699 if(instances > 0)
2700 {
2701 lv2Plug->writeConfiguration(handle [0], level, xml);
2702 }
2703 }
2704 #endif
2705
2706 #ifdef VST_NATIVE_SUPPORT
2707 if(_plugin != nullptr && _plugin->isVstNativePlugin())//save vst plugin state custom data before controls
2708 {
2709 VstNativePluginWrapper *vstPlug = static_cast<VstNativePluginWrapper *>(_plugin);
2710 //for multi-instance plugins write only first instance's state
2711 if(instances > 0)
2712 {
2713 vstPlug->writeConfiguration(handle [0], level, xml);
2714 }
2715 }
2716 #endif
2717 for (unsigned long i = 0; i < controlPorts; ++i) {
2718 unsigned long idx = controls[i].idx;
2719 QString s("control name=\"%1\" val=\"%2\" /");
2720 xml.tag(level, s.arg(Xml::xmlString(_plugin->portName(idx)).toLatin1().constData()).arg(double(controls[i].tmpVal)).toLatin1().constData());
2721 }
2722 if (_on == false)
2723 xml.intTag(level, "on", _on);
2724
2725 _quirks.write(level, xml);
2726
2727 if(guiVisible())
2728 xml.intTag(level, "gui", 1);
2729 int x, y, w, h;
2730 getGeometry(&x, &y, &w, &h);
2731 QRect r(x, y, w, h);
2732 xml.qrectTag(level, "geometry", r);
2733
2734 if (nativeGuiVisible())
2735 xml.intTag(level, "nativegui", 1);
2736 getNativeGeometry(&x, &y, &w, &h);
2737 QRect nr(x, y, w, h);
2738 xml.qrectTag(level, "nativeGeometry", nr);
2739
2740 xml.etag(--level, "plugin");
2741 }
2742
2743 //---------------------------------------------------------
2744 // loadControl
2745 //---------------------------------------------------------
2746
loadControl(Xml & xml)2747 bool PluginI::loadControl(Xml& xml)
2748 {
2749 QString file;
2750 QString label;
2751 QString name("mops");
2752 double val = 0.0;
2753
2754 for (;;) {
2755 Xml::Token token = xml.parse();
2756 const QString& tag = xml.s1();
2757
2758 switch (token) {
2759 case Xml::Error:
2760 case Xml::End:
2761 return true;
2762 case Xml::TagStart:
2763 xml.unknown("PluginI-Control");
2764 break;
2765 case Xml::Attribut:
2766 if (tag == "name")
2767 name = xml.s2();
2768 else if (tag == "val")
2769 val = xml.s2().toDouble();
2770 break;
2771 case Xml::TagEnd:
2772 if (tag == "control") {
2773 if(_plugin)
2774 {
2775 bool found = false;
2776 for(unsigned long i = 0; i < controlPorts; ++i)
2777 {
2778 if(_plugin->portName(controls[i].idx) == name)
2779 {
2780 controls[i].val = controls[i].tmpVal = val;
2781 found = true;
2782 }
2783 }
2784 if(!found)
2785 {
2786 printf("PluginI:loadControl(%s, %f) controller not found\n",
2787 name.toLatin1().constData(), val);
2788 return false;
2789 }
2790 initControlValues = true;
2791 }
2792 return false;
2793 }
2794 return true;
2795 default:
2796 break;
2797 }
2798 }
2799 return true;
2800 }
2801
2802 //---------------------------------------------------------
2803 // readConfiguration
2804 // return true on error
2805 //---------------------------------------------------------
2806
readConfiguration(Xml & xml,bool readPreset)2807 bool PluginI::readConfiguration(Xml& xml, bool readPreset)
2808 {
2809 QString file;
2810 QString label;
2811 QString uri;
2812
2813 //custom params in xml song file , synth tag, that will be passed to new PluginI:setCustomData(Xml &) method
2814 //now only lv2host uses them, others simply ignore
2815 std::vector<QString> accumulatedCustomParams;
2816
2817 if (!readPreset)
2818 channel = 1;
2819
2820 for (;;) {
2821 Xml::Token token(xml.parse());
2822 const QString& tag(xml.s1());
2823 switch (token) {
2824 case Xml::Error:
2825 case Xml::End:
2826 return true;
2827 case Xml::TagStart:
2828 if (!readPreset && _plugin == 0) {
2829 _plugin = MusEGlobal::plugins.find(file, uri, label);
2830
2831 if (_plugin)
2832 {
2833 if(initPluginInstance(_plugin, channel)) {
2834 _plugin = 0;
2835 xml.parse1();
2836 printf("Error initializing plugin instance (%s, %s, %s)\n",
2837 file.toLatin1().constData(),
2838 uri.toLatin1().constData(),
2839 label.toLatin1().constData());
2840 //break; // Don't break - let it read any control tags.
2841 }
2842 }
2843 }
2844 if (tag == "control")
2845 loadControl(xml);
2846 else if (tag == "on") {
2847 bool flag = xml.parseInt();
2848 if (!readPreset)
2849 _on = flag;
2850 }
2851 else if (tag == "quirks") {
2852 PluginQuirks q;
2853 q.read(xml);
2854 if (!readPreset)
2855 _quirks = q;
2856 }
2857 else if (tag == "gui") {
2858 const bool flag = xml.parseInt();
2859 if (_plugin)
2860 showGui(flag);
2861 }
2862 else if (tag == "nativegui") {
2863 // We can't tell OSC to show the native plugin gui
2864 // until the parent track is added to the lists.
2865 // OSC needs to find the plugin in the track lists.
2866 // Use this 'pending' flag so it gets done later.
2867 _showNativeGuiPending = xml.parseInt();
2868 }
2869 else if (tag == "geometry") {
2870 QRect r(readGeometry(xml, tag));
2871 setGeometry(r.x(), r.y(), r.width(), r.height());
2872 }
2873 else if (tag == "nativeGeometry") {
2874 QRect r(readGeometry(xml, tag));
2875 setNativeGeometry(r.x(), r.y(), r.width(), r.height());
2876 }
2877 else if (tag == "customData") { //just place tag contents in accumulatedCustomParams
2878 QString customData = xml.parse1();
2879 if(!customData.isEmpty()){
2880 accumulatedCustomParams.push_back(customData);
2881 //now process custom data immidiatly
2882 //because it MUST be processed before plugin controls
2883 //writeConfiguration places custom data before plugin controls values
2884 setCustomData(accumulatedCustomParams);
2885 accumulatedCustomParams.clear();
2886 }
2887 }
2888 else
2889 xml.unknown("PluginI");
2890 break;
2891 case Xml::Attribut:
2892 if (tag == "file") {
2893 QString s = xml.s2();
2894 if (readPreset) {
2895 if (s != plugin()->lib()) {
2896 printf("Error: Wrong preset type %s. Type must be a %s\n",
2897 s.toLatin1().constData(), plugin()->lib().toLatin1().constData());
2898 return true;
2899 }
2900 }
2901 else {
2902 file = s;
2903 }
2904 }
2905 else if (tag == "uri") {
2906 QString s = xml.s2();
2907 if (readPreset) {
2908 if (s != plugin()->uri()) {
2909 printf("Error: Wrong preset uri %s. Uri must be a %s\n",
2910 s.toLatin1().constData(), plugin()->uri().toLatin1().constData());
2911 return true;
2912 }
2913 }
2914 else {
2915 uri = s;
2916 }
2917 }
2918 else if (tag == "label") {
2919 if (!readPreset)
2920 label = xml.s2();
2921 }
2922 else if (tag == "channel") {
2923 if (!readPreset)
2924 channel = xml.s2().toInt();
2925 }
2926 break;
2927 case Xml::TagEnd:
2928 if (tag == "plugin") {
2929 if (!readPreset && _plugin == 0) {
2930 _plugin = MusEGlobal::plugins.find(file, uri, label);
2931 if (_plugin == 0)
2932 {
2933 QMessageBox::warning(0,"Plugin not found!",
2934 "Plugin: " + label + " not found, if the project is saved it will be removed from the project");
2935 fprintf(stderr, "Warning: - Plugin not found (%s, %s, %s)\n",
2936 file.toLatin1().constData(),
2937 uri.toLatin1().constData(),
2938 label.toLatin1().constData());
2939 return true;
2940 }
2941
2942 if (initPluginInstance(_plugin, channel))
2943 {
2944 fprintf(stderr, "Error initializing plugin instance (%s, %s, %s)\n",
2945 file.toLatin1().constData(),
2946 uri.toLatin1().constData(),
2947 label.toLatin1().constData());
2948 return true;
2949 }
2950 }
2951 if (_gui)
2952 _gui->updateValues();
2953 return false;
2954 }
2955 return true;
2956 default:
2957 break;
2958 }
2959 }
2960 return true;
2961 }
2962
2963 //---------------------------------------------------------
2964 // showGui
2965 //---------------------------------------------------------
2966
showGui()2967 void PluginI::showGui()
2968 {
2969 if(_plugin)
2970 PluginIBase::showGui();
2971 }
2972
showGui(bool flag)2973 void PluginI::showGui(bool flag)
2974 {
2975 if(_plugin)
2976 PluginIBase::showGui(flag);
2977 }
2978
2979 //---------------------------------------------------------
2980 // showNativeGui
2981 //---------------------------------------------------------
2982
showNativeGui()2983 void PluginI::showNativeGui()
2984 {
2985
2986 #ifdef LV2_SUPPORT
2987 if(plugin() && plugin()->isLV2Plugin())
2988 {
2989 if(((LV2PluginWrapper *)plugin())->nativeGuiVisible(this))
2990 ((LV2PluginWrapper *)plugin())->showNativeGui(this, false);
2991 else
2992 ((LV2PluginWrapper *)plugin())->showNativeGui(this, true);
2993 return;
2994 }
2995 #endif
2996
2997 #ifdef VST_NATIVE_SUPPORT
2998 if(plugin() && plugin()->isVstNativePlugin())
2999 {
3000 if(((VstNativePluginWrapper *)plugin())->nativeGuiVisible(this))
3001 ((VstNativePluginWrapper *)plugin())->showNativeGui(this, false);
3002 else
3003 ((VstNativePluginWrapper *)plugin())->showNativeGui(this, true);
3004 return;
3005 }
3006 #endif
3007 #ifdef OSC_SUPPORT
3008 if (_plugin)
3009 {
3010 if (_oscif.oscGuiVisible())
3011 _oscif.oscShowGui(false);
3012 else
3013 _oscif.oscShowGui(true);
3014 }
3015 #endif
3016 _showNativeGuiPending = false;
3017 }
3018
showNativeGui(bool flag)3019 void PluginI::showNativeGui(
3020 bool
3021 #if defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT)
3022 flag
3023 #endif
3024 )
3025 {
3026 #ifdef LV2_SUPPORT
3027 if(plugin() && plugin()->isLV2Plugin())
3028 {
3029 ((LV2PluginWrapper *)plugin())->showNativeGui(this, flag);
3030 return;
3031 }
3032 #endif
3033
3034 #ifdef VST_NATIVE_SUPPORT
3035 if(plugin() && plugin()->isVstNativePlugin())
3036 {
3037 ((VstNativePluginWrapper *)plugin())->showNativeGui(this, flag);
3038 return;
3039 }
3040 #endif
3041 #ifdef OSC_SUPPORT
3042 if(_plugin)
3043 {
3044 _oscif.oscShowGui(flag);
3045 }
3046 #endif
3047 _showNativeGuiPending = false;
3048 }
3049
3050 //---------------------------------------------------------
3051 // nativeGuiVisible
3052 //---------------------------------------------------------
3053
nativeGuiVisible() const3054 bool PluginI::nativeGuiVisible() const
3055 {
3056 #ifdef LV2_SUPPORT
3057 if(plugin() && plugin()->isLV2Plugin())
3058 return ((LV2PluginWrapper *)plugin())->nativeGuiVisible(this);
3059 #endif
3060 #ifdef VST_NATIVE_SUPPORT
3061 if(plugin() && plugin()->isVstNativePlugin())
3062 return ((VstNativePluginWrapper *)plugin())->nativeGuiVisible(this);
3063 #endif
3064 #ifdef OSC_SUPPORT
3065 return _oscif.oscGuiVisible();
3066 #endif
3067
3068 return false;
3069 }
3070
3071 //---------------------------------------------------------
3072 // makeGui
3073 //---------------------------------------------------------
3074
makeGui()3075 void PluginIBase::makeGui()
3076 {
3077 _gui = new MusEGui::PluginGui(this);
3078 }
3079
3080 //---------------------------------------------------------
3081 // deleteGui
3082 //---------------------------------------------------------
deleteGui()3083 void PluginIBase::deleteGui()
3084 {
3085 if(_gui)
3086 {
3087 delete _gui;
3088 _gui = 0;
3089 }
3090 }
3091
3092 //---------------------------------------------------------
3093 // enableAllControllers
3094 //---------------------------------------------------------
3095
enableAllControllers(bool v)3096 void PluginI::enableAllControllers(bool v)
3097 {
3098 for(unsigned long i = 0; i < controlPorts; ++i)
3099 controls[i].enCtrl = v;
3100 }
3101
3102 //---------------------------------------------------------
3103 // titlePrefix
3104 //---------------------------------------------------------
3105
titlePrefix() const3106 QString PluginI::titlePrefix() const
3107 {
3108 if (_track)
3109 return _track->name() + QString(": ");
3110 else return ":";
3111 }
3112
3113 //---------------------------------------------------------
3114 // apply
3115 // If ports is 0, just process controllers only, not audio (do not 'run').
3116 //---------------------------------------------------------
3117
apply(unsigned pos,unsigned long n,unsigned long ports,float ** bufIn,float ** bufOut,float latency_corr_offset)3118 void PluginI::apply(unsigned pos, unsigned long n,
3119 unsigned long ports, float** bufIn, float** bufOut, float latency_corr_offset)
3120 {
3121 const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame();
3122 unsigned long sample = 0;
3123
3124 // FIXME Better support for PluginPowerOf2BlockSize, by quantizing the control period times.
3125 // For now we treat it like fixed size.
3126 const bool usefixedrate = (requiredFeatures() & (PluginFixedBlockSize | PluginPowerOf2BlockSize | PluginCoarseBlockSize));
3127
3128 // Note for dssi-vst this MUST equal audio period. It doesn't like broken-up runs (it stutters),
3129 // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length.
3130 // For now, the fixed size is clamped to the audio buffer size.
3131 // TODO: We could later add slower processing over several cycles -
3132 // so that users can select a small audio period but a larger control period.
3133 const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > n) ? n : MusEGlobal::config.minControlProcessPeriod;
3134 const unsigned long min_per_mask = min_per-1; // min_per must be power of 2
3135
3136 AutomationType at = AUTO_OFF;
3137 CtrlListList* cll = nullptr;
3138 ciCtrlList icl_first;
3139 if(_track)
3140 {
3141 // Correction value is negative for correction.
3142 latency_corr_offset += _track->getLatencyInfo(false)._sourceCorrectionValue;
3143
3144 at = _track->automationType();
3145 cll = _track->controller();
3146 if(_id != -1 && ports != 0) // Don't bother if not 'running'.
3147 icl_first = cll->lower_bound(genACnum(_id, 0));
3148 }
3149 const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF;
3150 const unsigned long in_ctrls = _plugin->controlInPorts();
3151
3152 // Special for plugins: Deal with tmpVal. TODO: Get rid of tmpVal, maybe by using the FIFO...
3153 for(unsigned long k = 0; k < controlPorts; ++k)
3154 controls[k].val = controls[k].tmpVal;
3155
3156 int cur_slice = 0;
3157 while(sample < n)
3158 {
3159 unsigned long nsamp = n - sample;
3160 const unsigned long slice_frame = pos + sample;
3161
3162 // Process automation control values, while also determining the maximum acceptable
3163 // size of this run. Further processing, from FIFOs for example, can lower the size
3164 // from there, but this section determines where the next highest maximum frame
3165 // absolutely needs to be for smooth playback of the controller value stream...
3166 //
3167 if(ports != 0) // Don't bother if not 'running'.
3168 {
3169 ciCtrlList icl = icl_first;
3170 for(unsigned long k = 0; k < controlPorts; ++k)
3171 {
3172 CtrlList* cl = (cll && _id != -1 && icl != cll->end()) ? icl->second : nullptr;
3173 CtrlInterpolate& ci = controls[k].interp;
3174 // Always refresh the interpolate struct at first, since things may have changed.
3175 // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct.
3176 if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() &&
3177 (slice_frame < (unsigned long)ci.sFrame || (ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame)) ) )
3178 {
3179 if(cl && _id != -1 && (unsigned long)cl->id() == genACnum(_id, k))
3180 {
3181 cl->getInterpolation(slice_frame, no_auto || !controls[k].enCtrl, &ci);
3182 if(icl != cll->end())
3183 ++icl;
3184 }
3185 else
3186 {
3187 // No matching controller, or end. Just copy the current value into the interpolator.
3188 // Keep the current icl iterator, because since they are sorted by frames,
3189 // if the IDs didn't match it means we can just let k catch up with icl.
3190 ci.sFrame = 0;
3191 ci.eFrame = 0;
3192 ci.eFrameValid = false;
3193 ci.sVal = controls[k].val;
3194 ci.eVal = ci.sVal;
3195 ci.doInterp = false;
3196 ci.eStop = false;
3197 }
3198 }
3199 else
3200 {
3201 if(ci.eStop && ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right.
3202 {
3203 // Clear the stop condition and set up the interp struct appropriately as an endless value.
3204 ci.sFrame = 0; //ci->eFrame;
3205 ci.eFrame = 0;
3206 ci.eFrameValid = false;
3207 ci.sVal = ci.eVal;
3208 ci.doInterp = false;
3209 ci.eStop = false;
3210 }
3211 if(cl && cll && icl != cll->end())
3212 ++icl;
3213 }
3214
3215 if(!usefixedrate && MusEGlobal::audio->isPlaying())
3216 {
3217 unsigned long samps = nsamp;
3218 if(ci.eFrameValid)
3219 samps = (unsigned long)ci.eFrame - slice_frame;
3220
3221 if(!ci.doInterp && samps > min_per)
3222 {
3223 samps &= ~min_per_mask;
3224 if((samps & min_per_mask) != 0)
3225 samps += min_per;
3226 }
3227 else
3228 samps = min_per;
3229
3230 if(samps < nsamp)
3231 nsamp = samps;
3232
3233 }
3234
3235 if(ci.doInterp && cl)
3236 controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci);
3237 else
3238 controls[k].val = ci.sVal;
3239
3240 #ifdef LV2_SUPPORT
3241 if(_plugin->isLV2Plugin())
3242 {
3243 for(int i = 0; i < instances; ++i)
3244 {
3245 (reinterpret_cast<LV2PluginWrapper *>(_plugin))->setLastStateControls(handle [i], k, true, false, true, 0.0f);
3246 }
3247 }
3248 #endif
3249
3250 controls[k].tmpVal = controls[k].val; // Special for plugins: Deal with tmpVal.
3251
3252 #ifdef PLUGIN_DEBUGIN_PROCESS
3253 printf("PluginI::apply k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp);
3254 #endif
3255 }
3256 }
3257
3258 #ifdef PLUGIN_DEBUGIN_PROCESS
3259 printf("PluginI::apply sample:%lu nsamp:%lu\n", sample, nsamp);
3260 #endif
3261
3262 //
3263 // Process all control ring buffer items valid for this time period...
3264 //
3265 bool found = false;
3266 unsigned long frame = 0;
3267 unsigned long index = 0;
3268 unsigned long evframe;
3269 // Get all control ring buffer items valid for this time period...
3270 while(!_controlFifo.isEmpty())
3271 {
3272 const ControlEvent& v = _controlFifo.peek();
3273 // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio.
3274 // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame.
3275 evframe = (syncFrame > v.frame + n) ? 0 : v.frame - syncFrame + n;
3276
3277 #ifdef PLUGIN_DEBUGIN_PROCESS
3278 fprintf(stderr, "PluginI::apply found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n",
3279 found, evframe, frame, v.frame, v.idx, v.value, v.unique);
3280 #endif
3281
3282 // Protection. Observed this condition. Why? Supposed to be linear timestamps.
3283 if(found && evframe < frame)
3284 {
3285 fprintf(stderr,
3286 "PluginI::apply *** Error: Event out of order: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d syncFrame:%lu nframes:%lu v.frame:%lu\n",
3287 evframe, frame, v.idx, v.value, v.unique, syncFrame, n, v.frame);
3288
3289 // No choice but to ignore it.
3290 _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
3291 continue;
3292 }
3293
3294 if(evframe >= n // Next events are for a later period.
3295 || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.)
3296 || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close.
3297 || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all.
3298 break;
3299 // _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
3300
3301 if(v.idx >= in_ctrls) // Sanity check
3302 {
3303 _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
3304 break;
3305 }
3306
3307 found = true;
3308 frame = evframe;
3309 index = v.idx;
3310
3311 if(ports == 0) // Don't bother if not 'running'.
3312 controls[v.idx].val = controls[v.idx].tmpVal = v.value; // Might as well at least update these.
3313 else
3314 {
3315 CtrlInterpolate* ci = &controls[v.idx].interp;
3316 // Tell it to stop the current ramp at this frame, when it does stop, set this value:
3317 ci->eFrame = frame;
3318 ci->eFrameValid = true;
3319 ci->eVal = v.value;
3320 ci->eStop = true;
3321 }
3322
3323 // Need to update the automation value, otherwise it overwrites later with the last automation value.
3324 if(_track && _id != -1)
3325 _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value);
3326
3327 #ifdef LV2_SUPPORT
3328 if(v.fromGui)
3329 {
3330 if(_plugin->isLV2Plugin())
3331 {
3332 for(int i = 0; i < instances; ++i)
3333 {
3334 (reinterpret_cast<LV2PluginWrapper *>(_plugin))->setLastStateControls(handle [i], v.idx, true, true, false, v.value);
3335 }
3336 }
3337 }
3338 #endif
3339
3340 _controlFifo.remove(); // Done with the ring buffer's item. Remove it.
3341 }
3342
3343 if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream.
3344 nsamp = frame - sample;
3345
3346 if(sample + nsamp > n) // Safety check.
3347 nsamp = n - sample;
3348
3349 // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead.
3350 // Note this means it is still possible to get stuck in the top loop (at least for a while).
3351 if(nsamp != 0)
3352 {
3353 if(ports != 0) // Don't bother if not 'running'.
3354 {
3355 connect(ports, sample, bufIn, bufOut);
3356
3357 for(int i = 0; i < instances; ++i)
3358 _plugin->apply(handle[i], nsamp, latency_corr_offset);
3359 }
3360
3361 sample += nsamp;
3362 }
3363
3364 ++cur_slice; // Slice is done. Moving on to any next slice now...
3365 }
3366 }
3367
3368 //---------------------------------------------------------
3369 // oscConfigure
3370 //---------------------------------------------------------
3371
3372 #ifdef OSC_SUPPORT
oscConfigure(LADSPA_Handle handle,const char * key,const char * value)3373 int Plugin::oscConfigure(
3374 LADSPA_Handle
3375 #if defined(DSSI_SUPPORT)
3376 handle
3377 #endif
3378 ,
3379 const char*
3380 #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN)
3381 key
3382 #endif
3383 ,
3384 const char*
3385 #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN)
3386 value
3387 #endif
3388 )
3389 {
3390 #ifdef PLUGIN_DEBUGIN
3391 printf("Plugin::oscConfigure effect plugin label:%s key:%s value:%s\n", plugin->Label, key, value);
3392 #endif
3393
3394 #ifdef DSSI_SUPPORT
3395 if(!dssi_descr || !dssi_descr->configure)
3396 return 0;
3397
3398 if (!strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX,
3399 strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) {
3400 fprintf(stderr, "Plugin::oscConfigure OSC: UI for plugin '%s' attempted to use reserved configure key \"%s\", ignoring\n",
3401 plugin->Label, key);
3402
3403 return 0;
3404 }
3405
3406 char* message = dssi_descr->configure(handle, key, value);
3407 if (message) {
3408 printf("Plugin::oscConfigure on configure '%s' '%s', plugin '%s' returned error '%s'\n",
3409 key, value, plugin->Label, message);
3410
3411 free(message);
3412 }
3413
3414 // also call back on UIs for plugins other than the one
3415 // that requested this:
3416 // if (n != instance->number && instances[n].uiTarget) {
3417 // lo_send(instances[n].uiTarget,
3418 // instances[n].ui_osc_configure_path, "ss", key, value);
3419 // }
3420
3421 #endif // DSSI_SUPPORT
3422
3423 return 0;
3424 }
3425
3426 //---------------------------------------------------------
3427 // oscConfigure
3428 //---------------------------------------------------------
3429
oscConfigure(const char * key,const char * value)3430 int PluginI::oscConfigure(
3431 const char *
3432 #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN)
3433 key
3434 #endif
3435 ,
3436 const char *
3437 #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN)
3438 value
3439 #endif
3440 )
3441 {
3442 if(!_plugin)
3443 return 0;
3444
3445 // "The host has the option to remember the set of (key,value)
3446 // pairs associated with a particular instance, so that if it
3447 // wants to restore the "same" instance on another occasion it can
3448 // just call configure() on it for each of those pairs and so
3449 // restore state without any input from a GUI. Any real-world GUI
3450 // host will probably want to do that."
3451
3452 #ifdef PLUGIN_DEBUGIN
3453 printf("PluginI::oscConfigure effect plugin name:%s label:%s key:%s value:%s\n", _name.toLatin1().constData(), _label.toLatin1().constData(), key, value);
3454 #endif
3455
3456 #ifdef DSSI_SUPPORT
3457 // FIXME: Don't think this is right, should probably do as example shows below.
3458 for(int i = 0; i < instances; ++i)
3459 _plugin->oscConfigure(handle[i], key, value);
3460 #endif // DSSI_SUPPORT
3461
3462 return 0;
3463 }
3464
3465 //---------------------------------------------------------
3466 // oscUpdate
3467 //---------------------------------------------------------
3468
oscUpdate()3469 int PluginI::oscUpdate()
3470 {
3471 #ifdef DSSI_SUPPORT
3472 // Send project directory.
3473 _oscif.oscSendConfigure(DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::museProject.toLatin1().constData()); // MusEGlobal::song->projectPath()
3474
3475 /* DELETETHIS 20
3476 // Send current string configuration parameters.
3477 StringParamMap& map = synti->stringParameters();
3478 int i = 0;
3479 for(ciStringParamMap r = map.begin(); r != map.end(); ++r)
3480 {
3481 _oscIF.oscSendConfigure(r->first.c_str(), r->second.c_str());
3482 // Avoid overloading the GUI if there are lots and lots of params.
3483 if((i+1) % 50 == 0)
3484 usleep(300000);
3485 ++i;
3486 }
3487
3488 // Send current bank and program.
3489 unsigned long bank, prog;
3490 synti->currentProg(&prog, &bank, 0);
3491 _oscIF.oscSendProgram(prog, bank, true); // "true" means "force"
3492 */
3493
3494 // FIXME: TESTING FLAM: I have to put a delay because flammer hasn't opened yet.
3495 // How to make sure gui is ready?
3496 usleep(300000);
3497
3498 // Send current control values.
3499 for(unsigned long i = 0; i < controlPorts; ++i)
3500 {
3501 _oscif.oscSendControl(controls[i].idx, controls[i].val, true /*force*/);
3502 // Avoid overloading the GUI if there are lots and lots of ports.
3503 if((i+1) % 50 == 0)
3504 usleep(300000);
3505 }
3506 #endif // DSSI_SUPPORT
3507
3508 return 0;
3509 }
3510
3511 //---------------------------------------------------------
3512 // oscControl
3513 //---------------------------------------------------------
3514
oscControl(unsigned long port,float value)3515 int PluginI::oscControl(unsigned long port, float value)
3516 {
3517 #ifdef PLUGIN_DEBUGIN
3518 printf("PluginI::oscControl received oscControl port:%lu val:%f\n", port, value);
3519 #endif
3520
3521 if(port >= _plugin->rpIdx.size())
3522 {
3523 fprintf(stderr, "PluginI::oscControl: port number:%lu is out of range of index list size:%zd\n", port, _plugin->rpIdx.size());
3524 return 0;
3525 }
3526
3527 // Convert from DSSI port number to control input port index.
3528 unsigned long cport = _plugin->rpIdx[port];
3529
3530 if((int)cport == -1)
3531 {
3532 fprintf(stderr, "PluginI::oscControl: port number:%lu is not a control input\n", port);
3533 return 0;
3534 }
3535
3536 // Record automation:
3537 // Take care of this immediately, because we don't want the silly delay associated with
3538 // processing the fifo one-at-a-time in the apply().
3539 // NOTE: With some vsts we don't receive control events until the user RELEASES a control.
3540 // So the events all arrive at once when the user releases a control.
3541 // That makes this pretty useless... But what the heck...
3542 if(_track && _id != -1)
3543 {
3544 unsigned long id = genACnum(_id, cport);
3545 _track->recordAutomation(id, value);
3546 }
3547
3548 // (From DSSI module).
3549 // p3.3.39 Set the DSSI control input port's value.
3550 // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all
3551 // ie. they don't update the DSSI control port values themselves.
3552 // Hence in response to the call to this oscControl, sent by the native GUI, it is required to do that here.
3553 /// controls[cport].val = value;
3554 // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls.
3555 // AND this function is called !
3556 // Despite the descrepancy we are STILL required to update the DSSI control port values here
3557 // because dssi-vst is WAITING FOR A RESPONSE! (A CHANGE in the control port value).
3558 // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES
3559 // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes.
3560 // TODO: May need FIFOs on each control(!) so that the control changes get sent one per process cycle!
3561 // Observed countdown not actually going to zero upon string of changes.
3562 // Try this ...
3563
3564 // Schedules a timed control change:
3565 ControlEvent ce;
3566 ce.unique = _plugin->_isDssiVst; // Special for messages from vst gui to host - requires processing every message.
3567 ce.fromGui = true; // It came from the plugin's own GUI.
3568 ce.idx = cport;
3569 ce.value = value;
3570 // Don't use timestamp(), because it's circular, which is making it impossible to deal
3571 // with 'modulo' events which slip in 'under the wire' before processing the ring buffers.
3572 ce.frame = MusEGlobal::audio->curFrame();
3573 if(_controlFifo.put(ce))
3574 fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%lu\n", cport);
3575
3576 enableController(cport, false); //TODO maybe re-enable the ctrl soon?
3577
3578 /* DELETETHIS 12
3579 const DSSI_Descriptor* dssi = synth->dssi;
3580 const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin;
3581
3582 ciMidiCtl2LadspaPort ip = synth->port2MidiCtlMap.find(cport);
3583 if(ip != synth->port2MidiCtlMap.end())
3584 {
3585 // TODO: TODO: Update midi MusE's midi controller knobs, sliders, boxes etc with a call to the midi port's setHwCtrlState() etc.
3586 // But first we need a ladspa2MidiValue() function! ...
3587 //
3588 //
3589 //float val = ladspa2MidiValue(ld, i, ?, ?);
3590
3591 }
3592 */
3593
3594 return 0;
3595 }
3596
3597 #endif // OSC_SUPPORT
3598
3599 } // namespace MusECore
3600
3601 namespace MusEGui {
3602
3603 // TODO: We need to use .qrc files to use icons in WhatsThis bubbles. See Qt
3604 // Resource System in Qt documentation - ORCAN
3605 //const char* presetOpenText = "<img source=\"fileopen\"> "
3606 // "Click this button to load a saved <em>preset</em>.";
3607 static const char* presetOpenText = "Click this button to load a saved <em>preset</em>.";
3608 static const char* presetSaveText = "Click this button to save curent parameter "
3609 "settings as a <em>preset</em>. You will be prompted for a file name.";
3610 static const char* presetBypassText = "Click this button to bypass effect unit";
3611
3612 //---------------------------------------------------------
3613 // PluginGui
3614 //---------------------------------------------------------
3615
PluginGui(MusECore::PluginIBase * p)3616 PluginGui::PluginGui(MusECore::PluginIBase* p)
3617 : QMainWindow(nullptr)
3618 {
3619 gw = nullptr;
3620 params = nullptr;
3621 paramsOut = nullptr;
3622 plugin = p;
3623 updateWindowTitle();
3624
3625 QToolBar* tools = addToolBar(tr("File Buttons"));
3626 tools->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize));
3627
3628 QAction* fileOpen = new QAction(*fileopenSVGIcon, tr("Load Preset"), this);
3629 connect(fileOpen, &QAction::triggered, [this]() { load(); } );
3630 tools->addAction(fileOpen);
3631
3632 QAction* fileSave = new QAction(*filesaveasSVGIcon, tr("Save Preset"), this);
3633 connect(fileSave, &QAction::triggered, [this]() { save(); } );
3634 tools->addAction(fileSave);
3635
3636 QAction* whatsthis = QWhatsThis::createAction(this);
3637 whatsthis->setIcon(*whatsthisSVGIcon);
3638 tools->addAction(whatsthis);
3639
3640 tools->addSeparator();
3641
3642 onOff = new QAction(*muteSVGIcon, tr("Bypass plugin"), this);
3643 onOff->setCheckable(true);
3644 onOff->setChecked(!plugin->on());
3645 onOff->setEnabled(plugin->hasBypass());
3646 onOff->setToolTip(tr("Bypass plugin"));
3647 connect(onOff, &QAction::toggled, [this](bool v) { bypassToggled(v); } );
3648 tools->addAction(onOff);
3649
3650 QAction* settings = new QAction(*settingsSVGIcon, tr("Plugin settings"), this);
3651 connect(settings, &QAction::triggered, this, &PluginGui::showSettings);
3652 tools->addAction(settings);
3653
3654 fileOpen->setWhatsThis(tr(presetOpenText));
3655 onOff->setWhatsThis(tr(presetBypassText));
3656 fileSave->setWhatsThis(tr(presetSaveText));
3657
3658 QString id;
3659 id.setNum(plugin->pluginID());
3660 QString name(MusEGlobal::museGlobalShare + QString("/plugins/") + id + QString(".ui"));
3661 QFile uifile(name);
3662 if (uifile.exists())
3663 constructGUIFromFile(uifile);
3664 else
3665 constructGUIFromPluginMetadata();
3666
3667 connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat()));
3668 }
3669
3670 //---------------------------------------------------------
3671 // PluginGui
3672 //---------------------------------------------------------
3673
~PluginGui()3674 PluginGui::~PluginGui()
3675 {
3676 if (gw)
3677 delete[] gw;
3678 if (params)
3679 delete[] params;
3680 if (paramsOut)
3681 delete[] paramsOut;
3682 }
3683
3684 //---------------------------------------------------------
3685 // construct GUI from *.ui file
3686 //---------------------------------------------------------
3687
constructGUIFromFile(QFile & uifile)3688 void PluginGui::constructGUIFromFile(QFile& uifile) {
3689
3690 PluginLoader loader;
3691 QFile file(uifile.fileName());
3692 file.open(QFile::ReadOnly);
3693 mw = loader.load(&file, this);
3694 file.close();
3695 setCentralWidget(mw);
3696
3697 QObjectList l = mw->children();
3698 QObject *obj;
3699
3700 nobj = 0;
3701 QList<QObject*>::iterator it;
3702 for (it = l.begin(); it != l.end(); ++it) {
3703 obj = *it;
3704 QByteArray ba = obj->objectName().toLatin1();
3705 const char* name = ba.constData();
3706 if (*name !='P')
3707 continue;
3708 unsigned long parameter;
3709 int rv = sscanf(name, "P%lu", ¶meter);
3710 if(rv != 1)
3711 continue;
3712 ++nobj;
3713 }
3714 it = l.begin();
3715 gw = new GuiWidgets[nobj];
3716 nobj = 0;
3717
3718 // FIXME: There's no unsigned for gui params. We would need to limit nobj to MAXINT.
3719 // FIXME: Our MusEGui::Slider class uses doubles for values, giving some problems with float conversion.
3720
3721 DoubleLabel* dl_obj;
3722 QCheckBox* cb_obj;
3723 QComboBox* combobox_obj;
3724 unsigned long int nn;
3725
3726 for (it = l.begin(); it != l.end(); ++it) {
3727 obj = *it;
3728 QByteArray ba = obj->objectName().toLatin1();
3729 const char* name = ba.constData();
3730 if (*name !='P')
3731 continue;
3732 unsigned long parameter;
3733 int rv = sscanf(name, "P%lu", ¶meter);
3734 if(rv != 1)
3735 continue;
3736
3737 // For some reason lambdas need this local copy (nn) of nobj otherwise they fail and crash.
3738 nn = nobj;
3739
3740 gw[nobj].widget = (QWidget*)obj;
3741 gw[nobj].param = parameter;
3742 gw[nobj].type = -1;
3743 gw[nobj].pressed = false;
3744
3745 if (strcmp(obj->metaObject()->className(), "MusEGui::Slider") == 0) {
3746 gw[nobj].type = GuiWidgets::SLIDER;
3747 Slider* s = static_cast<Slider*>(obj);
3748 s->setId(nobj);
3749 s->setCursorHoming(true);
3750
3751 LADSPA_PortRangeHint range = plugin->range(parameter);
3752 double lower = 0.0; // default values
3753 double upper = 1.0;
3754 double dlower = lower;
3755 double dupper = upper;
3756 double val = plugin->param(parameter);
3757 double dval = val;
3758 getPluginConvertedValues(range, lower, upper, dlower, dupper, dval);
3759
3760 // TODO
3761 //s->setThumbLength(1);
3762 //s->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep);
3763 //s->setScaleMaxMinor(5);
3764 //s->setScale(MusEGlobal::config.minSlider-0.1, 10.0, 6.0, false);
3765 //s->setScale(dlower, dupper, 1.0, false);
3766 //s->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character.
3767 //s->setScaleBackBone(false);
3768 //s->setFillThumb(false);
3769
3770 QFont fnt;
3771 fnt.setFamily("Sans");
3772 fnt.setPixelSize(9);
3773 //fnt.setStyleStrategy(QFont::PreferBitmap);
3774 fnt.setStyleStrategy(QFont::NoAntialias);
3775 fnt.setHintingPreference(QFont::PreferVerticalHinting);
3776 s->setFont(fnt);
3777 s->setStyleSheet(MusECore::font2StyleSheetFull(fnt));
3778 s->setSizeHint(200, 8);
3779
3780 for(unsigned long i = 0; i < nobj; i++)
3781 {
3782 if(gw[i].type == GuiWidgets::DOUBLE_LABEL && gw[i].param == parameter)
3783 ((DoubleLabel*)gw[i].widget)->setSlider(s);
3784 }
3785 connect(s, QOverload<double, int, int>::of(&Slider::valueChanged), [=]() { guiParamChanged(nn); } );
3786 connect(s, &Slider::sliderPressed, [this](double v, int i) { guiSliderPressed(v, i); } );
3787 connect(s, &Slider::sliderReleased, [this](double v, int i) { guiSliderReleased(v, i); } );
3788 connect(s, &Slider::sliderRightClicked, [this](const QPoint &p, int i) { guiSliderRightClicked(p, i); } );
3789 }
3790 else if (strcmp(obj->metaObject()->className(), "MusEGui::DoubleLabel") == 0) {
3791 gw[nobj].type = GuiWidgets::DOUBLE_LABEL;
3792 dl_obj = static_cast<DoubleLabel*>(obj);
3793 dl_obj->setId(nobj);
3794 dl_obj->setAlignment(Qt::AlignCenter);
3795 for(unsigned long i = 0; i < nobj; i++)
3796 {
3797 if(gw[i].type == GuiWidgets::SLIDER && gw[i].param == parameter)
3798 {
3799 dl_obj->setSlider((Slider*)gw[i].widget);
3800 break;
3801 }
3802 }
3803 connect((DoubleLabel*)obj, &DoubleLabel::valueChanged, [this, nn]() { guiParamChanged(nn); } );
3804 }
3805 else if (strcmp(obj->metaObject()->className(), "QCheckBox") == 0) {
3806 gw[nobj].type = GuiWidgets::QCHECKBOX;
3807 gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu);
3808 cb_obj = static_cast<QCheckBox*>(obj);
3809 connect(cb_obj, &QCheckBox::toggled, [this, nn]() { guiParamChanged(nn); } );
3810 connect(cb_obj, &QCheckBox::pressed, [this, nn]() { guiParamPressed(nn); } );
3811 connect(cb_obj, &QCheckBox::released, [this, nn]() { guiParamReleased(nn); } );
3812 connect(cb_obj, &QCheckBox::customContextMenuRequested, [this, nn]() { guiContextMenuReq(nn); } );
3813 }
3814 else if (strcmp(obj->metaObject()->className(), "QComboBox") == 0) {
3815 gw[nobj].type = GuiWidgets::QCOMBOBOX;
3816 gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu);
3817 combobox_obj = static_cast<QComboBox*>(obj);
3818 connect(combobox_obj, QOverload<int>::of(&QComboBox::activated), [=]() { guiParamChanged(nn); } );
3819 connect(combobox_obj, &QComboBox::customContextMenuRequested, [this, nn]() { guiContextMenuReq(nn); } );
3820 }
3821 else {
3822 printf("unknown widget class %s\n", obj->metaObject()->className());
3823 continue;
3824 }
3825 ++nobj;
3826 }
3827 updateValues(); // otherwise the GUI won't have valid data
3828 }
3829
3830 //---------------------------------------------------------
3831 // construct GUI from plugin's meta data
3832 //---------------------------------------------------------
constructGUIFromPluginMetadata()3833 void PluginGui::constructGUIFromPluginMetadata() {
3834
3835 view = new QScrollArea;
3836 view->setWidgetResizable(true);
3837 setCentralWidget(view);
3838
3839 mw = new QWidget(view);
3840 view->setWidget(mw);
3841
3842 QVBoxLayout* vbox = new QVBoxLayout(mw);
3843 vbox->setSizeConstraint(QLayout::SetMinAndMaxSize);
3844
3845 QGroupBox* groupBox = nullptr;
3846 QGridLayout* grid = nullptr;
3847
3848
3849 // input ports
3850 unsigned long paramCnt = plugin->parameters();
3851 params = new GuiParam[paramCnt];
3852
3853 QFontMetrics fm = fontMetrics();
3854 int h = fm.height() + 4;
3855
3856 Slider* sl_obj;
3857 CheckBox* cb_obj;
3858 ComboBoxPI* cmb_obj;
3859
3860 QString lastGroup;
3861
3862 for (unsigned long i = 0; i < paramCnt; ++i) {
3863
3864 if (!i || plugin->portGroup(i) != lastGroup) {
3865 if (plugin->portGroup(i).isEmpty()) {
3866 grid = new QGridLayout();
3867 grid->setColumnMinimumWidth(0, 100);
3868 vbox->addLayout(grid);
3869 } else {
3870 groupBox = new QGroupBox(plugin->portGroup(i));
3871 grid = new QGridLayout(groupBox);
3872 grid->setColumnMinimumWidth(0, 100);
3873 groupBox->setLayout(grid);
3874 vbox->addWidget(groupBox);
3875 }
3876 lastGroup = plugin->portGroup(i);
3877 }
3878
3879 QLabel* label = nullptr;
3880 LADSPA_PortRangeHint range = plugin->range(i);
3881 double lower = 0.0; // default values
3882 double upper = 1.0;
3883 double dlower = lower;
3884 double dupper = upper;
3885 double val = plugin->param(i);
3886 double dval = val;
3887 params[i].pressed = false;
3888 params[i].hint = range.HintDescriptor;
3889 params[i].label = nullptr;
3890
3891 getPluginConvertedValues(range, lower, upper, dlower, dupper, dval);
3892
3893 if (LADSPA_IS_HINT_TOGGLED(range.HintDescriptor)
3894 || plugin->ctrlValueType(i) == MusECore::CtrlValueType::VAL_BOOL) {
3895
3896 // TODO: There should be special handling for triggers (=button),
3897 // but I can't find any plugin with trigger property (kybos)
3898 // if (plugin->ctrlIsTrigger(i))...
3899
3900 params[i].type = GuiParam::GUI_SWITCH;
3901 label = new QLabel(QString(plugin->paramName(i)), nullptr);
3902 CheckBox* cb = new CheckBox(mw, i, "param");
3903 cb->setId(i);
3904 cb->setChecked(plugin->param(i) != 0.0);
3905 cb->setFixedHeight(h);
3906 params[i].actuator = cb;
3907
3908 grid->addWidget(label, i, 0);
3909 grid->addWidget(params[i].actuator, i, 1);
3910
3911 cb_obj = (CheckBox*)params[i].actuator;
3912
3913 connect(cb_obj, &CheckBox::checkboxPressed, [this](int i) { switchPressed(i); } );
3914 connect(cb_obj, &CheckBox::checkboxReleased, [this](int i) { switchReleased(i); } );
3915 connect(cb_obj, &CheckBox::checkboxRightClicked, [this](const QPoint &p, int i) { ctrlRightClicked(p, i); } );
3916 }
3917 else if (plugin->ctrlValueType(i) == MusECore::CtrlValueType::VAL_ENUM
3918 && plugin->ctrlEnumValues(i)) {
3919 label = new QLabel(QString(plugin->paramName(i)), nullptr);
3920 params[i].type = GuiParam::GUI_ENUM;
3921 ComboBoxPI* cmb = new ComboBoxPI(mw, i, "enum");
3922
3923 int curItem = -1;
3924 int cnt = 0;
3925 for (const auto& it : *plugin->ctrlEnumValues(i)) {
3926 cmb->addItem(it.second, it.first);
3927 if (curItem == -1 && it.first == static_cast<float>(val))
3928 curItem = cnt;
3929 cnt++;
3930 }
3931 cmb->setCurrentIndex(curItem);
3932
3933 params[i].actuator = cmb;
3934
3935 grid->addWidget(label, i, 0);
3936 grid->addWidget(params[i].actuator, i, 1, 1, 2);
3937
3938 cmb_obj = static_cast<ComboBoxPI*>(params[i].actuator);
3939
3940 connect(cmb_obj, QOverload<int>::of(&ComboBoxPI::currentIndexChanged), [=]() { comboChanged(i); } );
3941 connect(cmb_obj, &ComboBoxPI::rightClicked, [this](const QPoint &p, int i) { ctrlRightClicked(p, i); } );
3942
3943 }
3944 else {
3945 label = new QLabel(QString(plugin->paramName(i)), nullptr);
3946 params[i].type = GuiParam::GUI_SLIDER;
3947 params[i].label = new DoubleLabel(val, lower, upper, nullptr);
3948 params[i].label->setFrame(true);
3949 params[i].label->setAlignment(Qt::AlignCenter);
3950 params[i].label->setId(i);
3951
3952 if (plugin->ctrlValueType(i) == MusECore::CtrlValueType::VAL_INT)
3953 params[i].label->setPrecision(0);
3954 else
3955 params[i].label->setPrecision(2);
3956
3957 // Let sliders all have different but unique colors
3958 // Some prime number magic
3959 uint j = i+1;
3960 uint c1 = j * 211 % 256;
3961 uint c2 = j * j * 137 % 256;
3962 uint c3 = j * j * j * 43 % 256;
3963 QColor color(c1, c2, c3);
3964
3965 Slider* s = new Slider(0, "param", Qt::Horizontal,
3966 Slider::InsideHorizontal, 8, color, ScaleDraw::TextHighlightSplitAndShadow);
3967
3968 // TODO
3969 //s->setThumbLength(1);
3970 //s->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep);
3971 //s->setScaleMaxMinor(5);
3972 //s->setScale(MusEGlobal::config.minSlider-0.1, 10.0, 6.0, false);
3973 //s->setScale(dlower, dupper, 1.0, false);
3974 //s->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character.
3975 //s->setScaleBackBone(false);
3976 //s->setFillThumb(false);
3977
3978 QFont fnt;
3979 fnt.setFamily("Sans");
3980 fnt.setPixelSize(9);
3981 fnt.setStyleStrategy(QFont::NoAntialias);
3982 fnt.setHintingPreference(QFont::PreferVerticalHinting);
3983 s->setFont(fnt);
3984 s->setStyleSheet(MusECore::font2StyleSheetFull(fnt));
3985
3986 s->setCursorHoming(true);
3987 s->setId(i);
3988 s->setSizeHint(200, 8);
3989 s->setRange(dlower, dupper);
3990 if(LADSPA_IS_HINT_INTEGER(range.HintDescriptor))
3991 s->setStep(1.0);
3992 s->setValue(dval);
3993 params[i].actuator = s;
3994 params[i].label->setSlider(s);
3995
3996 label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
3997 params[i].label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
3998 grid->addWidget(label, i, 0);
3999 grid->addWidget(params[i].label, i, 1);
4000 grid->addWidget(params[i].actuator, i, 2);
4001
4002 sl_obj = static_cast<Slider*>(params[i].actuator);
4003 connect(sl_obj, QOverload<double, int, int>::of(&Slider::valueChanged),
4004 [=](double v, int id, int scroll_mode) { sliderChanged(v, id, scroll_mode); } );
4005 connect(params[i].label, &DoubleLabel::valueChanged, [this](double v, int i) { labelChanged(v, i); } );
4006 connect(sl_obj, &Slider::sliderPressed, [this](double v, int i) { sliderPressed(v, i); } );
4007 connect(sl_obj, &Slider::sliderReleased, [this](double v, int i) { sliderReleased(v, i); } );
4008 connect(sl_obj, &Slider::sliderRightClicked, [this](const QPoint &p, int i) { ctrlRightClicked(p, i); } );
4009 }
4010
4011 params[i].actuator->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed));
4012
4013 if (plugin->ctrlNotOnGui(i)) {
4014 params[i].actuator->setVisible(false);
4015 label->setVisible(false);
4016 if (params[i].label)
4017 params[i].label->setVisible(false);
4018 }
4019 }
4020
4021
4022 paramCnt = plugin->parametersOut();
4023 if (paramCnt > 0) {
4024 paramsOut = new GuiParam[paramCnt];
4025
4026 groupBox = new QGroupBox(tr("Output controls"));
4027 grid = new QGridLayout(groupBox);
4028 // grid->setSpacing(8);
4029 groupBox->setLayout(grid);
4030 vbox->addWidget(groupBox);
4031
4032 for (unsigned long i = 0; i < paramCnt; ++i) {
4033 QLabel* label = nullptr;
4034 LADSPA_PortRangeHint range = plugin->rangeOut(i);
4035 double lower = 0.0; // default values
4036 double upper = 32768.0; // Many latency outs have no hints so set this arbitrarily high
4037 double dlower = lower;
4038 double dupper = upper;
4039 double val = plugin->paramOut(i);
4040 double dval = val;
4041 paramsOut[i].pressed = false;
4042 paramsOut[i].hint = range.HintDescriptor;
4043
4044 getPluginConvertedValues(range, lower, upper, dlower, dupper, dval);
4045 label = new QLabel(QString(plugin->paramOutName(i)), nullptr);
4046 paramsOut[i].type = GuiParam::GUI_METER;
4047 paramsOut[i].label = new DoubleLabel(val, lower, upper, nullptr);
4048 paramsOut[i].label->setFrame(true);
4049 paramsOut[i].label->setAlignment(Qt::AlignCenter);
4050 paramsOut[i].label->setPrecision(2);
4051 paramsOut[i].label->setId(i);
4052
4053 Meter::MeterType mType=Meter::LinMeter;
4054 //if(LADSPA_IS_HINT_INTEGER(range.HintDescriptor))
4055 if(LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
4056 mType=Meter::DBMeter;
4057 Meter* m = new Meter(this,
4058 mType,
4059 Qt::Horizontal,
4060 dlower, dupper,
4061 Meter::InsideHorizontal); //, ScaleDraw::TextHighlightNone);
4062 m->setRefreshRate(MusEGlobal::config.guiRefresh);
4063 m->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
4064 m->setVal(dval, dval, false);
4065 m->setScaleBackBone(false);
4066 m->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor);
4067
4068 QFont fnt;
4069 fnt.setFamily("Sans");
4070 fnt.setPixelSize(9);
4071 fnt.setStyleStrategy(QFont::NoAntialias);
4072 fnt.setHintingPreference(QFont::PreferVerticalHinting);
4073 m->setFont(fnt);
4074 m->setStyleSheet(MusECore::font2StyleSheetFull(fnt));
4075
4076 paramsOut[i].actuator = m;
4077 label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
4078 paramsOut[i].label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
4079 grid->addWidget(label, paramCnt+i, 0);
4080 grid->addWidget(paramsOut[i].label, paramCnt+i, 1);
4081 grid->addWidget(paramsOut[i].actuator, paramCnt+i, 2);
4082 }
4083 }
4084
4085 vbox->addStretch(0);
4086 mw->setLayout(vbox);
4087 }
4088
4089
updateWindowTitle()4090 void PluginGui::updateWindowTitle()
4091 {
4092 if(plugin)
4093 setWindowTitle(plugin->titlePrefix() + plugin->name() +
4094 (plugin->uri().isEmpty() ? QString() : QString(" : ") + plugin->uri()));
4095 }
4096
hideEvent(QHideEvent * e)4097 void PluginGui::hideEvent(QHideEvent *e)
4098 {
4099 if(plugin)
4100 plugin->saveGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height());
4101
4102 e->ignore();
4103 QMainWindow::hideEvent(e);
4104 }
4105
showEvent(QShowEvent * e)4106 void PluginGui::showEvent(QShowEvent *e)
4107 {
4108 int x = 0, y = 0, w = 0, h = 0;
4109 if(plugin)
4110 plugin->savedGeometry(&x, &y, &w, &h);
4111
4112 #ifdef QT_SHOW_POS_BUG_WORKAROUND
4113 // Because of the bug, no matter what we must supply a position,
4114 // even upon first showing...
4115
4116 // Check sane size.
4117 if(w == 0)
4118 w = sizeHint().width();
4119 if(h == 0)
4120 h = sizeHint().height();
4121
4122 // No size hint? Try minimum size.
4123 if(w == 0)
4124 w = minimumSize().width();
4125 if(h == 0)
4126 h = minimumSize().height();
4127
4128 // Fallback.
4129 if(w == 0)
4130 w = 200;
4131 if(h == 0)
4132 h = 200;
4133
4134 setGeometry(x, y, w, h);
4135
4136 #else
4137
4138 // If the saved geometry is valid, use it.
4139 // Otherwise this is probably the first time showing,
4140 // so do not set a geometry - let Qt pick one
4141 // (using auto-placement and sizeHint).
4142 if(!(x == 0 && y == 0 && w == 0 && h == 0))
4143 {
4144 // Check sane size.
4145 if(w == 0)
4146 w = sizeHint().width();
4147 if(h == 0)
4148 h = sizeHint().height();
4149
4150 // No size hint? Try minimum size.
4151 if(w == 0)
4152 w = minimumSize().width();
4153 if(h == 0)
4154 h = minimumSize().height();
4155
4156 // Fallback.
4157 if(w == 0)
4158 w = 200;
4159 if(h == 0)
4160 h = 200;
4161
4162 setGeometry(x, y, w, h);
4163 }
4164 #endif
4165
4166 // Convenience: If the window was minimized, restore it.
4167 if(isMinimized())
4168 setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
4169
4170 e->ignore();
4171 QMainWindow::showEvent(e);
4172 }
4173
getPluginConvertedValues(LADSPA_PortRangeHint range,double & lower,double & upper,double & dlower,double & dupper,double & dval)4174 void PluginGui::getPluginConvertedValues(LADSPA_PortRangeHint range,
4175 double &lower, double &upper, double &dlower, double &dupper, double &dval)
4176 {
4177 if (LADSPA_IS_HINT_BOUNDED_BELOW(range.HintDescriptor)) {
4178 dlower = lower = range.LowerBound;
4179 }
4180 if (LADSPA_IS_HINT_BOUNDED_ABOVE(range.HintDescriptor)) {
4181 dupper = upper = range.UpperBound;
4182 }
4183 if (LADSPA_IS_HINT_SAMPLE_RATE(range.HintDescriptor)) {
4184 lower *= MusEGlobal::sampleRate;
4185 upper *= MusEGlobal::sampleRate;
4186 dlower = lower;
4187 dupper = upper;
4188 }
4189 if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) {
4190 if (lower == 0.0)
4191 lower = 0.001;
4192 dlower = MusECore::fast_log10(lower)*20.0;
4193 dupper = MusECore::fast_log10(upper)*20.0;
4194 dval = MusECore::fast_log10(dval) * 20.0;
4195 }
4196
4197 }
4198
4199 //---------------------------------------------------------
4200 // heartBeat
4201 //---------------------------------------------------------
4202
heartBeat()4203 void PluginGui::heartBeat()
4204 {
4205 updateControls();
4206 }
4207
4208 //---------------------------------------------------------
4209 // ctrlPressed
4210 //---------------------------------------------------------
4211
sliderPressed(double,int param)4212 void PluginGui::sliderPressed(double /*val*/, int param)
4213 {
4214 params[param].pressed = true;
4215 MusECore::AudioTrack* track = plugin->track();
4216 int id = plugin->id();
4217 if(id != -1)
4218 {
4219 id = MusECore::genACnum(id, param);
4220 double val = ((Slider*)params[param].actuator)->value();
4221 if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
4222 val = muse_db2val(val);
4223 else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
4224 val = rint(val);
4225 params[param].label->blockSignals(true);
4226 params[param].label->setValue(val);
4227 params[param].label->blockSignals(false);
4228 if(track)
4229 {
4230 track->startAutoRecord(id, val);
4231 track->setPluginCtrlVal(id, val);
4232 }
4233 }
4234 plugin->enableController(param, false);
4235 }
4236
4237 //---------------------------------------------------------
4238 // ctrlReleased
4239 //---------------------------------------------------------
4240
sliderReleased(double,int param)4241 void PluginGui::sliderReleased(double /*val*/, int param)
4242 {
4243 MusECore::AudioTrack* track = plugin->track();
4244 int id = plugin->id();
4245 if(track && id != -1)
4246 {
4247 id = MusECore::genACnum(id, param);
4248 double val = ((Slider*)params[param].actuator)->value();
4249 if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
4250 val = muse_db2val(val);
4251 else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
4252 val = rint(val);
4253 track->stopAutoRecord(id, val);
4254 }
4255
4256 params[param].pressed = false;
4257 }
4258
4259 //---------------------------------------------------------
4260 // ctrlRightClicked
4261 //---------------------------------------------------------
4262
ctrlRightClicked(const QPoint & p,int param)4263 void PluginGui::ctrlRightClicked(const QPoint &p, int param)
4264 {
4265 int id = plugin->id();
4266 if(id != -1)
4267 MusEGlobal::song->execAutomationCtlPopup(plugin->track(), p, MusECore::genACnum(id, param));
4268 }
4269
4270 //---------------------------------------------------------
4271 // switchPressed
4272 //---------------------------------------------------------
4273
switchPressed(int param)4274 void PluginGui::switchPressed(int param)
4275 {
4276 params[param].pressed = true;
4277 MusECore::AudioTrack* track = plugin->track();
4278 int id = plugin->id();
4279 if (track && id != -1)
4280 {
4281 id = MusECore::genACnum(id, param);
4282 float val = (float)((CheckBox*)params[param].actuator)->isChecked();
4283 track->startAutoRecord(id, val);
4284 track->setPluginCtrlVal(id, val);
4285 }
4286 plugin->enableController(param, false);
4287 }
4288
4289 //---------------------------------------------------------
4290 // switchReleased
4291 //---------------------------------------------------------
4292
switchReleased(int param)4293 void PluginGui::switchReleased(int param)
4294 {
4295 MusECore::AutomationType at = MusECore::AUTO_OFF;
4296 MusECore::AudioTrack* track = plugin->track();
4297 if(track)
4298 at = track->automationType();
4299
4300 // don't enable controller until transport stopped.
4301 if ((at == MusECore::AUTO_OFF) ||
4302 (at == MusECore::AUTO_TOUCH && !MusEGlobal::audio->isPlaying()) )
4303 plugin->enableController(param, true);
4304
4305 params[param].pressed = false;
4306 }
4307
4308 //---------------------------------------------------------
4309 // sliderChanged
4310 //---------------------------------------------------------
4311
sliderChanged(double val,int param,int scrollMode)4312 void PluginGui::sliderChanged(double val, int param, int scrollMode)
4313 {
4314 MusECore::AudioTrack* track = plugin->track();
4315
4316 if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
4317 val = muse_db2val(val);
4318 else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
4319 val = rint(val);
4320
4321 params[param].label->blockSignals(true);
4322 params[param].label->setValue(val);
4323 params[param].label->blockSignals(false);
4324 int id = plugin->id();
4325 if(track && id != -1)
4326 {
4327 id = MusECore::genACnum(id, param);
4328 // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals.
4329 // ScrDirect mode is one-time only on press with modifier.
4330 if(scrollMode != SliderBase::ScrDirect)
4331 track->recordAutomation(id, val);
4332 }
4333 plugin->setParam(param, val); // Schedules a timed control change.
4334 plugin->enableController(param, false);
4335 }
4336
4337 //---------------------------------------------------------
4338 // labelChanged
4339 //---------------------------------------------------------
4340
labelChanged(double val,int param)4341 void PluginGui::labelChanged(double val, int param)
4342 {
4343 MusECore::AudioTrack* track = plugin->track();
4344
4345 double dval = val;
4346 if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint))
4347 dval = MusECore::fast_log10(val) * 20.0;
4348 else if (LADSPA_IS_HINT_INTEGER(params[param].hint))
4349 dval = rint(val);
4350 params[param].actuator->blockSignals(true);
4351 ((Slider*)params[param].actuator)->setValue(dval);
4352 params[param].actuator->blockSignals(false);
4353 int id = plugin->id();
4354 if(track && id != -1)
4355 {
4356 id = MusECore::genACnum(id, param);
4357 track->startAutoRecord(id, val);
4358 }
4359 plugin->setParam(param, val); // Schedules a timed control change.
4360 plugin->enableController(param, false);
4361 }
4362
4363 //---------------------------------------------------------
4364 // comboChanged
4365 //---------------------------------------------------------
4366
comboChanged(unsigned long param)4367 void PluginGui::comboChanged(unsigned long param)
4368 {
4369 MusECore::AudioTrack* track = plugin->track();
4370
4371 ComboBoxPI *c = static_cast<ComboBoxPI*>(params[param].actuator);
4372 double val = rint( c->currentData().toDouble() );
4373 int id = plugin->id();
4374 if(track && id != -1)
4375 {
4376 id = MusECore::genACnum(id, param);
4377 track->startAutoRecord(id, val);
4378 }
4379 plugin->setParam(param, val); // Schedules a timed control change.
4380 plugin->enableController(param, false);
4381 }
4382
4383 //---------------------------------------------------------
4384 // load
4385 //---------------------------------------------------------
4386
load()4387 void PluginGui::load()
4388 {
4389 QString s("presets/plugins/");
4390 s += plugin->pluginLabel();
4391 s += "/";
4392
4393 QString fn = getOpenFileName(s, MusEGlobal::preset_file_pattern,
4394 this, tr("MusE: load preset"), nullptr);
4395 if (fn.isEmpty())
4396 return;
4397 bool popenFlag;
4398 FILE* f = fileOpen(this, fn, QString(".pre"), "r", popenFlag, true);
4399 if (f == nullptr)
4400 return;
4401
4402 MusECore::Xml xml(f);
4403 int mode = 0;
4404 for (;;) {
4405 MusECore::Xml::Token token = xml.parse();
4406 QString tag = xml.s1();
4407 switch (token) {
4408 case MusECore::Xml::Error:
4409 case MusECore::Xml::End:
4410 return;
4411 case MusECore::Xml::TagStart:
4412 if (mode == 0 && tag == "muse")
4413 mode = 1;
4414 else if (mode == 1 && tag == "plugin") {
4415
4416 if(plugin->readConfiguration(xml, true))
4417 {
4418 QMessageBox::critical(this, QString("MusE"),
4419 tr("Error reading preset. Might not be right type for this plugin"));
4420 goto ende;
4421 }
4422
4423 mode = 0;
4424 }
4425 else
4426 xml.unknown("PluginGui");
4427 break;
4428 case MusECore::Xml::Attribut:
4429 break;
4430 case MusECore::Xml::TagEnd:
4431 if (!mode && tag == "muse")
4432 {
4433 plugin->updateControllers();
4434 goto ende;
4435 }
4436 default:
4437 break;
4438 }
4439 }
4440 ende:
4441 if (popenFlag)
4442 pclose(f);
4443 else
4444 fclose(f);
4445 }
4446
4447 //---------------------------------------------------------
4448 // save
4449 //---------------------------------------------------------
4450
save()4451 void PluginGui::save()
4452 {
4453 QString s("presets/plugins/");
4454 s += plugin->pluginLabel();
4455 s += "/";
4456
4457 QString fn = getSaveFileName(s, MusEGlobal::preset_file_save_pattern, this,
4458 tr("MusE: Save preset"));
4459 if (fn.isEmpty())
4460 return;
4461 bool popenFlag;
4462 FILE* f = fileOpen(this, fn, QString(".pre"), "w", popenFlag, false, true);
4463 if (f == nullptr)
4464 return;
4465 MusECore::Xml xml(f);
4466 xml.header();
4467 xml.tag(0, "muse version=\"1.0\"");
4468 plugin->writeConfiguration(1, xml);
4469 xml.tag(1, "/muse");
4470
4471 if (popenFlag)
4472 pclose(f);
4473 else
4474 fclose(f);
4475 }
4476
4477 //---------------------------------------------------------
4478 // bypassToggled
4479 //---------------------------------------------------------
4480
bypassToggled(bool val)4481 void PluginGui::bypassToggled(bool val)
4482 {
4483 updateWindowTitle();
4484 plugin->setOn(!val);
4485 MusEGlobal::song->update(SC_ROUTE);
4486 }
4487
showSettings()4488 void PluginGui::showSettings()
4489 {
4490 PluginSettings settingsDialog(plugin, MusEGlobal::config.noPluginScaling, this);
4491 settingsDialog.setWindowTitle(tr("Plugin Settings"));
4492 settingsDialog.exec();
4493 }
4494
4495 //---------------------------------------------------------
4496 // setOn
4497 //---------------------------------------------------------
4498
setOn(bool val)4499 void PluginGui::setOn(bool val)
4500 {
4501 updateWindowTitle();
4502 onOff->blockSignals(true);
4503 onOff->setChecked(!val);
4504 onOff->blockSignals(false);
4505 }
4506
4507 //---------------------------------------------------------
4508 // updateValues
4509 //---------------------------------------------------------
4510
updateValues()4511 void PluginGui::updateValues()
4512 {
4513 if (params) {
4514 for (unsigned long i = 0; i < plugin->parameters(); ++i) {
4515 GuiParam* gp = ¶ms[i];
4516 if (gp->type == GuiParam::GUI_SLIDER) {
4517 double lv = plugin->param(i);
4518 double sv = lv;
4519 if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint))
4520 sv = MusECore::fast_log10(lv) * 20.0;
4521 else if (LADSPA_IS_HINT_INTEGER(params[i].hint))
4522 {
4523 sv = rint(lv);
4524 lv = sv;
4525 }
4526 gp->label->blockSignals(true);
4527 gp->actuator->blockSignals(true);
4528 gp->label->setValue(lv);
4529 ((Slider*)(gp->actuator))->setValue(sv);
4530 gp->label->blockSignals(false);
4531 gp->actuator->blockSignals(false);
4532 }
4533 else if (gp->type == GuiParam::GUI_SWITCH) {
4534 gp->actuator->blockSignals(true);
4535 ((CheckBox*)(gp->actuator))->setChecked(int(plugin->param(i)));
4536 gp->actuator->blockSignals(false);
4537 }
4538 else if (gp->type == GuiParam::GUI_ENUM) {
4539 float sv = static_cast<float>(plugin->param(i));
4540 ComboBoxPI *c = static_cast<ComboBoxPI*>(gp->actuator);
4541 int idx = c->findData(sv);
4542 gp->actuator->blockSignals(true);
4543 c->setCurrentIndex(idx);
4544 gp->actuator->blockSignals(false);
4545 }
4546 }
4547 }
4548 else if (gw) {
4549 for (unsigned long i = 0; i < nobj; ++i) {
4550 QWidget* widget = gw[i].widget;
4551 int type = gw[i].type;
4552 unsigned long param = gw[i].param;
4553 double val = plugin->param(param);
4554 widget->blockSignals(true);
4555 switch(type) {
4556 case GuiWidgets::SLIDER:
4557 ((Slider*)widget)->setValue(val); // Note conversion to double
4558 break;
4559 case GuiWidgets::DOUBLE_LABEL:
4560 ((DoubleLabel*)widget)->setValue(val); // Note conversion to double
4561 break;
4562 case GuiWidgets::QCHECKBOX:
4563 ((QCheckBox*)widget)->setChecked(int(val));
4564 break;
4565 case GuiWidgets::QCOMBOBOX:
4566 ((QComboBox*)widget)->setCurrentIndex(int(val));
4567 break;
4568 }
4569 widget->blockSignals(false);
4570 }
4571 }
4572 }
4573
4574 //---------------------------------------------------------
4575 // updateControls
4576 //---------------------------------------------------------
4577
updateControls()4578 void PluginGui::updateControls()
4579 {
4580 if (!plugin->track() || plugin->id() == -1)
4581 return;
4582
4583 // update outputs
4584
4585 if (paramsOut) {
4586 for (unsigned long i = 0; i < plugin->parametersOut(); ++i) {
4587 GuiParam* gp = ¶msOut[i];
4588 if (gp->type == GuiParam::GUI_METER) {
4589 double lv = plugin->paramOut(i);
4590 double sv = lv;
4591 if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint))
4592 sv = MusECore::fast_log10(lv) * 20.0;
4593 else if (LADSPA_IS_HINT_INTEGER(params[i].hint))
4594 {
4595 sv = rint(lv);
4596 lv = sv;
4597 }
4598 ((Meter*)(gp->actuator))->setVal(sv, sv, false);
4599 gp->label->setValue(lv);
4600
4601 }
4602 }
4603 }
4604
4605
4606 if (params) {
4607 for (unsigned long i = 0; i < plugin->parameters(); ++i) {
4608 GuiParam* gp = ¶ms[i];
4609 if(gp->pressed) // Inhibit the controller stream if control is currently pressed.
4610 continue;
4611 double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i),
4612 MusEGlobal::audio->curFramePos(),
4613 !MusEGlobal::automation ||
4614 plugin->track()->automationType() == MusECore::AUTO_OFF ||
4615 !plugin->controllerEnabled(i));
4616 if (gp->type == GuiParam::GUI_SLIDER) {
4617 {
4618 double sv = v;
4619 if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint))
4620 sv = MusECore::fast_log10(v) * 20.0;
4621 else
4622 if (LADSPA_IS_HINT_INTEGER(params[i].hint))
4623 {
4624 sv = rint(v);
4625 v = sv;
4626 }
4627 if(((Slider*)(gp->actuator))->value() != sv)
4628 {
4629 gp->label->blockSignals(true);
4630 gp->actuator->blockSignals(true);
4631 ((Slider*)(gp->actuator))->setValue(sv);
4632 gp->label->setValue(v);
4633 gp->actuator->blockSignals(false);
4634 gp->label->blockSignals(false);
4635 }
4636 }
4637 }
4638 else if (gp->type == GuiParam::GUI_SWITCH) {
4639 {
4640 bool b = (int)v;
4641 if(((CheckBox*)(gp->actuator))->isChecked() != b)
4642 {
4643 gp->actuator->blockSignals(true);
4644 ((CheckBox*)(gp->actuator))->setChecked(b);
4645 gp->actuator->blockSignals(false);
4646 }
4647 }
4648 }
4649 else if (gp->type == GuiParam::GUI_ENUM) {
4650 {
4651 float sv = static_cast<float>(v);
4652 ComboBoxPI *c = static_cast<ComboBoxPI*>(gp->actuator);
4653 if (c->currentData().toFloat() != sv)
4654 {
4655 int idx = c->findData(sv);
4656 gp->actuator->blockSignals(true);
4657 c->setCurrentIndex(idx);
4658 gp->actuator->blockSignals(false);
4659 }
4660 }
4661 }
4662 }
4663 }
4664 else if (gw) {
4665 for (unsigned long i = 0; i < nobj; ++i) {
4666 if(gw[i].pressed) // Inhibit the controller stream if control is currently pressed.
4667 continue;
4668 QWidget* widget = gw[i].widget;
4669 int type = gw[i].type;
4670 unsigned long param = gw[i].param;
4671 double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param),
4672 MusEGlobal::audio->curFramePos(),
4673 !MusEGlobal::automation ||
4674 plugin->track()->automationType() == MusECore::AUTO_OFF ||
4675 !plugin->controllerEnabled(param));
4676 widget->blockSignals(true);
4677 switch(type) {
4678 case GuiWidgets::SLIDER:
4679 {
4680 if(((Slider*)widget)->value() != v)
4681 ((Slider*)widget)->setValue(v);
4682 }
4683 break;
4684 case GuiWidgets::DOUBLE_LABEL:
4685 {
4686 if(((DoubleLabel*)widget)->value() != v)
4687 ((DoubleLabel*)widget)->setValue(v);
4688 }
4689 break;
4690 case GuiWidgets::QCHECKBOX:
4691 {
4692 bool b = (bool)v;
4693 if(((QCheckBox*)widget)->isChecked() != b)
4694 ((QCheckBox*)widget)->setChecked(b);
4695 }
4696 break;
4697 case GuiWidgets::QCOMBOBOX:
4698 {
4699 int n = (int)v;
4700 if(((QComboBox*)widget)->currentIndex() != n)
4701 ((QComboBox*)widget)->setCurrentIndex(n);
4702 }
4703 break;
4704 }
4705 widget->blockSignals(false);
4706 }
4707 }
4708 }
4709
4710 //---------------------------------------------------------
4711 // guiParamChanged
4712 //---------------------------------------------------------
4713
guiParamChanged(unsigned long int idx)4714 void PluginGui::guiParamChanged(unsigned long int idx)
4715 {
4716 QWidget* w = gw[idx].widget;
4717 unsigned long param = gw[idx].param;
4718 int type = gw[idx].type;
4719
4720 MusECore::AudioTrack* track = plugin->track();
4721
4722 double val = 0.0;
4723 bool ignoreRecAutomation = false;
4724 switch(type) {
4725 case GuiWidgets::SLIDER:
4726 val = ((Slider*)w)->value();
4727 // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals.
4728 // ScrDirect mode is one-time only on press with modifier.
4729 if(((Slider*)w)->scrollMode() == Slider::ScrDirect)
4730 ignoreRecAutomation = true;
4731 break;
4732 case GuiWidgets::DOUBLE_LABEL:
4733 val = ((DoubleLabel*)w)->value();
4734 break;
4735 case GuiWidgets::QCHECKBOX:
4736 val = double(((QCheckBox*)w)->isChecked());
4737 break;
4738 case GuiWidgets::QCOMBOBOX:
4739 val = double(((QComboBox*)w)->currentIndex());
4740 break;
4741 }
4742
4743 for (unsigned long i = 0; i < nobj; ++i) {
4744 QWidget* widget = gw[i].widget;
4745 if (widget == w || param != gw[i].param)
4746 continue;
4747 int type = gw[i].type;
4748 widget->blockSignals(true);
4749 switch(type) {
4750 case GuiWidgets::SLIDER:
4751 ((Slider*)widget)->setValue(val);
4752 break;
4753 case GuiWidgets::DOUBLE_LABEL:
4754 ((DoubleLabel*)widget)->setValue(val);
4755 break;
4756 case GuiWidgets::QCHECKBOX:
4757 ((QCheckBox*)widget)->setChecked(int(val));
4758 break;
4759 case GuiWidgets::QCOMBOBOX:
4760 ((QComboBox*)widget)->setCurrentIndex(int(val));
4761 break;
4762 }
4763 widget->blockSignals(false);
4764 }
4765
4766 int id = plugin->id();
4767 if(track && id != -1)
4768 {
4769 id = MusECore::genACnum(id, param);
4770 switch(type)
4771 {
4772 case GuiWidgets::DOUBLE_LABEL:
4773 case GuiWidgets::QCHECKBOX:
4774 track->startAutoRecord(id, val);
4775 break;
4776 default:
4777 if(!ignoreRecAutomation)
4778 track->recordAutomation(id, val);
4779 break;
4780 }
4781 }
4782
4783 plugin->setParam(param, val); // Schedules a timed control change.
4784 plugin->enableController(param, false);
4785 }
4786
4787 //---------------------------------------------------------
4788 // guiParamPressed
4789 //---------------------------------------------------------
4790
guiParamPressed(unsigned long int idx)4791 void PluginGui::guiParamPressed(unsigned long int idx)
4792 {
4793 gw[idx].pressed = true;
4794 unsigned long param = gw[idx].param;
4795 plugin->enableController(param, false);
4796
4797 //MusECore::AudioTrack* track = plugin->track();
4798 //int id = plugin->id();
4799 //if(!track || id == -1)
4800 // return;
4801 //id = MusECore::genACnum(id, param);
4802 // NOTE: For this to be of any use, the freeverb gui 2142.ui
4803 // would have to be used, and changed to use CheckBox and ComboBox
4804 // instead of QCheckBox and QComboBox, since both of those would
4805 // need customization (Ex. QCheckBox doesn't check on click). RECHECK: Qt4 it does?
4806 /*
4807 switch(type) {
4808 case GuiWidgets::QCHECKBOX:
4809 double val = (double)((CheckBox*)w)->isChecked();
4810 track->startAutoRecord(id, val);
4811 break;
4812 case GuiWidgets::QCOMBOBOX:
4813 double val = (double)((ComboBox*)w)->currentIndex();
4814 track->startAutoRecord(id, val);
4815 break;
4816 }
4817 */
4818 }
4819
4820 //---------------------------------------------------------
4821 // guiParamReleased
4822 //---------------------------------------------------------
4823
guiParamReleased(unsigned long int idx)4824 void PluginGui::guiParamReleased(unsigned long int idx)
4825 {
4826 unsigned long param = gw[idx].param;
4827 int type = gw[idx].type;
4828
4829 MusECore::AutomationType at = MusECore::AUTO_OFF;
4830 MusECore::AudioTrack* track = plugin->track();
4831 if(track)
4832 at = track->automationType();
4833
4834 // Special for switch - don't enable controller until transport stopped.
4835 if ((at == MusECore::AUTO_OFF) ||
4836 (at == MusECore::AUTO_TOUCH && (type != GuiWidgets::QCHECKBOX ||
4837 !MusEGlobal::audio->isPlaying()) ) )
4838 plugin->enableController(param, true);
4839
4840 //int id = plugin->id();
4841 //if(!track || id == -1)
4842 // return;
4843 //id = MusECore::genACnum(id, param);
4844 // NOTE: For this to be of any use, the freeverb gui 2142.ui
4845 // would have to be used, and changed to use CheckBox and ComboBox
4846 // instead of QCheckBox and QComboBox, since both of those would
4847 // need customization (Ex. QCheckBox doesn't check on click). // RECHECK Qt4 it does?
4848 /*
4849 switch(type) {
4850 case GuiWidgets::QCHECKBOX:
4851 double val = (double)((CheckBox*)w)->isChecked();
4852 track->stopAutoRecord(id, param);
4853 break;
4854 case GuiWidgets::QCOMBOBOX:
4855 double val = (double)((ComboBox*)w)->currentIndex();
4856 track->stopAutoRecord(id, param);
4857 break;
4858 }
4859 */
4860
4861 gw[idx].pressed = false;
4862 }
4863
4864 //---------------------------------------------------------
4865 // guiSliderPressed
4866 //---------------------------------------------------------
4867
guiSliderPressed(double,unsigned long int idx)4868 void PluginGui::guiSliderPressed(double /*val*/, unsigned long int idx)
4869 {
4870 gw[idx].pressed = true;
4871 unsigned long param = gw[idx].param;
4872 QWidget *w = gw[idx].widget;
4873 MusECore::AudioTrack* track = plugin->track();
4874 int id = plugin->id();
4875 if(track && id != -1)
4876 {
4877 id = MusECore::genACnum(id, param);
4878 double val = ((Slider*)w)->value();
4879 track->startAutoRecord(id, val);
4880 // Needed so that paging a slider updates a label or other buddy control.
4881 for (unsigned long i = 0; i < nobj; ++i) {
4882 QWidget* widget = gw[i].widget;
4883 if (widget == w || param != gw[i].param)
4884 continue;
4885 int type = gw[i].type;
4886 widget->blockSignals(true);
4887 switch(type) {
4888 case GuiWidgets::SLIDER:
4889 ((Slider*)widget)->setValue(val);
4890 break;
4891 case GuiWidgets::DOUBLE_LABEL:
4892 ((DoubleLabel*)widget)->setValue(val);
4893 break;
4894 case GuiWidgets::QCHECKBOX:
4895 ((QCheckBox*)widget)->setChecked(int(val));
4896 break;
4897 case GuiWidgets::QCOMBOBOX:
4898 ((QComboBox*)widget)->setCurrentIndex(int(val));
4899 break;
4900 }
4901 widget->blockSignals(false);
4902 }
4903 track->setPluginCtrlVal(id, val);
4904 }
4905 plugin->enableController(param, false);
4906 }
4907
4908 //---------------------------------------------------------
4909 // guiSliderReleased
4910 //---------------------------------------------------------
4911
guiSliderReleased(double,unsigned long int idx)4912 void PluginGui::guiSliderReleased(double /*val*/, unsigned long int idx)
4913 {
4914 int param = gw[idx].param;
4915 QWidget *w = gw[idx].widget;
4916
4917 MusECore::AutomationType at = MusECore::AUTO_OFF;
4918 MusECore::AudioTrack* track = plugin->track();
4919 if(track)
4920 at = track->automationType();
4921
4922 int id = plugin->id();
4923
4924 if(track && id != -1)
4925 {
4926 id = MusECore::genACnum(id, param);
4927
4928 double val = ((Slider*)w)->value();
4929 track->stopAutoRecord(id, val);
4930 }
4931
4932 if (at == MusECore::AUTO_OFF ||
4933 at == MusECore::AUTO_TOUCH)
4934 plugin->enableController(param, true);
4935
4936 gw[idx].pressed = false;
4937 }
4938
4939 //---------------------------------------------------------
4940 // guiSliderRightClicked
4941 //---------------------------------------------------------
4942
guiSliderRightClicked(const QPoint & p,unsigned long int idx)4943 void PluginGui::guiSliderRightClicked(const QPoint &p, unsigned long int idx)
4944 {
4945 int param = gw[idx].param;
4946 int id = plugin->id();
4947 if(id != -1)
4948 MusEGlobal::song->execAutomationCtlPopup(plugin->track(), p, MusECore::genACnum(id, param));
4949 }
4950
4951 //---------------------------------------------------------
4952 // guiContextMenuReq
4953 //---------------------------------------------------------
4954
guiContextMenuReq(unsigned long int idx)4955 void PluginGui::guiContextMenuReq(unsigned long int idx)
4956 {
4957 guiSliderRightClicked(QCursor().pos(), idx);
4958 }
4959
4960 //---------------------------------------------------------
4961 // PluginLoader
4962 //---------------------------------------------------------
createWidget(const QString & className,QWidget * parent,const QString & name)4963 QWidget* PluginLoader::createWidget(const QString & className, QWidget * parent, const QString & name)
4964 {
4965 if(className == QString("MusEGui::DoubleLabel"))
4966 return new DoubleLabel(parent, name.toLatin1().constData());
4967 if(className == QString("MusEGui::Slider"))
4968 return new Slider(parent, name.toLatin1().constData(), Qt::Horizontal, Slider::InsideHorizontal, 8, QColor(), ScaleDraw::TextHighlightSplitAndShadow);
4969
4970 return QUiLoader::createWidget(className, parent, name);
4971 }
4972
4973 } // namespace MusEGui
4974
4975
4976 namespace MusEGlobal {
4977
writePluginGroupNames(int level,MusECore::Xml & xml)4978 static void writePluginGroupNames(int level, MusECore::Xml& xml)
4979 {
4980 xml.tag(level++, "group_names");
4981
4982 for (QList<QString>::iterator it=plugin_group_names.begin(); it!=plugin_group_names.end(); it++)
4983 xml.strTag(level, "name", *it);
4984
4985 xml.etag(--level, "group_names");
4986 }
4987
writePluginGroupMap(int level,MusECore::Xml & xml)4988 static void writePluginGroupMap(int level, MusECore::Xml& xml)
4989 {
4990 using MusECore::PluginGroups;
4991
4992 xml.tag(level++, "group_map");
4993
4994 for (PluginGroups::iterator it=plugin_groups.begin(); it!=plugin_groups.end(); it++)
4995 if (!it.value().empty())
4996 {
4997 xml.tag(level++, "entry");
4998
4999 xml.strTag(level, "lib", it.key().first);
5000 xml.strTag(level, "label", it.key().second);
5001
5002 for (QSet<int>::iterator it2=it.value().begin(); it2!=it.value().end(); it2++)
5003 xml.intTag(level, "group", *it2);
5004
5005 xml.etag(--level, "entry");
5006 }
5007
5008 xml.etag(--level, "group_map");
5009 }
5010
writePluginGroupConfiguration(int level,MusECore::Xml & xml)5011 void writePluginGroupConfiguration(int level, MusECore::Xml& xml)
5012 {
5013 xml.tag(level++, "plugin_groups");
5014
5015 writePluginGroupNames(level, xml);
5016 writePluginGroupMap(level, xml);
5017
5018 xml.etag(--level, "plugin_groups");
5019 }
5020
readPluginGroupNames(MusECore::Xml & xml)5021 static void readPluginGroupNames(MusECore::Xml& xml)
5022 {
5023 plugin_group_names.clear();
5024
5025 for (;;)
5026 {
5027 MusECore::Xml::Token token = xml.parse();
5028 if (token == MusECore::Xml::Error || token == MusECore::Xml::End)
5029 break;
5030
5031 const QString& tag = xml.s1();
5032 switch (token)
5033 {
5034 case MusECore::Xml::TagStart:
5035 if (tag=="name")
5036 plugin_group_names.append(xml.parse1());
5037 else
5038 xml.unknown("readPluginGroupNames");
5039 break;
5040
5041 case MusECore::Xml::TagEnd:
5042 if (tag == "group_names")
5043 return;
5044
5045 default:
5046 break;
5047 }
5048 }
5049 }
5050
readPluginGroupMap(MusECore::Xml & xml)5051 static void readPluginGroupMap(MusECore::Xml& xml)
5052 {
5053 plugin_groups.clear();
5054
5055 for (;;)
5056 {
5057 MusECore::Xml::Token token = xml.parse();
5058 if (token == MusECore::Xml::Error || token == MusECore::Xml::End)
5059 break;
5060
5061 const QString& tag = xml.s1();
5062 switch (token)
5063 {
5064 case MusECore::Xml::TagStart:
5065 if (tag=="entry")
5066 {
5067 QString lib;
5068 QString label;
5069 QSet<int> groups;
5070 bool read_lib=false, read_label=false;
5071
5072 for (;;)
5073 {
5074 MusECore::Xml::Token token = xml.parse();
5075 if (token == MusECore::Xml::Error || token == MusECore::Xml::End)
5076 break;
5077
5078 const QString& tag = xml.s1();
5079 switch (token)
5080 {
5081 case MusECore::Xml::TagStart:
5082 if (tag=="lib")
5083 {
5084 lib=xml.parse1();
5085 read_lib=true;
5086 }
5087 else if (tag=="label")
5088 {
5089 label=xml.parse1();
5090 read_label=true;
5091 }
5092 else if (tag=="group")
5093 groups.insert(xml.parseInt());
5094 else
5095 xml.unknown("readPluginGroupMap");
5096 break;
5097
5098 case MusECore::Xml::TagEnd:
5099 if (tag == "entry")
5100 goto done_reading_entry;
5101
5102 default:
5103 break;
5104 }
5105 }
5106
5107 done_reading_entry:
5108
5109 if (read_lib && read_label)
5110 plugin_groups.get(lib,label)=groups;
5111 else
5112 fprintf(stderr,"ERROR: plugin group map entry without lib or label!\n");
5113 }
5114 else
5115 xml.unknown("readPluginGroupMap");
5116 break;
5117
5118 case MusECore::Xml::TagEnd:
5119 if (tag == "group_map")
5120 return;
5121
5122 default:
5123 break;
5124 }
5125 }
5126 }
5127
readPluginGroupConfiguration(MusECore::Xml & xml)5128 void readPluginGroupConfiguration(MusECore::Xml& xml)
5129 {
5130 for (;;)
5131 {
5132 MusECore::Xml::Token token = xml.parse();
5133 if (token == MusECore::Xml::Error || token == MusECore::Xml::End)
5134 break;
5135
5136 const QString& tag = xml.s1();
5137 switch (token)
5138 {
5139 case MusECore::Xml::TagStart:
5140 if (tag=="group_names")
5141 readPluginGroupNames(xml);
5142 else if (tag=="group_map")
5143 readPluginGroupMap(xml);
5144 else
5145 xml.unknown("readPluginGroupConfiguration");
5146 break;
5147
5148 case MusECore::Xml::TagEnd:
5149 if (tag == "plugin_groups")
5150 return;
5151
5152 default:
5153 break;
5154 }
5155 }
5156 }
5157
5158 } // namespace MusEGlobal
5159