zynaddsubfx

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

commit 8e0c4794c6e3d8f87cdde74dbf9ef7129ff3d5ac
parent e65483585d7aaf225f92965718fbffac315c4dbf
Author: fundamental <[email protected]>
Date:   Sun, 14 Mar 2010 06:43:38 -0400

Merge branch 'dssi'

Diffstat:
MAUTHORS.txt | 1+
Msrc/Makefile | 4++--
Msrc/Makefile.inc | 1+
Msrc/Output/DSSIaudiooutput.cpp | 806+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/Output/DSSIaudiooutput.h | 100++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/main.cpp | 4++--
6 files changed, 684 insertions(+), 232 deletions(-)

diff --git a/AUTHORS.txt b/AUTHORS.txt @@ -15,4 +15,5 @@ Contributors: Alexis Ballier (const char* <-> string mismatch, NULLMidi prototype fix) Tobias Doerffel (static-instance variables fix, missing include fix) James Morris (Memory leaks in FLTK GUI) + Stephen Parry (DSSI rebuild) diff --git a/src/Makefile b/src/Makefile @@ -1,9 +1,9 @@ include Makefile.inc ifneq ($(MAKECMDGOALS),debug) - CXXFLAGS= -O2 -Wall -g + CXXFLAGS= -O2 -Wall -g -fPIC else - CXXFLAGS= -O2 -Wall -Wpointer-arith + CXXFLAGS= -O2 -Wall -Wpointer-arith -fPIC endif CXXFLAGS += -DOS_$(OS_PORT) -D$(MIDIIN)MIDIIN -DFFTW_VERSION_$(FFTW_VERSION) -DASM_F2I_$(ASM_F2I) -ggdb diff --git a/src/Makefile.inc b/src/Makefile.inc @@ -68,6 +68,7 @@ ifeq ($(OS_PORT),LINUX) AUDIOOUT=$(LINUX_AUDIOOUT) WINDOWS_VST=NO ifeq ($(LINUX_DSSI),YES) + DISABLE_GUI=YES MIDIIN=DSSI AUDIOOUT=DSSI endif diff --git a/src/Output/DSSIaudiooutput.cpp b/src/Output/DSSIaudiooutput.cpp @@ -20,268 +20,666 @@ */ +/* + * Inital working DSSI output code contributed by Stephen G. Parry + */ + //this file contains code used from trivial_synth.c from -//the DSSI (published by Steve Harris under public domain) as a template -//the code is incomplete +//the DSSI (published by Steve Harris under public domain) as a template. + #include <string.h> #include "DSSIaudiooutput.h" +#include "../Misc/Config.h" +#include "../Misc/Bank.h" +#include <limits.h> + +// +// Static stubs for LADSPA member functions +// +// LADSPA is essentially a C handle based API; This plug-in implementation is +// a C++ OO one so we need stub functions to map from C API calls to C++ object +// method calls. +void DSSIaudiooutput::stub_connectPort(LADSPA_Handle instance, unsigned long port, LADSPA_Data * data) +{ + getInstance(instance)->connectPort(port, data); +} -static LADSPA_Descriptor *tsLDescriptor = NULL; -static DSSI_Descriptor *tsDDescriptor = NULL; - -typedef struct { - LADSPA_Data *outl; - LADSPA_Data *outr; -// note_data data[MIDI_NOTES]; -// float omega[MIDI_NOTES]; -} TS; +void DSSIaudiooutput::stub_activate(LADSPA_Handle instance) +{ + getInstance(instance)->activate(); +} +void DSSIaudiooutput::stub_run(LADSPA_Handle instance, unsigned long sample_count) +{ + getInstance(instance)->run(sample_count); +} -static void cleanupTS(LADSPA_Handle instance) +void DSSIaudiooutput::stub_deactivate(LADSPA_Handle instance) { - free(instance); + getInstance(instance)->deactivate(); } -static void connectPortTS(LADSPA_Handle instance, unsigned long port, - LADSPA_Data *data) + + +void DSSIaudiooutput::stub_cleanup(LADSPA_Handle instance) { - TS *plugin; - plugin = (TS *) instance; - switch(port) { - case 0: - plugin->outl = data; - break; - case 1: - plugin->outr = data; - break; - } + DSSIaudiooutput* plugin_instance = getInstance(instance); + plugin_instance->cleanup(); + delete plugin_instance; } + const LADSPA_Descriptor *ladspa_descriptor(unsigned long index) { - switch(index) { - case 0: - return tsLDescriptor; - default: + return DSSIaudiooutput::getLadspaDescriptor(index); +} + +// +// Static stubs for DSSI member functions +// +// DSSI is essentially a C handle based API; This plug-in implementation is +// a C++ OO one so we need stub functions to map from C API calls to C++ object +// method calls. +const DSSI_Program_Descriptor* DSSIaudiooutput::stub_getProgram (LADSPA_Handle instance, unsigned long index) +{ + return getInstance(instance)->getProgram(index); +} + +void DSSIaudiooutput::stub_selectProgram(LADSPA_Handle instance, unsigned long bank, unsigned long program) +{ + getInstance(instance)->selectProgram(bank, program); +} + +int DSSIaudiooutput::stub_getMidiControllerForPort(LADSPA_Handle instance, unsigned long port) +{ + return getInstance(instance)->getMidiControllerForPort(port); +} + +void DSSIaudiooutput::stub_runSynth(LADSPA_Handle instance, unsigned long sample_count, + snd_seq_event_t *events, unsigned long event_count) +{ + getInstance(instance)->runSynth(sample_count, events, event_count); +} + +const DSSI_Descriptor *dssi_descriptor(unsigned long index) +{ + return DSSIaudiooutput::getDssiDescriptor(index); +} + +// +// LADSPA member functions +// + +/** + * Instantiates a plug-in. + * + * This LADSPA member function instantiates a plug-in. + * Note that instance initialisation should generally occur in + * activate() rather than here. + * + * Zyn Implementation + * ------------------ + * This implementation creates a C++ class object and hides its pointer + * in the handle by type casting. + * + * @param descriptor [in] the descriptor for this plug-in + * @param s_rate [in] the sample rate + * @return the plug-in instance handle if successful else NULL + */ +LADSPA_Handle DSSIaudiooutput::instantiate(const LADSPA_Descriptor * descriptor, unsigned long s_rate) +{ + if(descriptor->UniqueID == dssiDescriptor->LADSPA_Plugin->UniqueID) + { + return (LADSPA_Handle)(new DSSIaudiooutput(s_rate)); + } + else + { return NULL; } } -const DSSI_Descriptor *dssi_descriptor(unsigned long index) +/** + * Connects a port on an instantiated plug-in. + * + * This LADSPA member function connects a port on an instantiated plug-in to a + * memory location at which a block of data for the port will be read/written. + * The data location is expected to be an array of LADSPA_Data for audio ports + * or a single LADSPA_Data value for control ports. Memory issues will be + * managed by the host. The plug-in must read/write the data at these locations + * every time run() or run_adding() is called and the data present at the time + * of this connection call should not be considered meaningful. + * + * Zyn Implementation + * ------------------ + * The buffer pointers are stored as member variables + * + * @param port [in] the port to be connected + * @param data [in] the data buffer to write to / read from + */ +void DSSIaudiooutput::connectPort(unsigned long port, LADSPA_Data * data) { -// FILE *a=fopen("/tmp/zzzzz11z","w"); -// fprintf(a,"aaaaaaaaaaa TEST\n"); -// fclose(a); - switch(index) { + switch (port) { case 0: - return tsDDescriptor; - default: - return NULL; + outl = data; + break; + case 1: + outr = data; + break; } } -static LADSPA_Handle instantiateTS(const LADSPA_Descriptor *descriptor, - unsigned long s_rate) +/** + * Initialises a plug-in instance and activates it for use. + * + * This LADSPA member function initialises a plug-in instance and activates it + * for use. This is separated from instantiate() to aid real-time support and + * so that hosts can reinitialise a plug-in instance by calling deactivate() and + * then activate(). In this case the plug-in instance must reset all state + * information dependent on the history of the plug-in instance except for any + * data locations provided by connect_port() and any gain set by + * set_run_adding_gain(). + * + * Zyn Implementation + * ------------------ + * Currently this does nothing; Care must be taken as to code placed here as + * too much code here seems to cause time-out problems in jack-dssi-host. +*/ +void DSSIaudiooutput::activate() { - TS *plugin_data = (TS *) malloc(sizeof(TS)); - /* for (i=0; i<MIDI_NOTES; i++) { - plugin_data->omega[i] = M_PI * 2.0 / (double)s_rate * - pow(2.0, (i-69.0) / 12.0); - } - */ - return (LADSPA_Handle) plugin_data; } -static void activateTS(LADSPA_Handle instance) +/** + * Runs an instance of a plug-in for a block. + * + * This LADSPA member function runs an instance of a plug-in for a block. + * Note that if an activate() function exists then it must be called before + * run() or run_adding(). If deactivate() is called for a plug-in instance then + * the plug-in instance may not be reused until activate() has been called again. + * + * Zyn Implementation + * ------------------ + * This is a LADSPA function that does not process any MIDI events; it is hence + * implemented by simply calling runSynth() with an empty event list. + * + * @param sample_count [in] the block size (in samples) for which the plug-in instance may run + */ +void DSSIaudiooutput::run(unsigned long sample_count) +{ + runSynth(sample_count,NULL,(unsigned long)0); +} + +/** + * Counterpart to activate(). + * + * This LADSPA member function is the counterpart to activate() (see above). + * Deactivation is not similar to pausing as the plug-in instance will be + * reinitialised when activate() is called to reuse it. + * + * Zyn Implementation + * ------------------ + * Currently this function does nothing. + */ +void DSSIaudiooutput::deactivate() { - TS *plugin_data = (TS *) instance; -// for (i=0; i<MIDI_NOTES; i++) { -// plugin_data->data[i].active = 0; -// } } +/** + * Deletes a plug-in instance that is no longer required. + * + * LADSPA member function; once an instance of a plug-in has been finished with + * it can be deleted using this function. The instance handle ceases to be + * valid after this call. + * + * If activate() was called for a plug-in instance then a corresponding call to + * deactivate() must be made before cleanup() is called. + * + * Zyn Implementation + * ------------------ + * Currently cleanup is deferred to the destructor that is invoked after cleanup() + */ +void DSSIaudiooutput::cleanup() +{ +} + +/** + * Initial entry point for the LADSPA plug-in library. + * + * This LADSPA function is the initial entry point for the plug-in library. + * The LADSPA host looks for this entry point in each shared library object it + * finds and then calls the function to enumerate the plug-ins within the + * library. + * + * Zyn Implementation + * ------------------ + * As the Zyn plug-in is a DSSI plug-in, the LADSPA descriptor is embedded inside + * the DSSI descriptor, which is created by DSSIaudiooutput::initDssiDescriptor() + * statically when the library is loaded. This function then merely returns a pointer + * to that embedded descriptor. + * + * @param index [in] the index number of the plug-in within the library. + * @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL + */ +const LADSPA_Descriptor* DSSIaudiooutput::getLadspaDescriptor(unsigned long index) +{ + if(index > 0 || dssiDescriptor == NULL) + return NULL; + else + return dssiDescriptor->LADSPA_Plugin; +} -static void runTS(LADSPA_Handle instance, unsigned long sample_count, - snd_seq_event_t *events, unsigned long event_count) +// +// DSSI member functions +// + +/** + * Provides a description of a program available on this synth. + * + * This DSSI member function pointer provides a description of a program (named + * preset sound) available on this synth. + * + * Zyn Implementation + * ------------------ + * The instruments in all Zyn's bank directories, as shown by the `instrument + * -> show instrument bank` command, are enumerated to the host by this + * function, allowing access to all those instruments. + * The first time an instrument is requested, the bank it is in and any + * unmapped ones preceding that are mapped; all the instruments names and + * filenames from those banks are stored in the programMap member variable for + * later use. This is done on demand in this way, rather than up front in one + * go because loading all the instrument names in one go can lead to timeouts + * and zombies. + * + * @param index [in] index into the plug-in's list of + * programs, not a program number as represented by the Program + * field of the DSSI_Program_Descriptor. (This distinction is + * needed to support synths that use non-contiguous program or + * bank numbers.) + * @return a DSSI_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program, + * deactivate, or configure, on the same plug-in instance, or NULL if index is out of range. + */ +const DSSI_Program_Descriptor* DSSIaudiooutput::getProgram (unsigned long index) { - TS *plugin_data = (TS *) instance; -// LADSPA_Data *const output = plugin_data->output; -// LADSPA_Data freq = *(plugin_data->freq); -// LADSPA_Data vol = *(plugin_data->vol); -// note_data *data = plugin_data->data; - unsigned long pos; - unsigned long event_pos; - unsigned long note; - - /* if (freq < 1.0) { - freq = 440.0f; - } - if (vol < 0.000001) { - vol = 1.0f; - } + static DSSI_Program_Descriptor retVal; - if (event_count > 0) { - printf("trivial_synth: have %ld events\n", event_count); - } + /* Make sure we have the list of banks loaded */ + initBanks(); + + /* Make sure that the bank containing the instrument has been mapped */ + while (index >= programMap.size() && mapNextBank()) + /* DO NOTHING MORE */; - for (pos = 0, event_pos = 0; pos < sample_count; pos++) { + if(index >= programMap.size()) + { + /* No more instruments */ + return NULL; + } + else + { + /* OK, return the instrument */ + retVal.Name = programMap[index].name.c_str(); + retVal.Program = programMap[index].program; + retVal.Bank = programMap[index].bank; + return &retVal; + } +} - while (event_pos < event_count - && pos == events[event_pos].time.tick) { +/** + * Selects a new program for this synth. + * + * This DSSI member function selects a new program for this synth. The program + * change will take effect immediately at the start of the next run_synth() + * call. An invalid bank / instrument combination is ignored. + * + * Zyn Implementation + * ------------------ + * the banks and instruments are as shown in the `instrument -> show instrument + * bank` command in Zyn. The bank no is a 1-based index into the list of banks + * Zyn loads and shows in the drop down and the program number is the + * instrument within that bank. + * + * @param bank [in] the bank number to select + * @param program [in] the program number within the bank to select + */ +void DSSIaudiooutput::selectProgram(unsigned long bank, unsigned long program) +{ + initBanks(); +// cerr << "selectProgram(" << (bank & 0x7F) << ':' << ((bank >> 7) & 0x7F) << "," << program << ")" << '\n'; + if(bank < MAX_NUM_BANKS && program < BANK_SIZE) + { + char* bankdir = master->bank.banks[ bank ].dir; + if(bankdir != NULL) + { + pthread_mutex_lock(&master->mutex); + + /* We have to turn off the CheckPADsynth functionality, else + * the program change takes way too long and we get timeouts + * and hence zombies (!) */ + int save = config.cfg.CheckPADsynth; + config.cfg.CheckPADsynth = 0; + + /* Load the bank... */ + master->bank.loadbank(bankdir); + + /* restore the CheckPADsynth flag */ + config.cfg.CheckPADsynth = save; + + /* Now load the instrument... */ + master->bank.loadfromslot((unsigned int)program, master->part[0]); + + pthread_mutex_unlock(&master->mutex); + } + } +} - printf("trivial_synth: event type %d\n", events[event_pos].type); +/** + * Returns the MIDI controller number or NRPN for a input control port + * + * This DSSI member function returns the MIDI controller number or NRPN that + * should be mapped to the given input control port. If the given port should + * not have any MIDI controller mapped to it, the function will return DSSI_NONE. + * The behaviour of this function is undefined if the given port + * number does not correspond to an input control port. + * + * Zyn Implementation + * ------------------ + * Currently Zyn does not define any controller ports, but may do in the future. + * + * @param port [in] the input controller port + * @return the CC and NRPN values shifted and ORed together. + */ +int DSSIaudiooutput::getMidiControllerForPort(unsigned long port) +{ + return DSSI_NONE; +} - if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) { - data[events[event_pos].data.note.note].amp = - events[event_pos].data.note.velocity / 512.0f; - data[events[event_pos].data.note.note]. - active = events[event_pos].data.note.velocity > 0; - data[events[event_pos].data.note.note]. - phase = 0.0; - } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF) { - data[events[event_pos].data.note.note]. - active = 0; - } - event_pos++; +/** + * Runs the synth for a block. + * + * This DSSI member function runs the synth for a block. This is identical in + * function to the LADSPA run() function, except that it also supplies events + * to the synth. + * + * Zyn Implementation + * ------------------ + * Zyn implements synthesis in Master::GetAudioOutSamples; runSynth calls this + * function in chunks delimited by the sample_count and the frame indexes in + * the events block, calling the appropriate NoteOn, NoteOff and SetController + * members of Master to process the events supplied between each chunk. + * + * @param sample_count [in] the block size (in samples) for which the synth + * instance may run. + * @param events [in] The Events pointer points to a block of ALSA + * sequencer events, used to communicate MIDI and related events to the synth. + * Each event must be timestamped relative to the start of the block, + * (mis)using the ALSA "tick time" field as a frame count. The host is + * responsible for ensuring that events with differing timestamps are already + * ordered by time. Must not include NOTE (only NOTE_ON / NOTE_OFF), LSB or MSB + * events. + * @param event_count [in] the number of entries in the `events` block + */ +void DSSIaudiooutput::runSynth(unsigned long sample_count, snd_seq_event_t *events, unsigned long event_count) +{ + unsigned long from_frame = 0; + unsigned long event_index = 0; + unsigned long next_event_frame = 0; + unsigned long to_frame = 0; + pthread_mutex_lock(&master->mutex); + + do { + /* Find the time of the next event, if any */ + if(events == NULL || event_index >= event_count) + next_event_frame = ULONG_MAX; + else + next_event_frame = events[event_index].time.tick; + + /* find the end of the sub-sample to be processed this time round... */ + /* if the next event falls within the desired sample interval... */ + if(next_event_frame < sample_count && next_event_frame >= to_frame) + /* set the end to be at that event */ + to_frame = next_event_frame; + else + /* ...else go for the whole remaining sample */ + to_frame = sample_count; + if(from_frame<to_frame) + { + // call master to fill from `from_frame` to `to_frame`: + master->GetAudioOutSamples(to_frame - from_frame, (int)sampleRate, &(outl[from_frame]), &(outr[from_frame])); + // next sub-sample please... + from_frame = to_frame; } - output[pos] = 0.0f; - for (note = 0; note < MIDI_NOTES; note++) { - if (data[note].active) { - output[pos] += sin(data[note].phase) * data[note].amp * vol; - data[note].phase += plugin_data->omega[note] * freq; - if (data[note].phase > M_PI * 2.0) { - data[note].phase -= M_PI * 2.0; + // Now process any event(s) at the current timing point + while(events != NULL && event_index < event_count && events[event_index].time.tick == to_frame) + { + if(events[event_index].type == SND_SEQ_EVENT_NOTEON) + { + master->NoteOn(events[event_index].data.note.channel, events[event_index].data.note.note, events[event_index].data.note.velocity); } + else if(events[event_index].type == SND_SEQ_EVENT_NOTEOFF) + { + master->NoteOff(events[event_index].data.note.channel, events[event_index].data.note.note); } + else if(events[event_index].type == SND_SEQ_EVENT_CONTROLLER) + { + master->SetController(events[event_index].data.control.channel, events[event_index].data.control.param, events[event_index].data.control.value); + } + else + { + } + event_index++; } - } - */ -} + // Keep going until we have the desired total length of sample... + } while(to_frame < sample_count); -static void runTSWrapper(LADSPA_Handle instance, - unsigned long sample_count) -{ - runTS(instance, sample_count, NULL, 0); + pthread_mutex_unlock(&master->mutex); } -int getControllerTS(LADSPA_Handle instance, unsigned long port) +/** + * Initial entry point for the DSSI plug-in library. + * + * This DSSI function is the initial entry point for the plug-in library. + * The DSSI host looks for this entry point in each shared library object it + * finds and then calls the function to enumerate the plug-ins within the + * library. + * + * Zyn Implementation + * ------------------ + * The descriptor is created statically by DSSIaudiooutput::initDssiDescriptor() + * when the plug-in library is loaded. This function merely returns a pointer to + * that descriptor. + * + * @param index [in] the index number of the plug-in within the library. + * @return if index is in range, a pointer to the plug-in descriptor is returned, else NULL + */ +const DSSI_Descriptor* DSSIaudiooutput::getDssiDescriptor(unsigned long index) { - return -1; + if(index > 0 || dssiDescriptor == NULL) + return NULL; + else + return dssiDescriptor; } -void _init() +// +// Internal member functions +// + +// Initialise the DSSI descriptor, statically: +DSSI_Descriptor* DSSIaudiooutput::dssiDescriptor = DSSIaudiooutput::initDssiDescriptor(); + +/** + * Initializes the DSSI (and LADSPA) descriptor, returning it is an object. + */ +DSSI_Descriptor* DSSIaudiooutput::initDssiDescriptor() { - char **port_names; - LADSPA_PortDescriptor *port_descriptors; - LADSPA_PortRangeHint *port_range_hints; - - FILE *a = fopen("/tmp/zzzzzz", "w"); - fprintf(a, "aaaaaaaaaaa TEST\n"); - fclose(a); - - - tsLDescriptor = (LADSPA_Descriptor *) malloc(sizeof(LADSPA_Descriptor)); - if(tsLDescriptor) { - tsLDescriptor->UniqueID = 100; - tsLDescriptor->Label = "ZASF"; - tsLDescriptor->Properties = 0; - tsLDescriptor->Name = "ZynAddSubFX"; - tsLDescriptor->Maker = - "Nasca Octavian Paul <[email protected]>"; - tsLDescriptor->Copyright = "GNU General Public License v.2"; - tsLDescriptor->PortCount = 2; - - port_descriptors = (LADSPA_PortDescriptor *) - calloc(tsLDescriptor->PortCount, sizeof - (LADSPA_PortDescriptor)); - tsLDescriptor->PortDescriptors = - (const LADSPA_PortDescriptor *) port_descriptors; - - port_range_hints = (LADSPA_PortRangeHint *) - calloc(tsLDescriptor->PortCount, sizeof - (LADSPA_PortRangeHint)); - tsLDescriptor->PortRangeHints = - (const LADSPA_PortRangeHint *) port_range_hints; - - port_names = (char **) calloc(tsLDescriptor->PortCount, sizeof(char *)); - tsLDescriptor->PortNames = (const char **) port_names; - - port_descriptors[0] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; - port_names[0] = "Output L"; - port_range_hints[0].HintDescriptor = 0; - port_descriptors[1] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; - port_names[1] = "Output R"; - port_range_hints[1].HintDescriptor = 0; - - tsLDescriptor->activate = activateTS; - tsLDescriptor->cleanup = cleanupTS; - tsLDescriptor->connect_port = connectPortTS; - tsLDescriptor->deactivate = NULL; - tsLDescriptor->instantiate = instantiateTS; - tsLDescriptor->run = runTSWrapper; - tsLDescriptor->run_adding = NULL; - tsLDescriptor->set_run_adding_gain = NULL; + DSSI_Descriptor* newDssiDescriptor = new DSSI_Descriptor; + + LADSPA_PortDescriptor* newPortDescriptors; + char** newPortNames; + LADSPA_PortRangeHint* newPortRangeHints; + + if (newDssiDescriptor) + { + LADSPA_Descriptor* newLadspaDescriptor = new LADSPA_Descriptor; + if (newLadspaDescriptor) + { + newLadspaDescriptor->UniqueID = 100; + newLadspaDescriptor->Label = "ZASF"; + newLadspaDescriptor->Properties = 0; + newLadspaDescriptor->Name = "ZynAddSubFX"; + newLadspaDescriptor->Maker = "Nasca Octavian Paul <[email protected]>"; + newLadspaDescriptor->Copyright = "GNU General Public License v.2"; + newLadspaDescriptor->PortCount = 2; + + newPortNames = new char *[newLadspaDescriptor->PortCount]; + newPortNames[0] = "Output L"; + newPortNames[1] = "Output R"; + newLadspaDescriptor->PortNames = newPortNames; + + newPortDescriptors = new LADSPA_PortDescriptor[newLadspaDescriptor->PortCount]; + newPortDescriptors[0] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + newPortDescriptors[1] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + newLadspaDescriptor->PortDescriptors = newPortDescriptors; + + newPortRangeHints = new LADSPA_PortRangeHint[newLadspaDescriptor->PortCount]; + newPortRangeHints[0].HintDescriptor = 0; + newPortRangeHints[1].HintDescriptor = 0; + newLadspaDescriptor->PortRangeHints = newPortRangeHints; + + newLadspaDescriptor->activate = stub_activate; + newLadspaDescriptor->cleanup = stub_cleanup; + newLadspaDescriptor->connect_port = stub_connectPort; + newLadspaDescriptor->deactivate = stub_deactivate; + newLadspaDescriptor->instantiate = instantiate; + newLadspaDescriptor->run = stub_run; + newLadspaDescriptor->run_adding = NULL; + newLadspaDescriptor->set_run_adding_gain = NULL; + } + newDssiDescriptor->LADSPA_Plugin = newLadspaDescriptor; + newDssiDescriptor->DSSI_API_Version = 1; + newDssiDescriptor->configure = NULL; + newDssiDescriptor->get_program = stub_getProgram; + newDssiDescriptor->get_midi_controller_for_port = stub_getMidiControllerForPort; + newDssiDescriptor->select_program = stub_selectProgram; + newDssiDescriptor->run_synth = stub_runSynth; + newDssiDescriptor->run_synth_adding = NULL; + newDssiDescriptor->run_multiple_synths = NULL; + newDssiDescriptor->run_multiple_synths_adding = NULL; } - tsDDescriptor = (DSSI_Descriptor *) malloc(sizeof(DSSI_Descriptor)); - if(tsDDescriptor) { - tsDDescriptor->DSSI_API_Version = 1; - tsDDescriptor->LADSPA_Plugin = tsLDescriptor; - tsDDescriptor->configure = NULL; - tsDDescriptor->get_program = NULL; - tsDDescriptor->get_midi_controller_for_port = getControllerTS; - tsDDescriptor->select_program = NULL; - tsDDescriptor->run_synth = runTS; - tsDDescriptor->run_synth_adding = NULL; - tsDDescriptor->run_multiple_synths = NULL; - tsDDescriptor->run_multiple_synths_adding = NULL; - } -} + dssiDescriptor = newDssiDescriptor; -void _fini() -{} + return dssiDescriptor; +} +/** + * Converts a LADSPA / DSSI handle into a DSSIaudiooutput instance. + * + * @param instance [in] + * @return the instance + */ +DSSIaudiooutput* DSSIaudiooutput::getInstance(LADSPA_Handle instance) +{ + return (DSSIaudiooutput*)(instance); +} +/** + * The private sole constructor for the DSSIaudiooutput class. + * + * Only ever called via instantiate(). + * @param sampleRate [in] the sample rate to be used by the synth. + * @return + */ +DSSIaudiooutput::DSSIaudiooutput(unsigned long sampleRate) +{ + this->sampleRate = sampleRate; + this->banksInited = false; + config.init(); + srand(time(NULL)); + denormalkillbuf=new REALTYPE [SOUND_BUFFER_SIZE]; + for (int i=0;i<SOUND_BUFFER_SIZE;i++) denormalkillbuf[i]=(RND-0.5)*1e-16; + this->master = new Master(); +} +/** + * The destructor for the DSSIaudiooutput class + * @return + */ +DSSIaudiooutput::~DSSIaudiooutput() +{ +} -//the constructor and the destructor are defined in main.C -/* -void VSTSynth::process (float **inputs, float **outputs, long sampleframes){ - float *outl=outputs[0]; - float *outr=outputs[1]; - pthread_mutex_lock(&vmaster->mutex); - vmaster->GetAudioOutSamples(sampleframes,(int) getSampleRate(),outl,outr); - pthread_mutex_unlock(&vmaster->mutex); -}; - -void VSTSynth::processReplacing (float **inputs, float **outputs, long sampleframes){ - process(inputs,outputs,sampleframes); -}; - -long int VSTSynth::canDo(char *txt){ - if (strcmp(txt,"receiveVstEvents")==0) return (1); - if (strcmp(txt,"receiveVstMidiEvent")==0) return (1); - return(-1); -}; - -bool VSTSynth::getVendorString(char *txt){ - strcpy(txt,"Nasca O. Paul"); - return(true); -}; - -bool VSTSynth::getProductString(char *txt){ - strcpy(txt,"ZynAddSubFX"); - return(true); -}; - -void VSTSynth::resume(){ - wantEvents(); -}; +/** + * Ensures the list of bank (directories) has been initialised. + */ +void DSSIaudiooutput::initBanks(void) +{ + if(!banksInited) + { + pthread_mutex_lock(&master->mutex); + master->bank.rescanforbanks(); + banksInited = true; + pthread_mutex_unlock(&master->mutex); + } +} -*/ +/** + * constructor for the internally used ProgramDescriptor class + * + * @param _bank [in] bank number + * @param _program [in] program number + * @param _name [in] instrument / sample name + * @return + */ +DSSIaudiooutput::ProgramDescriptor::ProgramDescriptor(unsigned long _bank, unsigned long _program, char* _name) : + bank(_bank), program(_program), name(_name) +{ +} +/** + * The map of programs available; held as a single shared statically allocated object. + */ +vector <DSSIaudiooutput::ProgramDescriptor> DSSIaudiooutput::programMap = vector<DSSIaudiooutput::ProgramDescriptor>(); + +/** + * Index controlling the map of banks + */ +long DSSIaudiooutput::bankNoToMap = 1; + +/** + * Queries and maps the next available bank of instruments. + * + * If the program index requested to getProgram() lies beyond the banks mapped to date, + * this member function is called to map the next one. + * @return true if a new bank has been found and mapped, else false. + */ +bool DSSIaudiooutput::mapNextBank() +{ + pthread_mutex_lock(&master->mutex); + Bank& bank = master->bank; + bool retval; + if(bankNoToMap >= MAX_NUM_BANKS || bank.banks[bankNoToMap].dir == NULL) + { + retval = false; + } + else + { + bank.loadbank(bank.banks[bankNoToMap].dir); + for(unsigned long instrument = 0; instrument < BANK_SIZE; instrument++) + { + char* insName = bank.getname(instrument); + if(insName != NULL && insName[0] != '\0' && insName[0] != ' ') + { + programMap.push_back(ProgramDescriptor(bankNoToMap,instrument,insName)); + } + } + bankNoToMap ++; + retval = true; + } + pthread_mutex_unlock(&master->mutex); + return retval; +} diff --git a/src/Output/DSSIaudiooutput.h b/src/Output/DSSIaudiooutput.h @@ -26,34 +26,86 @@ #include "../globals.h" #include "../Misc/Master.h" -#include "../UI/MasterUI.h" #include <dssi.h> #include <ladspa.h> +#include <vector> -/* -class VSTSynth:public AudioEffectX{ - public: - VSTSynth (audioMasterCallback audioMaster); - ~VSTSynth(); - - virtual void process (float **inputs, float **outputs, long sampleframes); - virtual void processReplacing (float **inputs, float **outputs, long sampleframes); - virtual long processEvents(VstEvents *events);//this is used for Midi input - virtual long int canDo(char *txt); - virtual bool getVendorString(char *txt); - virtual bool getProductString(char *txt); - virtual void resume(); - - virtual long getChunk(void** data,bool isPreset=false); - virtual void setChunk(void *data,long size,bool isPreset=false); - - MasterUI *ui; - int Pexitprogram; - - Master *vmaster; - pthread_t thr; +class DSSIaudiooutput +{ +public: + // + // Static stubs for LADSPA member functions + // + static void stub_connectPort(LADSPA_Handle instance, unsigned long port, LADSPA_Data * data); + static void stub_activate(LADSPA_Handle instance); + static void stub_run(LADSPA_Handle instance, unsigned long sample_count); + static void stub_deactivate(LADSPA_Handle Instance); + static void stub_cleanup(LADSPA_Handle instance); + + // + // Static stubs for DSSI member functions + // + static const DSSI_Program_Descriptor* stub_getProgram (LADSPA_Handle instance, unsigned long Index); + static void stub_selectProgram(LADSPA_Handle instance, unsigned long bank, unsigned long program); + static int stub_getMidiControllerForPort(LADSPA_Handle instance, unsigned long port); + static void stub_runSynth(LADSPA_Handle instance, unsigned long sample_count, + snd_seq_event_t *events, unsigned long event_count); + + /* + * LADSPA member functions + */ + static LADSPA_Handle instantiate(const LADSPA_Descriptor * descriptor, unsigned long s_rate); + void connectPort(unsigned long port, LADSPA_Data * data); + void activate(); + void run(unsigned long sample_count); + void deactivate(); + void cleanup(); + static const LADSPA_Descriptor* getLadspaDescriptor(unsigned long index); + + /* + * DSSI member functions + */ + const DSSI_Program_Descriptor* getProgram (unsigned long Index); + void selectProgram(unsigned long bank, unsigned long program); + int getMidiControllerForPort(unsigned long port); + void runSynth(unsigned long sample_count, snd_seq_event_t *events, unsigned long event_count); + static const DSSI_Descriptor* getDssiDescriptor(unsigned long index); + + struct ProgramDescriptor + { + unsigned long bank; + unsigned long program; + string name; + ProgramDescriptor(unsigned long _bank, unsigned long _program, char* _name); + }; + +private: + + DSSIaudiooutput(unsigned long sampleRate); + ~DSSIaudiooutput(); + static DSSI_Descriptor* initDssiDescriptor(); + static DSSIaudiooutput* getInstance(LADSPA_Handle instance); + void initBanks(); + bool mapNextBank(); + + LADSPA_Data *outl; + LADSPA_Data *outr; + long sampleRate; + Master* master; + static DSSI_Descriptor* dssiDescriptor; + static string bankDirNames[]; + static + vector <ProgramDescriptor> programMap; + + /** + * Flag controlling the list of bank directories + */ + bool banksInited; + + static + long bankNoToMap; }; -*/ + #endif diff --git a/src/main.cpp b/src/main.cpp @@ -28,7 +28,7 @@ #ifdef OS_LINUX #include <getopt.h> -#elif OS_WINDOIWS +#elif OS_WINDOWS #include <winbase.h> #include <windows.h> #endif @@ -46,7 +46,7 @@ extern Dump dump; #include "Input/OSSMidiIn.h" #endif -#if (defined(NONEMIDIIN) || defined(VSTMIDIIN)) +#if (defined(NONEMIDIIN) || defined(VSTMIDIIN) || defined(DSSIMIDIIN)) #include "Input/NULLMidiIn.h" #endif