zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

DSSIaudiooutput.cpp (25620B)


      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 {
     37     bool start(void){return 1;};
     38     void stop(void){};
     39     void masterSwap(zyn::Master *){};
     40     void waveNew(WavFile *){}
     41     void waveStart(void){}
     42     void waveStop(void){}
     43     void waveEnd(void){}
     44     bool setSource(string){return true;}
     45     bool setSink(string){return true;}
     46     set<string> getSources(void){return set<string>();}
     47     set<string> getSinks(void){return set<string>();}
     48     string getSource(void){return "";}
     49     string getSink(void){return "";}
     50     void setAudioCompressor(bool){}
     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.
     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 
     68 void DSSIaudiooutput::stub_activate(LADSPA_Handle instance)
     69 {
     70     getInstance(instance)->activate();
     71 }
     72 
     73 void DSSIaudiooutput::stub_run(LADSPA_Handle instance,
     74                                unsigned long sample_count)
     75 {
     76     getInstance(instance)->run(sample_count);
     77 }
     78 
     79 void DSSIaudiooutput::stub_deactivate(LADSPA_Handle instance)
     80 {
     81     getInstance(instance)->deactivate();
     82 }
     83 
     84 
     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 
     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.
    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 
    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 
    118 int DSSIaudiooutput::stub_getMidiControllerForPort(LADSPA_Handle instance,
    119                                                    unsigned long port)
    120 {
    121     return getInstance(instance)->getMidiControllerForPort(port);
    122 }
    123 
    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 
    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  */
    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  */
    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 */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 <[email protected]>";
    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  */
    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  */
    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();
    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  */
    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  */
    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  */
    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 }