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", &parameter);
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", &parameter);
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 = &params[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 = &paramsOut[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 = &params[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