diff --git a/fluidsynth/doc/fluidsynth-v11-devdoc.txt b/fluidsynth/doc/fluidsynth-v11-devdoc.txt index 7639abf8..963c7bc4 100644 --- a/fluidsynth/doc/fluidsynth-v11-devdoc.txt +++ b/fluidsynth/doc/fluidsynth-v11-devdoc.txt @@ -19,6 +19,7 @@ All the source code examples in this document are in the public domain; you can - \ref Disclaimer - \ref Introduction +- \ref NewIn1_1_0 - \ref CreatingSettings - \ref CreatingSynth - \ref CreatingAudioDriver @@ -54,6 +55,57 @@ What is FluidSynth? - FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org + +\section NewIn1_1_0 Whats new in 1.1.0? + +Overview of changes in FluidSynth 1.1.0 concerning developers: + +- Extensive work to make FluidSynth thread safe. Previous versions had many multi-thread + issues which could lead to crashes or synthesis glitches. Some of the API additions, + deprecations and function recommended conditions of use are related to this work. +- File renderer object for rendering audio to files. +- Sequencer objects can now use the system timer or the sample clock. When using the sample + clock, events are triggered based on the current output audio sample position. This means + that MIDI is synchronized with the audio and identical output will be achieved for the same + MIDI event input. +- libsndfile support for rendering audio to different formats and file types. +- API for using the MIDI router subsystem. +- MIDI Tuning Standard functions were added for specifying whether to activate tuning changes + in realtime or not. +- SYSEX support (MIDI Tuning Standard only at the moment). +- Changed all yes/no boolean string settings to integer #FLUID_HINT_TOGGLED settings with + backwards compatibility (assignment and query of boolean values as strings). +- Many other improvements and bug fixes. + +API additions: + +- A file renderer can be created with new_fluid_file_renderer(), deleted with + delete_fluid_file_renderer() and a block of audio processed with fluid_file_renderer_process_block(). +- Additional functions were added for using the MIDI router subsystem. + To clear all rules from a router use fluid_midi_router_clear_rules() and to set a router to default rules + use fluid_midi_router_set_default_rules(). + To create a router rule use new_fluid_midi_router_rule() and to delete a rule use + delete_fluid_midi_router_rule() (seldom used). Set values of a router rule with + fluid_midi_router_rule_set_chan(), fluid_midi_router_rule_set_param1() and fluid_midi_router_rule_set_param2(). + fluid_midi_router_add_rule() can be used to add a rule to a router. +- New MIDI event functions were added, including fluid_event_channel_pressure(), fluid_event_system_reset(), + and fluid_event_unregistering(). Additional sequencer functions include fluid_sequencer_add_midi_event_to_buffer(), + fluid_sequencer_get_use_system_timer() and fluid_sequencer_process(). new_fluid_sequencer2() was added to + allow for the timer type to be specified (system or sample clock). +- The settings subsystem has some new functions for thread safety: fluid_settings_copystr() and fluid_settings_dupstr(). + Also there are new convenience functions to count the number of string options for a setting: fluid_settings_option_count() + and for concatenating setting options with a separator: fluid_settings_option_concat(). +- MIDI Tuning Standard functions added include: fluid_synth_activate_key_tuning(), fluid_synth_activate_octave_tuning(), + fluid_synth_activate_tuning() and fluid_synth_deactivate_tuning(). All of which provide a parameter for specifying if + tuning changes should occur in realtime (affect existing voices) or not. +- Additional synthesizer API: fluid_synth_get_sfont_by_name() to get a SoundFont by name, + fluid_synth_program_select_by_sfont_name() to select an instrument by SoundFont name/bank/program, + fluid_synth_set_gen2() for specifying additional parameters when assigning a generator value, + and fluid_synth_sysex() for sending SYSEX messages to the synth. +- Miscellaneous additions: fluid_player_set_loop() to set playlist loop count. + + + \section CreatingSettings Creating and changing the settings Before you can use the synthesizer, you have to create a settings object. The settings objects is used by many components of the FluidSynth library. It gives a unified API to set the parameters of the audio drivers, the midi drivers, the synthesizer, and so forth. A number of default settings are defined by the current implementation. diff --git a/fluidsynth/include/fluidsynth/audio.h b/fluidsynth/include/fluidsynth/audio.h index 57005a5a..3c689187 100644 --- a/fluidsynth/include/fluidsynth/audio.h +++ b/fluidsynth/include/fluidsynth/audio.h @@ -64,8 +64,6 @@ FLUIDSYNTH_API fluid_audio_driver_t* new_fluid_audio_driver2(fluid_settings_t* s FLUIDSYNTH_API void delete_fluid_audio_driver(fluid_audio_driver_t* driver); -FLUIDSYNTH_API void fluid_audio_driver_get_names(char* buf, size_t buflen, const char* separator); - FLUIDSYNTH_API fluid_file_renderer_t *new_fluid_file_renderer(fluid_synth_t* synth); FLUIDSYNTH_API int fluid_file_renderer_process_block(fluid_file_renderer_t* dev); FLUIDSYNTH_API void delete_fluid_file_renderer(fluid_file_renderer_t* dev); diff --git a/fluidsynth/include/fluidsynth/midi.h b/fluidsynth/include/fluidsynth/midi.h index 6b00b80b..c399ddc5 100644 --- a/fluidsynth/include/fluidsynth/midi.h +++ b/fluidsynth/include/fluidsynth/midi.h @@ -108,8 +108,6 @@ fluid_midi_driver_t* new_fluid_midi_driver(fluid_settings_t* settings, FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t* driver); -FLUIDSYNTH_API void fluid_midi_driver_get_names(char* buf, size_t buflen, const char* separator); - /** * MIDI player status enum. diff --git a/fluidsynth/include/fluidsynth/settings.h b/fluidsynth/include/fluidsynth/settings.h index a7b55c4d..8c038e06 100644 --- a/fluidsynth/include/fluidsynth/settings.h +++ b/fluidsynth/include/fluidsynth/settings.h @@ -196,6 +196,9 @@ void fluid_settings_foreach_option(fluid_settings_t* settings, fluid_settings_foreach_option_t func); FLUIDSYNTH_API int fluid_settings_option_count (fluid_settings_t* settings, char* name); +FLUIDSYNTH_API char *fluid_settings_option_concat (fluid_settings_t* settings, + const char* name, + const char* separator); /** * Callback function type used with fluid_settings_foreach() diff --git a/fluidsynth/include/fluidsynth/synth.h b/fluidsynth/include/fluidsynth/synth.h index b044b72c..607e95fb 100644 --- a/fluidsynth/include/fluidsynth/synth.h +++ b/fluidsynth/include/fluidsynth/synth.h @@ -70,6 +70,10 @@ FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsi 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_program_select_by_sfont_name (fluid_synth_t* synth, int chan, + char* sfont_name, unsigned int bank_num, + unsigned int preset_num); 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); @@ -96,6 +100,8 @@ FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth); FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num); FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name (fluid_synth_t* synth, + char *name); FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset); FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id); @@ -179,6 +185,9 @@ enum fluid_interp { FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value); +FLUIDSYNTH_API int fluid_synth_set_gen2 (fluid_synth_t* synth, int chan, + int param, float value, + int absolute, int normalized); FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param); diff --git a/fluidsynth/src/fluid_adriver.c b/fluidsynth/src/fluid_adriver.c index 300ef7e8..b3942a7f 100644 --- a/fluidsynth/src/fluid_adriver.c +++ b/fluidsynth/src/fluid_adriver.c @@ -289,35 +289,6 @@ void fluid_audio_driver_settings(fluid_settings_t* settings) } } -/** - * Write a list of audio driver names into a buffer. - * (A buffer length of 256 characters should be more than enough.) - * @param buf buffert to write names into - * @param buflen maximum amount of characters in buf. - * @param separator separator string, written between names. - * @since 1.1.0 - */ -void -fluid_audio_driver_get_names(char* buf, size_t buflen, const char* separator) -{ - int i; - - if (buflen <= 0) { - return; - } - buf[0] = '\0'; - buflen--; - - for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { - if (i > 0) { - strncat(buf, separator, buflen - strlen(buf)); - } - strncat(buf, fluid_audio_drivers[i].name, buflen - strlen(buf)); - } - buf[buflen] = '\0'; -} - - /** * Create a new audio driver. * @param settings Configuration settings used to select and create the audio @@ -334,7 +305,7 @@ new_fluid_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) int i; fluid_audio_driver_t* driver = NULL; char* name; - char allnames[256]; + char *allnames; for (i = 0; fluid_audio_drivers[i].name != NULL; i++) { if (fluid_settings_str_equal(settings, "audio.driver", fluid_audio_drivers[i].name)) { @@ -347,11 +318,12 @@ new_fluid_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) } } - fluid_audio_driver_get_names(allnames, sizeof(allnames), ", "); - fluid_settings_dupstr(settings, "audio.driver", &name); /* ++ alloc name */ + allnames = fluid_settings_option_concat (settings, "audio.driver", NULL); + 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); + name ? name : "NULL", allnames ? allnames : "ERROR"); if (name) FLUID_FREE (name); + if (allnames) FLUID_FREE (allnames); return NULL; } diff --git a/fluidsynth/src/fluid_filerenderer.c b/fluidsynth/src/fluid_filerenderer.c index 91a1aabe..62cd45e4 100644 --- a/fluidsynth/src/fluid_filerenderer.c +++ b/fluidsynth/src/fluid_filerenderer.c @@ -337,16 +337,16 @@ void delete_fluid_file_renderer(fluid_file_renderer_t* dev) return; } -#if LIBSNDFILE_SUPPORT /** * Write period_size samples to file. * @param dev File renderer instance - * @return FLUID_OK or FLUID_FAILED if an error occurred + * @return #FLUID_OK or #FLUID_FAILED if an error occurred * @since 1.1.0 */ int fluid_file_renderer_process_block(fluid_file_renderer_t* dev) { +#if LIBSNDFILE_SUPPORT int n; fluid_synth_write_float(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2); @@ -359,11 +359,9 @@ fluid_file_renderer_process_block(fluid_file_renderer_t* dev) return FLUID_FAILED; } return FLUID_OK; -} + #else /* No libsndfile support */ -int -fluid_file_renderer_process_block(fluid_file_renderer_t* dev) -{ + int n, offset; fluid_synth_write_s16(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2); @@ -378,9 +376,8 @@ fluid_file_renderer_process_block(fluid_file_renderer_t* dev) } } return FLUID_OK; -} - #endif +} #if LIBSNDFILE_SUPPORT diff --git a/fluidsynth/src/fluid_mdriver.c b/fluidsynth/src/fluid_mdriver.c index 783910c6..d537c32e 100644 --- a/fluidsynth/src/fluid_mdriver.c +++ b/fluidsynth/src/fluid_mdriver.c @@ -195,36 +195,6 @@ void fluid_midi_driver_settings(fluid_settings_t* settings) } } - -/** - * Write a list of MIDI driver names into a buffer. - * (A buffer length of 256 characters should be more than enough.) - * @param buf Buffer to write names into - * @param buflen Maximum amount of characters in buf. - * @param separator Separator string, written between names. - * @since 1.1.0 - */ -void -fluid_midi_driver_get_names(char* buf, size_t buflen, const char* separator) -{ - int i; - - if (buflen <= 0) { - return; - } - buf[0] = '\0'; - buflen--; - - for (i = 0; fluid_midi_drivers[i].name != NULL; i++) { - if (i > 0) { - strncat(buf, separator, buflen - strlen(buf)); - } - strncat(buf, fluid_midi_drivers[i].name, buflen - strlen(buf)); - } - buf[buflen] = '\0'; -} - - /** * Create a new MIDI driver instance. * @param settings Settings used to configure new MIDI driver. @@ -235,9 +205,10 @@ fluid_midi_driver_get_names(char* buf, size_t buflen, const char* separator) */ fluid_midi_driver_t* new_fluid_midi_driver(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data) { - int i; - char allnames[256]; fluid_midi_driver_t* driver = NULL; + char *allnames; + int i; + for (i = 0; fluid_midi_drivers[i].name != NULL; i++) { if (fluid_settings_str_equal(settings, "midi.driver", fluid_midi_drivers[i].name)) { FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", fluid_midi_drivers[i].name); @@ -249,8 +220,11 @@ fluid_midi_driver_t* new_fluid_midi_driver(fluid_settings_t* settings, handle_mi } } - fluid_midi_driver_get_names(allnames, sizeof(allnames), ", "); - FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver. Valid drivers are: %s.", allnames); + allnames = fluid_settings_option_concat (settings, "midi.driver", NULL); + FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver. Valid drivers are: %s.", + allnames ? allnames : "ERROR"); + if (allnames) FLUID_FREE (allnames); + return NULL; } diff --git a/fluidsynth/src/fluid_settings.c b/fluidsynth/src/fluid_settings.c index 38bf0c37..db554d91 100644 --- a/fluidsynth/src/fluid_settings.c +++ b/fluidsynth/src/fluid_settings.c @@ -38,7 +38,7 @@ extern void fluid_file_renderer_settings (fluid_settings_t* settings); static void fluid_settings_init(fluid_settings_t* settings); 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); +static int fluid_settings_tokenize(const char *s, char *buf, char **ptr); /* Common structure to all settings nodes */ typedef struct { @@ -302,7 +302,7 @@ fluid_settings_init(fluid_settings_t* settings) } static int -fluid_settings_tokenize(char* s, char *buf, char** ptr) +fluid_settings_tokenize(const char *s, char *buf, char **ptr) { char *tokstr, *tok; int n = 0; @@ -341,7 +341,7 @@ fluid_settings_tokenize(char* s, char *buf, char** ptr) * @return 1 if the node exists, 0 otherwise */ static int -fluid_settings_get(fluid_settings_t* settings, char* name, +fluid_settings_get(fluid_settings_t* settings, const char *name, fluid_setting_node_t **value) { fluid_hashtable_t* table = settings; @@ -489,6 +489,9 @@ fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); + /* For now, all floating point settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, name, &node)) { @@ -521,7 +524,7 @@ fluid_settings_register_num(fluid_settings_t* settings, char* name, double def, } /** returns 1 if the value has been register correctly, zero - otherwise */ + otherwise. */ int fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, int min, int max, int hints, @@ -533,6 +536,9 @@ fluid_settings_register_int(fluid_settings_t* settings, char* name, int def, fluid_return_val_if_fail (settings != NULL, 0); fluid_return_val_if_fail (name != NULL, 0); + /* For now, all integer settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + fluid_rec_mutex_lock (settings->mutex); if (!fluid_settings_get(settings, name, &node)) { @@ -1399,6 +1405,80 @@ fluid_settings_option_count (fluid_settings_t *settings, char *name) return (count); } +/** + * Concatenate options for a string setting together with a separator between. + * @param settings Settings object + * @param name Settings name + * @param separator String to use between options (NULL to use ", ") + * @return Newly allocated string or NULL on error (out of memory, not a valid + * setting \a name or not a string setting). Free the string when finished with it. + * @since 1.1.0 + */ +char * +fluid_settings_option_concat (fluid_settings_t *settings, const char *name, + const char *separator) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + fluid_list_t *p, *newlist = NULL; + int count, len; + char *str, *option; + + fluid_return_val_if_fail (settings != NULL, NULL); + fluid_return_val_if_fail (name != NULL, NULL); + + if (!separator) separator = ", "; + + fluid_rec_mutex_lock (settings->mutex); /* ++ lock */ + + if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ + return (NULL); + } + + setting = (fluid_str_setting_t*)node; + + /* Duplicate option list, count options and get total string length */ + for (p = setting->options, count = 0, len = 0; p; p = p->next, count++) + { + option = fluid_list_get (p); + + if (option) + { + newlist = fluid_list_append (newlist, option); + len += strlen (option); + } + } + + if (count > 1) len += (count - 1) * strlen (separator); + len++; /* For terminator */ + + /* Sort by name */ + newlist = fluid_list_sort (newlist, fluid_list_str_compare_func); + + str = FLUID_MALLOC (len); + str[0] = 0; + + if (str) + { + for (p = newlist; p; p = p->next) + { + option = fluid_list_get (p); + strcat (str, option); + if (p->next) strcat (str, separator); + } + } + + fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */ + + delete_fluid_list (newlist); + + if (!str) FLUID_LOG (FLUID_ERR, "Out of memory"); + + return (str); +} + /* Structure passed to fluid_settings_foreach_iter recursive function */ typedef struct { diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index 1d6730d7..117d96b8 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -133,26 +133,6 @@ static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id); fluid_sfloader_t* new_fluid_defsfloader(void); -/************************************************************************ - * - * These functions were added after the v1.0 API freeze. They are not - * in synth.h. They should be added as soon as a new development - * version is started. - * - ************************************************************************/ - -int fluid_synth_program_select2(fluid_synth_t* synth, - int chan, - char* sfont_name, - unsigned int bank_num, - unsigned int preset_num); - -fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name); - -int fluid_synth_set_gen2(fluid_synth_t* synth, int chan, - int param, float value, - int absolute, int normalized); - /*************************************************************** * @@ -2456,10 +2436,12 @@ fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id * @param bank_num MIDI bank number * @param preset_num MIDI program number * @return FLUID_OK on success, FLUID_FAILED otherwise + * @since 1.1.0 */ int -fluid_synth_program_select2(fluid_synth_t* synth, int chan, char* sfont_name, - unsigned int bank_num, unsigned int preset_num) +fluid_synth_program_select_by_sfont_name (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; @@ -4063,6 +4045,7 @@ fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id) * @param synth FluidSynth instance * @param name Name of SoundFont * @return SoundFont instance or NULL if invalid name + * @since 1.1.0 * * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for * the duration of use of the returned pointer. @@ -5449,10 +5432,11 @@ fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float valu * @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, + * @param normalized 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 + * @since 1.1.0 * * 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 diff --git a/fluidsynth/src/fluidsynth.c b/fluidsynth/src/fluidsynth.c index 834d3591..3adf91b0 100644 --- a/fluidsynth/src/fluidsynth.c +++ b/fluidsynth/src/fluidsynth.c @@ -54,7 +54,7 @@ #endif void print_usage(void); -void print_help(void); +void print_help(fluid_settings_t *settings); void print_welcome(void); static fluid_cmd_handler_t* newclient(void* data, char* addr); @@ -443,7 +443,7 @@ int main(int argc, char** argv) fluid_settings_setnum(settings, "synth.gain", atof(optarg)); break; case 'h': - print_help(); + print_help(settings); break; case 'i': interactive = 0; @@ -831,17 +831,20 @@ print_welcome() * print_help */ void -print_help() +print_help (fluid_settings_t *settings) { - char allnames[256]; + char *audio_options; + char *midi_options; + + audio_options = fluid_settings_option_concat (settings, "audio.driver", NULL); + midi_options = fluid_settings_option_concat (settings, "midi.driver", NULL); printf("Usage: \n"); printf(" fluidsynth [options] [soundfonts] [midifiles]\n"); printf("Possible options:\n"); - fluid_audio_driver_get_names(allnames, sizeof(allnames), ", "); printf(" -a, --audio-driver=[label]\n" " The name of the audio driver to use.\n" - " Valid values: %s\n", allnames); + " Valid values: %s\n", audio_options ? audio_options : "ERROR"); printf(" -c, --audio-bufcount=[count]\n" " Number of audio buffers\n"); printf(" -C, --chorus\n" @@ -872,10 +875,9 @@ print_help() #endif printf(" -L, --audio-channels=[num]\n" " The number of stereo audio channels [default = 1]\n"); - fluid_midi_driver_get_names(allnames, sizeof(allnames), ", "); printf(" -m, --midi-driver=[label]\n" " The name of the midi driver to use.\n" - " Valid values: %s\n", allnames); + " Valid values: %s\n", midi_options ? midi_options : "ERROR"); printf(" -n, --no-midi-in\n" " Don't create a midi driver to read MIDI input events [default = yes]\n"); printf(" -o\n" @@ -898,5 +900,11 @@ print_help() " Show version of program\n"); printf(" -z, --audio-bufsize=[size]\n" " Size of each audio buffer\n"); + + if (audio_options) FLUID_FREE (audio_options); + if (midi_options) FLUID_FREE (midi_options); + + delete_fluid_settings (settings); + exit(0); }