zynaddsubfx

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

commit 8b7b52aa958173e09e69c86ff82f14a7bab97c99
parent e42ca6c882d80fba6e0e7c8a656290c498fd38d5
Author: fundamental <[email protected]>
Date:   Sun, 14 Mar 2010 14:11:20 -0400

Merge branch 'master' into effects

Diffstat:
MAUTHORS.txt | 1+
MCMakeLists.txt | 4++++
MChangeLog | 7+++++++
Mdoc/build.txt | 15++++++++-------
Adoc/getting.txt | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/zynaddsubfx.txt | 4++++
Msrc/CMakeLists.txt | 23++++++++++++++++++++++-
Msrc/Makefile | 4++--
Msrc/Makefile.inc | 1+
Msrc/Misc/CMakeLists.txt | 4++++
Msrc/Output/DSSIaudiooutput.cpp | 806+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/Output/DSSIaudiooutput.h | 100++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/main.cpp | 21+++++++--------------
13 files changed, 799 insertions(+), 252 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/CMakeLists.txt b/CMakeLists.txt @@ -5,3 +5,7 @@ project(zynaddsubfx) enable_testing() #Currently the only directory that uses cmake add_subdirectory(src) + +install(FILES AUTHORS.txt COPYING FAQ.txt HISTORY.txt README.txt + DESTINATION share/doc/zynaddsubfx + ) diff --git a/ChangeLog b/ChangeLog @@ -974,3 +974,10 @@ the Wextra flag - Minor change to Filter_ and FormantFilter to reduce unwanted warnings +14 Jan 2010 (Mark McCurry) + - Fixed No UI Flag "-U" as it was previously partially initializing + the gui + +14 Feb 2010 (Stephen Parry) + - DSSI Support Repaired + diff --git a/doc/build.txt b/doc/build.txt @@ -1,5 +1,5 @@ -Building ZynAddSubFX -==================== +Appendix B: Building ZynAddSubFX +================================ Introduction to CMake --------------------- @@ -33,6 +33,11 @@ Quick start guide For the impatient ones, here is a quick guide on how to immediately build ZynAddSubFX from source. + +************************************************************** +Note: This assumes that you already have a copy of the source. +************************************************************** + --------------------------------- #enter the source directory cd zynaddsubfx @@ -46,13 +51,9 @@ cd build cmake .. #OPTIONAL: Adjust compile variables in the Cache file: -$EDITOR CMakeCache.txt +ccmake . #And finally, build as usual using make make --------------------------------- -Customizing the build ---------------------- - - diff --git a/doc/getting.txt b/doc/getting.txt @@ -0,0 +1,61 @@ +Appendix C: Getting ZynAddSubFX +=============================== + +Usually there are several methods to obtain a copy of ZynAddSubFX. + +SourceForge:: + http://sourceforge.net/projects/zynaddsubfx/files/ +Distribuition:: + apt/yum/others +Git:: + git://zynaddsubfx.git.sourceforge.net/gitroot/zynaddsubfx/zynaddsubfx + +Introduction to Git +------------------- + +For those who want to live on the bleeding edge or who want to assist with +making sure that the next release has fewer bugs, you will want to get aquanted +with git. +Git is used to manage the source code for this project and can be used to +quickly and easily get an up-to-date copy of the source code. + +Getting the Source Code +~~~~~~~~~~~~~~~~~~~~~~~ + +In order to get a copy of the ZynAddSubFX source code, all that needs to be done is: + +--------------------------------------------- +git clone git://zynaddsubfx.git.sourceforge.net/gitroot/zynaddsubfx/zynaddsubfx + +cd zynaddsubfx +--------------------------------------------- + +You should now be in the directory of the source code. + +For simple steps on building, please see Appendix B of the manual. + +Checking out a branch +~~~~~~~~~~~~~~~~~~~~~ + +Lets say that development has extended into the creation of a new feature that +you want to preview. +For the sake of this guide, lets assume that the name of the branch that the +feature is on is foo. + +----------------------------------------- +#checkout the foo branch from sourceforge +git checkout --track -b foo origin/foo + +#lets checkout the primary branch again +git checkout master + +#hop back to the other branch +git checkout foo +---------------------------------------- + +Now one should be able to change branches and go into the build directory (as +described in Appendix B) and recompile ZynAddSubFX. + +NOTE: When using branches other than the master be aware that stability may + suffer + diff --git a/doc/zynaddsubfx.txt b/doc/zynaddsubfx.txt @@ -18,3 +18,7 @@ include::./controller.txt[] include::./nrpn.txt[] include::./mididefaults.txt[] + +include::./build.txt[] + +include::./getting.txt[] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -2,11 +2,13 @@ find_package(zlib REQUIRED) find_package(fftw REQUIRED) find_package(MXML REQUIRED) +find_package(PkgConfig REQUIRED) #find_package(pthread REQUIRED) find_package(Alsa) find_package(JACK) find_package(PortAudio) find_package(FLTK) +find_package(OpenGL) #for FLTK ######### Settings ########### # NOTE: These cache variables should normallly not be changed in this @@ -17,6 +19,7 @@ SET (OutputModule alsa CACHE STRING "Output module, either alsa, jack or portaud SET (GuiModule fltk CACHE STRING "GUI module, either fltk, qt or off") SET (CompileTests OFF CACHE BOOL "whether tests should be compiled in or not") + # Now, handle the incoming settings and set define flags/variables based # on this @@ -42,6 +45,14 @@ else () message(FATAL_ERROR "OutputModule must be either alsa, jack or portaudio") endif() +if(NOT PKG_CONFIG_FOUND) + message(FATAL_ERROR "pkg-config not found") +endif(NOT PKG_CONFIG_FOUND) + +# lash +pkg_search_module(LASH lash-1.0) +mark_as_advanced(LASH_LIBRARIES) + ########### Settings dependant code ########### # From here on, the setting variables have been prepared so concentrate # on the actual compiling. @@ -67,6 +78,13 @@ if(PortAudioOutput) set(AUDIO_LIBRARIES ${AUDIO_LIBRARIES} ${PORTAUDIO_LIBRARIES}) endif() +if(LASH_FOUND) + include_directories(${LASH_INCLUDE_DIRS}) + add_definitions(-DUSE_LASH) + set(AUDIO_LIBRARIES ${AUDIO_LIBRARIES} ${LASH_LIBRARIES}) + message(STATUS "Compiling with lash") +endif() + add_definitions(-DFFTW_VERSION_${FFTW_VERSION} -DOS_LINUX -DALSAMIDIIN @@ -95,7 +113,7 @@ if(FltkGui) mark_as_advanced(FORCE FLTK_MATH_LIBRARY) - set(GUI_LIBRARIES ${FLTK_LIBRARIES} ${MYFLTK_LIBRARIES} zynaddsubfx_gui) + set(GUI_LIBRARIES ${FLTK_LIBRARIES} ${MYFLTK_LIBRARIES} ${OPENGL_LIBRARIES} zynaddsubfx_gui) add_definitions(-DFLTK_GUI) message(STATUS "Will build fltk gui") @@ -193,3 +211,6 @@ target_link_libraries(zynaddsubfx ${MIDIINPUT_LIBRARIES} ) +install(TARGETS zynaddsubfx + RUNTIME DESTINATION bin + ) 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/Misc/CMakeLists.txt b/src/Misc/CMakeLists.txt @@ -11,6 +11,10 @@ set(zynaddsubfx_misc_SRCS XMLwrapper.cpp ) +if (LASH_FOUND) + set(zynaddsubfx_misc_SRCS ${zynaddsubfx_misc_SRCS} LASHClient.cpp) +endif() + add_library(zynaddsubfx_misc STATIC ${zynaddsubfx_misc_SRCS} ) 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 @@ -351,9 +351,6 @@ void initprogram() #if (defined(NONEMIDIIN) || (defined(VSTMIDIIN))) Midi = new NULLMidiIn(); #endif -#ifndef DISABLE_GUI - ui = new MasterUI(master, &Pexitprogram); -#endif } /* @@ -655,10 +652,6 @@ int main(int argc, char *argv[]) } else { master->applyparameters(); -#ifndef DISABLE_GUI - if(noui == 0) - ui->refresh_master_ui(); -#endif cout << "Master file loaded." << endl; } } @@ -673,10 +666,6 @@ int main(int argc, char *argv[]) } else { master->part[loadtopart]->applyparameters(); -#ifndef DISABLE_GUI - if(noui == 0) - ui->refresh_master_ui(); -#endif cout << "Instrument file loaded." << endl; } } @@ -701,8 +690,12 @@ int main(int argc, char *argv[]) // setregid(getuid(),getuid()); #endif */ - if(noui == 0) +#ifndef DISABLE_GUI + if(noui == 0) { + ui = new MasterUI(master, &Pexitprogram); pthread_create(&thr3, NULL, thread3, NULL); + } +#endif pthread_create(&thr4, NULL, thread4, NULL); #ifdef WINMIDIIN