From e5c832c5872c1e0cf631761268aad21ed10e0df3 Mon Sep 17 00:00:00 2001 From: Josh Green Date: Tue, 22 Sep 2009 07:04:07 +0000 Subject: [PATCH] Merged fluidsynth_event_queue branch r207:223 into trunk. --- fluidsynth/AUTHORS | 4 + fluidsynth/include/fluidsynth/settings.h | 6 + fluidsynth/include/fluidsynth/synth.h | 598 +--- fluidsynth/src/Makefile.am | 2 + fluidsynth/src/fluid_adriver.c | 14 +- fluidsynth/src/fluid_alsa.c | 77 +- fluidsynth/src/fluid_aufile.c | 8 +- fluidsynth/src/fluid_chan.c | 386 +-- fluidsynth/src/fluid_chan.h | 78 +- fluidsynth/src/fluid_chorus.c | 185 +- fluidsynth/src/fluid_chorus.h | 33 +- fluidsynth/src/fluid_cmd.c | 78 +- fluidsynth/src/fluid_coremidi.c | 12 +- fluidsynth/src/fluid_dsound.c | 9 +- fluidsynth/src/fluid_event_queue.c | 155 + fluidsynth/src/fluid_event_queue.h | 130 + fluidsynth/src/fluid_hash.c | 1328 ++++++-- fluidsynth/src/fluid_hash.h | 105 +- fluidsynth/src/fluid_jack.c | 8 +- fluidsynth/src/fluid_midi.c | 8 +- fluidsynth/src/fluid_oss.c | 47 +- fluidsynth/src/fluid_portaudio.c | 7 +- fluidsynth/src/fluid_pulse.c | 26 +- fluidsynth/src/fluid_rev.c | 86 +- fluidsynth/src/fluid_rev.h | 48 +- fluidsynth/src/fluid_seq.c | 4 +- fluidsynth/src/fluid_settings.c | 1164 ++++--- fluidsynth/src/fluid_synth.c | 3769 +++++++++++++++------- fluidsynth/src/fluid_synth.h | 184 +- fluidsynth/src/fluid_sys.c | 95 +- fluidsynth/src/fluid_sys.h | 65 +- fluidsynth/src/fluid_winmidi.c | 17 +- fluidsynth/src/fluidsynth.c | 25 +- fluidsynth/src/fluidsynth_priv.h | 6 +- 34 files changed, 5735 insertions(+), 3032 deletions(-) create mode 100644 fluidsynth/src/fluid_event_queue.c create mode 100644 fluidsynth/src/fluid_event_queue.h diff --git a/fluidsynth/AUTHORS b/fluidsynth/AUTHORS index d158852d..9ca95d4a 100644 --- a/fluidsynth/AUTHORS +++ b/fluidsynth/AUTHORS @@ -4,6 +4,7 @@ Current development team Josh Green Bernat Arlandis i Maņķ Pedro Lopez-Cabanillas +David Henningsson [:Idea:] @@ -72,6 +73,9 @@ summary of contributions. to make iiwusynth independent from any library for maximum portability. +* David Henningsson added code for fast rendering of MIDI files and + fixed many bugs. + * The midi device uses code from jMax's alsarawmidi.c file and from Smurf's midi_alsaraw.c by Josh Green. file: iiwu_alsa.c diff --git a/fluidsynth/include/fluidsynth/settings.h b/fluidsynth/include/fluidsynth/settings.h index 03d016fc..d81754aa 100644 --- a/fluidsynth/include/fluidsynth/settings.h +++ b/fluidsynth/include/fluidsynth/settings.h @@ -133,6 +133,12 @@ int fluid_settings_is_realtime(fluid_settings_t* settings, char* name); FLUIDSYNTH_API int fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str); +FLUIDSYNTH_API +int fluid_settings_copystr(fluid_settings_t* settings, char* name, char* str, int len); + +FLUIDSYNTH_API +int fluid_settings_dupstr(fluid_settings_t* settings, char* name, char** str); + FLUIDSYNTH_API int fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str); diff --git a/fluidsynth/include/fluidsynth/synth.h b/fluidsynth/include/fluidsynth/synth.h index cd3fee33..a7d9f072 100644 --- a/fluidsynth/include/fluidsynth/synth.h +++ b/fluidsynth/include/fluidsynth/synth.h @@ -27,306 +27,113 @@ extern "C" { #endif - /** Embedded synthesizer - * - * You create a new synthesizer with new_fluid_synth() and you destroy - * if with delete_fluid_synth(). Use the settings structure to specify - * the synthesizer characteristics. - * - * You have to load a SoundFont in order to hear any sound. For that - * you use the fluid_synth_sfload() function. - * - * You can use the audio driver functions described below to open - * the audio device and create a background audio thread. - * - * The API for sending MIDI events is probably what you expect: - * fluid_synth_noteon(), fluid_synth_noteoff(), ... - * - */ +/** + * Embedded synthesizer + * + * You create a new synthesizer with new_fluid_synth() and you destroy + * if with delete_fluid_synth(). Use the settings structure to specify + * the synthesizer characteristics. + * + * You have to load a SoundFont in order to hear any sound. For that + * you use the fluid_synth_sfload() function. + * + * You can use the audio driver functions described below to open + * the audio device and create a background audio thread. + * + * The API for sending MIDI events is probably what you expect: + * fluid_synth_noteon(), fluid_synth_noteoff(), ... + */ - /** Creates a new synthesizer object. - * - * Creates a new synthesizer object. As soon as the synthesizer is - * created, it will start playing. - * - * \param settings a pointer to a settings structure - * \return a newly allocated synthesizer or NULL in case of error - */ FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings); - - - /** - * Deletes the synthesizer previously created with new_fluid_synth. - * - * \param synth the synthesizer object - * \return 0 if no error occured, -1 otherwise - */ FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth); - - - /** Get a reference to the settings of the synthesizer. - * - * \param synth the synthesizer object - * \return pointer to the settings - */ FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth); - /* - * - * MIDI channel messages - * - */ +/* MIDI channel messages */ - /** Send a noteon message. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel); - - /** Send a noteoff message. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key); - - /** Send a control change message. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val); - - /** Get a control value. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval); - - /** Send a pitch bend message. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val); - - /** Get the pitch bend value. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API -int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend); - - /** Set the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend); FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val); - - /** Get the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval); - - /** Send a program change message. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program); - - /** Send a channel aftertouch message. Returns 0 if no error occurred, -1 otherwise. */ FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val); - - /** Select a bank. Returns 0 if no error occurred, -1 otherwise. */ +FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); +FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id); +FLUIDSYNTH_API +int fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id, + unsigned int bank_num, unsigned int preset_num); FLUIDSYNTH_API -int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); - - /** Select a sfont. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API -int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id); - - /** Select a preset for a channel. The preset is specified by the - SoundFont ID, the bank number, and the preset number. This - allows any preset to be selected and circumvents preset masking - due to previously loaded SoundFonts on the SoundFont stack. - - \param synth The synthesizer - \param chan The channel on which to set the preset - \param sfont_id The ID of the SoundFont - \param bank_num The bank number - \param preset_num The preset number - \return 0 if no errors occured, -1 otherwise - */ -FLUIDSYNTH_API -int fluid_synth_program_select(fluid_synth_t* synth, int chan, - unsigned int sfont_id, - unsigned int bank_num, - unsigned int preset_num); - - /** Returns the program, bank, and SoundFont number of the preset on - a given channel. Returns 0 if no error occurred, -1 otherwise. */ -FLUIDSYNTH_API -int fluid_synth_get_program(fluid_synth_t* synth, int chan, - unsigned int* sfont_id, - unsigned int* bank_num, - unsigned int* preset_num); - - /** Send a bank select and a program change to every channel to - * reinitialize the preset of the channel. This function is useful - * mainly after a SoundFont has been loaded, unloaded or - * reloaded. . Returns 0 if no error occurred, -1 otherwise. */ +int fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, + unsigned int* bank_num, unsigned int* preset_num); FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth); - - /** Send a reset. A reset turns all the notes off and resets the - controller values. */ FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth); - /* - * - * Low level access - * - */ - - /** Create and start voices using a preset. The id passed as - * argument will be used as the voice group id. */ +/* Low level access */ +FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan); FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, int audio_chan, int midi_chan, int key, int vel); - - /** Stop the voices in the voice group defined by id. */ FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id); - /** Change the value of a generator of the voices in the voice group - * defined by id. */ -/* FLUIDSYNTH_API int fluid_synth_ctrl(fluid_synth_t* synth, int id, */ -/* int gen, float value, */ -/* int absolute, int normalized); */ +/* SoundFont management */ - /* - * - * SoundFont management - * - */ - - /** Loads a SoundFont file and creates a new SoundFont. The newly - loaded SoundFont will be put on top of the SoundFont - stack. Presets are searched starting from the SoundFont on the - top of the stack, working the way down the stack until a preset - is found. - - \param synth The synthesizer object - \param filename The file name - \param reset_presets If non-zero, the presets on the channels will be reset - \returns The ID of the loaded SoundFont, or -1 in case of error - */ FLUIDSYNTH_API int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets); - - /** Reload a SoundFont. The reloaded SoundFont retains its ID and - index on the stack. - - \param synth The synthesizer object - \param id The id of the SoundFont - \returns The ID of the loaded SoundFont, or -1 in case of error - */ FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id); - - /** Removes a SoundFont from the stack and deallocates it. - - \param synth The synthesizer object - \param id The id of the SoundFont - \param reset_presets If TRUE then presets will be reset for all channels - \returns 0 if no error, -1 otherwise - */ FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets); - - /** Add a SoundFont. The SoundFont will be put on top of - the SoundFont stack. - - \param synth The synthesizer object - \param sfont The SoundFont - \returns The ID of the loaded SoundFont, or -1 in case of error - */ FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); - - /** Remove a SoundFont that was previously added using - * fluid_synth_add_sfont(). The synthesizer does not delete the - * SoundFont; this is responsability of the caller. - - \param synth The synthesizer object - \param sfont The SoundFont - */ FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont); - - /** Count the number of loaded SoundFonts. - - \param synth The synthesizer object - \returns The number of loaded SoundFonts - */ FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth); - - /** Get a SoundFont. The SoundFont is specified by its index on the - stack. The top of the stack has index zero. - - \param synth The synthesizer object - \param num The number of the SoundFont (0 <= num < sfcount) - \returns A pointer to the SoundFont - */ FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num); - - /** Get a SoundFont. The SoundFont is specified by its ID. - - \param synth The synthesizer object - \param id The id of the sfont - \returns A pointer to the SoundFont - */ FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id); - - - /** Get the preset of a channel */ -FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan); - - /** Offset the bank numbers in a SoundFont. Returns -1 if an error - * occured (out of memory or negative offset) */ FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset); - - /** Get the offset of the bank numbers in a SoundFont. */ FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id); +/* Reverb */ - /* - * - * Reverb - * - */ - - /** Set the parameters for the built-in reverb unit */ FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, - double damping, double width, double level); - - /** Turn on (1) / off (0) the built-in reverb unit */ + double damping, double width, double level); FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on); - - - /** Query the current state of the reverb. */ FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t* synth); - /* Those are the default settings for the reverb */ +/* Default settings for the reverb */ #define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f #define FLUID_REVERB_DEFAULT_DAMP 0.0f #define FLUID_REVERB_DEFAULT_WIDTH 0.5f #define FLUID_REVERB_DEFAULT_LEVEL 0.9f +/* Chorus */ - /* - * - * Chorus - * - */ - +/** + * Chorus modulation waveform type. + */ enum fluid_chorus_mod { - FLUID_CHORUS_MOD_SINE = 0, - FLUID_CHORUS_MOD_TRIANGLE = 1 + FLUID_CHORUS_MOD_SINE = 0, /**< Sine wave chorus modulation */ + FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */ }; - /** Set up the chorus. It should be turned on with fluid_synth_set_chorus_on. - * If faulty parameters are given, all new settings are discarded. - * Keep in mind, that the needed CPU time is proportional to 'nr'. - */ FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, double speed, double depth_ms, int type); - - /** Turn on (1) / off (0) the built-in chorus unit */ FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on); - - /** Query the current state of the chorus. */ FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth); FLUIDSYNTH_API double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t* synth); /* see fluid_chorus_mod */ - /* Those are the default settings for the chorus. */ +/* Default settings for the chorus. */ #define FLUID_CHORUS_DEFAULT_N 3 #define FLUID_CHORUS_DEFAULT_LEVEL 2.0f #define FLUID_CHORUS_DEFAULT_SPEED 0.3f @@ -334,367 +141,122 @@ FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t* synth); /* see flu #define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE +/* Audio and MIDI channels */ - /* - * - * Audio and MIDI channels - * - */ - - /** Returns the number of MIDI channels that the synthesizer uses - internally */ FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t* synth); - - /** Returns the number of audio channels that the synthesizer uses - internally */ FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t* synth); - - /** Returns the number of audio groups that the synthesizer uses - internally. This is usually identical to audio_channels. */ FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t* synth); - - /** Returns the number of effects channels that the synthesizer uses - internally */ FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t* synth); +/* Synthesis parameters */ - /* - * - * Synthesis parameters - * - */ - - /** Set the master gain */ FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t* synth, float gain); - - /** Get the master gain */ FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t* synth); - - /** Set the polyphony limit (FluidSynth >= 1.0.6) */ FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony); - - /** Get the polyphony limit (FluidSynth >= 1.0.6) */ FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth); - - /** Get the internal buffer size. The internal buffer size if not the - same thing as the buffer size specified in the - settings. Internally, the synth *always* uses a specific buffer - size independent of the buffer size used by the audio driver. The - internal buffer size is normally 64 samples. The reason why it - uses an internal buffer size is to allow audio drivers to call the - synthesizer with a variable buffer length. The internal buffer - size is useful for client who want to optimize their buffer sizes. - */ FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth); - /** Set the interpolation method for one channel or all channels (chan = -1) */ FLUIDSYNTH_API int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method); - /* Flags to choose the interpolation method */ +/** + * Synthesis interpolation method. + */ enum fluid_interp { - /* no interpolation: Fastest, but questionable audio quality */ - FLUID_INTERP_NONE = 0, - /* Straight-line interpolation: A bit slower, reasonable audio quality */ - FLUID_INTERP_LINEAR = 1, - /* Fourth-order interpolation: Requires 50 % of the whole DSP processing time, good quality - * Default. */ - FLUID_INTERP_DEFAULT = 4, - FLUID_INTERP_4THORDER = 4, - FLUID_INTERP_7THORDER = 7, - FLUID_INTERP_HIGHEST=7 + FLUID_INTERP_NONE = 0, /**< No interpolation: Fastest, but questionable audio quality */ + FLUID_INTERP_LINEAR = 1, /**< Straight-line interpolation: A bit slower, reasonable audio quality */ + FLUID_INTERP_4THORDER = 4, /**< Fourth-order interpolation, good quality, the default */ + FLUID_INTERP_7THORDER = 7, /**< Seventh-order interpolation */ }; +/** Default interpolation method from #fluid_interp. */ +#define FLUID_INTERP_DEFAULT FLUID_INTERP_4THORDER + +/** Highest interpolation method from #fluid_interp. */ +#define FLUID_INTERP_HIGHEST FLUID_INTERP_7THORDER +/* Generator interface */ - /* - * - * Generator interface - * - */ - - /** Change the value of a generator. This function allows to control - all synthesis parameters in real-time. The changes are additive, - i.e. they add up to the existing parameter value. This function is - similar to sending an NRPN message to the synthesizer. The - function accepts a float as the value of the parameter. The - parameter numbers and ranges are described in the SoundFont 2.01 - specification, paragraph 8.1.3, page 48. See also 'fluid_gen_type'. - - \param synth The synthesizer object. - \param chan The MIDI channel number. - \param param The parameter number. - \param value The parameter value. - \returns Your favorite dish. - */ FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value); - - - /** Retreive the value of a generator. This function returns the value - set by a previous call 'fluid_synth_set_gen' or by an NRPN message. - - \param synth The synthesizer object. - \param chan The MIDI channel number. - \param param The generator number. - \returns The value of the generator. - */ FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param); +/* Tuning */ - - /* - * - * Tuning - * - */ - - /** Create a new key-based tuning with given name, number, and - pitches. The array 'pitches' should have length 128 and contains - the pitch in cents of every key in cents. However, if 'pitches' is - NULL, a new tuning is created with the well-tempered scale. - - \param synth The synthesizer object - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - \param name The name of the tuning - \param pitch The array of pitch values. The array length has to be 128. - */ FLUIDSYNTH_API int fluid_synth_create_key_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, char* name, double* pitch); - - /** Create a new octave-based tuning with given name, number, and - pitches. The array 'pitches' should have length 12 and contains - derivation in cents from the well-tempered scale. For example, if - pitches[0] equals -33, then the C-keys will be tuned 33 cents - below the well-tempered C. - - \param synth The synthesizer object - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - \param name The name of the tuning - \param pitch The array of pitch derivations. The array length has to be 12. - */ FLUIDSYNTH_API int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog, char* name, double* pitch); - - /** Request a note tuning changes. Both they 'keys' and 'pitches' - arrays should be of length 'num_pitches'. If 'apply' is non-zero, - the changes should be applied in real-time, i.e. sounding notes - will have their pitch updated. 'APPLY' IS CURRENTLY IGNORED. The - changes will be available for newly triggered notes only. - - \param synth The synthesizer object - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - \param len The length of the keys and pitch arrays - \param keys The array of keys values. - \param pitch The array of pitch values. - \param apply Flag to indicate whether to changes should be applied in real-time. - */ FLUIDSYNTH_API int fluid_synth_tune_notes(fluid_synth_t* synth, int tuning_bank, int tuning_prog, int len, int *keys, double* pitch, int apply); - - /** Select a tuning for a channel. - - \param synth The synthesizer object - \param chan The channel number [0-max channels] - \param tuning_bank The tuning bank number [0-127] - \param tuning_prog The tuning program number [0-127] - */ FLUIDSYNTH_API int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int tuning_bank, int tuning_prog); - - /** Set the tuning to the default well-tempered tuning on a channel. - - \param synth The synthesizer object - \param chan The channel number [0-max channels] - */ FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan); - - /** Start the iteration throught the list of available tunings. - - \param synth The synthesizer object - */ FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth); - - - /** Get the next tuning in the iteration. This functions stores the - bank and program number of the next tuning in the pointers given as - arguments. - - \param synth The synthesizer object - \param bank Pointer to an int to store the bank number - \param prog Pointer to an int to store the program number - \returns 1 if there is a next tuning, 0 otherwise - */ FLUIDSYNTH_API int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog); - - - /** Dump the data of a tuning. This functions stores the name and - pitch values of a tuning in the pointers given as arguments. Both - name and pitch can be NULL is the data is not needed. - - \param synth The synthesizer object - \param bank The tuning bank number [0-127] - \param prog The tuning program number [0-127] - \param name Pointer to a buffer to store the name - \param len The length of the name buffer - \param pitch Pointer to buffer to store the pitch values - */ FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, char* name, int len, double* pitch); +/* Misc */ - - - /* - * - * Misc - * - */ - - /** Get an estimation of the CPU load due to the audio synthesis. - Returns a percentage (0-100). - - \param synth The synthesizer object - */ FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t* synth); - - /** Get a textual representation of the last error */ FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth); - /* - * - * Synthesizer plugin - * - * - * To create a synthesizer plugin, create the synthesizer as - * explained above. Once the synthesizer is created you can call - * any of the functions below to get the audio. - * - */ - - /** Generate a number of samples. This function expects two signed - * 16bits buffers (left and right channel) that will be filled with - * samples. - * - * \param synth The synthesizer - * \param len The number of samples to generate - * \param lout The sample buffer for the left channel - * \param loff The offset, in samples, in the left buffer where the writing pointer starts - * \param lincr The increment, in samples, of the writing pointer in the left buffer - * \param rout The sample buffer for the right channel - * \param roff The offset, in samples, in the right buffer where the writing pointer starts - * \param rincr The increment, in samples, of the writing pointer in the right buffer - * \returns 0 if no error occured, non-zero otherwise - */ +/* + * Synthesizer plugin + * + * To create a synthesizer plugin, create the synthesizer as + * explained above. Once the synthesizer is created you can call + * any of the functions below to get the audio. + */ FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t* synth, int len, void* lout, int loff, int lincr, void* rout, int roff, int rincr); - - - /** Generate a number of samples. This function expects two floating - * point buffers (left and right channel) that will be filled with - * samples. - * - * \param synth The synthesizer - * \param len The number of samples to generate - * \param lout The sample buffer for the left channel - * \param loff The offset, in samples, in the left buffer where the writing pointer starts - * \param lincr The increment, in samples, of the writing pointer in the left buffer - * \param rout The sample buffer for the right channel - * \param roff The offset, in samples, in the right buffer where the writing pointer starts - * \param rincr The increment, in samples, of the writing pointer in the right buffer - * \returns 0 if no error occured, non-zero otherwise - */ - FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t* synth, int len, void* lout, int loff, int lincr, void* rout, int roff, int rincr); - FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t* synth, int len, float** left, float** right, float** fx_left, float** fx_right); - - /** Generate a number of samples. This function implements the - * default interface defined in fluidsynth/audio.h. This function - * ignores the input buffers and expects at least two output - * buffer. - * - * \param synth The synthesizer - * \param len The number of samples to generate - * \param nin The number of input buffers - * \param in The array of input buffers - * \param nout The number of output buffers - * \param out The array of output buffers - * \returns 0 if no error occured, non-zero otherwise - */ - FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in, int nout, float** out); - - - /* Type definition of the synthesizer's audio callback function. */ +/** + * Type definition of the synthesizer's audio callback function. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param out1 Array to store left channel of audio to + * @param loff Offset index in 'out1' for first sample + * @param lincr Increment between samples stored to 'out1' + * @param out2 Array to store right channel of audio to + * @param roff Offset index in 'out2' for first sample + * @param rincr Increment between samples stored to 'out2' + */ typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len, void* out1, int loff, int lincr, void* out2, int roff, int rincr); +/* Synthesizer's interface to handle SoundFont loaders */ - - - - /* - * Synthesizer's interface to handle SoundFont loaders - */ - - - /** Add a SoundFont loader to the synthesizer. Note that SoundFont - loader don't necessarily load SoundFonts. They can load any type - of wavetable data but export a SoundFont interface. */ FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader); - - /** Allocate a synthesis voice. This function is called by a - soundfont's preset in response to a noteon event. - The returned voice comes with default modulators installed (velocity-to-attenuation, - velocity to filter, ...) - Note: A single noteon event may create any number of voices, when the preset is layered. - Typically 1 (mono) or 2 (stereo).*/ -FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, - int channum, int key, int vel); - - /** Start a synthesis voice. This function is called by a - soundfont's preset in response to a noteon event after the voice - has been allocated with fluid_synth_alloc_voice() and - initialized. - Exclusive classes are processed here.*/ +FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, + int channum, int key, int vel); FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice); - - - /** Write a list of all voices matching ID into buf, but not more than bufsize voices. - * If ID <0, return all voices. */ -FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, - fluid_voice_t* buf[], int bufsize, int ID); - - - /** Callback function for the MIDI router. Any event goes through this. */ +FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, + fluid_voice_t* buf[], int bufsize, int ID); FLUIDSYNTH_API int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event); - - - /** This is a hack to get command handlers working */ -FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth, - fluid_midi_router_t* router); +FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth, + fluid_midi_router_t* router); #ifdef __cplusplus } diff --git a/fluidsynth/src/Makefile.am b/fluidsynth/src/Makefile.am index bc967415..46af2e60 100644 --- a/fluidsynth/src/Makefile.am +++ b/fluidsynth/src/Makefile.am @@ -82,6 +82,8 @@ libfluidsynth_la_SOURCES = \ fluid_dsp_float.c \ fluid_event.c \ fluid_event_priv.h \ + fluid_event_queue.c \ + fluid_event_queue.h \ fluid_gen.c \ fluid_gen.h \ fluid_hash.c \ diff --git a/fluidsynth/src/fluid_adriver.c b/fluidsynth/src/fluid_adriver.c index 6ad127a5..37f65404 100644 --- a/fluidsynth/src/fluid_adriver.c +++ b/fluidsynth/src/fluid_adriver.c @@ -332,8 +332,6 @@ new_fluid_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) char* name; char allnames[256]; - fluid_settings_getstr(settings, "audio.driver", &name); - for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { if (fluid_settings_str_equal(settings, "audio.driver", fluid_audio_drivers[i].name)) { FLUID_LOG(FLUID_DBG, "Using '%s' audio driver", fluid_audio_drivers[i].name); @@ -346,7 +344,10 @@ new_fluid_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) } fluid_audio_driver_get_names(allnames, sizeof(allnames), ", "); - FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver %s. Valid drivers are: %s.", name, allnames); + fluid_settings_dupstr(settings, "audio.driver", &name); /* ++ alloc name */ + FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver %s. Valid drivers are: %s.", + name ? name : "NULL", allnames); + if (name) FLUID_FREE (name); return NULL; } @@ -370,8 +371,6 @@ new_fluid_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, voi fluid_audio_driver_t* driver = NULL; char* name; - fluid_settings_getstr(settings, "audio.driver", &name); - for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { if (fluid_settings_str_equal(settings, "audio.driver", fluid_audio_drivers[i].name) && (fluid_audio_drivers[i].new2 != NULL)) { @@ -384,7 +383,10 @@ new_fluid_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, voi } } - FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver: %s", name); + fluid_settings_dupstr(settings, "audio.driver", &name); /* ++ alloc name */ + FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver: %s", + name ? name : "NULL"); + if (name) FLUID_FREE (name); return NULL; } diff --git a/fluidsynth/src/fluid_alsa.c b/fluidsynth/src/fluid_alsa.c index 25e738f0..f7b581fc 100644 --- a/fluidsynth/src/fluid_alsa.c +++ b/fluidsynth/src/fluid_alsa.c @@ -172,7 +172,7 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, fluid_alsa_audio_driver_t* dev; double sample_rate; int periods, period_size; - char* device; + char* device = NULL; pthread_attr_t attr; int sched = SCHED_FIFO; struct sched_param priority; @@ -192,7 +192,7 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); - fluid_settings_getstr(settings, "audio.alsa.device", &device); + fluid_settings_dupstr(settings, "audio.alsa.device", &device); /* ++ dup device name */ dev->data = data; dev->callback = func; @@ -200,12 +200,15 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, dev->buffer_size = period_size; /* Open the PCM device */ - if ((err = snd_pcm_open(&dev->pcm, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) != 0) { + if ((err = snd_pcm_open(&dev->pcm, device ? device : "default", + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) != 0) { if (err == -EBUSY) { - FLUID_LOG(FLUID_ERR, "The \"%s\" audio device is used by another application", device); + FLUID_LOG(FLUID_ERR, "The \"%s\" audio device is used by another application", + device ? device : "default"); goto error_recovery; } else { - FLUID_LOG(FLUID_ERR, "Failed to open the \"%s\" audio device", device); + FLUID_LOG(FLUID_ERR, "Failed to open the \"%s\" audio device", + device ? device : "default"); goto error_recovery; } } @@ -362,9 +365,12 @@ new_fluid_alsa_audio_driver2(fluid_settings_t* settings, break; } + if (device) FLUID_FREE (device); /* -- free device name */ + return (fluid_audio_driver_t*) dev; error_recovery: + if (device) FLUID_FREE (device); /* -- free device name */ delete_fluid_alsa_audio_driver((fluid_audio_driver_t*) dev); return NULL; } @@ -661,13 +667,11 @@ new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, } /* get the device name. if none is specified, use the default device. */ - fluid_settings_getstr(settings, "midi.alsa.device", &device); - if (device == NULL) { - device = "default"; - } + fluid_settings_dupstr(settings, "midi.alsa.device", &device); /* ++ alloc device name */ /* open the hardware device. only use midi in. */ - if ((err = snd_rawmidi_open(&dev->rawmidi_in, NULL, device, SND_RAWMIDI_NONBLOCK)) < 0) { + if ((err = snd_rawmidi_open(&dev->rawmidi_in, NULL, device ? device : "default", + SND_RAWMIDI_NONBLOCK)) < 0) { FLUID_LOG(FLUID_ERR, "Error opening ALSA raw MIDI port"); goto error_recovery; } @@ -746,9 +750,13 @@ new_fluid_alsa_rawmidi_driver(fluid_settings_t* settings, } break; } + + if (device) FLUID_FREE (device); /* -- free device name */ + return (fluid_midi_driver_t*) dev; error_recovery: + if (device) FLUID_FREE (device); /* -- free device name */ delete_fluid_alsa_rawmidi_driver((fluid_midi_driver_t*) dev); return NULL; @@ -897,12 +905,11 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings, struct sched_param priority; int count; struct pollfd *pfd = NULL; - char * device = NULL; - char * id; - char * portname; + char *device = NULL; + char *id = NULL; + char *portname = NULL; char full_id[64]; char full_name[64]; - char id_pid[16]; snd_seq_port_info_t *port_info = NULL; int midi_channels; @@ -924,24 +931,33 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings, /* get the device name. if none is specified, use the default device. */ - fluid_settings_getstr(settings, "midi.alsa_seq.device", &device); - if (device == NULL) { - device = "default"; - } + if (fluid_settings_dupstr(settings, "midi.alsa_seq.device", &device) == 0) /* ++ alloc device name */ + goto error_recovery; + + if (fluid_settings_dupstr(settings, "midi.alsa_seq.id", &id) == 0) /* ++ alloc id string */ + goto error_recovery; - fluid_settings_getstr(settings, "midi.alsa_seq.id", &id); if (id == NULL) { - sprintf(id_pid, "%d", getpid()); - id = id_pid; + id = FLUID_MALLOC (32); + if (!id) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + sprintf(id, "%d", getpid()); } /* get the midi portname */ - fluid_settings_getstr(settings, "midi.portname", &portname); - if (!strcmp(portname, "")) - portname = NULL; + fluid_settings_dupstr(settings, "midi.portname", &portname); + if (portname && FLUID_STRLEN (portname) == 0) + { + FLUID_FREE (portname); /* -- free port name */ + portname = NULL; + } /* open the sequencer INPUT only */ - err = snd_seq_open(&dev->seq_handle, device, SND_SEQ_OPEN_INPUT, 0); + err = snd_seq_open(&dev->seq_handle, device ? device : "default", SND_SEQ_OPEN_INPUT, 0); if (err < 0) { FLUID_LOG(FLUID_ERR, "Error opening ALSA sequencer"); goto error_recovery; @@ -1056,10 +1072,21 @@ new_fluid_alsa_seq_driver(fluid_settings_t* settings, } break; } + + if (portname) FLUID_FREE (portname); + if (id) FLUID_FREE (id); + if (device) FLUID_FREE (device); + return (fluid_midi_driver_t*) dev; error_recovery: + + if (portname) FLUID_FREE (portname); + if (id) FLUID_FREE (id); + if (device) FLUID_FREE (device); + delete_fluid_alsa_seq_driver((fluid_midi_driver_t*) dev); + return NULL; } diff --git a/fluidsynth/src/fluid_aufile.c b/fluidsynth/src/fluid_aufile.c index d23ad3d4..3d49c268 100644 --- a/fluidsynth/src/fluid_aufile.c +++ b/fluidsynth/src/fluid_aufile.c @@ -72,7 +72,7 @@ new_fluid_file_audio_driver(fluid_settings_t* settings, { fluid_file_audio_driver_t* dev; int err; - char* filename; + char* filename = NULL; int msec; dev = FLUID_NEW(fluid_file_audio_driver_t); @@ -89,19 +89,21 @@ new_fluid_file_audio_driver(fluid_settings_t* settings, dev->callback = (fluid_audio_func_t) fluid_synth_process; dev->samples = 0; - if (fluid_settings_getstr(settings, "audio.file.name", &filename) == 0) { + if (fluid_settings_dupstr(settings, "audio.file.name", &filename) == 0) { /* ++ alloc filename */ FLUID_LOG(FLUID_ERR, "No file name specified"); goto error_recovery; } dev->renderer = new_fluid_file_renderer(synth, filename, dev->period_size); if (dev->renderer == NULL) { + if (filename) FLUID_FREE (filename); /* -- free filename */ goto error_recovery; } + if (filename) FLUID_FREE (filename); /* -- free filename */ msec = (int) (0.5 + dev->period_size / dev->sample_rate * 1000.0); - dev->timer = new_fluid_timer(msec, fluid_file_audio_run_s16, (void*) dev, 1, 0); + dev->timer = new_fluid_timer(msec, fluid_file_audio_run_s16, (void*) dev, TRUE, FALSE, TRUE); if (dev->timer == NULL) { FLUID_LOG(FLUID_PANIC, "Couldn't create the audio thread."); goto error_recovery; diff --git a/fluidsynth/src/fluid_chan.c b/fluidsynth/src/fluid_chan.c index 8dbfd3b3..0c0657a8 100644 --- a/fluidsynth/src/fluid_chan.c +++ b/fluidsynth/src/fluid_chan.c @@ -23,11 +23,24 @@ #include "fluid_synth.h" #include "fluid_sfont.h" +/* Field shift amounts for sfont_bank_prog bit field integer */ +#define PROG_SHIFTVAL 0 +#define BANK_SHIFTVAL 7 +#define SFONT_SHIFTVAL 21 + +/* Field mask values for sfont_bank_prog bit field integer */ +#define PROG_MASKVAL 0x0000007F +#define BANK_MASKVAL 0x001FFF80 +#define BANKLSB_MASKVAL 0x00003F80 +#define BANKMSB_MASKVAL 0x001FC000 +#define SFONT_MASKVAL 0xFFE00000 + + #define SETCC(_c,_n,_v) _c->cc[_n] = _v -/* - * new_fluid_channel - */ +static void fluid_channel_init(fluid_channel_t* chan); + + fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num) { @@ -49,15 +62,20 @@ new_fluid_channel(fluid_synth_t* synth, int num) return chan; } -void +static void fluid_channel_init(fluid_channel_t* chan) { - chan->prognum = 0; - chan->banknum = (chan->channum == 9)? 128 : 0; /* ?? */ - chan->sfontnum = 0; + fluid_preset_t *newpreset; + int prognum, banknum; - if (chan->preset) delete_fluid_preset (chan->preset); - chan->preset = fluid_synth_find_preset(chan->synth, chan->banknum, chan->prognum); + prognum = 0; + banknum = (chan->channum == 9)? 128 : 0; /* ?? */ + + chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL + | prognum << PROG_SHIFTVAL; + + newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum); + fluid_channel_set_preset(chan, newpreset); chan->interp_method = FLUID_INTERP_DEFAULT; chan->tuning = NULL; @@ -142,16 +160,7 @@ fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off) } } -void -fluid_channel_reset(fluid_channel_t* chan) -{ - fluid_channel_init(chan); - fluid_channel_init_ctrl(chan, 0); -} - -/* - * delete_fluid_channel - */ +/* Only called by delete_fluid_synth(), so no need to queue a preset free event */ int delete_fluid_channel(fluid_channel_t* chan) { @@ -160,275 +169,158 @@ delete_fluid_channel(fluid_channel_t* chan) return FLUID_OK; } -/* - * fluid_channel_set_preset - */ +void +fluid_channel_reset(fluid_channel_t* chan) +{ + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan, 0); +} + +/* Should only be called from synthesis context */ int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset) { - fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); - fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum); + fluid_event_queue_elem_t *event; + + fluid_preset_notify (chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); + + if (chan->preset) /* Queue preset free (shouldn't free() in synth context) */ + { + event = fluid_event_queue_get_inptr (chan->synth->return_queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synth return event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_FREE_PRESET; + event->pval = chan->preset; + fluid_event_queue_next_inptr (chan->synth->return_queue); + } - if (chan->preset) delete_fluid_preset (chan->preset); chan->preset = preset; + + fluid_preset_notify (preset, FLUID_PRESET_SELECTED, chan->channum); + return FLUID_OK; } -/* - * fluid_channel_get_preset - */ fluid_preset_t* fluid_channel_get_preset(fluid_channel_t* chan) { return chan->preset; } -/* - * fluid_channel_get_banknum - */ -unsigned int -fluid_channel_get_banknum(fluid_channel_t* chan) +/* Set SoundFont ID, MIDI bank and/or program. Use -1 to use current value. */ +void +fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfontnum, + int banknum, int prognum) { - return chan->banknum; -} + int oldval, newval, oldmask; -/* - * fluid_channel_set_prognum - */ -int -fluid_channel_set_prognum(fluid_channel_t* chan, int prognum) -{ - chan->prognum = prognum; - return FLUID_OK; -} + newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0) + | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0) + | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0); -/* - * fluid_channel_get_prognum - */ -int -fluid_channel_get_prognum(fluid_channel_t* chan) -{ - return chan->prognum; -} + oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL) + | ((banknum != -1) ? 0 : BANK_MASKVAL) + | ((prognum != -1) ? 0 : PROG_MASKVAL); -/* - * fluid_channel_set_banknum - */ -int -fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int banknum) -{ - chan->banknum = banknum; - return FLUID_OK; -} - -/* - * fluid_channel_cc - */ -int -fluid_channel_cc(fluid_channel_t* chan, int num, int value) -{ - chan->cc[num] = value; - - switch (num) { - - case SUSTAIN_SWITCH: - { - if (value < 64) { -/* printf("** sustain off\n"); */ - fluid_synth_damp_voices(chan->synth, chan->channum); - } else { -/* printf("** sustain on\n"); */ - } - } - break; - - case BANK_SELECT_MSB: - { - fluid_channel_set_banknum(chan, (((unsigned int) value & 0x7F) << 7) + - (chan->banknum & 0x7F)); - } - break; - - case BANK_SELECT_LSB: - { - /* FIXME: according to the Downloadable Sounds II specification, - bit 31 should be set when we receive the message on channel - 10 (drum channel) */ - fluid_channel_set_banknum(chan, ((unsigned int) value & 0x7F) - + (chan->banknum & ~0x7F)); - } - break; - - case ALL_NOTES_OFF: - fluid_synth_all_notes_off(chan->synth, chan->channum); - break; - - case ALL_SOUND_OFF: - fluid_synth_all_sounds_off(chan->synth, chan->channum); - break; - - case ALL_CTRL_OFF: - fluid_channel_init_ctrl(chan, 1); - fluid_synth_modulate_voices_all(chan->synth, chan->channum); - break; - - case DATA_ENTRY_MSB: - { - int data = (value << 7) + chan->cc[DATA_ENTRY_LSB]; - - if (chan->nrpn_active) /* NRPN is active? */ - { - /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ - if ((chan->cc[NRPN_MSB] == 120) && (chan->cc[NRPN_LSB] < 100)) - { - if (chan->nrpn_select < GEN_LAST) - { - float val = fluid_gen_scale_nrpn(chan->nrpn_select, data); - fluid_synth_set_gen(chan->synth, chan->channum, chan->nrpn_select, val); - } - - chan->nrpn_select = 0; /* Reset to 0 */ - } - } - else if (chan->cc[RPN_MSB] == 0) /* RPN is active: MSB = 0? */ - { - switch (chan->cc[RPN_LSB]) - { - case RPN_PITCH_BEND_RANGE: - fluid_channel_pitch_wheel_sens (chan, value); /* Set bend range in semitones */ - /* FIXME - Handle LSB? (Fine bend range in cents) */ - break; - case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over 1 semitone (+/- 50 cents, 8192 = center) */ - fluid_synth_set_gen(chan->synth, chan->channum, GEN_FINETUNE, - (data - 8192) / 8192.0 * 50.0); - break; - case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ - fluid_synth_set_gen(chan->synth, chan->channum, GEN_COARSETUNE, - value - 64); - break; - case RPN_TUNING_PROGRAM_CHANGE: - break; - case RPN_TUNING_BANK_SELECT: - break; - case RPN_MODULATION_DEPTH_RANGE: - break; - } - } - - break; - } - - case NRPN_MSB: - chan->cc[NRPN_LSB] = 0; - chan->nrpn_select = 0; - chan->nrpn_active = 1; - break; - - case NRPN_LSB: - /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ - if (chan->cc[NRPN_MSB] == 120) { - if (value == 100) { - chan->nrpn_select += 100; - } else if (value == 101) { - chan->nrpn_select += 1000; - } else if (value == 102) { - chan->nrpn_select += 10000; - } else if (value < 100) { - chan->nrpn_select += value; - } - } - - chan->nrpn_active = 1; - break; - - case RPN_MSB: - case RPN_LSB: - chan->nrpn_active = 0; - break; - - default: - fluid_synth_modulate_voices(chan->synth, chan->channum, 1, num); + /* Loop until SoundFont, bank and program integer is atomically assigned */ + do + { + oldval = fluid_atomic_int_get (&chan->sfont_bank_prog); + newval = (newval & ~oldmask) | (oldval & oldmask); } - - return FLUID_OK; + while (newval != oldval + && !fluid_atomic_int_compare_and_exchange (&chan->sfont_bank_prog, + oldval, newval)); } -/* - * fluid_channel_get_cc - */ +/* Set bank LSB 7 bits */ +void +fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb) +{ + int oldval, newval; + + /* Loop until bank LSB is atomically assigned */ + do + { + oldval = fluid_atomic_int_get (&chan->sfont_bank_prog); + newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL); + } + while (newval != oldval + && !fluid_atomic_int_compare_and_exchange (&chan->sfont_bank_prog, + oldval, newval)); +} + +/* Set bank MSB 7 bits */ +void +fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb) +{ + int oldval, newval; + + /* Loop until bank MSB is atomically assigned */ + do + { + oldval = fluid_atomic_int_get (&chan->sfont_bank_prog); + newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7)); + } + while (newval != oldval + && !fluid_atomic_int_compare_and_exchange (&chan->sfont_bank_prog, + oldval, newval)); +} + +/* Get SoundFont ID, MIDI bank and/or program. Use NULL to ignore a value. */ +void +fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont, + int *bank, int *prog) +{ + int sfont_bank_prog; + + sfont_bank_prog = fluid_atomic_int_get (&chan->sfont_bank_prog); + + if (sfont) *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL; + if (bank) *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL; + if (prog) *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL; +} + +/* Set MIDI custom controller value for a channel */ +void +fluid_channel_set_cc(fluid_channel_t* chan, int num, int val) +{ + if (num >= 0 && num < 128) + fluid_atomic_int_set (&chan->cc[num], val); +} + +/* Get MIDI custom controller value for a channel */ int fluid_channel_get_cc(fluid_channel_t* chan, int num) { - return ((num >= 0) && (num < 128))? chan->cc[num] : 0; + return ((num >= 0) && (num < 128)) ? fluid_atomic_int_get (&chan->cc[num]) : 0; } -/* - * fluid_channel_pressure - */ -int -fluid_channel_pressure(fluid_channel_t* chan, int val) -{ - chan->channel_pressure = val; - fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_CHANNELPRESSURE); - return FLUID_OK; -} - -/* - * fluid_channel_pitch_bend - */ -int -fluid_channel_pitch_bend(fluid_channel_t* chan, int val) -{ - chan->pitch_bend = val; - fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEEL); - return FLUID_OK; -} - -/* - * fluid_channel_pitch_wheel_sens - */ -int -fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val) -{ - chan->pitch_wheel_sensitivity = val; - fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEELSENS); - return FLUID_OK; -} - -/* - * fluid_channel_get_num - */ +/* Get MIDI channel number */ int fluid_channel_get_num(fluid_channel_t* chan) { - return chan->channum; + return chan->channum; /* Set only once on channel init */ } -/* Purpose: +/* * Sets the index of the interpolation method used on this channel, * as in fluid_interp in fluidsynth.h */ void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method) { - chan->interp_method = new_method; + fluid_atomic_int_set (&chan->interp_method, new_method); } -/* Purpose: +/* * Returns the index of the interpolation method used on this channel, * as in fluid_interp in fluidsynth.h */ int fluid_channel_get_interp_method(fluid_channel_t* chan) { - return chan->interp_method; -} - -unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan) -{ - return chan->sfontnum; -} - -int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfontnum) -{ - chan->sfontnum = sfontnum; - return FLUID_OK; + return fluid_atomic_int_get (&chan->interp_method); } diff --git a/fluidsynth/src/fluid_chan.h b/fluidsynth/src/fluid_chan.h index fffdfa14..84f2493e 100644 --- a/fluidsynth/src/fluid_chan.h +++ b/fluidsynth/src/fluid_chan.h @@ -27,36 +27,58 @@ /* * fluid_channel_t + * + * Mutual exclusion notes: + * + * Set once on init: + * channum + * synth + * + * Synthesis thread context only: + * preset + * key_pressure + * tuning + * nrpn_select + * nrpn_active + * gen[] + * gen_abs[] + * + * Uses atomic operations: + * sfont_bank_prog + * channel_pressure + * pitch_bend + * pitch_wheel_sensitivity + * cc[] + * interp_method */ struct _fluid_channel_t { - int channum; - unsigned int sfontnum; - unsigned int banknum; - unsigned int prognum; - fluid_preset_t* preset; - fluid_synth_t* synth; - short key_pressure; - short channel_pressure; - short pitch_bend; - short pitch_wheel_sensitivity; + fluid_mutex_t mutex; /* Lock for thread sensitive parameters */ - /* controller values */ - short cc[128]; + fluid_synth_t* synth; /**> Parent synthesizer instance */ + int channum; /**> MIDI channel number */ - int interp_method; + int sfont_bank_prog; /**> SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ + fluid_preset_t* preset; /**> Selected preset */ - /* the micro-tuning */ - fluid_tuning_t* tuning; + int key_pressure; /**> MIDI key pressure */ + int channel_pressure; /**> MIDI channel pressure */ + int pitch_bend; /**> Current pitch bend value */ + int pitch_wheel_sensitivity; /**> Current pitch wheel sensitivity */ + + int cc[128]; /**> MIDI controller values */ + + int interp_method; /**> Interpolation method (enum fluid_interp) */ + fluid_tuning_t* tuning; /**> Micro tuning */ /* NRPN system */ - short nrpn_select; - short nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ + int nrpn_select; /* Generator ID of SoundFont NRPN message */ + int nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ /* The values of the generators, set by NRPN messages, or by * fluid_synth_set_gen(), are cached in the channel so they can be * applied to future notes. They are copied to a voice's generators - * in fluid_voice_init(), wihich calls fluid_gen_init(). */ + * in fluid_voice_init(), which calls fluid_gen_init(). */ fluid_real_t gen[GEN_LAST]; /* By default, the NRPN values are relative to the values of the @@ -73,22 +95,18 @@ struct _fluid_channel_t }; fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num); -int delete_fluid_channel(fluid_channel_t* chan); -void fluid_channel_init(fluid_channel_t* chan); void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off); +int delete_fluid_channel(fluid_channel_t* chan); void fluid_channel_reset(fluid_channel_t* chan); int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset); fluid_preset_t* fluid_channel_get_preset(fluid_channel_t* chan); -unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan); -int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfont); -unsigned int fluid_channel_get_banknum(fluid_channel_t* chan); -int fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int bank); -int fluid_channel_set_prognum(fluid_channel_t* chan, int prognum); -int fluid_channel_get_prognum(fluid_channel_t* chan); -int fluid_channel_cc(fluid_channel_t* chan, int ctrl, int val); -int fluid_channel_pressure(fluid_channel_t* chan, int val); -int fluid_channel_pitch_bend(fluid_channel_t* chan, int val); -int fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val); +void fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfont, + int bank, int prog); +void fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb); +void fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb); +void fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont, + int *bank, int *prog); +void fluid_channel_set_cc(fluid_channel_t* chan, int num, int val); int fluid_channel_get_cc(fluid_channel_t* chan, int num); int fluid_channel_get_num(fluid_channel_t* chan); void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method); diff --git a/fluidsynth/src/fluid_chorus.c b/fluidsynth/src/fluid_chorus.c index 0f199951..53b468ed 100644 --- a/fluidsynth/src/fluid_chorus.c +++ b/fluidsynth/src/fluid_chorus.c @@ -134,8 +134,10 @@ struct _fluid_chorus_t { fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES]; }; -void fluid_chorus_triangle(int *buf, int len, int depth); -void fluid_chorus_sine(int *buf, int len, int depth); +static void fluid_chorus_update(fluid_chorus_t* chorus); +static void fluid_chorus_triangle(int *buf, int len, int depth); +static void fluid_chorus_sine(int *buf, int len, int depth); + fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate) @@ -202,110 +204,6 @@ new_fluid_chorus(fluid_real_t sample_rate) return NULL; } - -int -fluid_chorus_init(fluid_chorus_t* chorus) -{ - int i; - - for (i = 0; i < MAX_SAMPLES; i++) { - chorus->chorusbuf[i] = 0.0; - } - - /* initialize the chorus with the default settings */ - fluid_chorus_set_nr(chorus, FLUID_CHORUS_DEFAULT_N); - fluid_chorus_set_level(chorus, FLUID_CHORUS_DEFAULT_LEVEL); - fluid_chorus_set_speed_Hz(chorus, FLUID_CHORUS_DEFAULT_SPEED); - fluid_chorus_set_depth_ms(chorus, FLUID_CHORUS_DEFAULT_DEPTH); - fluid_chorus_set_type(chorus, FLUID_CHORUS_MOD_SINE); - - return fluid_chorus_update(chorus); -} - -/* Purpose: - * Sets the number of stages. - * Requires call to fluid_chorus_update afterwards. - * Range checking is performed there.*/ -void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr) -{ - chorus->new_number_blocks = nr; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -int fluid_chorus_get_nr(fluid_chorus_t* chorus) -{ - return chorus->number_blocks; -}; - -/* Purpose: - * Sets the mixing level of the signal from each delay line (linear). - * Requires calling fluid_chorus_update afterwards.*/ -void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level) -{ - chorus->new_level = level; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus) -{ - return chorus->level; -}; - -/* Purpose: - * Sets the modulation frequency. - * Requires call to fluid_chorus_update afterwards. - * Range checking is performed there.*/ -void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz) -{ - chorus->new_speed_Hz = speed_Hz; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus) -{ - return chorus->speed_Hz; -}; - -/* Purpose: - * Sets the modulation depth in ms. - * Requires call to fluid_chorus_update afterwards. - * Range checking is performed there.*/ -void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms) -{ - chorus->new_depth_ms=depth_ms; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus) -{ - return chorus->depth_ms; -}; - -/* Purpose: - * Sets the type of the modulation waveform. - * Requires call to fluid_chorus_update afterwards. - * Check for meaningful values is performed there.*/ -void fluid_chorus_set_type(fluid_chorus_t* chorus, int type) -{ - chorus->new_type=type; -} - -/* Purpose: - * API function, read the current state of the chorus - */ -int fluid_chorus_get_type(fluid_chorus_t* chorus) -{ - return chorus->type; -}; - void delete_fluid_chorus(fluid_chorus_t* chorus) { @@ -324,11 +222,56 @@ delete_fluid_chorus(fluid_chorus_t* chorus) FLUID_FREE(chorus); } +int +fluid_chorus_init(fluid_chorus_t* chorus) +{ + int i; + + for (i = 0; i < MAX_SAMPLES; i++) { + chorus->chorusbuf[i] = 0.0; + } + + /* initialize the chorus with the default settings */ + fluid_chorus_set (chorus, FLUID_CHORUS_SET_ALL, FLUID_CHORUS_DEFAULT_N, + FLUID_CHORUS_DEFAULT_LEVEL, FLUID_CHORUS_DEFAULT_SPEED, + FLUID_CHORUS_DEFAULT_DEPTH, FLUID_CHORUS_MOD_SINE); + return FLUID_OK; +} + +void +fluid_chorus_reset(fluid_chorus_t* chorus) +{ + fluid_chorus_init(chorus); +} + +/** + * Set one or more chorus parameters. + * @param chorus Chorus instance + * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t) + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) + * @param level Chorus level (0.0-1.0) + * @param speed Chorus speed in Hz (0.29-5.0) + * @param depth_ms Chorus depth (max value depends on synth sample rate, + * 0.0-21.0 is safe for sample rate values up to 96KHz) + * @param type Chorus waveform type (#fluid_chorus_mod) + */ +void +fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level, + float speed, float depth_ms, int type) +{ + if (set & FLUID_CHORUS_SET_NR) chorus->new_number_blocks = nr; + if (set & FLUID_CHORUS_SET_LEVEL) chorus->new_level = level; + if (set & FLUID_CHORUS_SET_SPEED) chorus->new_speed_Hz = speed; + if (set & FLUID_CHORUS_SET_DEPTH) chorus->new_depth_ms = depth_ms; + if (set & FLUID_CHORUS_SET_TYPE) chorus->new_type = type; + + return fluid_chorus_update (chorus); +} /* Purpose: - * Calculates the internal chorus parameters using the settings from - * fluid_chorus_set_xxx. */ -int + * Calculates the internal chorus parameters. */ +static void fluid_chorus_update(fluid_chorus_t* chorus) { int i; @@ -408,18 +351,6 @@ fluid_chorus_update(fluid_chorus_t* chorus) chorus->level = chorus->new_level; chorus->speed_Hz = chorus->new_speed_Hz; chorus->number_blocks = chorus->new_number_blocks; - return FLUID_OK; - -/* failure: */ - /* Note: This lives on the assumption, that the last chorus values were correct. - * If not, this will loop forever and a day. */ -/* fluid_log(FLUID_WARN, "chorus: Restoring last good settings"); */ -/* chorus->new_type = chorus->type; */ -/* chorus->new_depth_ms = chorus->depth_ms; */ -/* chorus->new_level = chorus->level; */ -/* chorus->new_speed_Hz = chorus->speed_Hz; */ -/* chorus->new_number_blocks = chorus->number_blocks; */ -/* return FLUID_FAILED; */ } @@ -567,7 +498,8 @@ void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, * a couple of times here, the resulting (current position in * buffer)-(waveform sample) will always be positive. */ -void fluid_chorus_sine(int *buf, int len, int depth) +static void +fluid_chorus_sine(int *buf, int len, int depth) { int i; double val; @@ -584,7 +516,8 @@ void fluid_chorus_sine(int *buf, int len, int depth) * Calculates a modulation waveform (triangle) * See fluid_chorus_sine for comments. */ -void fluid_chorus_triangle(int *buf, int len, int depth) +static void +fluid_chorus_triangle(int *buf, int len, int depth) { int i=0; int ii=len-1; @@ -598,9 +531,3 @@ void fluid_chorus_triangle(int *buf, int len, int depth) buf[ii--] = (int) val2; } } - -void -fluid_chorus_reset(fluid_chorus_t* chorus) -{ - fluid_chorus_init(chorus); -} diff --git a/fluidsynth/src/fluid_chorus.h b/fluidsynth/src/fluid_chorus.h index e82b2ff8..c9d85c73 100644 --- a/fluidsynth/src/fluid_chorus.h +++ b/fluidsynth/src/fluid_chorus.h @@ -27,30 +27,35 @@ typedef struct _fluid_chorus_t fluid_chorus_t; +/** Flags for fluid_chorus_set() */ +typedef enum +{ + FLUID_CHORUS_SET_NR = 1 << 0, + FLUID_CHORUS_SET_LEVEL = 1 << 1, + FLUID_CHORUS_SET_SPEED = 1 << 2, + FLUID_CHORUS_SET_DEPTH = 1 << 3, + FLUID_CHORUS_SET_TYPE = 1 << 4, +} fluid_chorus_set_t; + +/** Value for fluid_chorus_set() which sets all chorus parameters. */ +#define FLUID_CHORUS_SET_ALL 0x1F + /* * chorus */ fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate); void delete_fluid_chorus(fluid_chorus_t* chorus); +int fluid_chorus_init(fluid_chorus_t* chorus); +void fluid_chorus_reset(fluid_chorus_t* chorus); + +void fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level, + float speed, float depth_ms, int type); + void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); -int fluid_chorus_init(fluid_chorus_t* chorus); -void fluid_chorus_reset(fluid_chorus_t* chorus); - -void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr); -void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level); -void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz); -void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms); -void fluid_chorus_set_type(fluid_chorus_t* chorus, int type); -int fluid_chorus_update(fluid_chorus_t* chorus); -int fluid_chorus_get_nr(fluid_chorus_t* chorus); -fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus); -fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus); -fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus); -int fluid_chorus_get_type(fluid_chorus_t* chorus); #endif /* _FLUID_CHORUS_H */ diff --git a/fluidsynth/src/fluid_cmd.c b/fluidsynth/src/fluid_cmd.c index 08e77def..97d66e2f 100644 --- a/fluidsynth/src/fluid_cmd.c +++ b/fluidsynth/src/fluid_cmd.c @@ -260,19 +260,18 @@ void delete_fluid_shell(fluid_shell_t* shell) int fluid_shell_run(fluid_shell_t* shell) { char workline[FLUID_WORKLINELENGTH]; - char* prompt = ""; + char* prompt = NULL; int cont = 1; int errors = 0; int n; - if (shell->settings) { - fluid_settings_getstr(shell->settings, "shell.prompt", &prompt); - } + if (shell->settings) + fluid_settings_dupstr(shell->settings, "shell.prompt", &prompt); /* ++ alloc prompt */ /* handle user input */ while (cont) { - n = fluid_istream_readline(shell->in, prompt, workline, FLUID_WORKLINELENGTH); + n = fluid_istream_readline(shell->in, prompt ? prompt : "", workline, FLUID_WORKLINELENGTH); if (n < 0) { break; @@ -305,6 +304,8 @@ int fluid_shell_run(fluid_shell_t* shell) } } + if (prompt) FLUID_FREE (prompt); /* -- free prompt */ + return errors; } @@ -684,7 +685,8 @@ fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_os fluid_ostream_printf(out, "rev_setroomsize: Room size too big!\n"); return -1; } - fluid_revmodel_setroomsize(synth->reverb, room_size); + fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ROOMSIZE, + room_size, 0.0, 0.0, 0.0); return 0; } @@ -704,7 +706,8 @@ fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostrea fluid_ostream_printf(out, "rev_setdamp: damp must be between 0 and 1!\n"); return -1; } - fluid_revmodel_setdamp(synth->reverb, damp); + fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_DAMPING, + 0.0, damp, 0.0, 0.0); return 0; } @@ -724,7 +727,8 @@ fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostre fluid_ostream_printf(out, "rev_setroomsize: Too wide! (0..100)\n"); return 0; } - fluid_revmodel_setwidth(synth->reverb, width); + fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_WIDTH, + 0.0, 0.0, width, 0.0); return 0; } @@ -744,7 +748,8 @@ fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostre fluid_ostream_printf(out, "rev_setlevel: Value too high! (Value of 10 =+20 dB)\n"); return 0; } - fluid_revmodel_setlevel(synth->reverb, level); + fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_LEVEL, + 0.0, 0.0, 0.0, level); return 0; } @@ -783,8 +788,7 @@ fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t o return -1; } nr = atoi(av[0]); - fluid_chorus_set_nr(synth->chorus, nr); - return fluid_chorus_update(synth->chorus); + return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_NR, nr, 0.0, 0.0, 0.0, 0); } /* Purpose: @@ -798,9 +802,7 @@ fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_ return -1; } level = atof(av[0]); - fluid_chorus_set_level(synth->chorus, level); - return fluid_chorus_update(synth->chorus); - + return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_LEVEL, 0, level, 0.0, 0.0, 0); } /* Purpose: @@ -814,8 +816,7 @@ fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_ return -1; } speed = atof(av[0]); - fluid_chorus_set_speed_Hz(synth->chorus, speed); - return fluid_chorus_update(synth->chorus); + return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_SPEED, 0, 0.0, speed, 0.0, 0); } /* Purpose: @@ -829,8 +830,7 @@ fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_ return -1; } depth = atof(av[0]); - fluid_chorus_set_depth_ms(synth->chorus, depth); - return fluid_chorus_update(synth->chorus); + return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_DEPTH, 0, 0.0, 0.0, depth, 0); } int @@ -1238,8 +1238,9 @@ fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) case FLUID_STR_TYPE: { char* s; - fluid_synth_getstr(synth, av[0], &s); - fluid_ostream_printf(out, "%s", s); + fluid_synth_dupstr(synth, av[0], &s); /* ++ alloc string */ + fluid_ostream_printf(out, "%s", s ? s : "NULL"); + if (s) FLUID_FREE (s); /* -- free string */ break; } @@ -1295,8 +1296,9 @@ static void fluid_handle_settings_iter2(void* data, char* name, int type) case FLUID_STR_TYPE: { char* s; - fluid_synth_getstr(d->synth, name, &s); - fluid_ostream_printf(d->out, "%s\n", s); + fluid_synth_dupstr(d->synth, name, &s); /* ++ alloc string */ + fluid_ostream_printf(d->out, "%s\n", s ? s : "NULL"); + if (s) FLUID_FREE (s); /* -- free string */ break; } } @@ -1384,13 +1386,15 @@ fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) case FLUID_STR_TYPE: { char *s; - fluid_settings_getstr(settings, av[0], &s); + fluid_settings_dupstr(settings, av[0], &s); /* ++ alloc string */ fluid_ostream_printf(out, "%s:\n", av[0]); fluid_ostream_printf(out, "Type: string\n"); - fluid_ostream_printf(out, "Value: %s\n", s); + fluid_ostream_printf(out, "Value: %s\n", s ? s : "NULL"); fluid_ostream_printf(out, "Default value: %s\n", fluid_settings_getstr_default(settings, av[0])); + if (s) FLUID_FREE (s); + data.out = out; data.first = 1; fluid_ostream_printf(out, "Options: "); @@ -1567,9 +1571,10 @@ void delete_fluid_cmd(fluid_cmd_t* cmd) * Command handler */ -void fluid_cmd_handler_delete(void* value, int type) +static void +fluid_cmd_handler_destroy_hash_value (void *value) { - delete_fluid_cmd((fluid_cmd_t*) value); + delete_fluid_cmd ((fluid_cmd_t *)value); } fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth) @@ -1582,7 +1587,8 @@ fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth) "source filename Load a file and parse every line as a command" }; - handler = new_fluid_hashtable(fluid_cmd_handler_delete); + handler = new_fluid_hashtable_full (fluid_str_hash, fluid_str_equal, + NULL, fluid_cmd_handler_destroy_hash_value); if (handler == NULL) { return NULL; } @@ -1603,13 +1609,13 @@ fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth) void delete_fluid_cmd_handler(fluid_cmd_handler_t* handler) { - delete_fluid_hashtable(handler); + delete_fluid_hashtable (handler); } int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd) { fluid_cmd_t* copy = fluid_cmd_copy(cmd); - fluid_hashtable_insert(handler, copy->name, copy, 0); + fluid_hashtable_insert(handler, copy->name, copy); return 0; } @@ -1620,17 +1626,15 @@ int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, char* cmd) int fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) { - void *vp; /* use a void pointer to avoid GCC "type-punned pointer" warning */ fluid_cmd_t* cmd; - if (fluid_hashtable_lookup(handler, av[0], &vp, NULL) - && ((fluid_cmd_t *)vp)->handler) { - cmd = vp; + cmd = fluid_hashtable_lookup(handler, av[0]); + + if (cmd && cmd->handler) return (*cmd->handler)(cmd->data, ac - 1, av + 1, out); - } else { - fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]); - return -1; - } + + fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]); + return -1; } diff --git a/fluidsynth/src/fluid_coremidi.c b/fluidsynth/src/fluid_coremidi.c index a48a1c10..5f02a9c8 100644 --- a/fluidsynth/src/fluid_coremidi.c +++ b/fluidsynth/src/fluid_coremidi.c @@ -91,8 +91,8 @@ new_fluid_coremidi_driver(fluid_settings_t* settings, handle_midi_event_func_t h goto error_recovery; } - fluid_settings_getstr(settings, "midi.coremidi.id", &id); - if (FLUID_STRCMP(id, "pid") == 0) + fluid_settings_dupstr(settings, "midi.coremidi.id", &id); /* ++ alloc id string */ + if (!id || FLUID_STRCMP(id, "pid") == 0) str_clientname = CFStringCreateWithFormat(NULL, NULL, CFSTR("FluidSynth %qi"), (long long) getpid()); @@ -100,8 +100,10 @@ new_fluid_coremidi_driver(fluid_settings_t* settings, handle_midi_event_func_t h str_clientname = CFStringCreateWithFormat(NULL, NULL, CFSTR("FluidSynth %s"), id); - fluid_settings_getstr(settings, "midi.portname", &portname); - if (strlen(portname) == 0) + if (id) FLUID_FREE (id); /* -- free id string */ + + fluid_settings_dupstr(settings, "midi.portname", &portname); /* ++ alloc port name */ + if (!portname || strlen(portname) == 0) str_portname = CFStringCreateWithFormat(NULL, NULL, CFSTR("FluidSynth virtual port %qi"), (long long) getpid()); @@ -109,6 +111,8 @@ new_fluid_coremidi_driver(fluid_settings_t* settings, handle_midi_event_func_t h str_portname = CFStringCreateWithCString(NULL, portname, kCFStringEncodingASCII); + if (portname) FLUID_FREE (portname); /* -- free port name */ + OSStatus result = MIDIClientCreate( str_clientname, NULL, NULL, &client ); if ( result != noErr ) { FLUID_LOG(FLUID_ERR, "Failed to create the MIDI input client"); diff --git a/fluidsynth/src/fluid_dsound.c b/fluidsynth/src/fluid_dsound.c index 8dda8eb9..454076bd 100644 --- a/fluidsynth/src/fluid_dsound.c +++ b/fluidsynth/src/fluid_dsound.c @@ -169,11 +169,14 @@ new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) devsel.devGUID = NULL; /* get the selected device name. if none is specified, use NULL for the default device. */ - if(fluid_settings_getstr(settings, "audio.dsound.device", &devsel.devname)) { + if(fluid_settings_dupstr(settings, "audio.dsound.device", &devsel.devname) /* ++ alloc device name */ + && devsel.devname && strlen (devsel.devname) > 0) { /* look for the GUID of the selected device */ - DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback2, (void *)&devsel); + DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback2, (void *)&devsel); } - + + if (devsel.devname) FLUID_FREE (devsel.devname); /* -- free device name */ + /* open DirectSound */ hr = DirectSoundCreate(devsel.devGUID, &dev->direct_sound, NULL); if (hr != DS_OK) { diff --git a/fluidsynth/src/fluid_event_queue.c b/fluidsynth/src/fluid_event_queue.c new file mode 100644 index 00000000..1d57fe6b --- /dev/null +++ b/fluidsynth/src/fluid_event_queue.c @@ -0,0 +1,155 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +/* + * Josh Green + * 2009-05-28 + */ + +#include "fluid_event_queue.h" +#include "fluidsynth_priv.h" + + +/** + * Create a lock free queue with a fixed maximum count and size of elements. + * @param count Count of elements in queue (fixed max number of queued elements) + * @return New lock free queue or NULL if out of memory (error message logged) + * + * Lockless FIFO queues don't use any locking mechanisms and can therefore be + * advantageous in certain situations, such as passing data between a lower + * priority thread and a higher "real time" thread, without potential lock + * contention which could stall the high priority thread. Note that there may + * only be one producer thread and one consumer thread. + */ +fluid_event_queue_t * +fluid_event_queue_new (int count) +{ + fluid_event_queue_t *queue; + + fluid_return_val_if_fail (count > 0, NULL); + + queue = FLUID_NEW (fluid_event_queue_t); + + if (!queue) + { + FLUID_LOG (FLUID_ERR, "Out of memory"); + return NULL; + } + + queue->array = FLUID_ARRAY (fluid_event_queue_elem_t, count); + + if (!queue->array) + { + FLUID_FREE (queue); + FLUID_LOG (FLUID_ERR, "Out of memory"); + return NULL; + } + + /* Clear array, in case dynamic pointer reclaiming is being done */ + FLUID_MEMSET (queue->array, 0, sizeof (fluid_event_queue_elem_t) * count); + + queue->totalcount = count; + queue->count = 0; + queue->in = 0; + queue->out = 0; + + return (queue); +} + +/** + * Free an event queue. + * @param queue Lockless queue instance + * + * Care must be taken when freeing a queue, to ensure that the consumer and + * producer threads will no longer access it. + */ +void +fluid_event_queue_free (fluid_event_queue_t *queue) +{ + FLUID_FREE (queue->array); + FLUID_FREE (queue); +} + + +/** + * Get pointer to next input array element in queue. + * @param queue Lockless queue instance + * @return Pointer to array element in queue to store data to or NULL if queue is full + * + * This function along with fluid_queue_next_inptr() form a queue "push" + * operation and is split into 2 functions to avoid an element copy. Note that + * the returned array element pointer may contain the data of a previous element + * if the queue has wrapped around. This can be used to reclaim pointers to + * allocated memory, etc. + */ +FLUID_INLINE fluid_event_queue_elem_t * +fluid_event_queue_get_inptr (fluid_event_queue_t *queue) +{ + return fluid_atomic_int_get (&queue->count) == queue->totalcount ? NULL + : queue->array + queue->in; +} + +/** + * Advance the input queue index to complete a "push" operation. + * @param queue Lockless queue instance + * + * This function along with fluid_queue_get_inptr() form a queue "push" + * operation and is split into 2 functions to avoid element copy. + */ +FLUID_INLINE void +fluid_event_queue_next_inptr (fluid_event_queue_t *queue) +{ + fluid_atomic_int_inc (&queue->count); + + if (++queue->in == queue->totalcount) + queue->in = 0; +} + +/** + * Get pointer to next output array element in queue. + * @param queue Lockless queue instance + * @return Pointer to array element data in the queue or NULL if empty, can only + * be used up until fluid_queue_next_outptr() is called. + * + * This function along with fluid_queue_next_outptr() form a queue "pop" + * operation and is split into 2 functions to avoid an element copy. + */ +FLUID_INLINE fluid_event_queue_elem_t * +fluid_event_queue_get_outptr (fluid_event_queue_t *queue) +{ + return fluid_atomic_int_get (&queue->count) == 0 ? NULL + : queue->array + queue->out; +} + +/** + * Advance the output queue index to complete a "pop" operation. + * @param queue Lockless queue instance + * + * This function along with fluid_queue_get_outptr() form a queue "pop" + * operation and is split into 2 functions to avoid an element copy. + */ +FLUID_INLINE void +fluid_event_queue_next_outptr (fluid_event_queue_t *queue) +{ + fluid_atomic_int_dec_and_test (&queue->count); + + if (++queue->out == queue->totalcount) + queue->out = 0; +} diff --git a/fluidsynth/src/fluid_event_queue.h b/fluidsynth/src/fluid_event_queue.h new file mode 100644 index 00000000..464c7cee --- /dev/null +++ b/fluidsynth/src/fluid_event_queue.h @@ -0,0 +1,130 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _FLUID_EVENT_QUEUE_H +#define _FLUID_EVENT_QUEUE_H + +#include "fluid_sys.h" +#include "fluid_midi.h" + +/** + * Type of queued event. + */ +enum fluid_event_queue_elem +{ + FLUID_EVENT_QUEUE_ELEM_MIDI, /**< MIDI event. Uses midi field of event value */ + FLUID_EVENT_QUEUE_ELEM_GAIN, /**< Synth gain set or return event. Uses dval field of event value */ + FLUID_EVENT_QUEUE_ELEM_POLYPHONY, /**< Synth polyphony event. Uses ival field of event value */ + FLUID_EVENT_QUEUE_ELEM_GEN, /**< Generator event. Uses gen field of event value */ + FLUID_EVENT_QUEUE_ELEM_PRESET, /**< Preset set event. Uses preset field of event value */ + FLUID_EVENT_QUEUE_ELEM_STOP_VOICES, /**< Stop voices event. Uses ival field of event value */ + FLUID_EVENT_QUEUE_ELEM_REVERB, /**< Reverb set or return event. Uses reverb field of event value */ + FLUID_EVENT_QUEUE_ELEM_CHORUS, /**< Chorus set or return event. Uses chorus field of event value */ + FLUID_EVENT_QUEUE_ELEM_FREE_PRESET /**< Free a preset return event. Uses pval field of event value */ +}; + +/** + * SoundFont generator set event structure. + */ +typedef struct +{ + int channel; /**< MIDI channel number */ + int param; /**< FluidSynth generator ID */ + float value; /**< Value for the generator (absolute or relative) */ + int absolute; /**< 1 if value is absolute, 0 if relative */ +} fluid_event_gen_t; + +/** + * Preset channel assignment event structure. + */ +typedef struct +{ + int channel; /**< MIDI channel number */ + fluid_preset_t *preset; /**< Preset to assign (synth thread owns) */ +} fluid_event_preset_t; + +/** + * Reverb assignment structure. + */ +typedef struct +{ + char set; /**< Bit 0: roomsize, 1: damping, 2: width, 3: level */ + float roomsize; + float damping; + float width; + float level; +} fluid_event_reverb_t; + +/** + * Chorus assignment structure. + */ +typedef struct +{ + char set; /**< Bit 0: nr, 1: type, 2: level, 3: speed, 4: depth */ + char nr; + char type; + float level; + float speed; + float depth; +} fluid_event_chorus_t; + + +/** + * Event queue element structure. + */ +typedef struct +{ + char type; /**< #fluid_event_queue_elem */ + + union + { + fluid_midi_event_t midi; /**< If type == #FLUID_EVENT_QUEUE_ELEM_MIDI */ + fluid_event_gen_t gen; /**< If type == #FLUID_EVENT_QUEUE_ELEM_GEN */ + fluid_event_preset_t preset; /**< If type == #FLUID_EVENT_QUEUE_ELEM_PRESET */ + fluid_event_reverb_t reverb; /**< If type == #FLUID_EVENT_QUEUE_ELEM_REVERB */ + fluid_event_chorus_t chorus; /**< If type == #FLUID_EVENT_QUEUE_ELEM_CHORUS */ + double dval; /**< A floating point payload value */ + int ival; /**< An integer payload value */ + void *pval; /**< A pointer payload value */ + }; +} fluid_event_queue_elem_t; + +/** + * Lockless event queue instance. + */ +typedef struct +{ + fluid_event_queue_elem_t *array; /**< Queue array of arbitrary size elements */ + int totalcount; /**< Total count of elements in array */ + int count; /**< Current count of elements */ + int in; /**< Index in queue to store next pushed element */ + int out; /**< Index in queue of next popped element */ + void *synth; /**< Owning fluid_synth_t instance */ +} fluid_event_queue_t; + + +fluid_event_queue_t *fluid_event_queue_new (int count); +void fluid_event_queue_free (fluid_event_queue_t *queue); +fluid_event_queue_elem_t *fluid_event_queue_get_inptr (fluid_event_queue_t *queue); +void fluid_event_queue_next_inptr (fluid_event_queue_t *queue); +fluid_event_queue_elem_t *fluid_event_queue_get_outptr (fluid_event_queue_t *queue); +void fluid_event_queue_next_outptr (fluid_event_queue_t *queue); + +#endif /* _FLUID_EVENT_QUEUE_H */ diff --git a/fluidsynth/src/fluid_hash.c b/fluidsynth/src/fluid_hash.c index 9094908a..f02da517 100644 --- a/fluidsynth/src/fluid_hash.c +++ b/fluidsynth/src/fluid_hash.c @@ -8,7 +8,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -22,6 +22,9 @@ * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. + * + * Adapted for FluidSynth use by Josh Green + * September 8, 2009 from glib 2.18.4 */ /* @@ -30,141 +33,773 @@ #include "fluidsynth_priv.h" #include "fluid_hash.h" +#include "fluid_list.h" -#define HASH_TABLE_MIN_SIZE 7 +#define HASH_TABLE_MIN_SIZE 11 #define HASH_TABLE_MAX_SIZE 13845163 -typedef struct _fluid_hashnode_t fluid_hashnode_t; +typedef struct +{ + fluid_hashtable_t *hashtable; + fluid_hashnode_t *prev_node; + fluid_hashnode_t *node; + int position; + int pre_advanced; // Boolean + int version; +} RealIter; -struct _fluid_hashnode_t { - char* key; - void* value; - int type; + +/* Excerpt from glib gprimes.c */ + +static const guint primes[] = +{ + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, +}; + +static const unsigned int nprimes = sizeof (primes) / sizeof (primes[0]); + +unsigned int +spaced_primes_closest (unsigned int num) +{ + unsigned int i; + + for (i = 0; i < nprimes; i++) + if (primes[i] > num) + return primes[i]; + + return primes[nprimes - 1]; +} + +/* End excerpt from glib gprimes.c */ + + +/* + * @hashtable: our #fluid_hashtable_t + * @key: the key to lookup against + * @hash_return: optional key hash return location + * Return value: a pointer to the described #fluid_hashnode_t pointer + * + * Performs a lookup in the hash table. Virtually all hash operations + * will use this function internally. + * + * This function first computes the hash value of the key using the + * user's hash function. + * + * If an entry in the table matching @key is found then this function + * returns a pointer to the pointer to that entry in the table. In + * the case that the entry is at the head of a chain, this pointer + * will be an item in the nodes[] array. In the case that the entry + * is not at the head of a chain, this pointer will be the ->next + * pointer on the node that preceeds it. + * + * In the case that no matching entry exists in the table, a pointer + * to a %NULL pointer will be returned. To insert a item, this %NULL + * pointer should be updated to point to the new #fluid_hashnode_t. + * + * If @hash_return is a pass-by-reference parameter. If it is + * non-%NULL then the computed hash value is returned. This is to + * save insertions from having to compute the hash record again for + * the new record. + */ +static inline fluid_hashnode_t ** +fluid_hashtable_lookup_node (fluid_hashtable_t *hashtable, const void *key, + unsigned int *hash_return) +{ + fluid_hashnode_t **node_ptr, *node; + unsigned int hash_value; + + hash_value = (* hashtable->hash_func)(key); + node_ptr = &hashtable->nodes[hash_value % hashtable->size]; + + if (hash_return) + *hash_return = hash_value; + + /* Hash table lookup needs to be fast. + * We therefore remove the extra conditional of testing + * whether to call the key_equal_func or not from + * the inner loop. + * + * Additional optimisation: first check if our full hash + * values are equal so we can avoid calling the full-blown + * key equality function in most cases. + */ + if (hashtable->key_equal_func) + { + while ((node = *node_ptr)) + { + if (node->key_hash == hash_value && + hashtable->key_equal_func (node->key, key)) + break; + + node_ptr = &(*node_ptr)->next; + } + } + else + { + while ((node = *node_ptr)) + { + if (node->key == key) + break; + + node_ptr = &(*node_ptr)->next; + } + } + + return node_ptr; +} + +/* + * @hashtable: our #fluid_hashtable_t + * @node_ptr_ptr: a pointer to the return value from + * fluid_hashtable_lookup_node() + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Removes a node from the hash table and updates the node count. The + * node is freed. No table resize is performed. + * + * If @notify is %TRUE then the destroy notify functions are called + * for the key and value of the hash node. + * + * @node_ptr_ptr is a pass-by-reference in/out parameter. When the + * function is called, it should point to the pointer to the node to + * remove. This level of indirection is required so that the pointer + * may be updated appropriately once the node has been removed. + * + * Before the function returns, the pointer at @node_ptr_ptr will be + * updated to point to the position in the table that contains the + * pointer to the "next" node in the chain. This makes this function + * convenient to use from functions that iterate over the entire + * table. If there is no further item in the chain then the + * #fluid_hashnode_t pointer will be %NULL (ie: **node_ptr_ptr == %NULL). + * + * Since the pointer in the table to the removed node is replaced with + * either a pointer to the next node or a %NULL pointer as + * appropriate, the pointer at the end of @node_ptr_ptr will never be + * modified at all. Stay tuned. :) + */ +static void +fluid_hashtable_remove_node (fluid_hashtable_t *hashtable, + fluid_hashnode_t ***node_ptr_ptr, int notify) +{ + fluid_hashnode_t **node_ptr, *node; + + node_ptr = *node_ptr_ptr; + node = *node_ptr; + + *node_ptr = node->next; + + if (notify && hashtable->key_destroy_func) + hashtable->key_destroy_func (node->key); + + if (notify && hashtable->value_destroy_func) + hashtable->value_destroy_func (node->value); + + FLUID_FREE (node); + + hashtable->nnodes--; +} + +/* + * fluid_hashtable_remove_all_nodes: + * @hashtable: our #fluid_hashtable_t + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Removes all nodes from the table. Since this may be a precursor to + * freeing the table entirely, no resize is performed. + * + * If @notify is %TRUE then the destroy notify functions are called + * for the key and value of the hash node. + */ +static void +fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify) +{ + fluid_hashnode_t **node_ptr; + int i; + + for (i = 0; i < hashtable->size; i++) + for (node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;) + fluid_hashtable_remove_node (hashtable, &node_ptr, notify); + + hashtable->nnodes = 0; +} + +/* + * fluid_hashtable_resize: + * @hashtable: our #fluid_hashtable_t + * + * Resizes the hash table to the optimal size based on the number of + * nodes currently held. If you call this function then a resize will + * occur, even if one does not need to occur. Use + * fluid_hashtable_maybe_resize() instead. + */ +static void +fluid_hashtable_resize (fluid_hashtable_t *hashtable) +{ + fluid_hashnode_t **new_nodes; + fluid_hashnode_t *node; fluid_hashnode_t *next; -}; + unsigned int hash_val; + int new_size; + int i; -static fluid_hashnode_t* new_fluid_hashnode(char* key, void* value, int type); -static void delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del); -static void delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del); + new_size = spaced_primes_closest (hashtable->nnodes); + new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE : + ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size); -struct _fluid_hashtable_t { - unsigned int size; - unsigned int nnodes; - fluid_hashnode_t **nodes; - fluid_hash_delete_t del; -}; + new_nodes = FLUID_ARRAY (fluid_hashnode_t *, new_size); -#define FLUID_HASHTABLE_RESIZE(hash_table) \ - if ((3 * hash_table->size <= hash_table->nnodes) \ - && (hash_table->size < HASH_TABLE_MAX_SIZE)) { \ - fluid_hashtable_resize(hash_table); \ - } + if (!new_nodes) + { + FLUID_LOG (FLUID_ERR, "Out of memory"); + return; + } -static void fluid_hashtable_resize(fluid_hashtable_t *hash_table); -static fluid_hashnode_t** fluid_hashtable_lookup_node(fluid_hashtable_t *hash_table, char* key); + FLUID_MEMSET (new_nodes, 0, new_size * sizeof (fluid_hashnode_t *)); + + for (i = 0; i < hashtable->size; i++) + for (node = hashtable->nodes[i]; node; node = next) + { + next = node->next; + + hash_val = node->key_hash % new_size; + + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + + FLUID_FREE (hashtable->nodes); + hashtable->nodes = new_nodes; + hashtable->size = new_size; +} + +/* + * fluid_hashtable_maybe_resize: + * @hashtable: our #fluid_hashtable_t + * + * Resizes the hash table, if needed. + * + * Essentially, calls fluid_hashtable_resize() if the table has strayed + * too far from its ideal size for its number of nodes. + */ +static inline void +fluid_hashtable_maybe_resize (fluid_hashtable_t *hashtable) +{ + int nnodes = hashtable->nnodes; + int size = hashtable->size; + + if ((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || + (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) + fluid_hashtable_resize (hashtable); +} /** * new_fluid_hashtable: + * @hash_func: a function to create a hash value from a key. + * Hash values are used to determine where keys are stored within the + * #fluid_hashtable_t data structure. The fluid_direct_hash(), fluid_int_hash() and + * fluid_str_hash() functions are provided for some common types of keys. + * If hash_func is %NULL, fluid_direct_hash() is used. + * @key_equal_func: a function to check two keys for equality. This is + * used when looking up keys in the #fluid_hashtable_t. The fluid_direct_equal(), + * fluid_int_equal() and fluid_str_equal() functions are provided for the most + * common types of keys. If @key_equal_func is %NULL, keys are compared + * directly in a similar fashion to fluid_direct_equal(), but without the + * overhead of a function call. * - * Creates a new #fluid_hashtable_t. + * Creates a new #fluid_hashtable_t with a reference count of 1. * * Return value: a new #fluid_hashtable_t. **/ fluid_hashtable_t* -new_fluid_hashtable(fluid_hash_delete_t del) +new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func) { - fluid_hashtable_t *hash_table; - unsigned int i; + return new_fluid_hashtable_full (hash_func, key_equal_func, NULL, NULL); +} - hash_table = FLUID_NEW(fluid_hashtable_t); - hash_table->size = HASH_TABLE_MIN_SIZE; - hash_table->nnodes = 0; - hash_table->nodes = FLUID_ARRAY(fluid_hashnode_t*, hash_table->size); - hash_table->del = del; - for (i = 0; i < hash_table->size; i++) { - hash_table->nodes[i] = NULL; +/** + * new_fluid_hashtable_full: + * @hash_func: a function to create a hash value from a key. + * @key_equal_func: a function to check two keys for equality. + * @key_destroy_func: a function to free the memory allocated for the key + * used when removing the entry from the #fluid_hashtable_t or %NULL if you + * don't want to supply such a function. + * @value_destroy_func: a function to free the memory allocated for the + * value used when removing the entry from the #fluid_hashtable_t or %NULL if + * you don't want to supply such a function. + * + * Creates a new #fluid_hashtable_t like fluid_hashtable_new() with a reference count + * of 1 and allows to specify functions to free the memory allocated for the + * key and value that get called when removing the entry from the #fluid_hashtable_t. + * + * Return value: a new #fluid_hashtable_t. + **/ +fluid_hashtable_t* +new_fluid_hashtable_full (fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func) +{ + fluid_hashtable_t *hashtable; + + hashtable = FLUID_NEW (fluid_hashtable_t); + + if (!hashtable) + { + FLUID_LOG (FLUID_ERR, "Out of memory"); + return NULL; } - return hash_table; + hashtable->size = HASH_TABLE_MIN_SIZE; + hashtable->nnodes = 0; + hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash; + hashtable->key_equal_func = key_equal_func; + hashtable->ref_count = 1; + hashtable->key_destroy_func = key_destroy_func; + hashtable->value_destroy_func = value_destroy_func; + hashtable->nodes = FLUID_ARRAY (fluid_hashnode_t*, hashtable->size); + FLUID_MEMSET (hashtable->nodes, 0, hashtable->size * sizeof (fluid_hashnode_t *)); + + return hashtable; +} + +/** + * fluid_hashtable_iter_init: + * @iter: an uninitialized #fluid_hashtable_iter_t. + * @hashtable: a #fluid_hashtable_t. + * + * Initializes a key/value pair iterator and associates it with + * @hashtable. Modifying the hash table after calling this function + * invalidates the returned iterator. + * |[ + * fluid_hashtable_iter_t iter; + * gpointer key, value; + * + * fluid_hashtable_iter_init (&iter, hashtable); + * while (fluid_hashtable_iter_next (&iter, &key, &value)) + * { + * /* do something with key and value */ + * } + * ]| + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, + fluid_hashtable_t *hashtable) +{ + RealIter *ri = (RealIter *) iter; + + fluid_return_if_fail (iter != NULL); + fluid_return_if_fail (hashtable != NULL); + + ri->hashtable = hashtable; + ri->prev_node = NULL; + ri->node = NULL; + ri->position = -1; + ri->pre_advanced = FALSE; +} + +/** + * fluid_hashtable_iter_next: + * @iter: an initialized #fluid_hashtable_iter_t. + * @key: a location to store the key, or %NULL. + * @value: a location to store the value, or %NULL. + * + * Advances @iter and retrieves the key and/or value that are now + * pointed to as a result of this advancement. If %FALSE is returned, + * @key and @value are not set, and the iterator becomes invalid. + * + * Return value: %FALSE if the end of the #fluid_hashtable_t has been reached. + * + * Since: 2.16 + **/ +int +fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, + void **value) +{ + RealIter *ri = (RealIter *) iter; + + fluid_return_val_if_fail (iter != NULL, FALSE); + + if (ri->pre_advanced) + { + ri->pre_advanced = FALSE; + + if (ri->node == NULL) + return FALSE; + } + else + { + if (ri->node != NULL) + { + ri->prev_node = ri->node; + ri->node = ri->node->next; + } + + while (ri->node == NULL) + { + ri->position++; + if (ri->position >= ri->hashtable->size) + return FALSE; + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; + } + } + + if (key != NULL) + *key = ri->node->key; + if (value != NULL) + *value = ri->node->value; + + return TRUE; +} + +/** + * fluid_hashtable_iter_get_hash_table: + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Returns the #fluid_hashtable_t associated with @iter. + * + * Return value: the #fluid_hashtable_t associated with @iter. + * + * Since: 2.16 + **/ +fluid_hashtable_t * +fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter) +{ + fluid_return_val_if_fail (iter != NULL, NULL); + + return ((RealIter *) iter)->hashtable; +} + +static void +iter_remove_or_steal (RealIter *ri, int notify) +{ + fluid_hashnode_t *prev; + fluid_hashnode_t *node; + int position; + + fluid_return_if_fail (ri != NULL); + fluid_return_if_fail (ri->node != NULL); + + prev = ri->prev_node; + node = ri->node; + position = ri->position; + + /* pre-advance the iterator since we will remove the node */ + + ri->node = ri->node->next; + /* ri->prev_node is still the correct previous node */ + + while (ri->node == NULL) + { + ri->position++; + if (ri->position >= ri->hashtable->size) + break; + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; + } + + ri->pre_advanced = TRUE; + + /* remove the node */ + + if (prev != NULL) + prev->next = node->next; + else + ri->hashtable->nodes[position] = node->next; + + if (notify) + { + if (ri->hashtable->key_destroy_func) + ri->hashtable->key_destroy_func(node->key); + if (ri->hashtable->value_destroy_func) + ri->hashtable->value_destroy_func(node->value); + } + + FLUID_FREE (node); + + ri->hashtable->nnodes--; +} + +/** + * fluid_hashtable_iter_remove(): + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Removes the key/value pair currently pointed to by the iterator + * from its associated #fluid_hashtable_t. Can only be called after + * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more + * than once for the same key/value pair. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the + * key and value are freed using the supplied destroy functions, otherwise + * you have to make sure that any dynamically allocated values are freed + * yourself. + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter) +{ + iter_remove_or_steal ((RealIter *) iter, TRUE); +} + +/** + * fluid_hashtable_iter_steal(): + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Removes the key/value pair currently pointed to by the iterator + * from its associated #fluid_hashtable_t, without calling the key and value + * destroy functions. Can only be called after + * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more + * than once for the same key/value pair. + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter) +{ + iter_remove_or_steal ((RealIter *) iter, FALSE); +} + + +/** + * fluid_hashtable_ref: + * @hashtable: a valid #fluid_hashtable_t. + * + * Atomically increments the reference count of @hashtable by one. + * This function is MT-safe and may be called from any thread. + * + * Return value: the passed in #fluid_hashtable_t. + * + * Since: 2.10 + **/ +fluid_hashtable_t* +fluid_hashtable_ref (fluid_hashtable_t *hashtable) +{ + fluid_return_val_if_fail (hashtable != NULL, NULL); + fluid_return_val_if_fail (hashtable->ref_count > 0, hashtable); + + fluid_atomic_int_add (&hashtable->ref_count, 1); + return hashtable; +} + +/** + * fluid_hashtable_unref: + * @hashtable: a valid #fluid_hashtable_t. + * + * Atomically decrements the reference count of @hashtable by one. + * If the reference count drops to 0, all keys and values will be + * destroyed, and all memory allocated by the hash table is released. + * This function is MT-safe and may be called from any thread. + * + * Since: 2.10 + **/ +void +fluid_hashtable_unref (fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail (hashtable != NULL); + fluid_return_if_fail (hashtable->ref_count > 0); + + if (fluid_atomic_int_exchange_and_add (&hashtable->ref_count, -1) - 1 == 0) + { + fluid_hashtable_remove_all_nodes (hashtable, TRUE); + FLUID_FREE (hashtable->nodes); + FLUID_FREE (hashtable); + } } /** * delete_fluid_hashtable: - * @hash_table: a #fluid_hashtable_t. + * @hashtable: a #fluid_hashtable_t. * - * Destroys the #fluid_hashtable_t. If keys and/or values are dynamically - * allocated, you should either free them first or create the #fluid_hashtable_t - * using fluid_hashtable_new_full(). In the latter case the destroy functions - * you supplied will be called on all keys and values before destroying - * the #fluid_hashtable_t. + * Destroys all keys and values in the #fluid_hashtable_t and decrements its + * reference count by 1. If keys and/or values are dynamically allocated, + * you should either free them first or create the #fluid_hashtable_t with destroy + * notifiers using fluid_hashtable_new_full(). In the latter case the destroy + * functions you supplied will be called on all keys and values during the + * destruction phase. **/ void -delete_fluid_hashtable(fluid_hashtable_t *hash_table) +delete_fluid_hashtable (fluid_hashtable_t *hashtable) { - unsigned int i; + fluid_return_if_fail (hashtable != NULL); + fluid_return_if_fail (hashtable->ref_count > 0); - if (hash_table == NULL) { - return; - } - - for (i = 0; i < hash_table->size; i++) { - delete_fluid_hashnodes(hash_table->nodes[i], hash_table->del); - } - - FLUID_FREE(hash_table->nodes); - FLUID_FREE(hash_table); -} - - -static /*inline*/ fluid_hashnode_t** -fluid_hashtable_lookup_node (fluid_hashtable_t* hash_table, char* key) -{ - fluid_hashnode_t **node; - - node = &hash_table->nodes[fluid_str_hash(key) % hash_table->size]; - - while (*node && (FLUID_STRCMP((*node)->key, key) != 0)) { - node = &(*node)->next; - } - - return node; + fluid_hashtable_remove_all (hashtable); + fluid_hashtable_unref (hashtable); } /** * fluid_hashtable_lookup: - * @hash_table: a #fluid_hashtable_t. + * @hashtable: a #fluid_hashtable_t. * @key: the key to look up. * - * Looks up a key in a #fluid_hashtable_t. + * Looks up a key in a #fluid_hashtable_t. Note that this function cannot + * distinguish between a key that is not present and one which is present + * and has the value %NULL. If you need this distinction, use + * fluid_hashtable_lookup_extended(). * * Return value: the associated value, or %NULL if the key is not found. **/ -int -fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type) +void * +fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key) { fluid_hashnode_t *node; - node = *fluid_hashtable_lookup_node(hash_table, key); + fluid_return_val_if_fail (hashtable != NULL, NULL); - if (node) { - if (value) { - *value = node->value; + node = *fluid_hashtable_lookup_node (hashtable, key, NULL); + + return node ? node->value : NULL; +} + +/** + * fluid_hashtable_lookup_extended: + * @hashtable: a #fluid_hashtable_t. + * @lookup_key: the key to look up. + * @orig_key: returns the original key. + * @value: returns the value associated with the key. + * + * Looks up a key in the #fluid_hashtable_t, returning the original key and the + * associated value and a #gboolean which is %TRUE if the key was found. This + * is useful if you need to free the memory allocated for the original key, + * for example before calling fluid_hashtable_remove(). + * + * Return value: %TRUE if the key was found in the #fluid_hashtable_t. + **/ +int +fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, + const void *lookup_key, + void **orig_key, void **value) +{ + fluid_hashnode_t *node; + + fluid_return_val_if_fail (hashtable != NULL, FALSE); + + node = *fluid_hashtable_lookup_node (hashtable, lookup_key, NULL); + + if (node == NULL) + return FALSE; + + if (orig_key) + *orig_key = node->key; + + if (value) + *value = node->value; + + return TRUE; +} + +/* + * fluid_hashtable_insert_internal: + * @hashtable: our #fluid_hashtable_t + * @key: the key to insert + * @value: the value to insert + * @keep_new_key: if %TRUE and this key already exists in the table + * then call the destroy notify function on the old key. If %FALSE + * then call the destroy notify function on the new key. + * + * Implements the common logic for the fluid_hashtable_insert() and + * fluid_hashtable_replace() functions. + * + * Do a lookup of @key. If it is found, replace it with the new + * @value (and perhaps the new @key). If it is not found, create a + * new node. + */ +static void +fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key, + void *value, int keep_new_key) +{ + fluid_hashnode_t **node_ptr, *node; + unsigned int key_hash; + + fluid_return_if_fail (hashtable != NULL); + fluid_return_if_fail (hashtable->ref_count > 0); + + node_ptr = fluid_hashtable_lookup_node (hashtable, key, &key_hash); + + if ((node = *node_ptr)) + { + if (keep_new_key) + { + if (hashtable->key_destroy_func) + hashtable->key_destroy_func (node->key); + node->key = key; + } + else + { + if (hashtable->key_destroy_func) + hashtable->key_destroy_func (key); + } + + if (hashtable->value_destroy_func) + hashtable->value_destroy_func (node->value); + + node->value = value; } - if (type) { - *type = node->type; + else + { + node = FLUID_NEW (fluid_hashnode_t); + + if (!node) + { + FLUID_LOG (FLUID_ERR, "Out of memory"); + return; + } + + node->key = key; + node->value = value; + node->key_hash = key_hash; + node->next = NULL; + + *node_ptr = node; + hashtable->nnodes++; + fluid_hashtable_maybe_resize (hashtable); } - return 1; - } else { - return 0; - } } /** * fluid_hashtable_insert: - * @hash_table: a #fluid_hashtable_t. + * @hashtable: a #fluid_hashtable_t. * @key: a key to insert. * @value: the value to associate with the key. * @@ -177,59 +812,64 @@ fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, i * using that function. **/ void -fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type) +fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value) { - fluid_hashnode_t **node; - - node = fluid_hashtable_lookup_node(hash_table, key); - - if (*node) { - (*node)->value = value; - (*node)->type = type; - } else { - *node = new_fluid_hashnode(key, value, type); - hash_table->nnodes++; - FLUID_HASHTABLE_RESIZE(hash_table); - } + fluid_hashtable_insert_internal (hashtable, key, value, FALSE); } - /** * fluid_hashtable_replace: - * @hash_table: a #GHashTable. + * @hashtable: a #fluid_hashtable_t. * @key: a key to insert. * @value: the value to associate with the key. * - * Inserts a new key and value into a #GHashTable similar to + * Inserts a new key and value into a #fluid_hashtable_t similar to * fluid_hashtable_insert(). The difference is that if the key already exists - * in the #GHashTable, it gets replaced by the new key. If you supplied a - * @value_destroy_func when creating the #GHashTable, the old value is freed + * in the #fluid_hashtable_t, it gets replaced by the new key. If you supplied a + * @value_destroy_func when creating the #fluid_hashtable_t, the old value is freed * using that function. If you supplied a @key_destroy_func when creating the - * #GHashTable, the old key is freed using that function. + * #fluid_hashtable_t, the old key is freed using that function. **/ void -fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type) +fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value) { - fluid_hashnode_t **node; + fluid_hashtable_insert_internal (hashtable, key, value, TRUE); +} - node = fluid_hashtable_lookup_node(hash_table, key); +/* + * fluid_hashtable_remove_internal: + * @hashtable: our #fluid_hashtable_t + * @key: the key to remove + * @notify: %TRUE if the destroy notify handlers are to be called + * Return value: %TRUE if a node was found and removed, else %FALSE + * + * Implements the common logic for the fluid_hashtable_remove() and + * fluid_hashtable_steal() functions. + * + * Do a lookup of @key and remove it if it is found, calling the + * destroy notify handlers only if @notify is %TRUE. + */ +static int +fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key, + int notify) +{ + fluid_hashnode_t **node_ptr; - if (*node) { - if (hash_table->del) { - hash_table->del((*node)->value, (*node)->type); - } - (*node)->value = value; + fluid_return_val_if_fail (hashtable != NULL, FALSE); - } else { - *node = new_fluid_hashnode(key, value, type); - hash_table->nnodes++; - FLUID_HASHTABLE_RESIZE(hash_table); - } + node_ptr = fluid_hashtable_lookup_node (hashtable, key, NULL); + if (*node_ptr == NULL) + return FALSE; + + fluid_hashtable_remove_node (hashtable, &node_ptr, notify); + fluid_hashtable_maybe_resize (hashtable); + + return TRUE; } /** * fluid_hashtable_remove: - * @hash_table: a #fluid_hashtable_t. + * @hashtable: a #fluid_hashtable_t. * @key: the key to remove. * * Removes a key and its associated value from a #fluid_hashtable_t. @@ -242,28 +882,163 @@ fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, i * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. **/ int -fluid_hashtable_remove (fluid_hashtable_t *hash_table, char* key) +fluid_hashtable_remove (fluid_hashtable_t *hashtable, void *key) { - fluid_hashnode_t **node, *dest; + return fluid_hashtable_remove_internal (hashtable, key, TRUE); +} - node = fluid_hashtable_lookup_node(hash_table, key); - if (*node) { - dest = *node; - (*node) = dest->next; - delete_fluid_hashnode(dest, hash_table->del); - hash_table->nnodes--; +/** + * fluid_hashtable_steal: + * @hashtable: a #fluid_hashtable_t. + * @key: the key to remove. + * + * Removes a key and its associated value from a #fluid_hashtable_t without + * calling the key and value destroy functions. + * + * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. + **/ +int +fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key) +{ + return fluid_hashtable_remove_internal (hashtable, key, FALSE); +} - FLUID_HASHTABLE_RESIZE (hash_table); +/** + * fluid_hashtable_remove_all: + * @hashtable: a #fluid_hashtable_t + * + * Removes all keys and their associated values from a #fluid_hashtable_t. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the keys + * and values are freed using the supplied destroy functions, otherwise you + * have to make sure that any dynamically allocated values are freed + * yourself. + * + * Since: 2.12 + **/ +void +fluid_hashtable_remove_all (fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail (hashtable != NULL); - return 1; - } + fluid_hashtable_remove_all_nodes (hashtable, TRUE); + fluid_hashtable_maybe_resize (hashtable); +} - return 0; +/** + * fluid_hashtable_steal_all: + * @hashtable: a #fluid_hashtable_t. + * + * Removes all keys and their associated values from a #fluid_hashtable_t + * without calling the key and value destroy functions. + * + * Since: 2.12 + **/ +void +fluid_hashtable_steal_all (fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail (hashtable != NULL); + + fluid_hashtable_remove_all_nodes (hashtable, FALSE); + fluid_hashtable_maybe_resize (hashtable); +} + +/* + * fluid_hashtable_foreach_remove_or_steal: + * @hashtable: our #fluid_hashtable_t + * @func: the user's callback function + * @user_data: data for @func + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Implements the common logic for fluid_hashtable_foreach_remove() and + * fluid_hashtable_foreach_steal(). + * + * Iterates over every node in the table, calling @func with the key + * and value of the node (and @user_data). If @func returns %TRUE the + * node is removed from the table. + * + * If @notify is true then the destroy notify handlers will be called + * for each removed node. + */ +static unsigned int +fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data, + int notify) +{ + fluid_hashnode_t *node, **node_ptr; + unsigned int deleted = 0; + int i; + + for (i = 0; i < hashtable->size; i++) + for (node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;) + if ((* func) (node->key, node->value, user_data)) + { + fluid_hashtable_remove_node (hashtable, &node_ptr, notify); + deleted++; + } + else + node_ptr = &node->next; + + fluid_hashtable_maybe_resize (hashtable); + + return deleted; +} + +/** + * fluid_hashtable_foreach_remove: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each key/value pair in the #fluid_hashtable_t. + * If the function returns %TRUE, then the key/value pair is removed from the + * #fluid_hashtable_t. If you supplied key or value destroy functions when creating + * the #fluid_hashtable_t, they are used to free the memory allocated for the removed + * keys and values. + * + * See #fluid_hashtable_iter_t for an alternative way to loop over the + * key/value pairs in the hash table. + * + * Return value: the number of key/value pairs removed. + **/ +unsigned int +fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) +{ + fluid_return_val_if_fail (hashtable != NULL, 0); + fluid_return_val_if_fail (func != NULL, 0); + + return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, TRUE); +} + +/** + * fluid_hashtable_foreach_steal: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each key/value pair in the #fluid_hashtable_t. + * If the function returns %TRUE, then the key/value pair is removed from the + * #fluid_hashtable_t, but no key or value destroy functions are called. + * + * See #fluid_hashtable_iter_t for an alternative way to loop over the + * key/value pairs in the hash table. + * + * Return value: the number of key/value pairs removed. + **/ +unsigned int +fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) +{ + fluid_return_val_if_fail (hashtable != NULL, 0); + fluid_return_val_if_fail (func != NULL, 0); + + return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, FALSE); } /** * fluid_hashtable_foreach: - * @hash_table: a #fluid_hashtable_t. + * @hashtable: a #fluid_hashtable_t. * @func: the function to call for each key/value pair. * @user_data: user data to pass to the function. * @@ -272,117 +1047,262 @@ fluid_hashtable_remove (fluid_hashtable_t *hash_table, char* key) * pair, and the given @user_data parameter. The hash table may not * be modified while iterating over it (you can't add/remove * items). To remove all items matching a predicate, use - * fluid_hashtable_remove(). + * fluid_hashtable_foreach_remove(). + * + * See fluid_hashtable_find() for performance caveats for linear + * order searches in contrast to fluid_hashtable_lookup(). **/ void -fluid_hashtable_foreach(fluid_hashtable_t *hash_table, fluid_hash_iter_t func, void* data) +fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func, + void *user_data) { - fluid_hashnode_t *node = NULL; - unsigned int i; + fluid_hashnode_t *node; + int i; - for (i = 0; i < hash_table->size; i++) { - for (node = hash_table->nodes[i]; node != NULL; node = node->next) { - (*func)(node->key, node->value, node->type, data); - } - } + fluid_return_if_fail (hashtable != NULL); + fluid_return_if_fail (func != NULL); + + for (i = 0; i < hashtable->size; i++) + for (node = hashtable->nodes[i]; node; node = node->next) + (* func) (node->key, node->value, user_data); +} + +/** + * fluid_hashtable_find: + * @hashtable: a #fluid_hashtable_t. + * @predicate: function to test the key/value pairs for a certain property. + * @user_data: user data to pass to the function. + * + * Calls the given function for key/value pairs in the #fluid_hashtable_t until + * @predicate returns %TRUE. The function is passed the key and value of + * each pair, and the given @user_data parameter. The hash table may not + * be modified while iterating over it (you can't add/remove items). + * + * Note, that hash tables are really only optimized for forward lookups, + * i.e. fluid_hashtable_lookup(). + * So code that frequently issues fluid_hashtable_find() or + * fluid_hashtable_foreach() (e.g. in the order of once per every entry in a + * hash table) should probably be reworked to use additional or different + * data structures for reverse lookups (keep in mind that an O(n) find/foreach + * operation issued for all n values in a hash table ends up needing O(n*n) + * operations). + * + * Return value: The value of the first key/value pair is returned, for which + * func evaluates to %TRUE. If no pair with the requested property is found, + * %NULL is returned. + * + * Since: 2.4 + **/ +void * +fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data) +{ + fluid_hashnode_t *node; + int i; + + fluid_return_val_if_fail (hashtable != NULL, NULL); + fluid_return_val_if_fail (predicate != NULL, NULL); + + for (i = 0; i < hashtable->size; i++) + for (node = hashtable->nodes[i]; node; node = node->next) + if (predicate (node->key, node->value, user_data)) + return node->value; + return NULL; } /** * fluid_hashtable_size: - * @hash_table: a #fluid_hashtable_t. + * @hashtable: a #fluid_hashtable_t. * * Returns the number of elements contained in the #fluid_hashtable_t. * * Return value: the number of key/value pairs in the #fluid_hashtable_t. **/ unsigned int -fluid_hashtable_size(fluid_hashtable_t *hash_table) +fluid_hashtable_size (fluid_hashtable_t *hashtable) { - return hash_table->nnodes; + fluid_return_val_if_fail (hashtable != NULL, 0); + + return hashtable->nnodes; } -static void -fluid_hashtable_resize(fluid_hashtable_t *hash_table) +/** + * fluid_hashtable_get_keys: + * @hashtable: a #fluid_hashtable_t + * + * Retrieves every key inside @hashtable. The returned data is valid + * until @hashtable is modified. + * + * Return value: a #GList containing all the keys inside the hash + * table. The content of the list is owned by the hash table and + * should not be modified or freed. Use delete_fluid_list() when done + * using the list. + * + * Since: 2.14 + */ +fluid_list_t * +fluid_hashtable_get_keys (fluid_hashtable_t *hashtable) { - fluid_hashnode_t **new_nodes; fluid_hashnode_t *node; - fluid_hashnode_t *next; - unsigned int hash_val; - int new_size; - unsigned int i; + int i; + fluid_list_t *retval; - new_size = 3 * hash_table->size + 1; - new_size = (new_size > HASH_TABLE_MAX_SIZE)? HASH_TABLE_MAX_SIZE : new_size; + fluid_return_val_if_fail (hashtable != NULL, NULL); -/* printf("%s: %d: resizing, new size = %d\n", __FILE__, __LINE__, new_size); */ + retval = NULL; + for (i = 0; i < hashtable->size; i++) + for (node = hashtable->nodes[i]; node; node = node->next) + retval = fluid_list_prepend (retval, node->key); - new_nodes = FLUID_ARRAY(fluid_hashnode_t*, new_size); - FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t*)); - - for (i = 0; i < hash_table->size; i++) { - for (node = hash_table->nodes[i]; node; node = next) { - next = node->next; - hash_val = fluid_str_hash(node->key) % new_size; - node->next = new_nodes[hash_val]; - new_nodes[hash_val] = node; - } - } - - FLUID_FREE(hash_table->nodes); - hash_table->nodes = new_nodes; - hash_table->size = new_size; + return retval; } -static fluid_hashnode_t* -new_fluid_hashnode(char* key, void* value, int type) +/** + * fluid_hashtable_get_values: + * @hashtable: a #fluid_hashtable_t + * + * Retrieves every value inside @hashtable. The returned data is + * valid until @hashtable is modified. + * + * Return value: a #GList containing all the values inside the hash + * table. The content of the list is owned by the hash table and + * should not be modified or freed. Use delete_fluid_list() when done + * using the list. + * + * Since: 2.14 + */ +fluid_list_t * +fluid_hashtable_get_values (fluid_hashtable_t *hashtable) { - fluid_hashnode_t *hash_node; + fluid_hashnode_t *node; + int i; + fluid_list_t *retval; - hash_node = FLUID_NEW(fluid_hashnode_t); + fluid_return_val_if_fail (hashtable != NULL, NULL); - hash_node->key = FLUID_STRDUP(key); - hash_node->value = value; - hash_node->type = type; - hash_node->next = NULL; + retval = NULL; + for (i = 0; i < hashtable->size; i++) + for (node = hashtable->nodes[i]; node; node = node->next) + retval = fluid_list_prepend (retval, node->value); - return hash_node; + return retval; } -static void -delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del) + +/* Extracted from glib/gstring.c */ + + +/** + * fluid_str_equal: + * @v1: a key + * @v2: a key to compare with @v1 + * + * Compares two strings for byte-by-byte equality and returns %TRUE + * if they are equal. It can be passed to new_fluid_hashtable() as the + * @key_equal_func parameter, when using strings as keys in a #Ghashtable. + * + * Returns: %TRUE if the two keys match + */ +int +fluid_str_equal (const void *v1, const void *v2) { - if (del) { - (*del)(hash_node->value, hash_node->type); - } - if (hash_node->key) { - FLUID_FREE(hash_node->key); - } - FLUID_FREE(hash_node); + const char *string1 = v1; + const char *string2 = v2; + + return strcmp (string1, string2) == 0; } -static void -delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del) -{ - while (hash_node) { - fluid_hashnode_t *next = hash_node->next; - delete_fluid_hashnode(hash_node, del); - hash_node = next; - } -} - - -/* 31 bit hash function */ +/** + * fluid_str_hash: + * @v: a string key + * + * Converts a string to a hash value. + * It can be passed to new_fluid_hashtable() as the @hash_func + * parameter, when using strings as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key + */ unsigned int -fluid_str_hash(char* key) +fluid_str_hash (const void *v) { - char *p = key; - unsigned int h = *p; + /* 31 bit hash function */ + const signed char *p = v; + uint32 h = *p; - if (h) { - for (p += 1; *p != '\0'; p++) { + if (h) + for (p += 1; *p != '\0'; p++) h = (h << 5) - h + *p; - } - } return h; } + + +/* Extracted from glib/gutils.c */ + + +/** + * fluid_direct_equal: + * @v1: a key. + * @v2: a key to compare with @v1. + * + * Compares two #gpointer arguments and returns %TRUE if they are equal. + * It can be passed to new_fluid_hashtable() as the @key_equal_func + * parameter, when using pointers as keys in a #fluid_hashtable_t. + * + * Returns: %TRUE if the two keys match. + */ +int +fluid_direct_equal (const void *v1, const void *v2) +{ + return v1 == v2; +} + +/** + * fluid_direct_hash: + * @v: a void * key + * + * Converts a gpointer to a hash value. + * It can be passed to g_hashtable_new() as the @hash_func parameter, + * when using pointers as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key. + */ +unsigned int +fluid_direct_hash (const void *v) +{ + return FLUID_POINTER_TO_UINT (v); +} + +/** + * fluid_int_equal: + * @v1: a pointer to a int key. + * @v2: a pointer to a int key to compare with @v1. + * + * Compares the two #gint values being pointed to and returns + * %TRUE if they are equal. + * It can be passed to g_hashtable_new() as the @key_equal_func + * parameter, when using pointers to integers as keys in a #fluid_hashtable_t. + * + * Returns: %TRUE if the two keys match. + */ +int +fluid_int_equal (const void *v1, const void *v2) +{ + return *((const int*) v1) == *((const int*) v2); +} + +/** + * fluid_int_hash: + * @v: a pointer to a int key + * + * Converts a pointer to a #gint to a hash value. + * It can be passed to g_hashtable_new() as the @hash_func parameter, + * when using pointers to integers values as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key. + */ +unsigned int +fluid_int_hash (const void *v) +{ + return *(const int*) v; +} diff --git a/fluidsynth/src/fluid_hash.h b/fluidsynth/src/fluid_hash.h index d2e8577e..467d3d26 100644 --- a/fluidsynth/src/fluid_hash.h +++ b/fluidsynth/src/fluid_hash.h @@ -25,40 +25,107 @@ */ /* - * Demolished by Peter Hanappe [December 2002] - * - * - only string as key - * - stores additional type info - * - removed use of GLib types (gpointer, gint, ...) - * - reduced the number of API functions + * Adapted for FluidSynth use by Josh Green + * September 8, 2009 from glib 2.18.4 + * + * - Self contained (no dependencies on glib) * - changed names to fluid_hashtable_... */ #ifndef _FLUID_HASH_H #define _FLUID_HASH_H +#include "fluidsynth_priv.h" +#include "fluid_list.h" +#include "fluid_sys.h" -typedef int (*fluid_hash_iter_t)(char* key, void* value, int type, void* data); -typedef void (*fluid_hash_delete_t)(void* value, int type); +/* Extracted from gtypes.h */ +typedef void (*fluid_destroy_notify_t)(void *data); +typedef unsigned int (*fluid_hash_func_t)(const void *key); +typedef int (*fluid_equal_func_t)(const void *a, const void *b); +/* End gtypes.h extraction */ -fluid_hashtable_t* new_fluid_hashtable(fluid_hash_delete_t delete); -void delete_fluid_hashtable(fluid_hashtable_t *hash_table); +typedef int (*fluid_hr_func_t)(void *key, void *value, void *user_data); +typedef struct _fluid_hashtable_iter_t fluid_hashtable_iter_t; -void fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type); +typedef struct _fluid_hashnode_t fluid_hashnode_t; -void fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type); +struct _fluid_hashnode_t +{ + void *key; + void *value; + fluid_hashnode_t *next; + unsigned int key_hash; +}; -/* Returns non-zero if found, 0 if not found */ -int fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type); +struct _fluid_hashtable_t +{ + int size; + int nnodes; + fluid_hashnode_t **nodes; + fluid_hash_func_t hash_func; + fluid_equal_func_t key_equal_func; + volatile int ref_count; + fluid_destroy_notify_t key_destroy_func; + fluid_destroy_notify_t value_destroy_func; + fluid_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) +}; -/* Returns non-zero if removed, 0 if not removed */ -int fluid_hashtable_remove(fluid_hashtable_t *hash_table, char* key); +struct _fluid_hashtable_iter_t +{ + /*< private >*/ + void * dummy1; + void * dummy2; + void * dummy3; + int dummy4; + int dummy5; // Bool + void * dummy6; +}; -void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hash_iter_t fun, void* data); +fluid_hashtable_t* new_fluid_hashtable (fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func); +fluid_hashtable_t* new_fluid_hashtable_full (fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func); +void delete_fluid_hashtable(fluid_hashtable_t *hashtable); -unsigned int fluid_hashtable_size(fluid_hashtable_t *hash_table); +void fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable); +int fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, void **value); +fluid_hashtable_t *fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter); -unsigned int fluid_str_hash(char* v); +fluid_hashtable_t* fluid_hashtable_ref (fluid_hashtable_t *hashtable); +void fluid_hashtable_unref (fluid_hashtable_t *hashtable); + +void *fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key); +int fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, const void *lookup_key, + void **orig_key, void **value); + +void fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value); +void fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value); + +int fluid_hashtable_remove (fluid_hashtable_t *hashtable, void *key); +int fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key); +void fluid_hashtable_remove_all (fluid_hashtable_t *hashtable); +void fluid_hashtable_steal_all (fluid_hashtable_t *hashtable); +unsigned int fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data); +void fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func, + void *user_data); +void *fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data); +unsigned int fluid_hashtable_size (fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_keys (fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_values (fluid_hashtable_t *hashtable); + +int fluid_str_equal (const void *v1, const void *v2); +unsigned int fluid_str_hash (const void *v); +int fluid_direct_equal (const void *v1, const void *v2); +unsigned int fluid_direct_hash (const void *v); +int fluid_int_equal (const void *v1, const void *v2); +unsigned int fluid_int_hash (const void *v); #endif /* _FLUID_HASH_H */ diff --git a/fluidsynth/src/fluid_jack.c b/fluidsynth/src/fluid_jack.c index 072c6537..048a7b7e 100644 --- a/fluidsynth/src/fluid_jack.c +++ b/fluidsynth/src/fluid_jack.c @@ -127,7 +127,7 @@ new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func /* try to become a client of the JACK server */ - if (fluid_settings_getstr(settings, "audio.jack.id", &client_name) + if (fluid_settings_dupstr(settings, "audio.jack.id", &client_name) /* ++ alloc client name */ && (client_name != NULL) && (strlen(client_name) > 0)) { snprintf(name, 64, "%s", client_name); @@ -137,6 +137,8 @@ new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func name[63] = '\0'; + if (client_name) FLUID_FREE (client_name); /* -- free client name */ + if ((dev->client = jack_client_new(name)) == 0) { FLUID_LOG(FLUID_ERR, "Jack server not running?"); goto error_recovery; @@ -414,7 +416,7 @@ new_fluid_jack_midi_driver (fluid_settings_t *settings, /* try to become a client of the JACK server */ - if (fluid_settings_getstr(settings, "midi.jack.id", &client_name) + if (fluid_settings_dupstr(settings, "midi.jack.id", &client_name) /* ++ alloc client name */ && (client_name != NULL) && (strlen(client_name) > 0)) snprintf(name, 64, "%s", client_name); @@ -422,6 +424,8 @@ new_fluid_jack_midi_driver (fluid_settings_t *settings, name[63] = '\0'; + if (client_name) FLUID_FREE (client_name); /* -- free client name */ + if ((dev->client = jack_client_new (name)) == 0) { FLUID_LOG (FLUID_ERR, "Jack server not running?"); diff --git a/fluidsynth/src/fluid_midi.c b/fluidsynth/src/fluid_midi.c index e012474c..25e9d91e 100644 --- a/fluidsynth/src/fluid_midi.c +++ b/fluidsynth/src/fluid_midi.c @@ -278,6 +278,10 @@ int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int } } + /* Skip remaining track data, if any */ + if (mf->trackpos < mf->tracklen) + fluid_midi_file_skip (mf, mf->tracklen - mf->trackpos); + fluid_player_add_track(player, track); } else { @@ -1374,7 +1378,7 @@ int fluid_player_play(fluid_player_t* player) if (player->use_system_timer) { player->system_timer = new_fluid_timer((int) player->deltatime, fluid_player_callback, - (void*) player, 1, 0); + (void*) player, TRUE, FALSE, TRUE); if (player->system_timer == NULL) { return FLUID_FAILED; } @@ -1637,7 +1641,7 @@ fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigne /* Purpose: * Returns the length of the MIDI message. */ int fluid_midi_event_length(unsigned char event){ - switch (event && 0xF0) { + switch (event & 0xF0) { case NOTE_OFF: case NOTE_ON: case KEY_PRESSURE: diff --git a/fluidsynth/src/fluid_oss.c b/fluidsynth/src/fluid_oss.c index 3ba3f23b..7ac0828e 100644 --- a/fluidsynth/src/fluid_oss.c +++ b/fluidsynth/src/fluid_oss.c @@ -118,7 +118,7 @@ new_fluid_oss_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) int queuesize; double sample_rate; int periods, period_size; - char* devname; + char* devname = NULL; int format; pthread_attr_t attr; int err; @@ -167,8 +167,13 @@ new_fluid_oss_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) goto error_recovery; } - if (!fluid_settings_getstr(settings, "audio.oss.device", &devname)) { - devname = "/dev/dsp"; + if (!fluid_settings_dupstr(settings, "audio.oss.device", &devname) || !devname) { /* ++ alloc device name */ + devname = FLUID_STRDUP ("/dev/dsp"); + + if (devname == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } } if (stat(devname, &devstat) == -1) { @@ -265,9 +270,12 @@ new_fluid_oss_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) break; } + if (devname) FLUID_FREE (devname); /* -- free device name */ + return (fluid_audio_driver_t*) dev; error_recovery: + if (devname) FLUID_FREE (devname); /* -- free device name */ delete_fluid_oss_audio_driver((fluid_audio_driver_t*) dev); return NULL; } @@ -281,7 +289,7 @@ new_fluid_oss_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, int queuesize; double sample_rate; int periods, period_size; - char* devname; + char* devname = NULL; int format; pthread_attr_t attr; int err; @@ -310,9 +318,16 @@ new_fluid_oss_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, dev->buffer_byte_size = dev->buffer_size * 2 * 2; /* 2 channels * 16 bits audio */ - if (!fluid_settings_getstr(settings, "audio.oss.device", &devname)) { - devname = "/dev/dsp"; + if (!fluid_settings_dupstr(settings, "audio.oss.device", &devname) || !devname) { + devname = FLUID_STRDUP ("/dev/dsp"); + + if (!devname) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } } + if (stat(devname, &devstat) == -1) { FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", devname); goto error_recovery; @@ -416,9 +431,12 @@ new_fluid_oss_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, break; } + if (devname) FLUID_FREE (devname); /* -- free device name */ + return (fluid_audio_driver_t*) dev; error_recovery: + if (devname) FLUID_FREE (devname); /* -- free device name */ delete_fluid_oss_audio_driver((fluid_audio_driver_t*) dev); return NULL; } @@ -668,7 +686,7 @@ new_fluid_oss_midi_driver(fluid_settings_t* settings, pthread_attr_t attr; int sched = SCHED_FIFO; struct sched_param priority; - char* device; + char* device = NULL; /* not much use doing anything */ if (handler == NULL) { @@ -696,9 +714,16 @@ new_fluid_oss_midi_driver(fluid_settings_t* settings, } /* get the device name. if none is specified, use the default device. */ - fluid_settings_getstr(settings, "midi.oss.device", &device); + fluid_settings_dupstr(settings, "midi.oss.device", &device); /* ++ alloc device name */ + if (device == NULL) { - device = "/dev/midi"; + device = FLUID_STRDUP ("/dev/midi"); + + if (!device) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } } /* open the default hardware device. only use midi in. */ @@ -746,9 +771,13 @@ new_fluid_oss_midi_driver(fluid_settings_t* settings, } break; } + + if (device) FLUID_FREE (device); /* ++ free device */ + return (fluid_midi_driver_t*) dev; error_recovery: + if (device) FLUID_FREE (device); /* ++ free device */ delete_fluid_oss_midi_driver((fluid_midi_driver_t*) dev); return NULL; } diff --git a/fluidsynth/src/fluid_portaudio.c b/fluidsynth/src/fluid_portaudio.c index fc3360ed..0875f380 100644 --- a/fluidsynth/src/fluid_portaudio.c +++ b/fluidsynth/src/fluid_portaudio.c @@ -106,7 +106,7 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) { fluid_portaudio_driver_t *dev = NULL; PaStreamParameters outputParams; - char *device; + char *device = NULL; double sample_rate; int period_size; PaError err; @@ -125,7 +125,7 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) fluid_settings_getint (settings, "audio.period-size", &period_size); fluid_settings_getnum (settings, "synth.sample-rate", &sample_rate); - fluid_settings_getstr(settings, "audio.portaudio.device", &device); + fluid_settings_dupstr(settings, "audio.portaudio.device", &device); /* ++ alloc device name */ bzero (&outputParams, sizeof (outputParams)); outputParams.channelCount = 2; @@ -209,9 +209,12 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth) goto error_recovery; } + if (device) FLUID_FREE (device); /* -- free device name */ + return (fluid_audio_driver_t *)dev; error_recovery: + if (device) FLUID_FREE (device); /* -- free device name */ delete_fluid_portaudio_driver ((fluid_audio_driver_t *)dev); return NULL; } diff --git a/fluidsynth/src/fluid_pulse.c b/fluidsynth/src/fluid_pulse.c index b28e1d44..c89c2b4b 100644 --- a/fluidsynth/src/fluid_pulse.c +++ b/fluidsynth/src/fluid_pulse.c @@ -86,8 +86,8 @@ new_fluid_pulse_audio_driver2(fluid_settings_t* settings, pa_buffer_attr bufattr; double sample_rate; int period_size, period_bytes; - char *server; - char *device; + char *server = NULL; + char *device = NULL; pthread_attr_t attr; int sched = SCHED_FIFO; struct sched_param priority; @@ -104,11 +104,20 @@ new_fluid_pulse_audio_driver2(fluid_settings_t* settings, // fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); - fluid_settings_getstr(settings, "audio.pulseaudio.server", &server); - fluid_settings_getstr(settings, "audio.pulseaudio.device", &device); + fluid_settings_dupstr(settings, "audio.pulseaudio.server", &server); /* ++ alloc server string */ + fluid_settings_dupstr(settings, "audio.pulseaudio.device", &device); /* ++ alloc device string */ - if (strcmp (server, "default") == 0) server = NULL; - if (strcmp (device, "default") == 0) device = NULL; + if (server && strcmp (server, "default") == 0) + { + FLUID_FREE (server); /* -- free server string */ + server = NULL; + } + + if (device && strcmp (device, "default") == 0) + { + FLUID_FREE (device); /* -- free device string */ + device = NULL; + } dev->data = data; dev->callback = func; @@ -184,9 +193,14 @@ new_fluid_pulse_audio_driver2(fluid_settings_t* settings, break; } + if (server) FLUID_FREE (server); /* -- free server string */ + if (device) FLUID_FREE (device); /* -- free device string */ + return (fluid_audio_driver_t*) dev; error_recovery: + if (server) FLUID_FREE (server); /* -- free server string */ + if (device) FLUID_FREE (device); /* -- free device string */ delete_fluid_pulse_audio_driver((fluid_audio_driver_t*) dev); return NULL; } diff --git a/fluidsynth/src/fluid_rev.c b/fluidsynth/src/fluid_rev.c index 3f09b6d6..1ff59ef4 100644 --- a/fluidsynth/src/fluid_rev.c +++ b/fluidsynth/src/fluid_rev.c @@ -313,8 +313,8 @@ struct _fluid_revmodel_t { fluid_real_t bufallpassR4[allpasstuningR4]; }; -void fluid_revmodel_update(fluid_revmodel_t* rev); -void fluid_revmodel_init(fluid_revmodel_t* rev); +static void fluid_revmodel_update(fluid_revmodel_t* rev); +static void fluid_revmodel_init(fluid_revmodel_t* rev); fluid_revmodel_t* new_fluid_revmodel() @@ -382,7 +382,7 @@ delete_fluid_revmodel(fluid_revmodel_t* rev) FLUID_FREE(rev); } -void +static void fluid_revmodel_init(fluid_revmodel_t* rev) { int i; @@ -478,7 +478,7 @@ fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in, } } -void +static void fluid_revmodel_update(fluid_revmodel_t* rev) { /* Recalculate internal values after parameter change */ @@ -498,64 +498,34 @@ fluid_revmodel_update(fluid_revmodel_t* rev) } } -/* - The following get/set functions are not inlined, because - speed is never an issue when calling them, and also - because as you develop the reverb model, you may - wish to take dynamic action when they are called. -*/ +/** + * Set one or more reverb parameters. + * @param rev Reverb instance + * @param set One or more flags from #fluid_revmodel_set_t indicating what + * parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters) + * @param roomsize Reverb room size + * @param damping Reverb damping + * @param width Reverb width + * @param level Reverb level + */ void -fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value) +fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize, + float damping, float width, float level) { -/* fluid_clip(value, 0.0f, 1.0f); */ - rev->roomsize = (value * scaleroom) + offsetroom; - fluid_revmodel_update(rev); -} + if (set & FLUID_REVMODEL_SET_ROOMSIZE) + rev->roomsize = (roomsize * scaleroom) + offsetroom; -fluid_real_t -fluid_revmodel_getroomsize(fluid_revmodel_t* rev) -{ - return (rev->roomsize - offsetroom) / scaleroom; -} + if (set & FLUID_REVMODEL_SET_DAMPING) + rev->damp = damping * scaledamp; -void -fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value) -{ -/* fluid_clip(value, 0.0f, 1.0f); */ - rev->damp = value * scaledamp; - fluid_revmodel_update(rev); -} + if (set & FLUID_REVMODEL_SET_WIDTH) + rev->width = width; -fluid_real_t -fluid_revmodel_getdamp(fluid_revmodel_t* rev) -{ - return rev->damp / scaledamp; -} + if (set & FLUID_REVMODEL_SET_LEVEL) + { + fluid_clip(level, 0.0f, 1.0f); + rev->wet = level * scalewet; + } -void -fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value) -{ - fluid_clip(value, 0.0f, 1.0f); - rev->wet = value * scalewet; - fluid_revmodel_update(rev); -} - -fluid_real_t -fluid_revmodel_getlevel(fluid_revmodel_t* rev) -{ - return rev->wet / scalewet; -} - -void -fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value) -{ -/* fluid_clip(value, 0.0f, 1.0f); */ - rev->width = value; - fluid_revmodel_update(rev); -} - -fluid_real_t -fluid_revmodel_getwidth(fluid_revmodel_t* rev) -{ - return rev->width; + fluid_revmodel_update (rev); } diff --git a/fluidsynth/src/fluid_rev.h b/fluidsynth/src/fluid_rev.h index 1d0fd2ed..8244038f 100644 --- a/fluidsynth/src/fluid_rev.h +++ b/fluidsynth/src/fluid_rev.h @@ -27,6 +27,30 @@ typedef struct _fluid_revmodel_t fluid_revmodel_t; +/** Flags for fluid_revmodel_set() */ +typedef enum +{ + FLUID_REVMODEL_SET_ROOMSIZE = 1 << 0, + FLUID_REVMODEL_SET_DAMPING = 1 << 1, + FLUID_REVMODEL_SET_WIDTH = 1 << 2, + FLUID_REVMODEL_SET_LEVEL = 1 << 3 +} fluid_revmodel_set_t; + +/** Value for fluid_revmodel_set() which sets all reverb parameters. */ +#define FLUID_REVMODEL_SET_ALL 0x0F + +/* + * reverb preset + */ +typedef struct _fluid_revmodel_presets_t { + char* name; + fluid_real_t roomsize; + fluid_real_t damp; + fluid_real_t width; + fluid_real_t level; +} fluid_revmodel_presets_t; + + /* * reverb */ @@ -41,27 +65,7 @@ void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in, void fluid_revmodel_reset(fluid_revmodel_t* rev); -void fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value); -void fluid_revmodel_setmode(fluid_revmodel_t* rev, fluid_real_t value); - -fluid_real_t fluid_revmodel_getroomsize(fluid_revmodel_t* rev); -fluid_real_t fluid_revmodel_getdamp(fluid_revmodel_t* rev); -fluid_real_t fluid_revmodel_getlevel(fluid_revmodel_t* rev); -fluid_real_t fluid_revmodel_getwidth(fluid_revmodel_t* rev); - -/* - * reverb preset - */ -typedef struct _fluid_revmodel_presets_t { - char* name; - fluid_real_t roomsize; - fluid_real_t damp; - fluid_real_t width; - fluid_real_t level; -} fluid_revmodel_presets_t; - +void fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize, + float damping, float width, float level); #endif /* _FLUID_REV_H */ diff --git a/fluidsynth/src/fluid_seq.c b/fluidsynth/src/fluid_seq.c index 46a2019d..1bc2b373 100644 --- a/fluidsynth/src/fluid_seq.c +++ b/fluidsynth/src/fluid_seq.c @@ -468,7 +468,7 @@ void fluid_sequencer_set_time_scale(fluid_sequencer_t* seq, double scale) /* re-start timer */ if (seq->useSystemTimer) { - seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, 1, 0); + seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, (void *)seq, TRUE, FALSE, TRUE); } } } @@ -592,7 +592,7 @@ _fluid_seq_queue_init(fluid_sequencer_t* seq, int maxEvents) /* start timer */ if (seq->useSystemTimer) { seq->timer = new_fluid_timer((int)(1000/seq->scale), _fluid_seq_queue_process, - (void *)seq, 1, 0); + (void *)seq, TRUE, FALSE, TRUE); } return (0); } diff --git a/fluidsynth/src/fluid_settings.c b/fluidsynth/src/fluid_settings.c index 2f15c0b3..709c854a 100644 --- a/fluidsynth/src/fluid_settings.c +++ b/fluidsynth/src/fluid_settings.c @@ -33,11 +33,17 @@ #define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */ static void fluid_settings_init(fluid_settings_t* settings); -static void fluid_settings_hash_delete(void* value, int type); +static void fluid_settings_key_destroy_func(void* value); +static void fluid_settings_value_destroy_func(void* value); static int fluid_settings_tokenize(char* s, char *buf, char** ptr); +/* Common structure to all settings nodes */ +typedef struct { + int type; /**> fluid_types_enum */ +} fluid_setting_node_t; typedef struct { + fluid_setting_node_t node; char* value; char* def; int hints; @@ -46,47 +52,8 @@ typedef struct { void* data; } fluid_str_setting_t; -static fluid_str_setting_t* -new_fluid_str_setting(char* value, char* def, int hints, fluid_str_update_t fun, void* data) -{ - fluid_str_setting_t* str; - str = FLUID_NEW(fluid_str_setting_t); - str->value = value? FLUID_STRDUP(value) : NULL; - str->def = def? FLUID_STRDUP(def) : NULL; - str->hints = hints; - str->options = NULL; - str->update = fun; - str->data = data; - return str; -} - -static void delete_fluid_str_setting(fluid_str_setting_t* str) -{ - if (str) { - if (str->value) { - FLUID_FREE(str->value); - } - if (str->def) { - FLUID_FREE(str->def); - } - if (str->options) { - fluid_list_t* list = str->options; - - while (list) { - FLUID_FREE (list->data); - list = fluid_list_next(list); - } - - delete_fluid_list(str->options); - } - FLUID_FREE(str); - } -} - - - - typedef struct { + fluid_setting_node_t node; double value; double def; double min; @@ -96,34 +63,8 @@ typedef struct { void* data; } fluid_num_setting_t; - -static fluid_num_setting_t* -new_fluid_num_setting(double min, double max, double def, - int hints, fluid_num_update_t fun, void* data) -{ - fluid_num_setting_t* setting; - setting = FLUID_NEW(fluid_num_setting_t); - setting->value = def; - setting->def = def; - setting->min = min; - setting->max = max; - setting->hints = hints; - setting->update = fun; - setting->data = data; - return setting; -} - -static void delete_fluid_num_setting(fluid_num_setting_t* setting) -{ - if (setting) { - FLUID_FREE(setting); - } -} - - - - typedef struct { + fluid_setting_node_t node; int value; int def; int min; @@ -133,13 +74,73 @@ typedef struct { void* data; } fluid_int_setting_t; +typedef struct { + fluid_setting_node_t node; + fluid_hashtable_t *hashtable; +} fluid_set_setting_t; -static fluid_int_setting_t* -new_fluid_int_setting(int min, int max, int def, - int hints, fluid_int_update_t fun, void* data) + +static fluid_str_setting_t* +new_fluid_str_setting(char* value, char* def, int hints, fluid_str_update_t fun, void* data) { - fluid_int_setting_t* setting; - setting = FLUID_NEW(fluid_int_setting_t); + fluid_str_setting_t* str; + + str = FLUID_NEW(fluid_str_setting_t); + + if (!str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + str->node.type = FLUID_STR_TYPE; + str->value = value? FLUID_STRDUP(value) : NULL; + str->def = def? FLUID_STRDUP(def) : NULL; + str->hints = hints; + str->options = NULL; + str->update = fun; + str->data = data; + return str; +} + +static void +delete_fluid_str_setting(fluid_str_setting_t* str) +{ + if (!str) return; + + if (str->value) FLUID_FREE(str->value); + if (str->def) FLUID_FREE(str->def); + + if (str->options) { + fluid_list_t* list = str->options; + + while (list) { + FLUID_FREE (list->data); + list = fluid_list_next(list); + } + + delete_fluid_list(str->options); + } + + FLUID_FREE(str); +} + + +static fluid_num_setting_t* +new_fluid_num_setting(double min, double max, double def, + int hints, fluid_num_update_t fun, void* data) +{ + fluid_num_setting_t* setting; + + setting = FLUID_NEW(fluid_num_setting_t); + + if (!setting) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + setting->node.type = FLUID_NUM_TYPE; setting->value = def; setting->def = def; setting->min = min; @@ -150,24 +151,94 @@ new_fluid_int_setting(int min, int max, int def, return setting; } -static void delete_fluid_int_setting(fluid_int_setting_t* setting) +static void +delete_fluid_num_setting(fluid_num_setting_t* setting) { - if (setting) { - FLUID_FREE(setting); + if (setting) FLUID_FREE(setting); +} + +static fluid_int_setting_t* +new_fluid_int_setting(int min, int max, int def, + int hints, fluid_int_update_t fun, void* data) +{ + fluid_int_setting_t* setting; + + setting = FLUID_NEW(fluid_int_setting_t); + + if (!setting) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + setting->node.type = FLUID_INT_TYPE; + setting->value = def; + setting->def = def; + setting->min = min; + setting->max = max; + setting->hints = hints; + setting->update = fun; + setting->data = data; + return setting; +} + +static void +delete_fluid_int_setting(fluid_int_setting_t* setting) +{ + if (setting) FLUID_FREE(setting); +} + +static fluid_set_setting_t* +new_fluid_set_setting(void) +{ + fluid_set_setting_t* setting; + + setting = FLUID_NEW(fluid_set_setting_t); + + if (!setting) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + setting->node.type = FLUID_SET_TYPE; + setting->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); + if (!setting->hashtable) + { + FLUID_FREE (setting); + return NULL; + } + + return setting; +} + +static void +delete_fluid_set_setting(fluid_set_setting_t* setting) +{ + if (setting) + { + delete_fluid_hashtable(setting->hashtable); + FLUID_FREE(setting); } } - /** * Create a new settings object - * @return the pointer to the setting object + * @return the pointer to the settings object */ -fluid_settings_t* new_fluid_settings() +fluid_settings_t * +new_fluid_settings(void) { - fluid_settings_t* settings = new_fluid_hashtable(fluid_settings_hash_delete); - if (settings == NULL) { - return NULL; - } + fluid_settings_t* settings; + + settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); + if (settings == NULL) return NULL; + + fluid_mutex_init (settings->mutex); fluid_settings_init(settings); return settings; } @@ -176,14 +247,29 @@ fluid_settings_t* new_fluid_settings() * Delete the provided settings object * @param settings a settings object */ -void delete_fluid_settings(fluid_settings_t* settings) +void +delete_fluid_settings(fluid_settings_t* settings) { + fluid_return_if_fail (settings != NULL); + + fluid_mutex_destroy (settings->mutex); delete_fluid_hashtable(settings); } -void fluid_settings_hash_delete(void* value, int type) +/* Settings hash key destroy function */ +static void +fluid_settings_key_destroy_func(void* value) { - switch (type) { + FLUID_FREE (value); /* Free the string key value */ +} + +/* Settings hash value destroy function */ +static void +fluid_settings_value_destroy_func(void* value) +{ + fluid_setting_node_t *node = value; + + switch (node->type) { case FLUID_NUM_TYPE: delete_fluid_num_setting((fluid_num_setting_t*) value); break; @@ -194,13 +280,16 @@ void fluid_settings_hash_delete(void* value, int type) delete_fluid_str_setting((fluid_str_setting_t*) value); break; case FLUID_SET_TYPE: - delete_fluid_hashtable((fluid_hashtable_t*) value); + delete_fluid_set_setting((fluid_set_setting_t*) value); break; } } -void fluid_settings_init(fluid_settings_t* settings) +void +fluid_settings_init(fluid_settings_t* settings) { + fluid_return_if_fail (settings != NULL); + fluid_synth_settings(settings); fluid_shell_settings(settings); fluid_player_settings(settings); @@ -208,7 +297,8 @@ void fluid_settings_init(fluid_settings_t* settings) fluid_midi_driver_settings(settings); } -static int fluid_settings_tokenize(char* s, char *buf, char** ptr) +static int +fluid_settings_tokenize(char* s, char *buf, char** ptr) { char *tokstr, *tok; int n = 0; @@ -242,41 +332,30 @@ static int fluid_settings_tokenize(char* s, char *buf, char** ptr) * Get a setting name, value and type * * @param settings a settings object - * @param name a setting's name - * @param len - * @param value - * @param type - * @return 1 if the value exists, 0 otherwise + * @param name Array of strings specifying setting node + * @param len Length of name array + * @param value Location to store 7setting node if found + * @return 1 if the node exists, 0 otherwise */ -static int fluid_settings_get(fluid_settings_t* settings, - char** name, int len, - void** value, int* type) +static int +fluid_settings_get(fluid_settings_t* settings, char** name, int len, + fluid_setting_node_t **value) { fluid_hashtable_t* table = settings; - int t; - void* v; + fluid_setting_node_t *node; int n; for (n = 0; n < len; n++) { - if (table == NULL) { - return 0; - } + if (table == NULL) return 0; - if (!fluid_hashtable_lookup(table, name[n], &v, &t)) { - return 0; - } + node = fluid_hashtable_lookup(table, name[n]); + if (!node) return 0; - table = (t == FLUID_SET_TYPE)? (fluid_hashtable_t*) v : NULL; + table = (node->type == FLUID_SET_TYPE) ? ((fluid_set_setting_t *)node)->hashtable : NULL; } - if (value) { - *value = v; - } - - if (type) { - *type = t; - } + if (value) *value = node; return 1; } @@ -285,27 +364,28 @@ static int fluid_settings_get(fluid_settings_t* settings, * Set a setting name, value and type, replacing it if already exists * * @param settings a settings object - * @param name a setting's name - * @param len - * @param value - * @param type + * @param name Array of strings specifying setting node + * @param len Length of name array + * @param value Node instance to assign (used directly) * @return 1 if the value has been set, zero otherwise */ -static int fluid_settings_set(fluid_settings_t* settings, - char** name, int len, - void* value, int type) +static int +fluid_settings_set(fluid_settings_t* settings, + char** name, int len, void* value) { fluid_hashtable_t* table = settings; - int t; - void* v; + fluid_setting_node_t *node; int n, num = len - 1; + char *dupname; for (n = 0; n < num; n++) { - if (fluid_hashtable_lookup(table, name[n], &v, &t)) { + node = fluid_hashtable_lookup(table, name[n]); - if (t == FLUID_SET_TYPE) { - table = (fluid_hashtable_t*) v; + if (node) { + + if (node->type == FLUID_SET_TYPE) { + table = ((fluid_set_setting_t *)node)->hashtable; } else { /* path ends prematurely */ FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]); @@ -314,130 +394,179 @@ static int fluid_settings_set(fluid_settings_t* settings, } else { /* create a new node */ - fluid_hashtable_t* tmp; - tmp = new_fluid_hashtable(fluid_settings_hash_delete); - fluid_hashtable_insert(table, name[n], tmp, FLUID_SET_TYPE); - table = tmp; + fluid_set_setting_t* setnode; + + dupname = FLUID_STRDUP (name[n]); + setnode = new_fluid_set_setting (); + + if (!dupname || !setnode) + { + if (dupname) FLUID_FREE (dupname); + else FLUID_LOG(FLUID_ERR, "Out of memory"); + + if (setnode) delete_fluid_set_setting (setnode); + + return 0; + } + + fluid_hashtable_insert(table, dupname, setnode); + table = setnode->hashtable; } } - fluid_hashtable_replace(table, name[num], value, type); + dupname = FLUID_STRDUP (name[num]); + + if (!dupname) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } + + fluid_hashtable_insert(table, dupname, value); return 1; } /** returns 1 if the value has been registered correctly, 0 otherwise */ -int fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints, - fluid_str_update_t fun, void* data) +int +fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints, + fluid_str_update_t fun, void* data) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; fluid_str_setting_t* setting; + int retval; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - setting = new_fluid_str_setting(def, def, hints, fun, data); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); + fluid_mutex_lock (settings->mutex); + if (!fluid_settings_get(settings, tokens, ntokens, &node)) { + setting = new_fluid_str_setting(def, def, hints, fun, data); + retval = fluid_settings_set(settings, tokens, ntokens, setting); + if (retval != 1) delete_fluid_str_setting (setting); } else { /* if variable already exists, don't change its value. */ - if (type == FLUID_STR_TYPE) { - setting = (fluid_str_setting_t*) value; + if (node->type == FLUID_STR_TYPE) { + setting = (fluid_str_setting_t*) node; setting->update = fun; setting->data = data; setting->def = def? FLUID_STRDUP(def) : NULL; setting->hints = hints; - return 1; + retval = 1; } else { FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - return 1; + retval = 0; } } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** returns 1 if the value has been register correctly, zero otherwise */ -int fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, - double min, double max, int hints, - fluid_num_update_t fun, void* data) +int +fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, + double min, double max, int hints, + fluid_num_update_t fun, void* data) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + fluid_mutex_lock (settings->mutex); + + if (!fluid_settings_get(settings, tokens, ntokens, &node)) { /* insert a new setting */ fluid_num_setting_t* setting; setting = new_fluid_num_setting(min, max, def, hints, fun, data); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); - + retval = fluid_settings_set(settings, tokens, ntokens, setting); + if (retval != 1) delete_fluid_num_setting (setting); } else { - if (type == FLUID_NUM_TYPE) { + if (node->type == FLUID_NUM_TYPE) { /* update the existing setting but don't change its value */ - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + fluid_num_setting_t* setting = (fluid_num_setting_t*) node; setting->update = fun; setting->data = data; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; - return 1; - + retval = 1; } else { /* type mismatch */ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - return 0; + retval = 0; } } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** returns 1 if the value has been register correctly, zero otherwise */ -int fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, - int min, int max, int hints, - fluid_int_update_t fun, void* data) +int +fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, + int min, int max, int hints, + fluid_int_update_t fun, void* data) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + fluid_mutex_lock (settings->mutex); + + if (!fluid_settings_get(settings, tokens, ntokens, &node)) { /* insert a new setting */ fluid_int_setting_t* setting; setting = new_fluid_int_setting(min, max, def, hints, fun, data); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); - + retval = fluid_settings_set(settings, tokens, ntokens, setting); + if (retval != 1) delete_fluid_int_setting (setting); } else { - if (type == FLUID_INT_TYPE) { + if (node->type == FLUID_INT_TYPE) { /* update the existing setting but don't change its value */ - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; + fluid_int_setting_t* setting = (fluid_int_setting_t*) node; setting->update = fun; setting->data = data; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; - return 1; - + retval = 1; } else { /* type mismatch */ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); - return 0; + retval = 0; } } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -450,15 +579,22 @@ int fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, int fluid_settings_get_type(fluid_settings_t* settings, char* name) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int type; + + fluid_return_val_if_fail (settings != NULL, FLUID_NO_TYPE); + fluid_return_val_if_fail (name != NULL, FLUID_NO_TYPE); ntokens = fluid_settings_tokenize(name, buf, tokens); - return (fluid_settings_get(settings, tokens, ntokens, &value, &type))? type : FLUID_NO_TYPE; + fluid_mutex_lock (settings->mutex); + type = fluid_settings_get (settings, tokens, ntokens, &node) ? node->type : FLUID_NO_TYPE; + fluid_mutex_unlock (settings->mutex); + + return (type); } /** @@ -471,27 +607,32 @@ fluid_settings_get_type(fluid_settings_t* settings, char* name) int fluid_settings_get_hints(fluid_settings_t* settings, char* name) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int hints = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - if (type == FLUID_NUM_TYPE) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - return setting->hints; - } else if (type == FLUID_STR_TYPE) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return setting->hints; - } else { - return 0; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node)) { + if (node->type == FLUID_NUM_TYPE) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) node; + hints = setting->hints; + } else if (node->type == FLUID_STR_TYPE) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; + hints = setting->hints; } - } else { - return 0; } + + fluid_mutex_unlock (settings->mutex); + + return hints; } /** @@ -504,28 +645,32 @@ fluid_settings_get_hints(fluid_settings_t* settings, char* name) int fluid_settings_is_realtime(fluid_settings_t* settings, char* name) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int isrealtime = FALSE; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { - if (type == FLUID_NUM_TYPE) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - return setting->update != NULL; + fluid_mutex_lock (settings->mutex); - } else if (type == FLUID_STR_TYPE) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return setting->update != NULL; - } else { - return 0; + if (fluid_settings_get(settings, tokens, ntokens, &node)) { + if (node->type == FLUID_NUM_TYPE) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) node; + isrealtime = setting->update != NULL; + } else if (node->type == FLUID_STR_TYPE) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; + isrealtime = setting->update != NULL; } - } else { - return 0; } + + fluid_mutex_unlock (settings->mutex); + + return isrealtime; } /** @@ -536,75 +681,188 @@ fluid_settings_is_realtime(fluid_settings_t* settings, char* name) * @param str new string value * @return 1 if the value has been set, 0 otherwise */ -int fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str) +int +fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str) { char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; - int type; - void* value; + fluid_setting_node_t *node; fluid_str_setting_t* setting; + int retval = 0; - ntokens = fluid_settings_tokenize(name, buf, tokens); + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + ntokens = fluid_settings_tokenize (name, buf, tokens); - if (type != FLUID_STR_TYPE) { - return 0; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get (settings, tokens, ntokens, &node)) { + if (node->type == FLUID_STR_TYPE) { + setting = (fluid_str_setting_t *)node; + + if (setting->value) FLUID_FREE (setting->value); + setting->value = str ? FLUID_STRDUP (str) : NULL; + + /* Call under lock to keep update() synchronized with the current value */ + if (setting->update) (*setting->update)(setting->data, name, str); + retval = 1; } - - setting = (fluid_str_setting_t*) value; - - if (setting->value) { - FLUID_FREE(setting->value); - } - setting->value = str? FLUID_STRDUP(str) : NULL; - - if (setting->update) { - (*setting->update)(setting->data, name, setting->value); - } - - return 1; - } else { /* insert a new setting */ fluid_str_setting_t* setting; setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL); - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); + retval = fluid_settings_set(settings, tokens, ntokens, setting); + if (retval != 1) delete_fluid_str_setting (setting); } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** - * Get the value of a string setting. - * - * If the value does not exists, 'str' is set to NULL. Otherwise, 'str' will - * point to the value. The application does not own the returned value. Instead, - * the application should make a copy of the value if it needs it later. - * + * Copy the value of a string setting * @param settings a settings object * @param name a setting's name - * @param str pointer to the string containing the setting's value + * @param str Caller supplied buffer to copy string value to + * @param len Size of 'str' buffer (no more than len bytes will be written, which + * will always include a '\0' terminator) * @return 1 if the value exists, 0 otherwise + * @since 1.1.0 + * + * Like fluid_settings_getstr() but is thread safe. A size of 256 should be + * more than sufficient for the string buffer. + */ +int +fluid_settings_copystr(fluid_settings_t* settings, char* name, char* str, int len) +{ + fluid_setting_node_t *node; + char* tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL+1]; + int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (str != NULL, 0); + fluid_return_val_if_fail (len > 0, 0); + + str[0] = 0; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; + + if (setting->value) + { + FLUID_STRNCPY (str, setting->value, len); + str[len - 1] = 0; /* Force terminate, in case of truncation */ + } + + retval = 1; + } + + fluid_mutex_unlock (settings->mutex); + + return retval; +} + +/** + * Duplicate the value of a string setting + * @param settings a settings object + * @param name a setting's name + * @param str Location to store pointer to allocated duplicate string + * @return 1 if the value exists and was successfully duplicated, 0 otherwise + * @since 1.1.0 + * + * Like fluid_settings_copystr() but allocates a new copy of the string. Caller + * owns the string and should free it with free() when done using it. + */ +int +fluid_settings_dupstr(fluid_settings_t* settings, char* name, char** str) +{ + fluid_setting_node_t *node; + char* tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL+1]; + int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (str != NULL, 0); + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; + + if (setting->value) + { + *str = FLUID_STRDUP (setting->value); + if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory"); + } + + if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */ + } + + fluid_mutex_unlock (settings->mutex); + + return retval; +} + +/** + * Get the value of a string setting + * @param settings a settings object + * @param name a setting's name + * @param str Location to store pointer to the settings string value + * @return 1 if the value exists, 0 otherwise + * + * If the value does not exists, 'str' is set to NULL. Otherwise, 'str' will + * point to the value. The application does not own the returned value and it + * is valid only until a new value is assigned to the setting of the given name. + * + * NOTE: In a multi-threaded environment, caller must ensure that the setting + * being read by fluid_settings_getstr() is not assigned during the + * duration of callers use of the setting's value. Use fluid_settings_copystr() + * or fluid_settings_dupstr() which does not have this restriction. */ int fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (str != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; *str = setting->value; - return 1; + retval = 1; } - *str = NULL; - return 0; + else *str = NULL; + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -615,26 +873,37 @@ fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str) * @param s a string to be tested * @return 1 if the value exists and is equal to 's', 0 otherwise */ -int fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* s) +int +fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* s) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (s != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return FLUID_STRCMP(setting->value, s) == 0; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; + if (setting->value) retval = FLUID_STRCMP (setting->value, s) == 0; } - return 0; + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** - * Get the default value of a string setting. + * Get the default value of a string setting. Note that the returned string is + * not owned by the caller and should not be modified or freed. * * @param settings a settings object * @param name a setting's name @@ -643,58 +912,95 @@ int fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* s) char* fluid_settings_getstr_default(fluid_settings_t* settings, char* name) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + char *retval = NULL; + + fluid_return_val_if_fail (settings != NULL, NULL); + fluid_return_val_if_fail (name != NULL, NULL); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; - return setting->def; - } else { - return NULL; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; + retval = setting->def; } + + fluid_mutex_unlock (settings->mutex); + + return retval; } -int fluid_settings_add_option(fluid_settings_t* settings, char* name, char* s) +/** + * Add an option to a string setting (like an enumeration value). + * @param settings a settings object + * @param name a setting's name + * @param s option string to add + * @return 1 if the setting exists and option was added, 0 otherwise + */ +int +fluid_settings_add_option(fluid_settings_t* settings, char* name, char* s) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (s != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; char* copy = FLUID_STRDUP(s); setting->options = fluid_list_append(setting->options, copy); - return 1; - } else { - return 0; + retval = 1; } + + fluid_mutex_unlock (settings->mutex); + + return retval; } -int fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s) +/** + * Remove an option previously assigned by fluid_settings_add_option(). + * @param settings a settings object + * @param name a setting's name + * @param s option string to remove + * @return 1 if the setting exists and option was removed, 0 otherwise + */ +int +fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (s != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { + fluid_mutex_lock (settings->mutex); - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; fluid_list_t* list = setting->options; while (list) { @@ -702,15 +1008,16 @@ int fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s if (FLUID_STRCMP(s, option) == 0) { FLUID_FREE (option); setting->options = fluid_list_remove_link(setting->options, list); - return 1; + retval = 1; + break; } list = fluid_list_next(list); } - - return 0; - } else { - return 0; } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -721,46 +1028,48 @@ int fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s * @param val new setting's value * @return 1 if the value has been set, 0 otherwise */ -int fluid_settings_setnum(fluid_settings_t* settings, char* name, double val) +int +fluid_settings_setnum(fluid_settings_t* settings, char* name, double val) { - int type; - void* value; + fluid_setting_node_t *node; fluid_num_setting_t* setting; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + fluid_mutex_lock (settings->mutex); - if (type != FLUID_NUM_TYPE) { - return 0; + if (fluid_settings_get(settings, tokens, ntokens, &node)) { + if (node->type == FLUID_NUM_TYPE) { + setting = (fluid_num_setting_t*) node; + + if (val < setting->min) val = setting->min; + else if (val > setting->max) val = setting->max; + + setting->value = val; + + /* Call under lock to keep update() synchronized with the current value */ + if (setting->update) (*setting->update)(setting->data, name, val); + retval = 1; } - - setting = (fluid_num_setting_t*) value; - - if (val < setting->min) { - val = setting->min; - } else if (val > setting->max) { - val = setting->max; - } - - setting->value = val; - - if (setting->update) { - (*setting->update)(setting->data, name, val); - } - - return 1; - } else { /* insert a new setting */ fluid_num_setting_t* setting; setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL); setting->value = val; - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); + retval = fluid_settings_set(settings, tokens, ntokens, setting); + if (retval != 1) delete_fluid_num_setting (setting); } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -771,23 +1080,33 @@ int fluid_settings_setnum(fluid_settings_t* settings, char* name, double val) * @param val variable pointer to receive the setting's numeric value * @return 1 if the value exists, 0 otherwise */ -int fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val) +int +fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (val != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_NUM_TYPE)) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) node; *val = setting->value; - return 1; + retval = 1; } - return 0; + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -801,20 +1120,28 @@ int fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val) void fluid_settings_getnum_range(fluid_settings_t* settings, char* name, double* min, double* max) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + fluid_return_if_fail (settings != NULL); + fluid_return_if_fail (name != NULL); + fluid_return_if_fail (min != NULL); + fluid_return_if_fail (max != NULL); + ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_NUM_TYPE)) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) node; *min = setting->min; *max = setting->max; } + + fluid_mutex_unlock (settings->mutex); } /** @@ -827,21 +1154,28 @@ fluid_settings_getnum_range(fluid_settings_t* settings, char* name, double* min, double fluid_settings_getnum_default(fluid_settings_t* settings, char* name) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + double retval = 0.0; + + fluid_return_val_if_fail (settings != NULL, 0.0); + fluid_return_val_if_fail (name != NULL, 0.0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_NUM_TYPE)) { - fluid_num_setting_t* setting = (fluid_num_setting_t*) value; - return setting->def; - } else { - return 0.0f; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_NUM_TYPE)) { + fluid_num_setting_t* setting = (fluid_num_setting_t*) node; + retval = setting->def; } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -852,46 +1186,48 @@ fluid_settings_getnum_default(fluid_settings_t* settings, char* name) * @param val new setting's integer value * @return 1 if the value has been set, 0 otherwise */ -int fluid_settings_setint(fluid_settings_t* settings, char* name, int val) +int +fluid_settings_setint(fluid_settings_t* settings, char* name, int val) { - int type; - void* value; + fluid_setting_node_t *node; fluid_int_setting_t* setting; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { + fluid_mutex_lock (settings->mutex); - if (type != FLUID_INT_TYPE) { - return 0; + if (fluid_settings_get(settings, tokens, ntokens, &node)) { + if (node->type == FLUID_INT_TYPE) { + setting = (fluid_int_setting_t*) node; + + if (val < setting->min) val = setting->min; + else if (val > setting->max) val = setting->max; + + setting->value = val; + + /* Call under lock to keep update() synchronized with the current value */ + if (setting->update) (*setting->update)(setting->data, name, val); + retval = 1; } - - setting = (fluid_int_setting_t*) value; - - if (val < setting->min) { - val = setting->min; - } else if (val > setting->max) { - val = setting->max; - } - - setting->value = val; - - if (setting->update) { - (*setting->update)(setting->data, name, val); - } - - return 1; - } else { /* insert a new setting */ fluid_int_setting_t* setting; setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL); setting->value = val; - return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); + retval = fluid_settings_set(settings, tokens, ntokens, setting); + if (retval != 1) delete_fluid_int_setting (setting); } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -902,23 +1238,33 @@ int fluid_settings_setint(fluid_settings_t* settings, char* name, int val) * @param val pointer to a variable to receive the setting's integer value * @return 1 if the value exists, 0 otherwise */ -int fluid_settings_getint(fluid_settings_t* settings, char* name, int* val) +int +fluid_settings_getint(fluid_settings_t* settings, char* name, int* val) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); + fluid_return_val_if_fail (val != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_INT_TYPE)) { + fluid_int_setting_t* setting = (fluid_int_setting_t*) node; *val = setting->value; - return 1; + retval = 1; } - return 0; + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** @@ -928,22 +1274,31 @@ int fluid_settings_getint(fluid_settings_t* settings, char* name, int* val) * @param min setting's range lower limit * @param max setting's range upper limit */ -void fluid_settings_getint_range(fluid_settings_t* settings, char* name, int* min, int* max) +void +fluid_settings_getint_range(fluid_settings_t* settings, char* name, int* min, int* max) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + fluid_return_if_fail (settings != NULL); + fluid_return_if_fail (name != NULL); + fluid_return_if_fail (min != NULL); + fluid_return_if_fail (max != NULL); + ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_INT_TYPE)) { + fluid_int_setting_t* setting = (fluid_int_setting_t*) node; *min = setting->min; *max = setting->max; } + + fluid_mutex_unlock (settings->mutex); } /** @@ -956,25 +1311,32 @@ void fluid_settings_getint_range(fluid_settings_t* settings, char* name, int* mi int fluid_settings_getint_default(fluid_settings_t* settings, char* name) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; + int retval = 0; + + fluid_return_val_if_fail (settings != NULL, 0); + fluid_return_val_if_fail (name != NULL, 0); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_INT_TYPE)) { - fluid_int_setting_t* setting = (fluid_int_setting_t*) value; - return setting->def; - } else { - return 0; + fluid_mutex_lock (settings->mutex); + + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_INT_TYPE)) { + fluid_int_setting_t* setting = (fluid_int_setting_t*) node; + retval = setting->def; } + + fluid_mutex_unlock (settings->mutex); + + return retval; } /** - * Iterate the available options for a named setting, calling the provided + * Iterate the available options for a named string setting, calling the provided * callback function for each existing option. * * @param settings a settings object @@ -984,24 +1346,25 @@ fluid_settings_getint_default(fluid_settings_t* settings, char* name) */ void fluid_settings_foreach_option (fluid_settings_t* settings, char* name, void* data, - fluid_settings_foreach_option_t func) + fluid_settings_foreach_option_t func) { - int type; - void* value; + fluid_setting_node_t *node; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; - if (!func) { - return; - } + fluid_return_if_fail (settings != NULL); + fluid_return_if_fail (name != NULL); + fluid_return_if_fail (func != NULL); ntokens = fluid_settings_tokenize(name, buf, tokens); - if (fluid_settings_get(settings, tokens, ntokens, &value, &type) - && (type == FLUID_STR_TYPE)) { + fluid_mutex_lock (settings->mutex); - fluid_str_setting_t* setting = (fluid_str_setting_t*) value; + if (fluid_settings_get(settings, tokens, ntokens, &node) + && (node->type == FLUID_STR_TYPE)) { + + fluid_str_setting_t* setting = (fluid_str_setting_t*) node; fluid_list_t* list = setting->options; while (list) { @@ -1010,34 +1373,50 @@ fluid_settings_foreach_option (fluid_settings_t* settings, char* name, void* dat list = fluid_list_next(list); } } + + fluid_mutex_unlock (settings->mutex); } - -static fluid_settings_foreach_t fluid_settings_foreach_func; -static void* fluid_settings_foreach_data; - -int fluid_settings_foreach_iter(char* key, void* value, int type, void* data) +/* Structure passed to fluid_settings_foreach_iter recursive function */ +typedef struct { - char path[1024]; + fluid_settings_foreach_t func; + void *data; + char path[MAX_SETTINGS_LABEL+1]; /* Maximum settings label length */ +} fluid_settings_foreach_bag_t; - if (data == 0) { - snprintf(path, 1024, "%s", key); - } else { - snprintf(path, 1024, "%s.%s", (char*) data, key); +int +fluid_settings_foreach_iter (void* key, void* value, void* data) +{ + fluid_settings_foreach_bag_t *bag = data; + char *keystr = key; + fluid_setting_node_t *node = value; + int pathlen; + + pathlen = strlen (bag->path); + + if (pathlen > 0) + { + bag->path[pathlen] = '.'; + bag->path[pathlen + 1] = 0; } - path[1023] = 0; - switch (type) { + strcat (bag->path, keystr); + + switch (node->type) { case FLUID_NUM_TYPE: case FLUID_INT_TYPE: case FLUID_STR_TYPE: - (*fluid_settings_foreach_func)(fluid_settings_foreach_data, path, type); + (*bag->func)(bag->data, bag->path, node->type); break; case FLUID_SET_TYPE: - fluid_hashtable_foreach((fluid_hashtable_t*) value, fluid_settings_foreach_iter, &path[0]); + fluid_hashtable_foreach(((fluid_set_setting_t *)value)->hashtable, + fluid_settings_foreach_iter, bag); break; } + bag->path[pathlen] = 0; + return 0; } @@ -1052,7 +1431,16 @@ int fluid_settings_foreach_iter(char* key, void* value, int type, void* data) void fluid_settings_foreach(fluid_settings_t* settings, void* data, fluid_settings_foreach_t func) { - fluid_settings_foreach_func = func; - fluid_settings_foreach_data = data; - fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, 0); + fluid_settings_foreach_bag_t bag; + + fluid_return_if_fail (settings != NULL); + fluid_return_if_fail (func != NULL); + + bag.func = func; + bag.data = data; + bag.path[0] = 0; + + fluid_mutex_lock (settings->mutex); + fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, &bag); + fluid_mutex_unlock (settings->mutex); } diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index 922beb29..7807ea1b 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -26,6 +26,7 @@ #include "fluid_tuning.h" #include "fluid_settings.h" #include "fluid_sfont.h" +#include "fluid_hash.h" #ifdef TRAP_ON_FPE #define _GNU_SOURCE @@ -35,35 +36,83 @@ extern int feenableexcept (int excepts); #endif +/* A descriptive alias for fluid_return_if_fail/fluid_return_val_if_fail */ +#define fluid_synth_is_synth_thread(_s) (fluid_thread_get_id() == (_s)->synth_thread_id) + +/* Macro used to check if an event should be queued or not (not in synthesis thread context?) */ +#define fluid_synth_should_queue(_s) (!fluid_synth_is_synth_thread(_s)) + + static void fluid_synth_init(void); +static int fluid_synth_return_event_process_callback (void* data, unsigned int msec); +static fluid_event_queue_t *fluid_synth_get_event_queue (fluid_synth_t* synth); +static int fluid_synth_queue_midi_event (fluid_synth_t* synth, int type, int chan, + int param1, int param2); +static int fluid_synth_queue_gen_event (fluid_synth_t* synth, int chan, + int param, float value, int absolute); +static int fluid_synth_queue_float_event (fluid_synth_t* synth, int type, float val); +static int fluid_synth_queue_int_event (fluid_synth_t* synth, int type, int val); +static void fluid_synth_thread_queue_destroy_notify (void *data); +static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, + int vel); +static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key); +static int fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan); +static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num, + int value); +static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); +static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan); +static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth); +static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, + int is_cc, int ctrl); +static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan); +static int fluid_synth_channel_pressure_LOCAL(fluid_synth_t* synth, int channum, + int val); +static int fluid_synth_pitch_bend_LOCAL(fluid_synth_t* synth, int chan, int val); +static int fluid_synth_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan, + int val); +static int fluid_synth_set_preset (fluid_synth_t *synth, int chan, + fluid_preset_t *preset); +static int fluid_synth_set_preset_LOCAL (fluid_synth_t *synth, int chan, + fluid_preset_t *preset); static fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, - unsigned int banknum, unsigned int prognum); + unsigned int banknum, unsigned int prognum); static fluid_preset_t* -fluid_synth_get_preset2(fluid_synth_t* synth, char* sfont_name, - unsigned int banknum, unsigned int prognum); +fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname, + unsigned int banknum, unsigned int prognum); + static void fluid_synth_update_presets(fluid_synth_t* synth); static int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value); +static void fluid_synth_set_gain_LOCAL(fluid_synth_t* synth, float gain); static int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value); +static int fluid_synth_set_polyphony_LOCAL(fluid_synth_t* synth, int polyphony); static void init_dither(void); static inline int roundi (float x); static int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out); -static fluid_voice_t* fluid_synth_free_voice_by_kill(fluid_synth_t* synth); -static void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, - fluid_voice_t* new_voice); +static FLUID_INLINE void fluid_synth_process_event_queue_LOCAL + (fluid_synth_t *synth, fluid_event_queue_t *queue); +static fluid_voice_t* fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth); +static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth, + fluid_voice_t* new_voice); +static fluid_sfont_info_t *new_fluid_sfont_info (fluid_synth_t *synth, + fluid_sfont_t *sfont); +static void fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont); static int fluid_synth_sfunload_callback(void* data, unsigned int msec); -static void fluid_synth_sfunload_macos9(fluid_synth_t* synth); -static void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, - int chan, int key); +static int fluid_synth_set_reverb_LOCAL(fluid_synth_t* synth, int set, double roomsize, + double damping, double width, double level); +static int fluid_synth_set_chorus_LOCAL(fluid_synth_t* synth, int set, int nr, float level, + float speed, float depth_ms, int type); +static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, + int chan, int key); static fluid_tuning_t* fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog); static fluid_tuning_t* fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, char* name); -static fluid_bank_offset_t* fluid_synth_get_bank_offset0(fluid_synth_t* synth, - int sfont_id); -static void fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id); +static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, + int param, float value, int absolute); +static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id); fluid_sfloader_t* new_fluid_defsfloader(void); @@ -427,8 +476,13 @@ int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer) * FLUID SYNTH */ -/* - * new_fluid_synth +/** + * Create new FluidSynth instance. + * @param settings Configuration parameters to use (used directly). + * @return New FluidSynth instance or NULL on error + * + * NOTE: The settings parameter is used directly and should not be modified + * or freed independently. */ fluid_synth_t* new_fluid_synth(fluid_settings_t *settings) @@ -450,7 +504,15 @@ new_fluid_synth(fluid_settings_t *settings) } FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); - fluid_mutex_init(synth->busy); + fluid_mutex_init(synth->mutex); + fluid_private_init(synth->thread_queues); + + synth->return_queue = fluid_event_queue_new (FLUID_MAX_RETURN_EVENTS); + + if (synth->return_queue == NULL) { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } synth->settings = settings; @@ -528,7 +590,8 @@ new_fluid_synth(fluid_settings_t *settings) /* as soon as the synth is created it starts playing. */ synth->state = FLUID_SYNTH_PLAYING; - synth->sfont = NULL; + synth->sfont_info = NULL; + synth->sfont_hash = new_fluid_hashtable (NULL, NULL); synth->noteid = 0; synth->ticks = 0; synth->tuning = NULL; @@ -632,11 +695,14 @@ new_fluid_synth(fluid_settings_t *settings) goto error_recovery; } - fluid_synth_set_reverb(synth, - FLUID_REVERB_DEFAULT_ROOMSIZE, - FLUID_REVERB_DEFAULT_DAMP, - FLUID_REVERB_DEFAULT_WIDTH, - FLUID_REVERB_DEFAULT_LEVEL); + synth->reverb_roomsize = FLUID_REVERB_DEFAULT_ROOMSIZE; + synth->reverb_damping = FLUID_REVERB_DEFAULT_DAMP; + synth->reverb_width = FLUID_REVERB_DEFAULT_WIDTH; + synth->reverb_level = FLUID_REVERB_DEFAULT_LEVEL; + + fluid_revmodel_set (synth->reverb, FLUID_REVMODEL_SET_ALL, + synth->reverb_roomsize, synth->reverb_damping, + synth->reverb_width, synth->reverb_level); /* allocate the chorus module */ synth->chorus = new_fluid_chorus(synth->sample_rate); @@ -648,6 +714,9 @@ new_fluid_synth(fluid_settings_t *settings) /* FIXME */ synth->start = fluid_curtime(); + /* Spawn low priority periodic timer thread to process synth thread return events */ + synth->return_queue_timer = new_fluid_timer (200, fluid_synth_return_event_process_callback, + synth, TRUE, TRUE, FALSE); return synth; error_recovery: @@ -655,16 +724,68 @@ new_fluid_synth(fluid_settings_t *settings) return NULL; } -/* - * delete_fluid_synth +/* Callback to process synthesis thread return events */ +static int +fluid_synth_return_event_process_callback (void* data, unsigned int msec) +{ + fluid_synth_t *synth = data; + fluid_event_queue_elem_t *event; + fluid_preset_t *preset; + + while ((event = fluid_event_queue_get_outptr (synth->return_queue))) + { + switch (event->type) + { + case FLUID_EVENT_QUEUE_ELEM_GAIN: /* Sync gain variable */ + fluid_mutex_lock (synth->mutex); /* ++ Lock gain variable */ + synth->gain = event->dval; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + break; + case FLUID_EVENT_QUEUE_ELEM_REVERB: /* Sync reverb shadow variables */ + fluid_mutex_lock (synth->mutex); /* ++ Lock reverb shadow variables */ + synth->reverb_roomsize = event->reverb.roomsize; + synth->reverb_damping = event->reverb.damping; + synth->reverb_width = event->reverb.width; + synth->reverb_level = event->reverb.level; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + break; + case FLUID_EVENT_QUEUE_ELEM_CHORUS: /* Sync chorus shadow variables */ + fluid_mutex_lock (synth->mutex); /* ++ Lock chorus shadow variables */ + synth->chorus_nr = event->chorus.nr; + synth->chorus_level = event->chorus.level; + synth->chorus_speed = event->chorus.speed; + synth->chorus_depth = event->chorus.depth; + synth->chorus_type = event->chorus.type; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + break; + case FLUID_EVENT_QUEUE_ELEM_FREE_PRESET: /* Preset free event */ + preset = (fluid_preset_t *)(event->pval); + fluid_synth_sfont_unref (synth, preset->sfont); /* -- unref preset's SoundFont */ + delete_fluid_preset (preset); + break; + } + + fluid_event_queue_next_outptr (synth->return_queue); + } + + return TRUE; /* Keep this timer callback active */ +} + +/** + * Delete a FluidSynth instance. + * @param synth FluidSynth instance to delete + * @return FLUID_OK + * + * NOTE: Other users of a synthesizer instance, such as audio and MIDI drivers, + * should be deleted prior to freeing the FluidSynth instance. */ int delete_fluid_synth(fluid_synth_t* synth) { int i, k; fluid_list_t *list; - fluid_sfont_t* sfont; - fluid_bank_offset_t* bank_offset; + fluid_sfont_info_t* sfont_info; + fluid_event_queue_t* queue; fluid_sfloader_t* loader; if (synth == NULL) { @@ -675,6 +796,15 @@ delete_fluid_synth(fluid_synth_t* synth) synth->state = FLUID_SYNTH_STOPPED; + /* Stop return event queue thread, and process remaining events */ + if (synth->return_queue_timer) + delete_fluid_timer (synth->return_queue_timer); + + if (synth->return_queue) { + fluid_synth_return_event_process_callback(synth, 0); + fluid_event_queue_free(synth->return_queue); + } + /* turn off all voices, needed to unload SoundFont data */ if (synth->voice != NULL) { for (i = 0; i < synth->nvoice; i++) { @@ -684,20 +814,17 @@ delete_fluid_synth(fluid_synth_t* synth) } /* delete all the SoundFonts */ - for (list = synth->sfont; list; list = fluid_list_next(list)) { - sfont = (fluid_sfont_t*) fluid_list_get(list); - delete_fluid_sfont(sfont); + for (list = synth->sfont_info; list; list = fluid_list_next (list)) { + sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + delete_fluid_sfont (sfont_info->sfont); + FLUID_FREE (sfont_info); } - delete_fluid_list(synth->sfont); + delete_fluid_list(synth->sfont_info); - /* and the SoundFont offsets */ - for (list = synth->bank_offsets; list; list = fluid_list_next(list)) { - bank_offset = (fluid_bank_offset_t*) fluid_list_get(list); - FLUID_FREE(bank_offset); - } - delete_fluid_list(synth->bank_offsets); + /* Delete the SoundFont info hash */ + if (synth->sfont_hash) delete_fluid_hashtable (synth->sfont_hash); /* delete all the SoundFont loaders */ @@ -781,7 +908,7 @@ delete_fluid_synth(fluid_synth_t* synth) if (synth->tuning[i] != NULL) { for (k = 0; k < 128; k++) { if (synth->tuning[i][k] != NULL) { - FLUID_FREE(synth->tuning[i][k]); + delete_fluid_tuning(synth->tuning[i][k]); } } FLUID_FREE(synth->tuning[i]); @@ -796,44 +923,282 @@ delete_fluid_synth(fluid_synth_t* synth) FLUID_FREE(synth->LADSPA_FxUnit); #endif - fluid_mutex_destroy(synth->busy); + /* free any queues in pool */ + for (list = synth->queue_pool; list; list = list->next) { + queue = (fluid_event_queue_t *)(list->data); + /* Prevent double-free later */ + for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++) + if (synth->queues[i] == queue) synth->queues[i] = NULL; + fluid_event_queue_free (queue); + } + + /* free remaining event queues, if any */ + for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++) + if (synth->queues[i]) fluid_event_queue_free (synth->queues[i]); + + + delete_fluid_list (synth->queue_pool); + + fluid_mutex_destroy(synth->mutex); FLUID_FREE(synth); return FLUID_OK; } -/* - * fluid_synth_error - * - * The error messages are not thread-save, yet. They are still stored - * in a global message buffer (see fluid_sys.c). - * */ +/** + * Get a textual representation of the last error + * @param synth FluidSynth instance + * @return Pointer to string of last error message. Valid until the same + * calling thread calls another FluidSynth function which fails. String is + * internal and should not be modified or freed. + */ +/* FIXME - The error messages are not thread-safe, yet. They are still stored + * in a global message buffer (see fluid_sys.c). */ char* fluid_synth_error(fluid_synth_t* synth) { return fluid_error(); } -/* - * fluid_synth_noteon +/* Get event queue for the current thread (create if necessary) */ +static fluid_event_queue_t * +fluid_synth_get_event_queue (fluid_synth_t* synth) +{ + fluid_event_queue_t *queue; + int i; + + queue = fluid_private_get (synth->thread_queues); /* Get event queue for this thread */ + + if (!queue) /* This thread has no queue yet? */ + { + fluid_mutex_lock (synth->mutex); /* ++ lock queue_pool */ + + /* Use an unclaimed queue, if any (it will already be in synth->queues[] in that case) */ + if (synth->queue_pool) + { + fluid_list_t *p; + + queue = synth->queue_pool->data; + + /* Remove from queue_pool list */ + p = synth->queue_pool; + synth->queue_pool = fluid_list_remove_link (p, p); + delete1_fluid_list (p); + } + + fluid_mutex_unlock (synth->mutex); /* -- unlock queue_pool */ + + if (!queue) /* Create event queue, if one wasn't re-claimed */ + { + queue = fluid_event_queue_new (FLUID_MAX_EVENTS_PER_BUFSIZE); + if (!queue) return NULL; /* Error has already been logged */ + + queue->synth = synth; + + /* Atomicly and in a lock free fashion, put queue pointer in queues[] array */ + for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++) + { + if (!fluid_atomic_pointer_get (&synth->queues[i])) + { + if (fluid_atomic_pointer_compare_and_exchange ((void **)&synth->queues[i], + NULL, (void *)queue)) + break; + } + } + + if (i == FLUID_MAX_EVENT_QUEUES) + { + FLUID_LOG (FLUID_ERR, "Maximum thread event queues exceeded"); + return NULL; + } + } + + fluid_private_set (synth->thread_queues, queue, fluid_synth_thread_queue_destroy_notify); + } + + return queue; +} + +/** + * Queues a MIDI event to the FluidSynth synthesis thread. + * @param synth FluidSynth instance + * @param type MIDI event type (#fluid_midi_event_type) + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param1 MIDI event first parameter (depends on type) + * @param param2 MIDI event second parameter (depends on type) + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +static int +fluid_synth_queue_midi_event (fluid_synth_t* synth, int type, int chan, + int param1, int param2) +{ + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; + + queue = fluid_synth_get_event_queue (synth); + if (!queue) return FLUID_FAILED; + + event = fluid_event_queue_get_inptr (queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_MIDI; + event->midi.type = type; + event->midi.channel = chan; + event->midi.param1 = param1; + event->midi.param2 = param2; + + fluid_event_queue_next_inptr (queue); + + return FLUID_OK; +} + +/** + * Queues a generator assignment event to the FluidSynth synthesis thread. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param Generator ID (#fluid_gen_type) + * @param value Value to assign to generator + * @param absolute TRUE if value is an absolute assignment, FALSE for relative + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +static int +fluid_synth_queue_gen_event (fluid_synth_t* synth, int chan, + int param, float value, int absolute) +{ + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; + + queue = fluid_synth_get_event_queue (synth); + if (!queue) return FLUID_FAILED; + + event = fluid_event_queue_get_inptr (queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_GEN; + event->gen.channel = chan; + event->gen.param = param; + event->gen.value = value; + event->gen.absolute = absolute; + + fluid_event_queue_next_inptr (queue); + + return FLUID_OK; +} + +/** + * Queues an event with a floating point value payload. + * @param synth FluidSynth instance + * @param type Event type (#fluid_event_queue_elem) + * @param val Event value + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +static int +fluid_synth_queue_float_event (fluid_synth_t* synth, int type, float val) +{ + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; + + queue = fluid_synth_get_event_queue (synth); + if (!queue) return FLUID_FAILED; + + event = fluid_event_queue_get_inptr (queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return FLUID_FAILED; + } + + event->type = type; + event->dval = val; + + fluid_event_queue_next_inptr (queue); + + return FLUID_OK; +} + +/** + * Queues an event with an integer value payload. + * @param synth FluidSynth instance + * @param type Event type (#fluid_event_queue_elem) + * @param val Event value + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +static int +fluid_synth_queue_int_event (fluid_synth_t* synth, int type, int val) +{ + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; + + queue = fluid_synth_get_event_queue (synth); + if (!queue) return FLUID_FAILED; + + event = fluid_event_queue_get_inptr (queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return FLUID_FAILED; + } + + event->type = type; + event->ival = val; + + fluid_event_queue_next_inptr (queue); + + return FLUID_OK; +} + +/* Gets called when a thread ends, which has been assigned a queue */ +static void +fluid_synth_thread_queue_destroy_notify (void *data) +{ + fluid_event_queue_t *queue = data; + fluid_synth_t *synth = queue->synth; + + /* Queues are not freed (can't be thread safe without locking in synth thread), + * added to pool for potential future use */ + fluid_mutex_lock (synth->mutex); /* ++ lock queue_pool */ + synth->queue_pool = fluid_list_prepend (synth->queue_pool, queue); + fluid_mutex_unlock (synth->mutex); /* -- unlock queue_pool */ +} + +/** + * Send a note-on event to a FluidSynth object. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @param vel MIDI velocity (0-127, 0=noteoff) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) { - fluid_channel_t* channel; - int r = FLUID_FAILED; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail (vel >= 0 && vel <= 127, FLUID_FAILED); - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, NOTE_ON, chan, key, vel); + else return fluid_synth_noteon_LOCAL (synth, chan, key, vel); +} + +/* Local synthesis thread variant of fluid_synth_noteon */ +static int +fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel) +{ + fluid_channel_t* channel; /* notes with velocity zero go to noteoff */ - if (vel == 0) { - return fluid_synth_noteoff(synth, chan, key); - } + if (vel == 0) return fluid_synth_noteoff_LOCAL(synth, chan, key); channel = synth->channel[chan]; @@ -851,22 +1216,40 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) /* If there is another voice process on the same channel and key, advance it to the release phase. */ - fluid_synth_release_voice_on_same_note(synth, chan, key); + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); - return fluid_synth_start(synth, synth->noteid++, channel->preset, 0, chan, key, vel); + synth->storeid = synth->noteid++; + + return fluid_preset_noteon(channel->preset, synth, chan, key, vel); } -/* - * fluid_synth_noteoff +/** + * Send a note-off event to a FluidSynth object. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @return FLUID_OK on success, FLUID_FAILED otherwise (may just mean that no + * voices matched the note off event) */ int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) +{ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); + + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, NOTE_OFF, chan, key, 0); + else return fluid_synth_noteoff_LOCAL (synth, chan, key); +} + +/* Local synthesis thread variant of fluid_synth_noteoff */ +static int +fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key) { int i; fluid_voice_t* voice; int status = FLUID_FAILED; -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; @@ -886,6 +1269,7 @@ fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) (float) voice->ticks / 44100.0f, used_voices); } /* if verbose */ + fluid_voice_noteoff(voice); status = FLUID_OK; } /* if voice on */ @@ -893,144 +1277,278 @@ fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) return status; } -/* - * fluid_synth_damp_voices - */ -int -fluid_synth_damp_voices(fluid_synth_t* synth, int chan) +/* Damp all voices on a channel (turn notes off) */ +static int +fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan) { - int i; fluid_voice_t* voice; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ + int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; - if ((voice->chan == chan) && _SUSTAINED(voice)) { -/* printf("turned off sustained note: chan=%d, key=%d, vel=%d\n", voice->chan, voice->key, voice->vel); */ + + if ((voice->chan == chan) && _SUSTAINED(voice)) fluid_voice_noteoff(voice); - } } return FLUID_OK; } -/* - * fluid_synth_cc +/** + * Send a MIDI controller event on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param num MIDI controller number (0-127) + * @param val MIDI controller value (0-127) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) { -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (num >= 0 && num <= 127, FLUID_FAILED); + fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - if ((num < 0) || (num >= 128)) { - FLUID_LOG(FLUID_WARN, "Ctrl out of range"); - return FLUID_FAILED; - } - if ((val < 0) || (val >= 128)) { - FLUID_LOG(FLUID_WARN, "Value out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); - } - - /* set the controller value in the channel */ - fluid_channel_cc(synth->channel[chan], num, val); + /* Process bank MSB/LSB events immediately to prevent out of order issues with program change */ + if (fluid_synth_should_queue (synth) && num != BANK_SELECT_MSB && num != BANK_SELECT_LSB) + return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, chan, num, val); + else return fluid_synth_cc_LOCAL (synth, chan, num, val); return FLUID_OK; } -/* - * fluid_synth_cc +/* Local synthesis thread variant of MIDI CC set function. + * NOTE: Gets called out of synthesis context for BANK_SELECT_MSB and + * BANK_SELECT_LSB events, since they should be processed immediately. */ +static int +fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num, int value) +{ + fluid_channel_t* chan = synth->channel[channum]; + int banknum, nrpn_select; + + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", channum, num, value); + + fluid_channel_set_cc (chan, num, value); + + switch (num) { + case SUSTAIN_SWITCH: + if (value < 64) fluid_synth_damp_voices_LOCAL (synth, channum); + break; + case BANK_SELECT_MSB: + fluid_channel_set_bank_msb (chan, value & 0x7F); + break; + case BANK_SELECT_LSB: + fluid_channel_set_bank_lsb (chan, value & 0x7F); + break; + case ALL_NOTES_OFF: + fluid_synth_all_notes_off_LOCAL (synth, channum); + break; + case ALL_SOUND_OFF: + fluid_synth_all_sounds_off_LOCAL (synth, channum); + break; + case ALL_CTRL_OFF: + fluid_channel_init_ctrl (chan, 1); + fluid_synth_modulate_voices_all_LOCAL (synth, channum); + break; + case DATA_ENTRY_MSB: + { + int data = (value << 7) + fluid_channel_get_cc (chan, DATA_ENTRY_LSB); + + if (fluid_atomic_int_get (&chan->nrpn_active)) /* NRPN is active? */ + { /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if ((fluid_channel_get_cc (chan, NRPN_MSB) == 120) + && (fluid_channel_get_cc (chan, NRPN_LSB) < 100)) + { + nrpn_select = fluid_atomic_int_get (&chan->nrpn_select); + + if (nrpn_select < GEN_LAST) + { + float val = fluid_gen_scale_nrpn (nrpn_select, data); + fluid_synth_set_gen_LOCAL (synth, channum, nrpn_select, val, FALSE); + } + + fluid_atomic_int_set (&chan->nrpn_select, 0); /* Reset to 0 */ + } + } + else if (chan->cc[RPN_MSB] == 0) /* RPN is active: MSB = 0? */ + { + switch (chan->cc[RPN_LSB]) + { + case RPN_PITCH_BEND_RANGE: + fluid_synth_pitch_wheel_sens_LOCAL (synth, channum, value); /* Set bend range in semitones */ + /* FIXME - Handle LSB? (Fine bend range in cents) */ + break; + case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over 1 semitone (+/- 50 cents, 8192 = center) */ + fluid_synth_set_gen_LOCAL (synth, channum, GEN_FINETUNE, + (data - 8192) / 8192.0 * 50.0, FALSE); + break; + case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ + fluid_synth_set_gen_LOCAL (synth, channum, GEN_COARSETUNE, + value - 64, FALSE); + break; + case RPN_TUNING_PROGRAM_CHANGE: + break; + case RPN_TUNING_BANK_SELECT: + break; + case RPN_MODULATION_DEPTH_RANGE: + break; + } + } + break; + } + case NRPN_MSB: + fluid_channel_set_cc (chan, NRPN_LSB, 0); + fluid_atomic_int_set (&chan->nrpn_select, 0); + fluid_atomic_int_set (&chan->nrpn_active, 1); + break; + case NRPN_LSB: + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if (fluid_channel_get_cc (chan, NRPN_MSB) == 120) { + if (value == 100) { + fluid_atomic_int_add (&chan->nrpn_select, 100); + } else if (value == 101) { + fluid_atomic_int_add (&chan->nrpn_select, 1000); + } else if (value == 102) { + fluid_atomic_int_add (&chan->nrpn_select, 10000); + } else if (value < 100) { + fluid_atomic_int_add (&chan->nrpn_select, value); + } + } + + fluid_atomic_int_set (&chan->nrpn_active, 1); + break; + case RPN_MSB: + case RPN_LSB: + fluid_atomic_int_set (&chan->nrpn_active, 0); + break; + default: + return fluid_synth_modulate_voices_LOCAL (synth, channum, 1, num); + } + + return FLUID_OK; +} + +/** + * Get current MIDI controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param num MIDI controller number (0-127) + * @param pval Location to store MIDI controller value (0-127) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) { - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - if ((num < 0) || (num >= 128)) { - FLUID_LOG(FLUID_WARN, "Ctrl out of range"); - return FLUID_FAILED; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (num >= 0 && num < 128, FLUID_FAILED); + fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); - *pval = synth->channel[chan]->cc[num]; + *pval = fluid_channel_get_cc (synth->channel[chan], num); return FLUID_OK; } -/* - * fluid_synth_all_notes_off - * - * put all notes on this channel into released state. +/** + * Turn off all notes on a MIDI channel (put them into release phase). + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan) { - int i; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, chan, + ALL_NOTES_OFF, 0); + else return fluid_synth_all_notes_off_LOCAL (synth, chan); +} + +/* Local synthesis thread variant of all notes off */ +static int +fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan) +{ fluid_voice_t* voice; + int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; - if (_PLAYING(voice) && (voice->chan == chan)) { + + if (_PLAYING(voice) && (voice->chan == chan)) fluid_voice_noteoff(voice); - } } return FLUID_OK; } -/* - * fluid_synth_all_sounds_off - * - * immediately stop all notes on this channel. +/** + * Immediately stop all notes on a MIDI channel (skips release phase). + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan) { - int i; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, CONTROL_CHANGE, chan, + ALL_SOUND_OFF, 0); + else return fluid_synth_all_sounds_off_LOCAL (synth, chan); +} + +/* Local synthesis thread variant of all sounds off */ +static int +fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan) +{ fluid_voice_t* voice; + int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; - if (_PLAYING(voice) && (voice->chan == chan)) { + + if (_PLAYING(voice) && (voice->chan == chan)) fluid_voice_off(voice); - } } return FLUID_OK; } -/* - * fluid_synth_system_reset - * - * Purpose: - * Respond to the MIDI command 'system reset' (0xFF, big red 'panic' button) +/** + * Send MIDI system reset command (big red 'panic' button), turns off notes and + * resets controllers. + * @param synth FluidSynth instance + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_system_reset(fluid_synth_t* synth) { - int i; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, MIDI_SYSTEM_RESET, 0, 0, 0); + else return fluid_synth_system_reset_LOCAL (synth); +} + +/* Local variant of the system reset command */ +static int +fluid_synth_system_reset_LOCAL(fluid_synth_t* synth) +{ fluid_voice_t* voice; + int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; - if (_PLAYING(voice)) { + + if (_PLAYING(voice)) fluid_voice_off(voice); - } } - for (i = 0; i < synth->midi_channels; i++) { + for (i = 0; i < synth->midi_channels; i++) fluid_channel_reset(synth->channel[i]); - } fluid_chorus_reset(synth->chorus); fluid_revmodel_reset(synth->reverb); @@ -1038,51 +1556,48 @@ fluid_synth_system_reset(fluid_synth_t* synth) return FLUID_OK; } -/* - * fluid_synth_modulate_voices - * - * tell all synthesis processes on this channel to update their - * synthesis parameters after a control change. +/** + * Update voices on a MIDI channel after a MIDI control change. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param is_cc Boolean value indicating if ctrl is a CC controller or not + * @param ctrl MIDI controller value + * @return FLUID_OK on success, FLUID_FAILED otherwise */ -int -fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl) +static int +fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl) { - int i; fluid_voice_t* voice; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ + int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; - if (voice->chan == chan) { + + if (voice->chan == chan) fluid_voice_modulate(voice, is_cc, ctrl); - } } return FLUID_OK; } -/* - * fluid_synth_modulate_voices_all - * - * Tell all synthesis processes on this channel to update their - * synthesis parameters after an all control off message (i.e. all - * controller have been reset to their default value). +/** + * Update voices on a MIDI channel after all MIDI controllers have been changed. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param is_cc Boolean value indicating if ctrl is a CC controller or not + * @param ctrl MIDI controller value + * @return FLUID_OK on success, FLUID_FAILED otherwise */ -int -fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan) +static int +fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan) { - int i; fluid_voice_t* voice; - -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ + int i; for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; - if (voice->chan == chan) { + + if (voice->chan == chan) fluid_voice_modulate_all(voice); - } } return FLUID_OK; } @@ -1090,230 +1605,301 @@ fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan) /** * Set the MIDI channel pressure controller value. * @param synth FluidSynth instance - * @param chan MIDI channel number - * @param val MIDI channel pressure value (7 bit, 0-127) - * @return FLUID_OK on success - * - * Assign to the MIDI channel pressure controller value on a specific MIDI channel - * in real time. + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val MIDI channel pressure value (0-127) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, CHANNEL_PRESSURE, chan, val, 0); + else return fluid_synth_channel_pressure_LOCAL (synth, chan, val); +} - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { +/* Local synthesis thread variant of channel pressure set */ +static int +fluid_synth_channel_pressure_LOCAL(fluid_synth_t* synth, int chan, int val) +{ + if (synth->verbose) FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); - } - /* set the channel pressure value in the channel */ - fluid_channel_pressure(synth->channel[chan], val); - - return FLUID_OK; + fluid_atomic_int_set (&synth->channel[chan]->channel_pressure, val); + return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_CHANNELPRESSURE); } /** - * Set the MIDI pitch bend controller value. + * Set the MIDI pitch bend controller value on a MIDI channel. * @param synth FluidSynth instance - * @param chan MIDI channel number - * @param val MIDI pitch bend value (14 bit, 0-16383 with 8192 being center) - * @return FLUID_OK on success - * - * Assign to the MIDI pitch bend controller value on a specific MIDI channel - * in real time. + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val MIDI pitch bend value (0-16383 with 8192 being center) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (val >= 0 && val <= 16383, FLUID_FAILED); -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); - } - - /* set the pitch-bend value in the channel */ - fluid_channel_pitch_bend(synth->channel[chan], val); - - return FLUID_OK; + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, PITCH_BEND, chan, val, 0); + else return fluid_synth_pitch_bend_LOCAL (synth, chan, val); } -/* - * fluid_synth_pitch_bend +/* Local synthesis thread variant of pitch bend */ +static int +fluid_synth_pitch_bend_LOCAL(fluid_synth_t* synth, int chan, int val) +{ + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + + fluid_atomic_int_set (&synth->channel[chan]->pitch_bend, val); + return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEEL); +} + +/** + * Get the MIDI pitch bend controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with + * 8192 being center) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) { - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (ppitch_bend != NULL, FLUID_FAILED); - *ppitch_bend = synth->channel[chan]->pitch_bend; + *ppitch_bend = fluid_atomic_int_get (&synth->channel[chan]->pitch_bend); return FLUID_OK; } -/* - * Fluid_synth_pitch_wheel_sens +/** + * Set MIDI pitch wheel sensitivity on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val Pitch wheel sensitivity value in semitones + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ - /* check the ranges of the arguments */ - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if (synth->verbose) { - FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); - } - - /* set the pitch-bend value in the channel */ - fluid_channel_pitch_wheel_sens(synth->channel[chan], val); - - return FLUID_OK; + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_midi_event (synth, RPN_LSB, chan, + RPN_PITCH_BEND_RANGE, val); + else return fluid_synth_pitch_wheel_sens_LOCAL (synth, chan, val); } -/* - * fluid_synth_get_pitch_wheel_sens - * - * Note : this function was added after version 1.0 API freeze. - * So its API is not in the synth.h file. It should be added in some later - * version of fluidsynth. Maybe v2.0 ? -- Antoine Schmitt May 2003 - */ +/* Local synthesis thread variant of set pitch wheel sensitivity */ +static int +fluid_synth_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan, int val) +{ + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); + fluid_atomic_int_set (&synth->channel[chan]->pitch_wheel_sensitivity, val); + return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEELSENS); +} + +/** + * Get MIDI pitch wheel sensitivity on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param pval Location to store pitch wheel sensitivity value in semitones + * @return FLUID_OK on success, FLUID_FAILED otherwise + * @since: ?? Seems it was added sometime AFTER v1.0 API freeze. + */ int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); - // check the ranges of the arguments - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - // get the pitch-bend value in the channel - *pval = synth->channel[chan]->pitch_wheel_sensitivity; - + *pval = fluid_atomic_int_get (&synth->channel[chan]->pitch_wheel_sensitivity); return FLUID_OK; } -/* - * fluid_synth_get_preset +/** + * Assign a preset to a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param preset Preset to assign to channel or NULL to clear (ownership is taken over) + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +static int +fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset) +{ + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; + + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + + if (fluid_synth_should_queue (synth)) + { + queue = fluid_synth_get_event_queue (synth); + if (!queue) return FLUID_FAILED; + + event = fluid_event_queue_get_inptr (queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_PRESET; + event->preset.channel = chan; + event->preset.preset = preset; + + fluid_event_queue_next_inptr (queue); + return FLUID_OK; + } + else return fluid_synth_set_preset_LOCAL (synth, chan, preset); +} + +/* Local synthesis thread variant of set channel preset */ +static int +fluid_synth_set_preset_LOCAL (fluid_synth_t *synth, int chan, + fluid_preset_t *preset) +{ + fluid_channel_t *channel; + + channel = synth->channel[chan]; + fluid_channel_set_preset (channel, preset); + return FLUID_OK; +} + +/* Get a preset by SoundFont, bank and program numbers. + * Returns preset pointer or NULL. + * + * NOTE: The returned preset has been allocated, caller owns it and should + * free it when finished using it. */ static fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum, - unsigned int banknum, unsigned int prognum) + unsigned int banknum, unsigned int prognum) { - fluid_preset_t* preset = NULL; - fluid_sfont_t* sfont = NULL; - fluid_list_t* list = synth->sfont; - int offset; + fluid_preset_t *preset = NULL; + fluid_sfont_info_t *sfont_info; + fluid_list_t *list; - sfont = fluid_synth_get_sfont_by_id(synth, sfontnum); + fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ - if (sfont != NULL) { - offset = fluid_synth_get_bank_offset(synth, sfontnum); - preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); - if (preset != NULL) { - return preset; + for (list = synth->sfont_info; list; list = fluid_list_next (list)) { + sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + + if (fluid_sfont_get_id (sfont_info->sfont) == sfontnum) + { + preset = fluid_sfont_get_preset (sfont_info->sfont, + banknum - sfont_info->bankofs, prognum); + if (preset) sfont_info->refcount++; /* Add reference to SoundFont */ + break; } } - return NULL; + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + return preset; } -/* - * fluid_synth_get_preset2 +/* Get a preset by SoundFont name, bank and program. + * Returns preset pointer or NULL. + * + * NOTE: The returned preset has been allocated, caller owns it and should + * free it when finished using it. */ static fluid_preset_t* -fluid_synth_get_preset2(fluid_synth_t* synth, char* sfont_name, - unsigned int banknum, unsigned int prognum) +fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname, + unsigned int banknum, unsigned int prognum) { - fluid_preset_t* preset = NULL; - fluid_sfont_t* sfont = NULL; - int offset; + fluid_preset_t *preset = NULL; + fluid_sfont_info_t *sfont_info; + fluid_list_t *list; - sfont = fluid_synth_get_sfont_by_name(synth, sfont_name); + fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ - if (sfont != NULL) { - offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); - preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); - if (preset != NULL) { - return preset; + for (list = synth->sfont_info; list; list = fluid_list_next (list)) { + sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + + if (FLUID_STRCMP (fluid_sfont_get_name (sfont_info->sfont), sfontname) == 0) + { + preset = fluid_sfont_get_preset (sfont_info->sfont, + banknum - sfont_info->bankofs, prognum); + if (preset) sfont_info->refcount++; /* Add reference to SoundFont */ + break; } } - return NULL; + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + return preset; } -fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, - unsigned int banknum, - unsigned int prognum) +/* Find a preset by bank and program numbers. + * Returns preset pointer or NULL. + * + * NOTE: The returned preset has been allocated, caller owns it and should + * free it when finished using it. */ +fluid_preset_t* +fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, + unsigned int prognum) { - fluid_preset_t* preset = NULL; - fluid_sfont_t* sfont = NULL; - fluid_list_t* list = synth->sfont; - int offset; + fluid_preset_t *preset = NULL; + fluid_sfont_info_t *sfont_info; + fluid_list_t *list; + int ofs; - while (list) { + fluid_mutex_lock (synth->mutex); /* ++ lock sfont list, bank offset list and sfont */ - sfont = (fluid_sfont_t*) fluid_list_get(list); - offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); - preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum); + for (list = synth->sfont_info; list; list = fluid_list_next (list)) { + sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); - if (preset != NULL) { - preset->sfont = sfont; /* FIXME */ - return preset; + preset = fluid_sfont_get_preset (sfont_info->sfont, + banknum - sfont_info->bankofs, prognum); + if (preset) + { + sfont_info->refcount++; /* Add reference to SoundFont */ + break; } - - list = fluid_list_next(list); - } - return NULL; + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + return preset; } - -/* - * fluid_synth_program_change +/** + * Send a program change event on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param prognum MIDI program number (0-127) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) { fluid_preset_t* preset = NULL; fluid_channel_t* channel; - unsigned int banknum; - unsigned int sfont_id; - int subst_bank, subst_prog; + int subst_bank, subst_prog, banknum; - if ((prognum < 0) || (prognum >= FLUID_NUM_PROGRAMS) || - (chan < 0) || (chan >= synth->midi_channels)) - { - FLUID_LOG(FLUID_ERR, "Index out of range (chan=%d, prog=%d)", chan, prognum); - return FLUID_FAILED; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (prognum >= 0 && prognum <= FLUID_NUM_PROGRAMS, FLUID_FAILED); channel = synth->channel[chan]; - banknum = fluid_channel_get_banknum(channel); - - /* inform the channel of the new program number */ - fluid_channel_set_prognum(channel, prognum); + fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); if (synth->verbose) FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); @@ -1323,7 +1909,7 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) * * FIXME - Shouldn't hard code bank selection for channel 10. I think this * is a hack for MIDI files that do bank changes in GM mode. Proper way to - * handle this would probably be to ignore bank changes when in GM mode. + * handle this would probably be to ignore bank changes when in GM mode. - JG */ if (channel->channum == 9) preset = fluid_synth_find_preset(synth, DRUM_INST_BANK, prognum); @@ -1361,120 +1947,136 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) chan, banknum, prognum, subst_bank, subst_prog); } - sfont_id = preset? fluid_sfont_get_id(preset->sfont) : 0; - fluid_channel_set_sfontnum(channel, sfont_id); - fluid_channel_set_preset(channel, preset); + /* Assign the SoundFont ID and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0, + -1, prognum); + return fluid_synth_set_preset (synth, chan, preset); +} +/** + * Set instrument bank number on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param bank MIDI bank number + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) +{ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1); return FLUID_OK; } -/* - * fluid_synth_bank_select - */ -int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) -{ - if ((chan >= 0) && (chan < synth->midi_channels)) { - fluid_channel_set_banknum(synth->channel[chan], bank); - return FLUID_OK; - } - return FLUID_FAILED; -} - - -/* - * fluid_synth_sfont_select - */ -int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) -{ - if ((chan >= 0) && (chan < synth->midi_channels)) { - fluid_channel_set_sfontnum(synth->channel[chan], sfont_id); - return FLUID_OK; - } - return FLUID_FAILED; -} - -/* - * fluid_synth_get_program +/** + * Set SoundFont ID on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id ID of a loaded SoundFont + * @return FLUID_OK on success, FLUID_FAILED otherwise */ int -fluid_synth_get_program(fluid_synth_t* synth, int chan, - unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num) +fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) { - fluid_channel_t* channel; - if ((chan >= 0) && (chan < synth->midi_channels)) { - channel = synth->channel[chan]; - *sfont_id = fluid_channel_get_sfontnum(channel); - *bank_num = fluid_channel_get_banknum(channel); - *preset_num = fluid_channel_get_prognum(channel); - return FLUID_OK; - } - return FLUID_FAILED; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + return FLUID_OK; } -/* - * fluid_synth_program_select +/** + * Get current SoundFont ID, bank number and program number for a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id Location to store SoundFont ID + * @param bank_num Location to store MIDI bank number + * @param preset_num Location to store MIDI program number + * @return FLUID_OK on success, FLUID_FAILED otherwise */ -int fluid_synth_program_select(fluid_synth_t* synth, - int chan, - unsigned int sfont_id, - unsigned int bank_num, - unsigned int preset_num) +int +fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, + unsigned int* bank_num, unsigned int* preset_num) +{ + fluid_channel_t* channel; + + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (sfont_id != NULL, FLUID_FAILED); + fluid_return_val_if_fail (bank_num != NULL, FLUID_FAILED); + fluid_return_val_if_fail (preset_num != NULL, FLUID_FAILED); + + channel = synth->channel[chan]; + fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num, + (int *)preset_num); + return FLUID_OK; +} + +/** + * Select an instrument on a MIDI channel by SoundFont ID, bank and program numbers. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id, + unsigned int bank_num, unsigned int preset_num) { fluid_preset_t* preset = NULL; fluid_channel_t* channel; - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan); - return FLUID_FAILED; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + channel = synth->channel[chan]; - preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + /* ++ Allocate preset */ + preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num); + if (preset == NULL) { FLUID_LOG(FLUID_ERR, - "There is no preset with bank number %d and preset number %d in SoundFont %d", - bank_num, preset_num, sfont_id); + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); return FLUID_FAILED; } - /* inform the channel of the new bank and program number */ - fluid_channel_set_sfontnum(channel, sfont_id); - fluid_channel_set_banknum(channel, bank_num); - fluid_channel_set_prognum(channel, preset_num); - - fluid_channel_set_preset(channel, preset); - - return FLUID_OK; + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num); + return fluid_synth_set_preset (synth, chan, preset); } -/* - * fluid_synth_program_select2 +/** + * Select an instrument on a MIDI channel by SoundFont name, bank and program numbers. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_name Name of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return FLUID_OK on success, FLUID_FAILED otherwise */ -int fluid_synth_program_select2(fluid_synth_t* synth, - int chan, - char* sfont_name, - unsigned int bank_num, - unsigned int preset_num) +int +fluid_synth_program_select2(fluid_synth_t* synth, int chan, char* sfont_name, + unsigned int bank_num, unsigned int preset_num) { fluid_preset_t* preset = NULL; fluid_channel_t* channel; fluid_sfont_t* sfont = NULL; int offset; - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan); - return FLUID_FAILED; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (sfont_name != NULL, FLUID_FAILED); + channel = synth->channel[chan]; - sfont = fluid_synth_get_sfont_by_name(synth, sfont_name); - if (sfont == NULL) { - FLUID_LOG(FLUID_ERR, "Could not find SoundFont %s", sfont_name); - return FLUID_FAILED; - } - - offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont)); - preset = fluid_sfont_get_preset(sfont, bank_num - offset, preset_num); + /* ++ Allocate preset */ + preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num, + preset_num); if (preset == NULL) { FLUID_LOG(FLUID_ERR, "There is no preset with bank number %d and preset number %d in SoundFont %s", @@ -1482,41 +2084,34 @@ int fluid_synth_program_select2(fluid_synth_t* synth, return FLUID_FAILED; } - /* inform the channel of the new bank and program number */ - fluid_channel_set_sfontnum(channel, fluid_sfont_get_id(sfont)); - fluid_channel_set_banknum(channel, bank_num); - fluid_channel_set_prognum(channel, preset_num); - - fluid_channel_set_preset(channel, preset); - - return FLUID_OK; + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont), + bank_num, preset_num); + return fluid_synth_set_preset (synth, chan, preset); } /* - * This function assures that every MIDI channels has a valid preset + * This function assures that every MIDI channel has a valid preset * (NULL is okay). This function is called after a SoundFont is * unloaded or reloaded. */ static void fluid_synth_update_presets(fluid_synth_t* synth) { + fluid_channel_t *channel; + fluid_preset_t *preset; + int sfont, bank, prog; int chan; - fluid_channel_t* channel; for (chan = 0; chan < synth->midi_channels; chan++) { channel = synth->channel[chan]; - fluid_channel_set_preset(channel, - fluid_synth_get_preset(synth, - fluid_channel_get_sfontnum(channel), - fluid_channel_get_banknum(channel), - fluid_channel_get_prognum(channel))); + fluid_channel_get_sfont_bank_prog (channel, &sfont, &bank, &prog); + preset = fluid_synth_get_preset (synth, sfont, bank, prog); + fluid_synth_set_preset (synth, chan, preset); } } - -/* - * fluid_synth_update_gain - */ +/* Handler for synth.gain setting. */ static int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value) { @@ -1524,15 +2119,31 @@ fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value) return 0; } -/* - * fluid_synth_set_gain +/** + * Set synth output gain value. + * @param synth FluidSynth instance + * @param gain Gain value (function clamps value to the range 0.0 to 10.0) */ -void fluid_synth_set_gain(fluid_synth_t* synth, float gain) +void +fluid_synth_set_gain(fluid_synth_t* synth, float gain) { + fluid_return_if_fail (synth != NULL); + + fluid_clip (gain, 0.0f, 10.0f); + + if (fluid_synth_should_queue (synth)) + fluid_synth_queue_float_event (synth, FLUID_EVENT_QUEUE_ELEM_GAIN, gain); + else fluid_synth_set_gain_LOCAL (synth, gain); +} + +/* Called by synthesis thread to update the gain in all voices */ +static void +fluid_synth_set_gain_LOCAL(fluid_synth_t* synth, float gain) +{ + fluid_event_queue_elem_t *event; int i; - fluid_clip(gain, 0.0f, 10.0f); - synth->gain = gain; + synth->st_gain = gain; /* Set Synth Thread gain value */ for (i = 0; i < synth->polyphony; i++) { fluid_voice_t* voice = synth->voice[i]; @@ -1540,18 +2151,41 @@ void fluid_synth_set_gain(fluid_synth_t* synth, float gain) fluid_voice_set_gain(voice, gain); } } + + /* Send a return gain event to sync synth's copy of gain */ + + event = fluid_event_queue_get_inptr (synth->return_queue); + + if (event) + { + event->type = FLUID_EVENT_QUEUE_ELEM_GAIN; + event->dval = gain; + fluid_event_queue_next_inptr (synth->return_queue); + } + else FLUID_LOG (FLUID_ERR, "Synth return event queue full"); } -/* - * fluid_synth_get_gain +/** + * Get synth output gain value. + * @param synth FluidSynth instance + * @return Synth gain value (0.0 to 10.0) */ -float fluid_synth_get_gain(fluid_synth_t* synth) +float +fluid_synth_get_gain(fluid_synth_t* synth) { - return synth->gain; + float gain; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); + gain = synth->gain; + fluid_mutex_unlock (synth->mutex); + + return (gain); } /* - * fluid_synth_update_polyphony + * Handler for synth.polyphony setting. */ static int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value) @@ -1560,16 +2194,32 @@ fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value) return 0; } -/* - * fluid_synth_set_polyphony +/** + * Set synthesizer polyphony (max number of voices). + * @param synth FluidSynth instance + * @param polyphony Polyphony to assign + * @return FLUID_OK on success, FLUID_FAILED otherwise + * @since 1.0.6 */ -int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony) +int +fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony) +{ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (polyphony >= 1 && polyphony <= synth->nvoice, FLUID_FAILED); + + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_int_event (synth, FLUID_EVENT_QUEUE_ELEM_POLYPHONY, + polyphony); + else return fluid_synth_set_polyphony_LOCAL (synth, polyphony); +} + +/* Called by synthesis thread to update the polyphony value */ +static int +fluid_synth_set_polyphony_LOCAL(fluid_synth_t* synth, int polyphony) { int i; - if (polyphony < 1 || polyphony > synth->nvoice) { - return FLUID_FAILED; - } + fluid_atomic_int_set (&synth->polyphony, polyphony); /* turn off any voices above the new limit */ for (i = polyphony; i < synth->nvoice; i++) { @@ -1579,151 +2229,71 @@ int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony) } } - synth->polyphony = polyphony; - return FLUID_OK; } -/* - * fluid_synth_get_polyphony +/** + * Get current synthesizer polyphony (max number of voices). + * @param synth FluidSynth instance + * @return Synth polyphony value. + * @since 1.0.6 */ -int fluid_synth_get_polyphony(fluid_synth_t* synth) +int +fluid_synth_get_polyphony(fluid_synth_t* synth) { - return synth->polyphony; + return fluid_atomic_int_get (&synth->polyphony); } -/* - * fluid_synth_get_internal_buffer_size +/** + * Get the internal synthesis buffer size value. + * @param synth FluidSynth instance + * @return Internal buffer size in audio frames. + * + * Audio is synthesized this number of frames at a time. Defaults to 64 frames. */ -int fluid_synth_get_internal_bufsize(fluid_synth_t* synth) +int +fluid_synth_get_internal_bufsize(fluid_synth_t* synth) { return FLUID_BUFSIZE; } /* - * fluid_synth_program_reset + * Resend a bank select and a program change for every channel. + * @param synth FluidSynth instance + * @return FLUID_OK on success, FLUID_FAILED otherwise * - * Resend a bank select and a program change for every channel. This - * function is called mainly after a SoundFont has been loaded, - * unloaded or reloaded. */ + * This function is called mainly after a SoundFont has been loaded, + * unloaded or reloaded. + */ int fluid_synth_program_reset(fluid_synth_t* synth) { - int i; + int i, prog; + /* try to set the correct presets */ for (i = 0; i < synth->midi_channels; i++){ - fluid_synth_program_change(synth, i, fluid_channel_get_prognum(synth->channel[i])); + fluid_channel_get_sfont_bank_prog (synth->channel[i], NULL, NULL, &prog); + fluid_synth_program_change(synth, i, prog); } return FLUID_OK; } -/* - * fluid_synth_set_reverb_preset - */ -int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num) -{ - int i = 0; - while (revmodel_preset[i].name != NULL) { - if (i == num) { - fluid_revmodel_setroomsize(synth->reverb, revmodel_preset[i].roomsize); - fluid_revmodel_setdamp(synth->reverb, revmodel_preset[i].damp); - fluid_revmodel_setwidth(synth->reverb, revmodel_preset[i].width); - fluid_revmodel_setlevel(synth->reverb, revmodel_preset[i].level); - return FLUID_OK; - } - i++; - } - return FLUID_FAILED; -} - -/* - * fluid_synth_set_reverb - */ -void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, - double width, double level) -{ -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - fluid_revmodel_setroomsize(synth->reverb, roomsize); - fluid_revmodel_setdamp(synth->reverb, damping); - fluid_revmodel_setwidth(synth->reverb, width); - fluid_revmodel_setlevel(synth->reverb, level); -} - -/* - * fluid_synth_set_chorus - */ -void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, - double speed, double depth_ms, int type) -{ -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - - fluid_chorus_set_nr(synth->chorus, nr); - fluid_chorus_set_level(synth->chorus, (fluid_real_t)level); - fluid_chorus_set_speed_Hz(synth->chorus, (fluid_real_t)speed); - fluid_chorus_set_depth_ms(synth->chorus, (fluid_real_t)depth_ms); - fluid_chorus_set_type(synth->chorus, type); - fluid_chorus_update(synth->chorus); -} - -/****************************************************** - -#define COMPRESS 1 -#define COMPRESS_X1 4.0 -#define COMPRESS_Y1 0.6 -#define COMPRESS_X2 10.0 -#define COMPRESS_Y2 1.0 - - len2 = 2 * len; - alpha1 = COMPRESS_Y1 / COMPRESS_X1; - alpha2 = (COMPRESS_Y2 - COMPRESS_Y1) / (COMPRESS_X2 - COMPRESS_X1); - if (COMPRESS_X1 == COMPRESS_Y1) { - for (j = 0; j < len2; j++) { - if (buf[j] > COMPRESS_X1) { - if (buf[j] > COMPRESS_X2) { - buf[j] = COMPRESS_Y2; - } else { - buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1); - } - } else if (buf[j] < -COMPRESS_X1) { - if (buf[j] < -COMPRESS_X2) { - buf[j] = -COMPRESS_Y2; - } else { - buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1); - } - } - } - } else { - for (j = 0; j < len2; j++) { - if ((buf[j] >= -COMPRESS_X1) && (buf[j] <= COMPRESS_X1)) { - buf[j] *= alpha1; - } else if (buf[j] > COMPRESS_X1) { - if (buf[j] > COMPRESS_X2) { - buf[j] = COMPRESS_Y2; - } else { - buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1); - } - } else { - if (buf[j] < -COMPRESS_X2) { - buf[j] = -COMPRESS_Y2; - } else { - buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1); - } - } - } - } - -***************************************************/ - -/* - * fluid_synth_nwrite_float +/** + * Synthesize a block of floating point audio to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param left Array of floats to store left channel of audio (len in size) + * @param right Array of floats to store right channel of audio (len in size) + * @param fx_left Not currently used + * @param fx_right Not currently used + * @return FLUID_OK on success, FLUID_FAIL otherwise + * + * NOTE: Should only be called from synthesis thread. */ int fluid_synth_nwrite_float(fluid_synth_t* synth, int len, - float** left, float** right, - float** fx_left, float** fx_right) + float** left, float** right, + float** fx_left, float** fx_right) { fluid_real_t** left_in = synth->left_buf; fluid_real_t** right_in = synth->right_buf; @@ -1731,9 +2301,8 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len, int i, num, available, count, bytes; /* make sure we're playing */ - if (synth->state != FLUID_SYNTH_PLAYING) { - return 0; - } + if (synth->state != FLUID_SYNTH_PLAYING) + return FLUID_OK; /* First, take what's still available in the buffer */ count = 0; @@ -1775,13 +2344,28 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len, /* printf("CPU: %.2f\n", synth->cpu_load); */ - return 0; + return FLUID_OK; } - -int fluid_synth_process(fluid_synth_t* synth, int len, - int nin, float** in, - int nout, float** out) +/** + * Synthesize floating point audio to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param nin Ignored + * @param in Ignored + * @param nout Count of arrays in 'out' + * @param out Array of arrays to store audio to + * @return FLUID_OK on success, FLUID_FAIL otherwise + * + * This function implements the default interface defined in fluidsynth/audio.h. + * NOTE: Should only be called from synthesis thread. + */ +/* + * FIXME: Currently if nout != 2 memory allocation will occur! + */ +int +fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in, + int nout, float** out) { if (nout==2) { return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1); @@ -1798,18 +2382,31 @@ int fluid_synth_process(fluid_synth_t* synth, int len, fluid_synth_nwrite_float(synth, len, left, right, NULL, NULL); FLUID_FREE(left); FLUID_FREE(right); - return 0; + return FLUID_OK; } } - -/* - * fluid_synth_write_float +/** + * Synthesize a block of floating point audio samples to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param lout Array of floats to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of floats to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * @return FLUID_OK on success, FLUID_FAIL otherwise + * + * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, + * lincr = 2, rincr = 2). + * + * NOTE: Should only be called from synthesis thread. */ int fluid_synth_write_float(fluid_synth_t* synth, int len, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr) + void* lout, int loff, int lincr, + void* rout, int roff, int rincr) { int i, j, k, l; float* left_out = (float*) lout; @@ -1819,9 +2416,8 @@ fluid_synth_write_float(fluid_synth_t* synth, int len, double time = fluid_utime(); /* make sure we're playing */ - if (synth->state != FLUID_SYNTH_PLAYING) { - return 0; - } + if (synth->state != FLUID_SYNTH_PLAYING) + return FLUID_OK; l = synth->cur; @@ -1844,7 +2440,7 @@ fluid_synth_write_float(fluid_synth_t* synth, int len, /* printf("CPU: %.2f\n", synth->cpu_load); */ - return 0; + return FLUID_OK; } #define DITHER_SIZE 48000 @@ -1852,6 +2448,7 @@ fluid_synth_write_float(fluid_synth_t* synth, int len, static float rand_table[DITHER_CHANNELS][DITHER_SIZE]; +/* Init dither table */ static void init_dither(void) { @@ -1879,13 +2476,29 @@ roundi (float x) return (int)(x-0.5f); } -/* - * fluid_synth_write_s16 +/** + * Synthesize a block of 16 bit audio samples to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param lout Array of 16 bit words to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of 16 bit words to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * @return FLUID_OK on success, FLUID_FAIL otherwise + * + * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, + * lincr = 2, rincr = 2). + * + * NOTE: Should only be called from synthesis thread. + * NOTE: Dithering is performed when converting from internal floating point to + * 16 bit audio. */ int fluid_synth_write_s16(fluid_synth_t* synth, int len, - void* lout, int loff, int lincr, - void* rout, int roff, int rincr) + void* lout, int loff, int lincr, + void* rout, int roff, int rincr) { int i, j, k, cur; signed short* left_out = (signed short*) lout; @@ -1949,13 +2562,22 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len, return 0; } -/* - * fluid_synth_dither_s16 - * Converts stereo floating point sample data to signed 16 bit data with - * dithering. 'dither_index' parameter is a caller supplied pointer to an - * integer which should be initialized to 0 before the first call and passed - * unmodified to additional calls which are part of the same synthesis output. - * Only used internally currently. +/** + * Converts stereo floating point sample data to signed 16 bit data with dithering. + * @param dither_index Pointer to an integer which should be initialized to 0 + * before the first call and passed unmodified to additional calls which are + * part of the same synthesis output. + * @param len Length in frames to convert + * @param lin Buffer of left audio samples to convert from + * @param rin Buffer of right audio samples to convert from + * @param lout Array of 16 bit words to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of 16 bit words to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * + * NOTE: Currently private to libfluidsynth. */ void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, @@ -1994,7 +2616,7 @@ fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, } /* - * fluid_synth_one_block + * Process a single block (FLUID_BUFSIZE) of audio. */ static int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) @@ -2008,7 +2630,9 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t); double prof_ref = fluid_profile_ref(); -/* fluid_mutex_lock(synth->busy); /\* Here comes the audio thread. Lock the synth. *\/ */ + /* Assign ID of synthesis thread, if not already set */ + if (synth->synth_thread_id == FLUID_THREAD_ID_NULL) + synth->synth_thread_id = fluid_thread_get_id (); fluid_check_fpe("??? Just starting up ???"); @@ -2016,6 +2640,14 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) fluid_check_fpe("fluid_sample_timer_process"); + /* Process queued events */ + for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++) + { + if (synth->queues[i]) + fluid_synth_process_event_queue_LOCAL (synth, synth->queues[i]); + else break; /* First NULL ends the array (values are never set to NULL) */ + } + /* clean the audio buffers */ for (i = 0; i < synth->nbuf; i++) { FLUID_MEMSET(synth->left_buf[i], 0, byte_size); @@ -2129,20 +2761,84 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) #endif fluid_check_fpe("??? Remainder of synth_one_block ???"); -/* fluid_mutex_unlock(synth->busy); /\* Allow other threads to touch the synth *\/ */ - return 0; } +/* Process events in an event queue */ +static FLUID_INLINE void +fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth, + fluid_event_queue_t *queue) +{ + fluid_event_queue_elem_t *event; -/* - * fluid_synth_free_voice_by_kill - * - * selects a voice for killing. the selection algorithm is a refinement - * of the algorithm previously in fluid_synth_alloc_voice. - */ + while ((event = fluid_event_queue_get_outptr (queue))) + { + if (event->type == FLUID_EVENT_QUEUE_ELEM_MIDI) + { + switch (event->midi.type) + { + case NOTE_ON: + fluid_synth_noteon_LOCAL (synth, event->midi.channel, + event->midi.param1, event->midi.param2); + break; + case NOTE_OFF: + fluid_synth_noteoff_LOCAL (synth, event->midi.channel, + event->midi.param1); + break; + case CONTROL_CHANGE: + fluid_synth_cc_LOCAL (synth, event->midi.channel, event->midi.param1, + event->midi.param2); + break; + case MIDI_SYSTEM_RESET: + fluid_synth_system_reset_LOCAL (synth); + break; + case CHANNEL_PRESSURE: + fluid_synth_channel_pressure_LOCAL (synth, event->midi.channel, + event->midi.param1); + break; + case PITCH_BEND: + fluid_synth_pitch_bend_LOCAL (synth, event->midi.channel, + event->midi.param1); + break; + case RPN_LSB: + switch (event->midi.param1) + { + case RPN_PITCH_BEND_RANGE: + fluid_synth_pitch_wheel_sens_LOCAL (synth, event->midi.channel, + event->midi.param2); + break; + } + break; + } + } + else if (event->type == FLUID_EVENT_QUEUE_ELEM_GAIN) + fluid_synth_set_gain_LOCAL (synth, event->dval); + else if (event->type == FLUID_EVENT_QUEUE_ELEM_POLYPHONY) + fluid_synth_set_polyphony_LOCAL (synth, event->ival); + else if (event->type == FLUID_EVENT_QUEUE_ELEM_GEN) + fluid_synth_set_gen_LOCAL (synth, event->gen.channel, event->gen.param, + event->gen.value, event->gen.absolute); + else if (event->type == FLUID_EVENT_QUEUE_ELEM_PRESET) + fluid_synth_set_preset_LOCAL (synth, event->preset.channel, + event->preset.preset); + else if (event->type == FLUID_EVENT_QUEUE_ELEM_STOP_VOICES) + fluid_synth_stop_LOCAL (synth, event->ival); + else if (event->type == FLUID_EVENT_QUEUE_ELEM_REVERB) + fluid_synth_set_reverb_LOCAL (synth, event->reverb.set, event->reverb.roomsize, + event->reverb.damping, event->reverb.width, + event->reverb.level); + else if (event->type == FLUID_EVENT_QUEUE_ELEM_CHORUS) + fluid_synth_set_chorus_LOCAL (synth, event->chorus.set, event->chorus.nr, + event->chorus.type, event->chorus.level, + event->chorus.speed, event->chorus.depth); + + fluid_event_queue_next_outptr (queue); + } +} + +/* Selects a voice for killing. */ static fluid_voice_t* -fluid_synth_free_voice_by_kill(fluid_synth_t* synth) +fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth) { int i; fluid_real_t best_prio = 999999.; @@ -2150,9 +2846,6 @@ fluid_synth_free_voice_by_kill(fluid_synth_t* synth) fluid_voice_t* voice; int best_voice_index=-1; -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; @@ -2221,8 +2914,21 @@ fluid_synth_free_voice_by_kill(fluid_synth_t* synth) return voice; } -/* - * fluid_synth_alloc_voice +/** + * Allocate a synthesis voice. + * @param synth FluidSynth instance + * @param sample Sample to assign to the voice + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number for the voice (0-127) + * @param vel MIDI velocity for the voice (0-127) + * @return Allocated synthesis voice or NULL on error + * + * This function is called by a SoundFont's preset in response to a noteon event. + * The returned voice comes with default modulators and generators. + * A single noteon event may create any number of voices, when the preset is layered. + * + * NOTE: Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. */ fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel) @@ -2231,8 +2937,10 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, fluid_voice_t* voice = NULL; fluid_channel_t* channel = NULL; -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ + fluid_return_val_if_fail (synth != NULL, NULL); + fluid_return_val_if_fail (sample != NULL, NULL); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, NULL); + fluid_return_val_if_fail (fluid_synth_is_synth_thread (synth), NULL); /* check if there's an available synthesis process */ for (i = 0; i < synth->polyphony; i++) { @@ -2244,7 +2952,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, /* No success yet? Then stop a running voice. */ if (voice == NULL) { - voice = fluid_synth_free_voice_by_kill(synth); + voice = fluid_synth_free_voice_by_kill_LOCAL(synth); } if (voice == NULL) { @@ -2293,114 +3001,114 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, return voice; } -/* - * fluid_synth_kill_by_exclusive_class +/* Kill all voices on a given channel, which have the same exclusive class + * generator as new_voice. */ static void -fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* new_voice) +fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth, + fluid_voice_t* new_voice) { - /** Kill all voices on a given channel, which belong into - excl_class. This function is called by a SoundFont's preset in - response to a noteon event. If one noteon event results in - several voice processes (stereo samples), ignore_ID must name - the voice ID of the first generated voice (so that it is not - stopped). The first voice uses ignore_ID=-1, which will - terminate all voices on a channel belonging into the exclusive - class excl_class. - */ - - int i; int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS); - - /* Check if the voice belongs to an exclusive class. In that case, - previous notes from the same class are released. */ + fluid_voice_t* existing_voice; + int i; /* Excl. class 0: No exclusive class */ - if (excl_class == 0) { - return; - } - - // FLUID_LOG(FLUID_INFO, "Voice belongs to exclusive class (class=%d, ignore_id=%d)", excl_class, ignore_ID); - - /* Kill all notes on the same channel with the same exclusive class */ + if (excl_class == 0) return; + /* Kill all notes on the same channel with the same exclusive class */ for (i = 0; i < synth->polyphony; i++) { - fluid_voice_t* existing_voice = synth->voice[i]; + existing_voice = synth->voice[i]; - /* Existing voice does not play? Leave it alone. */ - if (!_PLAYING(existing_voice)) { - continue; - } + /* If voice is playing, on the same channel, has same exclusive + * class and is not part of the same noteon event (voice group), then kill it */ - /* An exclusive class is valid for a whole channel (or preset). - * Is the voice on a different channel? Leave it alone. */ - if (existing_voice->chan != new_voice->chan) { - continue; - } + if (_PLAYING(existing_voice) + && existing_voice->chan == new_voice->chan + && (int)_GEN (existing_voice, GEN_EXCLUSIVECLASS) == excl_class + && fluid_voice_get_id (existing_voice) != fluid_voice_get_id(new_voice)) + fluid_voice_kill_excl(existing_voice); + } +} - /* Existing voice has a different (or no) exclusive class? Leave it alone. */ - if ((int)_GEN(existing_voice, GEN_EXCLUSIVECLASS) != excl_class) { - continue; - } - - /* Existing voice is a voice process belonging to this noteon - * event (for example: stereo sample)? Leave it alone. */ - if (fluid_voice_get_id(existing_voice) == fluid_voice_get_id(new_voice)) { - continue; - } - - // FLUID_LOG(FLUID_INFO, "Releasing previous voice of exclusive class (class=%d, id=%d)", - // (int)_GEN(existing_voice, GEN_EXCLUSIVECLASS), (int)fluid_voice_get_id(existing_voice)); - - fluid_voice_kill_excl(existing_voice); - }; -}; - -/* - * fluid_synth_start_voice +/** + * Activate a voice previously allocated with fluid_synth_alloc_voice(). + * @param synth FluidSynth instance + * @param voice Voice to activate + * + * This function is called by a SoundFont's preset in response to a noteon + * event. Exclusive classes are processed here. + * + * NOTE: Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. */ -void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) +void +fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) { -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ + fluid_return_if_fail (synth != NULL); + fluid_return_if_fail (voice != NULL); + fluid_return_if_fail (fluid_synth_is_synth_thread (synth)); /* Find the exclusive class of this voice. If set, kill all voices * that match the exclusive class and are younger than the first * voice process created by this noteon event. */ - fluid_synth_kill_by_exclusive_class(synth, voice); + fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice); - /* Start the new voice */ - - fluid_voice_start(voice); + fluid_voice_start(voice); /* Start the new voice */ } -/* - * fluid_synth_add_sfloader +/** + * Add a SoundFont loader interface. + * @param synth FluidSynth instance + * @param loader Loader API structure, used directly and should remain allocated + * as long as the synth instance is used. + * + * SoundFont loaders are used to add custom instrument loading to FluidSynth. + * The caller supplied functions for loading files, allocating presets, + * retrieving information on them and synthesizing note-on events. Using this + * method even non SoundFont instruments can be synthesized, although limited + * to the SoundFont synthesis model. + * + * NOTE: Should only be called before any SoundFont files are loaded. */ -void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) +void +fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader) { + gboolean sfont_already_loaded; + + fluid_return_if_fail (synth != NULL); + fluid_return_if_fail (loader != NULL); + sfont_already_loaded = synth->sfont_info != NULL; + fluid_return_if_fail (!sfont_already_loaded); + + fluid_mutex_lock (synth->mutex); synth->loaders = fluid_list_prepend(synth->loaders, loader); + fluid_mutex_unlock (synth->mutex); } - -/* - * fluid_synth_sfload +/** + * Load a SoundFont file (filename is interpreted by SoundFont loaders). + * The newly loaded SoundFont will be put on top of the SoundFont + * stack. Presets are searched starting from the SoundFont on the + * top of the stack, working the way down the stack until a preset is found. + * + * @param synth SoundFont instance + * @param filename File to load + * @param reset_presets TRUE to re-assign presets for all MIDI channels + * @return SoundFont ID on success, FLUID_FAILED on error */ int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets) { - fluid_sfont_t* sfont; - fluid_list_t* list; - fluid_sfloader_t* loader; + fluid_sfont_info_t *sfont_info; + fluid_sfont_t *sfont; + fluid_list_t *list; + fluid_sfloader_t *loader; + unsigned int sfont_id; -#if defined(MACOS9) - fluid_synth_sfunload_macos9(synth); -#endif + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (filename != NULL, FLUID_FAILED); - if (filename == NULL) { - FLUID_LOG(FLUID_ERR, "Invalid filename"); - return FLUID_FAILED; - } + /* MT NOTE: Loaders list should not change. */ for (list = synth->loaders; list; list = fluid_list_next(list)) { loader = (fluid_sfloader_t*) fluid_list_get(list); @@ -2408,134 +3116,190 @@ fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets sfont = fluid_sfloader_load(loader, filename); if (sfont != NULL) { + sfont_info = new_fluid_sfont_info (synth, sfont); - sfont->id = ++synth->sfont_id; - - /* insert the sfont as the first one on the list */ - synth->sfont = fluid_list_prepend(synth->sfont, sfont); - - /* reset the presets for all channels */ - if (reset_presets) { - fluid_synth_program_reset(synth); + if (!sfont_info) + { + delete_fluid_sfont (sfont_info->sfont); /* FIXME - Shouldn't fail right? - JG */ + return FLUID_FAILED; } - return (int) sfont->id; + fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_id and sfont list */ + sfont->id = sfont_id = ++synth->sfont_id; + synth->sfont_info = fluid_list_prepend(synth->sfont_info, sfont_info); /* prepend to list */ + fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + /* reset the presets for all channels if requested */ + if (reset_presets) fluid_synth_program_reset(synth); + + return (int)sfont_id; } } FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); - return -1; + return FLUID_FAILED; } -/* - * fluid_synth_sfunload_callback - */ -static int -fluid_synth_sfunload_callback(void* data, unsigned int msec) +/* Create a new SoundFont info structure, free with FLUID_FREE */ +static fluid_sfont_info_t * +new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont) { - fluid_sfont_t* sfont = (fluid_sfont_t*) data; - int r = delete_fluid_sfont(sfont); - if (r == 0) { - FLUID_LOG(FLUID_DBG,"Unloaded SoundFont"); + fluid_sfont_info_t *sfont_info; + + sfont_info = FLUID_NEW (fluid_sfont_info_t); + + if (!sfont_info) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; } - return r != 0; + + sfont_info->sfont = sfont; + sfont_info->synth = synth; + sfont_info->refcount = 1; /* Start with refcount of 1 for owning synth */ + sfont_info->bankofs = 0; + + return (sfont_info); } -/* - * fluid_synth_sfunload_macos9 - */ -static void -fluid_synth_sfunload_macos9(fluid_synth_t* synth) -{ -#if defined(MACOS9) - fluid_list_t *list, *next; - fluid_sfont_t* sfont; - - list = synth->unloading; - while (list) { - next = fluid_list_next(list); - sfont = (fluid_sfont_t*) fluid_list_get(list); - if (delete_fluid_sfont(sfont) == 0) { - synth->unloading = fluid_list_remove(synth->unloading, sfont); - } - list = next; - } -#endif -} - -/* - * fluid_synth_sfunload +/** + * Unload a SoundFont. + * @param synth SoundFont instance + * @param id ID of SoundFont to unload + * @param reset_presets TRUE to re-assign presets for all MIDI channels + * @return FLUID_OK on success, FLUID_FAILED on error */ int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets) { - fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, id); + fluid_sfont_info_t *sfont_info; + fluid_list_t *list; -#if defined(MACOS9) - fluid_synth_sfunload_macos9(synth); -#endif + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - if (!sfont) { + /* remove the SoundFont from the list */ + fluid_mutex_lock (synth->mutex); /* ++ Lock sfont list */ + + for (list = synth->sfont_info; list; list = fluid_list_next(list)) { + sfont_info = (fluid_sfont_info_t*) fluid_list_get(list); + + if (fluid_sfont_get_id (sfont_info->sfont) == id) + { + synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info); + break; + } + } + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + if (!list) { FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); return FLUID_FAILED; } - /* remove the SoundFont from the list */ - synth->sfont = fluid_list_remove(synth->sfont, sfont); + /* reset the presets for all channels (SoundFont will be freed when there are no more references) */ + if (reset_presets) fluid_synth_program_reset (synth); + else fluid_synth_update_presets (synth); - /* reset the presets for all channels */ - if (reset_presets) { - fluid_synth_program_reset(synth); - } else { - fluid_synth_update_presets(synth); - } - - if (delete_fluid_sfont(sfont) != 0) { -#if defined(MACOS9) - synth->unloading = fluid_list_prepend(synth->unloading, sfont); -#else - /* spin off a timer thread to unload the sfont later */ - new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, 1, 1); -#endif - } + /* -- Remove synth->sfont_info list's reference to SoundFont */ + fluid_synth_sfont_unref (synth, sfont_info->sfont); return FLUID_OK; } -/* fluid_synth_sfreload - * +/* Unref a SoundFont and destroy if no more references */ +static void +fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont) +{ + fluid_sfont_info_t *sfont_info; + int refcount = 0; + + fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_hash */ + + sfont_info = fluid_hashtable_lookup (synth->sfont_hash, sfont); + + if (sfont_info) + { + sfont_info->refcount--; /* -- Remove the sfont_info list's reference */ + refcount = sfont_info->refcount; + + if (refcount == 0) /* Remove SoundFont from hash if no more references */ + fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont); + } + + fluid_mutex_unlock (synth->mutex); /* -- Unlock sfont_hash */ + + fluid_return_if_fail (sfont_info != NULL); /* Shouldn't happen, programming error if so */ + + if (refcount == 0) /* No more references? - Attempt delete */ + { + if (delete_fluid_sfont (sfont_info->sfont) == 0) /* SoundFont loader can block SoundFont unload */ + { + FLUID_FREE (sfont_info); + FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); + } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ + else new_fluid_timer (100, fluid_synth_sfunload_callback, sfont_info, TRUE, TRUE, FALSE); + } +} + +/* Callback to continually attempt to unload a SoundFont, + * only if a SoundFont loader blocked the unload operation */ +static int +fluid_synth_sfunload_callback(void* data, unsigned int msec) +{ + fluid_sfont_info_t *sfont_info = (fluid_sfont_info_t *)data; + + if (delete_fluid_sfont (sfont_info->sfont) == 0) + { + FLUID_FREE (sfont_info); + FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); + return FALSE; + } + else return TRUE; +} + +/** + * Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack. + * @param synth SoundFont instance + * @param id ID of SoundFont to reload + * @return SoundFont ID on success, FLUID_FAILED on error */ -int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) +int +fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) { char filename[1024]; + fluid_sfont_info_t *sfont_info, *old_sfont_info; fluid_sfont_t* sfont; - int index = 0; - fluid_list_t *list; fluid_sfloader_t* loader; + fluid_list_t *list; + int index; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - sfont = fluid_synth_get_sfont_by_id(synth, id); - if (!sfont) { + fluid_mutex_lock (synth->mutex); /* ++ lock sfont list */ + + /* Search for SoundFont and get its index */ + for (list = synth->sfont_info, index = 0; list; list = fluid_list_next (list), index++) { + old_sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + if (fluid_sfont_get_id (old_sfont_info->sfont) == id) break; + } + + if (!list) { + fluid_mutex_unlock (synth->mutex); /* -- unlock */ FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); return FLUID_FAILED; } - /* find the index of the SoundFont */ - list = synth->sfont; - while (list) { - if (sfont == (fluid_sfont_t*) fluid_list_get(list)) { - break; - } - list = fluid_list_next(list); - index++; - } - /* keep a copy of the SoundFont's filename */ - FLUID_STRCPY(filename, fluid_sfont_get_name(sfont)); + FLUID_STRCPY (filename, fluid_sfont_get_name (old_sfont_info->sfont)); - if (fluid_synth_sfunload(synth, id, 0) != FLUID_OK) { + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + if (fluid_synth_sfunload (synth, id, FALSE) != FLUID_OK) return FLUID_FAILED; - } + + /* MT Note: SoundFont loader list will not change */ for (list = synth->loaders; list; list = fluid_list_next(list)) { loader = (fluid_sfloader_t*) fluid_list_get(list); @@ -2543,11 +3307,20 @@ int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) sfont = fluid_sfloader_load(loader, filename); if (sfont != NULL) { - sfont->id = id; - /* insert the sfont at the same index */ - synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); + sfont_info = new_fluid_sfont_info (synth, sfont); + + if (!sfont_info) + { + delete_fluid_sfont (sfont_info->sfont); /* FIXME - Shouldn't fail right? - JG */ + return FLUID_FAILED; + } + + fluid_mutex_lock (synth->mutex); /* ++ Lock sfont list */ + synth->sfont_info = fluid_list_insert_at(synth->sfont_info, index, sfont_info); /* insert the sfont at the same index */ + fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ + fluid_mutex_unlock (synth->mutex); /* -- unlock */ /* reset the presets for all channels */ fluid_synth_update_presets(synth); @@ -2557,202 +3330,650 @@ int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) } FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); - return -1; + return FLUID_FAILED; } - -/* - * fluid_synth_add_sfont +/** + * Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack. + * @param synth FluidSynth instance + * @param sfont SoundFont to add + * @return New assigned SoundFont ID or FLUID_FAILED on error */ -int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) +int +fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) { - sfont->id = ++synth->sfont_id; + fluid_sfont_info_t *sfont_info; + unsigned int sfont_id; - /* insert the sfont as the first one on the list */ - synth->sfont = fluid_list_prepend(synth->sfont, sfont); + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (sfont != NULL, FLUID_FAILED); - /* reset the presets for all channels */ - fluid_synth_program_reset(synth); + sfont_info = new_fluid_sfont_info (synth, sfont); + if (!sfont_info) return (FLUID_FAILED); - return sfont->id; + fluid_mutex_lock (synth->mutex); /* ++ lock sfont_id and sfont_info list */ + sfont->id = sfont_id = ++synth->sfont_id; + synth->sfont_info = fluid_list_prepend (synth->sfont_info, sfont_info); /* prepend to list */ + fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */ + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + /* reset the presets for all channels */ + fluid_synth_program_reset (synth); + + return sfont_id; } - -/* - * fluid_synth_remove_sfont - */ -void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) -{ - int sfont_id = fluid_sfont_get_id(sfont); - - synth->sfont = fluid_list_remove(synth->sfont, sfont); - - /* remove a possible bank offset */ - fluid_synth_remove_bank_offset(synth, sfont_id); - - /* reset the presets for all channels */ - fluid_synth_program_reset(synth); -} - - -/* fluid_synth_sfcount +/** + * Remove a SoundFont from the SoundFont stack without deleting it. + * @param synth FluidSynth instance + * @param sfont SoundFont to remove * - * Returns the number of loaded SoundFonts + * SoundFont is not freed and is left as the responsibility of the caller. + * + * NOTE: The SoundFont should only be freed after there are no presets + * referencing it. This can only be ensured by the SoundFont loader and + * therefore this function should not normally be used. + */ +void +fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont) +{ + fluid_sfont_info_t *sfont_info; + fluid_list_t *list; + + fluid_return_if_fail (synth != NULL); + fluid_return_if_fail (sfont != NULL); + + /* remove the SoundFont from the list */ + fluid_mutex_lock (synth->mutex); /* ++ Lock sfont_info list */ + + for (list = synth->sfont_info; list; list = fluid_list_next(list)) { + sfont_info = (fluid_sfont_info_t*) fluid_list_get(list); + + if (sfont_info->sfont == sfont) + { + synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info); + + /* Remove from SoundFont hash regardless of refcount (SoundFont delete is up to caller) */ + fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont); + break; + } + } + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + /* reset the presets for all channels */ + fluid_synth_program_reset (synth); +} + +/* + * Count number of loaded SoundFont files. + * @param synth FluidSynth instance + * @return Count of loaded SoundFont files. */ int fluid_synth_sfcount(fluid_synth_t* synth) { - return fluid_list_size(synth->sfont); + int count; + + fluid_return_val_if_fail (synth != NULL, 0); + + fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ + count = fluid_list_size (synth->sfont_info); + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + return count; } -/* fluid_synth_get_sfont +/** + * Get SoundFont by index. + * @param synth FluidSynth instance + * @param num SoundFont index on the stack (starting from 0 for top of stack). + * @return SoundFont instance or NULL if invalid index * - * Returns SoundFont num + * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. */ -fluid_sfont_t* +fluid_sfont_t * fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num) { - return (fluid_sfont_t*) fluid_list_get(fluid_list_nth(synth->sfont, num)); + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail (synth != NULL, NULL); + + fluid_mutex_lock (synth->mutex); /* ++ lock sfont list */ + list = fluid_list_nth (synth->sfont_info, num); + if (list) sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + return sfont; } -/* fluid_synth_get_sfont_by_id +/** + * Get SoundFont by ID. + * @param synth FluidSynth instance + * @param id SoundFont ID + * @return SoundFont instance or NULL if invalid ID * + * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. */ -fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) +fluid_sfont_t * +fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) { - fluid_list_t* list = synth->sfont; - fluid_sfont_t* sfont; + fluid_sfont_t* sfont = NULL; + fluid_list_t* list; - while (list) { - sfont = (fluid_sfont_t*) fluid_list_get(list); - if (fluid_sfont_get_id(sfont) == id) { - return sfont; - } - list = fluid_list_next(list); + fluid_return_val_if_fail (synth != NULL, NULL); + + fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ + + for (list = synth->sfont_info; list; list = fluid_list_next(list)) { + sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; + if (fluid_sfont_get_id (sfont) == id) + break; } - return NULL; + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + return list ? sfont : NULL; } -/* fluid_synth_get_sfont_by_name +/** + * Get SoundFont by name. + * @param synth FluidSynth instance + * @param name Name of SoundFont + * @return SoundFont instance or NULL if invalid name * + * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. */ -fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name) +fluid_sfont_t * +fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name) { - fluid_list_t* list = synth->sfont; - fluid_sfont_t* sfont; + fluid_sfont_t* sfont = NULL; + fluid_list_t* list; - while (list) { - sfont = (fluid_sfont_t*) fluid_list_get(list); - if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) { - return sfont; - } - list = fluid_list_next(list); + fluid_return_val_if_fail (synth != NULL, NULL); + fluid_return_val_if_fail (name != NULL, NULL); + + fluid_mutex_lock (synth->mutex); /* ++ lock */ + + for (list = synth->sfont_info; list; list = fluid_list_next(list)) { + sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont; + if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) + break; } - return NULL; + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + return list ? sfont : NULL; } -/* - * fluid_synth_get_channel_preset +/** + * Get active preset on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return Preset or NULL if no preset active on channel + * + * NOTE: Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon methods. */ -fluid_preset_t* +fluid_preset_t * fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan) { - if ((chan >= 0) && (chan < synth->midi_channels)) { - return fluid_channel_get_preset(synth->channel[chan]); + fluid_return_val_if_fail (synth != NULL, NULL); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, NULL); + + return fluid_channel_get_preset(synth->channel[chan]); +} + +/** + * Get list of voices. + * @param synth FluidSynth instance + * @param buf Array to store voices to (NULL terminated if not filled completely) + * @param bufsize Count of indexes in buf + * @param id Voice ID to search for or < 0 to return list of all playing voices + * + * NOTE: Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon methods. Voices are only guaranteed to remain + * unchanged until next synthesis process iteration. + */ +void +fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, + int id) +{ + int count = 0; + int i; + + fluid_return_if_fail (synth != NULL); + fluid_return_if_fail (buf != NULL); + + for (i = 0; i < synth->polyphony && count < bufsize; i++) { + fluid_voice_t* voice = synth->voice[i]; + + if (_PLAYING(voice) && (id < 0 || (int)voice->id == id)) + buf[count++] = voice; } - return NULL; + if (count < bufsize) buf[count] = NULL; +} + +/** + * Enable or disable reverb effect. + * @param synth FluidSynth instance + * @param on TRUE to enable reverb, FALSE to disable + */ +void +fluid_synth_set_reverb_on(fluid_synth_t* synth, int on) +{ + fluid_return_if_fail (synth != NULL); + + fluid_atomic_int_set (&synth->with_reverb, on != 0); +} + +/** + * Activate a reverb preset. + * @param synth FluidSynth instance + * @param num Reverb preset number + * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * NOTE: Currently private to libfluidsynth. + */ +int +fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num) +{ + int i = 0; + while (revmodel_preset[i].name != NULL) { + if (i == num) { + fluid_synth_set_reverb (synth, revmodel_preset[i].roomsize, + revmodel_preset[i].damp, revmodel_preset[i].width, + revmodel_preset[i].level); + return FLUID_OK; + } + i++; + } + return FLUID_FAILED; +} + +/** + * Set reverb parameters. + * @param synth FluidSynth instance + * @param roomsize Reverb room size value (0.0-1.2) + * @param damping Reverb damping value (0.0-1.0) + * @param width Reverb width value (0.0-100.0) + * @param level Reverb level value (0.0-1.0) + */ +void +fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, + double width, double level) +{ + fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ALL, + roomsize, damping, width, level); +} + +/** + * Set one or more reverb parameters. + * @param synth FluidSynth instance + * @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t) + * @param roomsize Reverb room size value (0.0-1.2) + * @param damping Reverb damping value (0.0-1.0) + * @param width Reverb width value (0.0-100.0) + * @param level Reverb level value (0.0-1.0) + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, + double damping, double width, double level) +{ + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; + + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + + if (!(set & FLUID_REVMODEL_SET_ALL)) + set = FLUID_REVMODEL_SET_ALL; + + if (fluid_synth_should_queue (synth)) + { + queue = fluid_synth_get_event_queue (synth); + if (!queue) return FLUID_FAILED; + + event = fluid_event_queue_get_inptr (queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_REVERB; + event->reverb.set = set; + event->reverb.roomsize = roomsize; + event->reverb.damping = damping; + event->reverb.width = width; + event->reverb.level = level; + + fluid_event_queue_next_inptr (queue); + return FLUID_OK; + } + else return fluid_synth_set_reverb_LOCAL (synth, set, roomsize, damping, width, level); +} + +/* Local synthesis thread reverb set function */ +static int +fluid_synth_set_reverb_LOCAL(fluid_synth_t* synth, int set, double roomsize, + double damping, double width, double level) +{ + fluid_event_queue_elem_t *event; + + fluid_revmodel_set (synth->reverb, set, roomsize, damping, width, level); + + /* Send return reverb event to sync synth's copy of reverb parameters */ + + event = fluid_event_queue_get_inptr (synth->return_queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synth return event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_REVERB; + event->reverb.set = set; + event->reverb.roomsize = roomsize; + event->reverb.damping = damping; + event->reverb.width = width; + event->reverb.level = level; + + fluid_event_queue_next_inptr (synth->return_queue); + + return FLUID_OK; +} + +/** + * Get reverb room size. + * @param synth FluidSynth instance + * @return Reverb room size (0.0-1.2) + */ +double +fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) +{ + double value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + value = synth->reverb_roomsize; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Get reverb damping. + * @param synth FluidSynth instance + * @return Reverb damping value (0.0-1.0) + */ +double +fluid_synth_get_reverb_damp(fluid_synth_t* synth) +{ + double value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + value = synth->reverb_damping; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Get reverb level. + * @param synth FluidSynth instance + * @return Reverb level value (0.0-1.0) + */ +double +fluid_synth_get_reverb_level(fluid_synth_t* synth) +{ + double value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + value = synth->reverb_level; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Get reverb width. + * @param synth FluidSynth instance + * @return Reverb width value (0.0-100.0) + */ +double +fluid_synth_get_reverb_width(fluid_synth_t* synth) +{ + double value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock reverb value */ + value = synth->reverb_width; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Enable or disable chorus effect. + * @param synth FluidSynth instance + * @param on TRUE to enable chorus, FALSE to disable + */ +void +fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) +{ + fluid_return_if_fail (synth != NULL); + + fluid_atomic_int_set (&synth->with_chorus, on != 0); +} + +/** + * Set chorus parameters. + * @param synth FluidSynth instance + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) + * @param level Chorus level (0.0-1.0) + * @param speed Chorus speed in Hz (0.29-5.0) + * @param depth_ms Chorus depth (max value depends on synth sample rate, + * 0.0-21.0 is safe for sample rate values up to 96KHz) + * @param type Chorus waveform type (#fluid_chorus_mod) + */ +void +fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, + double speed, double depth_ms, int type) +{ + fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_ALL, nr, level, speed, + depth_ms, type); +} + +/** + * Set one or more chorus parameters. + * @param synth FluidSynth instance + * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t) + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) + * @param level Chorus level (0.0-1.0) + * @param speed Chorus speed in Hz (0.29-5.0) + * @param depth_ms Chorus depth (max value depends on synth sample rate, + * 0.0-21.0 is safe for sample rate values up to 96KHz) + * @param type Chorus waveform type (#fluid_chorus_mod) + */ +int +fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, + double speed, double depth_ms, int type) +{ + fluid_event_queue_t *queue; + fluid_event_queue_elem_t *event; + + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + + if (!(set & FLUID_CHORUS_SET_ALL)) + set = FLUID_CHORUS_SET_ALL; + + if (fluid_synth_should_queue (synth)) + { + queue = fluid_synth_get_event_queue (synth); + if (!queue) return FLUID_FAILED; + + event = fluid_event_queue_get_inptr (queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synthesis event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_CHORUS; + event->chorus.set = set; + event->chorus.nr = nr; + event->chorus.type = type; + event->chorus.level = level; + event->chorus.speed = speed; + event->chorus.depth = depth_ms; + + fluid_event_queue_next_inptr (queue); + return FLUID_OK; + } + else return fluid_synth_set_chorus_LOCAL (synth, set, nr, level, speed, depth_ms, type); +} + +/* Local synthesis thread version of set chorus function */ +static int +fluid_synth_set_chorus_LOCAL(fluid_synth_t* synth, int set, int nr, float level, + float speed, float depth_ms, int type) +{ + fluid_event_queue_elem_t *event; + + fluid_chorus_set (synth->chorus, set, nr, level, speed, depth_ms, type); + + /* Send return chorus event to sync synth's copy of chorus parameters */ + + event = fluid_event_queue_get_inptr (synth->return_queue); + if (!event) + { + FLUID_LOG (FLUID_ERR, "Synth return event queue full"); + return FLUID_FAILED; + } + + event->type = FLUID_EVENT_QUEUE_ELEM_CHORUS; + event->chorus.set = set; + event->chorus.nr = nr; + event->chorus.type = type; + event->chorus.level = level; + event->chorus.speed = speed; + event->chorus.depth = depth_ms; + + fluid_event_queue_next_inptr (synth->return_queue); + + return FLUID_OK; +} + +/** + * Get chorus voice number (delay line count) value. + * @param synth FluidSynth instance + * @return Chorus voice count (0-99) + */ +int +fluid_synth_get_chorus_nr(fluid_synth_t* synth) +{ + int value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + value = synth->chorus_nr; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Get chorus level. + * @param synth FluidSynth instance + * @return Chorus level value (0.0-10.0) + */ +double +fluid_synth_get_chorus_level(fluid_synth_t* synth) +{ + double value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + value = synth->chorus_level; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Get chorus speed in Hz. + * @param synth FluidSynth instance + * @return Chorus speed in Hz (0.29-5.0) + */ +double +fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) +{ + double value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + value = synth->chorus_speed; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Get chorus depth. + * @param synth FluidSynth instance + * @return Chorus depth + */ +double +fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) +{ + double value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + value = synth->chorus_depth; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; +} + +/** + * Get chorus waveform type. + * @param synth FluidSynth instance + * @return Chorus waveform type (#fluid_chorus_mod) + */ +int +fluid_synth_get_chorus_type(fluid_synth_t* synth) +{ + int value; + + fluid_return_val_if_fail (synth != NULL, 0.0); + + fluid_mutex_lock (synth->mutex); /* ++ Lock chorus value */ + value = synth->chorus_type; + fluid_mutex_unlock (synth->mutex); /* -- Unlock */ + + return value; } /* - * fluid_synth_get_voicelist - */ -void -fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, int ID) -{ - int i; - int count = 0; - for (i = 0; i < synth->polyphony; i++) { - fluid_voice_t* voice = synth->voice[i]; - if (count >= bufsize) { - return; - } - - if (_PLAYING(voice) && ((int)voice->id == ID || ID < 0)) { - buf[count++] = voice; - } - } - if (count >= bufsize) { - return; - } - buf[count++] = NULL; -} - -/* Purpose: - * Turns on / off the reverb unit in the synth */ -void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on) -{ - synth->with_reverb = on; -} - -/* Purpose: - * Turns on / off the chorus unit in the synth */ -void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on) -{ - synth->with_chorus = on; -} - -/* Purpose: - * Reports the current setting of the chorus unit. */ -int fluid_synth_get_chorus_nr(fluid_synth_t* synth) -{ - return fluid_chorus_get_nr(synth->chorus); -} - -double fluid_synth_get_chorus_level(fluid_synth_t* synth) -{ - return (double)fluid_chorus_get_level(synth->chorus); -} - -double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth) -{ - return (double)fluid_chorus_get_speed_Hz(synth->chorus); -} - -double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth) -{ - return (double)fluid_chorus_get_depth_ms(synth->chorus); -} - -int fluid_synth_get_chorus_type(fluid_synth_t* synth) -{ - return fluid_chorus_get_type(synth->chorus); -} - -/* Purpose: - * Returns the current settings_old of the reverb unit */ -double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth) -{ - return (double)fluid_revmodel_getroomsize(synth->reverb); -} - -double fluid_synth_get_reverb_damp(fluid_synth_t* synth) -{ - return (double) fluid_revmodel_getdamp(synth->reverb); -} - -double fluid_synth_get_reverb_level(fluid_synth_t* synth) -{ - return (double) fluid_revmodel_getlevel(synth->reverb); -} - -double fluid_synth_get_reverb_width(fluid_synth_t* synth) -{ - return (double) fluid_revmodel_getwidth(synth->reverb); -} - -/* Purpose: - * * If the same note is hit twice on the same channel, then the older * voice process is advanced to the release stage. Using a mechanical * MIDI controller, the only way this can happen is when the sustain @@ -2762,14 +3983,12 @@ double fluid_synth_get_reverb_width(fluid_synth_t* synth) * release those... */ static void -fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key) +fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, + int key) { int i; fluid_voice_t* voice; -/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */ -/* fluid_mutex_unlock(synth->busy); */ - for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (_PLAYING(voice) @@ -2781,96 +4000,126 @@ fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key) } } -/* Purpose: - * Sets the interpolation method to use on channel chan. - * If chan is < 0, then set the interpolation method on all channels. +/** + * Set synthesis interpolation method on one or all MIDI channels. + * @param synth FluidSynth instance + * @param chan MIDI channel to set interpolation method on or -1 for all channels + * @param interp_method Interpolation method (#fluid_interp) + * @return FLUID_OK on success, FLUID_FAILED otherwise */ -int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method){ +int +fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method) +{ int i; + + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + + if (synth->channel[0] == NULL) { + FLUID_LOG (FLUID_ERR, "Channels don't exist (yet)!"); + return FLUID_FAILED; + } + for (i = 0; i < synth->midi_channels; i++) { - if (synth->channel[i] == NULL){ - FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!"); - return FLUID_FAILED; - }; - if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan){ + if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan) fluid_channel_set_interp_method(synth->channel[i], interp_method); - }; - }; + } + return FLUID_OK; }; -/* Purpose: - * Returns the number of allocated midi channels +/** + * Get the total count of MIDI channels. + * @param synth FluidSynth instance + * @return Count of MIDI channels */ int fluid_synth_count_midi_channels(fluid_synth_t* synth) { + fluid_return_val_if_fail (synth != NULL, 0); + return synth->midi_channels; } -/* Purpose: - * Returns the number of allocated audio channels +/** + * Get the total count of audio channels. + * @param synth FluidSynth instance + * @return Count of audio channels */ int fluid_synth_count_audio_channels(fluid_synth_t* synth) { + fluid_return_val_if_fail (synth != NULL, 0); + return synth->audio_channels; } -/* Purpose: - * Returns the number of allocated audio channels +/** + * Get the total number of allocated audio channels. Usually identical to the + * number of audio channels. + * + * @param synth FluidSynth instance + * @return Count of allocated audio channels */ int fluid_synth_count_audio_groups(fluid_synth_t* synth) { + fluid_return_val_if_fail (synth != NULL, 0); + return synth->audio_groups; } -/* Purpose: - * Returns the number of allocated effects channels +/** + * Get the total number of allocated effects channels. + * @param synth FluidSynth instance + * @return Count of allocated effects channels */ int fluid_synth_count_effects_channels(fluid_synth_t* synth) { + fluid_return_val_if_fail (synth != NULL, 0); + return synth->effects_channels; } -double fluid_synth_get_cpu_load(fluid_synth_t* synth) +/** + * Get the synth CPU load value. + * @param synth FluidSynth instance + * @return Estimated CPU load value in percent (0-100) + */ +double +fluid_synth_get_cpu_load(fluid_synth_t* synth) { + fluid_return_val_if_fail (synth != NULL, 0); + return synth->cpu_load; } -static fluid_tuning_t* +/* Get tuning for a given bank:program */ +static fluid_tuning_t * fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog) { - if ((bank < 0) || (bank >= 128)) { - FLUID_LOG(FLUID_WARN, "Bank number out of range"); - return NULL; - } - if ((prog < 0) || (prog >= 128)) { - FLUID_LOG(FLUID_WARN, "Program number out of range"); - return NULL; - } + fluid_return_val_if_fail (synth != NULL, NULL); + fluid_return_val_if_fail (bank >= 0 && bank < 128, NULL); + fluid_return_val_if_fail (prog >= 0 && prog < 128, NULL); + if ((synth->tuning == NULL) || (synth->tuning[bank] == NULL) || (synth->tuning[bank][prog] == NULL)) { FLUID_LOG(FLUID_WARN, "No tuning at bank %d, prog %d", bank, prog); return NULL; } + return synth->tuning[bank][prog]; } +/* Create tuning for a given bank:program */ static fluid_tuning_t* fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, char* name) { - if ((bank < 0) || (bank >= 128)) { - FLUID_LOG(FLUID_WARN, "Bank number out of range"); - return NULL; - } - if ((prog < 0) || (prog >= 128)) { - FLUID_LOG(FLUID_WARN, "Program number out of range"); - return NULL; - } + fluid_return_val_if_fail (synth != NULL, NULL); + fluid_return_val_if_fail (bank >= 0 && bank < 128, NULL); + fluid_return_val_if_fail (prog >= 0 && prog < 128, NULL); + if (synth->tuning == NULL) { synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128); if (synth->tuning == NULL) { @@ -2904,92 +4153,148 @@ fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, char* name) return synth->tuning[bank][prog]; } -int fluid_synth_create_key_tuning(fluid_synth_t* synth, - int bank, int prog, - char* name, double* pitch) +/** + * Set the tuning of the entire MIDI note scale. + * @param synth FluidSynth instance + * @param bank MIDI bank number for this tuning (0-16383) + * @param prog MIDI program number for this tuning (0-127) + * @param name Label name for this tuning + * @param pitch Array of pitch values (length of 128, each value is number of + * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). + * Pass NULL to create a well-tempered (normal) scale. + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog, + char* name, double* pitch) { fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name); - if (tuning == NULL) { - return FLUID_FAILED; - } - if (pitch) { - fluid_tuning_set_all(tuning, pitch); - } + + if (tuning == NULL) return FLUID_FAILED; + if (pitch) fluid_tuning_set_all(tuning, pitch); + return FLUID_OK; } - -int fluid_synth_create_octave_tuning(fluid_synth_t* synth, - int bank, int prog, - char* name, double* pitch) +/** + * Apply an octave tuning to every octave in the MIDI note scale. + * @param synth FluidSynth instance + * @param bank MIDI bank number for this tuning (0-16383) + * @param prog MIDI program number for this tuning (0-127) + * @param name Label name for this tuning + * @param pitch Array of pitch values (length of 12 for each note of an octave + * starting at note C, values are number of offset cents to add to the normal + * tuning amount) + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog, + char* name, double* pitch) { fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name); - if (tuning == NULL) { - return FLUID_FAILED; - } + + if (tuning == NULL) return FLUID_FAILED; fluid_tuning_set_octave(tuning, pitch); + return FLUID_OK; } -int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, - int len, int *key, double* pitch, int apply) +/** + * Set tuning values for one or more MIDI notes for an existing tuning. + * @param synth FluidSynth instance + * @param bank MIDI bank number for this tuning (0-16383) + * @param prog MIDI program number for this tuning (0-127) + * @param len Number of MIDI notes to assign + * @param key Array of MIDI key numbers (length of 'len', values 0-127) + * @param pitch Array of pitch values (length of 'len', values are number of + * cents from MIDI note 0) + * @param apply Not used currently, may be used in the future to apply tuning + * change in realtime. + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog, + int len, int *key, double* pitch, int apply) { fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); int i; - if (tuning == NULL) { - return FLUID_FAILED; - } + if (tuning == NULL) return FLUID_FAILED; - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) fluid_tuning_set_pitch(tuning, key[i], pitch[i]); - } return FLUID_OK; } -int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, - int bank, int prog) +/** + * Activate an existing tuning scale on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param bank MIDI bank number of tuning + * @param prog MIDI program number of tuning + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog) { fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); - if (tuning == NULL) { - return FLUID_FAILED; - } - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } + if (tuning == NULL) return FLUID_FAILED; + + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); fluid_channel_set_tuning(synth->channel[chan], synth->tuning[bank][prog]); return FLUID_OK; } -int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) +/** + * Clear tuning scale on a MIDI channel (set it to the default well-tempered scale). + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_reset_tuning(fluid_synth_t* synth, int chan) { - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); fluid_channel_set_tuning(synth->channel[chan], NULL); return FLUID_OK; } -void fluid_synth_tuning_iteration_start(fluid_synth_t* synth) +/** + * Start tuning iteration. + * @param synth FluidSynth instance + */ +void +fluid_synth_tuning_iteration_start(fluid_synth_t* synth) { + fluid_return_if_fail (synth != NULL); + synth->cur_tuning = NULL; } -int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) +/** + * Advance to next tuning. + * @param synth FluidSynth instance + * @param bank Location to store MIDI bank number of next tuning scale + * @param prog Location to store MIDI program number of next tuning scale + * @return 1 if tuning iteration advanced, 0 if no more tunings + */ +int +fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog) { int b = 0, p = 0; - if (synth->tuning == NULL) { - return 0; - } + fluid_return_val_if_fail (synth != NULL, 0); + fluid_return_val_if_fail (bank != NULL, 0); + fluid_return_val_if_fail (prog != NULL, 0); + + if (synth->tuning == NULL) return 0; if (synth->cur_tuning != NULL) { /* get the next program number */ @@ -3020,171 +4325,268 @@ int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog return 0; } -int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, - char* name, int len, double* pitch) +/** + * Get the entire note tuning for a given MIDI bank and program. + * @param synth FluidSynth instance + * @param bank MIDI bank number of tuning + * @param prog MIDI program number of tuning + * @param name Location to store tuning name or NULL to ignore + * @param len Maximum number of chars to store to 'name' (including NULL byte) + * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog, + char* name, int len, double* pitch) { fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog); - if (tuning == NULL) { - return FLUID_FAILED; - } + if (tuning == NULL) return FLUID_FAILED; if (name) { snprintf(name, len - 1, "%s", fluid_tuning_get_name(tuning)); name[len - 1] = 0; /* make sure the string is null terminated */ } - if (pitch) { + + if (pitch) FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double)); - } return FLUID_OK; } -fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth) +/** + * Get settings assigned to a synth. + * @param synth FluidSynth instance + * @return FluidSynth settings which are assigned to the synth + */ +fluid_settings_t * +fluid_synth_get_settings(fluid_synth_t* synth) { + fluid_return_val_if_fail (synth != NULL, NULL); + return synth->settings; } -int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str) +/** + * Convenience function to set a string setting of a synth. + * @param synth FluidSynth instance + * @param name Name of setting parameter + * @param str Value to assign to the setting + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + return fluid_settings_setstr(synth->settings, name, str); } -int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str) +/** + * Convenience function to duplicate a string setting of a synth. + * @param synth FluidSynth instance + * @param name Name of setting parameter + * @param str Location to store a pointer to the newly allocated string value + * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * The returned string is owned by the caller and should be freed with free() + * when finished with it. + */ +int +fluid_synth_dupstr(fluid_synth_t* synth, char* name, char** str) { - return fluid_settings_getstr(synth->settings, name, str); + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + fluid_return_val_if_fail (str != NULL, FLUID_FAILED); + + return fluid_settings_dupstr(synth->settings, name, str); } -int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val) +/** + * Convenience function to set a floating point setting of a synth. + * @param synth FluidSynth instance + * @param name Name of setting parameter + * @param val Value to assign to the setting + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_setnum(fluid_synth_t* synth, char* name, double val) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + return fluid_settings_setnum(synth->settings, name, val); } -int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val) +/** + * Convenience function to get a floating point setting of a synth. + * @param synth FluidSynth instance + * @param name Name of setting parameter + * @param val Location to store the current value of the setting + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + return fluid_settings_getnum(synth->settings, name, val); } -int fluid_synth_setint(fluid_synth_t* synth, char* name, int val) +/** + * Convenience function to set an integer setting of a synth. + * @param synth FluidSynth instance + * @param name Name of setting parameter + * @param val Value to assign to the setting + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_setint(fluid_synth_t* synth, char* name, int val) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + return fluid_settings_setint(synth->settings, name, val); } -int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val) +/** + * Convenience function to get an integer setting of a synth. + * @param synth FluidSynth instance + * @param name Name of setting parameter + * @param val Location to store the current value of the setting + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_getint(fluid_synth_t* synth, char* name, int* val) { + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (name != NULL, FLUID_FAILED); + return fluid_settings_getint(synth->settings, name, val); } +/** + * Set a SoundFont generator (effect) value on a MIDI channel in real-time. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @param value Offset generator value to assign to the MIDI channel + * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * Parameter numbers and ranges are described in the SoundFont 2.01 + * specification PDF, paragraph 8.1.3, page 48. See #fluid_gen_type. + */ int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) { - int i; - fluid_voice_t* voice; + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if ((param < 0) || (param >= GEN_LAST)) { - FLUID_LOG(FLUID_WARN, "Parameter number out of range"); - return FLUID_FAILED; - } - - fluid_channel_set_gen(synth->channel[chan], param, value, 0); - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (voice->chan == chan) { - fluid_voice_set_param(voice, param, value, 0); - } - } + if (fluid_synth_should_queue (synth)) + return (fluid_synth_queue_gen_event (synth, chan, param, value, FALSE)); + else fluid_synth_set_gen_LOCAL (synth, chan, param, value, FALSE); return FLUID_OK; } -/** Change the value of a generator. This function allows to control - all synthesis parameters in real-time. The changes are additive, - i.e. they add up to the existing parameter value. This function is - similar to sending an NRPN message to the synthesizer. The - function accepts a float as the value of the parameter. The - parameter numbers and ranges are described in the SoundFont 2.01 - specification, paragraph 8.1.3, page 48. See also - 'fluid_gen_type'. +/* Synthesis thread local set gen function */ +static void +fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float value, + int absolute) +{ + fluid_voice_t* voice; + int i; - Using the fluid_synth_set_gen2() function, it is possible to set - the absolute value of a generator. This is an extension to the - SoundFont standard. If 'absolute' is non-zero, the value of the - generator specified in the SoundFont is completely ignored and the - generator is fixed to the value passed as argument. To undo this - behavior, you must call fluid_synth_set_gen2 again, with - 'absolute' set to 0 (and possibly 'value' set to zero). + fluid_channel_set_gen (synth->channel[chan], param, value, absolute); - If 'normalized' is non-zero, the value is supposed to be - normalized between 0 and 1. Before applying the value, it will be - scaled and shifted to the range defined in the SoundFont - specifications. + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + if (voice->chan == chan) + fluid_voice_set_param (voice, param, value, absolute); + } +} + +/** + * Set a SoundFont generator (effect) value on a MIDI channel in real-time. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @param value Offset or absolute generator value to assign to the MIDI channel + * @param absolute 0 to assign a relative value, non-zero to assign an absolute value + * @param normalize 0 if value is specified in the native units of the generator, + * non-zero to take the value as a 0.0-1.0 range and apply it to the valid + * generator effect range (scaled and shifted as necessary). + * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * This function allows for setting all effect parameters in real time on a + * MIDI channel. Setting absolute to non-zero will cause the value to override + * any generator values set in the instruments played on the MIDI channel. + * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont + * generator parameters and valid ranges. */ int fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param, float value, int absolute, int normalized) { - int i; - fluid_voice_t* voice; float v; - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); - if ((param < 0) || (param >= GEN_LAST)) { - FLUID_LOG(FLUID_WARN, "Parameter number out of range"); - return FLUID_FAILED; - } + v = normalized ? fluid_gen_scale(param, value) : value; - v = (normalized)? fluid_gen_scale(param, value) : value; - - fluid_channel_set_gen(synth->channel[chan], param, v, absolute); - - for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (voice->chan == chan) { - fluid_voice_set_param(voice, param, v, absolute); - } - } + if (fluid_synth_should_queue (synth)) + return (fluid_synth_queue_gen_event (synth, chan, param, v, absolute)); + else fluid_synth_set_gen_LOCAL (synth, chan, param, v, absolute); return FLUID_OK; } -float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) +/** + * Get generator value assigned to a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @return Current generator value assigned to MIDI channel + */ +float +fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param) { - if ((chan < 0) || (chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return 0.0; - } - - if ((param < 0) || (param >= GEN_LAST)) { - FLUID_LOG(FLUID_WARN, "Parameter number out of range"); - return 0.0; - } + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED); return fluid_channel_get_gen(synth->channel[chan], param); } -/* The synth needs to know the router for the command line handlers (they only - * supply the synth as argument) +/** + * Assign a MIDI router to a synth. + * @param synth FluidSynth instance + * @param router MIDI router to assign to the synth + * + * NOTE: This should only be done once and prior to using the synth. */ -void fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router){ - synth->midi_router=router; +void +fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router) +{ + fluid_return_if_fail (synth != NULL); + + synth->midi_router = router; }; -/* Purpose: - * Any MIDI event from the MIDI router arrives here and is handed - * to the appropriate function. +/** + * Handle MIDI event from MIDI router, used as a callback function. + * @param data FluidSynth instance + * @param event MIDI event to handle + * @return FLUID_OK on success, FLUID_FAILED otherwise */ -int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) +int +fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) { fluid_synth_t* synth = (fluid_synth_t*) data; int type = fluid_midi_event_get_type(event); @@ -3193,7 +4595,7 @@ int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) switch(type) { case NOTE_ON: return fluid_synth_noteon(synth, chan, - fluid_midi_event_get_key(event), + fluid_midi_event_get_key(event), fluid_midi_event_get_velocity(event)); case NOTE_OFF: @@ -3219,116 +4621,145 @@ int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) return FLUID_FAILED; } - -int fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, - int audio_chan, int midi_chan, int key, int vel) +/** + * Create and start voices using a preset and a MIDI note on event. + * @param synth FluidSynth instance + * @param id Voice group ID to use (can be used with fluid_synth_stop()). + * @param preset Preset to synthesize + * @param audio_chan Unused currently, set to 0 + * @param midi_chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @param vel MIDI velocity number (1-127) + * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * NOTE: Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +int +fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset, + int audio_chan, int midi_chan, int key, int vel) { - int r; + fluid_channel_t* channel; - /* check the ranges of the arguments */ - if ((midi_chan < 0) || (midi_chan >= synth->midi_channels)) { - FLUID_LOG(FLUID_WARN, "Channel out of range"); - return FLUID_FAILED; - } - - if ((key < 0) || (key >= 128)) { - FLUID_LOG(FLUID_WARN, "Key out of range"); - return FLUID_FAILED; - } - - if ((vel <= 0) || (vel >= 128)) { - FLUID_LOG(FLUID_WARN, "Velocity out of range"); - return FLUID_FAILED; - } - - fluid_mutex_lock(synth->busy); /* One at a time, please */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (preset != NULL, FLUID_FAILED); + fluid_return_val_if_fail (midi_chan >= 0 && midi_chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail (vel >= 1 && vel <= 127, FLUID_FAILED); + fluid_return_val_if_fail (fluid_synth_is_synth_thread (synth), FLUID_FAILED); synth->storeid = id; - r = fluid_preset_noteon(preset, synth, midi_chan, key, vel); - - fluid_mutex_unlock(synth->busy); - - return r; + return fluid_preset_noteon (preset, synth, midi_chan, key, vel); } -int fluid_synth_stop(fluid_synth_t* synth, unsigned int id) +/** + * Stop notes for a given note event voice ID. + * @param synth FluidSynth instance + * @param id Voice note event ID + * @return FLUID_OK on success, FLUID_FAILED otherwise + * + * NOTE: In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned + * if no matching voice note event ID was found. Versions after 1.1.0 only + * return #FLUID_FAILED if an error occurs. + */ +int +fluid_synth_stop(fluid_synth_t* synth, unsigned int id) +{ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + + if (fluid_synth_should_queue (synth)) + return fluid_synth_queue_int_event (synth, FLUID_EVENT_QUEUE_ELEM_STOP_VOICES, id); + + fluid_synth_stop_LOCAL (synth, id); + return FLUID_OK; +} + +/* Local synthesis thread variant of fluid_synth_stop */ +static void +fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id) { - int i; fluid_voice_t* voice; - int status = FLUID_FAILED; - int count = 0; + int i; for (i = 0; i < synth->polyphony; i++) { - voice = synth->voice[i]; - if (_ON(voice) && (fluid_voice_get_id(voice) == id)) { - count++; + if (_ON(voice) && (fluid_voice_get_id (voice) == id)) fluid_voice_noteoff(voice); - status = FLUID_OK; - } } - - return status; -} - -static fluid_bank_offset_t* -fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id) -{ - fluid_list_t* list = synth->bank_offsets; - fluid_bank_offset_t* offset; - - while (list) { - - offset = (fluid_bank_offset_t*) fluid_list_get(list); - if (offset->sfont_id == sfont_id) { - return offset; - } - - list = fluid_list_next(list); - } - - return NULL; } +/** + * Offset the bank numbers of a loaded SoundFont. + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param offset Bank offset value to apply to all instruments + */ int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset) { - fluid_bank_offset_t* bank_offset; + fluid_sfont_info_t *sfont_info; + fluid_list_t *list; - bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); - if (bank_offset == NULL) { - bank_offset = FLUID_NEW(fluid_bank_offset_t); - if (bank_offset == NULL) { - return -1; - } - bank_offset->sfont_id = sfont_id; - bank_offset->offset = offset; - synth->bank_offsets = fluid_list_prepend(synth->bank_offsets, bank_offset); - } else { - bank_offset->offset = offset; - } + fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ - return 0; + for (list = synth->sfont_info; list; list = fluid_list_next(list)) { + sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + + if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id) + { + sfont_info->bankofs = offset; + break; + } + } + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + if (!list) + { + FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id); + return FLUID_FAILED; + } + + return FLUID_OK; } +/** + * Get bank offset of a loaded SoundFont. + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @return SoundFont bank offset value + */ int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id) { - fluid_bank_offset_t* bank_offset; + fluid_sfont_info_t *sfont_info; + fluid_list_t *list; + int offset; - bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); - return (bank_offset == NULL)? 0 : bank_offset->offset; -} - -static void -fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id) -{ - fluid_bank_offset_t* bank_offset; - - bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id); - if (bank_offset != NULL) { - synth->bank_offsets = fluid_list_remove(synth->bank_offsets, bank_offset); - } + fluid_return_val_if_fail (synth != NULL, 0); + + fluid_mutex_lock (synth->mutex); /* ++ lock sfont_info list */ + + for (list = synth->sfont_info; list; list = fluid_list_next(list)) { + sfont_info = (fluid_sfont_info_t *)fluid_list_get (list); + + if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id) + { + offset = sfont_info->bankofs; + break; + } + } + + fluid_mutex_unlock (synth->mutex); /* -- unlock */ + + if (!list) + { + FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id); + return 0; + } + + return offset; } diff --git a/fluidsynth/src/fluid_synth.h b/fluidsynth/src/fluid_synth.h index 06965e2b..48e53e0f 100644 --- a/fluidsynth/src/fluid_synth.h +++ b/fluidsynth/src/fluid_synth.h @@ -32,6 +32,7 @@ #include "config.h" #endif #include "fluidsynth_priv.h" +#include "fluid_event_queue.h" #include "fluid_list.h" #include "fluid_rev.h" #include "fluid_voice.h" @@ -76,53 +77,117 @@ enum fluid_synth_status #define SYNTH_REVERB_CHANNEL 0 #define SYNTH_CHORUS_CHANNEL 1 -typedef struct _fluid_bank_offset_t fluid_bank_offset_t; - -struct _fluid_bank_offset_t { - int sfont_id; - int offset; -}; +/** + * Structure used for sfont_info field in #fluid_synth_t for each loaded + * SoundFont with the SoundFont instance and additional fields. + */ +typedef struct _fluid_sfont_info_t { + fluid_sfont_t *sfont; /**> Loaded SoundFont */ + fluid_synth_t *synth; /**> Parent synth */ + int refcount; /**> SoundFont reference count (0 if no presets referencing it) */ + int bankofs; /**> Bank offset */ +} fluid_sfont_info_t; /* * fluid_synth_t + * + * Mutual exclusion notes: + * + * Set only once on init: + * ---------------------- + * verbose + * dump + * sample_rate (will be runtime change-able in the future) + * midi_channels + * audio_channels + * audio_groups + * effects_channels + * start + * channel[] (Contents change) + * nvoice + * voice[] (Contents change) + * nbuf + * left_buf[], right_buf[] (Contents change) + * fx_left_buf[], fx_right_buf[] (Contents change) + * LADSPA_FxUnit (Contents change) + * + * Single thread use only (modify only prior to synthesis): + * loaders<> + * midi_router + * + * Mutex protected: + * settings{} (has its own mutex) + * sfont_info<> + * tuning + * cur_tuning + * sfont_id + * gain + * reverb_roomsize, reverb_damping, reverb_width, reverb_level + * chorus_nr, chorus_level, chorus_speed, chorus_depth, chorus_type + * + * Atomic int operations: + * ---------------------- + * with_reverb + * with_chorus + * state + * + * noteid + * storeid + * outbuf + * sample_timers + * + * Only synth thread changes + * ------------------------- + * ticks + * reverb{} + * chorus{} + * cur + * dither_index + * cpu_load + * polyphony (atomic int get for non-synth threads) + * st_gain */ struct _fluid_synth_t { - /* fluid_settings_old_t settings_old; the old synthesizer settings */ - fluid_settings_t* settings; /** the synthesizer settings */ - int polyphony; /** maximum polyphony */ - char with_reverb; /** Should the synth use the built-in reverb unit? */ - char with_chorus; /** Should the synth use the built-in chorus unit? */ - char verbose; /** Turn verbose mode on? */ - char dump; /** Dump events to stdout to hook up a user interface? */ - double sample_rate; /** The sample rate */ - int midi_channels; /** the number of MIDI channels (>= 16) */ - int audio_channels; /** the number of audio channels (1 channel=left+right) */ - int audio_groups; /** the number of (stereo) 'sub'groups from the synth. - Typically equal to audio_channels. */ - int effects_channels; /** the number of effects channels (>= 2) */ - unsigned int state; /** the synthesizer state */ - unsigned int ticks; /** the number of audio samples since the start */ - unsigned int start; /** the start in msec, as returned by system clock */ + fluid_thread_id_t synth_thread_id; /**> ID of the synthesis thread or FLUID_THREAD_ID_NULL if not yet set */ + fluid_private_t thread_queues; /**> Thread private data for event queues for each non-synthesis thread queuing events */ + fluid_event_queue_t *queues[FLUID_MAX_EVENT_QUEUES]; /**> Thread event queues (NULL for unused elements) */ - fluid_list_t *loaders; /** the soundfont loaders */ - fluid_list_t* sfont; /** the loaded soundfont */ - unsigned int sfont_id; - fluid_list_t* bank_offsets; /** the offsets of the soundfont banks */ + fluid_mutex_t mutex; /**> Lock for multi-thread sensitive variables (not used by synthesis process) */ + fluid_list_t *queue_pool; /**> List of event queues whose threads have been destroyed and which can be re-used */ + fluid_event_queue_t *return_queue; /**> Event queue for events from synthesis thread to non-synthesis threads (memory frees, etc) */ + fluid_timer_t *return_queue_timer; /**> Timer thread to process return event queue */ -#if defined(MACOS9) - fluid_list_t* unloading; /** the soundfonts that need to be unloaded */ -#endif + fluid_settings_t* settings; /**> the synthesizer settings */ + int polyphony; /**> maximum polyphony */ + char with_reverb; /**> Should the synth use the built-in reverb unit? */ + char with_chorus; /**> Should the synth use the built-in chorus unit? */ + char verbose; /**> Turn verbose mode on? */ + char dump; /**> Dump events to stdout to hook up a user interface? */ + double sample_rate; /**> The sample rate */ + int midi_channels; /**> the number of MIDI channels (>= 16) */ + int audio_channels; /**> the number of audio channels (1 channel=left+right) */ + int audio_groups; /**> the number of (stereo) 'sub'groups from the synth. + Typically equal to audio_channels. */ + int effects_channels; /**> the number of effects channels (>= 2) */ + int state; /**> the synthesizer state */ + unsigned int ticks; /**> the number of audio samples since the start */ + unsigned int start; /**> the start in msec, as returned by system clock */ - double gain; /** master gain */ - fluid_channel_t** channel; /** the channels */ - int num_channels; /** the number of channels */ - int nvoice; /** the length of the synthesis process array */ - fluid_voice_t** voice; /** the synthesis processes */ - unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */ + fluid_list_t *loaders; /**> the SoundFont loaders */ + fluid_list_t *sfont_info; /**> List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ + fluid_hashtable_t *sfont_hash; /**> Hash of fluid_sfont_t->fluid_sfont_info_t (remains until SoundFont is deleted) */ + unsigned int sfont_id; /**> Incrementing ID assigned to each loaded SoundFont */ + + double gain; /**> master gain */ + double st_gain; /**> Synth thread gain shadow value */ + fluid_channel_t** channel; /**> the channels */ + int nvoice; /**> the length of the synthesis process array (max polyphony allowed) */ + fluid_voice_t** voice; /**> the synthesis voices */ + unsigned int noteid; /**> the id is incremented for every new note. it's used for noteoff's */ unsigned int storeid; - int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/ + int nbuf; /**> How many audio buffers are used? (depends on nr of audio channels / groups)*/ fluid_real_t** left_buf; fluid_real_t** right_buf; @@ -131,24 +196,32 @@ struct _fluid_synth_t fluid_revmodel_t* reverb; fluid_chorus_t* chorus; - int cur; /** the current sample in the audio buffers to be output */ - int dither_index; /* current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ - char outbuf[256]; /** buffer for message output */ + float reverb_roomsize; /**> Shadow of reverb roomsize */ + float reverb_damping; /**> Shadow of reverb damping */ + float reverb_width; /**> Shadow of reverb width */ + float reverb_level; /**> Shadow of reverb level */ + + int chorus_nr; /**> Shadow of chorus number */ + float chorus_level; /**> Shadow of chorus level */ + float chorus_speed; /**> Shadow of chorus speed */ + float chorus_depth; /**> Shadow of chorus depth */ + int chorus_type; /**> Shadow of chorus type */ + + int cur; /**> the current sample in the audio buffers to be output */ + int dither_index; /**> current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ + + char outbuf[256]; /**> buffer for message output */ double cpu_load; - fluid_tuning_t*** tuning; /** 128 banks of 128 programs for the tunings */ - fluid_tuning_t* cur_tuning; /** current tuning in the iteration */ + fluid_tuning_t*** tuning; /**> 128 banks of 128 programs for the tunings */ + fluid_tuning_t* cur_tuning; /**> current tuning in the iteration */ - fluid_midi_router_t* midi_router; /* The midi router. Could be done nicer. */ - fluid_mutex_t busy; /* Indicates, whether the audio thread is currently running. - * Note: This simple scheme does -not- provide 100 % protection against - * thread problems, for example from MIDI thread and shell thread - */ - fluid_sample_timer_t* sample_timers; /* List of timers triggered after a block has been processed */ + fluid_midi_router_t* midi_router; /**> The midi router. Could be done nicer. */ + fluid_sample_timer_t* sample_timers; /**> List of timers triggered after a block has been processed */ #ifdef LADSPA - fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /** Effects unit for LADSPA support */ + fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**> Effects unit for LADSPA support */ #endif }; @@ -156,7 +229,7 @@ struct _fluid_synth_t int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str); /** returns 1 if the value exists, 0 otherwise */ -int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str); +int fluid_synth_dupstr(fluid_synth_t* synth, char* name, char** str); /** returns 1 if the value has been set, 0 otherwise */ int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val); @@ -170,18 +243,12 @@ int fluid_synth_setint(fluid_synth_t* synth, char* name, int val); /** returns 1 if the value exists, 0 otherwise */ int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val); - -int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num); - fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum, unsigned int prognum); int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan); int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan); -int fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl); -int fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan); -int fluid_synth_damp_voices(fluid_synth_t* synth, int chan); int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice); void fluid_synth_print_voice(fluid_synth_t* synth); @@ -190,6 +257,13 @@ void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin, void* lout, int loff, int lincr, void* rout, int roff, int rincr); +int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num); +int fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize, + double damping, double width, double level); + +int fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, + double speed, double depth_ms, int type); + fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data); int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer); diff --git a/fluidsynth/src/fluid_sys.c b/fluidsynth/src/fluid_sys.c index 127b20cf..71818343 100644 --- a/fluidsynth/src/fluid_sys.c +++ b/fluidsynth/src/fluid_sys.c @@ -386,7 +386,7 @@ DWORD WINAPI fluid_timer_run(LPVOID data); fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, - int new_thread, int auto_destroy) + int new_thread, int auto_destroy, int high_priority) { fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); if (timer == NULL) { @@ -408,7 +408,8 @@ new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, FLUID_FREE(timer); return NULL; } - SetThreadPriority(timer->thread, THREAD_PRIORITY_TIME_CRITICAL); + if (high_priority) + SetThreadPriority(timer->thread, THREAD_PRIORITY_TIME_CRITICAL); } else { fluid_timer_run((LPVOID) timer); } @@ -419,7 +420,7 @@ DWORD WINAPI fluid_timer_run(LPVOID data) { int count = 0; - int cont = 1; + int cont; long start; long delay; fluid_timer_t* timer; @@ -434,22 +435,19 @@ fluid_timer_run(LPVOID data) /* keep track of the start time for absolute positioning */ start = fluid_curtime(); - while (cont) { + while (timer->cont) { /* do whatever we have to do */ cont = (*timer->callback)(timer->data, fluid_curtime() - start); count++; + if (!cont) break; /* to avoid incremental time errors, I calculate the delay between two callbacks bringing in the "absolute" time (count * timer->msec) */ delay = (count * timer->msec) - (fluid_curtime() - start); - if (delay > 0) { - Sleep(delay); - } - - cont &= timer->cont; + if (delay > 0) Sleep (delay); } FLUID_LOG(FLUID_DBG, "Timer thread finished"); @@ -511,7 +509,7 @@ void fluid_timer_run(void *data); fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, - int new_thread, int auto_destroy) + int new_thread, int auto_destroy, int high_priority) { fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); if (timer == NULL) { @@ -533,7 +531,8 @@ new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, FLUID_FREE(timer); return NULL; } - DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, timer->thread_id); + if (high_priority) + DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, timer->thread_id); } else { fluid_timer_run(( void * )timer); } @@ -544,7 +543,7 @@ void fluid_timer_run(void *data) { int count = 0; - int cont = 1; + int cont; long start; long delay; fluid_timer_t* timer; @@ -559,22 +558,19 @@ fluid_timer_run(void *data) /* keep track of the start time for absolute positioning */ start = fluid_curtime(); - while (cont) { + while (timer->cont) { /* do whatever we have to do */ cont = (*timer->callback)(timer->data, fluid_curtime() - start); count++; + if (!cont) break; /* to avoid incremental time errors, I calculate the delay between two callbacks bringing in the "absolute" time (count * timer->msec) */ delay = (count * timer->msec) - (fluid_curtime() - start); - if (delay > 0) { - DosSleep(delay); - } - - cont &= timer->cont; + if (delay > 0) DosSleep (delay); } FLUID_LOG(FLUID_DBG, "Timer thread finished"); @@ -631,10 +627,10 @@ struct _fluid_timer_t }; void* -fluid_timer_start(void *data) +fluid_timer_run(void *data) { int count = 0; - int cont = 1; + int cont; long start; long delay; fluid_timer_t* timer; @@ -643,22 +639,19 @@ fluid_timer_start(void *data) /* keep track of the start time for absolute positioning */ start = fluid_curtime(); - while (cont) { + while (timer->cont) { /* do whatever we have to do */ cont = (*timer->callback)(timer->data, fluid_curtime() - start); count++; + if (!cont) break; /* to avoid incremental time errors, calculate the delay between two callbacks bringing in the "absolute" time (count * timer->msec) */ delay = (count * timer->msec) - (fluid_curtime() - start); - if (delay > 0) { - usleep(delay * 1000); - } - - cont &= timer->cont; + if (delay > 0) usleep (delay * 1000); } FLUID_LOG(FLUID_DBG, "Timer thread finished"); @@ -675,7 +668,7 @@ fluid_timer_start(void *data) fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, - int new_thread, int auto_destroy) + int new_thread, int auto_destroy, int high_priority) { pthread_attr_t *attr = NULL; pthread_attr_t rt_attr; @@ -695,35 +688,33 @@ new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, timer->thread = 0; timer->auto_destroy = auto_destroy; - err = pthread_attr_init(&rt_attr); - if (err == 0) { - err = pthread_attr_setschedpolicy(&rt_attr, SCHED_FIFO); - if (err == 0) { - priority.sched_priority = 10; - err = pthread_attr_setschedparam(&rt_attr, &priority); - if (err == 0) { - attr = &rt_attr; - } - } + if (high_priority) { + priority.sched_priority = 10; + + if (pthread_attr_init (&rt_attr) == 0 + && pthread_attr_setschedpolicy (&rt_attr, SCHED_FIFO) == 0 + && pthread_attr_setschedparam (&rt_attr, &priority) == 0) + attr = &rt_attr; } if (new_thread) { - err = pthread_create(&timer->thread, attr, fluid_timer_start, (void*) timer); - if (err == 0) { - FLUID_LOG(FLUID_DBG, "The timer thread was created with real-time priority"); - } else { - /* Create the thread with default attributes */ - err = pthread_create(&timer->thread, NULL, fluid_timer_start, (void*) timer); - if (err != 0) { - FLUID_LOG(FLUID_ERR, "Failed to create the timer thread"); - FLUID_FREE(timer); - return NULL; - } else { - FLUID_LOG(FLUID_DBG, "The timer thread does not have real-time priority"); - } - } + err = pthread_create(&timer->thread, attr, fluid_timer_run, (void*) timer); + + if (err == 0) { + if (attr) FLUID_LOG(FLUID_DBG, "Timer thread created with real-time priority"); + else FLUID_LOG(FLUID_DBG, "Timer thread created with normal priority"); + } else { + if (attr == NULL + || pthread_create (&timer->thread, NULL, fluid_timer_run, (void*) timer) != 0) { + FLUID_LOG(FLUID_ERR, "Failed to create timer thread"); + FLUID_FREE(timer); + return NULL; + } else { + FLUID_LOG(FLUID_DBG, "Timer thread created, but not with real-time priority"); + } + } } else { - fluid_timer_start((void*) timer); + fluid_timer_run((void*) timer); } return timer; } diff --git a/fluidsynth/src/fluid_sys.h b/fluidsynth/src/fluid_sys.h index c0eafb62..853ef869 100644 --- a/fluidsynth/src/fluid_sys.h +++ b/fluidsynth/src/fluid_sys.h @@ -54,6 +54,14 @@ void fluid_log_config(void); void fluid_time_config(void); +/* Misc */ + +#define fluid_return_val_if_fail g_return_val_if_fail +#define fluid_return_if_fail g_return_if_fail +#define FLUID_INLINE inline +#define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT + + /* * Utility functions */ @@ -64,7 +72,6 @@ char *fluid_strtok (char **str, char *delim); Additional debugging system, separate from the log system. This allows to print selected debug messages of a specific subsystem. - */ extern unsigned int fluid_debug_flags; @@ -105,7 +112,8 @@ typedef int (*fluid_timer_callback_t)(void* data, unsigned int msec); typedef struct _fluid_timer_t fluid_timer_t; fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, - void* data, int new_thread, int auto_destroy); + void* data, int new_thread, int auto_destroy, + int high_priority); int delete_fluid_timer(fluid_timer_t* timer); int fluid_timer_join(fluid_timer_t* timer); @@ -113,21 +121,48 @@ int fluid_timer_stop(fluid_timer_t* timer); /** - Muteces + Muteces */ -typedef GStaticMutex fluid_mutex_t; -#define fluid_mutex_init(_m) g_static_mutex_init(&(_m)) -#define fluid_mutex_destroy(_m) g_static_mutex_free(&(_m)) -#define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m)) -#define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m)) +/* Recursive locks allowed */ +typedef GStaticRecMutex fluid_mutex_t; +#define fluid_mutex_init(_m) g_static_rec_mutex_init(&(_m)) +#define fluid_mutex_destroy(_m) g_static_rec_mutex_free(&(_m)) +#define fluid_mutex_lock(_m) g_static_rec_mutex_lock(&(_m)) +#define fluid_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m)) + + +/* Atomic operations */ + +#define fluid_atomic_int_inc(_pi) g_atomic_int_inc(_pi) +#define fluid_atomic_int_add(_pi, _val) g_atomic_int_add(_pi, _val) +#define fluid_atomic_int_get(_pi) g_atomic_int_get(_pi) +#define fluid_atomic_int_set(_pi, _val) g_atomic_int_set(_pi, _val) +#define fluid_atomic_int_dec_and_test(_pi) g_atomic_int_dec_and_test(_pi) +#define fluid_atomic_int_compare_and_exchange(_pi, _old, _new) \ + g_atomic_int_compare_and_exchange(_pi, _old, _new) +#define fluid_atomic_int_exchange_and_add(_pi, _add) \ + g_atomic_int_exchange_and_add(_pi, _add) + +#define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp) +#define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \ + g_atomic_pointer_compare_and_exchange(_pp, _old, _new) + + +/* Thread private data */ + +typedef GStaticPrivate fluid_private_t; +#define fluid_private_init(_priv) g_static_private_init(&(_priv)) +#define fluid_private_get(_priv) g_static_private_get(&(_priv)) +#define fluid_private_set(_priv, _data, _notify) g_static_private_set(&(_priv), _data, _notify) +#define fluid_private_free(_priv) g_static_private_free(&(_priv)) /** - Threads + Threads -*/ + */ typedef GThread fluid_thread_t; typedef void (*fluid_thread_func_t)(void* data); @@ -138,10 +173,14 @@ fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detac int delete_fluid_thread(fluid_thread_t* thread); int fluid_thread_join(fluid_thread_t* thread); -/** - Sockets and I/O +#define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */ +#define fluid_thread_id_t GThread * /* Data type for a thread ID */ +#define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */ -*/ +/** + Sockets and I/O + + */ fluid_istream_t fluid_get_stdin (void); fluid_ostream_t fluid_get_stdout (void); diff --git a/fluidsynth/src/fluid_winmidi.c b/fluidsynth/src/fluid_winmidi.c index be25112f..f445b367 100644 --- a/fluidsynth/src/fluid_winmidi.c +++ b/fluidsynth/src/fluid_winmidi.c @@ -105,8 +105,14 @@ new_fluid_winmidi_driver(fluid_settings_t* settings, dev->driver.data = data; /* get the device name. if none is specified, use the default device. */ - if(!fluid_settings_getstr(settings, "midi.winmidi.device", &devname)) { - devname = "default"; + if(!fluid_settings_getstr(settings, "midi.winmidi.device", &devname) || !devname) { + devname = FLUID_DUPSTR ("default"); + + if (!devname) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } } /* check if there any midi devices installed */ @@ -150,9 +156,12 @@ new_fluid_winmidi_driver(fluid_settings_t* settings, goto error_recovery; } + if (devname) FLUID_FREE (devname); /* -- free device name */ + return (fluid_midi_driver_t*) dev; error_recovery: + if (devname) FLUID_FREE (devname); /* -- free device name */ delete_fluid_winmidi_driver((fluid_midi_driver_t*) dev); return NULL; } @@ -193,6 +202,10 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD dwInstance, DWORD msg, DWOR event.channel = msg_chan(msg); event.param1 = msg_p1(msg); event.param2 = msg_p2(msg); + if(event.type==PITCH_BEND){ + event.param1 = ((event.param2 & 0x7f) << 7) | (event.param1 & 0x7f); + event.param2 = 0; + } (*dev->driver.handler)(dev->driver.data, &event); } break; diff --git a/fluidsynth/src/fluidsynth.c b/fluidsynth/src/fluidsynth.c index 0755f778..1a5a1f65 100644 --- a/fluidsynth/src/fluidsynth.c +++ b/fluidsynth/src/fluidsynth.c @@ -183,15 +183,20 @@ void fast_render_loop(fluid_settings_t* settings, fluid_synth_t* synth, fluid_pl int period_size = 0; fluid_settings_getint(settings, "audio.period-size", &period_size); - fluid_settings_getstr(settings, "audio.file.name", &filename); + fluid_settings_dupstr(settings, "audio.file.name", &filename); /* ++ alloc file name */ if (filename == NULL || period_size <= 0) { fprintf(stderr, "Failed to fetch parameters for file renderer\n"); + if (filename) FLUID_FREE (filename); /* -- free file name */ + return; } renderer = new_fluid_file_renderer(synth, filename, period_size); + + FLUID_FREE (filename); /* -- free file name */ + if (!renderer) { - return; + return; } while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) { @@ -225,7 +230,7 @@ int main(int argc, char** argv) int midi_in = 1; fluid_player_t* player = NULL; fluid_midi_router_t* router = NULL; - fluid_sequencer_t* sequencer = NULL; + //fluid_sequencer_t* sequencer = NULL; fluid_midi_driver_t* mdriver = NULL; fluid_audio_driver_t* adriver = NULL; fluid_synth_t* synth = NULL; @@ -524,20 +529,20 @@ int main(int argc, char** argv) /* In dump mode, text output is generated for events going into and out of the router. * The example dump functions are put into the chain before and after the router.. */ - sequencer = new_fluid_sequencer2(0); + //sequencer = new_fluid_sequencer2(0); router = new_fluid_midi_router( settings, - dump ? fluid_midi_dump_postrouter : fluid_sequencer_add_midi_event_to_buffer, - (void*)sequencer); + dump ? fluid_midi_dump_postrouter : fluid_synth_handle_midi_event, + (void*)synth); - if (router == NULL || sequencer == NULL) { + if (router == NULL) { fprintf(stderr, "Failed to create the MIDI input router; no MIDI input\n" "will be available. You can access the synthesizer \n" "through the console.\n"); } else { fluid_synth_set_midi_router(synth, router); /* Fixme, needed for command handler */ - fluid_sequencer_register_fluidsynth(sequencer, synth); +// fluid_sequencer_register_fluidsynth(sequencer, synth); mdriver = new_fluid_midi_driver( settings, dump ? fluid_midi_dump_prerouter : fluid_midi_router_handle_midi_event, @@ -654,9 +659,9 @@ int main(int argc, char** argv) #endif } - if (sequencer) { + /*if (sequencer) { delete_fluid_sequencer(sequencer); - } + }*/ if (adriver) { delete_fluid_audio_driver(adriver); diff --git a/fluidsynth/src/fluidsynth_priv.h b/fluidsynth/src/fluidsynth_priv.h index 9f30559d..436e710b 100644 --- a/fluidsynth/src/fluidsynth_priv.h +++ b/fluidsynth/src/fluidsynth_priv.h @@ -198,7 +198,10 @@ typedef struct _fluid_sample_timer_t fluid_sample_timer_t; * CONSTANTS */ -#define FLUID_BUFSIZE 64 +#define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */ +#define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */ +#define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */ +#define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */ #ifndef PI #define PI 3.141592654 @@ -225,6 +228,7 @@ typedef FILE* fluid_file; #define FLUID_STRCMP(_s,_t) strcmp(_s,_t) #define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n) #define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src) +#define FLUID_STRNCPY(_dst,_src,_n) strncpy(_dst,_src,_n) #define FLUID_STRCHR(_s,_c) strchr(_s,_c) #ifdef strdup #define FLUID_STRDUP(s) strdup(s)