diff --git a/CMakeLists.txt b/CMakeLists.txt index 318c3d9a..ff1f9e15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,9 +266,11 @@ unset ( WITH_PROFILING CACHE ) if ( enable-profiling ) set ( WITH_PROFILING 1 ) if ( CMAKE_C_COMPILER_ID STREQUAL "Clang" ) - set ( OPT_FLAGS "-Rpass=loop-vectorize -Rpass-analysis=loop-vectorize" ) + set ( OPT_FLAGS "-Rpass=loop-vectorize -Rpass-analysis=loop-vectorize -Wpadded" ) elseif ( CMAKE_C_COMPILER_ID STREQUAL "Intel" ) set ( OPT_FLAGS "-qopt-report=3" ) + elseif ( CMAKE_C_COMPILER_ID STREQUAL "GNU" ) + set ( OPT_FLAGS "-Wpadded" ) endif ( ) set ( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${OPT_FLAGS}" ) diff --git a/TODO b/TODO index 7195e6af..347200d5 100644 --- a/TODO +++ b/TODO @@ -66,7 +66,6 @@ FluidSynth Next Generation Top of the list - Use FIFOs to send events to the audio thread -- Redo sfloader api using "interface" api - 3D audio output MIDI player diff --git a/doc/fluidsynth-v11-devdoc.txt b/doc/fluidsynth-v11-devdoc.txt index 0dcddb6c..1d3264bd 100644 --- a/doc/fluidsynth-v11-devdoc.txt +++ b/doc/fluidsynth-v11-devdoc.txt @@ -112,11 +112,12 @@ Changes in FluidSynth 2.0.0 concerning developers: - add individual reverb setters: fluid_synth_set_reverb_roomsize(), fluid_synth_set_reverb_damp(), fluid_synth_set_reverb_width(), fluid_synth_set_reverb_level() - add individual chorus setters: fluid_synth_set_chorus_nr(), fluid_synth_set_chorus_level(), fluid_synth_set_chorus_speed(), fluid_synth_set_chorus_depth(), fluid_synth_set_chorus_type() - introduce a separate data type for sequencer client IDs: #fluid_seq_id_t -- add file callback struct to _fluid_sfloader_t and expose new_fluid_defsfloader() to enable soundfont loading from memory ( see fluid_sfload_mem.c ) - add seek support to midi-player, see fluid_player_seek() - expose functions to manipulate the ladspa effects unit (see ladspa.h) - add support for text and lyrics midi events, see fluid_midi_event_set_lyrics() and fluid_midi_event_set_text() -- add 24 bit sample support, see _fluid_sample_t::data24 +- complete rewrite of the soundfont loader API, see sfont.h + - support for 24 bit audio samples, see fluid_sample_set_sound_data() + - expose new_fluid_defsfloader() to support loading soundfonts from memory, see fluid_sfloader_set_callbacks() and fluidsynth_sfload_mem.c - add an additional general-purpose IIR filter, see fluid_synth_set_custom_filter() - add a custom sinusoidal modulator mapping function, see #FLUID_MOD_SIN diff --git a/doc/fluidsynth_sfload_mem.c b/doc/fluidsynth_sfload_mem.c index b77955f4..caa2a674 100644 --- a/doc/fluidsynth_sfload_mem.c +++ b/doc/fluidsynth_sfload_mem.c @@ -46,15 +46,6 @@ long my_tell(void * handle) return 0; } -fluid_file_callbacks_t my_cb = -{ - .fopen = my_open, - .fread = my_read, - .fseek = my_seek, - .fclose = my_close, - .ftell = my_tell -}; - int main() { int err = 0; @@ -63,7 +54,12 @@ int main() fluid_synth_t* synth = new_fluid_synth(settings); fluid_sfloader_t* my_sfloader = new_fluid_defsfloader(settings); - my_sfloader->file_callbacks = &my_cb; + fluid_sfloader_set_callbacks(my_sfloader, + my_open, + my_read, + my_seek, + my_tell, + my_close); fluid_synth_add_sfloader(synth, my_sfloader); diff --git a/include/fluidsynth/mod.h b/include/fluidsynth/mod.h index 08dfebe8..2449fdb1 100644 --- a/include/fluidsynth/mod.h +++ b/include/fluidsynth/mod.h @@ -71,6 +71,7 @@ enum fluid_mod_src FLUIDSYNTH_API fluid_mod_t* new_fluid_mod(void); FLUIDSYNTH_API void delete_fluid_mod(fluid_mod_t * mod); +FLUIDSYNTH_API size_t fluid_mod_sizeof(void); FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags); FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags); diff --git a/include/fluidsynth/ramsfont.h b/include/fluidsynth/ramsfont.h index 3fef94ec..2035ed1d 100644 --- a/include/fluidsynth/ramsfont.h +++ b/include/fluidsynth/ramsfont.h @@ -57,12 +57,6 @@ int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num, fluid_sample_t* sample, int on, float loopstart, float loopend); -FLUIDSYNTH_API fluid_sample_t* new_fluid_ramsample(void); -FLUIDSYNTH_API void delete_fluid_ramsample(fluid_sample_t* sample); -FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t* sample, const char *name); -FLUIDSYNTH_API -int fluid_sample_set_sound_data(fluid_sample_t* sample, short *data, - unsigned int nbframes, short copy_data, int rootkey); #ifdef __cplusplus diff --git a/include/fluidsynth/sfont.h b/include/fluidsynth/sfont.h index 59dfda64..2e0321de 100644 --- a/include/fluidsynth/sfont.h +++ b/include/fluidsynth/sfont.h @@ -83,239 +83,199 @@ enum fluid_sample_type FLUID_SAMPLETYPE_ROM = 0x8000 /**< Indicates ROM samples, causes sample to be ignored */ }; -/** - * SoundFont loader structure. - */ -struct _fluid_sfloader_t { - void* data; /**< User defined data pointer used by _fluid_sfloader_t::load() */ - - /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */ - const fluid_file_callbacks_t* file_callbacks; - - /** - * The free method should free the memory allocated for this loader instance in - * addition to any private data. - * @param loader SoundFont loader - */ - void (*free)(fluid_sfloader_t* loader); - - /** - * Method to load an instrument file (does not actually need to be a real file name, - * could be another type of string identifier that the \a loader understands). - * @param loader SoundFont loader - * @param filename File name or other string identifier - * @return The loaded instrument file (SoundFont) or NULL if an error occured. - */ - fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename); -}; /** - * File callback structure to enable custom soundfont loading (e.g. from memory). + * Method to load an instrument file (does not actually need to be a real file name, + * could be another type of string identifier that the \a loader understands). + * @param loader SoundFont loader + * @param filename File name or other string identifier + * @return The loaded instrument file (SoundFont) or NULL if an error occured. */ -struct _fluid_file_callbacks_t { - /** - * Opens the file or memory indicated by \c filename in binary read mode. - * \c filename matches the one provided during the fluid_synth_sfload() call. - * - * @return returns a file handle on success, NULL otherwise - */ - void * (* fopen )(const char * filename); +typedef fluid_sfont_t* (*fluid_sfloader_load_t)(fluid_sfloader_t* loader, const char* filename); - /** - * Reads \c count bytes to the specified buffer \c buf. - * - * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else #FLUID_FAILED - */ - int (* fread )(void *buf, int count, void * handle); +/** + * The free method should free the memory allocated for a fluid_sfloader_t instance in + * addition to any private data. Any custom user provided cleanup function must ultimately call + * delete_fluid_sfloader() to ensure proper cleanup of the #fluid_sfloader_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfloader() is sufficient. + * @param loader SoundFont loader + */ +typedef void (*fluid_sfloader_free_t)(fluid_sfloader_t* loader); - /** - * Same purpose and behaviour as fseek. - * - * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END - * - * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise */ - int (* fseek )(void * handle, long offset, int origin); - - /** - * Closes the handle and frees used ressources. - * - * @return returns #FLUID_OK on success, #FLUID_FAILED on error */ - int (* fclose)(void * handle); - - /** @return returns current file offset or #FLUID_FAILED on error */ - long (* ftell )(void * handle); -}; +FLUIDSYNTH_API fluid_sfloader_t* new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free); +FLUIDSYNTH_API void delete_fluid_sfloader(fluid_sfloader_t* loader); FLUIDSYNTH_API fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings); /** - * Virtual SoundFont instance structure. + * Opens the file or memory indicated by \c filename in binary read mode. + * \c filename matches the string provided during the fluid_synth_sfload() call. + * + * @return returns a file handle on success, NULL otherwise */ -struct _fluid_sfont_t { - void* data; /**< User defined data */ - unsigned int id; /**< SoundFont ID */ +typedef void * (* fluid_sfloader_callback_open_t )(const char * filename); - /** - * Method to free a virtual SoundFont bank. - * @param sfont Virtual SoundFont to free. - * @return Should return 0 when it was able to free all resources or non-zero - * if some of the samples could not be freed because they are still in use, - * in which case the free will be tried again later, until success. - */ - int (*free)(fluid_sfont_t* sfont); +/** + * Reads \c count bytes to the specified buffer \c buf. + * + * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else returns #FLUID_FAILED and leaves \a buf unmodified. + */ +typedef int (* fluid_sfloader_callback_read_t )(void *buf, int count, void * handle); - /** - * Method to return the name of a virtual SoundFont. - * @param sfont Virtual SoundFont - * @return The name of the virtual SoundFont. - */ - const char* (*get_name)(fluid_sfont_t* sfont); +/** + * Same purpose and behaviour as fseek. + * + * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END + * + * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise + */ +typedef int (* fluid_sfloader_callback_seek_t )(void * handle, long offset, int origin); - /** - * Get a virtual SoundFont preset by bank and program numbers. - * @param sfont Virtual SoundFont - * @param bank MIDI bank number (0-16384) - * @param prenum MIDI preset number (0-127) - * @return Should return an allocated virtual preset or NULL if it could not - * be found. - */ - fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); +/** + * Closes the handle returned by #fluid_sfloader_callback_open and frees used ressources. + * + * @return returns #FLUID_OK on success, #FLUID_FAILED on error + */ +typedef int (* fluid_sfloader_callback_close_t )(void * handle); - /** - * Start virtual SoundFont preset iteration method. - * @param sfont Virtual SoundFont - * - * Starts/re-starts virtual preset iteration in a SoundFont. - */ - void (*iteration_start)(fluid_sfont_t* sfont); +/** @return returns current file offset or #FLUID_FAILED on error */ +typedef long (* fluid_sfloader_callback_tell_t )(void * handle); + + +FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t* loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close); + +FLUIDSYNTH_API int fluid_sfloader_set_data(fluid_sfloader_t* loader, void* data); +FLUIDSYNTH_API void* fluid_sfloader_get_data(fluid_sfloader_t* loader); - /** - * Virtual SoundFont preset iteration function. - * @param sfont Virtual SoundFont - * @param preset Caller supplied preset to fill in with current preset information - * @return 0 when no more presets are available, 1 otherwise - * - * Should store preset information to the caller supplied \a preset structure - * and advance the internal iteration state to the next preset for subsequent - * calls. - */ - int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset); -}; /** - * Virtual SoundFont preset. + * Method to return the name of a virtual SoundFont. + * @param sfont Virtual SoundFont + * @return The name of the virtual SoundFont. */ -struct _fluid_preset_t { - void* data; /**< User supplied data */ - fluid_sfont_t* sfont; /**< Parent virtual SoundFont */ - - /** - * Method to free a virtual SoundFont preset. - * @param preset Virtual SoundFont preset - * @return Should return 0 - */ - int (*free)(fluid_preset_t* preset); - - /** - * Method to get a virtual SoundFont preset name. - * @param preset Virtual SoundFont preset - * @return Should return the name of the preset. The returned string must be - * valid for the duration of the virtual preset (or the duration of the - * SoundFont, in the case of preset iteration). - */ - const char* (*get_name)(fluid_preset_t* preset); - - /** - * Method to get a virtual SoundFont preset MIDI bank number. - * @param preset Virtual SoundFont preset - * @param return The bank number of the preset - */ - int (*get_banknum)(fluid_preset_t* preset); - - /** - * Method to get a virtual SoundFont preset MIDI program number. - * @param preset Virtual SoundFont preset - * @param return The program number of the preset - */ - int (*get_num)(fluid_preset_t* preset); - - /** - * Method to handle a noteon event (synthesize the instrument). - * @param preset Virtual SoundFont preset - * @param synth Synthesizer instance - * @param chan MIDI channel number of the note on event - * @param key MIDI note number (0-127) - * @param vel MIDI velocity (0-127) - * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise - * - * This method may be called from within synthesis context and therefore - * should be as efficient as possible and not perform any operations considered - * bad for realtime audio output (memory allocations and other OS calls). - * - * Call fluid_synth_alloc_voice() for every sample that has - * to be played. fluid_synth_alloc_voice() expects a pointer to a - * #fluid_sample_t structure and returns a pointer to the opaque - * #fluid_voice_t structure. To set or increment the values of a - * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are - * finished initializing the voice call fluid_voice_start() to - * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices - * created will be started at the same time. - */ - int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); - - /** - * Virtual SoundFont preset notify method. - * @param preset Virtual SoundFont preset - * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED - * @param chan MIDI channel number - * @return Should return #FLUID_OK - * - * Implement this optional method if the preset needs to be notified about - * preset select and unselect events. - * - * This method may be called from within synthesis context and therefore - * should be as efficient as possible and not perform any operations considered - * bad for realtime audio output (memory allocations and other OS calls). - */ - int (*notify)(fluid_preset_t* preset, int reason, int chan); -}; +typedef const char* (*fluid_sfont_get_name_t)(fluid_sfont_t* sfont); + +/** + * Get a virtual SoundFont preset by bank and program numbers. + * @param sfont Virtual SoundFont + * @param bank MIDI bank number (0-16383) + * @param prenum MIDI preset number (0-127) + * @return Should return an allocated virtual preset or NULL if it could not + * be found. + */ +typedef fluid_preset_t* (*fluid_sfont_get_preset_t)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum); /** - * Virtual SoundFont sample. + * Method to free a virtual SoundFont bank. Any custom user provided cleanup function must ultimately call + * delete_fluid_sfont() to ensure proper cleanup of the #fluid_sfont_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfont() is sufficient. + * @param sfont Virtual SoundFont to free. + * @return Should return 0 when it was able to free all resources or non-zero + * if some of the samples could not be freed because they are still in use, + * in which case the free will be tried again later, until success. */ -struct _fluid_sample_t -{ - char name[21]; /**< Sample name */ - unsigned int start; /**< Start index */ - unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */ - unsigned int loopstart; /**< Loop start index */ - unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */ - unsigned int samplerate; /**< Sample rate */ - int origpitch; /**< Original pitch (MIDI note number, 0-127) */ - int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */ - int sampletype; /**< Specifies the type of this sample as indicated by the #fluid_sample_type enum */ - int valid; /**< Should be TRUE if sample data is valid, FALSE otherwise (in which case it will not be synthesized) */ - short* data; /**< Pointer to the sample's 16 bit PCM data */ - char* data24; /**< If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples */ +typedef int (*fluid_sfont_free_t)(fluid_sfont_t* sfont); - int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */ - double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */ - unsigned int refcount; /**< Count of voices using this sample */ +FLUIDSYNTH_API fluid_sfont_t* new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_free_t free); +FLUIDSYNTH_API int delete_fluid_sfont(fluid_sfont_t* sfont); - /** - * Implement this function to receive notification when sample is no longer used. - * @param sample Virtual SoundFont sample - * @param reason #FLUID_SAMPLE_DONE only currently - * @return Should return #FLUID_OK - */ - int (*notify)(fluid_sample_t* sample, int reason); +FLUIDSYNTH_API int fluid_sfont_set_data(fluid_sfont_t* sfont, void* data); +FLUIDSYNTH_API void* fluid_sfont_get_data(fluid_sfont_t* sfont); - void* userdata; /**< User defined data */ -}; +/** + * Method to get a virtual SoundFont preset name. + * @param preset Virtual SoundFont preset + * @return Should return the name of the preset. The returned string must be + * valid for the duration of the virtual preset (or the duration of the + * SoundFont, in the case of preset iteration). + */ +typedef const char* (*fluid_preset_get_name_t)(fluid_preset_t* preset); + +/** + * Method to get a virtual SoundFont preset MIDI bank number. + * @param preset Virtual SoundFont preset + * @param return The bank number of the preset + */ +typedef int (*fluid_preset_get_banknum_t)(fluid_preset_t* preset); + +/** + * Method to get a virtual SoundFont preset MIDI program number. + * @param preset Virtual SoundFont preset + * @param return The program number of the preset + */ +typedef int (*fluid_preset_get_num_t)(fluid_preset_t* preset); + +/** + * Method to handle a noteon event (synthesize the instrument). + * @param preset Virtual SoundFont preset + * @param synth Synthesizer instance + * @param chan MIDI channel number of the note on event + * @param key MIDI note number (0-127) + * @param vel MIDI velocity (0-127) + * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + * + * Call fluid_synth_alloc_voice() for every sample that has + * to be played. fluid_synth_alloc_voice() expects a pointer to a + * #fluid_sample_t structure and returns a pointer to the opaque + * #fluid_voice_t structure. To set or increment the values of a + * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are + * finished initializing the voice call fluid_voice_start() to + * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices + * created will be started at the same time. + */ +typedef int (*fluid_preset_noteon_t)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel); + +/** + * Method to free a virtual SoundFont preset. Any custom user provided cleanup function must ultimately call + * delete_fluid_preset() to ensure proper cleanup of the #fluid_preset_t struct. If no private data + * needs to be freed, setting this to delete_fluid_preset() is sufficient. + * @param preset Virtual SoundFont preset + * @return Should return 0 + */ +typedef void (*fluid_preset_free_t)(fluid_preset_t* preset); + +FLUIDSYNTH_API fluid_preset_t* new_fluid_preset(fluid_sfont_t* parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free); +FLUIDSYNTH_API void delete_fluid_preset(fluid_preset_t* preset); + +FLUIDSYNTH_API int fluid_preset_set_data(fluid_preset_t* preset, void* data); +FLUIDSYNTH_API void* fluid_preset_get_data(fluid_preset_t* preset); + + + +FLUIDSYNTH_API fluid_sample_t* new_fluid_sample(void); +FLUIDSYNTH_API void delete_fluid_sample(fluid_sample_t* sample); +FLUIDSYNTH_API size_t fluid_sample_sizeof(void); + +FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t* sample, const char *name); +FLUIDSYNTH_API int fluid_sample_set_sound_data (fluid_sample_t* sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data); + +FLUIDSYNTH_API int fluid_sample_set_loop(fluid_sample_t* sample, unsigned int loop_start, unsigned int loop_end); +FLUIDSYNTH_API int fluid_sample_set_pitch(fluid_sample_t* sample, int root_key, int fine_tune); #ifdef __cplusplus } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de985f09..c50de161 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -128,6 +128,7 @@ set ( libfluidsynth_SOURCES sfloader/fluid_ramsfont.c sfloader/fluid_ramsfont.h sfloader/fluid_sfont.h + sfloader/fluid_sfont.c rvoice/fluid_adsr_env.c rvoice/fluid_adsr_env.h rvoice/fluid_chorus.c diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 8d037be5..223baf00 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -23,7 +23,7 @@ #include "fluid_defsfont.h" -/* Todo: Get rid of that 'include' */ +#include "fluid_sfont.h" #include "fluid_sys.h" #if LIBSNDFILE_SUPPORT @@ -42,116 +42,59 @@ * SFONT LOADER */ -static void * default_fopen(const char * path) -{ - return FLUID_FOPEN(path, "rb"); -} - -static int default_fclose(void * handle) -{ - return FLUID_FCLOSE((FILE *)handle); -} - -static long default_ftell(void * handle) -{ - return FLUID_FTELL((FILE *)handle); -} - -static int safe_fread (void *buf, int count, void * fd) -{ - if (FLUID_FREAD(buf, count, 1, (FILE *)fd) != 1) - { - if (feof ((FILE *)fd)) - gerr (ErrEof, _("EOF while attemping to read %d bytes"), count); - else - FLUID_LOG (FLUID_ERR, _("File read failed")); - - return FLUID_FAILED; - } - return FLUID_OK; -} - -static int safe_fseek (void * fd, long ofs, int whence) -{ - if (FLUID_FSEEK((FILE *)fd, ofs, whence) != 0) { - FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence); - return FLUID_FAILED; - } - return FLUID_OK; -} - -static const fluid_file_callbacks_t def_file_callbacks = -{ - default_fopen, - safe_fread, - safe_fseek, - default_fclose, - default_ftell -}; - /** * Creates a default soundfont2 loader that can be used with fluid_synth_add_sfloader(). * By default every synth instance has an initial default soundfont loader instance. - * Calling this function is usually only necessary to load a soundfont from memory, by overriding \c fluid_sfloader_t::file_callbacks member. + * Calling this function is usually only necessary to load a soundfont from memory, by providing custom callback functions via fluid_sfloader_set_callbacks(). * * @param settings A settings instance obtained by new_fluid_settings() * @return A default soundfont2 loader struct */ fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings) { - fluid_sfloader_t* loader; - + fluid_sfloader_t* loader; fluid_return_val_if_fail(settings != NULL, NULL); - loader = FLUID_NEW(fluid_sfloader_t); - if (loader == NULL) { + loader = new_fluid_sfloader(fluid_defsfloader_load, delete_fluid_sfloader); + + if (loader == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } - loader->data = settings; - loader->file_callbacks = &def_file_callbacks; - loader->free = delete_fluid_defsfloader; - loader->load = fluid_defsfloader_load; - + fluid_sfloader_set_data(loader, settings); + return loader; } -void delete_fluid_defsfloader(fluid_sfloader_t* loader) -{ - fluid_return_if_fail(loader != NULL); - - FLUID_FREE(loader); -} - fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename) { fluid_defsfont_t* defsfont; fluid_sfont_t* sfont; - defsfont = new_fluid_defsfont(loader->data); + defsfont = new_fluid_defsfont(fluid_sfloader_get_data(loader)); if (defsfont == NULL) { return NULL; } - if (fluid_defsfont_load(defsfont, loader->file_callbacks, filename) == FLUID_FAILED) { + if (fluid_defsfont_load(defsfont, &loader->file_callbacks, filename) == FLUID_FAILED) { delete_fluid_defsfont(defsfont); return NULL; } - sfont = FLUID_NEW(fluid_sfont_t); - if (sfont == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); + sfont = new_fluid_sfont(fluid_defsfont_sfont_get_name, + fluid_defsfont_sfont_get_preset, + fluid_defsfont_sfont_delete); + if (sfont == NULL) + { return NULL; } - - sfont->data = defsfont; - sfont->free = fluid_defsfont_sfont_delete; - sfont->get_name = fluid_defsfont_sfont_get_name; - sfont->get_preset = fluid_defsfont_sfont_get_preset; - sfont->iteration_start = fluid_defsfont_sfont_iteration_start; - sfont->iteration_next = fluid_defsfont_sfont_iteration_next; + + fluid_sfont_set_iteration_start(sfont, fluid_defsfont_sfont_iteration_start); + fluid_sfont_set_iteration_next(sfont, fluid_defsfont_sfont_iteration_next); + fluid_sfont_set_data(sfont, defsfont); return sfont; } @@ -165,16 +108,16 @@ fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* file int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont) { - if (delete_fluid_defsfont(sfont->data) != FLUID_OK) { + if (delete_fluid_defsfont(fluid_sfont_get_data(sfont)) != FLUID_OK) { return -1; } - FLUID_FREE(sfont); + delete_fluid_sfont(sfont); return 0; } const char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont) { - return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data); + return fluid_defsfont_get_name(fluid_sfont_get_data(sfont)); } fluid_preset_t* @@ -182,7 +125,7 @@ fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigne { fluid_preset_t* preset = NULL; fluid_defpreset_t* defpreset; - fluid_defsfont_t* defsfont = sfont->data; + fluid_defsfont_t* defsfont = fluid_sfont_get_data(sfont); defpreset = fluid_defsfont_get_preset(defsfont, bank, prenum); @@ -215,7 +158,7 @@ fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigne void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont) { - fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data); + fluid_defsfont_iteration_start(fluid_sfont_get_data(sfont)); } int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) @@ -227,12 +170,12 @@ int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* pr preset->noteon = fluid_defpreset_preset_noteon; preset->notify = NULL; - return fluid_defsfont_iteration_next((fluid_defsfont_t*) sfont->data, preset); + return fluid_defsfont_iteration_next(fluid_sfont_get_data(sfont), preset); } -int fluid_defpreset_preset_delete(fluid_preset_t* preset) +void fluid_defpreset_preset_delete(fluid_preset_t* preset) { - fluid_defpreset_t* defpreset = preset ? preset->data : NULL; + fluid_defpreset_t* defpreset = fluid_preset_get_data(preset); fluid_defsfont_t* sfont = defpreset ? defpreset->sfont : NULL; if (sfont && sfont->preset_stack_size < sfont->preset_stack_capacity) { @@ -240,30 +183,28 @@ int fluid_defpreset_preset_delete(fluid_preset_t* preset) sfont->preset_stack_size++; } else - FLUID_FREE(preset); - - return 0; + delete_fluid_preset(preset); } const char* fluid_defpreset_preset_get_name(fluid_preset_t* preset) { - return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data); + return fluid_defpreset_get_name(fluid_preset_get_data(preset)); } int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset) { - return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data); + return fluid_defpreset_get_banknum(fluid_preset_get_data(preset)); } int fluid_defpreset_preset_get_num(fluid_preset_t* preset) { - return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data); + return fluid_defpreset_get_num(fluid_preset_get_data(preset)); } int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel) { - return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel); + return fluid_defpreset_noteon(fluid_preset_get_data(preset), synth, chan, key, vel); } @@ -1884,44 +1825,6 @@ fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel) * SAMPLE */ -/* - * new_fluid_sample - */ -fluid_sample_t* -new_fluid_sample() -{ - fluid_sample_t* sample = NULL; - - sample = FLUID_NEW(fluid_sample_t); - if (sample == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - memset(sample, 0, sizeof(fluid_sample_t)); - sample->valid = 1; - - return sample; -} - -/* - * delete_fluid_sample - */ -void -delete_fluid_sample(fluid_sample_t* sample) -{ - fluid_return_if_fail(sample != NULL); - - if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) - { -#if LIBSNDFILE_SUPPORT - FLUID_FREE(sample->data); -#endif - } - - FLUID_FREE(sample); -} - /* * fluid_sample_in_rom */ @@ -2064,6 +1967,7 @@ fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defs // point sample data to uncompressed data stream sample->data = sampledata_ogg; + sample->auto_free = TRUE; sample->start = 0; sample->end = sfinfo.frames - 1; @@ -2115,6 +2019,8 @@ fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defs /* sample->loopend = sample->end - 8; */ /* } */ } + + sample->valid = TRUE; return FLUID_OK; } diff --git a/src/sfloader/fluid_defsfont.h b/src/sfloader/fluid_defsfont.h index 23e167fa..c1d1cb6d 100644 --- a/src/sfloader/fluid_defsfont.h +++ b/src/sfloader/fluid_defsfont.h @@ -351,7 +351,6 @@ typedef struct _fluid_inst_zone_t fluid_inst_zone_t; */ -void delete_fluid_defsfloader(fluid_sfloader_t* loader); fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename); @@ -362,7 +361,7 @@ void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont); int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); -int fluid_defpreset_preset_delete(fluid_preset_t* preset); +void fluid_defpreset_preset_delete(fluid_preset_t* preset); const char* fluid_defpreset_preset_get_name(fluid_preset_t* preset); int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset); int fluid_defpreset_preset_get_num(fluid_preset_t* preset); @@ -500,8 +499,6 @@ fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone); -fluid_sample_t* new_fluid_sample(void); -void delete_fluid_sample(fluid_sample_t* sample); int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont); int fluid_sample_in_rom(fluid_sample_t* sample); diff --git a/src/sfloader/fluid_ramsfont.c b/src/sfloader/fluid_ramsfont.c index 15783649..c796896b 100644 --- a/src/sfloader/fluid_ramsfont.c +++ b/src/sfloader/fluid_ramsfont.c @@ -22,8 +22,6 @@ #include "fluid_sys.h" #include "fluid_synth.h" -/* thenumber of samples before the start and after the end */ -#define SAMPLE_LOOP_MARGIN 8 /* Prototypes */ static int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont); @@ -34,7 +32,6 @@ static fluid_preset_t *fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, static void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont); static int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset); -static int fluid_rampreset_preset_delete(fluid_preset_t* preset); static const char *fluid_rampreset_preset_get_name(fluid_preset_t* preset); static int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset); static int fluid_rampreset_preset_get_num(fluid_preset_t* preset); @@ -94,21 +91,19 @@ fluid_ramsfont_create_sfont() if (ramsfont == NULL) { return NULL; } - - sfont = FLUID_NEW(fluid_sfont_t); - if (sfont == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - delete_fluid_ramsfont(ramsfont); + + sfont = new_fluid_sfont(fluid_ramsfont_sfont_get_name, + fluid_ramsfont_sfont_get_preset, + fluid_ramsfont_sfont_delete); + if (sfont == NULL) + { return NULL; } - - sfont->data = ramsfont; - sfont->free = fluid_ramsfont_sfont_delete; - sfont->get_name = fluid_ramsfont_sfont_get_name; - sfont->get_preset = fluid_ramsfont_sfont_get_preset; - sfont->iteration_start = fluid_ramsfont_sfont_iteration_start; - sfont->iteration_next = fluid_ramsfont_sfont_iteration_next; - + + fluid_sfont_set_iteration_start(sfont, fluid_ramsfont_sfont_iteration_start); + fluid_sfont_set_iteration_next(sfont, fluid_ramsfont_sfont_iteration_next); + fluid_sfont_set_data(sfont, ramsfont); + return sfont; } @@ -118,7 +113,7 @@ fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont) { if (delete_fluid_ramsfont(sfont->data) != 0) return -1; - FLUID_FREE(sfont); + delete_fluid_sfont(sfont); return 0; } @@ -142,20 +137,16 @@ fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigne return NULL; } - preset = FLUID_NEW(fluid_preset_t); + preset = new_fluid_preset(sfont, + fluid_rampreset_preset_get_name, + fluid_rampreset_preset_get_banknum, + fluid_rampreset_preset_get_num, + fluid_rampreset_preset_noteon, + delete_fluid_preset); /* TODO: free modulators */ if (preset == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } - - preset->sfont = sfont; - preset->data = rampreset; - preset->free = fluid_rampreset_preset_delete; - preset->get_name = fluid_rampreset_preset_get_name; - preset->get_banknum = fluid_rampreset_preset_get_banknum; - preset->get_num = fluid_rampreset_preset_get_num; - preset->noteon = fluid_rampreset_preset_noteon; - preset->notify = NULL; + fluid_preset_set_data(preset, rampreset); return preset; } @@ -171,7 +162,7 @@ fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont) static int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset) { - preset->free = fluid_rampreset_preset_delete; + preset->free = delete_fluid_preset; preset->get_name = fluid_rampreset_preset_get_name; preset->get_banknum = fluid_rampreset_preset_get_banknum; preset->get_num = fluid_rampreset_preset_get_num; @@ -181,17 +172,6 @@ fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset return fluid_ramsfont_iteration_next((fluid_ramsfont_t*) sfont->data, preset); } -/* RAM SoundFont loader delete preset method */ -static int -fluid_rampreset_preset_delete(fluid_preset_t* preset) -{ - FLUID_FREE(preset); - -/* TODO: free modulators */ - - return 0; -} - /* RAM SoundFont loader get preset name method */ static const char * fluid_rampreset_preset_get_name(fluid_preset_t* preset) @@ -264,7 +244,7 @@ delete_fluid_ramsfont (fluid_ramsfont_t* sfont) for (list = sfont->sample; list; list = fluid_list_next(list)) { /* in ram soundfonts, the samples hold their data : so we should free it ourselves */ fluid_sample_t* sam = (fluid_sample_t*)fluid_list_get(list); - delete_fluid_ramsample(sam); + delete_fluid_sample(sam); } if (sfont->sample) { @@ -1096,128 +1076,3 @@ fluid_rampreset_noteon (fluid_rampreset_t* preset, fluid_synth_t* synth, int cha return FLUID_OK; } - - - -/*************************************************************** - * - * SAMPLE - */ - - -/** - * Set the name of a RAM SoundFont sample. - * @param sample RAM SoundFont sample - * @param name Name to assign to sample (20 chars in length, 0 terminated) - * @return #FLUID_OK - */ -int -fluid_sample_set_name(fluid_sample_t* sample, const char *name) -{ - FLUID_MEMCPY(sample->name, name, 20); - return FLUID_OK; -} - -/** - * Assign sample data to a RAM SoundFont sample. - * @param sample RAM SoundFont sample - * @param data Buffer containing 16 bit audio sample data - * @param nbframes Number of samples in \a data - * @param copy_data TRUE to copy the data, FALSE to use it directly - * @param rootkey Root MIDI note of sample (0-127) - * @return #FLUID_OK on success, #FLUID_FAILED otherwise - * - * WARNING: If \a copy_data is FALSE, data should have 8 unused frames at start - * and 8 unused frames at the end. - */ -int -fluid_sample_set_sound_data (fluid_sample_t* sample, short *data, - unsigned int nbframes, short copy_data, int rootkey) -{ - /* 16 bit mono 44.1KHz data in */ - /* in all cases, the sample has ownership of the data : it will release it in the end */ - unsigned int storedNbFrames; - - /* in case we already have some data */ - if (sample->data != NULL) { - FLUID_FREE(sample->data); - } - - if (copy_data) { - - /* nbframes should be >= 48 (SoundFont specs) */ - storedNbFrames = nbframes; - if (storedNbFrames < 48) storedNbFrames = 48; - - sample->data = FLUID_MALLOC(storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); - if (sample->data == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return FLUID_FAILED; - } - FLUID_MEMSET(sample->data, 0, storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN); - FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN, data, nbframes*2); - -#if 0 - /* this would do the fill of the margins */ - FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN + storedNbFrames*2, (char*)data, 2*SAMPLE_LOOP_MARGIN); - FLUID_MEMCPY((char*)(sample->data), (char*)data + nbframes*2 - 2*SAMPLE_LOOP_MARGIN, 2*SAMPLE_LOOP_MARGIN); -#endif - - /* pointers */ - /* all from the start of data */ - sample->start = SAMPLE_LOOP_MARGIN; - sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames; - } else { - /* we cannot assure the SAMPLE_LOOP_MARGIN */ - sample->data = data; - sample->start = 0; - sample->end = nbframes; - } - - /* only used as markers for the LOOP generators : set them on the first real frame */ - sample->loopstart = sample->start; - sample->loopend = sample->end; - - sample->samplerate = 44100; - sample->origpitch = rootkey; - sample->pitchadj = 0; - sample->sampletype = FLUID_SAMPLETYPE_MONO; - sample->valid = 1; - - return FLUID_OK; -} - -/** - * Create new RAM SoundFont sample. - * @return New RAM SoundFont sample or NULL if out of memory - */ -fluid_sample_t * -new_fluid_ramsample (void) -{ - /* same as new_fluid_sample. Only here so that it is exported */ - fluid_sample_t* sample = NULL; - - sample = FLUID_NEW(fluid_sample_t); - if (sample == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - return NULL; - } - - memset(sample, 0, sizeof(fluid_sample_t)); - - return sample; -} - -/** - * Delete a RAM SoundFont sample. - * @param sample Sample to delete - */ -void -delete_fluid_ramsample (fluid_sample_t* sample) -{ - fluid_return_if_fail(sample != NULL); - - /* same as delete_fluid_sample, plus frees the data */ - FLUID_FREE(sample->data); - FLUID_FREE(sample); -} diff --git a/src/sfloader/fluid_ramsfont.h b/src/sfloader/fluid_ramsfont.h index a6e2b4b0..94dc948d 100644 --- a/src/sfloader/fluid_ramsfont.h +++ b/src/sfloader/fluid_ramsfont.h @@ -26,6 +26,7 @@ #include "fluidsynth.h" #include "fluidsynth_priv.h" +#include "fluid_sfont.h" #include "fluid_defsfont.h" diff --git a/src/sfloader/fluid_sfont.c b/src/sfloader/fluid_sfont.c new file mode 100644 index 00000000..0f5bc943 --- /dev/null +++ b/src/sfloader/fluid_sfont.c @@ -0,0 +1,548 @@ +/* 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 Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sfont.h" +#include "fluid_sys.h" + +void * default_fopen(const char * path) +{ + return FLUID_FOPEN(path, "rb"); +} + +int default_fclose(void * handle) +{ + return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED; +} + +long default_ftell(void * handle) +{ + return FLUID_FTELL((FILE *)handle); +} + +int safe_fread (void *buf, int count, void * fd) +{ + if (FLUID_FREAD(buf, count, 1, (FILE *)fd) != 1) + { + if (feof ((FILE *)fd)) + FLUID_LOG (FLUID_ERR, _("EOF while attemping to read %d bytes"), count); + else + FLUID_LOG (FLUID_ERR, _("File read failed")); + + return FLUID_FAILED; + } + return FLUID_OK; +} + +int safe_fseek (void * fd, long ofs, int whence) +{ + if (FLUID_FSEEK((FILE *)fd, ofs, whence) != 0) { + FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence); + return FLUID_FAILED; + } + return FLUID_OK; +} + +/** + * Creates a new SoundFont loader. + * + * @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t). + * @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t). + * Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader(). + * + * @return the SoundFont loader instance on success, NULL otherwise. + */ +fluid_sfloader_t* new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free) +{ + fluid_sfloader_t *loader; + + fluid_return_val_if_fail(load != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + loader = FLUID_NEW(fluid_sfloader_t); + if (loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(loader, 0, sizeof(*loader)); + + loader->load = load; + loader->free = free; + fluid_sfloader_set_callbacks(loader, + default_fopen, + safe_fread, + safe_fseek, + default_ftell, + default_fclose); + + return loader; +} + +/** + * Frees a SoundFont loader created with new_fluid_sfloader(). + * + * @param loader The SoundFont loader instance to free. + */ +void delete_fluid_sfloader(fluid_sfloader_t* loader) +{ + fluid_return_if_fail(loader != NULL); + + FLUID_FREE(loader); +} + +/** + * Specify private data to be used by #fluid_sfloader_load_t. + * + * @param loader The SoundFont loader instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfloader_set_data(fluid_sfloader_t* loader, void* data) +{ + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + + loader->data = data; + return FLUID_OK; +} + +/** + * Obtain private data previously set with fluid_sfloader_set_data(). + * + * @param loader The SoundFont loader instance. + * @return The private data or NULL if none explicitly set before. + */ +void* fluid_sfloader_get_data(fluid_sfloader_t* loader) +{ + fluid_return_val_if_fail(loader != NULL, NULL); + + return loader->data; +} + +/** + * Set custom callbacks to be used upon soundfont loading. + * + * Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example. + * + * @param loader The SoundFont loader instance. + * @param open A function implementing #fluid_sfloader_callback_open_t. + * @param read A function implementing #fluid_sfloader_callback_read_t. + * @param seek A function implementing #fluid_sfloader_callback_seek_t. + * @param tell A function implementing #fluid_sfloader_callback_tell_t. + * @param close A function implementing #fluid_sfloader_callback_close_t. + * @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise. + */ +int fluid_sfloader_set_callbacks(fluid_sfloader_t* loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close) +{ + fluid_file_callbacks_t *cb; + + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + fluid_return_val_if_fail(open != NULL, FLUID_FAILED); + fluid_return_val_if_fail(read != NULL, FLUID_FAILED); + fluid_return_val_if_fail(seek != NULL, FLUID_FAILED); + fluid_return_val_if_fail(tell != NULL, FLUID_FAILED); + fluid_return_val_if_fail(close != NULL, FLUID_FAILED); + + cb = &loader->file_callbacks; + + cb->fopen = open; + cb->fread = read; + cb->fseek = seek; + cb->ftell = tell; + cb->fclose = close; + + return FLUID_OK; +} + +/** + * Creates a new virtual SoundFont instance structure. + * @param get_name A function implementing #fluid_sfont_get_name_t. + * @param get_preset A function implementing #fluid_sfont_get_preset_t. + * @param free A function implementing #fluid_sfont_free_t. + * @return The soundfont instance on success or NULL otherwise. + */ +fluid_sfont_t* new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_free_t free) +{ + fluid_sfont_t* sfont; + + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_preset != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + sfont = FLUID_NEW(fluid_sfont_t); + if (sfont == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(sfont, 0, sizeof(*sfont)); + + sfont->get_name = get_name; + sfont->get_preset = get_preset; + sfont->free = free; + + return sfont; +} + +/** + * Set private data to use with a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfont_set_data(fluid_sfont_t* sfont, void* data) +{ + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + + sfont->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The private data or NULL if none explicitly set before. + */ +void* fluid_sfont_get_data(fluid_sfont_t* sfont) +{ + fluid_return_val_if_fail(sfont != NULL, NULL); + + return sfont->data; +} + +/** + * @internal KISS! No need to expose this to public API currently. + */ +void fluid_sfont_set_iteration_start(fluid_sfont_t* sfont, fluid_sfont_iteration_start_t iter_start) +{ + sfont->iteration_start = iter_start; +} + +/** + * @internal KISS! No need to expose this to public API currently. + */ +void fluid_sfont_set_iteration_next(fluid_sfont_t* sfont, fluid_sfont_iteration_next_t iter_next) +{ + sfont->iteration_next = iter_next; +} + +/** + * Destroys a SoundFont instance created with new_fluid_sfont(). + * + * Implements #fluid_sfont_free_t. + * + * @param sfont The SoundFont instance to destroy. + * @return Always returns 0. + */ +int delete_fluid_sfont(fluid_sfont_t* sfont) +{ + fluid_return_val_if_fail(sfont != NULL, 0); + + FLUID_FREE(sfont); + return 0; +} + +/** + * Create a virtual SoundFont preset instance. + * + * @param parent_sfont The SoundFont instance this preset shall belong to + * @param get_name A function implementing #fluid_preset_get_name_t + * @param get_bank A function implementing #fluid_preset_get_banknum_t + * @param get_num A function implementing #fluid_preset_get_num_t + * @param noteon A function implementing #fluid_preset_noteon_t + * @param free A function implementing #fluid_preset_free_t + * @return The preset instance on success, NULL otherwise. + */ +fluid_preset_t* new_fluid_preset(fluid_sfont_t* parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free) +{ + fluid_preset_t* preset; + + fluid_return_val_if_fail(parent_sfont != NULL, NULL); + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_bank != NULL, NULL); + fluid_return_val_if_fail(get_num != NULL, NULL); + fluid_return_val_if_fail(noteon != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + preset = FLUID_NEW(fluid_preset_t); + if (preset == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(preset, 0, sizeof(*preset)); + + preset->sfont = parent_sfont; + preset->get_name = get_name; + preset->get_banknum = get_bank; + preset->get_num = get_num; + preset->noteon = noteon; + preset->free = free; + + return preset; +} + +/** + * Set private data to use with a SoundFont preset instance. + * + * @param preset The SoundFont preset instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_preset_set_data(fluid_preset_t* preset, void* data) +{ + fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); + + preset->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont preset instance. + * + * @param sfont The SoundFont preset instance. + * @return The private data or NULL if none explicitly set before. + */ +void* fluid_preset_get_data(fluid_preset_t* preset) +{ + fluid_return_val_if_fail(preset != NULL, NULL); + + return preset->data; +} + +/** + * Destroys a SoundFont preset instance created with new_fluid_preset(). + * + * Implements #fluid_preset_free_t. + * + * @param preset The SoundFont preset instance to destroy. + */ +void delete_fluid_preset(fluid_preset_t* preset) +{ + fluid_return_if_fail(preset != NULL); + + FLUID_FREE(preset); +} + +/** + * Create a new sample instance. + * @return The sample on success, NULL otherwise. + */ +fluid_sample_t* +new_fluid_sample() +{ + fluid_sample_t* sample = NULL; + + sample = FLUID_NEW(fluid_sample_t); + if (sample == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(sample, 0, sizeof(*sample)); + + return sample; +} + +/** + * Destroy a sample instance previously created with new_fluid_sample(). + * @param sample The sample to destroy. + */ +void +delete_fluid_sample(fluid_sample_t* sample) +{ + fluid_return_if_fail(sample != NULL); + + if (sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + } + + FLUID_FREE(sample); +} + +/** + * Returns the size of the fluid_sample_t structure. + * + * Useful in low latency scenarios e.g. to allocate a sample on the stack. + */ +size_t fluid_sample_sizeof() +{ + return sizeof(fluid_sample_t); +} + +/** + * Set the name of a SoundFont sample. + * @param sample SoundFont sample + * @param name Name to assign to sample (20 chars in length + zero terminator) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_sample_set_name(fluid_sample_t* sample, const char *name) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + FLUID_STRNCPY(sample->name, name, sizeof(sample->name)); + return FLUID_OK; +} + +/** + * Assign sample data to a SoundFont sample. + * @param sample SoundFont sample + * @param data Buffer containing 16 bit (mono-)audio sample data + * @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples + * @param nbframes Number of samples in \a data + * @param sample_rate Sampling rate of the sample data + * @param copy_data TRUE to copy the sample data (and automatically free it upon delete_fluid_sample()), FALSE to use it directly (and not free it) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note If \a copy_data is FALSE, data should have 8 unused frames at start + * and 8 unused frames at the end and \a nbframes should be >=48 + */ +int +fluid_sample_set_sound_data (fluid_sample_t* sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data + ) +{ + /* the number of samples before the start and after the end */ + #define SAMPLE_LOOP_MARGIN 8U + + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(nbframes == 0, FLUID_FAILED); + + /* in case we already have some data */ + if ((sample->data != NULL || sample->data24 != NULL) && sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + } + + if (copy_data) + { + unsigned int storedNbFrames; + + /* nbframes should be >= 48 (SoundFont specs) */ + storedNbFrames = nbframes; + if (storedNbFrames < 48) storedNbFrames = 48; + + storedNbFrames += 2*SAMPLE_LOOP_MARGIN; + + sample->data = FLUID_ARRAY(short, storedNbFrames); + if (sample->data == NULL) + { + goto error_rec; + } + FLUID_MEMSET(sample->data, 0, storedNbFrames); + FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes*sizeof(short)); + + if(data24 != NULL) + { + sample->data24 = FLUID_ARRAY(char, storedNbFrames); + if (sample->data24 == NULL) + { + goto error_rec; + } + FLUID_MEMSET(sample->data24, 0, storedNbFrames); + FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes*sizeof(char)); + } + + /* pointers */ + /* all from the start of data */ + sample->start = SAMPLE_LOOP_MARGIN; + sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames - 1; + } + else + { + /* we cannot assure the SAMPLE_LOOP_MARGIN */ + sample->data = data; + sample->data24 = data24; + sample->start = 0; + sample->end = nbframes - 1; + } + + sample->samplerate = sample_rate; + sample->sampletype = FLUID_SAMPLETYPE_MONO; + sample->valid = 1; + sample->auto_free = copy_data; + + return FLUID_OK; + +error_rec: + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + return FLUID_FAILED; + +#undef SAMPLE_LOOP_MARGIN +} + +/** + * Set the loop of a sample. + * + * @param loop_start Start sample index of the loop. + * @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played). + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_loop(fluid_sample_t* sample, unsigned int loop_start, unsigned int loop_end) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + + sample->loopstart = loop_start; + sample->loopend = loop_end; + + return FLUID_OK; +} + +/** + * Set the pitch of a sample. + * + * @param rootkey Root MIDI note of sample (0-127) + * @param fine_tune Fine tune in cents + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_pitch(fluid_sample_t* sample, int root_key, int fine_tune) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(0<=root_key && root_key<=127, FLUID_FAILED); + + sample->origpitch = root_key; + sample->pitchadj = fine_tune; + + return FLUID_OK; +} diff --git a/src/sfloader/fluid_sfont.h b/src/sfloader/fluid_sfont.h index 226d8d9a..2c83a156 100644 --- a/src/sfloader/fluid_sfont.h +++ b/src/sfloader/fluid_sfont.h @@ -22,6 +22,7 @@ #ifndef _PRIV_FLUID_SFONT_H #define _PRIV_FLUID_SFONT_H +#include "fluidsynth.h" /* * Utility macros to access soundfonts, presets, and samples @@ -31,22 +32,18 @@ #define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename) -#define delete_fluid_sfont(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) +#define fluid_sfont_delete_internal(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) #define fluid_sfont_get_id(_sf) ((_sf)->id) #define fluid_sfont_get_name(_sf) (*(_sf)->get_name)(_sf) #define fluid_sfont_get_preset(_sf,_bank,_prenum) (*(_sf)->get_preset)(_sf,_bank,_prenum) -#define fluid_sfont_iteration_start(_sf) (*(_sf)->iteration_start)(_sf) -#define fluid_sfont_iteration_next(_sf,_pr) (*(_sf)->iteration_next)(_sf,_pr) -#define fluid_sfont_get_data(_sf) (_sf)->data -#define fluid_sfont_set_data(_sf,_p) { (_sf)->data = (void*) (_p); } +#define fluid_sfont_iteration_start(_sf) { if((_sf) && (_sf)->iteration_start) (*(_sf)->iteration_start)(_sf); } +#define fluid_sfont_iteration_next(_sf,_pr) (((_sf) && (_sf)->iteration_start) ? (*(_sf)->iteration_next)(_sf,_pr) : 0) -#define delete_fluid_preset(_preset) \ +#define fluid_preset_delete_internal(_preset) \ { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }} -#define fluid_preset_get_data(_preset) (_preset)->data -#define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); } #define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset) #define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset) #define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset) @@ -67,4 +64,141 @@ +/** + * File callback structure to enable custom soundfont loading (e.g. from memory). + */ +struct _fluid_file_callbacks_t +{ + fluid_sfloader_callback_open_t fopen; + fluid_sfloader_callback_read_t fread; + fluid_sfloader_callback_seek_t fseek; + fluid_sfloader_callback_close_t fclose; + fluid_sfloader_callback_tell_t ftell; +}; + +/** + * SoundFont loader structure. + */ +struct _fluid_sfloader_t { + void* data; /**< User defined data pointer used by _fluid_sfloader_t::load() */ + + /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */ + fluid_file_callbacks_t file_callbacks; + + fluid_sfloader_free_t free; + + fluid_sfloader_load_t load; +}; + +/** + * Start virtual SoundFont preset iteration method. + * @param sfont Virtual SoundFont + * + * Starts/re-starts virtual preset iteration in a SoundFont. + */ +typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t* sfont); + +/** + * Virtual SoundFont preset iteration function. + * @param sfont Virtual SoundFont + * @param preset Caller supplied uninitialized buffer to fill in with current preset information + * @return 0 when no more presets are available, 1 otherwise + * + * Should store preset information to the caller supplied \a preset structure + * and advance the internal iteration state to the next preset for subsequent + * calls. + */ +typedef int (*fluid_sfont_iteration_next_t)(fluid_sfont_t* sfont, fluid_preset_t* preset); + +void fluid_sfont_set_iteration_start(fluid_sfont_t* sfont, fluid_sfont_iteration_start_t iter_start); +void fluid_sfont_set_iteration_next(fluid_sfont_t* sfont, fluid_sfont_iteration_next_t iter_next); + +/** + * Virtual SoundFont instance structure. + */ +struct _fluid_sfont_t { + void* data; /**< User defined data */ + unsigned int id; /**< SoundFont ID */ + + fluid_sfont_free_t free; + + fluid_sfont_get_name_t get_name; + + fluid_sfont_get_preset_t get_preset; + + fluid_sfont_iteration_start_t iteration_start; + + fluid_sfont_iteration_next_t iteration_next; +}; + +/** + * Virtual SoundFont preset. + */ +struct _fluid_preset_t { + void* data; /**< User supplied data */ + fluid_sfont_t* sfont; /**< Parent virtual SoundFont */ + + fluid_preset_free_t free; + + fluid_preset_get_name_t get_name; + + fluid_preset_get_banknum_t get_banknum; + + fluid_preset_get_num_t get_num; + + fluid_preset_noteon_t noteon; + + /** + * Virtual SoundFont preset notify method. + * @param preset Virtual SoundFont preset + * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED + * @param chan MIDI channel number + * @return Should return #FLUID_OK + * + * Implement this optional method if the preset needs to be notified about + * preset select and unselect events. + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + */ + int (*notify)(fluid_preset_t* preset, int reason, int chan); +}; + +/** + * Virtual SoundFont sample. + */ +struct _fluid_sample_t +{ + char name[21]; /**< Sample name */ + unsigned int start; /**< Start index */ + unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */ + unsigned int loopstart; /**< Loop start index */ + unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */ + unsigned int samplerate; /**< Sample rate */ + int origpitch; /**< Original pitch (MIDI note number, 0-127) */ + int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */ + int sampletype; /**< Specifies the type of this sample as indicated by the #fluid_sample_type enum */ + int valid; /**< Should be TRUE if sample data is valid, FALSE otherwise (in which case it will not be synthesized) */ + int auto_free; /**< TRUE if _fluid_sample_t::data and _fluid_sample_t::data24 should be freed upon sample destruction */ + short* data; /**< Pointer to the sample's 16 bit PCM data */ + char* data24; /**< If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples */ + + int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */ + double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */ + + unsigned int refcount; /**< Count of voices using this sample */ + + /** + * Implement this function to receive notification when sample is no longer used. + * @param sample Virtual SoundFont sample + * @param reason #FLUID_SAMPLE_DONE only currently + * @return Should return #FLUID_OK + */ + int (*notify)(fluid_sample_t* sample, int reason); + + void* userdata; /**< User defined data */ +}; + + #endif /* _PRIV_FLUID_SFONT_H */ diff --git a/src/synth/fluid_chan.c b/src/synth/fluid_chan.c index c307a87d..bc12005d 100644 --- a/src/synth/fluid_chan.c +++ b/src/synth/fluid_chan.c @@ -185,7 +185,7 @@ delete_fluid_channel(fluid_channel_t* chan) { fluid_return_if_fail(chan != NULL); - delete_fluid_preset (chan->preset); + fluid_preset_delete_internal (chan->preset); FLUID_FREE(chan); } @@ -207,7 +207,7 @@ fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset) if (chan->preset) { fluid_sfont_t *sfont; sfont = chan->preset->sfont; - delete_fluid_preset (chan->preset); + fluid_preset_delete_internal (chan->preset); fluid_synth_sfont_unref (chan->synth, sfont); /* -- unref preset's SoundFont */ } diff --git a/src/synth/fluid_mod.c b/src/synth/fluid_mod.c index d0e1112a..6185656f 100644 --- a/src/synth/fluid_mod.c +++ b/src/synth/fluid_mod.c @@ -442,6 +442,16 @@ delete_fluid_mod (fluid_mod_t *mod) FLUID_FREE(mod); } +/** + * Returns the size of the fluid_mod_t structure. + * + * Useful in low latency scenarios e.g. to allocate a modulator on the stack. + */ +size_t fluid_mod_sizeof() +{ + return sizeof(fluid_mod_t); +} + /** * Checks if two modulators are identical in sources, flags and destination. * @param mod1 First modulator diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 27347dcc..f437d7be 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -848,7 +848,7 @@ delete_fluid_synth(fluid_synth_t* synth) /* delete all the SoundFonts */ 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_sfont_delete_internal (sfont_info->sfont); FLUID_FREE (sfont_info); } @@ -3432,7 +3432,7 @@ fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets if (!sfont_info) { - delete_fluid_sfont (sfont); + fluid_sfont_delete_internal (sfont); FLUID_API_RETURN(FLUID_FAILED); } @@ -3537,7 +3537,7 @@ fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont) if (refcount == 0) /* No more references? - Attempt delete */ { - if (delete_fluid_sfont (sfont_info->sfont) == 0) /* SoundFont loader can block SoundFont unload */ + if (fluid_sfont_delete_internal (sfont_info->sfont) == 0) /* SoundFont loader can block SoundFont unload */ { FLUID_FREE (sfont_info); FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); @@ -3553,7 +3553,7 @@ 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) + if (fluid_sfont_delete_internal (sfont_info->sfont) == 0) { FLUID_FREE (sfont_info); FLUID_LOG (FLUID_DBG, "Unloaded SoundFont"); @@ -3612,7 +3612,7 @@ fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id) if (!sfont_info) { - delete_fluid_sfont (sfont); + fluid_sfont_delete_internal (sfont); FLUID_API_RETURN(FLUID_FAILED); }