1 /*
2  * Copyright (C) 2011 Hermann Meyer, James Warden, Andreas Degert, Pete Shorthose
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include <dlfcn.h>
20 #include <dirent.h>
21 
22 #include "engine.h"
23 
24 namespace gx_engine {
25 
26 /****************************************************************
27  ** class ParamRegImpl
28  */
29 
30 gx_engine::ParamMap *ParamRegImpl::pmap = 0;
31 
ParamRegImpl(gx_engine::ParamMap * pm)32 ParamRegImpl::ParamRegImpl(gx_engine::ParamMap* pm): ParamReg() {
33     pmap = pm;
34     plugin = 0;
35     registerFloatVar = registerFloatVar_;
36     registerIntVar = registerIntVar_;
37     registerBoolVar = registerBoolVar_;
38 }
39 
40 struct param_opts {
41     bool shared;
42     bool log;
43     bool nomidi;
44     bool output;
45     bool nosavable;
46     bool no_warning;
47     bool maxlevel;
48     string name;
49     param_opts(const char* tp, const char *id, const char *name);
50     void set_common(Parameter *p, const char *tooltip);
51 };
52 
param_opts(const char * tp,const char * id,const char * name_)53 param_opts::param_opts(const char* tp, const char *id, const char *name_)
54     : shared(false), log(false), nomidi(false), output(false),
55       nosavable(false), no_warning(false), maxlevel(false), name() {
56     if (!name_[0]) {
57         assert(strrchr(id, '.'));
58         name = strrchr(id, '.')+1;
59         if (!name.empty()) {
60             name[0] = toupper(name[0]);
61         }
62     } else {
63         name = name_;
64     }
65     assert(tp[0] == 'S' || tp[0] == 'B');
66 
67     for (const char *p = tp+1; *p; p++) {
68         switch (*p) {
69         case 'A': shared = true; break;
70         case 'L': log = true; assert(tp[0] == 'S'); break;
71         case 'N': nomidi = true; break;
72         case 'O': output = true; nomidi = true; nosavable = true; break;
73         case 'o': output = true; break;
74         case 's': nosavable = true; break;
75         case 'w': no_warning = true; break;
76         case 'M': maxlevel = true; break;
77         default:
78             cerr << id << ": unknown type char: " << *p << endl;
79             assert(false);
80             break;
81         }
82     }
83 }
84 
set_common(Parameter * p,const char * tooltip)85 void param_opts::set_common(Parameter *p, const char *tooltip) {
86     if (!p) {
87         return;
88     }
89     if (output) {
90         p->setOutput(true);
91     }
92     if (nosavable) {
93         p->setSavable(false);
94     }
95     if (no_warning) {
96         p->setNoWarning(true);
97     }
98     if (tooltip && tooltip[0]) {
99         p->set_desc(tooltip);
100     }
101 }
102 
registerFloatVar_(const char * id,const char * name,const char * tp,const char * tooltip,float * var,float val,float low,float up,float step,const value_pair * values)103 float *ParamRegImpl::registerFloatVar_(const char* id, const char* name, const char* tp,
104                                        const char* tooltip, float* var, float val,
105                                        float low, float up, float step, const value_pair* values) {
106     param_opts opts(tp, id, name);
107     if (opts.shared) {
108         if (pmap->hasId(id)) {
109             gx_engine::Parameter& p = (*pmap)[id];
110 #ifndef NDEBUG
111             gx_engine::FloatParameter p2(
112                 id, opts.name, (values ? gx_engine::Parameter::Enum :
113                 (tp[0] == 'B' ? Parameter::Switch : gx_engine::Parameter::Continuous)),
114                 true, p.getFloat().value, val, low, up, step, true, false);
115             p2.set_desc(tooltip);
116             gx_engine::compare_parameter("Alias Parameter", &p, &p2);
117 #endif
118             return p.getFloat().value;
119         }
120     }
121     gx_engine::Parameter *p = 0;
122     if (values) {
123         assert(!opts.log);
124         p = pmap->reg_enum_par(id, opts.name, values, var, val, low, !opts.nomidi);
125     } else {
126         if (tp[0] == 'S') {
127             p = pmap->reg_par(id, opts.name, var, val, low, up, step, !opts.nomidi);
128             if (opts.log) {
129                 assert(step > 1 || opts.output);
130                 p->set_log_display();
131             }
132             if (opts.maxlevel) {
133                 p->setMaxlevel(true);
134             }
135         } else if (tp[0] == 'B') {
136             p = pmap->reg_par(id, opts.name, var, val, !opts.nomidi);
137         }
138     }
139     opts.set_common(p, tooltip);
140     return var;
141 }
142 
registerIntVar_(const char * id,const char * name,const char * tp,const char * tooltip,int * var,int val,int low,int up,const value_pair * values)143 int *ParamRegImpl::registerIntVar_(const char* id, const char* name, const char* tp,
144                                    const char* tooltip, int* var, int val,
145                                    int low, int up, const value_pair* values) {
146     assert(up > low || values);
147     param_opts opts(tp, id, name);
148     assert(!opts.log);
149     if (opts.shared) {
150         if (pmap->hasId(id)) {
151             gx_engine::Parameter& p = (*pmap)[id];
152 #if 0
153 #ifndef NDEBUG
154             gx_engine::IntParameter p2(
155                 id, opts.name, (tp[0] == 'B' ? Parameter::Switch : gx_engine::Parameter::Continuous),
156                 true, p.getInt().value, val, low, up, true, false);
157             p2.set_desc(tooltip);
158             gx_engine::compare_parameter("Alias Parameter", &p, &p2);
159 #endif
160 #endif
161             return p.getInt().value;
162         }
163     }
164     gx_engine::Parameter *p = 0;
165     if (values) {
166         p = pmap->reg_enum_par(id, opts.name, values, var, val, !opts.nomidi);
167     } else {
168         if (tp[0] == 'S') {
169             p = pmap->reg_par(id, opts.name, var, val, low, up, !opts.nomidi);
170         } else if (tp[0] == 'B') {
171             p = pmap->reg_par(id, opts.name, var, val, !opts.nomidi);
172         }
173     }
174     opts.set_common(p, tooltip);
175     return var;
176 }
177 
registerBoolVar_(const char * id,const char * name,const char * tp,const char * tooltip,bool * var,bool val)178 bool *ParamRegImpl::registerBoolVar_(const char* id, const char* name, const char* tp,
179                                      const char* tooltip, bool* var, bool val) {
180     param_opts opts(tp, id, name);
181     assert(!opts.log);
182     if (opts.shared) {
183         if (pmap->hasId(id)) {
184             gx_engine::Parameter& p = (*pmap)[id];
185 #if 0
186 #ifndef NDEBUG
187             gx_engine::BoolParameter p2(
188                 id, opts.name, Parameter::Switch,
189                 true, p.getBool().value, val, true, false);
190             p2.set_desc(tooltip);
191             gx_engine::compare_parameter("Alias Parameter", &p, &p2);
192 #endif
193 #endif
194             return p.getBool().value;
195         }
196     }
197     gx_engine::Parameter *p = 0;
198     if (tp[0] == 'B') {
199         p = pmap->reg_par(id, opts.name, var, val, !opts.nomidi);
200     } else {
201         assert(false);
202     }
203     opts.set_common(p, tooltip);
204     return var;
205 }
206 
207 
208 /****************************************************************
209  ** class Plugin
210  */
211 
Plugin(PluginDef * pl)212 Plugin::Plugin(PluginDef *pl)
213     : pdef(0),
214       p_box_visible(0),
215       p_plug_visible(0),
216       p_on_off(0),
217       p_position(0),
218       p_effect_post_pre(0) {
219     set_pdef(pl);
220 }
221 
delete_plugindef_instance(PluginDef * p)222 static void delete_plugindef_instance(PluginDef *p) {
223     free((void*)(p->id));
224     free((void*)(p->name));
225     free((void*)(p->description));
226     free((void*)(p->category));
227     free((void*)(p->shortname));
228     if (p->groups) {
229         for (const char **q = p->groups; *q; q++) {
230             free((void*)(*q));
231         }
232         delete[] p->groups;
233     }
234     delete p;
235 }
236 
Plugin(gx_system::JsonParser & jp,ParamMap & pmap)237 Plugin::Plugin(gx_system::JsonParser& jp, ParamMap& pmap)
238     : pdef(0),
239       p_box_visible(0),
240       p_plug_visible(0),
241       p_on_off(0),
242       p_position(0),
243       p_effect_post_pre(0) {
244     PluginDef *p = new PluginDef();
245     p->delete_instance = delete_plugindef_instance;
246     jp.next(gx_system::JsonParser::begin_object);
247     while (jp.peek() != gx_system::JsonParser::end_object) {
248         jp.next(gx_system::JsonParser::value_key);
249         if (jp.read_kv("version", p->version) ||
250             jp.read_kv("flags", p->flags)) {
251         } else if (jp.current_value() == "id") {
252             jp.next(gx_system::JsonParser::value_string);
253             p->id = strdup(jp.current_value().c_str());
254         } else if (jp.current_value() == "name") {
255             jp.next(gx_system::JsonParser::value_string);
256             p->name = strdup(jp.current_value().c_str());
257         } else if (jp.current_value() == "groups") {
258             jp.next(gx_system::JsonParser::begin_array);
259             std::vector<std::string> v;
260             while (jp.peek() != gx_system::JsonParser::end_array) {
261                 jp.next(gx_system::JsonParser::value_string);
262                 v.push_back(jp.current_value());
263             }
264             jp.next(gx_system::JsonParser::end_array);
265             const char **pg  = new const char*[v.size()+1];
266             p->groups = pg;
267             for (std::vector<std::string>::iterator i = v.begin(); i != v.end(); ++i) {
268                 *pg++ = strdup(i->c_str());
269             }
270             *pg++ = 0;
271         } else if (jp.current_value() == "description") {
272             jp.next(gx_system::JsonParser::value_string);
273             p->description = strdup(jp.current_value().c_str());
274         } else if (jp.current_value() == "category") {
275             jp.next(gx_system::JsonParser::value_string);
276             p->category = strdup(jp.current_value().c_str());
277         } else if (jp.current_value() == "shortname") {
278             jp.next(gx_system::JsonParser::value_string);
279             p->shortname = strdup(jp.current_value().c_str());
280         }
281     }
282     jp.next(gx_system::JsonParser::end_object);
283     p->flags &= ~PGNI_UI_REG;
284     std::string s = p->id;
285     std::string id = "ui."+s;
286     if (pmap.hasId(id)) {
287         p_box_visible = &pmap[id].getBool();
288     }
289     id = s+".s_h";
290     if (pmap.hasId(id)) {
291         p_plug_visible = &pmap[id].getBool();
292     }
293     p_on_off = &pmap[s+".on_off"].getBool();
294     p_position = &pmap[s+".position"].getInt();
295     p_effect_post_pre = &pmap[s+".pp"].getInt();
296     set_pdef(p);
297 }
298 
writeJSON(gx_system::JsonWriter & jw)299 void Plugin::writeJSON(gx_system::JsonWriter& jw) {
300     jw.begin_object();
301     jw.write_kv("version", pdef->version);
302     jw.write_kv("flags", pdef->flags); //FIXME
303     jw.write_kv("id", pdef->id);
304     if (pdef->name) {
305         jw.write_kv("name", pdef->name);
306     }
307     if (pdef->groups) {
308         jw.write_key("groups");
309         jw.begin_array();
310         for (const char **p = pdef->groups; *p; p++) {
311             jw.write(*p);
312         }
313         jw.end_array();
314     }
315     if (pdef->description) {
316         jw.write_kv("description", pdef->description);
317     }
318     if (pdef->category) {
319         jw.write_kv("category", pdef->category);
320     }
321     if (pdef->shortname) {
322         jw.write_kv("shortname", pdef->shortname);
323     }
324     jw.end_object();
325 }
326 
set_midi_on_off_blocked(bool v)327 void Plugin::set_midi_on_off_blocked(bool v) {
328     p_on_off->set_midi_blocked(!v);
329 }
330 
register_vars(ParamMap & param,EngineControl & seq)331 void Plugin::register_vars(ParamMap& param, EngineControl& seq) {
332     string s = pdef->id;
333     p_on_off = param.reg_par(s+".on_off",N_("on/off"), (bool*)0, !(pdef->flags & (PGN_GUI|PGN_ALTERNATIVE)));
334     if (!(pdef->load_ui || (pdef->flags & PGN_GUI))) {
335         p_on_off->setSavable(false);
336     }
337     p_on_off->signal_changed_bool().connect(
338         sigc::hide(sigc::mem_fun(seq, &EngineControl::set_rack_changed)));
339     if ((pdef->load_ui || pdef->flags & PGN_GUI) &&
340         (pdef->flags & PGNI_DYN_POSITION || !(pdef->flags & PGN_FIXED_GUI))) {
341         p_box_visible = param.reg_non_midi_par("ui." + s, (bool*)0, true);
342         p_plug_visible = param.reg_non_midi_par(s + ".s_h", (bool*)0, false);
343         p_box_visible->signal_changed_bool().connect(
344             sigc::mem_fun(this, &Plugin::set_midi_on_off_blocked));
345         p_on_off->set_midi_blocked(true);
346     }
347     p_position = param.reg_non_midi_par(s + ".position", (int*)0, true, pos_tmp, -9999, 9999);
348     int pp = (pdef->flags & PGN_POST ? 0 : 1);
349     bool savable = false;
350     if (pdef->flags & PGNI_DYN_POSITION) {
351         // PLUGIN_POS_RACK .. PLUGIN_POS_POST_START-1
352         p_position->signal_changed_int().connect(
353             sigc::hide(sigc::mem_fun(seq, &EngineControl::set_rack_changed)));
354         if (pdef->mono_audio || (pdef->flags & PGN_POST_PRE)) {
355             if (pdef->flags & PGN_PRE) {
356                 pp = 1;
357             } else if (pdef->flags & PGN_POST) {
358                 pp = 0;
359             } else {
360                 savable = true;
361             }
362         }
363     } else {
364         p_position->setSavable(false);
365     }
366     static const value_pair post_pre[] = {{N_("post")}, {N_("pre")}, {0}};
367     p_effect_post_pre = param.reg_enum_par(s + ".pp", "select", post_pre, (int*)0, pp);
368     p_effect_post_pre->setSavable(savable);
369     if (savable) {
370         p_effect_post_pre->signal_changed_int().connect(
371             sigc::hide(sigc::mem_fun(seq, &EngineControl::set_rack_changed)));
372     }
373 }
374 
copy_position(const Plugin & plugin)375 void Plugin::copy_position(const Plugin& plugin) {
376     set_position(plugin.get_position());
377     set_effect_post_pre(plugin.get_effect_post_pre());
378 }
379 
380 
381 /****************************************************************
382  ** class PluginList
383  */
384 
PluginListBase()385 PluginListBase::PluginListBase() : pmap() {}
386 
cleanup()387 void PluginListBase::cleanup() {
388     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); ++p) {
389         PluginDef *pdef = p->second->get_pdef();
390         if (!(pdef->flags & PGNI_NOT_OWN)) {
391             if (pdef->delete_instance) {
392                 pdef->delete_instance(pdef);
393             }
394             delete p->second;
395         }
396     }
397     pmap.clear();
398 }
399 
~PluginListBase()400 PluginListBase::~PluginListBase() {
401     cleanup();
402 }
403 
PluginList(EngineControl & seq_)404 PluginList::PluginList(EngineControl& seq_)
405     : PluginListBase(), seq(seq_) {
406     plugin_pos[PLUGIN_POS_START]       = -1000;
407     plugin_pos[PLUGIN_POS_RACK]        = 1;
408     plugin_pos[PLUGIN_POS_END]         = 1000;
409     plugin_pos[PLUGIN_POS_RACK_STEREO] = 1;
410 };
411 
~PluginList()412 PluginList::~PluginList() {
413 }
414 
find_plugin(const std::string & id) const415 Plugin *PluginListBase::find_plugin(const std::string& id) const {
416     pluginmap::const_iterator p = pmap.find(id);
417     if (p == pmap.end()) {
418         return 0;
419     }
420     return p->second;
421 }
422 
lookup_plugin(const std::string & id) const423 Plugin *PluginListBase::lookup_plugin(const std::string& id) const {
424     Plugin *p = find_plugin(id);
425     if (!p) {
426         gx_print_fatal(
427             _("lookup plugin"),
428             boost::format("id not found: %1%") % id);
429     }
430     return p;
431 }
432 
load_library(const string & path,PluginPos pos)433 int PluginList::load_library(const string& path, PluginPos pos) {
434     void* handle = dlopen(path.c_str(), RTLD_LOCAL|RTLD_NOW);
435     if (!handle) {
436         gx_print_error(
437             _("Plugin Loader"),
438             boost::format(_("Cannot open library: %1%")) % dlerror());
439         return -1;
440     }
441     dlerror();    // reset errors
442     plugin_inifunc get_gx_plugin = (plugin_inifunc) dlsym(handle, "get_gx_plugin");
443     const char *dlsym_error = dlerror();
444     if (dlsym_error) {
445         gx_print_error(
446             _("Plugin Loader"),
447             boost::format(_("Cannot load symbol 'get_gx_plugin': %1%")) % dlsym_error);
448         dlclose(handle);
449         return -1;
450     }
451     int n = get_gx_plugin(0, 0);
452     if (n <= 0) {
453         return -1;
454     }
455     int cnt = 0;
456     for (int i = 0; i < n; i++) {
457         PluginDef *p;
458         if (get_gx_plugin(i, &p) < 0) {
459             continue;
460         }
461         if (!add(p, pos)) {
462             cnt++;
463             gx_print_info(_("Plugin Loader"), Glib::ustring::compose("loaded[%1]: %2", path, p->id));
464         }
465     }
466     return cnt;
467 }
468 
load_from_path(const string & path,PluginPos pos)469 int PluginList::load_from_path(const string& path, PluginPos pos) {
470     DIR *dp;
471     struct dirent *dirp;
472     if((dp = opendir(path.c_str())) == NULL) {
473         gx_print_warning(
474             _("Plugin Loader"),
475             boost::format(_("Error opening '%1%'")) % path);
476         return -1;
477     }
478     int cnt = 0;
479     while ((dirp = readdir(dp)) != NULL) {
480         string n = dirp->d_name;
481         if (n.size() > 3 && n.compare(n.size()-3,3,".so") == 0) {
482             int res = load_library(path+n, pos);
483             if (res > 0) {
484                 cnt += res;
485             }
486         }
487     }
488     closedir(dp);
489     return cnt;
490 }
491 
check_version(PluginDef * p)492 int PluginList::check_version(PluginDef *p) {
493     if (((p->version & PLUGINDEF_VERMAJOR_MASK) == (PLUGINDEF_VERSION & PLUGINDEF_VERMAJOR_MASK)) &&
494         ((p->version & PLUGINDEF_VERMINOR_MASK) <= (PLUGINDEF_VERSION & PLUGINDEF_VERMINOR_MASK))) {
495         return 0;
496     }
497     gx_print_error(
498         _("Plugin Loader"),
499         boost::format(_("Plugin '%1%' has wrong version %2$#4x (current version: %3$#4x)"))
500         % p->id % p->version % PLUGINDEF_VERSION);
501     return -1;
502 }
503 
delete_module(Plugin * pl)504 void PluginListBase::delete_module(Plugin *pl) {
505     PluginDef *p = pl->get_pdef();
506     insert_remove(p->id, false);
507 #ifndef NDEBUG // avoid unused variable compiler warning
508     size_t n = pmap.erase(p->id);
509     assert(n == 1);
510 #else
511     pmap.erase(p->id);
512 #endif
513     if (!(p->flags & PGNI_NOT_OWN)) {
514         if (p->delete_instance) {
515             p->delete_instance(p);
516         }
517         delete pl;
518     }
519 }
520 
insert_plugin(Plugin * pvars)521 int PluginListBase::insert_plugin(Plugin *pvars) {
522     const char *id = pvars->get_pdef()->id;
523     pair<pluginmap::iterator,bool> ret = pmap.insert(map_pair(id, pvars));
524     if (!ret.second) {
525         gx_print_error(
526             _("Plugin Loader"),
527             boost::format(_("Plugin '%1%' already exists: skipped")) % id);
528         return -1;
529     }
530     insert_remove(id, true);
531     return 0;
532 }
533 
update_plugin(Plugin * pvars)534 void PluginListBase::update_plugin(Plugin *pvars) {
535     pmap[pvars->get_pdef()->id]->set_pdef(pvars->get_pdef());
536 }
537 
add_module(Plugin * pvars,PluginPos pos,int flags)538 int PluginList::add_module(Plugin *pvars, PluginPos pos, int flags) {
539     const int mode_mask = (PGN_MODE_NORMAL|PGN_MODE_BYPASS|PGN_MODE_MUTE);  // all mode bits
540     PluginDef *p = pvars->get_pdef();
541     p->flags |= flags;
542     if (!(p->flags & mode_mask)) {
543         p->flags |= PGN_MODE_NORMAL;
544     }
545     if (p->stereo_audio) {
546         p->flags |= PGN_STEREO;
547     }
548     if (p->load_ui) {
549         p->flags |= PGN_GUI;
550     }
551     int ipos = pos;
552     if (ipos == PLUGIN_POS_RACK) {
553         p->flags |= PGNI_DYN_POSITION;
554         if (p->flags & PGN_STEREO) {
555             ipos = PLUGIN_POS_RACK_STEREO;
556         }
557     }
558     if (pvars->p_position) {
559         pvars->set_position(plugin_pos[ipos]);
560     } else {
561         pvars->pos_tmp = plugin_pos[ipos];
562     }
563     int ret = insert_plugin(pvars);
564     if (ret != 0) {
565         return ret;
566     }
567     if (!(p->flags & PGN_ALTERNATIVE)) {
568         // normal case: position will not be set by ModuleSelector
569         plugin_pos[ipos]++;
570     }
571     return 0;
572 }
573 
add(Plugin * pvars,PluginPos pos,int flags)574 int PluginList::add(Plugin *pvars, PluginPos pos, int flags) {
575     if (check_version(pvars->get_pdef()) != 0) {
576         return -1;
577     }
578     return add_module(pvars, pos, flags|PGNI_NOT_OWN);
579 }
580 
add(PluginDef * p,PluginPos pos,int flags)581 Plugin *PluginList::add(PluginDef *p, PluginPos pos, int flags) {
582     if (check_version(p) != 0) {
583         return 0;
584     }
585     Plugin *pl = new Plugin(p);
586     int ret = add_module(pl, pos, flags);
587     if (ret != 0) {
588         delete pl;
589         return 0;
590     }
591     return pl;
592 }
593 
add(PluginDef ** p,PluginPos pos,int flags)594 int PluginList::add(PluginDef **p, PluginPos pos, int flags) {
595     int count = 0;
596     while (*p) {
597         if (add(*p++, pos, flags) == 0) {
598             count++;
599         }
600     }
601     return count;
602 }
603 
add(plugindef_creator * p,PluginPos pos,int flags)604 int PluginList::add(plugindef_creator *p, PluginPos pos, int flags) {
605     int count = 0;
606     while (*p) {
607         if (add((*p++)(), pos, flags) == 0) {
608             count++;
609         }
610     }
611     return count;
612 }
613 
tr_name(const char * name)614 static const char* tr_name(const char *name) {
615     if (name && name[0]) {
616         return gettext(name);
617     }
618     return "";
619 }
620 
registerGroup(PluginDef * pd,ParameterGroups & groups)621 void PluginList::registerGroup(PluginDef *pd, ParameterGroups& groups) {
622     groups.insert(pd->id, tr_name(pd->name));
623     const char **gp = pd->groups;
624     if (gp) {
625         while (*gp) {
626             string id = *gp++;
627             const char *name = *gp++;
628             if (!name) {
629                 break;
630             }
631             if (id[0] == '.') {
632                 id = id.substr(1);
633             } else {
634                 id = string(pd->id) + "." + id;
635             }
636             groups.insert(id, tr_name(name));
637         }
638     }
639 }
640 
unregisterGroup(PluginDef * pd,ParameterGroups & groups)641 void PluginList::unregisterGroup(PluginDef *pd, ParameterGroups& groups) {
642     groups.erase(pd->id);
643     const char **gp = pd->groups;
644     if (gp) {
645         while (*gp) {
646             string id = *gp++;
647             const char *name = *gp++;
648             if (!name) {
649                 break;
650             }
651             if (id[0] == '.') {
652                 id = id.substr(1);
653             } else {
654                 id = string(pd->id) + "." + id;
655             }
656             groups.erase(id);
657         }
658     }
659 }
660 
rescueParameter(Plugin * pl,ParamMap & param)661 void PluginList::rescueParameter(Plugin *pl, ParamMap& param) {
662     PluginDef *pdef = pl->get_pdef();
663     string s = pdef->id;
664     param.unregister(pl->p_on_off);
665     pl->p_on_off = param.reg_par(s+".on_off",N_("on/off"), (bool*)0, !(pdef->flags & (PGN_GUI|PGN_ALTERNATIVE)));
666     if (!(pdef->load_ui || (pdef->flags & PGN_GUI))) {
667         pl->p_on_off->setSavable(false);
668     }
669     pl->p_on_off->signal_changed_bool().connect(
670         sigc::hide(sigc::mem_fun(seq, &EngineControl::set_rack_changed)));
671 }
672 
registerParameter(Plugin * pl,ParamMap & param,ParamRegImpl & preg)673 void PluginList::registerParameter(Plugin *pl, ParamMap& param, ParamRegImpl& preg) {
674     pl->register_vars(param, seq);
675     PluginDef *pd = pl->get_pdef();
676     if (pd->register_params) {
677         preg.plugin = pd;
678         pd->register_params(preg);
679     }
680 }
681 
unregisterParameter(Plugin * pl,ParamMap & param)682 void PluginList::unregisterParameter(Plugin *pl, ParamMap& param) {
683     PluginDef *pd = pl->get_pdef();
684     param.unregister(pl->p_on_off);
685     param.unregister(pl->p_position);
686     param.unregister(pl->p_box_visible);
687     param.unregister(pl->p_plug_visible);
688     param.unregister(pl->p_effect_post_pre);
689     std::vector<const std::string*> l;
690     if (pd->register_params) {
691         string s = pd->id;
692         s += ".";
693         for (ParamMap::iterator i = param.begin(); i != param.end(); ++i) {
694             if (i->first.compare(0, s.size(), s) == 0) {
695                 assert(i->second->isInPreset());
696                 l.push_back(&i->first);
697             }
698         }
699     }
700     for (std::vector<const std::string*>::iterator i = l.begin(); i != l.end(); ++i) {
701         param.unregister(**i);
702     }
703 }
704 
registerPlugin(Plugin * pl,ParamMap & param,ParameterGroups & groups)705 void PluginList::registerPlugin(Plugin *pl, ParamMap& param, ParameterGroups& groups) {
706     registerGroup(pl->get_pdef(), groups);
707     ParamRegImpl preg(&param);
708     registerParameter(pl, param, preg);
709 }
710 
unregisterPlugin(Plugin * pl,ParamMap & param,ParameterGroups & groups)711 void PluginList::unregisterPlugin(Plugin *pl, ParamMap& param, ParameterGroups& groups) {
712     ParamRegImpl preg(&param);
713     unregisterParameter(pl, param);
714     unregisterGroup(pl->get_pdef(), groups);
715 }
716 
registerAllPlugins(ParamMap & param,ParameterGroups & groups)717 void PluginList::registerAllPlugins(ParamMap& param, ParameterGroups& groups) {
718     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
719         registerGroup(p->second->get_pdef(), groups);
720     }
721     ParamRegImpl preg(&param);
722     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
723         registerParameter(p->second, param, preg);
724     }
725 }
726 
append_rack(UiBuilderBase & ui)727 void PluginListBase::append_rack(UiBuilderBase& ui) {
728     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
729         ui.load(p->second);
730     }
731 }
732 
plugin_order(Plugin * p1,Plugin * p2)733 static bool plugin_order(Plugin* p1, Plugin* p2) {
734     return p1->position_weight() < p2->position_weight();
735 }
736 
ordered_mono_list(list<Plugin * > & mono,int mode)737 void PluginList::ordered_mono_list(list<Plugin*>& mono, int mode) {
738     mono.clear();
739     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
740         Plugin *pl = p->second;
741         if (pl->get_on_off() && pl->get_pdef()->mono_audio && (pl->get_pdef()->flags & mode)) {
742             mono.push_back(pl);
743         }
744     }
745     mono.sort(plugin_order);
746 
747     // print active plugins
748     // for (list<Plugin*>::const_iterator i = mono.begin(); i != mono.end(); ++i) {
749     // printf("mono_list %s\n", (*i)->get_pdef()->id);
750     // }
751     // printf("\n");
752 }
753 
ordered_stereo_list(list<Plugin * > & stereo,int mode)754 void PluginList::ordered_stereo_list(list<Plugin*>& stereo, int mode) {
755     stereo.clear();
756     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
757         Plugin *pl = p->second;
758         if (pl->get_on_off() && pl->get_pdef()->stereo_audio && (pl->get_pdef()->flags & mode)) {
759             stereo.push_back(pl);
760         }
761     }
762     stereo.sort(plugin_order);
763 }
764 
ordered_list(list<Plugin * > & l,bool stereo,int flagmask,int flagvalue)765 void PluginList::ordered_list(list<Plugin*>& l, bool stereo, int flagmask, int flagvalue) {
766     flagmask |= (PGN_STEREO | PGN_MODE_NORMAL);
767     if (stereo) {
768         flagvalue |= PGN_STEREO;
769     }
770     flagvalue |= PGN_MODE_NORMAL;
771     l.clear();
772     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
773         PluginDef *pd = p->second->get_pdef();
774         if (((pd->flags & flagmask) == flagvalue) || (!stereo && strcmp(pd->id, "ampstack") == 0)) {
775             l.push_back(p->second);
776         }
777     }
778     l.sort(plugin_order);
779 }
780 
writeJSON(gx_system::JsonWriter & jw)781 void PluginListBase::writeJSON(gx_system::JsonWriter& jw) {
782     jw.begin_array();
783     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
784         p->second->writeJSON(jw);
785     }
786     jw.end_array();
787 }
788 
readJSON(gx_system::JsonParser & jp,ParamMap & param)789 void PluginListBase::readJSON(gx_system::JsonParser& jp, ParamMap& param) {
790     jp.next(gx_system::JsonParser::begin_array);
791     while (jp.peek() != gx_system::JsonParser::end_array) {
792         Plugin *p = new Plugin(jp, param);
793         pmap.insert(map_pair(p->get_pdef()->id, p));
794         insert_remove(p->get_pdef()->id, true);
795     }
796     jp.next(gx_system::JsonParser::end_array);
797 }
798 
set_samplerate(int samplerate)799 void PluginList::set_samplerate(int samplerate) {
800     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
801         inifunc f = p->second->get_pdef()->set_samplerate;
802         if (f) {
803             f(samplerate, p->second->get_pdef());
804         }
805     }
806 }
807 
808 #ifndef NDEBUG
printlist(bool order)809 void PluginList::printlist(bool order) {
810     list<Plugin*> pl_mono, pl_stereo;
811     for (pluginmap::iterator p = pmap.begin(); p != pmap.end(); p++) {
812         if (p->second->get_pdef()->flags & PGN_STEREO) {
813             pl_stereo.push_back(p->second);
814         } else {
815             pl_mono.push_back(p->second);
816         }
817     }
818     if (order) {
819         pl_mono.sort(plugin_order);
820         pl_stereo.sort(plugin_order);
821     }
822     gx_engine::printlist("Plugin Map", pl_mono);
823     gx_engine::printlist(0, pl_stereo, false);
824 }
825 
printlist(const char * title,const list<Plugin * > & modules,bool header)826 void printlist(const char *title, const list<Plugin*>& modules, bool header) {
827     if (!getenv("GUITARIX_MODULE_DEBUG")) {
828         return;
829     }
830     const char *fmth = "%1s %-25s %5s %5s %3s %2s %3s %8s\n";
831     const char *fmtl = "%1s %-25s %5d %5d %3d %2d %3d %8s\n";
832     static int cnt = 0;
833     if (header) {
834         printf("%d %s:\n", ++cnt, title);
835         printf(fmth, "F", "id","wght","pos","pre","on","vis","channels");
836     } else {
837         printf("\n");
838     }
839     for (list<Plugin*>::const_iterator i = modules.begin(); i != modules.end(); ++i) {
840         Plugin *p = *i;
841         PluginDef *pd = p->get_pdef();
842         const char *c = "-";
843         if (pd->mono_audio) {
844             c = "mono";
845         } else if (pd->stereo_audio) {
846             c = "stereo";
847         }
848         const char *f;
849         if (pd->flags & PGN_GUI) {
850             f = "";
851         } else if (pd->flags & PGN_ALTERNATIVE) {
852             f = "A";
853         } else {
854             f = "-";
855         }
856         printf(fmtl, f, pd->id, p->position_weight(), p->get_position(),
857                p->get_effect_post_pre(), p->get_on_off(),
858                (p->p_box_visible ? p->get_box_visible() : false), c);
859     }
860 }
861 #endif
862 
863 } // !namespace gx_engine
864