1 /*
2   ZynAddSubFX - a software synthesizer
3 
4   DSSIaudiooutput.cpp - Audio functions for DSSI
5   Copyright (C) 2002 Nasca Octavian Paul
6   Author: Nasca Octavian Paul
7 
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License
10   as published by the Free Software Foundation; either version 2
11   of the License, or (at your option) any later version.
12 */
13 
14 /*
15  * Initial working DSSI output code contributed by Stephen G. Parry
16  */
17 
18 //this file contains code used from trivial_synth.c from
19 //the DSSI (published by Steve Harris under public domain) as a template.
20 
21 #include "DSSIaudiooutput.h"
22 #include "../Misc/Master.h"
23 #include "../Misc/Util.h"
24 #include <unistd.h>
25 #include <limits.h>
26 
27 using std::set;
28 using std::string;
29 using std::vector;
30 
31 //Dummy variables and functions for linking purposes
32 const char *instance_name = 0;
33 
34 namespace zyn {
35 class WavFile;
36 namespace Nio {
start(void)37     bool start(void){return 1;};
stop(void)38     void stop(void){};
masterSwap(zyn::Master *)39     void masterSwap(zyn::Master *){};
waveNew(WavFile *)40     void waveNew(WavFile *){}
waveStart(void)41     void waveStart(void){}
waveStop(void)42     void waveStop(void){}
waveEnd(void)43     void waveEnd(void){}
setSource(string)44     bool setSource(string){return true;}
setSink(string)45     bool setSink(string){return true;}
getSources(void)46     set<string> getSources(void){return set<string>();}
getSinks(void)47     set<string> getSinks(void){return set<string>();}
getSource(void)48     string getSource(void){return "";}
getSink(void)49     string getSink(void){return "";}
setAudioCompressor(bool)50     void setAudioCompressor(bool){}
getAudioCompressor(void)51     bool getAudioCompressor(void){return false;}
52 }
53 } // namespace zyn
54 
55 //
56 // Static stubs for LADSPA member functions
57 //
58 // LADSPA is essentially a C handle based API; This plug-in implementation is
59 // a C++ OO one so we need stub functions to map from C API calls to C++ object
60 // method calls.
stub_connectPort(LADSPA_Handle instance,unsigned long port,LADSPA_Data * data)61 void DSSIaudiooutput::stub_connectPort(LADSPA_Handle instance,
62                                        unsigned long port,
63                                        LADSPA_Data *data)
64 {
65     getInstance(instance)->connectPort(port, data);
66 }
67 
stub_activate(LADSPA_Handle instance)68 void DSSIaudiooutput::stub_activate(LADSPA_Handle instance)
69 {
70     getInstance(instance)->activate();
71 }
72 
stub_run(LADSPA_Handle instance,unsigned long sample_count)73 void DSSIaudiooutput::stub_run(LADSPA_Handle instance,
74                                unsigned long sample_count)
75 {
76     getInstance(instance)->run(sample_count);
77 }
78 
stub_deactivate(LADSPA_Handle instance)79 void DSSIaudiooutput::stub_deactivate(LADSPA_Handle instance)
80 {
81     getInstance(instance)->deactivate();
82 }
83 
84 
stub_cleanup(LADSPA_Handle instance)85 void DSSIaudiooutput::stub_cleanup(LADSPA_Handle instance)
86 {
87     DSSIaudiooutput *plugin_instance = getInstance(instance);
88     plugin_instance->cleanup();
89     delete plugin_instance;
90 }
91 
92 
ladspa_descriptor(unsigned long index)93 const LADSPA_Descriptor *ladspa_descriptor(unsigned long index)
94 {
95     return DSSIaudiooutput::getLadspaDescriptor(index);
96 }
97 
98 //
99 // Static stubs for DSSI member functions
100 //
101 // DSSI is essentially a C handle based API; This plug-in implementation is
102 // a C++ OO one so we need stub functions to map from C API calls to C++ object
103 // method calls.
stub_getProgram(LADSPA_Handle instance,unsigned long index)104 const DSSI_Program_Descriptor *DSSIaudiooutput::stub_getProgram(
105     LADSPA_Handle instance,
106     unsigned long index)
107 {
108     return getInstance(instance)->getProgram(index);
109 }
110 
stub_selectProgram(LADSPA_Handle instance,unsigned long bank,unsigned long program)111 void DSSIaudiooutput::stub_selectProgram(LADSPA_Handle instance,
112                                          unsigned long bank,
113                                          unsigned long program)
114 {
115     getInstance(instance)->selectProgram(bank, program);
116 }
117 
stub_getMidiControllerForPort(LADSPA_Handle instance,unsigned long port)118 int DSSIaudiooutput::stub_getMidiControllerForPort(LADSPA_Handle instance,
119                                                    unsigned long port)
120 {
121     return getInstance(instance)->getMidiControllerForPort(port);
122 }
123 
stub_runSynth(LADSPA_Handle instance,unsigned long sample_count,snd_seq_event_t * events,unsigned long event_count)124 void DSSIaudiooutput::stub_runSynth(LADSPA_Handle instance,
125                                     unsigned long sample_count,
126                                     snd_seq_event_t *events,
127                                     unsigned long event_count)
128 {
129     getInstance(instance)->runSynth(sample_count, events, event_count);
130 }
131 
dssi_descriptor(unsigned long index)132 const DSSI_Descriptor *dssi_descriptor(unsigned long index)
133 {
134     return DSSIaudiooutput::getDssiDescriptor(index);
135 }
136 
137 //
138 // LADSPA member functions
139 //
140 
141 /**
142  * Instantiates a plug-in.
143  *
144  * This LADSPA member function instantiates a plug-in.
145  * Note that instance initialisation should generally occur in
146  * activate() rather than here.
147  *
148  * Zyn Implementation
149  * ------------------
150  * This implementation creates a C++ class object and hides its pointer
151  * in the handle by type casting.
152  *
153  * @param descriptor [in] the descriptor for this plug-in
154  * @param s_rate [in] the sample rate
155  * @return the plug-in instance handle if successful else NULL
156  */
instantiate(const LADSPA_Descriptor * descriptor,unsigned long s_rate)157 LADSPA_Handle DSSIaudiooutput::instantiate(const LADSPA_Descriptor *descriptor,
158                                            unsigned long s_rate)
159 {
160     if(descriptor->UniqueID == dssiDescriptor->LADSPA_Plugin->UniqueID)
161         return (LADSPA_Handle)(new DSSIaudiooutput(s_rate));
162     else
163         return NULL;
164 }
165 
166 /**
167  * Connects a port on an instantiated plug-in.
168  *
169  * This LADSPA member function connects a port on an instantiated plug-in to a
170  * memory location at which a block of data for the port will be read/written.
171  * The data location is expected to be an array of LADSPA_Data for audio ports
172  * or a single LADSPA_Data value for control ports. Memory issues will be
173  * managed by the host. The plug-in must read/write the data at these locations
174  * every time run() or run_adding() is called and the data present at the time
175  * of this connection call should not be considered meaningful.
176  *
177  * Zyn Implementation
178  * ------------------
179  * The buffer pointers are stored as member variables
180  *
181  * @param port [in] the port to be connected
182  * @param data [in] the data buffer to write to / read from
183  */
connectPort(unsigned long port,LADSPA_Data * data)184 void DSSIaudiooutput::connectPort(unsigned long port, LADSPA_Data *data)
185 {
186     switch(port) {
187         case 0:
188             outl = data;
189             break;
190         case 1:
191             outr = data;
192             break;
193         default:
194             if ( port - 2 < DSSIControlDescription::MAX_DSSI_CONTROLS ) {
195                 dssi_control[port - 2].data = data;
196             }
197             break;
198     }
199 }
200 
201 /**
202  * Initialises a plug-in instance and activates it for use.
203  *
204  * This LADSPA member function initialises a plug-in instance and activates it
205  * for use. This is separated from instantiate() to aid real-time support and
206  * so that hosts can reinitialise a plug-in instance by calling deactivate() and
207  * then activate(). In this case the plug-in instance must reset all state
208  * information dependent on the history of the plug-in instance except for any
209  * data locations provided by connect_port() and any gain set by
210  * set_run_adding_gain().
211  *
212  * Zyn Implementation
213  * ------------------
214  * Currently this does nothing; Care must be taken as to code placed here as
215  * too much code here seems to cause time-out problems in jack-dssi-host.
216 */
activate()217 void DSSIaudiooutput::activate()
218 {}
219 
220 /**
221  * Runs an instance of a plug-in for a block.
222  *
223  * This LADSPA member function runs an instance of a plug-in for a block.
224  * Note that if an activate() function exists then it must be called before
225  * run() or run_adding(). If deactivate() is called for a plug-in instance then
226  * the plug-in instance may not be reused until activate() has been called again.
227  *
228  * Zyn Implementation
229  * ------------------
230  * This is a LADSPA function that does not process any MIDI events; it is hence
231  * implemented by simply calling runSynth() with an empty event list.
232  *
233  * @param sample_count [in] the block size (in samples) for which the plug-in instance may run
234  */
run(unsigned long sample_count)235 void DSSIaudiooutput::run(unsigned long sample_count)
236 {
237     runSynth(sample_count, NULL, (unsigned long)0);
238 }
239 
240 /**
241  * Counterpart to activate().
242  *
243  * This LADSPA member function is the counterpart to activate() (see above).
244  * Deactivation is not similar to pausing as the plug-in instance will be
245  * reinitialised when activate() is called to reuse it.
246  *
247  * Zyn Implementation
248  * ------------------
249  * Currently this function does nothing.
250  */
deactivate()251 void DSSIaudiooutput::deactivate()
252 {}
253 
254 /**
255  * Deletes a plug-in instance that is no longer required.
256  *
257  * LADSPA member function; once an instance of a plug-in has been finished with
258  * it can be deleted using this function. The instance handle ceases to be
259  * valid after this call.
260  *
261  * If activate() was called for a plug-in instance then a corresponding call to
262  * deactivate() must be made before cleanup() is called.
263  *
264  * Zyn Implementation
265  * ------------------
266  * Currently cleanup is deferred to the destructor that is invoked after cleanup()
267  */
cleanup()268 void DSSIaudiooutput::cleanup()
269 {}
270 
271 /**
272  * Initial entry point for the LADSPA plug-in library.
273  *
274  * This LADSPA function is the initial entry point for the plug-in library.
275  * The LADSPA host looks for this entry point in each shared library object it
276  * finds and then calls the function to enumerate the plug-ins within the
277  * library.
278  *
279  * Zyn Implementation
280  * ------------------
281  * As the Zyn plug-in is a DSSI plug-in, the LADSPA descriptor is embedded inside
282  * the DSSI descriptor, which is created by DSSIaudiooutput::initDssiDescriptor()
283  * statically when the library is loaded. This function then merely returns a pointer
284  * to that embedded descriptor.
285  *
286  * @param index [in] the index number of the plug-in within the library.
287  * @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL
288  */
getLadspaDescriptor(unsigned long index)289 const LADSPA_Descriptor *DSSIaudiooutput::getLadspaDescriptor(
290     unsigned long index)
291 {
292     if((index > 0) || (dssiDescriptor == NULL))
293         return NULL;
294     else
295         return dssiDescriptor->LADSPA_Plugin;
296 }
297 
298 //
299 // DSSI member functions
300 //
301 
302 /**
303  * Provides a description of a program available on this synth.
304  *
305  * This DSSI member function pointer provides a description of a program (named
306  * preset sound) available on this synth.
307  *
308  * Zyn Implementation
309  * ------------------
310  * The instruments in all Zyn's bank directories, as shown by the `instrument
311  * -> show instrument bank` command, are enumerated to the host by this
312  * function, allowing access to all those instruments.
313  * The first time an instrument is requested, the bank it is in and any
314  * unmapped ones preceding that are mapped; all the instruments names and
315  * filenames from those banks are stored in the programMap member variable for
316  * later use. This is done on demand in this way, rather than up front in one
317  * go because loading all the instrument names in one go can lead to timeouts
318  * and zombies.
319  *
320  * @param index [in] index into the plug-in's list of
321  * programs, not a program number as represented by the Program
322  * field of the DSSI_Program_Descriptor.  (This distinction is
323  * needed to support synths that use non-contiguous program or
324  * bank numbers.)
325  * @return a DSSI_Program_Descriptor pointer that is
326  * guaranteed to be valid only until the next call to get_program,
327  * deactivate, or configure, on the same plug-in instance, or NULL if index is out of range.
328  */
getProgram(unsigned long index)329 const DSSI_Program_Descriptor *DSSIaudiooutput::getProgram(unsigned long index)
330 {
331     static DSSI_Program_Descriptor retVal;
332 
333     /* Make sure we have the list of banks loaded */
334     initBanks();
335 
336     /* Make sure that the bank containing the instrument has been mapped */
337     while(index >= programMap.size() && mapNextBank())
338         /* DO NOTHING MORE */;
339 
340     if(index >= programMap.size())
341         /* No more instruments */
342         return NULL;
343     else {
344         /* OK, return the instrument */
345         retVal.Name    = programMap[index].name.c_str();
346         retVal.Program = programMap[index].program;
347         retVal.Bank    = programMap[index].bank;
348         return &retVal;
349     }
350 }
351 
352 /**
353  * Selects a new program for this synth.
354  *
355  * This DSSI member function selects a new program for this synth.  The program
356  * change will take effect immediately at the start of the next run_synth()
357  * call. An invalid bank / instrument combination is ignored.
358  *
359  * Zyn Implementation
360  * ------------------
361  * the banks and instruments are as shown in the `instrument -> show instrument
362  * bank` command in Zyn. The bank no is a 1-based index into the list of banks
363  * Zyn loads and shows in the drop down and the program number is the
364  * instrument within that bank.
365  *
366  * @param bank [in] the bank number to select
367  * @param program [in] the program number within the bank to select
368  */
selectProgram(unsigned long bank,unsigned long program)369 void DSSIaudiooutput::selectProgram(unsigned long bank, unsigned long program)
370 {
371     middleware->pendingSetBank(bank);
372     middleware->pendingSetProgram(0, program);
373 }
374 
375 /**
376  * Returns the MIDI controller number or NRPN for a input control port
377  *
378  * This DSSI member function returns the MIDI controller number or NRPN that
379  * should be mapped to the given input control port. If the given port should
380  * not have any MIDI controller mapped to it, the function will return DSSI_NONE.
381  * The behaviour of this function is undefined if the given port
382  * number does not correspond to an input control port.
383  *
384  * Zyn Implementation
385  * ------------------
386  * Currently Zyn does not define any controller ports, but may do in the future.
387  *
388  * @param port [in] the input controller port
389  * @return the CC and NRPN values shifted and ORed together.
390  */
getMidiControllerForPort(unsigned long)391 int DSSIaudiooutput::getMidiControllerForPort(unsigned long /*port*/)
392 {
393     return DSSI_NONE;
394 }
395 
396 /**
397  * Runs the synth for a block.
398  *
399  * This DSSI member function runs the synth for a block.  This is identical in
400  * function to the LADSPA run() function, except that it also supplies events
401  * to the synth.
402  *
403  * Zyn Implementation
404  * ------------------
405  * Zyn implements synthesis in Master::GetAudioOutSamples; runSynth calls this
406  * function in chunks delimited by the sample_count and the frame indexes in
407  * the events block, calling the appropriate NoteOn, NoteOff and SetController
408  * members of Master to process the events supplied between each chunk.
409  *
410  * @param sample_count [in] the block size (in samples) for which the synth
411  * instance may run.
412  * @param events [in] The Events pointer points to a block of ALSA
413  * sequencer events, used to communicate MIDI and related events to the synth.
414  * Each event must be timestamped relative to the start of the block,
415  * (mis)using the ALSA "tick time" field as a frame count. The host is
416  * responsible for ensuring that events with differing timestamps are already
417  * ordered by time. Must not include NOTE (only NOTE_ON / NOTE_OFF), LSB or MSB
418  * events.
419  * @param event_count [in] the number of entries in the `events` block
420  */
runSynth(unsigned long sample_count,snd_seq_event_t * events,unsigned long event_count)421 void DSSIaudiooutput::runSynth(unsigned long sample_count,
422                                snd_seq_event_t *events,
423                                unsigned long event_count)
424 {
425     unsigned long from_frame       = 0;
426     unsigned long event_index      = 0;
427     unsigned long next_event_frame = 0;
428     unsigned long to_frame = 0;
429 
430     zyn::Master *master = middleware->spawnMaster();
431 
432     // forward all dssi control values to the middleware
433     for (size_t dssi_control_index = 0;
434          dssi_control_index < DSSIControlDescription::MAX_DSSI_CONTROLS; ++dssi_control_index) {
435         dssi_control[dssi_control_index].forward_control(master);
436     }
437 
438     do {
439         /* Find the time of the next event, if any */
440         if((events == NULL) || (event_index >= event_count))
441             next_event_frame = ULONG_MAX;
442         else
443             next_event_frame = events[event_index].time.tick;
444 
445         /* find the end of the sub-sample to be processed this time round... */
446         /* if the next event falls within the desired sample interval... */
447         if((next_event_frame < sample_count) && (next_event_frame >= to_frame))
448             /* set the end to be at that event */
449             to_frame = next_event_frame;
450         else
451             /* ...else go for the whole remaining sample */
452             to_frame = sample_count;
453         if(from_frame < to_frame) {
454             // call master to fill from `from_frame` to `to_frame`:
455             master->GetAudioOutSamples(to_frame - from_frame,
456                                        (int)sampleRate,
457                                        &(outl[from_frame]),
458                                        &(outr[from_frame]));
459             // next sub-sample please...
460             from_frame = to_frame;
461         }
462 
463         // Now process any event(s) at the current timing point
464         while(events != NULL && event_index < event_count
465               && events[event_index].time.tick == to_frame) {
466             if(events[event_index].type == SND_SEQ_EVENT_NOTEON)
467                 master->noteOn(events[event_index].data.note.channel,
468                                events[event_index].data.note.note,
469                                events[event_index].data.note.velocity);
470             else
471             if(events[event_index].type == SND_SEQ_EVENT_NOTEOFF)
472                 master->noteOff(events[event_index].data.note.channel,
473                                 events[event_index].data.note.note);
474             else
475             if(events[event_index].type == SND_SEQ_EVENT_CONTROLLER)
476                 master->setController(events[event_index].data.control.channel,
477                                       events[event_index].data.control.param,
478                                       events[event_index].data.control.value);
479             else {}
480             event_index++;
481         }
482 
483         // Keep going until we have the desired total length of sample...
484     } while(to_frame < sample_count);
485 
486 }
487 
488 /**
489  * Initial entry point for the DSSI plug-in library.
490  *
491  * This DSSI function is the initial entry point for the plug-in library.
492  * The DSSI host looks for this entry point in each shared library object it
493  * finds and then calls the function to enumerate the plug-ins within the
494  * library.
495  *
496  * Zyn Implementation
497  * ------------------
498  * The descriptor is created statically by DSSIaudiooutput::initDssiDescriptor()
499  * when the plug-in library is loaded. This function merely returns a pointer to
500  * that descriptor.
501  *
502  * @param index [in] the index number of the plug-in within the library.
503  * @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL
504  */
getDssiDescriptor(unsigned long index)505 const DSSI_Descriptor *DSSIaudiooutput::getDssiDescriptor(unsigned long index)
506 {
507     if(index > 0)
508         return NULL;
509     else
510         return initDssiDescriptor();
511 }
512 
513 //
514 // Internal member functions
515 //
516 
517 // Initialise the DSSI descriptor, statically:
518 DSSI_Descriptor *DSSIaudiooutput::dssiDescriptor =
519     DSSIaudiooutput::initDssiDescriptor();
520 
521 /**
522  * Initializes the DSSI (and LADSPA) descriptor, returning it is an object.
523  */
initDssiDescriptor()524 DSSI_Descriptor *DSSIaudiooutput::initDssiDescriptor()
525 {
526     DSSI_Descriptor *newDssiDescriptor = new DSSI_Descriptor;
527 
528     LADSPA_PortDescriptor *newPortDescriptors;
529     const char **newPortNames;
530     LADSPA_PortRangeHint *newPortRangeHints;
531 
532     LADSPA_Descriptor *newLadspaDescriptor = new LADSPA_Descriptor;
533     newLadspaDescriptor->UniqueID   = 100;
534     newLadspaDescriptor->Label      = "ZASF";
535     newLadspaDescriptor->Properties = 0;
536     newLadspaDescriptor->Name  = "ZynAddSubFX";
537     newLadspaDescriptor->Maker =
538         "Nasca Octavian Paul <zynaddsubfx@yahoo.com>";
539     newLadspaDescriptor->Copyright = "GNU General Public License v2 or later";
540     newLadspaDescriptor->PortCount = 2 + DSSIControlDescription::MAX_DSSI_CONTROLS;
541     newPortNames    = new const char *[newLadspaDescriptor->PortCount];
542     newPortNames[0] = "Output L";
543     newPortNames[1] = "Output R";
544     for (size_t dssi_control_index = 0;
545          dssi_control_index < DSSIControlDescription::MAX_DSSI_CONTROLS; ++dssi_control_index) {
546         newPortNames[2 + dssi_control_index] = dssi_control_description[dssi_control_index].name;
547     }
548     newLadspaDescriptor->PortNames = newPortNames;
549     newPortDescriptors =
550         new LADSPA_PortDescriptor[newLadspaDescriptor->PortCount];
551     newPortDescriptors[0] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
552     newPortDescriptors[1] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
553     for (size_t dssi_control_index = 0;
554          dssi_control_index < DSSIControlDescription::MAX_DSSI_CONTROLS; ++dssi_control_index) {
555         newPortDescriptors[2 + dssi_control_index] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
556     }
557     newLadspaDescriptor->PortDescriptors = newPortDescriptors;
558     newPortRangeHints =
559         new LADSPA_PortRangeHint[newLadspaDescriptor->PortCount];
560     newPortRangeHints[0].HintDescriptor = 0;
561     newPortRangeHints[1].HintDescriptor = 0;
562     for (size_t dssi_control_index = 0;
563          dssi_control_index < DSSIControlDescription::MAX_DSSI_CONTROLS; ++dssi_control_index) {
564         newPortRangeHints[2 + dssi_control_index] = dssi_control_description[dssi_control_index].port_range_hint;
565     }
566     newLadspaDescriptor->PortRangeHints = newPortRangeHints;
567     newLadspaDescriptor->activate     = stub_activate;
568     newLadspaDescriptor->cleanup      = stub_cleanup;
569     newLadspaDescriptor->connect_port = stub_connectPort;
570     newLadspaDescriptor->deactivate   = stub_deactivate;
571     newLadspaDescriptor->instantiate  = instantiate;
572     newLadspaDescriptor->run = stub_run;
573     newLadspaDescriptor->run_adding = NULL;
574     newLadspaDescriptor->set_run_adding_gain = NULL;
575     newDssiDescriptor->LADSPA_Plugin    = newLadspaDescriptor;
576     newDssiDescriptor->DSSI_API_Version = 1;
577     newDssiDescriptor->configure   = NULL;
578     newDssiDescriptor->get_program = stub_getProgram;
579     newDssiDescriptor->get_midi_controller_for_port =
580         stub_getMidiControllerForPort;
581     newDssiDescriptor->select_program      = stub_selectProgram;
582     newDssiDescriptor->run_synth           = stub_runSynth;
583     newDssiDescriptor->run_synth_adding    = NULL;
584     newDssiDescriptor->run_multiple_synths = NULL;
585     newDssiDescriptor->run_multiple_synths_adding = NULL;
586 
587     dssiDescriptor = newDssiDescriptor;
588 
589     return dssiDescriptor;
590 }
591 
592 /**
593  * Converts a LADSPA / DSSI handle into a DSSIaudiooutput instance.
594  *
595  * @param instance [in]
596  * @return the instance
597  */
getInstance(LADSPA_Handle instance)598 DSSIaudiooutput *DSSIaudiooutput::getInstance(LADSPA_Handle instance)
599 {
600     return (DSSIaudiooutput *)(instance);
601 }
602 
603 
604 /**
605  * The private sole constructor for the DSSIaudiooutput class.
606  *
607  * Only ever called via instantiate().
608  * @param sampleRate [in] the sample rate to be used by the synth.
609  * @return
610  */
DSSIaudiooutput(unsigned long sampleRate)611 DSSIaudiooutput::DSSIaudiooutput(unsigned long sampleRate) : dssi_control{dssi_control_description[0],
612                                                                            dssi_control_description[1],
613                                                                            dssi_control_description[2],
614                                                                            dssi_control_description[3],
615                                                                            dssi_control_description[4],
616                                                                            dssi_control_description[5],
617                                                                            dssi_control_description[6],
618                                                                            dssi_control_description[7],
619                                                                            dssi_control_description[8],
620                                                                            dssi_control_description[9],
621                                                                            dssi_control_description[10],
622                                                                            dssi_control_description[11]}
623 {
624     zyn::SYNTH_T synth;
625     synth.samplerate = sampleRate;
626 
627     this->sampleRate  = sampleRate;
628     this->banksInited = false;
629 
630     zyn::sprng(time(NULL));
631 
632     synth.alias();
633     middleware = new zyn::MiddleWare(std::move(synth), &config);
634     initBanks();
__anon6e3178640102() 635     loadThread = new std::thread([this]() {
636             while(middleware) {
637             middleware->tick();
638             usleep(1000);
639             }});
640 }
641 
642 /**
643  * The destructor for the DSSIaudiooutput class
644  * @return
645  */
~DSSIaudiooutput()646 DSSIaudiooutput::~DSSIaudiooutput()
647 {
648     auto *tmp = middleware;
649     middleware = 0;
650     loadThread->join();
651     delete tmp;
652     delete loadThread;
653 }
654 
655 /**
656  * Ensures the list of bank (directories) has been initialised.
657  */
initBanks(void)658 void DSSIaudiooutput::initBanks(void)
659 {
660     if(!banksInited) {
661         middleware->spawnMaster()->bank.rescanforbanks();
662         banksInited = true;
663     }
664 }
665 
666 /**
667  * The map of programs available; held as a single shared statically allocated object.
668  */
669 vector<DSSIaudiooutput::ProgramDescriptor> DSSIaudiooutput::programMap =
670     vector<DSSIaudiooutput::ProgramDescriptor>();
671 
672 /**
673  * Index controlling the map of banks
674  */
675 long DSSIaudiooutput::bankNoToMap = 1;
676 
677 /**
678  * Queries and maps the next available bank of instruments.
679  *
680  * If the program index requested to getProgram() lies beyond the banks mapped to date,
681  * this member function is called to map the next one.
682  * @return true if a new bank has been found and mapped, else false.
683  */
mapNextBank()684 bool DSSIaudiooutput::mapNextBank()
685 {
686     zyn::Bank &bank  = middleware->spawnMaster()->bank;
687     auto &banks = bank.banks;
688     if(bankNoToMap >= (int)banks.size() || banks[bankNoToMap].dir.empty())
689         return false;
690     else {
691         bank.loadbank(bank.banks[bankNoToMap].dir);
692         for(int instrument = 0; instrument < BANK_SIZE; ++instrument) {
693             string insName = bank.getname(instrument);
694             ProgramDescriptor prog{(unsigned long)bankNoToMap,
695                                    (unsigned long)instrument, insName};
696 
697             if(!insName.empty() && insName[0] != '\0' && insName[0] != ' ')
698                 programMap.push_back(prog);
699         }
700         bankNoToMap++;
701         return true;
702     }
703 }
704