diff --git a/libs/fluidsynth/README b/libs/fluidsynth/README index 153bab67a4..cdcb83a328 100644 --- a/libs/fluidsynth/README +++ b/libs/fluidsynth/README @@ -1,7 +1,7 @@ This is a stripped down version of fluidsynth (library only) from git://github.com/FluidSynth/fluidsynth.git -rev. v2.1.0-1-gb266cf2ab +rev. v2.2.0-41-gf55bc799 fluidsynth is licensed in terms of the LGPL-2+, see individual source files for (C) holders. diff --git a/libs/fluidsynth/config.h b/libs/fluidsynth/config.h index 7da00102b8..c89f988fce 100644 --- a/libs/fluidsynth/config.h +++ b/libs/fluidsynth/config.h @@ -2,9 +2,12 @@ #define CONFIG_H #define FLUIDSYNTH_VERSION_MAJOR 2 -#define FLUIDSYNTH_VERSION_MINOR 0 -#define FLUIDSYNTH_VERSION_MICRO 6 -#define FLUIDSYNTH_VERSION "2.0.6" +#define FLUIDSYNTH_VERSION_MINOR 2 +#define FLUIDSYNTH_VERSION_MICRO 0 +#define FLUIDSYNTH_VERSION "2.2.0" + +/* Version number of package */ +#define VERSION "2.2.0" /* Define to enable ALSA driver */ /* #undef ALSA_SUPPORT */ @@ -35,9 +38,6 @@ # define HAVE_ARPA_INET_H 1 #endif -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - /* Define to 1 if you have the header file. */ #define HAVE_ERRNO_H 1 @@ -47,42 +47,39 @@ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 -/* whether or not we are supporting ladcca */ -/* #undef HAVE_LADCCA */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IO_H */ /* whether or not we are supporting lash */ /* #undef HAVE_LASH */ -/* Define to 1 if you have the `dl' library (-ldl). */ -#define HAVE_LIBDL 1 - -/* Define to 1 if you have the `MidiShare' library (-lMidiShare). */ -/* #undef HAVE_LIBMIDISHARE */ - -/* Define to 1 if you have the `pthread' library (-lpthread). */ -#define HAVE_LIBPTHREAD TRUE +/* Define if systemd support is enabled */ +/* #undef SYSTEMD_SUPPORT */ /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_SOUNDCARD_H */ + /* Define to 1 if you have the header file. */ /* #undef HAVE_MACHINE_SOUNDCARD_H */ /* Define to 1 if you have the header file. */ #define HAVE_MATH_H 1 -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_MIDISHARE_H */ - /* Define to 1 if you have the header file. */ /* #undef HAVE_NETINET_IN_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_NETINET_TCP_H */ +/* Define if compiling the mixer with multi-thread support */ +/* #undef ENABLE_MIXER_THREADS */ + +/* Define if compiling with openMP to enable parallel audio rendering */ +/* #undef HAVE_OPENMP */ + /* Define to 1 if you have the header file. */ #define HAVE_PTHREAD_H 1 @@ -143,12 +140,27 @@ /* Define to 1 if you have the header file. */ /* #undef HAVE_GETOPT_H */ +/* Define to 1 if you have the inet_ntop() function. */ +/* #undef HAVE_INETNTOP */ + /* Define to enable JACK driver */ /* #undef JACK_SUPPORT */ /* Include the LADSPA Fx unit */ /* #undef LADSPA */ +/* Define to enable IPV6 support */ +/* #undef IPV6_SUPPORT */ + +/* Define to enable network support */ +/* #undef NETWORK_SUPPORT */ + +/* Defined when fluidsynth is build in an automated environment, where no MSVC++ Runtime Debug Assertion dialogs should pop up */ +/* #undef NO_GUI */ + +/* libinstpatch for DLS and GIG */ +/* #undef LIBINSTPATCH_SUPPORT */ + /* libsndfile has ogg vorbis support */ /* #undef LIBSNDFILE_HASVORBIS */ @@ -161,12 +173,15 @@ /* Define if using the MinGW32 environment */ /* #undef MINGW32 */ -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - /* Define to enable OSS driver */ /* #undef OSS_SUPPORT TRUE */ +/* Define to enable OPENSLES driver */ +/* #undef OPENSLES_SUPPORT */ + +/* Define to enable Oboe driver */ +/* #undef OBOE_SUPPORT */ + /* Name of package */ #define PACKAGE "fluidsynth" @@ -190,6 +205,20 @@ /* Define to enable PulseAudio driver */ /* #undef PULSE_SUPPORT */ +/* Define to enable DirectSound driver */ +/* #undef DSOUND_SUPPORT */ + +/* Define to enable Windows WASAPI driver */ +/* #undef WASAPI_SUPPORT */ + +/* Define to enable Windows WaveOut driver */ +/* #undef WAVEOUT_SUPPORT */ + +/* Define to enable Windows MIDI driver */ +/* #undef WINMIDI_SUPPORT */ + +/* Define to enable SDL2 audio driver */ +/* #undef SDL2_SUPPORT */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 @@ -197,9 +226,6 @@ /* Define to enable SIGFPE assertions */ /* #undef TRAP_ON_FPE */ -/* Version number of package */ -#define VERSION "2.0.6" - /* Define to do all DSP in single floating point precision */ #ifdef __arm__ # define WITH_FLOAT @@ -234,4 +260,22 @@ /* #undef inline */ #endif +/* Define to 1 if you have the sinf() function. */ +/* #undef HAVE_SINF */ + +/* Define to 1 if you have the cosf() function. */ +/* #undef HAVE_COSF */ + +/* Define to 1 if you have the fabsf() function. */ +/* #undef HAVE_FABSF */ + +/* Define to 1 if you have the powf() function. */ +/* #undef HAVE_POWF */ + +/* Define to 1 if you have the sqrtf() function. */ +/* #undef HAVE_SQRTF */ + +/* Define to 1 if you have the logf() function. */ +/* #undef HAVE_LOGF */ + #endif /* CONFIG_H */ diff --git a/libs/fluidsynth/fluidsynth/event.h b/libs/fluidsynth/fluidsynth/event.h index cbd1fa6a09..e50f974f5e 100644 --- a/libs/fluidsynth/fluidsynth/event.h +++ b/libs/fluidsynth/fluidsynth/event.h @@ -26,10 +26,12 @@ extern "C" { #endif /** - * @file event.h - * @brief Sequencer event functions and defines. + * @defgroup sequencer_events Sequencer Events + * @ingroup sequencer * - * Functions and constants for creating/processing sequencer events. + * Create, modify, query and destroy sequencer events. + * + * @{ */ /** @@ -46,7 +48,7 @@ enum fluid_seq_event_type FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */ FLUID_SEQ_PROGRAMSELECT, /**< Program select message */ FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ - FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was mispelled previously */ + FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was misspelled previously */ FLUID_SEQ_MODULATION, /**< Modulation controller event */ FLUID_SEQ_SUSTAIN, /**< Sustain controller event */ FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */ @@ -55,19 +57,21 @@ enum fluid_seq_event_type FLUID_SEQ_REVERBSEND, /**< Reverb send set event */ FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ FLUID_SEQ_TIMER, /**< Timer event (useful for giving a callback at a certain time) */ - FLUID_SEQ_ANYCONTROLCHANGE, /**< Any control change message (only internally used for remove_events) */ FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ FLUID_SEQ_KEYPRESSURE, /**< Polyphonic aftertouch event @since 2.0.0 */ FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */ FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ -#ifndef __DOXYGEN__ - FLUID_SEQ_LASTEVENT /**< @internal Defines the count of events enums @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ -#endif + FLUID_SEQ_SCALE, /**< Sets a new time scale for the sequencer @since 2.2.0 */ + FLUID_SEQ_LASTEVENT /**< @internal Defines the count of events enums @warning This symbol + is not part of the public API and ABI stability guarantee and + may change at any time! */ }; /* Event alloc/free */ +/** @startlifecycle{Sequencer Event} */ FLUIDSYNTH_API fluid_event_t *new_fluid_event(void); FLUIDSYNTH_API void delete_fluid_event(fluid_event_t *evt); +/** @endlifecycle */ /* Initializing events */ FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src); @@ -88,34 +92,32 @@ FLUIDSYNTH_API void fluid_event_all_notes_off(fluid_event_t *evt, int channel); /* Instrument selection */ FLUIDSYNTH_API void fluid_event_bank_select(fluid_event_t *evt, int channel, short bank_num); -FLUIDSYNTH_API void fluid_event_program_change(fluid_event_t *evt, int channel, short preset_num); +FLUIDSYNTH_API void fluid_event_program_change(fluid_event_t *evt, int channel, int preset_num); FLUIDSYNTH_API void fluid_event_program_select(fluid_event_t *evt, int channel, unsigned int sfont_id, short bank_num, short preset_num); /* Real-time generic instrument controllers */ FLUIDSYNTH_API -void fluid_event_control_change(fluid_event_t *evt, int channel, short control, short val); +void fluid_event_control_change(fluid_event_t *evt, int channel, short control, int val); /* Real-time instrument controllers shortcuts */ FLUIDSYNTH_API void fluid_event_pitch_bend(fluid_event_t *evt, int channel, int val); -FLUIDSYNTH_API void fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, short val); -FLUIDSYNTH_API void fluid_event_modulation(fluid_event_t *evt, int channel, short val); -FLUIDSYNTH_API void fluid_event_sustain(fluid_event_t *evt, int channel, short val); -FLUIDSYNTH_API void fluid_event_pan(fluid_event_t *evt, int channel, short val); -FLUIDSYNTH_API void fluid_event_volume(fluid_event_t *evt, int channel, short val); -FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t *evt, int channel, short val); -FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t *evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_modulation(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_sustain(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_pan(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_volume(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t *evt, int channel, int val); -FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, short val); -FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t *evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val); +FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val); FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t *evt); - -/* Only for removing events */ -FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_t *evt, int channel); - /* Only when unregistering clients */ FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t *evt); +FLUIDSYNTH_API void fluid_event_scale(fluid_event_t *evt, double new_scale); + /* Accessing event data */ FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t *evt); FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt); @@ -124,13 +126,15 @@ FLUIDSYNTH_API int fluid_event_get_channel(fluid_event_t *evt); FLUIDSYNTH_API short fluid_event_get_key(fluid_event_t *evt); FLUIDSYNTH_API short fluid_event_get_velocity(fluid_event_t *evt); FLUIDSYNTH_API short fluid_event_get_control(fluid_event_t *evt); -FLUIDSYNTH_API short fluid_event_get_value(fluid_event_t *evt); -FLUIDSYNTH_API short fluid_event_get_program(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_value(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_program(fluid_event_t *evt); FLUIDSYNTH_API void *fluid_event_get_data(fluid_event_t *evt); FLUIDSYNTH_API unsigned int fluid_event_get_duration(fluid_event_t *evt); FLUIDSYNTH_API short fluid_event_get_bank(fluid_event_t *evt); FLUIDSYNTH_API int fluid_event_get_pitch(fluid_event_t *evt); +FLUIDSYNTH_API double fluid_event_get_scale(fluid_event_t *evt); FLUIDSYNTH_API unsigned int fluid_event_get_sfont_id(fluid_event_t *evt); +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/gen.h b/libs/fluidsynth/fluidsynth/gen.h index 1f46fe2a99..13b07c0579 100644 --- a/libs/fluidsynth/fluidsynth/gen.h +++ b/libs/fluidsynth/fluidsynth/gen.h @@ -26,8 +26,12 @@ extern "C" { #endif /** - * @file gen.h - * @brief Functions and defines for SoundFont generator effects. + * @defgroup generators SoundFont Generators + * @ingroup soundfonts + * + * Functions and defines for SoundFont generator effects. + * + * @{ */ /** @@ -96,7 +100,7 @@ enum fluid_gen_type GEN_OVERRIDEROOTKEY, /**< Sample root note override */ /** - * @brief Initial Pitch + * Initial Pitch * * @note This is not "standard" SoundFont generator, because it is not * mentioned in the list of generators in the SF2 specifications. @@ -117,11 +121,11 @@ enum fluid_gen_type GEN_CUSTOM_FILTERFC, /**< Custom filter cutoff frequency */ GEN_CUSTOM_FILTERQ, /**< Custom filter Q */ -#ifndef __DOXYGEN__ - GEN_LAST /**< @internal Value defines the count of generators (#fluid_gen_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ -#endif + GEN_LAST /**< @internal Value defines the count of generators (#fluid_gen_type) + @warning This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ }; - +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/log.h b/libs/fluidsynth/fluidsynth/log.h index 3ea74b2614..ec553ba4a3 100644 --- a/libs/fluidsynth/fluidsynth/log.h +++ b/libs/fluidsynth/fluidsynth/log.h @@ -28,25 +28,31 @@ extern "C" { /** - * @file log.h - * @brief Logging interface + * @defgroup logging Logging * - * The default logging function of the fluidsynth prints its messages - * to the stderr. The synthesizer uses five level of messages: #FLUID_PANIC, + * Logging interface + * + * The default logging function of the fluidsynth prints its messages to the + * stderr. The synthesizer uses five level of messages: #FLUID_PANIC, * #FLUID_ERR, #FLUID_WARN, #FLUID_INFO, and #FLUID_DBG. * - * A client application can install a new log function to handle the - * messages differently. In the following example, the application - * sets a callback function to display #FLUID_PANIC messages in a dialog, - * and ignores all other messages by setting the log function to - * NULL: + * A client application can install a new log function to handle the messages + * differently. In the following example, the application sets a callback + * function to display #FLUID_PANIC messages in a dialog, and ignores all other + * messages by setting the log function to NULL: * * @code - * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window); - * fluid_set_log_function(FLUID_ERR, NULL, NULL); - * fluid_set_log_function(FLUID_WARN, NULL, NULL); - * fluid_set_log_function(FLUID_DBG, NULL, NULL); + * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window); + * fluid_set_log_function(FLUID_ERR, NULL, NULL); + * fluid_set_log_function(FLUID_WARN, NULL, NULL); + * fluid_set_log_function(FLUID_DBG, NULL, NULL); * @endcode + * + * @note The logging configuration is global and not tied to a specific + * synthesizer instance. That means that all synthesizer instances created in + * the same process share the same logging configuration. + * + * @{ */ /** @@ -59,13 +65,13 @@ enum fluid_log_level FLUID_WARN, /**< Warning */ FLUID_INFO, /**< Verbose informational messages */ FLUID_DBG, /**< Debugging messages */ -#ifndef __DOXYGEN__ - LAST_LOG_LEVEL /**< @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ -#endif + LAST_LOG_LEVEL /**< @internal This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ }; /** * Log function handler callback type used by fluid_set_log_function(). + * * @param level Log level (#fluid_log_level) * @param message Log message text * @param data User data pointer supplied to fluid_set_log_function(). @@ -82,7 +88,7 @@ FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))) #endif ; - +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/midi.h b/libs/fluidsynth/fluidsynth/midi.h index 9ddeef054a..721b1a62fc 100644 --- a/libs/fluidsynth/fluidsynth/midi.h +++ b/libs/fluidsynth/fluidsynth/midi.h @@ -26,12 +26,92 @@ extern "C" { #endif /** - * @file midi.h - * @brief Functions for MIDI events, drivers and MIDI file playback. + * @defgroup midi_input MIDI Input + * + * MIDI Input Subsystem + * + * There are multiple ways to send MIDI events to the synthesizer. They can come + * from MIDI files, from external MIDI sequencers or raw MIDI event sources, + * can be modified via MIDI routers and also generated manually. + * + * The interface connecting all sources and sinks of MIDI events in libfluidsynth + * is \ref handle_midi_event_func_t. + * + * @{ */ +/** + * Generic callback function for MIDI event handler. + * + * @param data User defined data pointer + * @param event The MIDI event + * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This callback is used to pass MIDI events + * - from \ref midi_player, \ref midi_router or \ref midi_driver + * - to \ref midi_router via fluid_midi_router_handle_midi_event() + * - or to \ref synth via fluid_synth_handle_midi_event(). + * + * Additionally, there is a translation layer to pass MIDI events to + * a \ref sequencer via fluid_sequencer_add_midi_event_to_buffer(). + */ +typedef int (*handle_midi_event_func_t)(void *data, fluid_midi_event_t *event); + +/** + * Generic callback function fired once by MIDI tick change. + * + * @param data User defined data pointer + * @param tick The current (zero-based) tick, which triggered the callback + * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This callback is fired at a constant rate depending on the current BPM and PPQ. + * e.g. for PPQ = 192 and BPM = 140 the callback is fired 192 * 140 times per minute (448/sec). + * + * It can be used to sync external elements with the beat, + * or stop / loop the song on a given tick. + * Ticks being BPM-dependent, you can manipulate values such as bars or beats, + * without having to care about BPM. + * + * For example, this callback loops the song whenever it reaches the 5th bar : + * + * @code{.cpp} +int handle_tick(void *data, int tick) +{ + fluid_player_t *player = (fluid_player_t *)data; + int ppq = 192; // From MIDI header + int beatsPerBar = 4; // From the song's time signature + int loopBar = 5; + int loopTick = (loopBar - 1) * ppq * beatsPerBar; + + if (tick == loopTick) + { + return fluid_player_seek(player, 0); + } + + return FLUID_OK; +} + * @endcode + */ +typedef int (*handle_midi_tick_func_t)(void *data, int tick); +/* @} */ + +/** + * @defgroup midi_events MIDI Events + * @ingroup midi_input + * + * Functions to create, modify, query and delete MIDI events. + * + * These functions are intended to be used in MIDI routers and other filtering + * and processing functions in the MIDI event path. If you want to simply + * send MIDI messages to the synthesizer, you can use the more convenient + * \ref midi_messages interface. + * + * @{ + */ +/** @startlifecycle{MIDI Event} */ FLUIDSYNTH_API fluid_midi_event_t *new_fluid_midi_event(void); FLUIDSYNTH_API void delete_fluid_midi_event(fluid_midi_event_t *event); +/** @endlifecycle */ FLUIDSYNTH_API int fluid_midi_event_set_type(fluid_midi_event_t *evt, int type); FLUIDSYNTH_API int fluid_midi_event_get_type(fluid_midi_event_t *evt); @@ -59,9 +139,20 @@ FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic); FLUIDSYNTH_API int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size); +/* @} */ + +/** + * @defgroup midi_router MIDI Router + * @ingroup midi_input + * + * Rule based tranformation and filtering of MIDI events. + * + * @{ + */ /** * MIDI router rule type. + * * @since 1.1.0 */ typedef enum @@ -72,35 +163,30 @@ typedef enum FLUID_MIDI_ROUTER_RULE_PITCH_BEND, /**< MIDI pitch bend rule */ FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE, /**< MIDI channel pressure rule */ FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE, /**< MIDI key pressure rule */ -#ifndef __DOXYGEN__ - FLUID_MIDI_ROUTER_RULE_COUNT /**< @internal Total count of rule types @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time!*/ -#endif + FLUID_MIDI_ROUTER_RULE_COUNT /**< @internal Total count of rule types. This symbol + is not part of the public API and ABI stability + guarantee and may change at any time!*/ } fluid_midi_router_rule_type; -/** - * Generic callback function for MIDI events. - * @param data User defined data pointer - * @param event The MIDI event - * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise - * - * Will be used between - * - MIDI driver and MIDI router - * - MIDI router and synth - * to communicate events. - * In the not-so-far future... - */ -typedef int (*handle_midi_event_func_t)(void *data, fluid_midi_event_t *event); +/** @startlifecycle{MIDI Router} */ FLUIDSYNTH_API fluid_midi_router_t *new_fluid_midi_router(fluid_settings_t *settings, handle_midi_event_func_t handler, void *event_handler_data); FLUIDSYNTH_API void delete_fluid_midi_router(fluid_midi_router_t *handler); +/** @endlifecycle */ + FLUIDSYNTH_API int fluid_midi_router_set_default_rules(fluid_midi_router_t *router); FLUIDSYNTH_API int fluid_midi_router_clear_rules(fluid_midi_router_t *router); FLUIDSYNTH_API int fluid_midi_router_add_rule(fluid_midi_router_t *router, fluid_midi_router_rule_t *rule, int type); + + +/** @startlifecycle{MIDI Router Rule} */ FLUIDSYNTH_API fluid_midi_router_rule_t *new_fluid_midi_router_rule(void); FLUIDSYNTH_API void delete_fluid_midi_router_rule(fluid_midi_router_rule_t *rule); +/** @endlifecycle */ + FLUIDSYNTH_API void fluid_midi_router_rule_set_chan(fluid_midi_router_rule_t *rule, int min, int max, float mul, int add); FLUIDSYNTH_API void fluid_midi_router_rule_set_param1(fluid_midi_router_rule_t *rule, @@ -110,38 +196,88 @@ FLUIDSYNTH_API void fluid_midi_router_rule_set_param2(fluid_midi_router_rule_t * FLUIDSYNTH_API int fluid_midi_router_handle_midi_event(void *data, fluid_midi_event_t *event); FLUIDSYNTH_API int fluid_midi_dump_prerouter(void *data, fluid_midi_event_t *event); FLUIDSYNTH_API int fluid_midi_dump_postrouter(void *data, fluid_midi_event_t *event); +/* @} */ +/** + * @defgroup midi_driver MIDI Driver + * @ingroup midi_input + * + * Functions for managing MIDI drivers. + * + * The available MIDI drivers depend on your platform. See \ref settings_midi for all + * available configuration options. + * + * To create a MIDI driver, you need to specify a source for the MIDI events to be + * forwarded to via the \ref fluid_midi_event_t callback. Normally this will be + * either a \ref midi_router via fluid_midi_router_handle_midi_event() or the synthesizer + * via fluid_synth_handle_midi_event(). + * + * But you can also write your own handler function that preprocesses the events and + * forwards them on to the router or synthesizer instead. + * + * @{ + */ +/** @startlifecycle{MIDI Driver} */ FLUIDSYNTH_API fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_midi_event_func_t handler, void *event_handler_data); FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t *driver); +/** @endlifecycle */ +/* @} */ /** - * MIDI player status enum. + * @defgroup midi_player MIDI File Player + * @ingroup midi_input + * + * Parse standard MIDI files and emit MIDI events. + * + * @{ + */ + +/** + * MIDI File Player status enum. * @since 1.1.0 */ enum fluid_player_status { FLUID_PLAYER_READY, /**< Player is ready */ FLUID_PLAYER_PLAYING, /**< Player is currently playing */ + FLUID_PLAYER_STOPPING, /**< Player is stopping, but hasn't finished yet */ FLUID_PLAYER_DONE /**< Player is finished playing */ }; +/** + * MIDI File Player tempo enum. + * @since 2.2.0 + */ +enum fluid_player_set_tempo_type +{ + FLUID_PLAYER_TEMPO_INTERNAL, /**< Use midi file tempo set in midi file (120 bpm by default). Multiplied by a factor */ + FLUID_PLAYER_TEMPO_EXTERNAL_BPM, /**< Set player tempo in bpm, supersede midi file tempo */ + FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, /**< Set player tempo in us per quarter note, supersede midi file tempo */ + FLUID_PLAYER_TEMPO_NBR /**< @internal Value defines the count of player tempo type (#fluid_player_set_tempo_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +/** @startlifecycle{MIDI File Player} */ FLUIDSYNTH_API fluid_player_t *new_fluid_player(fluid_synth_t *synth); FLUIDSYNTH_API void delete_fluid_player(fluid_player_t *player); +/** @endlifecycle */ + FLUIDSYNTH_API int fluid_player_add(fluid_player_t *player, const char *midifile); FLUIDSYNTH_API int fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len); FLUIDSYNTH_API int fluid_player_play(fluid_player_t *player); FLUIDSYNTH_API int fluid_player_stop(fluid_player_t *player); FLUIDSYNTH_API int fluid_player_join(fluid_player_t *player); FLUIDSYNTH_API int fluid_player_set_loop(fluid_player_t *player, int loop); -FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo); -FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t *player, int bpm); +FLUIDSYNTH_API int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t *player, int bpm); FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t *player, handle_midi_event_func_t handler, void *handler_data); +FLUIDSYNTH_API int fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data); FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t *player); FLUIDSYNTH_API int fluid_player_get_current_tick(fluid_player_t *player); @@ -149,8 +285,7 @@ FLUIDSYNTH_API int fluid_player_get_total_ticks(fluid_player_t *player); FLUIDSYNTH_API int fluid_player_get_bpm(fluid_player_t *player); FLUIDSYNTH_API int fluid_player_get_midi_tempo(fluid_player_t *player); FLUIDSYNTH_API int fluid_player_seek(fluid_player_t *player, int ticks); - -/// +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/misc.h b/libs/fluidsynth/fluidsynth/misc.h index e2f5d3f8bb..8da368d9c4 100644 --- a/libs/fluidsynth/fluidsynth/misc.h +++ b/libs/fluidsynth/fluidsynth/misc.h @@ -28,13 +28,15 @@ extern "C" { /** - * @file misc.h - * @brief Miscellaneous utility functions and defines + * @defgroup misc Miscellaneous + * + * Miscellaneous utility functions and defines + * + * @{ */ /** * Value that indicates success, used by most libfluidsynth functions. - * @since 1.1.0 * * @note This was not publicly defined prior to libfluidsynth 1.1.0. When * writing code which should also be compatible with older versions, something @@ -48,14 +50,17 @@ extern "C" { * #define FLUID_FAILED (-1) * #endif * @endcode + * + * @since 1.1.0 */ #define FLUID_OK (0) /** * Value that indicates failure, used by most libfluidsynth functions. - * @since 1.1.0 * * @note See #FLUID_OK for more details. + * + * @since 1.1.0 */ #define FLUID_FAILED (-1) @@ -63,7 +68,7 @@ extern "C" { FLUIDSYNTH_API int fluid_is_soundfont(const char *filename); FLUIDSYNTH_API int fluid_is_midifile(const char *filename); FLUIDSYNTH_API void fluid_free(void* ptr); - +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/mod.h b/libs/fluidsynth/fluidsynth/mod.h index 5ea5f89d4a..98a00b0c5d 100644 --- a/libs/fluidsynth/fluidsynth/mod.h +++ b/libs/fluidsynth/fluidsynth/mod.h @@ -26,11 +26,14 @@ extern "C" { #endif /** - * @file mod.h - * @brief SoundFont modulator functions and constants. + * @defgroup modulators SoundFont Modulators + * @ingroup soundfonts + * + * SoundFont modulator functions and constants. + * + * @{ */ - /** * Flags defining the polarity, mapping function and type of a modulator source. * Compare with SoundFont 2.04 PDF section 8.2. @@ -69,8 +72,11 @@ enum fluid_mod_src FLUID_MOD_PITCHWHEELSENS = 16 /**< Pitch wheel sensitivity */ }; +/** @startlifecycle{Modulator} */ FLUIDSYNTH_API fluid_mod_t *new_fluid_mod(void); FLUIDSYNTH_API void delete_fluid_mod(fluid_mod_t *mod); +/** @endlifecycle */ + FLUIDSYNTH_API size_t fluid_mod_sizeof(void); FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags); @@ -90,6 +96,7 @@ FLUIDSYNTH_API int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl FLUIDSYNTH_API int fluid_mod_has_dest(const fluid_mod_t *mod, int gen); FLUIDSYNTH_API void fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src); +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/settings.h b/libs/fluidsynth/fluidsynth/settings.h index aa7f083129..aba86e3379 100644 --- a/libs/fluidsynth/fluidsynth/settings.h +++ b/libs/fluidsynth/fluidsynth/settings.h @@ -26,9 +26,9 @@ extern "C" { #endif /** - * @file settings.h - * @brief Synthesizer settings - * @defgroup SettingsFunctions Functions for settings management + * @defgroup settings Settings + * + * Functions for settings management * * To create a synthesizer object you will have to specify its * settings. These settings are stored in a fluid_settings_t object. @@ -49,6 +49,8 @@ extern "C" { * } * @endcode * @sa @ref CreatingSettings + * + * @{ */ /** @@ -97,9 +99,10 @@ enum fluid_types_enum FLUID_SET_TYPE /**< Set of values */ }; - +/** @startlifecycle{Settings} */ FLUIDSYNTH_API fluid_settings_t *new_fluid_settings(void); FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t *settings); +/** @endlifecycle */ FLUIDSYNTH_API int fluid_settings_get_type(fluid_settings_t *settings, const char *name); @@ -120,7 +123,7 @@ FLUIDSYNTH_API int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str); FLUIDSYNTH_API -int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def); +int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char const **def); FLUIDSYNTH_API int fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *value); @@ -153,6 +156,7 @@ int fluid_settings_getint_range(fluid_settings_t *settings, const char *name, /** * Callback function type used with fluid_settings_foreach_option() + * * @param data User defined data pointer * @param name Setting name * @param option A string option for this setting (iterates through the list) @@ -171,6 +175,7 @@ FLUIDSYNTH_API char *fluid_settings_option_concat(fluid_settings_t *settings, /** * Callback function type used with fluid_settings_foreach() + * * @param data User defined data pointer * @param name Setting name * @param type Setting type (#fluid_types_enum) @@ -180,6 +185,7 @@ typedef void (*fluid_settings_foreach_t)(void *data, const char *name, int type) FLUIDSYNTH_API void fluid_settings_foreach(fluid_settings_t *settings, void *data, fluid_settings_foreach_t func); +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/fluidsynth/sfont.h b/libs/fluidsynth/fluidsynth/sfont.h index 0df4d6d4a8..b29d8b490b 100644 --- a/libs/fluidsynth/fluidsynth/sfont.h +++ b/libs/fluidsynth/fluidsynth/sfont.h @@ -25,10 +25,21 @@ extern "C" { #endif +/** + * @defgroup soundfonts SountFonts + * + * SoundFont related functions + * + * This part of the API contains functions, defines and types that are mostly + * only used by internal or custom SoundFont loaders or client code that + * modifies loaded presets, SoundFonts or voices directly. + */ /** - * @file sfont.h - * @brief SoundFont plugins + * @defgroup soundfont_loader SoundFont Loader + * @ingroup soundfonts + * + * Create custom SoundFont loaders * * It is possible to add new SoundFont loaders to the * synthesizer. This API allows for virtual SoundFont files to be loaded @@ -59,6 +70,8 @@ extern "C" { * 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. + * + * @{ */ /** @@ -68,52 +81,65 @@ enum { FLUID_PRESET_SELECTED, /**< Preset selected notify */ FLUID_PRESET_UNSELECTED, /**< Preset unselected notify */ - FLUID_SAMPLE_DONE /**< Sample no longer needed notify */ + FLUID_SAMPLE_DONE, /**< Sample no longer needed notify */ + FLUID_PRESET_PIN, /**< Request to pin preset samples to cache */ + FLUID_PRESET_UNPIN /**< Request to unpin preset samples from cache */ }; /** * Indicates the type of a sample used by the _fluid_sample_t::sampletype field. + * + * This enum corresponds to the \c SFSampleLink enum in the SoundFont spec. + * One \c flag may be bit-wise OR-ed with one \c value. */ enum fluid_sample_type { - FLUID_SAMPLETYPE_MONO = 0x1, /**< Used for mono samples */ - FLUID_SAMPLETYPE_RIGHT = 0x2, /**< Used for right samples of a stereo pair */ - FLUID_SAMPLETYPE_LEFT = 0x4, /**< Used for left samples of a stereo pair */ - FLUID_SAMPLETYPE_LINKED = 0x8, /**< Currently not used */ - FLUID_SAMPLETYPE_OGG_VORBIS = 0x10, /**< Used for Ogg Vorbis compressed samples @since 1.1.7 */ - FLUID_SAMPLETYPE_ROM = 0x8000 /**< Indicates ROM samples, causes sample to be ignored */ + FLUID_SAMPLETYPE_MONO = 0x1, /**< Value used for mono samples */ + FLUID_SAMPLETYPE_RIGHT = 0x2, /**< Value used for right samples of a stereo pair */ + FLUID_SAMPLETYPE_LEFT = 0x4, /**< Value used for left samples of a stereo pair */ + FLUID_SAMPLETYPE_LINKED = 0x8, /**< Value used for linked sample, which is currently not supported */ + FLUID_SAMPLETYPE_OGG_VORBIS = 0x10, /**< Flag used for Ogg Vorbis compressed samples (non-standard compliant extension) as found in the program "sftools" developed by Werner Schweer from MuseScore @since 1.1.7 */ + FLUID_SAMPLETYPE_ROM = 0x8000 /**< Flag that indicates ROM samples, causing the sample to be ignored */ }; /** * 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. + * @return The loaded instrument file (SoundFont) or NULL if an error occurred. */ typedef fluid_sfont_t *(*fluid_sfloader_load_t)(fluid_sfloader_t *loader, const char *filename); /** * 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 + * addition to any private data. + * + * @param loader SoundFont loader + * + * 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); +/** @startlifecycle{SoundFont Loader} */ 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); +/** @endlifecycle */ /** * 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 + * + * \c filename matches the string provided during the fluid_synth_sfload() call. */ typedef void *(* fluid_sfloader_callback_open_t)(const char *filename); @@ -122,26 +148,25 @@ typedef void *(* fluid_sfloader_callback_open_t)(const char *filename); * * @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); +typedef int (* fluid_sfloader_callback_read_t)(void *buf, fluid_long_long_t count, void *handle); /** * 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); +typedef int (* fluid_sfloader_callback_seek_t)(void *handle, fluid_long_long_t offset, int origin); /** - * Closes the handle returned by #fluid_sfloader_callback_open_t and frees used ressources. + * Closes the handle returned by #fluid_sfloader_callback_open_t and frees used resources. * * @return returns #FLUID_OK on success, #FLUID_FAILED on error */ typedef int (* fluid_sfloader_callback_close_t)(void *handle); /** @return returns current file offset or #FLUID_FAILED on error */ -typedef long (* fluid_sfloader_callback_tell_t)(void *handle); +typedef fluid_long_long_t (* fluid_sfloader_callback_tell_t)(void *handle); FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, @@ -158,6 +183,7 @@ FLUIDSYNTH_API void *fluid_sfloader_get_data(fluid_sfloader_t *loader); /** * Method to return the name of a virtual SoundFont. + * * @param sfont Virtual SoundFont * @return The name of the virtual SoundFont. */ @@ -165,6 +191,7 @@ 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) @@ -175,6 +202,7 @@ typedef fluid_preset_t *(*fluid_sfont_get_preset_t)(fluid_sfont_t *sfont, int ba /** * Start virtual SoundFont preset iteration method. + * * @param sfont Virtual SoundFont * * Starts/re-starts virtual preset iteration in a SoundFont. @@ -183,6 +211,7 @@ typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont); /** * Virtual SoundFont preset iteration function. + * * @param sfont Virtual SoundFont * @return NULL when no more presets are available, otherwise the a pointer to the current preset * @@ -192,17 +221,21 @@ typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont); typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont); /** - * 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. + * 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. + * + * 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. */ typedef int (*fluid_sfont_free_t)(fluid_sfont_t *sfont); +/** @startlifecycle{SoundFont} */ FLUIDSYNTH_API fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, fluid_sfont_get_preset_t get_preset, fluid_sfont_iteration_start_t iter_start, @@ -210,6 +243,7 @@ FLUIDSYNTH_API fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, fluid_sfont_free_t free); FLUIDSYNTH_API int delete_fluid_sfont(fluid_sfont_t *sfont); +/** @endlifecycle */ FLUIDSYNTH_API int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data); FLUIDSYNTH_API void *fluid_sfont_get_data(fluid_sfont_t *sfont); @@ -222,6 +256,7 @@ FLUIDSYNTH_API fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont); /** * 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 @@ -231,6 +266,7 @@ 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 */ @@ -238,6 +274,7 @@ 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 */ @@ -245,6 +282,7 @@ 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 @@ -268,14 +306,18 @@ typedef int (*fluid_preset_get_num_t)(fluid_preset_t *preset); 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. + * Method to free a virtual SoundFont preset. + * * @param preset Virtual SoundFont preset * @return Should return 0 + * + * 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. */ typedef void (*fluid_preset_free_t)(fluid_preset_t *preset); +/** @startlifecycle{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, @@ -283,6 +325,7 @@ FLUIDSYNTH_API fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, fluid_preset_noteon_t noteon, fluid_preset_free_t free); FLUIDSYNTH_API void delete_fluid_preset(fluid_preset_t *preset); +/** @endlifecycle */ FLUIDSYNTH_API int fluid_preset_set_data(fluid_preset_t *preset, void *data); FLUIDSYNTH_API void *fluid_preset_get_data(fluid_preset_t *preset); @@ -292,8 +335,11 @@ FLUIDSYNTH_API int fluid_preset_get_banknum(fluid_preset_t *preset); FLUIDSYNTH_API int fluid_preset_get_num(fluid_preset_t *preset); FLUIDSYNTH_API fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset); +/** @startlifecycle{Sample} */ FLUIDSYNTH_API fluid_sample_t *new_fluid_sample(void); FLUIDSYNTH_API void delete_fluid_sample(fluid_sample_t *sample); +/** @endlifecycle */ + FLUIDSYNTH_API size_t fluid_sample_sizeof(void); FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t *sample, const char *name); @@ -307,6 +353,8 @@ FLUIDSYNTH_API int fluid_sample_set_sound_data(fluid_sample_t *sample, 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 } #endif diff --git a/libs/fluidsynth/fluidsynth/synth.h b/libs/fluidsynth/fluidsynth/synth.h index 3003972542..126532554f 100644 --- a/libs/fluidsynth/fluidsynth/synth.h +++ b/libs/fluidsynth/fluidsynth/synth.h @@ -21,6 +21,7 @@ #ifndef _FLUIDSYNTH_SYNTH_H #define _FLUIDSYNTH_SYNTH_H +#define FLUID_DEPRECATED #ifdef __cplusplus extern "C" { @@ -28,8 +29,9 @@ extern "C" { /** - * @file synth.h - * @brief Embeddable SoundFont synthesizer + * @defgroup synth Synthesizer + * + * SoundFont synthesizer * * You create a new synthesizer with new_fluid_synth() and you destroy * it with delete_fluid_synth(). Use the fluid_settings_t structure to specify @@ -43,15 +45,29 @@ extern "C" { * * The API for sending MIDI events is probably what you expect: * fluid_synth_noteon(), fluid_synth_noteoff(), ... + * + * @{ */ - +/** @startlifecycle{Synthesizer} */ FLUIDSYNTH_API fluid_synth_t *new_fluid_synth(fluid_settings_t *settings); FLUIDSYNTH_API void delete_fluid_synth(fluid_synth_t *synth); -FLUIDSYNTH_API fluid_settings_t *fluid_synth_get_settings(fluid_synth_t *synth); +/** @endlifecycle */ -/* MIDI channel messages */ +FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); +/* @} */ +/** + * @defgroup midi_messages MIDI Channel Messages + * @ingroup synth + * + * The MIDI channel message functions are mostly directly named after their + * counterpart MIDI messages. They are a high-level interface to controlling + * the synthesizer, playing notes and changing note and channel parameters. + * + * @{ + */ FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel); FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key); FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t *synth, int chan, int ctrl, int val); @@ -84,28 +100,42 @@ FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t *synth); FLUIDSYNTH_API int fluid_synth_all_notes_off(fluid_synth_t *synth, int chan); FLUIDSYNTH_API int fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan); +FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t *synth, int chan, + int param, float value); +FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param); +/* @} MIDI Channel Messages */ + + /** - * The midi channel type used by fluid_synth_set_channel_type() + * @defgroup voice_control Synthesis Voice Control + * @ingroup synth + * + * Low-level access to synthesis voices. + * + * @{ */ -enum fluid_midi_channel_type -{ - CHANNEL_TYPE_MELODIC = 0, /**< Melodic midi channel */ - CHANNEL_TYPE_DRUM = 1 /**< Drum midi channel */ -}; - -FLUIDSYNTH_API int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type); - - -/* 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); FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t *synth, unsigned int id); +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); +FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t *synth, + fluid_voice_t *buf[], int bufsize, int ID); +/* @} Voice Control */ -/* SoundFont management */ +/** + * @defgroup soundfont_management SoundFont Management + * @ingroup synth + * + * Functions to load and unload SoundFonts. + * + * @{ + */ FLUIDSYNTH_API int fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets); FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t *synth, int id); @@ -119,26 +149,52 @@ FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name(fluid_synth_t *synth const 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); +/* @} Soundfont Management */ -/* Reverb */ +/** + * @defgroup reverb_effect Effect - Reverb + * @ingroup synth + * + * Functions for configuring the built-in reverb effect + * + * @{ + */ +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on); +FLUIDSYNTH_API int fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on); + +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, + double damping, double width, double level); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level); + +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t *synth); + +FLUIDSYNTH_API int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double roomsize); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, double damping); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, double width); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, double level); + +FLUIDSYNTH_API int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double *roomsize); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, double *damping); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, double *width); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, double *level); + /* @} Reverb */ -FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, - double damping, double width, double level); -FLUIDSYNTH_API int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize); -FLUIDSYNTH_API int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping); -FLUIDSYNTH_API int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width); -FLUIDSYNTH_API int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level); - -FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on); -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); - - -/* Chorus */ +/** + * @defgroup chorus_effect Effect - Chorus + * @ingroup synth + * + * Functions for configuring the built-in chorus effect + * + * @{ + */ /** * Chorus modulation waveform type. @@ -149,34 +205,53 @@ enum fluid_chorus_mod FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */ }; -FLUIDSYNTH_API int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, - double speed, double depth_ms, int type); -FLUIDSYNTH_API int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr); -FLUIDSYNTH_API int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level); -FLUIDSYNTH_API int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed); -FLUIDSYNTH_API int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms); -FLUIDSYNTH_API int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type); -FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on); -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(fluid_synth_t *synth); -FLUIDSYNTH_API double fluid_synth_get_chorus_depth(fluid_synth_t *synth); -FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t *synth); /* see fluid_chorus_mod */ +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on); +FLUIDSYNTH_API int fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, + double speed, double depth_ms, int type); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type); -/* Audio and MIDI channels */ +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_speed(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_depth(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t *synth); /* see fluid_chorus_mod */ +FLUIDSYNTH_API int fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type); + +FLUIDSYNTH_API int fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type); +/* @} Chorus */ + +/** + * @defgroup synthesis_params Synthesis Parameters + * @ingroup synth + * + * Functions to control and query synthesis parameters like gain and + * polyphony count. + * + * @{ + */ FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t *synth); FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t *synth); FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t *synth); FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t *synth); FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth); - -/* Synthesis parameters */ - -FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate); +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate); FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t *synth, float gain); FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t *synth); FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony); @@ -201,15 +276,28 @@ enum fluid_interp FLUID_INTERP_HIGHEST = FLUID_INTERP_7THORDER, /**< Highest interpolation method */ }; -/* Generator interface */ +/** + * Enum used with fluid_synth_add_default_mod() to specify how to handle duplicate modulators. + */ +enum fluid_synth_add_mod +{ + FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */ + FLUID_SYNTH_ADD, /**< Sum up modulator amounts */ +}; -FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t *synth, int chan, - int param, float value); -FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param); +FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode); +FLUIDSYNTH_API int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod); +/* @} Synthesis Parameters */ -/* Tuning */ - +/** + * @defgroup tuning MIDI Tuning + * @ingroup synth + * + * The functions in this section implement the MIDI Tuning Standard interface. + * + * @{ + */ FLUIDSYNTH_API int fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog, const char *name, const double *pitch, int apply); @@ -229,60 +317,46 @@ FLUIDSYNTH_API int fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog); FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog, char *name, int len, double *pitch); +/* @} MIDI Tuning */ -/* Misc */ - -FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); -const char *fluid_synth_error(fluid_synth_t *synth); - - -/* Default modulators */ /** - * Enum used with fluid_synth_add_default_mod() to specify how to handle duplicate modulators. - */ -enum fluid_synth_add_mod -{ - FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */ - FLUID_SYNTH_ADD, /**< Sum up modulator amounts */ -}; - -FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode); -FLUIDSYNTH_API int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod); - - -/* - * Synthesizer plugin + * @defgroup audio_rendering Audio Rendering + * @ingroup synth * - * 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. + * The functions in this section can be used to render audio directly to + * memory buffers. They are used internally by the \ref audio_driver and \ref file_renderer, + * but can also be used manually for custom processing of the rendered audio. + * + * @note Please note that all following functions block during rendering. If your goal is to + * render real-time audio, ensure that you call these functions from a high-priority + * thread with little to no other duties other than calling the rendering functions. + * + * @{ */ - 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); 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, +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len, float **left, float **right, float **fx_left, float **fx_right); FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], int nout, float *out[]); +/* @} Audio Rendering */ -/* Synthesizer's interface to handle SoundFont loaders */ - -FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader); -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); -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); +/** + * @defgroup iir_filter Effect - IIR Filter + * @ingroup synth + * + * Functions for configuring the built-in IIR filter effect + * + * @{ + */ /** * Specifies the type of filter to use for the custom IIR filter @@ -306,19 +380,43 @@ enum fluid_iir_filter_flags }; FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int flags); +/* @} IIR Filter */ -/* LADSPA */ - -#ifdef LADSPA -FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); -#endif -/* API: Poly mono mode */ - -/** Interface to poly/mono mode variables +/** + * @defgroup channel_setup MIDI Channel Setup + * @ingroup synth * + * The functions in this section provide interfaces to change the channel type + * and to configure basic channels, legato and portamento setups. + * + * @{ + */ + +/** @name Channel Type + * @{ + */ + +/** + * The midi channel type used by fluid_synth_set_channel_type() + */ +enum fluid_midi_channel_type +{ + CHANNEL_TYPE_MELODIC = 0, /**< Melodic midi channel */ + CHANNEL_TYPE_DRUM = 1 /**< Drum midi channel */ +}; + +FLUIDSYNTH_API int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type); +/** @} Channel Type */ + + +/** @name Basic Channel Mode + * @{ + */ + +/** * Channel mode bits OR-ed together so that it matches with the midi spec: poly omnion (0), mono omnion (1), poly omnioff (2), mono omnioff (3) */ enum fluid_channel_mode_flags @@ -327,15 +425,9 @@ enum fluid_channel_mode_flags FLUID_CHANNEL_OMNI_OFF = 0x02, /**< if flag is set, the basic channel is in omni off state, if not set omni is on */ }; -/** Indicates the breath mode a channel is set to */ -enum fluid_channel_breath_flags -{ - FLUID_CHANNEL_BREATH_POLY = 0x10, /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */ - FLUID_CHANNEL_BREATH_MONO = 0x20, /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */ - FLUID_CHANNEL_BREATH_SYNC = 0x40, /**< when channel is mono, this flag indicates that the breath controler(MSB)triggers noteon/noteoff on the running note */ -}; - -/** Indicates the mode a basic channel is set to */ +/** + * Indicates the mode a basic channel is set to + */ enum fluid_basic_channel_modes { FLUID_CHANNEL_MODE_MASK = (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< Mask Poly and Omni bits of #fluid_channel_mode_flags, usually only used internally */ @@ -354,8 +446,13 @@ FLUIDSYNTH_API int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan int *basic_val_out); FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val); -/** Interface to mono legato mode - * +/** @} Basic Channel Mode */ + +/** @name Legato Mode + * @{ + */ + +/** * Indicates the legato mode a channel is set to * n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played legato with previous note. */ enum fluid_channel_legato_mode @@ -367,9 +464,13 @@ enum fluid_channel_legato_mode FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode); FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode); +/** @} Legato Mode */ -/** Interface to portamento mode - * +/** @name Portamento Mode + * @{ + */ + +/** * Indicates the portamento mode a channel is set to */ enum fluid_channel_portamento_mode @@ -377,21 +478,64 @@ enum fluid_channel_portamento_mode FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE, /**< Mode 0 - Portamento on each note (staccato or legato) */ FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY, /**< Mode 1 - Portamento only on legato note */ FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY, /**< Mode 2 - Portamento only on staccato note */ - FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes (#fluid_channel_portamento_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ + FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes + @warning This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ }; FLUIDSYNTH_API int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan, int portamentomode); FLUIDSYNTH_API int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan, int *portamentomode); +/** @} Portamento Mode */ + +/**@name Breath Mode + * @{ + */ + +/** + * Indicates the breath mode a channel is set to + */ +enum fluid_channel_breath_flags +{ + FLUID_CHANNEL_BREATH_POLY = 0x10, /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */ + FLUID_CHANNEL_BREATH_MONO = 0x20, /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */ + FLUID_CHANNEL_BREATH_SYNC = 0x40, /**< when channel is mono, this flag indicates that the breath controller(MSB)triggers noteon/noteoff on the running note */ +}; -/* Interface to breath mode */ FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode); FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode); +/** @} Breath Mode */ +/* @} MIDI Channel Setup */ +/** @ingroup settings */ +FLUIDSYNTH_API fluid_settings_t *fluid_synth_get_settings(fluid_synth_t *synth); + +/** @ingroup soundfont_loader */ +FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader); + +/** @ingroup soundfont_loader */ +FLUIDSYNTH_API fluid_preset_t *fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan); + +/** @ingroup midi_input */ +FLUIDSYNTH_API int fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event); + +/** @ingroup soundfonts */ +FLUIDSYNTH_API +int fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); + +/** @ingroup soundfonts */ +FLUIDSYNTH_API +int fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); + +#ifdef LADSPA +/** @ingroup ladspa */ +FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); +#endif + #ifdef __cplusplus } #endif diff --git a/libs/fluidsynth/fluidsynth/types.h b/libs/fluidsynth/fluidsynth/types.h index 5ad29281ad..6c7994fe83 100644 --- a/libs/fluidsynth/fluidsynth/types.h +++ b/libs/fluidsynth/fluidsynth/types.h @@ -29,8 +29,10 @@ extern "C" { /** - * @file types.h + * @defgroup Types Types * @brief Type declarations + * + * @{ */ typedef struct _fluid_hashtable_t fluid_settings_t; /**< Configuration settings instance */ @@ -66,6 +68,18 @@ typedef int fluid_ostream_t; /**< Output stream descriptor */ typedef short fluid_seq_id_t; /**< Unique client IDs used by the sequencer and #fluid_event_t, obtained by fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() */ +#if defined(_MSC_VER) && (_MSC_VER < 1800) +typedef __int64 fluid_long_long_t; // even on 32bit windows +#else +/** + * A typedef for C99's type long long, which is at least 64-bit wide, as guaranteed by the C99. + * @p __int64 will be used as replacement for VisualStudio 2010 and older. + */ +typedef long long fluid_long_long_t; +#endif + +/* @} */ + #ifdef __cplusplus } #endif diff --git a/libs/fluidsynth/fluidsynth/voice.h b/libs/fluidsynth/fluidsynth/voice.h index f0644718b2..ead4f710ed 100644 --- a/libs/fluidsynth/fluidsynth/voice.h +++ b/libs/fluidsynth/fluidsynth/voice.h @@ -26,16 +26,21 @@ extern "C" { #endif /** - * @file voice.h - * @brief Synthesis voice manipulation functions. + * @defgroup voices Voice Manipulation + * @ingroup soundfonts + * + * Synthesis voice manipulation functions. * * The interface to the synthesizer's voices. - * Examples on using them can be found in fluid_defsfont.c. + * Examples on using them can be found in the source code of the default SoundFont + * loader (fluid_defsfont.c). + * * Most of these functions should only be called from within synthesis context, * such as the SoundFont loader's noteon method. + * + * @{ */ - /** * Enum used with fluid_voice_add_mod() to specify how to handle duplicate modulators. */ @@ -63,7 +68,7 @@ FLUIDSYNTH_API int fluid_voice_is_sustained(const fluid_voice_t *voice); FLUIDSYNTH_API int fluid_voice_is_sostenuto(const fluid_voice_t *voice); FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t *s); FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t *voice, int gen); - +/* @} */ #ifdef __cplusplus } diff --git a/libs/fluidsynth/src/fluid_chan.c b/libs/fluidsynth/src/fluid_chan.c index 365c0fd2a1..5f2674b5bb 100644 --- a/libs/fluidsynth/src/fluid_chan.c +++ b/libs/fluidsynth/src/fluid_chan.c @@ -115,7 +115,7 @@ fluid_channel_init(fluid_channel_t *chan) /* @param is_all_ctrl_off if nonzero, only resets some controllers, according to - http://www.midi.org/techspecs/rp15.php + https://www.midi.org/techspecs/rp15.php */ void fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) @@ -211,7 +211,7 @@ fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */ /* Note: although XG standard specifies the default amount of reverb to be 40, most people preferred having it at zero. - See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ + See https://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ } } @@ -224,7 +224,6 @@ delete_fluid_channel(fluid_channel_t *chan) FLUID_FREE(chan); } -/* FIXME - Calls fluid_channel_init() potentially in synthesis context */ void fluid_channel_reset(fluid_channel_t *chan) { @@ -324,7 +323,7 @@ fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb) { /* XG bank, do drum-channel auto-switch */ /* The number "120" was based on several keyboards having drums at 120 - 127, - reference: http://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */ + reference: https://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */ chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; return; } @@ -413,7 +412,7 @@ fluid_channel_update_legato_staccato_state(fluid_channel_t *chan) * prev_note keeps a trace of the note prior i_last note. * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state. * - * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). */ void fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, @@ -523,7 +522,7 @@ fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_p * - prev_note keeps a trace of the note removed if it is i_last. * - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state. * - * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). */ void fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev) diff --git a/libs/fluidsynth/src/fluid_chorus.c b/libs/fluidsynth/src/fluid_chorus.c index 7092d5e7d6..88ed39f0d1 100644 --- a/libs/fluidsynth/src/fluid_chorus.c +++ b/libs/fluidsynth/src/fluid_chorus.c @@ -63,7 +63,7 @@ * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n). * * The chorus unit process a monophonic input signal and produces stereo output - * controled by WIDTH macro. + * controlled by WIDTH macro. * Actually WIDTH is fixed to maximum value. But in the future, we could add a * setting (e.g "synth.chorus.width") allowing the user to get a gradually stereo * effect from minimum (monophonic) to maximum stereo effect. @@ -79,7 +79,7 @@ * The advantages are: * - Avoiding a lost of 608272 memory bytes when lfo speed is low (0.3Hz). * - Allows to diminish the lfo speed lower limit to 0.1Hz instead of 0.3Hz. - * A speed of 0.1 is interresting for chorus. Using a lookuptable for 0.1Hz + * A speed of 0.1 is interesting for chorus. Using a lookuptable for 0.1Hz * would require too much memory (1824816 bytes). * - Interpolation make use of first order all-pass interpolator instead of * bandlimited interpolation. @@ -244,7 +244,7 @@ struct _fluid_chorus_t static void set_sinus_frequency(sinus_modulator *mod, float freq, float sample_rate, float phase) { - fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* intial angle */ + fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ fluid_real_t a; mod->a1 = 2 * FLUID_COS(w); @@ -261,7 +261,7 @@ static void set_sinus_frequency(sinus_modulator *mod, y(n) = a1 . y(n-1) - y(n-2) out = a1 . buffer1 - buffer2 - @param pointer on modulator structure. + @param mod pointer on modulator structure. @return current value of the modulator sine wave. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) @@ -293,6 +293,11 @@ static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) in the period relative to the beginning of the period. For example: 0 is the beginning of the period, 1/4 is at 1/4 of the period relative to the beginning. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param frac_phase initial phase (see comment above). -----------------------------------------------------------------------------*/ static void set_triangle_frequency(triang_modulator *mod, float freq, float sample_rate, float frac_phase) @@ -313,7 +318,7 @@ static void set_triangle_frequency(triang_modulator *mod, float freq, mod->inc = 4 / ns_period; /* positive slope */ /* The initial value and the sign of the slope depend of initial phase: - intial value = = (ns_period * frac_phase) * slope + initial value = = (ns_period * frac_phase) * slope */ mod->val = ns_period * frac_phase * mod->inc; @@ -333,6 +338,9 @@ static void set_triangle_frequency(triang_modulator *mod, float freq, /*----------------------------------------------------------------------------- Get current value of triangular oscillator y(n) = y(n-1) + dy + + @param mod pointer on triang_modulator structure. + @return current value. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod) { @@ -354,18 +362,20 @@ static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod) } /*----------------------------------------------------------------------------- Reads the sample value out of the modulated delay line. - @param mdl, pointer on modulated delay line. - @return the sample value. + + @param chorus pointer on chorus unit. + @param mod pointer on modulator structure. + @return current value. -----------------------------------------------------------------------------*/ static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus, - modulator *mod) + modulator *mod) { fluid_real_t out_index; /* new modulated index position */ int int_out_index; /* integer part of out_index */ fluid_real_t out; /* value to return */ /* Checks if the modulator must be updated (every mod_rate samples). */ - /* Important: center_pos_mod must be used immediatly for the + /* Important: center_pos_mod must be used immediately for the first sample. So, mdl->index_rate must be initialized to mdl->mod_rate (new_mod_delay_line()) */ @@ -421,7 +431,7 @@ static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus, mod->line_out -= chorus->size; } - /* Fractional interpolation beetween next sample (at next position) and + /* Fractional interpolation between next sample (at next position) and previous output added to current sample. */ out += mod->frac_pos_mod * (chorus->line[mod->line_out] - mod->buffer); @@ -431,6 +441,9 @@ static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus, /*----------------------------------------------------------------------------- Push a sample val into the delay line + + @param dl delay line to push value into. + @param val the value to push into dl. -----------------------------------------------------------------------------*/ #define push_in_delay_line(dl, val) \ {\ @@ -444,13 +457,15 @@ static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus, center_pos_mod is initialized so that the delay between center_pos_mod and line_in is: mod_depth + INTERP_SAMPLES_NBR. + + @param chorus pointer on chorus unit. -----------------------------------------------------------------------------*/ static void set_center_position(fluid_chorus_t *chorus) { int center; /* Sets the modulation rate. This rate defines how often - the center position (center_pos_mod ) is modulated . + the center position (center_pos_mod ) is modulated . The value is expressed in samples. The default value is 1 that means that center_pos_mod is updated at every sample. For example with a value of 2, the center position position will be @@ -479,11 +494,68 @@ static void set_center_position(fluid_chorus_t *chorus) chorus->center_pos_mod = (fluid_real_t)center; /* index rate to control when to update center_pos_mod */ - /* Important: must be set to get center_pos_mod immediatly used for the + /* Important: must be set to get center_pos_mod immediately used for the reading of first sample (see get_mod_delay()) */ chorus->index_rate = chorus->mod_rate; } +/*----------------------------------------------------------------------------- + Update internal parameters dependent of sample rate. + - mod_depth. + - mod_rate, center_pos_mod, and index rate. + - modulators frequency. + + @param chorus, pointer on chorus unit. +-----------------------------------------------------------------------------*/ +static void update_parameters_from_sample_rate(fluid_chorus_t *chorus) +{ + int i; + + /* initialize modulation depth (peak to peak) (in samples) */ + /* convert modulation depth in ms to sample number */ + chorus->mod_depth = (int)(chorus->depth_ms / 1000.0 + * chorus->sample_rate); + + /* the delay line is fixed. So we reduce mod_depth (if necessary) */ + if(chorus->mod_depth > MAX_SAMPLES) + { + FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", + MAX_SAMPLES); + chorus->mod_depth = MAX_SAMPLES; + /* set depth_ms to maximum to avoid spamming console with above warning */ + chorus->depth_ms = (chorus->mod_depth * 1000) / chorus->sample_rate; + } + + chorus->mod_depth /= 2; /* amplitude is peak to peek / 2 */ +#ifdef DEBUG_PRINT + printf("depth_ms:%f, depth_samples/2:%d\n", chorus->depth_ms, chorus->mod_depth); +#endif + + /* Initializes the modulated center position: + mod_rate, center_pos_mod, and index rate. + */ + set_center_position(chorus); /* must be called before set_xxxx_frequency() */ +#ifdef DEBUG_PRINT + printf("mod_rate:%d\n", chorus->mod_rate); +#endif + + /* initialize modulator frequency */ + for(i = 0; i < chorus->number_blocks; i++) + { + set_sinus_frequency(&chorus->mod[i].sinus, + chorus->speed_Hz * chorus->mod_rate, + chorus->sample_rate, + /* phase offset between modulators waveform */ + (float)((360.0f / (float) chorus->number_blocks) * i)); + + set_triangle_frequency(&chorus->mod[i].triang, + chorus->speed_Hz * chorus->mod_rate, + chorus->sample_rate, + /* phase offset between modulators waveform */ + (float)i / chorus->number_blocks); + } +} + /*----------------------------------------------------------------------------- Modulated delay line initialization. @@ -491,7 +563,7 @@ static void set_center_position(fluid_chorus_t *chorus) Remark: the function sets the internal size accordling to the length delay_length. The size is augmented by INTERP_SAMPLES_NBR to take account of interpolation. - @param chorus, pointer chorus unit. + @param chorus, pointer on chorus unit. @param delay_length the length of the delay line in samples. @return FLUID_OK if success , FLUID_FAILED if memory error. @@ -545,8 +617,11 @@ static int new_mod_delay_line(fluid_chorus_t *chorus, int delay_length) API ------------------------------------------------------------------------------*/ /** - * Create the chorus unit. - * @sample_rate audio sample rate in Hz. + * Create the chorus unit. Once created the chorus have no parameters set, so + * fluid_chorus_set() must be called at least one time after calling + * new_fluid_chorus(). + * + * @param sample_rate, audio sample rate in Hz. * @return pointer on chorus unit. */ fluid_chorus_t * @@ -577,15 +652,11 @@ new_fluid_chorus(fluid_real_t sample_rate) if(new_mod_delay_line(chorus, MAX_SAMPLES) == FLUID_FAILED) { - goto error_recovery; + delete_fluid_chorus(chorus); + return NULL; } return chorus; - -error_recovery: - delete_fluid_chorus(chorus); - - return NULL; } /** @@ -628,22 +699,21 @@ fluid_chorus_reset(fluid_chorus_t *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 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-10.0) - * @param speed Chorus speed in Hz (0.1-5.0) + * this value). + * @param level Chorus level (0.0-10.0). + * @param speed Chorus speed in Hz (0.1-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) + * 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, fluid_real_t level, fluid_real_t speed, fluid_real_t depth_ms, int type) { - int i; - if(set & FLUID_CHORUS_SET_NR) /* number of block */ { chorus->number_blocks = nr; @@ -713,45 +783,8 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, chorus->level = 0.1; } - /* initialize modulation depth (peak to peak) (in samples)*/ - chorus->mod_depth = (int)(chorus->depth_ms / 1000.0 /* convert modulation depth in ms to s*/ - * chorus->sample_rate); - - if(chorus->mod_depth > MAX_SAMPLES) - { - FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); - chorus->mod_depth = MAX_SAMPLES; - // set depth to maximum to avoid spamming console with above warning - chorus->depth_ms = (chorus->mod_depth * 1000) / chorus->sample_rate; - } - - chorus->mod_depth /= 2; /* amplitude is peak to peek / 2 */ -#ifdef DEBUG_PRINT - printf("depth_ms:%f, depth_samples/2:%d\n", chorus->depth_ms, chorus->mod_depth); -#endif - /* Initializes the modulated center position: - mod_rate, center_pos_mod, and index rate. - */ - set_center_position(chorus); /* must be called before set_xxxx_frequency() */ -#ifdef DEBUG_PRINT - printf("mod_rate:%d\n", chorus->mod_rate); -#endif - - /* initialize modulator frequency */ - for(i = 0; i < chorus->number_blocks; i++) - { - set_sinus_frequency(&chorus->mod[i].sinus, - chorus->speed_Hz * chorus->mod_rate, - chorus->sample_rate, - /* phase offset between modulators waveform */ - (float)((360.0f / (float) chorus->number_blocks) * i)); - - set_triangle_frequency(&chorus->mod[i].triang, - chorus->speed_Hz * chorus->mod_rate, - chorus->sample_rate, - /* phase offset between modulators waveform */ - (float)i / chorus->number_blocks); - } + /* update parameters dependant of sample rate */ + update_parameters_from_sample_rate(chorus); #ifdef DEBUG_PRINT printf("lfo type:%d\n", chorus->type); @@ -800,7 +833,7 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, fluid_real_t wet = chorus->level * SCALE_WET ; - /* wet1 and wet2 are used by the stereo effect controled by the width setting + /* wet1 and wet2 are used by the stereo effect controlled by the width setting for producing a stereo ouptput from a monophonic chorus signal. Please see the note above about a side effect tendency */ @@ -859,6 +892,32 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, } } +/* +* Applies a sample rate change on the chorus. +* Note that while the chorus is used by calling any fluid_chorus_processXXX() +* function, calling fluid_chorus_samplerate_change() isn't multi task safe. +* To deal properly with this issue follow the steps: +* 1) Stop chorus processing (i.e disable calling to any fluid_chorus_processXXX(). +* chorus functions. +* 2) Change sample rate by calling fluid_chorus_samplerate_change(). +* 3) Restart chorus processing (i.e enabling calling any fluid_chorus_processXXX() +* chorus functions. +* +* Another solution is to substitute step (2): +* 2.1) delete the chorus by calling delete_fluid_chorus(). +* 2.2) create the chorus by calling new_fluid_chorus(). +* +* @param chorus pointer on the chorus. +* @param sample_rate new sample rate value. +*/ +void +fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate) +{ + chorus->sample_rate = sample_rate; + + /* update parameters dependant of sample rate */ + update_parameters_from_sample_rate(chorus); +} /** * Process chorus by mixing the result in output buffer. @@ -921,13 +980,17 @@ void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, d_out[1] += out ; } + /* Write the current input sample into the circular buffer. + * Note that 'in' may be aliased with 'left_out'. Hence this must be done + * before "processing stereo unit" (below). This ensures input buffer + * not being overwritten by stereo unit output. + */ + push_in_delay_line(chorus, in[sample_index]); + /* process stereo unit */ /* Add the chorus stereo unit d_out to left and right output */ left_out[sample_index] += d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; right_out[sample_index] += d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; - - /* Write the current input sample into the circular buffer */ - push_in_delay_line(chorus, in[sample_index]); } } @@ -993,12 +1056,16 @@ void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, d_out[1] += out ; } + /* Write the current input sample into the circular buffer. + * Note that 'in' may be aliased with 'left_out'. Hence this must be done + * before "processing stereo unit" (below). This ensures input buffer + * not being overwritten by stereo unit output. + */ + push_in_delay_line(chorus, in[sample_index]); + /* process stereo unit */ /* store the chorus stereo unit d_out to left and right output */ left_out[sample_index] = d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; right_out[sample_index] = d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; - - /* Write the current input sample into the circular buffer */ - push_in_delay_line(chorus, in[sample_index]); } } diff --git a/libs/fluidsynth/src/fluid_chorus.h b/libs/fluidsynth/src/fluid_chorus.h index f815ac42d4..c6d247fa5f 100644 --- a/libs/fluidsynth/src/fluid_chorus.h +++ b/libs/fluidsynth/src/fluid_chorus.h @@ -27,14 +27,28 @@ typedef struct _fluid_chorus_t fluid_chorus_t; +/* enum describing each chorus parameter */ +enum fluid_chorus_param +{ + FLUID_CHORUS_NR, /**< number of delay line */ + FLUID_CHORUS_LEVEL, /**< output level */ + FLUID_CHORUS_SPEED, /**< lfo frequency */ + FLUID_CHORUS_DEPTH, /**< modulation depth */ + FLUID_CHORUS_TYPE, /**< type of waveform */ + FLUID_CHORUS_PARAM_LAST /* number of enum fluid_chorus_param */ +}; + +/* return a bit flag from param: 2^param */ +#define FLUID_CHORPARAM_TO_SETFLAG(param) (1 << param) + /** 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_NR = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_NR), + FLUID_CHORUS_SET_LEVEL = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_LEVEL), + FLUID_CHORUS_SET_SPEED = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_SPEED), + FLUID_CHORUS_SET_DEPTH = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_DEPTH), + FLUID_CHORUS_SET_TYPE = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_TYPE), /** Value for fluid_chorus_set() which sets all chorus parameters. */ FLUID_CHORUS_SET_ALL = FLUID_CHORUS_SET_NR @@ -53,6 +67,8 @@ void fluid_chorus_reset(fluid_chorus_t *chorus); void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, fluid_real_t speed, fluid_real_t depth_ms, int type); +void +fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate); void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); diff --git a/libs/fluidsynth/src/fluid_conv.c b/libs/fluidsynth/src/fluid_conv.c index 5c055d2be9..65dcde3097 100644 --- a/libs/fluidsynth/src/fluid_conv.c +++ b/libs/fluidsynth/src/fluid_conv.c @@ -20,7 +20,7 @@ #include "fluid_conv.h" #include "fluid_sys.h" -#include "fluid_conv_tables.c" +#include "fluid_conv_tables.inc.h" /* * Converts absolute cents to Hertz diff --git a/libs/fluidsynth/src/fluid_conv_tables.h b/libs/fluidsynth/src/fluid_conv_tables.h index 744733b49b..8d1ae71540 100644 --- a/libs/fluidsynth/src/fluid_conv_tables.h +++ b/libs/fluidsynth/src/fluid_conv_tables.h @@ -13,7 +13,7 @@ Note about usefulness of 24 bits: 1)Even fluidsynth is a 24 bit synth, this format is only relevant if the sample format coming from the soundfont is 24 bits and the audio sample format - choosen by the application (audio.sample.format) is not 16 bits. + chosen by the application (audio.sample.format) is not 16 bits. 2)When the sample soundfont is 16 bits, the internal 24 bits number have 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of diff --git a/libs/fluidsynth/src/fluid_conv_tables.c b/libs/fluidsynth/src/fluid_conv_tables.inc.h similarity index 100% rename from libs/fluidsynth/src/fluid_conv_tables.c rename to libs/fluidsynth/src/fluid_conv_tables.inc.h diff --git a/libs/fluidsynth/src/fluid_defsfont.c b/libs/fluidsynth/src/fluid_defsfont.c index 4639574dd9..c45696fce9 100644 --- a/libs/fluidsynth/src/fluid_defsfont.c +++ b/libs/fluidsynth/src/fluid_defsfont.c @@ -34,6 +34,8 @@ #define EMU_ATTENUATION_FACTOR (0.4f) /* Dynamic sample loading functions */ +static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); static void unload_sample(fluid_sample_t *sample); @@ -228,6 +230,17 @@ int delete_fluid_defsfont(fluid_defsfont_t *defsfont) fluid_return_val_if_fail(defsfont != NULL, FLUID_OK); + /* If we use dynamic sample loading, make sure we unpin any + * pinned presets before removing this soundfont */ + if(defsfont->dynamic_samples) + { + for(list = defsfont->preset; list; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + unpin_preset_samples(defsfont, preset); + } + } + /* Check that no samples are currently used */ for(list = defsfont->sample; list; list = fluid_list_next(list)) { @@ -336,7 +349,7 @@ int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, f return FLUID_OK; } - /* Ogg Vorbis samples already have loop pointers relative to the invididual decompressed sample, + /* Ogg Vorbis samples already have loop pointers relative to the individual decompressed sample, * but SF2 samples are relative to sample chunk start, so they need to be adjusted */ if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) { @@ -361,6 +374,7 @@ int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdat fluid_list_t *list; fluid_sample_t *sample; int sf3_file = (sfdata->version.major == 3); + int sample_parsing_result = FLUID_OK; /* For SF2 files, we load the sample data in one large block */ if(!sf3_file) @@ -379,6 +393,8 @@ int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdat } } + #pragma omp parallel + #pragma omp single for(list = defsfont->sample; list; list = fluid_list_next(list)) { sample = fluid_list_get(list); @@ -387,26 +403,37 @@ int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdat { /* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format * anyway */ - if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED) + #pragma omp task firstprivate(sample,sfdata,defsfont) shared(sample_parsing_result) default(none) { - FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name); - return FLUID_FAILED; + if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED) + { + #pragma omp critical + { + FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name); + sample_parsing_result = FLUID_FAILED; + } + } + else + { + fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); + fluid_voice_optimize_sample(sample); + } } - - fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); } else { - /* Data pointers of SF2 samples point to large sample data block loaded above */ - sample->data = defsfont->sampledata; - sample->data24 = defsfont->sample24data; - fluid_sample_sanitize_loop(sample, defsfont->samplesize); + #pragma omp task firstprivate(sample, defsfont) default(none) + { + /* Data pointers of SF2 samples point to large sample data block loaded above */ + sample->data = defsfont->sampledata; + sample->data24 = defsfont->sample24data; + fluid_sample_sanitize_loop(sample, defsfont->samplesize); + fluid_voice_optimize_sample(sample); + } } - - fluid_voice_optimize_sample(sample); } - return FLUID_OK; + return sample_parsing_result; } /* @@ -506,7 +533,7 @@ int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t goto err_exit; } - if(fluid_defpreset_import_sfont(defpreset, sfpreset, defsfont) != FLUID_OK) + if(fluid_defpreset_import_sfont(defpreset, sfpreset, defsfont, sfdata) != FLUID_OK) { goto err_exit; } @@ -535,7 +562,7 @@ err_exit: */ int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample) { - defsfont->sample = fluid_list_append(defsfont->sample, sample); + defsfont->sample = fluid_list_prepend(defsfont->sample, sample); return FLUID_OK; } @@ -554,16 +581,16 @@ int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *def fluid_defpreset_preset_noteon, fluid_defpreset_preset_delete); - if(defsfont->dynamic_samples) - { - preset->notify = dynamic_samples_preset_notify; - } - if(preset == NULL) { return FLUID_FAILED; } + if(defsfont->dynamic_samples) + { + preset->notify = dynamic_samples_preset_notify; + } + fluid_preset_set_data(preset, defpreset); defsfont->preset = fluid_list_append(defsfont->preset, preset); @@ -637,6 +664,7 @@ new_fluid_defpreset(void) defpreset->num = 0; defpreset->global_zone = NULL; defpreset->zone = NULL; + defpreset->pinned = FALSE; return defpreset; } @@ -776,7 +804,7 @@ fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice, /* Although local_mod and global_mod lists was limited to FLUID_NUM_MOD at soundfont loading time, it is possible that local + global modulators exceeds FLUID_NUM_MOD. - So, checks if mod_list_count reachs the limit. + So, checks if mod_list_count reaches the limit. */ if(mod_list_count >= FLUID_NUM_MOD) { @@ -805,7 +833,7 @@ fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice, */ /* Restrict identity check to the actual number of voice modulators */ - /* Acual number of voice modulators : defaults + [instruments] */ + /* Actual number of voice modulators : defaults + [instruments] */ identity_limit_count = voice->mod_count; for(i = 0; i < mod_list_count; i++) @@ -1000,7 +1028,8 @@ fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_ int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, - fluid_defsfont_t *defsfont) + fluid_defsfont_t *defsfont, + SFData *sfdata) { fluid_list_t *p; SFZone *sfzone; @@ -1033,7 +1062,7 @@ fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, return FLUID_FAILED; } - if(fluid_preset_zone_import_sfont(zone, sfzone, defsfont) != FLUID_OK) + if(fluid_preset_zone_import_sfont(zone, sfzone, defsfont, sfdata) != FLUID_OK) { delete_fluid_preset_zone(zone); return FLUID_FAILED; @@ -1396,8 +1425,13 @@ fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone gen[sfgen->id].flags = GEN_SET; break; + case GEN_INSTRUMENT: + case GEN_SAMPLEID: + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.uword; + gen[sfgen->id].flags = GEN_SET; + break; + default: - /* FIXME: some generators have an unsigne word amount value but i don't know which ones */ gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; gen[sfgen->id].flags = GEN_SET; break; @@ -1413,7 +1447,7 @@ fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone * @param src, pointer on destination modulator source. * @param flags, pointer on destination modulator flags. * @param sf_source, soundfont modulator source. - * @return return TRUE if success, FALSE if source type is unknow. + * @return return TRUE if success, FALSE if source type is unknown. */ static int fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, unsigned short sf_source) @@ -1602,24 +1636,27 @@ fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone) * fluid_preset_zone_import_sfont */ int -fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont) +fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata) { /* import the generators */ fluid_zone_gen_import_sfont(zone->gen, &zone->range, sfzone); - if((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) + if(zone->gen[GEN_INSTRUMENT].flags == GEN_SET) { - SFInst *sfinst = sfzone->instsamp->data; + int inst_idx = (int) zone->gen[GEN_INSTRUMENT].val; - zone->inst = find_inst_by_idx(defsfont, sfinst->idx); + zone->inst = find_inst_by_idx(defsfont, inst_idx); if(zone->inst == NULL) { - zone->inst = fluid_inst_import_sfont(sfinst, defsfont); + zone->inst = fluid_inst_import_sfont(inst_idx, defsfont, sfdata); } if(zone->inst == NULL) { + + FLUID_LOG(FLUID_ERR, "Preset zone %s: Invalid instrument reference", + zone->name); return FLUID_FAILED; } @@ -1627,6 +1664,9 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_ { return FLUID_FAILED; } + + /* We don't need this generator anymore */ + zone->gen[GEN_INSTRUMENT].flags = GEN_UNUSED; } /* Import the modulators (only SF2.1 and higher) */ @@ -1707,15 +1747,30 @@ fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) * fluid_inst_import_sfont */ fluid_inst_t * -fluid_inst_import_sfont(SFInst *sfinst, fluid_defsfont_t *defsfont) +fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata) { fluid_list_t *p; + fluid_list_t *inst_list; fluid_inst_t *inst; SFZone *sfzone; + SFInst *sfinst; fluid_inst_zone_t *inst_zone; char zone_name[256]; int count; + for (inst_list = sfdata->inst; inst_list; inst_list = fluid_list_next(inst_list)) + { + sfinst = fluid_list_get(inst_list); + if (sfinst->idx == inst_idx) + { + break; + } + } + if (inst_list == NULL) + { + return NULL; + } + inst = (fluid_inst_t *) new_fluid_inst(); if(inst == NULL) @@ -1753,7 +1808,7 @@ fluid_inst_import_sfont(SFInst *sfinst, fluid_defsfont_t *defsfont) return NULL; } - if(fluid_inst_zone_import_sfont(inst_zone, sfzone, defsfont) != FLUID_OK) + if(fluid_inst_zone_import_sfont(inst_zone, sfzone, defsfont, sfdata) != FLUID_OK) { delete_fluid_inst_zone(inst_zone); return NULL; @@ -1885,7 +1940,8 @@ fluid_inst_zone_next(fluid_inst_zone_t *zone) * fluid_inst_zone_import_sfont */ int -fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont) +fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, + SFData *sfdata) { /* import the generators */ fluid_zone_gen_import_sfont(inst_zone->gen, &inst_zone->range, sfzone); @@ -1895,10 +1951,32 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid /* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */ /* } */ - /* fixup sample pointer */ - if((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) + if (inst_zone->gen[GEN_SAMPLEID].flags == GEN_SET) { - inst_zone->sample = ((SFSample *)(sfzone->instsamp->data))->fluid_sample; + fluid_list_t *list; + SFSample *sfsample; + int sample_idx = (int) inst_zone->gen[GEN_SAMPLEID].val; + + /* find the SFSample by index */ + for(list = sfdata->sample; list; list = fluid_list_next(list)) + { + sfsample = fluid_list_get(list); + if (sfsample->idx == sample_idx) + { + break; + } + } + if (list == NULL) + { + FLUID_LOG(FLUID_ERR, "Instrument zone '%s': Invalid sample reference", + inst_zone->name); + return FLUID_FAILED; + } + + inst_zone->sample = sfsample->fluid_sample; + + /* we don't need this generator anymore, mark it as unused */ + inst_zone->gen[GEN_SAMPLEID].flags = GEN_UNUSED; } /* Import the modulators (only SF2.1 and higher) */ @@ -2003,15 +2081,74 @@ static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int { FLUID_LOG(FLUID_DBG, "Selected preset '%s' on channel %d", fluid_preset_get_name(preset), chan); defsfont = fluid_sfont_get_data(preset->sfont); - load_preset_samples(defsfont, preset); + return load_preset_samples(defsfont, preset); } - else if(reason == FLUID_PRESET_UNSELECTED) + + if(reason == FLUID_PRESET_UNSELECTED) { FLUID_LOG(FLUID_DBG, "Deselected preset '%s' from channel %d", fluid_preset_get_name(preset), chan); defsfont = fluid_sfont_get_data(preset->sfont); - unload_preset_samples(defsfont, preset); + return unload_preset_samples(defsfont, preset); } + if(reason == FLUID_PRESET_PIN) + { + defsfont = fluid_sfont_get_data(preset->sfont); + return pin_preset_samples(defsfont, preset); + } + + if(reason == FLUID_PRESET_UNPIN) + { + defsfont = fluid_sfont_get_data(preset->sfont); + return unpin_preset_samples(defsfont, preset); + } + + return FLUID_OK; +} + + +static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + + defpreset = fluid_preset_get_data(preset); + if (defpreset->pinned) + { + return FLUID_OK; + } + + FLUID_LOG(FLUID_DBG, "Pinning preset '%s'", fluid_preset_get_name(preset)); + + if(load_preset_samples(defsfont, preset) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + defpreset->pinned = TRUE; + + return FLUID_OK; +} + + +static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + + defpreset = fluid_preset_get_data(preset); + if (!defpreset->pinned) + { + return FLUID_OK; + } + + FLUID_LOG(FLUID_DBG, "Unpinning preset '%s'", fluid_preset_get_name(preset)); + + if(unload_preset_samples(defsfont, preset) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + defpreset->pinned = FALSE; + return FLUID_OK; } diff --git a/libs/fluidsynth/src/fluid_defsfont.h b/libs/fluidsynth/src/fluid_defsfont.h index 7f50faa694..b5129936ab 100644 --- a/libs/fluidsynth/src/fluid_defsfont.h +++ b/libs/fluidsynth/src/fluid_defsfont.h @@ -148,12 +148,13 @@ struct _fluid_defpreset_t unsigned int num; /* the preset number */ fluid_preset_zone_t *global_zone; /* the global zone of the preset */ fluid_preset_zone_t *zone; /* the chained list of preset zones */ + int pinned; /* preset samples pinned to sample cache? */ }; fluid_defpreset_t *new_fluid_defpreset(void); void delete_fluid_defpreset(fluid_defpreset_t *defpreset); fluid_defpreset_t *fluid_defpreset_next(fluid_defpreset_t *defpreset); -int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont); +int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont, SFData *sfdata); int fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); int fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); fluid_preset_zone_t *fluid_defpreset_get_zone(fluid_defpreset_t *defpreset); @@ -181,7 +182,7 @@ fluid_preset_zone_t *new_fluid_preset_zone(char *name); void delete_fluid_list_mod(fluid_mod_t *mod); void delete_fluid_preset_zone(fluid_preset_zone_t *zone); fluid_preset_zone_t *fluid_preset_zone_next(fluid_preset_zone_t *zone); -int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defssfont); +int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defssfont, SFData *sfdata); fluid_inst_t *fluid_preset_zone_get_inst(fluid_preset_zone_t *zone); /* @@ -196,7 +197,7 @@ struct _fluid_inst_t }; fluid_inst_t *new_fluid_inst(void); -fluid_inst_t *fluid_inst_import_sfont(SFInst *sfinst, fluid_defsfont_t *defsfont); +fluid_inst_t *fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata); void delete_fluid_inst(fluid_inst_t *inst); int fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); int fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); @@ -220,7 +221,7 @@ struct _fluid_inst_zone_t fluid_inst_zone_t *new_fluid_inst_zone(char *name); void delete_fluid_inst_zone(fluid_inst_zone_t *zone); fluid_inst_zone_t *fluid_inst_zone_next(fluid_inst_zone_t *zone); -int fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont); +int fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata); fluid_sample_t *fluid_inst_zone_get_sample(fluid_inst_zone_t *zone); diff --git a/libs/fluidsynth/src/fluid_event.c b/libs/fluidsynth/src/fluid_event.c index d7962eac98..3741c65c9e 100644 --- a/libs/fluidsynth/src/fluid_event.c +++ b/libs/fluidsynth/src/fluid_event.c @@ -47,6 +47,7 @@ fluid_event_clear(fluid_event_t *evt) evt->dest = -1; evt->src = -1; evt->type = -1; + evt->id = -1; } /** @@ -95,6 +96,12 @@ fluid_event_set_time(fluid_event_t *evt, unsigned int time) evt->time = time; } +void +fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id) +{ + evt->id = id; +} + /** * Set source of a sequencer event. \c src must be a unique sequencer ID or -1 if not set. * @param evt Sequencer event structure @@ -161,11 +168,18 @@ fluid_event_noteoff(fluid_event_t *evt, int channel, short key) /** * Set a sequencer event to be a note duration event. + * + * Before fluidsynth 2.2.0, this event type was naively implemented when used in conjunction with fluid_sequencer_register_fluidsynth(), + * because it simply enqueued a fluid_event_noteon() and fluid_event_noteoff(). + * A handling for overlapping notes was not implemented. Starting with 2.2.0, this changes: If a fluid_event_note() is already playing, + * while another fluid_event_note() arrives on the same @c channel and @c key, the earlier event will be canceled. * @param evt Sequencer event structure * @param channel MIDI channel number * @param key MIDI note number (0-127) * @param vel MIDI velocity value (0-127) * @param duration Duration of note in the time scale used by the sequencer (by default milliseconds) + * + * @note The application should decide whether to use only Notes with duration, or separate NoteOn and NoteOff events. */ void fluid_event_note(fluid_event_t *evt, int channel, short key, short vel, unsigned int duration) @@ -222,7 +236,7 @@ fluid_event_bank_select(fluid_event_t *evt, int channel, short bank_num) * @param val MIDI program number (0-127) */ void -fluid_event_program_change(fluid_event_t *evt, int channel, short val) +fluid_event_program_change(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_PROGRAMCHANGE; evt->channel = channel; @@ -248,18 +262,6 @@ fluid_event_program_select(fluid_event_t *evt, int channel, evt->control = bank_num; } -/** - * Set a sequencer event to be an any control change event (for internal use). - * @param evt Sequencer event structure - * @param channel MIDI channel number - */ -void -fluid_event_any_control_change(fluid_event_t *evt, int channel) -{ - evt->type = FLUID_SEQ_ANYCONTROLCHANGE; - evt->channel = channel; -} - /** * Set a sequencer event to be a pitch bend event. * @param evt Sequencer event structure @@ -292,7 +294,7 @@ fluid_event_pitch_bend(fluid_event_t *evt, int channel, int pitch) * @param value MIDI pitch wheel sensitivity value in semitones */ void -fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, short value) +fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, int value) { evt->type = FLUID_SEQ_PITCHWHEELSENS; evt->channel = channel; @@ -306,7 +308,7 @@ fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, short value) * @param val MIDI modulation value (0-127) */ void -fluid_event_modulation(fluid_event_t *evt, int channel, short val) +fluid_event_modulation(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_MODULATION; evt->channel = channel; @@ -331,7 +333,7 @@ fluid_event_modulation(fluid_event_t *evt, int channel, short val) * @param val MIDI sustain value (0-127) */ void -fluid_event_sustain(fluid_event_t *evt, int channel, short val) +fluid_event_sustain(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_SUSTAIN; evt->channel = channel; @@ -357,7 +359,7 @@ fluid_event_sustain(fluid_event_t *evt, int channel, short val) * @param val MIDI control value (0-127) */ void -fluid_event_control_change(fluid_event_t *evt, int channel, short control, short val) +fluid_event_control_change(fluid_event_t *evt, int channel, short control, int val) { evt->type = FLUID_SEQ_CONTROLCHANGE; evt->channel = channel; @@ -372,7 +374,7 @@ fluid_event_control_change(fluid_event_t *evt, int channel, short control, short * @param val MIDI panning value (0-127, 0=left, 64 = middle, 127 = right) */ void -fluid_event_pan(fluid_event_t *evt, int channel, short val) +fluid_event_pan(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_PAN; evt->channel = channel; @@ -397,7 +399,7 @@ fluid_event_pan(fluid_event_t *evt, int channel, short val) * @param val Volume value (0-127) */ void -fluid_event_volume(fluid_event_t *evt, int channel, short val) +fluid_event_volume(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_VOLUME; evt->channel = channel; @@ -422,7 +424,7 @@ fluid_event_volume(fluid_event_t *evt, int channel, short val) * @param val Reverb amount (0-127) */ void -fluid_event_reverb_send(fluid_event_t *evt, int channel, short val) +fluid_event_reverb_send(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_REVERBSEND; evt->channel = channel; @@ -447,7 +449,7 @@ fluid_event_reverb_send(fluid_event_t *evt, int channel, short val) * @param val Chorus amount (0-127) */ void -fluid_event_chorus_send(fluid_event_t *evt, int channel, short val) +fluid_event_chorus_send(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_CHORUSSEND; evt->channel = channel; @@ -477,6 +479,20 @@ fluid_event_unregistering(fluid_event_t *evt) evt->type = FLUID_SEQ_UNREGISTERING; } +/** + * Set a sequencer event to be a scale change event. + * Useful for scheduling tempo changes. + * @param evt Sequencer event structure + * @param new_scale The new time scale to apply to the sequencer, see fluid_sequencer_set_time_scale() + * @since 2.2.0 + */ +void +fluid_event_scale(fluid_event_t *evt, double new_scale) +{ + evt->type = FLUID_SEQ_SCALE; + evt->scale = new_scale; +} + /** * Set a sequencer event to be a channel-wide aftertouch event. * @param evt Sequencer event structure @@ -485,7 +501,7 @@ fluid_event_unregistering(fluid_event_t *evt) * @since 1.1.0 */ void -fluid_event_channel_pressure(fluid_event_t *evt, int channel, short val) +fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val) { evt->type = FLUID_SEQ_CHANNELPRESSURE; evt->channel = channel; @@ -512,7 +528,7 @@ fluid_event_channel_pressure(fluid_event_t *evt, int channel, short val) * @since 2.0.0 */ void -fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, short val) +fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val) { evt->type = FLUID_SEQ_KEYPRESSURE; evt->channel = channel; @@ -579,6 +595,17 @@ unsigned int fluid_event_get_time(fluid_event_t *evt) return evt->time; } +/** + * @internal + * Get the time field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Time value + */ +fluid_note_id_t fluid_event_get_id(fluid_event_t *evt) +{ + return evt->id; +} + /** * Get the source sequencer client from a sequencer event structure. * @param evt Sequencer event structure @@ -651,7 +678,7 @@ short fluid_event_get_control(fluid_event_t *evt) * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME, * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND. */ -short fluid_event_get_value(fluid_event_t *evt) +int fluid_event_get_value(fluid_event_t *evt) { return evt->value; } @@ -713,7 +740,7 @@ int fluid_event_get_pitch(fluid_event_t *evt) * Used by the #FLUID_SEQ_PROGRAMCHANGE and #FLUID_SEQ_PROGRAMSELECT * event types. */ -short +int fluid_event_get_program(fluid_event_t *evt) { return evt->value; @@ -732,182 +759,14 @@ fluid_event_get_sfont_id(fluid_event_t *evt) return evt->duration; } - - -/********************/ -/* heap management */ -/********************/ - -fluid_evt_heap_t * -_fluid_evt_heap_init(int nbEvents) +/** + * Gets time scale field from a sequencer event structure. + * @param evt Sequencer event structure + * @return SoundFont identifier value. + * + * Used by the #FLUID_SEQ_SCALE event type. + */ +double fluid_event_get_scale(fluid_event_t *evt) { -#ifdef HEAP_WITH_DYNALLOC - - int i; - fluid_evt_heap_t *heap; - fluid_evt_entry *tmp; - - heap = FLUID_NEW(fluid_evt_heap_t); - - if(heap == NULL) - { - FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); - return NULL; - } - - heap->freelist = NULL; - fluid_mutex_init(heap->mutex); - - /* LOCK */ - fluid_mutex_lock(heap->mutex); - - /* Allocate the event entries */ - for(i = 0; i < nbEvents; i++) - { - tmp = FLUID_NEW(fluid_evt_entry); - tmp->next = heap->freelist; - heap->freelist = tmp; - } - - /* UNLOCK */ - fluid_mutex_unlock(heap->mutex); - - -#else - int i; - fluid_evt_heap_t *heap; - int siz = 2 * sizeof(fluid_evt_entry *) + sizeof(fluid_evt_entry) * nbEvents; - - heap = (fluid_evt_heap_t *)FLUID_MALLOC(siz); - - if(heap == NULL) - { - FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); - return NULL; - } - - FLUID_MEMSET(heap, 0, siz); - - /* link all heap events */ - { - fluid_evt_entry *tmp = &(heap->pool); - - for(i = 0 ; i < nbEvents - 1 ; i++) - { - tmp[i].next = &(tmp[i + 1]); - } - - tmp[nbEvents - 1].next = NULL; - - /* set head & tail */ - heap->tail = &(tmp[nbEvents - 1]); - heap->head = &(heap->pool); - } -#endif - return (heap); -} - -void -_fluid_evt_heap_free(fluid_evt_heap_t *heap) -{ -#ifdef HEAP_WITH_DYNALLOC - fluid_evt_entry *tmp, *next; - - /* LOCK */ - fluid_mutex_lock(heap->mutex); - - tmp = heap->freelist; - - while(tmp) - { - next = tmp->next; - FLUID_FREE(tmp); - tmp = next; - } - - /* UNLOCK */ - fluid_mutex_unlock(heap->mutex); - fluid_mutex_destroy(heap->mutex); - - FLUID_FREE(heap); - -#else - FLUID_FREE(heap); -#endif -} - -fluid_evt_entry * -_fluid_seq_heap_get_free(fluid_evt_heap_t *heap) -{ -#ifdef HEAP_WITH_DYNALLOC - fluid_evt_entry *evt = NULL; - - /* LOCK */ - fluid_mutex_lock(heap->mutex); - -#if !defined(MACOS9) - - if(heap->freelist == NULL) - { - heap->freelist = FLUID_NEW(fluid_evt_entry); - - if(heap->freelist != NULL) - { - heap->freelist->next = NULL; - } - } - -#endif - - evt = heap->freelist; - - if(evt != NULL) - { - heap->freelist = heap->freelist->next; - evt->next = NULL; - } - - /* UNLOCK */ - fluid_mutex_unlock(heap->mutex); - - return evt; - -#else - fluid_evt_entry *evt; - - if(heap->head == NULL) - { - return NULL; - } - - /* take from head of the heap */ - /* critical - should threadlock ? */ - evt = heap->head; - heap->head = heap->head->next; - - return evt; -#endif -} - -void -_fluid_seq_heap_set_free(fluid_evt_heap_t *heap, fluid_evt_entry *evt) -{ -#ifdef HEAP_WITH_DYNALLOC - - /* LOCK */ - fluid_mutex_lock(heap->mutex); - - evt->next = heap->freelist; - heap->freelist = evt; - - /* UNLOCK */ - fluid_mutex_unlock(heap->mutex); - -#else - /* append to the end of the heap */ - /* critical - should threadlock ? */ - heap->tail->next = evt; - heap->tail = evt; - evt->next = NULL; -#endif + return evt->scale; } diff --git a/libs/fluidsynth/src/fluid_event.h b/libs/fluidsynth/src/fluid_event.h index 4beb0147e5..4a0cca5d93 100644 --- a/libs/fluidsynth/src/fluid_event.h +++ b/libs/fluidsynth/src/fluid_event.h @@ -23,7 +23,12 @@ #define _FLUID_EVENT_PRIV_H #include "fluidsynth.h" -#include "fluid_sys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int fluid_note_id_t; /* Private data for event */ /* ?? should be optimized in size, using unions */ @@ -37,51 +42,24 @@ struct _fluid_event_t short key; short vel; short control; - short value; - short id; //?? unused ? + int value; + fluid_note_id_t id; int pitch; unsigned int duration; + double scale; void *data; }; unsigned int fluid_event_get_time(fluid_event_t *evt); void fluid_event_set_time(fluid_event_t *evt, unsigned int time); +fluid_note_id_t fluid_event_get_id(fluid_event_t *evt); +void fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id); + void fluid_event_clear(fluid_event_t *evt); -/* private data for sorter + heap */ -enum fluid_evt_entry_type -{ - FLUID_EVT_ENTRY_INSERT = 0, - FLUID_EVT_ENTRY_REMOVE -}; - -typedef struct _fluid_evt_entry fluid_evt_entry; -struct _fluid_evt_entry -{ - fluid_evt_entry *next; - short entryType; - fluid_event_t evt; -}; - -#define HEAP_WITH_DYNALLOC 1 -/* #undef HEAP_WITH_DYNALLOC */ - -typedef struct _fluid_evt_heap_t -{ -#ifdef HEAP_WITH_DYNALLOC - fluid_evt_entry *freelist; - fluid_mutex_t mutex; -#else - fluid_evt_entry *head; - fluid_evt_entry *tail; - fluid_evt_entry pool; +#ifdef __cplusplus +} #endif -} fluid_evt_heap_t; - -fluid_evt_heap_t *_fluid_evt_heap_init(int nbEvents); -void _fluid_evt_heap_free(fluid_evt_heap_t *heap); -fluid_evt_entry *_fluid_seq_heap_get_free(fluid_evt_heap_t *heap); -void _fluid_seq_heap_set_free(fluid_evt_heap_t *heap, fluid_evt_entry *evt); #endif /* _FLUID_EVENT_PRIV_H */ diff --git a/libs/fluidsynth/src/fluid_gen.c b/libs/fluidsynth/src/fluid_gen.c index 6472dd3258..2ce2b0f74b 100644 --- a/libs/fluidsynth/src/fluid_gen.c +++ b/libs/fluidsynth/src/fluid_gen.c @@ -23,73 +23,76 @@ #include "fluid_chan.h" +#define _GEN(_name) GEN_ ## _name, #_name + + /* See SFSpec21 $8.1.3 */ static const fluid_gen_info_t fluid_gen_info[] = { /* number/name init nrpn-scale min max def */ - { GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f }, - { GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f }, - { GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, - { GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, - { GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f }, - { GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f }, - { GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f }, - { GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, - { GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f }, - { GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f }, - { GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, - { GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, - { GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f }, - { GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, - { GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, - { GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f }, - { GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, - { GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f }, - { GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, - { GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, - { GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, - { GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, - { GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, - { GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f }, - { GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f }, - { GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f }, - { GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, - { GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f }, - { GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f }, - { GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f }, - { GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f }, - { GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f }, - { GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f }, - { GEN_CUSTOM_BALANCE, 1, 0, -960.0f, 960.0f, 0.0f }, - { GEN_CUSTOM_FILTERFC, 1, 2, 0.0f, 22050.0f, 0.0f }, - { GEN_CUSTOM_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f } + { _GEN(STARTADDROFS), 1, 1, 0.0f, 1e10f, 0.0f }, + { _GEN(ENDADDROFS), 1, 1, -1e10f, 0.0f, 0.0f }, + { _GEN(STARTLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(ENDLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(STARTADDRCOARSEOFS), 0, 1, 0.0f, 1e10f, 0.0f }, + { _GEN(MODLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(VIBLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(MODENVTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(FILTERFC), 1, 2, 1500.0f, 13500.0f, 13500.0f }, + { _GEN(FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f }, + { _GEN(MODLFOTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(MODENVTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(ENDADDRCOARSEOFS), 0, 1, -1e10f, 0.0f, 0.0f }, + { _GEN(MODLFOTOVOL), 1, 1, -960.0f, 960.0f, 0.0f }, + { _GEN(UNUSED1), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(CHORUSSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(REVERBSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(PAN), 1, 1, -500.0f, 500.0f, 0.0f }, + { _GEN(UNUSED2), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(UNUSED3), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(UNUSED4), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(MODLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, + { _GEN(VIBLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VIBLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, + { _GEN(MODENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(MODENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(MODENVSUSTAIN), 0, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(MODENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(KEYTOMODENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(KEYTOMODENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(VOLENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VOLENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(VOLENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VOLENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(VOLENVSUSTAIN), 0, 1, 0.0f, 1440.0f, 0.0f }, + { _GEN(VOLENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(KEYTOVOLENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(KEYTOVOLENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(INSTRUMENT), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(RESERVED1), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(KEYRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(VELRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(STARTLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(KEYNUM), 1, 0, 0.0f, 127.0f, -1.0f }, + { _GEN(VELOCITY), 1, 1, 0.0f, 127.0f, -1.0f }, + { _GEN(ATTENUATION), 1, 1, 0.0f, 1440.0f, 0.0f }, + { _GEN(RESERVED2), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(ENDLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(COARSETUNE), 0, 1, -120.0f, 120.0f, 0.0f }, + { _GEN(FINETUNE), 0, 1, -99.0f, 99.0f, 0.0f }, + { _GEN(SAMPLEID), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(SAMPLEMODE), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(RESERVED3), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(SCALETUNE), 0, 1, 0.0f, 1200.0f, 100.0f }, + { _GEN(EXCLUSIVECLASS), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(OVERRIDEROOTKEY), 1, 0, 0.0f, 127.0f, -1.0f }, + { _GEN(PITCH), 1, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(CUSTOM_BALANCE), 1, 0, -960.0f, 960.0f, 0.0f }, + { _GEN(CUSTOM_FILTERFC), 1, 2, 0.0f, 22050.0f, 0.0f }, + { _GEN(CUSTOM_FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f } }; /* fluid_gen_init @@ -122,3 +125,9 @@ fluid_real_t fluid_gen_scale_nrpn(int gen, int data) fluid_clip(data, -8192, 8192); return (fluid_real_t)(data * fluid_gen_info[gen].nrpn_scale); } + + +const char *fluid_gen_name(int gen) +{ + return fluid_gen_info[gen].name; +} diff --git a/libs/fluidsynth/src/fluid_gen.h b/libs/fluidsynth/src/fluid_gen.h index 3f8f14b1c5..75a4f39e8a 100644 --- a/libs/fluidsynth/src/fluid_gen.h +++ b/libs/fluidsynth/src/fluid_gen.h @@ -27,6 +27,7 @@ typedef struct _fluid_gen_info_t { char num; /* Generator number */ + char const *name; char init; /* Does the generator need to be initialized (not used) */ char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ float min; /* The minimum value */ @@ -60,6 +61,7 @@ enum fluid_gen_flags fluid_real_t fluid_gen_scale(int gen, float value); fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn); void fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel); +const char *fluid_gen_name(int gen); #endif /* _FLUID_GEN_H */ diff --git a/libs/fluidsynth/src/fluid_hash.c b/libs/fluidsynth/src/fluid_hash.c index 79f83a4583..46f701c4ba 100644 --- a/libs/fluidsynth/src/fluid_hash.c +++ b/libs/fluidsynth/src/fluid_hash.c @@ -129,7 +129,7 @@ spaced_primes_closest(unsigned int num) * 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. + * pointer on the node that precedes 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 diff --git a/libs/fluidsynth/src/fluid_list.c b/libs/fluidsynth/src/fluid_list.c index c92d731f89..69f6c60c4c 100644 --- a/libs/fluidsynth/src/fluid_list.c +++ b/libs/fluidsynth/src/fluid_list.c @@ -319,3 +319,19 @@ fluid_list_str_compare_func(void *a, void *b) return 1; } + +int fluid_list_idx(fluid_list_t *list, void *data) +{ + int i = 0; + + while(list) + { + if (list->data == data) + { + return i; + } + list = list->next; + } + + return -1; +} diff --git a/libs/fluidsynth/src/fluid_list.h b/libs/fluidsynth/src/fluid_list.h index 5be1cc3923..2c00446af5 100644 --- a/libs/fluidsynth/src/fluid_list.h +++ b/libs/fluidsynth/src/fluid_list.h @@ -52,6 +52,7 @@ fluid_list_t *fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink); fluid_list_t *fluid_list_nth(fluid_list_t *list, int n); fluid_list_t *fluid_list_last(fluid_list_t *list); fluid_list_t *fluid_list_insert_at(fluid_list_t *list, int n, void *data); +int fluid_list_idx(fluid_list_t *list, void *data); int fluid_list_size(fluid_list_t *list); #define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL) diff --git a/libs/fluidsynth/src/fluid_midi.c b/libs/fluidsynth/src/fluid_midi.c index 844de01c8f..830aada199 100644 --- a/libs/fluidsynth/src/fluid_midi.c +++ b/libs/fluidsynth/src/fluid_midi.c @@ -48,18 +48,13 @@ static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track); static int fluid_track_get_duration(fluid_track_t *track); static int fluid_track_reset(fluid_track_t *track); -static void fluid_track_send_events(fluid_track_t *track, - fluid_synth_t *synth, - fluid_player_t *player, - unsigned int ticks); - - static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track); static int fluid_player_callback(void *data, unsigned int msec); static int fluid_player_reset(fluid_player_t *player); static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item); static void fluid_player_advancefile(fluid_player_t *player); static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec); +static void fluid_player_update_tempo(fluid_player_t *player); static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length); static void delete_fluid_midi_file(fluid_midi_file *mf); @@ -180,7 +175,7 @@ fluid_file_read_full(fluid_file fp, size_t *length) return NULL; } - FLUID_LOG(FLUID_DBG, "File load: Allocating %lu bytes", buflen); + FLUID_LOG(FLUID_DBG, "File load: Allocating %lu bytes", (unsigned long)buflen); buffer = FLUID_MALLOC(buflen); if(buffer == NULL) @@ -193,8 +188,8 @@ fluid_file_read_full(fluid_file fp, size_t *length) if(n != buflen) { - FLUID_LOG(FLUID_ERR, "Only read %lu bytes; expected %lu", n, - buflen); + FLUID_LOG(FLUID_ERR, "Only read %lu bytes; expected %lu", (unsigned long)n, + (unsigned long)buflen); FLUID_FREE(buffer); return NULL; }; @@ -406,8 +401,7 @@ fluid_isasciistring(char *s) /* From ctype.h */ #define fluid_isascii(c) (((c) & ~0x7f) == 0) - int i; - int len = (int) FLUID_STRLEN(s); + size_t i, len = FLUID_STRLEN(s); for(i = 0; i < len; i++) { @@ -1458,7 +1452,7 @@ delete_fluid_track(fluid_track_t *track) int fluid_track_set_name(fluid_track_t *track, char *name) { - int len; + size_t len; if(track->name != NULL) { @@ -1553,18 +1547,20 @@ fluid_track_reset(fluid_track_t *track) /* * fluid_track_send_events */ -void +static void fluid_track_send_events(fluid_track_t *track, fluid_synth_t *synth, fluid_player_t *player, - unsigned int ticks) + unsigned int ticks, + int seek_ticks + ) { fluid_midi_event_t *event; - int seeking = player->seek_ticks >= 0; + int seeking = seek_ticks >= 0; if(seeking) { - ticks = player->seek_ticks; /* update target ticks */ + ticks = seek_ticks; /* update target ticks */ if(track->ticks > ticks) { @@ -1598,8 +1594,9 @@ fluid_track_send_events(fluid_track_t *track, if(!player || event->type == MIDI_EOT) { + /* don't send EOT events to the callback */ } - else if(seeking && (event->type == NOTE_ON || event->type == NOTE_OFF)) + else if(seeking && track->ticks != ticks && (event->type == NOTE_ON || event->type == NOTE_OFF)) { /* skip on/off messages */ } @@ -1611,9 +1608,11 @@ fluid_track_send_events(fluid_track_t *track, } } - if(event->type == MIDI_SET_TEMPO) + if(event->type == MIDI_SET_TEMPO && player != NULL) { - fluid_player_set_midi_tempo(player, event->param1); + /* memorize the tempo change value coming from the MIDI file */ + fluid_atomic_int_set(&player->miditempo, event->param1); + fluid_player_update_tempo(player); } fluid_track_next_event(track); @@ -1652,7 +1651,7 @@ new_fluid_player(fluid_synth_t *synth) return NULL; } - player->status = FLUID_PLAYER_READY; + fluid_atomic_int_set(&player->status, FLUID_PLAYER_READY); player->loop = 1; player->ntracks = 0; @@ -1667,13 +1666,22 @@ new_fluid_player(fluid_synth_t *synth) player->playlist = NULL; player->currentfile = NULL; player->division = 0; - player->send_program_change = 1; + + /* internal tempo (from MIDI file) in micro seconds per quarter note */ + player->sync_mode = 1; /* the player follows internal tempo change */ player->miditempo = 500000; + /* external tempo in micro seconds per quarter note */ + player->exttempo = 500000; + /* tempo multiplier */ + player->multempo = 1.0F; + player->deltatime = 4.0; player->cur_msec = 0; player->cur_ticks = 0; - player->seek_ticks = -1; + player->last_callback_ticks = -1; + fluid_atomic_int_set(&player->seek_ticks, -1); fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth); + fluid_player_set_tick_callback(player, NULL, NULL); player->use_system_timer = fluid_settings_str_equal(synth->settings, "player.timing-source", "system"); if(player->use_system_timer) @@ -1723,6 +1731,9 @@ delete_fluid_player(fluid_player_t *player) fluid_return_if_fail(player != NULL); + fluid_settings_callback_int(player->synth->settings, "player.reset-synth", + NULL, NULL); + fluid_player_stop(player); fluid_player_reset(player); @@ -1779,7 +1790,6 @@ fluid_player_reset(fluid_player_t *player) /* player->loop = 1; */ player->ntracks = 0; player->division = 0; - player->send_program_change = 1; player->miditempo = 500000; player->deltatime = 4.0; return 0; @@ -1803,16 +1813,18 @@ fluid_player_add_track(fluid_player_t *player, fluid_track_t *track) } /** - * Change the MIDI callback function. This is usually set to - * fluid_synth_handle_midi_event, but can optionally be changed - * to a user-defined function instead, for intercepting all MIDI - * messages sent to the synth. You can also use a midi router as - * the callback function to modify the MIDI messages before sending - * them to the synth. + * Change the MIDI callback function. + * * @param player MIDI player instance * @param handler Pointer to callback function * @param handler_data Parameter sent to the callback function * @returns FLUID_OK + * + * This is usually set to fluid_synth_handle_midi_event(), but can optionally + * be changed to a user-defined function instead, for intercepting all MIDI + * messages sent to the synth. You can also use a midi router as the callback + * function to modify the MIDI messages before sending them to the synth. + * * @since 1.1.4 */ int @@ -1824,6 +1836,28 @@ fluid_player_set_playback_callback(fluid_player_t *player, return FLUID_OK; } +/** + * Add a listener function for every MIDI tick change. + * + * @param player MIDI player instance + * @param handler Pointer to callback function + * @param handler_data Opaque parameter to be sent to the callback function + * @returns #FLUID_OK + * + * This callback is not set by default, but can optionally + * be changed to a user-defined function for intercepting all MIDI + * tick changes and react to them with precision. + * + * @since 2.2.0 + */ +int +fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data) +{ + player->tick_callback = handler; + player->tick_userdata = handler_data; + return FLUID_OK; +} + /** * Add a MIDI file to a player queue. * @param player MIDI player instance @@ -1944,7 +1978,7 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) } player->division = fluid_midi_file_get_division(midifile); - fluid_player_set_midi_tempo(player, player->miditempo); // Update deltatime + fluid_player_update_tempo(player); // Update deltatime /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ if(fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) @@ -2010,7 +2044,7 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) if(player->currentfile == NULL) { /* Failed to find next song, probably since we're finished */ - player->status = FLUID_PLAYER_DONE; + fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE); return; } @@ -2026,11 +2060,6 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) player->start_ticks = 0; player->cur_ticks = 0; - if(player->reset_synth_between_songs) - { - fluid_synth_system_reset(player->synth); - } - for(i = 0; i < player->ntracks; i++) { if(player->track[i] != NULL) @@ -2056,13 +2085,17 @@ fluid_player_callback(void *data, unsigned int msec) loadnextfile = player->currentfile == NULL ? 1 : 0; - if(player->status == FLUID_PLAYER_DONE) + if(fluid_player_get_status(player) != FLUID_PLAYER_PLAYING) { fluid_synth_all_notes_off(synth, -1); + fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_STOPPING, FLUID_PLAYER_DONE); return 1; } do { + float deltatime; + int seek_ticks; + if(loadnextfile) { loadnextfile = 0; @@ -2075,11 +2108,13 @@ fluid_player_callback(void *data, unsigned int msec) } player->cur_msec = msec; + deltatime = fluid_atomic_float_get(&player->deltatime); player->cur_ticks = (player->start_ticks + (int)((double)(player->cur_msec - player->start_msec) - / player->deltatime + 0.5)); /* 0.5 to average overall error when casting */ + / deltatime + 0.5)); /* 0.5 to average overall error when casting */ - if(player->seek_ticks >= 0) + seek_ticks = fluid_atomic_int_get(&player->seek_ticks); + if(seek_ticks >= 0) { fluid_synth_all_sounds_off(synth, -1); /* avoid hanging notes */ } @@ -2089,29 +2124,40 @@ fluid_player_callback(void *data, unsigned int msec) if(!fluid_track_eot(player->track[i])) { status = FLUID_PLAYER_PLAYING; - fluid_track_send_events(player->track[i], synth, player, player->cur_ticks); + fluid_track_send_events(player->track[i], synth, player, player->cur_ticks, seek_ticks); } } - if(player->seek_ticks >= 0) + if(seek_ticks >= 0) { - player->start_ticks = player->seek_ticks; /* tick position of last tempo value (which is now) */ - player->cur_ticks = player->seek_ticks; + player->start_ticks = seek_ticks; /* tick position of last tempo value (which is now) */ + player->cur_ticks = seek_ticks; player->begin_msec = msec; /* only used to calculate the duration of playing */ player->start_msec = msec; /* should be the (synth)-time of the last tempo change */ - player->seek_ticks = -1; /* clear seek_ticks */ + fluid_atomic_int_set(&player->seek_ticks, -1); /* clear seek_ticks */ } if(status == FLUID_PLAYER_DONE) { FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__, __LINE__, (msec - player->begin_msec) / 1000.0); + + if(player->reset_synth_between_songs) + { + fluid_synth_system_reset(player->synth); + } + loadnextfile = 1; } + + if (player->tick_callback != NULL && player->last_callback_ticks != player->cur_ticks) { + player->tick_callback(player->tick_userdata, player->cur_ticks); + player->last_callback_ticks = player->cur_ticks; + } } while(loadnextfile); - player->status = status; + fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_PLAYING, status); return 1; } @@ -2124,7 +2170,7 @@ fluid_player_callback(void *data, unsigned int msec) int fluid_player_play(fluid_player_t *player) { - if(player->status == FLUID_PLAYER_PLAYING || + if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING || player->playlist == NULL) { return FLUID_OK; @@ -2135,21 +2181,23 @@ fluid_player_play(fluid_player_t *player) fluid_sample_timer_reset(player->synth, player->sample_timer); } - player->status = FLUID_PLAYER_PLAYING; + fluid_atomic_int_set(&player->status, FLUID_PLAYER_PLAYING); return FLUID_OK; } /** * Pauses the MIDI playback. * - * It will not rewind to the beginning of the file, use fluid_player_seek() for this purpose. * @param player MIDI player instance * @return Always returns #FLUID_OK + * + * It will not rewind to the beginning of the file, use fluid_player_seek() for this purpose. */ int fluid_player_stop(fluid_player_t *player) { - player->status = FLUID_PLAYER_DONE; + fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_READY, FLUID_PLAYER_STOPPING); + fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_PLAYING, FLUID_PLAYER_STOPPING); fluid_player_seek(player, fluid_player_get_current_tick(player)); return FLUID_OK; } @@ -2163,40 +2211,66 @@ fluid_player_stop(fluid_player_t *player) int fluid_player_get_status(fluid_player_t *player) { - return player->status; + return fluid_atomic_int_get(&player->status); } /** * Seek in the currently playing file. + * * @param player MIDI player instance * @param ticks the position to seek to in the current file - * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file, - * #FLUID_OK otherwise - * @since 2.0.0 + * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file + * [or, since 2.1.3, if another seek operation is currently in progress], + * #FLUID_OK otherwise. * - * The actual seek is performed during the player_callback. + * The actual seek will be performed when the synth calls back the player (i.e. a few + * levels above the player's callback set with fluid_player_set_playback_callback()). + * If the player's status is #FLUID_PLAYER_PLAYING and a previous seek operation has + * not been completed yet, #FLUID_FAILED is returned. + * + * @since 2.0.0 */ int fluid_player_seek(fluid_player_t *player, int ticks) { - if(ticks < 0 || ticks > fluid_player_get_total_ticks(player)) + if(ticks < 0 || (fluid_player_get_status(player) != FLUID_PLAYER_READY && ticks > fluid_player_get_total_ticks(player))) { return FLUID_FAILED; } - player->seek_ticks = ticks; - return FLUID_OK; + if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) + { + if(fluid_atomic_int_compare_and_exchange(&player->seek_ticks, -1, ticks)) + { + // new seek position has been set, as no previous seek was in progress + return FLUID_OK; + } + } + else + { + // If the player is not currently playing, a new seek position can be set at any time. This allows + // the user to do: + // fluid_player_stop(); + // fluid_player_seek(0); // to beginning + fluid_atomic_int_set(&player->seek_ticks, ticks); + return FLUID_OK; + } + + // a previous seek is still in progress or hasn't been processed yet + return FLUID_FAILED; } /** * Enable looping of a MIDI player + * * @param player MIDI player instance * @param loop Times left to loop the playlist. -1 means loop infinitely. * @return Always returns #FLUID_OK - * @since 1.1.0 * * For example, if you want to loop the playlist twice, set loop to 2 * and call this function before you start the player. + * + * @since 1.1.0 */ int fluid_player_set_loop(fluid_player_t *player, int loop) { @@ -2205,15 +2279,32 @@ int fluid_player_set_loop(fluid_player_t *player, int loop) } /** - * Set the tempo of a MIDI player. + * update the MIDI player internal deltatime dependant of actual tempo. * @param player MIDI player instance - * @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec) - * @return Always returns #FLUID_OK */ -int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) +static void fluid_player_update_tempo(fluid_player_t *player) { - player->miditempo = tempo; - player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */ + int tempo; /* tempo in micro seconds by quarter note */ + float deltatime; + + if(fluid_atomic_int_get(&player->sync_mode)) + { + /* take internal tempo from MIDI file */ + tempo = fluid_atomic_int_get(&player->miditempo); + /* compute deltattime (in ms) from current tempo and apply tempo multiplier */ + deltatime = (float)tempo / (float)player->division / (float)1000.0; + deltatime /= fluid_atomic_float_get(&player->multempo); /* multiply tempo */ + } + else + { + /* take external tempo */ + tempo = fluid_atomic_int_get(&player->exttempo); + /* compute deltattime (in ms) from current tempo */ + deltatime = (float)tempo / (float)player->division / (float)1000.0; + } + + fluid_atomic_float_set(&player->deltatime, deltatime); + player->start_msec = player->cur_msec; player->start_ticks = player->cur_ticks; @@ -2221,6 +2312,106 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", tempo, player->deltatime, player->cur_msec, player->cur_ticks); +} + +/** + * Set the tempo of a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo expressed in BPM or in micro seconds + * per quarter note. + * + * @param player MIDI player instance. Must be a valid pointer. + * @param tempo_type Must a be value of #fluid_player_set_tempo_type and indicates the + * meaning of tempo value and how the player will be controlled, see below. + * @param tempo Tempo value or multiplier. + * + * #FLUID_PLAYER_TEMPO_INTERNAL, the player will be controlled by internal + * MIDI file tempo changes. If there are no tempo change in the MIDI file a default + * value of 120 bpm is used. The @c tempo parameter is used as a multiplier factor + * that must be in the range (0.001 to 1000). + * For example, if the current MIDI file tempo is 120 bpm and the multiplier + * value is 0.5 then this tempo will be slowed down to 60 bpm. + * At creation, the player is set to be controlled by internal tempo with a + * multiplier factor set to 1.0. + * + * #FLUID_PLAYER_TEMPO_EXTERNAL_BPM, the player will be controlled by the + * external tempo value provided by the tempo parameter in bpm + * (i.e in quarter notes per minute) which must be in the range (1 to 60000000). + * + * #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, similar as FLUID_PLAYER_TEMPO_EXTERNAL_BPM, + * but the tempo parameter value is in micro seconds per quarter note which + * must be in the range (1 to 60000000). + * Using micro seconds per quarter note is convenient when the tempo value is + * derived from MIDI clock realtime messages. + * + * @note When the player is controlled by an external tempo + * (#FLUID_PLAYER_TEMPO_EXTERNAL_BPM or #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI) it + * continues to memorize the most recent internal tempo change coming from the + * MIDI file so that next call to fluid_player_set_tempo() with + * #FLUID_PLAYER_TEMPO_INTERNAL will set the player to follow this internal + * tempo. + * + * @return #FLUID_OK if success or #FLUID_FAILED otherwise (incorrect parameters). + * @since 2.2.0 + */ +int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo) +{ + fluid_return_val_if_fail(player != NULL, FLUID_FAILED); + fluid_return_val_if_fail(tempo_type >= FLUID_PLAYER_TEMPO_INTERNAL, FLUID_FAILED); + fluid_return_val_if_fail(tempo_type < FLUID_PLAYER_TEMPO_NBR, FLUID_FAILED); + + switch(tempo_type) + { + /* set the player to be driven by internal tempo coming from MIDI file */ + case FLUID_PLAYER_TEMPO_INTERNAL: + /* check if the multiplier is in correct range */ + fluid_return_val_if_fail(tempo >= MIN_TEMPO_MULTIPLIER, FLUID_FAILED); + fluid_return_val_if_fail(tempo <= MAX_TEMPO_MULTIPLIER, FLUID_FAILED); + + /* set the tempo multiplier */ + fluid_atomic_float_set(&player->multempo, (float)tempo); + fluid_atomic_int_set(&player->sync_mode, 1); /* set internal mode */ + break; + + /* set the player to be driven by external tempo */ + case FLUID_PLAYER_TEMPO_EXTERNAL_BPM: /* value in bpm */ + case FLUID_PLAYER_TEMPO_EXTERNAL_MIDI: /* value in us/quarter note */ + /* check if tempo is in correct range */ + fluid_return_val_if_fail(tempo >= MIN_TEMPO_VALUE, FLUID_FAILED); + fluid_return_val_if_fail(tempo <= MAX_TEMPO_VALUE, FLUID_FAILED); + + /* set the tempo value */ + if(tempo_type == FLUID_PLAYER_TEMPO_EXTERNAL_BPM) + { + tempo = 60000000L / tempo; /* convert tempo in us/quarter note */ + } + fluid_atomic_int_set(&player->exttempo, (int)tempo); + fluid_atomic_int_set(&player->sync_mode, 0); /* set external mode */ + break; + + default: /* shouldn't happen */ + break; + } + + /* update deltatime depending of current tempo */ + fluid_player_update_tempo(player); + + return FLUID_OK; +} + +/** + * Set the tempo of a MIDI player. + * @param player MIDI player instance + * @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec) + * @return Always returns #FLUID_OK + * @note Tempo change events contained in the MIDI file can override the specified tempo at any time! + * @deprecated Use fluid_player_set_tempo() instead. + */ +int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) +{ + player->miditempo = tempo; + + fluid_player_update_tempo(player); return FLUID_OK; } @@ -2229,9 +2420,16 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) * @param player MIDI player instance * @param bpm Tempo in beats per minute * @return Always returns #FLUID_OK + * @note Tempo change events contained in the MIDI file can override the specified BPM at any time! + * @deprecated Use fluid_player_set_tempo() instead. */ int fluid_player_set_bpm(fluid_player_t *player, int bpm) { + if(bpm <= 0) + { + return FLUID_FAILED; /* to avoid a division by 0 */ + } + return fluid_player_set_midi_tempo(player, 60000000L / bpm); } @@ -2243,7 +2441,7 @@ int fluid_player_set_bpm(fluid_player_t *player, int bpm) int fluid_player_join(fluid_player_t *player) { - while(player->status != FLUID_PLAYER_DONE) + while(fluid_player_get_status(player) != FLUID_PLAYER_DONE) { fluid_msleep(10); } @@ -2289,25 +2487,51 @@ int fluid_player_get_total_ticks(fluid_player_t *player) } /** - * Get the tempo of a MIDI player in beats per minute. - * @param player MIDI player instance - * @return MIDI player tempo in BPM + * Get the tempo currently used by a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo see fluid_player_set_tempo(). + * @param player MIDI player instance. Must be a valid pointer. + * @return MIDI player tempo in BPM or FLUID_FAILED if error. * @since 1.1.7 */ int fluid_player_get_bpm(fluid_player_t *player) { - return 60000000L / player->miditempo; + + int midi_tempo = fluid_player_get_midi_tempo(player); + + if(midi_tempo > 0) + { + midi_tempo = 60000000L / midi_tempo; /* convert in bpm */ + } + + return midi_tempo; } /** - * Get the tempo of a MIDI player. - * @param player MIDI player instance - * @return Tempo of the MIDI player (in microseconds per quarter note, as per MIDI file spec) + * Get the tempo currently used by a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo see fluid_player_set_tempo(). + + * @param player MIDI player instance. Must be a valid pointer. + * @return Tempo of the MIDI player (in microseconds per quarter note, as per + * MIDI file spec) or FLUID_FAILED if error. * @since 1.1.7 */ int fluid_player_get_midi_tempo(fluid_player_t *player) { - return player->miditempo; + int midi_tempo; /* value to return */ + + fluid_return_val_if_fail(player != NULL, FLUID_FAILED); + + midi_tempo = fluid_atomic_int_get(&player->exttempo); + /* look if the player is internally synced */ + if(fluid_atomic_int_get(&player->sync_mode)) + { + midi_tempo = (int)((float)fluid_atomic_int_get(&player->miditempo)/ + fluid_atomic_float_get(&player->multempo)); + } + + return midi_tempo; } /************************************************************************ @@ -2355,7 +2579,7 @@ delete_fluid_midi_parser(fluid_midi_parser_t *parser) * apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out * the needed midi information using the getter functions of fluid_midi_event_t. * This parser however is incomplete as it e.g. only provides a limited buffer to - * store and process SYSEX data (i.e. doesnt allow arbitrary lengths) + * store and process SYSEX data (i.e. doesn't allow arbitrary lengths) */ fluid_midi_event_t * fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) diff --git a/libs/fluidsynth/src/fluid_midi.h b/libs/fluidsynth/src/fluid_midi.h index 9e34a0ffee..da5c412827 100644 --- a/libs/fluidsynth/src/fluid_midi.h +++ b/libs/fluidsynth/src/fluid_midi.h @@ -187,6 +187,7 @@ enum midi_sysex_manuf /* SYSEX sub-ID #1 which follows device ID */ #define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */ #define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */ +#define MIDI_SYSEX_GS_ID 0x42 /**< Model ID (GS) serving as sub-ID #1 for GS messages*/ /** * SYSEX tuning message IDs. @@ -208,6 +209,7 @@ enum midi_sysex_tuning_msg_id /* General MIDI sub-ID #2 */ #define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */ #define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */ +#define MIDI_SYSEX_GS_DT1 0x12 /**< GS DT1 command */ enum fluid_driver_status { @@ -267,12 +269,19 @@ typedef struct size_t buffer_len; /** Number of bytes in buffer; 0 if filename */ } fluid_playlist_item; +/* range of tempo values */ +#define MIN_TEMPO_VALUE (1.0f) +#define MAX_TEMPO_VALUE (60000000.0f) +/* range of tempo multiplier values */ +#define MIN_TEMPO_MULTIPLIER (0.001f) +#define MAX_TEMPO_MULTIPLIER (1000.0f) + /* * fluid_player */ struct _fluid_player_t { - int status; + fluid_atomic_int_t status; int ntracks; fluid_track_t *track[MAX_NUMBER_OF_TRACKS]; fluid_synth_t *synth; @@ -283,21 +292,35 @@ struct _fluid_player_t fluid_list_t *playlist; /* List of fluid_playlist_item* objects */ fluid_list_t *currentfile; /* points to an item in files, or NULL if not playing */ - char send_program_change; /* should we ignore the program changes? */ char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */ char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */ - int seek_ticks; /* new position in tempo ticks (midi ticks) for seeking */ + fluid_atomic_int_t seek_ticks; /* new position in tempo ticks (midi ticks) for seeking */ int start_ticks; /* the number of tempo ticks passed at the last tempo change */ int cur_ticks; /* the number of tempo ticks passed */ + int last_callback_ticks; /* the last tick number that was passed to player->tick_callback */ int begin_msec; /* the time (msec) of the beginning of the file */ int start_msec; /* the start time of the last tempo change */ int cur_msec; /* the current time */ - int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ - double deltatime; /* milliseconds per midi tick. depends on set-tempo */ + /* sync mode: indicates the tempo mode the player is driven by (see fluid_player_set_tempo()): + 1, the player is driven by internal tempo (miditempo). This is the default. + 0, the player is driven by external tempo (exttempo) + */ + int sync_mode; + /* miditempo: internal tempo comming from MIDI file tempo change events + (in micro seconds per quarter note) + */ + int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ + /* exttempo: external tempo set by fluid_player_set_tempo() (in micro seconds per quarter note) */ + int exttempo; + /* multempo: tempo multiplier set by fluid_player_set_tempo() */ + float multempo; + float deltatime; /* milliseconds per midi tick. depends on current tempo mode (see sync_mode) */ unsigned int division; handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */ void *playback_userdata; /* pointer to user-defined data passed to playback_callback function */ + handle_midi_tick_func_t tick_callback; /* function fired on each tick change */ + void *tick_userdata; /* pointer to user-defined data passed to tick_callback function */ }; void fluid_player_settings(fluid_settings_t *settings); diff --git a/libs/fluidsynth/src/fluid_mod.c b/libs/fluidsynth/src/fluid_mod.c index 57313caf42..3b2a827814 100644 --- a/libs/fluidsynth/src/fluid_mod.c +++ b/libs/fluidsynth/src/fluid_mod.c @@ -24,8 +24,10 @@ /** * Clone the modulators destination, sources, flags and amount. + * * @param mod the modulator to store the copy to * @param src the source modulator to retrieve the information from + * * @note The \c next member of \c mod will be left unchanged. */ void @@ -41,6 +43,7 @@ fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src) /** * Set a modulator's primary source controller and flags. + * * @param mod The modulator instance * @param src Modulator source (#fluid_mod_src or a MIDI controller number) * @param flags Flags determining mapping function and whether the source @@ -56,6 +59,7 @@ fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags) /** * Set a modulator's secondary source controller and flags. + * * @param mod The modulator instance * @param src Modulator source (#fluid_mod_src or a MIDI controller number) * @param flags Flags determining mapping function and whether the source @@ -71,6 +75,7 @@ fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags) /** * Set the destination effect of a modulator. + * * @param mod The modulator instance * @param dest Destination generator (#fluid_gen_type) */ @@ -82,6 +87,7 @@ fluid_mod_set_dest(fluid_mod_t *mod, int dest) /** * Set the scale amount of a modulator. + * * @param mod The modulator instance * @param amount Scale amount to assign */ @@ -93,6 +99,7 @@ fluid_mod_set_amount(fluid_mod_t *mod, double amount) /** * Get the primary source value from a modulator. + * * @param mod The modulator instance * @return The primary source value (#fluid_mod_src or a MIDI CC controller value). */ @@ -104,6 +111,7 @@ fluid_mod_get_source1(const fluid_mod_t *mod) /** * Get primary source flags from a modulator. + * * @param mod The modulator instance * @return The primary source flags (#fluid_mod_flags). */ @@ -115,6 +123,7 @@ fluid_mod_get_flags1(const fluid_mod_t *mod) /** * Get the secondary source value from a modulator. + * * @param mod The modulator instance * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value). */ @@ -126,6 +135,7 @@ fluid_mod_get_source2(const fluid_mod_t *mod) /** * Get secondary source flags from a modulator. + * * @param mod The modulator instance * @return The secondary source flags (#fluid_mod_flags). */ @@ -137,6 +147,7 @@ fluid_mod_get_flags2(const fluid_mod_t *mod) /** * Get destination effect from a modulator. + * * @param mod The modulator instance * @return Destination generator (#fluid_gen_type) */ @@ -148,6 +159,7 @@ fluid_mod_get_dest(const fluid_mod_t *mod) /** * Get the scale amount from a modulator. + * * @param mod The modulator instance * @return Scale amount */ @@ -466,6 +478,7 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) /** * Create a new uninitialized modulator structure. + * * @return New allocated modulator or NULL if out of memory */ fluid_mod_t * @@ -484,6 +497,7 @@ new_fluid_mod() /** * Free a modulator structure. + * * @param mod Modulator to free */ void @@ -495,9 +509,9 @@ delete_fluid_mod(fluid_mod_t *mod) /** * Returns the size of the fluid_mod_t structure. * - * Useful in low latency scenarios e.g. to allocate a modulator on the stack. - * * @return Size of fluid_mod_t in bytes + * + * Useful in low latency scenarios e.g. to allocate a modulator on the stack. */ size_t fluid_mod_sizeof() { @@ -518,13 +532,14 @@ fluid_mod_is_src1_none(const fluid_mod_t *mod) /** * Checks if modulators source other than CC source is invalid. - * (specs SF 2.01 7.4, 7.8, 8.2.1) * * @param mod, modulator. * @param src1_select, source input selection to check. * 1 to check src1 source. * 0 to check src2 source. * @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise. + * + * (specs SF 2.01 7.4, 7.8, 8.2.1) */ static int fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select) @@ -556,6 +571,7 @@ fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select) /** * Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1). + * * @param mod, modulator. * @src1_select, source input selection: * 1 to check src1 source or @@ -599,6 +615,7 @@ fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select) /** * Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1) + * * @param mod, modulator. * @param name,if not NULL, pointer on a string displayed as a warning. * @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise. @@ -677,6 +694,7 @@ int fluid_mod_check_sources(const fluid_mod_t *mod, const char *name) /** * Checks if two modulators are identical in sources, flags and destination. + * * @param mod1 First modulator * @param mod2 Second modulator * @return TRUE if identical, FALSE otherwise @@ -720,6 +738,7 @@ int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl) /** * Check if the modulator has the given destination. + * * @param mod The modulator instance * @param gen The destination generator of type #fluid_gen_type to check for * @return TRUE if the modulator has the given destination, FALSE otherwise. diff --git a/libs/fluidsynth/src/fluid_rev.c b/libs/fluidsynth/src/fluid_rev.c index f3ee0e47f9..a26f5a1f23 100644 --- a/libs/fluidsynth/src/fluid_rev.c +++ b/libs/fluidsynth/src/fluid_rev.c @@ -68,7 +68,7 @@ * output is the same. This sounds like a monophonic signal. * When 100, the separation between left and right is maximum. * - * - level (0 to 1), controls the ouput level reverberation. + * - level (0 to 1), controls the output level reverberation. * * This FDN reverb produces a better quality reverberation tail than Freeverb with * far less ringing by using modulated delay lines that help to cancel @@ -120,17 +120,17 @@ * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing * | 2 x 4 all-pass | | | * ----------|--------------------------------------------------------------- - * FDN | 8 | 0.650 % | 112160 | far less - * modulated | |(feeverb - 3%) | (55% freeverb) | ringing + * FDN | 8 | 0.650 % | 112480 | far less + * modulated | |(feeverb - 3%) | (56% freeverb) | ringing * |--------------------------------------------------------------- - * | 12 | 0.942 % | 168240 | best than + * | 12 | 0.942 % | 168720 | best than * | |(freeverb + 41%) | (82 %freeverb) | 8 lines *--------------------------------------------------------------------------- * * Note: * Values in this column is the memory consumption for sample rate <= 44100Hz. * For sample rate > 44100Hz , multiply these values by (sample rate / 44100Hz). - * + * For example: for sample rate 96000Hz, the memory consumed is 244760 bytes * *---------------------------------------------------------------------------- * 'Denormalise' method to avoid loss of performance. @@ -182,7 +182,7 @@ /* Number of delay lines (must be only 8 or 12) 8 is the default. - 12 produces a better quality but is +50% cpu expensive + 12 produces a better quality but is +50% cpu expensive. */ #define NBR_DELAYS 8 /* default*/ @@ -274,7 +274,7 @@ a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. * /* Number of samples to add to the desired length of a delay line. This allow to take account of modulation interpolation. - 1 is sufficient with MOD_DEPTH equal to 6. + 1 is sufficient with MOD_DEPTH equal to 4. */ #define INTERP_SAMPLES_NBR 1 @@ -368,7 +368,7 @@ static void set_fdn_delay_lpf(fdn_delay_lpf *lpf, typedef struct { fluid_real_t *line; /* buffer line */ - int size; /* effective internal size (in samples) */ + int size; /* effective internal size (in samples) */ /*-------------*/ int line_in; /* line in position */ int line_out; /* line out position */ @@ -428,7 +428,7 @@ typedef struct static void set_mod_frequency(sinus_modulator *mod, float freq, float sample_rate, float phase) { - fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* intial angle */ + fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ fluid_real_t a; mod->a1 = 2 * FLUID_COS(w); @@ -500,115 +500,6 @@ typedef struct fluid_real_t buffer; } mod_delay_line; - -/*----------------------------------------------------------------------------- - Modulated delay line initialization. - - Sets the length line ( alloc delay samples). - Remark: the function sets the internal size accordling to the length delay_length. - As the delay line is a modulated line, its internal size is augmented by mod_depth. - The size is also augmented by INTERP_SAMPLES_NBR to take account of interpolation. - - @param mdl, pointer on modulated delay line. - @param delay_length the length of the delay line in samples. - @param mod_depth depth of the modulation in samples (amplitude of the sine wave). - @param mod_rate the rate of the modulation in samples. - @return FLUID_OK if success , FLUID_FAILED if memory error. - - Return FLUID_OK if success, FLUID_FAILED if memory error. ------------------------------------------------------------------------------*/ -static int set_mod_delay_line(mod_delay_line *mdl, - int delay_length, - int mod_depth, - int mod_rate - ) -{ - /*-----------------------------------------------------------------------*/ - /* checks parameter */ - if(delay_length < 1) - { - return FLUID_FAILED; - } - - /* limits mod_depth to the requested delay length */ - if(mod_depth >= delay_length) - { - FLUID_LOG(FLUID_INFO, - "fdn reverb: modulation depth has been limited"); - mod_depth = delay_length - 1; - } - - mdl->mod_depth = mod_depth; - /*----------------------------------------------------------------------- - allocates delay_line and initialize members: - - line, size, line_in, line_out... - */ - { - /* total size of the line: - size = INTERP_SAMPLES_NBR + mod_depth + delay_length */ - mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR; - mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size); - - if(! mdl->dl.line) - { - return FLUID_FAILED; - } - - clear_delay_line(&mdl->dl); /* clears the buffer */ - - /* Initializes line_in to the start of the buffer */ - mdl->dl.line_in = 0; - /* Initializes line_out index INTERP_SAMPLES_NBR samples after line_in */ - /* so that the delay between line_out and line_in is: - mod_depth + delay_length */ - mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR; - } - - /* Damping low pass filter -------------------*/ - mdl->dl.damping.buffer = 0; - /*------------------------------------------------------------------------ - Initializes modulation members: - - modulated center position: center_pos_mod - - index rate to know when to update center_pos_mod:index_rate - - modulation rate (the speed at which center_pos_mod is modulated: mod_rate - - interpolator member: buffer, frac_pos_mod - -------------------------------------------------------------------------*/ - /* Sets the modulation rate. This rate defines how often - the center position (center_pos_mod ) is modulated . - The value is expressed in samples. The default value is 1 that means that - center_pos_mod is updated at every sample. - For example with a value of 2, the center position position will be - updated only one time every 2 samples only. - */ - mdl->mod_rate = 1; /* default modulation rate: every one sample */ - - if(mod_rate > mdl->dl.size) - { - FLUID_LOG(FLUID_INFO, - "fdn reverb: modulation rate is out of range"); - } - else - { - mdl->mod_rate = mod_rate; - } - - /* Initializes the modulated center position (center_pos_mod) so that: - - the delay between line_out and center_pos_mod is mod_depth. - - the delay between center_pos_mod and line_in is delay_length. - */ - mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth; - - /* index rate to control when to update center_pos_mod */ - /* Important: must be set to get center_pos_mod immediatly used for the - reading of first sample (see get_mod_delay()) */ - mdl->index_rate = mdl->mod_rate; - - /* initializes 1st order All-Pass interpolator members */ - mdl->buffer = 0; /* previous delay sample value */ - mdl->frac_pos_mod = 0; /* fractional position (between consecutives sample) */ - return FLUID_OK; -} - /*----------------------------------------------------------------------------- Return norminal delay length @@ -631,7 +522,7 @@ static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl) fluid_real_t out; /* value to return */ /* Checks if the modulator must be updated (every mod_rate samples). */ - /* Important: center_pos_mod must be used immediatly for the + /* Important: center_pos_mod must be used immediately for the first sample. So, mdl->index_rate must be initialized to mdl->mod_rate (set_mod_delay_line()) */ @@ -688,7 +579,7 @@ static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl) mdl->dl.line_out -= mdl->dl.size; } - /* Fractional interpolation beetween next sample (at next position) and + /* Fractional interpolation between next sample (at next position) and previous output added to current sample. */ out += mdl->frac_pos_mod * (mdl->dl.line[mdl->dl.line_out] - mdl->buffer); @@ -702,6 +593,7 @@ static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl) struct _fluid_late { fluid_real_t samplerate; /* sample rate */ + fluid_real_t sample_rate_max; /* sample rate maximum */ /*----- High pass tone corrector -------------------------------------*/ fluid_real_t tone_buffer; fluid_real_t b1, b2; @@ -904,118 +796,220 @@ static void delete_fluid_rev_late(fluid_late *late) } } -/*----------------------------------------------------------------------------- - Creates all modulated lines. - @param late, pointer on the fnd late reverb to initialize. - @param sample_rate, the audio sample rate. - @return FLUID_OK if success, FLUID_FAILED otherwise. ------------------------------------------------------------------------------*/ -static int create_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate) + +/* Nominal delay lines length table (in samples) */ +static const int nom_delay_length[NBR_DELAYS] = { - /* Delay lines length table (in samples) */ - static const int delay_length[NBR_DELAYS] = - { - DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3, - DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7, - #if (NBR_DELAYS == 12) - DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11 - #endif - }; + DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3, + DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7, +#if (NBR_DELAYS == 12) + DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11 +#endif +}; - int result; /* return value */ - int i; +/* + 1)"modal density" is one property that contributes to the quality of the reverb tail. + The more is the modal density, the less are unwanted resonant frequencies + build during the decay time: modal density = total delay / sample rate. - /* - 1)"modal density" is one property that contributes to the quality of the reverb tail. - The more is the modal density, the less are unwanted resonant frequencies - build during the decay time: modal density = total delay / sample rate. + Delay line's length given by static table delay_length[] are nominal + to get minimum modal density of 0.15 at sample rate 44100Hz. + Here we set length_factor to 2 to multiply this nominal modal + density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for + sample rate <= 44100. - Delay line's length given by static table delay_length[] is nominal - to get minimum modal density of 0.15 at sample rate 44100Hz. - Here we set length_factor to 2 to mutiply this nominal modal - density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for - sample rate <= 44100. + For sample rate > 44100, length_factor is multiplied by + sample_rate / 44100. This ensures that the default modal density keeps inchanged. + (Without this compensation, the default modal density would be diminished for + new sample rate change above 44100Hz). - For sample rate > 44100, length_factor is multiplied by - sample_rate / 44100. This ensures that the default modal density keeps inchanged. - (Without this compensation, the default modal density would be diminished for - new sample rate change above 44100Hz). - - 2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing"). - Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz. - For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures - that the effect of modulated delay line keeps inchanged. - */ - fluid_real_t length_factor = 2.0f; - fluid_real_t mod_depth = MOD_DEPTH; + 2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing"). + Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz. + For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures + that the effect of modulated delay line remains inchanged. +*/ +static void compensate_from_sample_rate(fluid_real_t sample_rate, + fluid_real_t *mod_depth, + fluid_real_t *length_factor) +{ + *mod_depth = MOD_DEPTH; + *length_factor = 2.0f; if(sample_rate > 44100.0f) { fluid_real_t sample_rate_factor = sample_rate/44100.0f; - length_factor *= sample_rate_factor; - mod_depth *= sample_rate_factor; + *length_factor *= sample_rate_factor; + *mod_depth *= sample_rate_factor; } +} + +/*----------------------------------------------------------------------------- + Creates all modulated lines. + @param late, pointer on the fnd late reverb to initialize. + @param sample_rate_max, the maximum audio sample rate expected. + @return FLUID_OK if success, FLUID_FAILED otherwise. +-----------------------------------------------------------------------------*/ +static int create_mod_delay_lines(fluid_late *late, + fluid_real_t sample_rate_max) +{ + int i; + + fluid_real_t mod_depth, length_factor; + + /* compute mod_depth, length factor */ + compensate_from_sample_rate(sample_rate_max, &mod_depth, &length_factor); + + late->sample_rate_max = sample_rate_max; + #ifdef INFOS_PRINT // allows message to be printed on the console. printf("length_factor:%f, mod_depth:%f\n", length_factor, mod_depth); /* Print: modal density and total memory bytes */ { int i; - int total_delay; /* total delay in samples */ - for (i = 0, total_delay = 0; i < NBR_DELAYS; i++) + int total_delay = 0; /* total delay in samples */ + for (i = 0; i < NBR_DELAYS; i++) { - total_delay += length_factor * delay_length[i]; + int length = (length_factor * nom_delay_length[i]) + + mod_depth + INTERP_SAMPLES_NBR; + total_delay += length; } /* modal density and total memory bytes */ - printf("modal density:%f, total memory:%d bytes\n", - total_delay / sample_rate , total_delay * sizeof(fluid_real_t)); + printf("modal density:%f, total delay:%d, total memory:%d bytes\n", + total_delay / sample_rate_max ,total_delay , + total_delay * sizeof(fluid_real_t)); } #endif for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ { - /* allocate delay line and set local delay lines's parameters */ - result = set_mod_delay_line(&late->mod_delay_lines[i], - delay_length[i] * length_factor, - mod_depth, MOD_RATE); + int delay_length = nom_delay_length[i] * length_factor; + mod_delay_line *mdl = &late->mod_delay_lines[i]; - if(result == FLUID_FAILED) + /*-------------------------------------------------------------------*/ + /* checks parameter */ + if(delay_length < 1) { return FLUID_FAILED; } - /* Sets local Modulators parameters: frequency and phase - Each modulateur are shifted of MOD_PHASE degree + /* limits mod_depth to the requested delay length */ + if(mod_depth >= delay_length) + { + FLUID_LOG(FLUID_INFO, + "fdn reverb: modulation depth has been limited"); + mod_depth = delay_length - 1; + } + + /*--------------------------------------------------------------------- + allocates delay lines */ - set_mod_frequency(&late->mod_delay_lines[i].mod, - MOD_FREQ * MOD_RATE, - late->samplerate, - (float)(MOD_PHASE * i)); + + /* real size of the line in use (in samples): + size = INTERP_SAMPLES_NBR + mod_depth + delay_length */ + mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR; + mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size); + + if(! mdl->dl.line) + { + return FLUID_FAILED; + } } return FLUID_OK; } /*----------------------------------------------------------------------------- - Creates the fdn reverb. + Initialize all modulated lines. @param late, pointer on the fnd late reverb to initialize. - @param sample_rate the sample rate. + @param sample_rate, the audio sample rate. @return FLUID_OK if success, FLUID_FAILED otherwise. -----------------------------------------------------------------------------*/ -static int create_fluid_rev_late(fluid_late *late, fluid_real_t sample_rate) +static void initialize_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate) { - FLUID_MEMSET(late, 0, sizeof(fluid_late)); + int i; + fluid_real_t mod_depth, length_factor; + /* update delay line parameter dependant of sample rate */ late->samplerate = sample_rate; - /*-------------------------------------------------------------------------- - First initialize the modulated delay lines - */ + /* compute mod_depth, length factor */ + compensate_from_sample_rate(sample_rate, &mod_depth, &length_factor); - if(create_mod_delay_lines(late, sample_rate) == FLUID_FAILED) + for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ { - return FLUID_FAILED; - } + mod_delay_line *mdl = &late->mod_delay_lines[i]; + int delay_length = nom_delay_length[i] * length_factor; - return FLUID_OK; + /* limits mod_depth to the requested delay length */ + if(mod_depth >= delay_length) + { + mod_depth = delay_length - 1; + } + + mdl->mod_depth = mod_depth; + + clear_delay_line(&mdl->dl); /* clears the buffer */ + + /* Initializes line_in to the start of the buffer */ + mdl->dl.line_in = 0; + + /* Initializes line_out index INTERP_SAMPLES_NBR samples after + line_in so that the delay between line_out and line_in is: + mod_depth + delay_length + */ + mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR; + + /* Damping low pass filter ------------------------------------------*/ + mdl->dl.damping.buffer = 0; + + /*--------------------------------------------------------------------- + Initializes modulation members: + - modulated center position: center_pos_mod + - modulation rate (the speed at which center_pos_mod is modulated: mod_rate + - index rate to know when to update center_pos_mod:index_rate + - interpolator member: buffer, frac_pos_mod + ---------------------------------------------------------------------*/ + /* Initializes the modulated center position (center_pos_mod) so that: + - the delay between line_out and center_pos_mod is mod_depth. + - the delay between center_pos_mod and line_in is delay_length. + */ + mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth; + + /* Sets the modulation rate. This rate defines how often + the center position (center_pos_mod ) is modulated . + The value is expressed in samples. The default value is 1 that means that + center_pos_mod is updated at every sample. + For example with a value of 2, the center position position will be + updated only one time every 2 samples only. + */ + if(MOD_RATE < 1 || MOD_RATE > mdl->dl.size) + { + FLUID_LOG(FLUID_INFO, "fdn reverb: modulation rate is out of range"); + mdl->mod_rate = 1; /* default modulation rate: every one sample */ + } + else + { + mdl->mod_rate = MOD_RATE; + } + + /* index rate to control when to update center_pos_mod. + Important: must be set to get center_pos_mod immediately used for + the reading of first sample (see get_mod_delay()) + */ + mdl->index_rate = mdl->mod_rate; + + /* initializes first order All-Pass interpolator members */ + mdl->buffer = 0; /* previous delay sample value */ + mdl->frac_pos_mod = 0; /* frac. position (between consecutives sample) */ + + + /* Sets local Modulators parameters: frequency and phase. + Each modulateur are shifted of MOD_PHASE degree + */ + set_mod_frequency(&mdl->mod, + MOD_FREQ * MOD_RATE, + sample_rate, + (float)(MOD_PHASE * i)); + } } /* @@ -1055,7 +1049,7 @@ fluid_revmodel_update(fluid_revmodel_t *rev) fluid_real_t wet = (rev->level * SCALE_WET) / (1.0f + rev->width * SCALE_WET_WIDTH); - /* wet1 and wet2 are used by the stereo effect controled by the width setting + /* wet1 and wet2 are used by the stereo effect controlled by the width setting for producing a stereo ouptput from a monophonic reverb signal. Please see the note above about a side effect tendency */ @@ -1077,20 +1071,27 @@ fluid_revmodel_update(fluid_revmodel_t *rev) /*---------------------------------------------------------------------------- Reverb API -----------------------------------------------------------------------------*/ - /* -* Creates a reverb. One created the reverb have no parameters set, so +* Creates a reverb. Once created the reverb have no parameters set, so * fluid_revmodel_set() must be called at least one time after calling * new_fluid_revmodel(). * -* @param sample_rate sample rate in Hz. +* @param sample_rate_max maximum sample rate expected in Hz. +* +* @param sample_rate actual sample rate needed in Hz. * @return pointer on the new reverb or NULL if memory error. * Reverb API. */ fluid_revmodel_t * -new_fluid_revmodel(fluid_real_t sample_rate) +new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate) { fluid_revmodel_t *rev; + + if(sample_rate <= 0) + { + return NULL; + } + rev = FLUID_NEW(fluid_revmodel_t); if(rev == NULL) @@ -1098,13 +1099,33 @@ new_fluid_revmodel(fluid_real_t sample_rate) return NULL; } - /* create fdn reverb */ - if(create_fluid_rev_late(&rev->late, sample_rate) != FLUID_OK) + FLUID_MEMSET(&rev->late, 0, sizeof(fluid_late)); + + /*-------------------------------------------------------------------------- + Create fdn late reverb. + */ + + /* update minimum value for sample_rate_max */ + if(sample_rate > sample_rate_max) + { + sample_rate_max = sample_rate; + } + + /*-------------------------------------------------------------------------- + Allocate the modulated delay lines + */ + if(create_mod_delay_lines(&rev->late, sample_rate_max) == FLUID_FAILED) { delete_fluid_revmodel(rev); return NULL; } + /*-------------------------------------------------------------------------- + Initialize the fdn reverb + */ + /* Initialize all modulated lines. */ + initialize_mod_delay_lines(&rev->late, sample_rate); + return rev; } @@ -1131,11 +1152,12 @@ delete_fluid_revmodel(fluid_revmodel_t *rev) /* * Sets one or more reverb parameters. Note this must be called at least one -* time after calling new_fluid_revmodel(). +* time after calling new_fluid_revmodel() and before any call to +* fluid_revmodel_processXXX() and fluid_revmodel_samplerate_change(). * * Note that while the reverb is used by calling any fluid_revmodel_processXXX() * function, calling fluid_revmodel_set() could produce audible clics. -* If this is a problem, optionnaly call fluid_revmodel_reset() before calling +* If this is a problem, optionally call fluid_revmodel_reset() before calling * fluid_revmodel_set(). * * @param rev Reverb instance. @@ -1152,6 +1174,8 @@ void fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, fluid_real_t damping, fluid_real_t width, fluid_real_t level) { + fluid_return_if_fail(rev != NULL); + /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_ROOMSIZE) { @@ -1185,12 +1209,15 @@ fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, /* * Applies a sample rate change on the reverb. +* fluid_revmodel_set() must be called at least one time before calling +* this function. +* * Note that while the reverb is used by calling any fluid_revmodel_processXXX() -* function, calling fluid_revmodel_samplerate_change() isn't multi task safe because -* delay line are memory reallocated. To deal properly with this issue follow -* the steps: +* function, calling fluid_revmodel_samplerate_change() isn't multi task safe. +* To deal properly with this issue follow the steps: * 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX(). * reverb functions. +* Optionally, call fluid_revmodel_reset() to damp the reverb. * 2) Change sample rate by calling fluid_revmodel_samplerate_change(). * 3) Restart reverb processing (i.e enabling calling of any fluid_revmodel_processXXX() * reverb functions. @@ -1199,29 +1226,44 @@ fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, * 2.1) delete the reverb by calling delete_fluid_revmodel(). * 2.2) create the reverb by calling new_fluid_revmodel(). * +* The best solution would be that this function be called only by the same task +* calling fluid_revmodel_processXXX(). +* * @param rev the reverb. -* @param sample_rate new sample rate value. -* @return FLUID_OK if success, FLUID_FAILED otherwise (memory error). +* @param sample_rate new sample rate value. Must be <= sample_rate_max +* @return FLUID_OK if success, FLUID_FAILED if new sample rate is greater +* then the maximumum sample rate set at creation time. The reverb will +* continue to work but with possible lost of quality. +* If this is a problem, the caller should follow steps 2.1 and 2.2. * Reverb API. */ int fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate) { - rev->late.samplerate = sample_rate; /* new sample rate value */ + int status = FLUID_OK; - /* free all delay lines */ - delete_fluid_rev_late(&rev->late); + fluid_return_val_if_fail(rev != NULL, FLUID_FAILED); - /* create all delay lines */ - if(create_mod_delay_lines(&rev->late, sample_rate) == FLUID_FAILED) + if(sample_rate > rev->late.sample_rate_max) { - return FLUID_FAILED; /* memory error */ + FLUID_LOG(FLUID_WARN, + "fdn reverb: sample rate %.0f Hz is deduced to %.0f Hz\n", + sample_rate, rev->late.sample_rate_max); + + /* Reduce sample rate to the maximum value set at creation time. + The reverb will continue to work with possible lost of quality. + */ + sample_rate = rev->late.sample_rate_max; + status = FLUID_FAILED; } + /* Initialize all modulated lines according to sample rate change. */ + initialize_mod_delay_lines(&rev->late, sample_rate); + /* updates damping filter coefficients according to sample rate change */ update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); - return FLUID_OK; + return status; } /* @@ -1233,6 +1275,8 @@ fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate void fluid_revmodel_reset(fluid_revmodel_t *rev) { + fluid_return_if_fail(rev != NULL); + fluid_revmodel_init(rev); } @@ -1298,7 +1342,7 @@ fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, process_damping_filter(delay_out_s, delay_out_s, mdl); /* Result in delay_out[], and matrix_factor. - These wil be use later during input line process */ + These will be of use later during input line process */ delay_out[i] = delay_out_s; /* result in delay_out[] */ matrix_factor += delay_out_s; /* result in matrix_factor */ @@ -1347,7 +1391,7 @@ fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, right_out[k] = out_right * rev->wet1 + out_left * rev->wet2; As wet1 is integrated in stereo coefficient wet 1 is now - integrated in out_left and out_right we simplify previous + integrated in out_left and out_right, so we simplify previous relation by suppression of one multiply as this: left_out[k] = out_left + out_right * rev->wet2; @@ -1419,7 +1463,7 @@ void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, process_damping_filter(delay_out_s, delay_out_s, mdl); /* Result in delay_out[], and matrix_factor. - These wil be use later during input line process */ + These will be of use later during input line process */ delay_out[i] = delay_out_s; /* result in delay_out[] */ matrix_factor += delay_out_s; /* result in matrix_factor */ @@ -1467,7 +1511,7 @@ void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, right_out[k] += out_right * rev->wet1 + out_left * rev->wet2; As wet1 is integrated in stereo coefficient wet 1 is now - integrated in out_left and out_right we simplify previous + integrated in out_left and out_right, so we simplify previous relation by suppression of one multiply as this: left_out[k] += out_left + out_right * rev->wet2; diff --git a/libs/fluidsynth/src/fluid_rev.h b/libs/fluidsynth/src/fluid_rev.h index b24fd5fd78..35c9cf664f 100644 --- a/libs/fluidsynth/src/fluid_rev.h +++ b/libs/fluidsynth/src/fluid_rev.h @@ -26,14 +26,26 @@ typedef struct _fluid_revmodel_t fluid_revmodel_t; +/* enum describing each reverb parameter */ +enum fluid_reverb_param +{ + FLUID_REVERB_ROOMSIZE, /**< reverb time */ + FLUID_REVERB_DAMP, /**< high frequency damping */ + FLUID_REVERB_WIDTH, /**< stereo width */ + FLUID_REVERB_LEVEL, /**< output level */ + FLUID_REVERB_PARAM_LAST /* number of enum fluid_reverb_param */ +}; + +/* return a bit flag from param: 2^param */ +#define FLUID_REVPARAM_TO_SETFLAG(param) (1 << param) /** 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_ROOMSIZE = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_ROOMSIZE), + FLUID_REVMODEL_SET_DAMPING = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_DAMP), + FLUID_REVMODEL_SET_WIDTH = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_WIDTH), + FLUID_REVMODEL_SET_LEVEL = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_LEVEL), /** Value for fluid_revmodel_set() which sets all reverb parameters. */ FLUID_REVMODEL_SET_ALL = FLUID_REVMODEL_SET_LEVEL @@ -58,7 +70,9 @@ typedef struct _fluid_revmodel_presets_t /* * reverb */ -fluid_revmodel_t *new_fluid_revmodel(fluid_real_t sample_rate); +fluid_revmodel_t * +new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate); + void delete_fluid_revmodel(fluid_revmodel_t *rev); void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, diff --git a/libs/fluidsynth/src/fluid_rvoice.c b/libs/fluidsynth/src/fluid_rvoice.c index 8837e4415d..5f54c33b17 100644 --- a/libs/fluidsynth/src/fluid_rvoice.c +++ b/libs/fluidsynth/src/fluid_rvoice.c @@ -493,7 +493,8 @@ fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t *buffers, unsigned int for(i = buffers->count; i <= bufnum; i++) { - buffers->bufs[i].amp = 0.0f; + buffers->bufs[i].target_amp = 0.0f; + buffers->bufs[i].current_amp = 0.0f; } buffers->count = bufnum + 1; @@ -512,7 +513,7 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp) return; } - buffers->bufs[bufnum].amp = value; + buffers->bufs[bufnum].target_amp = value; } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping) diff --git a/libs/fluidsynth/src/fluid_rvoice.h b/libs/fluidsynth/src/fluid_rvoice.h index 56cd53f46c..610afd7252 100644 --- a/libs/fluidsynth/src/fluid_rvoice.h +++ b/libs/fluidsynth/src/fluid_rvoice.h @@ -143,8 +143,14 @@ struct _fluid_rvoice_buffers_t unsigned int count; /* Number of records in "bufs" */ struct { - fluid_real_t amp; - int mapping; /* Mapping to mixdown buffer index */ + /* the actual, linearly interpolated amplitude with which the dsp sample should be mixed into the buf */ + fluid_real_t current_amp; + + /* the desired amplitude [...] mixed into the buf (directly set by e.g. rapidly changing PAN events) */ + fluid_real_t target_amp; + + /* Mapping to mixdown buffer index */ + int mapping; } bufs[FLUID_RVOICE_MAX_BUFS]; }; diff --git a/libs/fluidsynth/src/fluid_rvoice_dsp.c b/libs/fluidsynth/src/fluid_rvoice_dsp.c index aec79aab5e..b43a0f1907 100644 --- a/libs/fluidsynth/src/fluid_rvoice_dsp.c +++ b/libs/fluidsynth/src/fluid_rvoice_dsp.c @@ -21,7 +21,7 @@ #include "fluid_sys.h" #include "fluid_phase.h" #include "fluid_rvoice.h" -#include "fluid_rvoice_dsp_tables.c" +#include "fluid_rvoice_dsp_tables.inc.h" /* Purpose: * diff --git a/libs/fluidsynth/src/fluid_rvoice_dsp_tables.c b/libs/fluidsynth/src/fluid_rvoice_dsp_tables.inc.h similarity index 100% rename from libs/fluidsynth/src/fluid_rvoice_dsp_tables.c rename to libs/fluidsynth/src/fluid_rvoice_dsp_tables.inc.h diff --git a/libs/fluidsynth/src/fluid_rvoice_event.c b/libs/fluidsynth/src/fluid_rvoice_event.c index 4a513778bd..e60115f361 100644 --- a/libs/fluidsynth/src/fluid_rvoice_event.c +++ b/libs/fluidsynth/src/fluid_rvoice_event.c @@ -88,7 +88,7 @@ static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *han if(event == NULL) { fluid_atomic_int_add(&handler->queue_stored, -1); - FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!"); + FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing synth.polyphony!"); return FLUID_FAILED; // Buffer full... } @@ -114,7 +114,9 @@ fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *e fluid_rvoice_eventhandler_t * new_fluid_rvoice_eventhandler(int queuesize, - int finished_voices_size, int bufs, int fx_bufs, int fx_units, fluid_real_t sample_rate, int extra_threads, int prio) + int finished_voices_size, int bufs, int fx_bufs, int fx_units, + fluid_real_t sample_rate_max, fluid_real_t sample_rate, + int extra_threads, int prio) { fluid_rvoice_eventhandler_t *eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t); @@ -145,7 +147,8 @@ new_fluid_rvoice_eventhandler(int queuesize, goto error_recovery; } - eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units, sample_rate, eventhandler, extra_threads, prio); + eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units, + sample_rate_max, sample_rate, eventhandler, extra_threads, prio); if(eventhandler->mixer == NULL) { diff --git a/libs/fluidsynth/src/fluid_rvoice_event.h b/libs/fluidsynth/src/fluid_rvoice_event.h index d1fd8d62cb..225e9069e1 100644 --- a/libs/fluidsynth/src/fluid_rvoice_event.h +++ b/libs/fluidsynth/src/fluid_rvoice_event.h @@ -37,7 +37,7 @@ struct _fluid_rvoice_event_t /* * Bridge between the renderer thread and the midi state thread. - * fluid_rvoice_eventhandler_fetch_all() can be called in parallell + * fluid_rvoice_eventhandler_fetch_all() can be called in parallel * with fluid_rvoice_eventhandler_push/flush() */ struct _fluid_rvoice_eventhandler_t @@ -50,7 +50,7 @@ struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t *new_fluid_rvoice_eventhandler( int queuesize, int finished_voices_size, int bufs, - int fx_bufs, int fx_units, fluid_real_t sample_rate, int, int); + int fx_bufs, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, int, int); void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *); diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.c b/libs/fluidsynth/src/fluid_rvoice_mixer.c index d7fd2f541f..9bf7aec7bf 100644 --- a/libs/fluidsynth/src/fluid_rvoice_mixer.c +++ b/libs/fluidsynth/src/fluid_rvoice_mixer.c @@ -50,7 +50,7 @@ struct _fluid_mixer_buffers_t /** buffer to store the left part of a stereo channel to. * Specifically a two dimensional array, containing \c buf_count sample buffers - * (i.e. for each synth.audio-channels), of which each contains + * (i.e. for each synth.audio-groups), of which each contains * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) * @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT * boundary provided that this pointer points to an aligned buffer. @@ -76,7 +76,14 @@ typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t; struct _fluid_mixer_fx_t { fluid_revmodel_t *reverb; /**< Reverb unit */ + /* reverb shadow parameters here will be returned if queried */ + double reverb_param[FLUID_REVERB_PARAM_LAST]; + int reverb_on; /* reverb on/off */ + fluid_chorus_t *chorus; /**< Chorus unit */ + /* chorus shadow parameters here will be returned if queried */ + double chorus_param[FLUID_CHORUS_PARAM_LAST]; + int chorus_on; /* chorus on/off */ }; struct _fluid_rvoice_mixer_t @@ -124,6 +131,12 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcoun { const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units; int i, f; + int dry_count = mixer->buffers.buf_count; /* dry buffers count */ + int mix_fx_to_out = mixer->mix_fx_to_out; /* get mix_fx_to_out mode */ + int dry_idx = 0; /* dry buffer index */ + int buf_idx; /* buffer index */ + int samp_idx; /* sample index in buffer */ + int sample_count; /* sample count to process */ void (*reverb_process_func)(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); void (*chorus_process_func)(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); @@ -137,7 +150,7 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcoun fluid_profile_ref_var(prof_ref); - if(mixer->mix_fx_to_out) + if(mix_fx_to_out) { // mix effects to first stereo channel out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); @@ -162,16 +175,28 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcoun { for(f = 0; f < mixer->fx_units; f++) { - int buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL; - - for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) + if(!mixer->fx[f].reverb_on) + { + continue; /* this reverb unit is disabled */ + } + + buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL; + samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + sample_count = current_blockcount * FLUID_BUFSIZE; + + /* in mix mode, map fx out_rev at index f to a dry buffer at index dry_idx */ + if(mix_fx_to_out) + { + /* dry buffer mapping, should be done more flexible in the future */ + dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + } + + for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) { - int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i; - reverb_process_func(mixer->fx[f].reverb, &in_rev[samp_idx], - mixer->mix_fx_to_out ? &out_rev_l[i] : &out_rev_l[samp_idx], - mixer->mix_fx_to_out ? &out_rev_r[i] : &out_rev_r[samp_idx]); + mix_fx_to_out ? &out_rev_l[dry_idx + i] : &out_rev_l[samp_idx], + mix_fx_to_out ? &out_rev_r[dry_idx + i] : &out_rev_r[samp_idx]); } } @@ -183,16 +208,28 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcoun { for(f = 0; f < mixer->fx_units; f++) { - int buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL; - - for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) + if(!mixer->fx[f].chorus_on) + { + continue; /* this chorus unit is disabled */ + } + + buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL; + samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + sample_count = current_blockcount * FLUID_BUFSIZE; + + /* in mix mode, map fx out_ch at index f to a dry buffer at index dry_idx */ + if(mix_fx_to_out) + { + /* dry buffer mapping, should be done more flexible in the future */ + dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + } + + for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) { - int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i; - chorus_process_func(mixer->fx[f].chorus, &in_ch [samp_idx], - mixer->mix_fx_to_out ? &out_ch_l[i] : &out_ch_l[samp_idx], - mixer->mix_fx_to_out ? &out_ch_r[i] : &out_ch_r[samp_idx]); + mix_fx_to_out ? &out_ch_l[dry_idx + i] : &out_ch_l[samp_idx], + mix_fx_to_out ? &out_ch_r[dry_idx + i] : &out_ch_r[samp_idx]); } } @@ -242,18 +279,18 @@ fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbu for(i = 0; i < buffers->mixer->fx_units; i++) { int fx_idx = i * fx_channels_per_unit; - + outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] = (with_reverb) ? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] : NULL; - + outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] = (with_chorus) ? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] : NULL; } - + /* The output associated with a MIDI channel is wrapped around * using the number of audio groups as modulo divider. This is * typically the number of output channels on the 'sound card', @@ -378,7 +415,7 @@ fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, int bufcount = buffers->count; int i, dsp_i; - /* if there is nothing to mix, return immediatly */ + /* if there is nothing to mix, return immediately */ if(sample_count <= 0 || dest_bufcount <= 0) { return; @@ -391,27 +428,65 @@ fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, for(i = 0; i < bufcount; i++) { fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); - fluid_real_t amp = buffers->bufs[i].amp; + fluid_real_t target_amp = buffers->bufs[i].target_amp; + fluid_real_t current_amp = buffers->bufs[i].current_amp; + fluid_real_t amp_incr; - if(buf == NULL || amp == 0.0f) + if(buf == NULL || (current_amp == 0.0f && target_amp == 0.0f)) { continue; } + amp_incr = (target_amp - current_amp) / FLUID_BUFSIZE; + FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0); - /* mixdown sample_count samples in the current buffer buf - Note, that this loop could be unrolled by FLUID_BUFSIZE elements */ - #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) - for(dsp_i = 0; dsp_i < sample_count; dsp_i++) + /* Mixdown sample_count samples in the current buffer buf + * + * For the first FLUID_BUFSIZE samples, we linearly interpolate the buffers amplitude to + * avoid clicks/pops when rapidly changing the channels panning (issue 768). + * + * We could have squashed this into one single loop by using an if clause within the loop body. + * But it seems like having two separate loops is easier for compilers to understand, and therefore + * auto-vectorizing the loops. + */ + if(sample_count < FLUID_BUFSIZE) { - // Index by blocks (not by samples) to let the compiler know that we always start accessing - // buf and dsp_buf at the FLUID_BUFSIZE*sizeof(fluid_real_t) byte boundary and never somewhere - // in between. - // A good compiler should understand: Aha, so I don't need to add a peel loop when vectorizing - // this loop. Great. - buf[start_block * FLUID_BUFSIZE + dsp_i] += amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + // scalar loop variant, the voice will have finished afterwards + for(dsp_i = 0; dsp_i < sample_count; dsp_i++) + { + buf[start_block * FLUID_BUFSIZE + dsp_i] += current_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + current_amp += amp_incr; + } } + else + { + // here goes the vectorizable loop + #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) + for(dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i++) + { + // We cannot simply increment current_amp by amp_incr during every iteration, as this would create a dependency and prevent vectorization. + buf[start_block * FLUID_BUFSIZE + dsp_i] += (current_amp + amp_incr * dsp_i) * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + } + + // we have reached the target_amp + if(target_amp > 0) + { + /* Note, that this loop could be unrolled by FLUID_BUFSIZE elements */ + #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) + for(dsp_i = FLUID_BUFSIZE; dsp_i < sample_count; dsp_i++) + { + // Index by blocks (not by samples) to let the compiler know that we always start accessing + // buf and dsp_buf at the FLUID_BUFSIZE*sizeof(fluid_real_t) byte boundary and never somewhere + // in between. + // A good compiler should understand: Aha, so I don't need to add a peel loop when vectorizing + // this loop. Great. + buf[start_block * FLUID_BUFSIZE + dsp_i] += target_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + } + } + } + + buffers->bufs[i].current_amp = target_amp; } } @@ -431,20 +506,22 @@ fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers, { /* render one block in src_buf */ int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]); + if(s == -1) { /* the voice is silent, mix back all the previously rendered sound */ fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, - total_samples - (last_block_mixed*FLUID_BUFSIZE), + total_samples - (last_block_mixed * FLUID_BUFSIZE), dest_bufs, dest_bufcount); - last_block_mixed = i+1; /* future block start index to mix from */ + last_block_mixed = i + 1; /* future block start index to mix from */ total_samples += FLUID_BUFSIZE; /* accumulate samples count rendered */ } else { /* the voice wasn't quiet. Some samples have been rendered [0..FLUID_BUFSIZE] */ total_samples += s; + if(s < FLUID_BUFSIZE) { /* voice has finished */ @@ -455,7 +532,7 @@ fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers, /* Now mix the remaining blocks from last_block_mixed to total_sample */ fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, - total_samples - (last_block_mixed*FLUID_BUFSIZE), + total_samples - (last_block_mixed * FLUID_BUFSIZE), dest_bufs, dest_bufcount); if(total_samples < blockcount * FLUID_BUFSIZE) @@ -671,21 +748,27 @@ fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *m DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate) { fluid_rvoice_mixer_t *mixer = obj; - fluid_real_t samplerate = param[1].real; // becausee fluid_synth_update_mixer() puts real into arg2 + fluid_real_t samplerate = param[1].real; // because fluid_synth_update_mixer() puts real into arg2 int i; + for(i = 0; i < mixer->fx_units; i++) { if(mixer->fx[i].chorus) { - delete_fluid_chorus(mixer->fx[i].chorus); + fluid_chorus_samplerate_change(mixer->fx[i].chorus, samplerate); } - mixer->fx[i].chorus = new_fluid_chorus(samplerate); - if(mixer->fx[i].reverb) { fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate); + + /* + fluid_revmodel_samplerate_change() shouldn't fail if the reverb was created + with sample_rate_max set to the maximum sample rate indicated in the settings. + If this condition isn't respected, the reverb will continue to work but with + lost of quality. + */ } } @@ -705,7 +788,11 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate) * @param fx_buf_count number of stereo effect buffers */ fluid_rvoice_mixer_t * -new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *evthandler, int extra_threads, int prio) +new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, + fluid_real_t sample_rate_max, + fluid_real_t sample_rate, + fluid_rvoice_eventhandler_t *evthandler, + int extra_threads, int prio) { int i; fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t); @@ -724,17 +811,19 @@ new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real /* allocate the reverb module */ mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units); + if(mixer->fx == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } - + FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx)); - + for(i = 0; i < fx_units; i++) { - mixer->fx[i].reverb = new_fluid_revmodel(sample_rate); + /* create reverb and chorus units */ + mixer->fx[i].reverb = new_fluid_revmodel(sample_rate_max, sample_rate); mixer->fx[i].chorus = new_fluid_chorus(sample_rate); if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL) @@ -769,7 +858,7 @@ new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real #endif return mixer; - + error_recovery: delete_fluid_rvoice_mixer(mixer); return NULL; @@ -791,7 +880,7 @@ fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers) void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer) { int i; - + fluid_return_if_fail(mixer != NULL); #if ENABLE_MIXER_THREADS @@ -820,7 +909,7 @@ void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer) #endif fluid_mixer_buffers_free(&mixer->buffers); - + for(i = 0; i < mixer->fx_units; i++) { if(mixer->fx[i].reverb) @@ -839,7 +928,6 @@ void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer) FLUID_FREE(mixer); } - #ifdef LADSPA /** * Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers @@ -883,6 +971,130 @@ void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, } #endif +/** + * set one or more reverb shadow parameters for one fx group. + * These parameters will be returned if queried. + * (see fluid_rvoice_mixer_reverb_get_param()) + * + * @param mixer that contains all fx units. + * @param fx_group index of the fx group to which parameters must be set. + * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied to + * all fx units. + * @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t) + * @param values table of parameters values. + */ +void +fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]) +{ + fluid_mixer_fx_t *fx = mixer->fx; + int nr_units = mixer->fx_units; + + if(fx_group >= 0) /* apply parameters to this fx group only */ + { + nr_units = fx_group + 1; + } + else /* apply parameters to all fx groups */ + { + fx_group = 0; + } + + for(; fx_group < nr_units; fx_group++) + { + int param; + + for(param = 0; param < FLUID_REVERB_PARAM_LAST; param++) + { + if(set & FLUID_REVPARAM_TO_SETFLAG(param)) + { + fx[fx_group].reverb_param[param] = values[param]; + } + } + } +} + +/** + * get one reverb shadow parameter for one fx group. + * (see fluid_rvoice_mixer_set_reverb_full()) + * + * @param mixer that contains all fx group units. + * @param fx_group index of the fx group to get parameter from. + * must be in the range [0..mixer->fx_units[. + * @param enum indicating the parameter to get. + * FLUID_REVERB_ROOMSIZE, reverb room size value. + * FLUID_REVERB_DAMP, reverb damping value. + * FLUID_REVERB_WIDTH, reverb width value. + * FLUID_REVERB_LEVEL, reverb level value. + * @return value. + */ +double +fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param) +{ + return mixer->fx[fx_group].reverb_param[param]; +} + +/** + * set one or more chorus shadow parameters for one fx group. + * These parameters will be returned if queried. + * (see fluid_rvoice_mixer_chorus_get_param()) + * + * @param mixer that contains all fx units. + * @param fx_group index of the fx group to which parameters must be set. + * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied + * to all fx group. + * Keep in mind, that the needed CPU time is proportional to 'nr'. + * @param set Flags indicating which parameters to set (#fluid_chorus_set_t) + * @param values table of pararameters. + */ +void +fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]) +{ + fluid_mixer_fx_t *fx = mixer->fx; + int nr_units = mixer->fx_units; + + if(fx_group >= 0) /* apply parameters to this group fx only */ + { + nr_units = fx_group + 1; + } + else /* apply parameters to all fx units*/ + { + fx_group = 0; + } + + for(; fx_group < nr_units; fx_group++) + { + int param; + + for(param = 0; param < FLUID_CHORUS_PARAM_LAST; param++) + { + if(set & FLUID_CHORPARAM_TO_SETFLAG(param)) + { + fx[fx_group].chorus_param[param] = values[param]; + } + } + } +} + +/** + * get one chorus shadow parameter for one fx group. + * (see fluid_rvoice_mixer_set_chorus_full()) + * + * @param mixer that contains all fx groups units. + * @param fx_group index of the fx group to get parameter from. + * must be in the range [0..mixer->fx_units[. + * @param get Flags indicating which parameter to get (#fluid_chorus_set_t) + * @return the parameter value (0.0 is returned if error) + */ +double +fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param) +{ + return mixer->fx[fx_group].chorus_param[param]; +} + +/* @deprecated: use fluid_rvoice_mixer_reverb_enable instead */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled) { fluid_rvoice_mixer_t *mixer = obj; @@ -891,6 +1103,43 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled) mixer->with_reverb = on; } +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable) +{ + fluid_rvoice_mixer_t *mixer = obj; + int fx_group = param[0].i; /* reverb fx group index */ + int on = param[1].i; /* on/off */ + + int nr_units = mixer->fx_units; + + /* does on/off must be applied only to fx group at index fx_group ? */ + if(fx_group >= 0) + { + mixer->fx[fx_group].reverb_on = on; + } + /* on/off must be applied to all fx groups */ + else + { + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + mixer->fx[fx_group].reverb_on = on; + } + } + + /* set with_reverb if at least one reverb unit is on */ + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + on = mixer->fx[fx_group].reverb_on; + + if(on) + { + break; + } + } + + mixer->with_reverb = on; +} + +/* @deprecated: use fluid_rvoice_mixer_chorus_enable instead */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled) { fluid_rvoice_mixer_t *mixer = obj; @@ -898,6 +1147,42 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled) mixer->with_chorus = on; } +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable) +{ + fluid_rvoice_mixer_t *mixer = obj; + int fx_group = param[0].i; /* chorus fx group index */ + int on = param[1].i; /* on/off */ + + int nr_units = mixer->fx_units; + + /* does on/off must be applied only to fx group at index fx_group ? */ + if(fx_group >= 0) + { + mixer->fx[fx_group].chorus_on = on; + } + /* on/off must be applied to all fx groups */ + else + { + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + mixer->fx[fx_group].chorus_on = on; + } + } + + /* set with_chorus if at least one chorus unit is on */ + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + on = mixer->fx[fx_group].chorus_on; + + if(on) + { + break; + } + } + + mixer->with_chorus = on; +} + void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on) { mixer->mix_fx_to_out = on; @@ -906,33 +1191,57 @@ void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on) DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params) { fluid_rvoice_mixer_t *mixer = obj; - int set = param[0].i; - int nr = param[1].i; - fluid_real_t level = param[2].real; - fluid_real_t speed = param[3].real; - fluid_real_t depth_ms = param[4].real; - int type = param[5].i; + int i = param[0].i; + int set = param[1].i; + int nr = param[2].i; + fluid_real_t level = param[3].real; + fluid_real_t speed = param[4].real; + fluid_real_t depth_ms = param[5].real; + int type = param[6].i; - int i; - for(i = 0; i < mixer->fx_units; i++) + int nr_units = mixer->fx_units; + + /* does parameters must be applied only to fx group i ? */ + if(i >= 0) { - fluid_chorus_set(mixer->fx[i].chorus, set, nr, level, speed, depth_ms, type); + nr_units = i + 1; + } + else + { + i = 0; /* parameters must be applied to all fx groups */ + } + + while(i < nr_units) + { + fluid_chorus_set(mixer->fx[i++].chorus, set, nr, level, speed, depth_ms, type); } } DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params) { fluid_rvoice_mixer_t *mixer = obj; - int set = param[0].i; - fluid_real_t roomsize = param[1].real; - fluid_real_t damping = param[2].real; - fluid_real_t width = param[3].real; - fluid_real_t level = param[4].real; + int i = param[0].i; /* fx group index */ + int set = param[1].i; + fluid_real_t roomsize = param[2].real; + fluid_real_t damping = param[3].real; + fluid_real_t width = param[4].real; + fluid_real_t level = param[5].real; - int i; - for(i = 0; i < mixer->fx_units; i++) + int nr_units = mixer->fx_units; + + /* does parameters change should be applied only to fx group i ? */ + if(i >= 0) { - fluid_revmodel_set(mixer->fx[i].reverb, set, roomsize, damping, width, level); + nr_units = i + 1; /* parameters change must be applied to fx groups i */ + } + else + { + i = 0; /* parameters change must be applied to all fx groups */ + } + + while(i < nr_units) + { + fluid_revmodel_set(mixer->fx[i++].reverb, set, roomsize, damping, width, level); } } @@ -940,6 +1249,7 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb) { fluid_rvoice_mixer_t *mixer = obj; int i; + for(i = 0; i < mixer->fx_units; i++) { fluid_revmodel_reset(mixer->fx[i].reverb); @@ -950,6 +1260,7 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus) { fluid_rvoice_mixer_t *mixer = obj; int i; + for(i = 0; i < mixer->fx_units; i++) { fluid_chorus_reset(mixer->fx[i].chorus); @@ -1267,27 +1578,33 @@ fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcoun static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer) { int i; - fluid_atomic_int_set(&mixer->threads_should_terminate, 1); - // Signal threads to wake up - fluid_cond_mutex_lock(mixer->wakeup_threads_m); - for(i = 0; i < mixer->thread_count; i++) + // if no threads have been created yet (e.g. because a previous error prevented creation of threads + // mutexes and condition variables), skip terminating threads + if(mixer->thread_count != 0) { - fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE); - } + fluid_atomic_int_set(&mixer->threads_should_terminate, 1); + // Signal threads to wake up + fluid_cond_mutex_lock(mixer->wakeup_threads_m); - fluid_cond_broadcast(mixer->wakeup_threads); - fluid_cond_mutex_unlock(mixer->wakeup_threads_m); - - for(i = 0; i < mixer->thread_count; i++) - { - if(mixer->threads[i].thread) + for(i = 0; i < mixer->thread_count; i++) { - fluid_thread_join(mixer->threads[i].thread); - delete_fluid_thread(mixer->threads[i].thread); + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE); } - fluid_mixer_buffers_free(&mixer->threads[i]); + fluid_cond_broadcast(mixer->wakeup_threads); + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + for(i = 0; i < mixer->thread_count; i++) + { + if(mixer->threads[i].thread) + { + fluid_thread_join(mixer->threads[i].thread); + delete_fluid_thread(mixer->threads[i].thread); + } + + fluid_mixer_buffers_free(&mixer->threads[i]); + } } FLUID_FREE(mixer->threads); diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.h b/libs/fluidsynth/src/fluid_rvoice_mixer.h index 1b3fceb342..6139081185 100644 --- a/libs/fluidsynth/src/fluid_rvoice_mixer.h +++ b/libs/fluidsynth/src/fluid_rvoice_mixer.h @@ -37,16 +37,38 @@ int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer); int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer); #endif fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, - fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *, int, int); + fluid_real_t sample_rate_max, fluid_real_t sample_rate, + fluid_rvoice_eventhandler_t *, int, int); void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *); +void +fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]); + +double +fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param); +void +fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]); +double +fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param); + DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony); + +/* @deprecated */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled); +/* @deprecated */ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable); + DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params); DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params); diff --git a/libs/fluidsynth/src/fluid_samplecache.c b/libs/fluidsynth/src/fluid_samplecache.c index f464f178b7..64e9e9e709 100644 --- a/libs/fluidsynth/src/fluid_samplecache.c +++ b/libs/fluidsynth/src/fluid_samplecache.c @@ -36,7 +36,7 @@ typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t; struct _fluid_samplecache_entry_t { - /* The follwing members all form the cache key */ + /* The following members all form the cache key */ char *filename; time_t modification_time; unsigned int sf_samplepos; @@ -89,6 +89,7 @@ int fluid_samplecache_load(SFData *sf, if(entry == NULL) { + fluid_mutex_unlock(samplecache_mutex); entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime); if(entry == NULL) @@ -97,8 +98,10 @@ int fluid_samplecache_load(SFData *sf, goto unlock_exit; } + fluid_mutex_lock(samplecache_mutex); samplecache_list = fluid_list_prepend(samplecache_list, entry); } + fluid_mutex_unlock(samplecache_mutex); if(try_mlock && !entry->mlocked) { @@ -129,7 +132,6 @@ int fluid_samplecache_load(SFData *sf, ret = entry->sample_count; unlock_exit: - fluid_mutex_unlock(samplecache_mutex); return ret; } @@ -290,3 +292,22 @@ static int fluid_get_file_modification_time(char *filename, time_t *modification *modification_time = buf.st_mtime; return FLUID_OK; } + + +/* Only used for tests */ +int fluid_samplecache_count_entries(void) +{ + fluid_list_t *entry; + int count = 0; + + fluid_mutex_lock(samplecache_mutex); + + for(entry = samplecache_list; entry != NULL; entry = fluid_list_next(entry)) + { + count++; + } + + fluid_mutex_unlock(samplecache_mutex); + + return count; +} diff --git a/libs/fluidsynth/src/fluid_samplecache.h b/libs/fluidsynth/src/fluid_samplecache.h index 49b802ba61..de6206ba7d 100644 --- a/libs/fluidsynth/src/fluid_samplecache.h +++ b/libs/fluidsynth/src/fluid_samplecache.h @@ -31,4 +31,7 @@ int fluid_samplecache_load(SFData *sf, int fluid_samplecache_unload(const short *sample_data); +/* Only used for tests */ +int fluid_samplecache_count_entries(void); + #endif /* _FLUID_SAMPLECACHE_H */ diff --git a/libs/fluidsynth/src/fluid_settings.c b/libs/fluidsynth/src/fluid_settings.c index a825603a44..d5c6b940f3 100644 --- a/libs/fluidsynth/src/fluid_settings.c +++ b/libs/fluidsynth/src/fluid_settings.c @@ -252,6 +252,7 @@ delete_fluid_set_setting(fluid_setting_node_t *node) /** * Create a new settings object + * * @return the pointer to the settings object */ fluid_settings_t * @@ -275,6 +276,7 @@ new_fluid_settings(void) /** * Delete the provided settings object + * * @param settings a settings object */ void @@ -543,6 +545,7 @@ fluid_settings_register_str(fluid_settings_t *settings, const char *name, const if(node->type == FLUID_STR_TYPE) { fluid_str_setting_t *setting = &node->str; + FLUID_FREE(setting->def); setting->def = def ? FLUID_STRDUP(def) : NULL; setting->hints = hints; retval = FLUID_OK; @@ -790,6 +793,40 @@ int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, return FLUID_OK; } +void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name) +{ + fluid_setting_node_t *node; + void* retval = NULL; + + fluid_return_val_if_fail(settings != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_return_val_if_fail(name[0] != '\0', NULL); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + retval = setting->data; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + retval = setting->data; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + retval = setting->data; + } + } + + fluid_rec_mutex_unlock(settings->mutex); + return retval; +} + /** * Get the type of the setting with the given name * @@ -872,6 +909,10 @@ fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hint * @param settings a settings object * @param name a setting's name * @return TRUE if the setting is changeable in real-time, FALSE otherwise + * + * @note Before using this function, make sure the @p settings object has already been used to create + * a synthesizer, a MIDI driver, an audio driver, a MIDI player, or a command handler (depending on + * which settings you want to query). */ int fluid_settings_is_realtime(fluid_settings_t *settings, const char *name) @@ -980,15 +1021,17 @@ error_recovery: /** * Copy the value of a string setting into the provided buffer (thread safe) + * * @param settings a settings object * @param name a setting's name * @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 zero terminator) * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise - * @since 1.1.0 * * @note A size of 256 should be more than sufficient for the string buffer. + * + * @since 1.1.0 */ int fluid_settings_copystr(fluid_settings_t *settings, const char *name, @@ -1040,14 +1083,16 @@ fluid_settings_copystr(fluid_settings_t *settings, const char *name, /** * 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 #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED 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 fluid_free() when done using it. + * + * @since 1.1.0 */ int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str) @@ -1159,19 +1204,20 @@ fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const cha } /** - * 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. + * Get the default value of a string setting. * * @param settings a settings object * @param name a setting's name * @param def the default string value of the setting if it exists - * @return FLUID_OK on success, FLUID_FAILED otherwise + * @return FLUID_OK if a default vaule exists, FLUID_FAILED otherwise + * + * @note The returned string is not owned by the caller and should not be modified or freed. */ int -fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def) +fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char const **def) { fluid_setting_node_t *node; - char *retval = NULL; + char const *retval = NULL; fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); fluid_return_val_if_fail(name != NULL, FLUID_FAILED); @@ -1205,6 +1251,7 @@ fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char /** * 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 @@ -1242,6 +1289,7 @@ fluid_settings_add_option(fluid_settings_t *settings, const char *name, const ch /** * 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 @@ -1567,6 +1615,7 @@ fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val) /** * Get the range of values of an integer setting + * * @param settings a settings object * @param name a setting's name * @param min setting's range lower limit @@ -1692,10 +1741,12 @@ fluid_settings_foreach_option(fluid_settings_t *settings, const char *name, /** * Count option string values for a string setting. + * * @param settings a settings object * @param name Name of setting * @return Count of options for this string setting (0 if none, -1 if not found * or not a string setting) + * * @since 1.1.0 */ int @@ -1723,11 +1774,13 @@ fluid_settings_option_count(fluid_settings_t *settings, const char *name) /** * 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 \p name or not a string setting). Free the string when finished with it by using fluid_free(). + * * @since 1.1.0 */ char * diff --git a/libs/fluidsynth/src/fluid_settings.h b/libs/fluidsynth/src/fluid_settings.h index 4a952f1bad..c05e0ed068 100644 --- a/libs/fluidsynth/src/fluid_settings.h +++ b/libs/fluidsynth/src/fluid_settings.h @@ -52,4 +52,6 @@ int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, int fluid_settings_split_csv(const char *str, int *buf, int buf_len); +void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name); + #endif /* _FLUID_SETTINGS_H */ diff --git a/libs/fluidsynth/src/fluid_sffile.c b/libs/fluidsynth/src/fluid_sffile.c index 131b56b0c2..72ac9e1e2e 100644 --- a/libs/fluidsynth/src/fluid_sffile.c +++ b/libs/fluidsynth/src/fluid_sffile.c @@ -112,104 +112,34 @@ static const uint32_t idlist[] = SM24_FCC }; -/* generator types */ -typedef enum -{ - Gen_StartAddrOfs, - Gen_EndAddrOfs, - Gen_StartLoopAddrOfs, - Gen_EndLoopAddrOfs, - Gen_StartAddrCoarseOfs, - Gen_ModLFO2Pitch, - Gen_VibLFO2Pitch, - Gen_ModEnv2Pitch, - Gen_FilterFc, - Gen_FilterQ, - Gen_ModLFO2FilterFc, - Gen_ModEnv2FilterFc, - Gen_EndAddrCoarseOfs, - Gen_ModLFO2Vol, - Gen_Unused1, - Gen_ChorusSend, - Gen_ReverbSend, - Gen_Pan, - Gen_Unused2, - Gen_Unused3, - Gen_Unused4, - Gen_ModLFODelay, - Gen_ModLFOFreq, - Gen_VibLFODelay, - Gen_VibLFOFreq, - Gen_ModEnvDelay, - Gen_ModEnvAttack, - Gen_ModEnvHold, - Gen_ModEnvDecay, - Gen_ModEnvSustain, - Gen_ModEnvRelease, - Gen_Key2ModEnvHold, - Gen_Key2ModEnvDecay, - Gen_VolEnvDelay, - Gen_VolEnvAttack, - Gen_VolEnvHold, - Gen_VolEnvDecay, - Gen_VolEnvSustain, - Gen_VolEnvRelease, - Gen_Key2VolEnvHold, - Gen_Key2VolEnvDecay, - Gen_Instrument, - Gen_Reserved1, - Gen_KeyRange, - Gen_VelRange, - Gen_StartLoopAddrCoarseOfs, - Gen_Keynum, - Gen_Velocity, - Gen_Attenuation, - Gen_Reserved2, - Gen_EndLoopAddrCoarseOfs, - Gen_CoarseTune, - Gen_FineTune, - Gen_SampleId, - Gen_SampleModes, - Gen_Reserved3, - Gen_ScaleTune, - Gen_ExclusiveClass, - Gen_OverrideRootKey, - Gen_Dummy -} Gen_Type; - -#define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */ -#define Gen_Count Gen_Dummy /* count of generators */ -#define GenArrSize sizeof(SFGenAmount) * Gen_Count /* gen array size */ - - static const unsigned short invalid_inst_gen[] = { - Gen_Unused1, - Gen_Unused2, - Gen_Unused3, - Gen_Unused4, - Gen_Reserved1, - Gen_Reserved2, - Gen_Reserved3, - 0 + GEN_UNUSED1, + GEN_UNUSED2, + GEN_UNUSED3, + GEN_UNUSED4, + GEN_RESERVED1, + GEN_RESERVED2, + GEN_RESERVED3, + GEN_INSTRUMENT, }; static const unsigned short invalid_preset_gen[] = { - Gen_StartAddrOfs, - Gen_EndAddrOfs, - Gen_StartLoopAddrOfs, - Gen_EndLoopAddrOfs, - Gen_StartAddrCoarseOfs, - Gen_EndAddrCoarseOfs, - Gen_StartLoopAddrCoarseOfs, - Gen_Keynum, - Gen_Velocity, - Gen_EndLoopAddrCoarseOfs, - Gen_SampleModes, - Gen_ExclusiveClass, - Gen_OverrideRootKey, - 0 + GEN_STARTADDROFS, + GEN_ENDADDROFS, + GEN_STARTLOOPADDROFS, + GEN_ENDLOOPADDROFS, + GEN_STARTADDRCOARSEOFS, + GEN_ENDADDRCOARSEOFS, + GEN_STARTLOOPADDRCOARSEOFS, + GEN_KEYNUM, + GEN_VELOCITY, + GEN_ENDLOOPADDRCOARSEOFS, + GEN_SAMPLEMODE, + GEN_EXCLUSIVECLASS, + GEN_OVERRIDEROOTKEY, + GEN_SAMPLEID, }; @@ -300,17 +230,15 @@ static int load_body(SFData *sf); static int process_info(SFData *sf, int size); static int process_sdta(SFData *sf, unsigned int size); static int process_pdta(SFData *sf, int size); -static int load_phdr(SFData *sf, int size); +static int load_phdr(SFData *sf, unsigned int size); static int load_pbag(SFData *sf, int size); static int load_pmod(SFData *sf, int size); -static int load_pgen(SFData *sf, int size); -static int load_ihdr(SFData *sf, int size); +extern int load_pgen(SFData *sf, int size); +static int load_ihdr(SFData *sf, unsigned int size); static int load_ibag(SFData *sf, int size); static int load_imod(SFData *sf, int size); -static int load_igen(SFData *sf, int size); +extern int load_igen(SFData *sf, int size); static int load_shdr(SFData *sf, unsigned int size); -static int fixup_pgen(SFData *sf); -static int fixup_igen(SFData *sf); static int chunkid(uint32_t id); static int read_listchunk(SFData *sf, SFChunk *chunk); @@ -320,21 +248,17 @@ static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist); static int valid_inst_genid(unsigned short genid); static int valid_preset_genid(unsigned short genid); - -static void delete_preset(SFPreset *preset); -static void delete_inst(SFInst *inst); -static void delete_zone(SFZone *zone); - static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); /** * Check if a file is a SoundFont file. * - * If fluidsynth was built with DLS support, this function will also identify DLS files. * @param filename Path to the file to check * @return TRUE if it could be a SF2, SF3 or DLS file, FALSE otherwise * + * If fluidsynth was built with DLS support, this function will also identify DLS files. + * * @note This function only checks whether header(s) in the RIFF chunk are present. * A call to fluid_synth_sfload() might still fail. */ @@ -343,31 +267,37 @@ int fluid_is_soundfont(const char *filename) FILE *fp; uint32_t fcc; int retcode = FALSE; + const char* err_msg; do { - if((fp = fluid_file_open(filename, NULL)) == NULL) + if((fp = fluid_file_open(filename, &err_msg)) == NULL) { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): fopen() failed: '%s'", err_msg); return retcode; } if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read RIFF chunk id."); break; } if(fcc != RIFF_FCC) { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): expected RIFF chunk id '0x%04X' but got '0x%04X'.", (unsigned int) RIFF_FCC, (unsigned int)fcc); break; } if(FLUID_FSEEK(fp, 4, SEEK_CUR)) { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): cannot seek +4 bytes."); break; } if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read SFBK chunk id."); break; } @@ -405,7 +335,7 @@ int fluid_is_soundfont(const char *filename) SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs) { SFData *sf; - int fsize = 0; + fluid_long_long_t fsize = 0; if(!(sf = FLUID_NEW(SFData))) { @@ -415,6 +345,7 @@ SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs) FLUID_MEMSET(sf, 0, sizeof(SFData)); + fluid_rec_mutex_init(sf->mtx); sf->fcbs = fcbs; if((sf->sffd = fcbs->fopen(fname)) == NULL) @@ -523,6 +454,7 @@ void fluid_sffile_close(SFData *sf) SFPreset *preset; SFInst *inst; + fluid_rec_mutex_destroy(sf->mtx); if(sf->sffd) { sf->fcbs->fclose(sf->sffd); @@ -690,16 +622,6 @@ static int load_body(SFData *sf) return FALSE; } - if(!fixup_pgen(sf)) - { - return FALSE; - } - - if(!fixup_igen(sf)) - { - return FALSE; - } - /* sort preset list by bank, preset # */ sf->preset = fluid_list_sort(sf->preset, (fluid_compare_func_t)preset_compare_func); @@ -893,7 +815,7 @@ static int process_sdta(SFData *sf, unsigned int size) if(chunk.size > size) { - FLUID_LOG(FLUID_WARN, "SM24 exeeds SDTA chunk, ignoring SM24"); + FLUID_LOG(FLUID_WARN, "SM24 exceeds SDTA chunk, ignoring SM24"); goto ret; // no error } @@ -1047,9 +969,10 @@ static int process_pdta(SFData *sf, int size) } /* preset header loader */ -static int load_phdr(SFData *sf, int size) +static int load_phdr(SFData *sf, unsigned int size) { - int i, i2; + unsigned int i; + int i2; SFPreset *preset, *prev_preset = NULL; unsigned short pbag_idx, prev_pbag_idx = 0; @@ -1084,9 +1007,9 @@ static int load_phdr(SFData *sf, int size) READW(sf, preset->prenum); READW(sf, preset->bank); READW(sf, pbag_idx); - READD(sf, preset->libr); - READD(sf, preset->genre); - READD(sf, preset->morph); + FSKIP(sf, 4); /* library ignored */ + FSKIP(sf, 4); /* genre ignored */ + FSKIP(sf, 4); /* morphology ignored */ if(prev_preset) { @@ -1136,7 +1059,8 @@ static int load_phdr(SFData *sf, int size) /* preset bag loader */ static int load_pbag(SFData *sf, int size) { - fluid_list_t *p, *p2; + fluid_list_t *preset_list; + fluid_list_t *zone_list; SFZone *z, *pz = NULL; unsigned short genndx, modndx; unsigned short pgenndx = 0, pmodndx = 0; @@ -1148,16 +1072,16 @@ static int load_pbag(SFData *sf, int size) return FALSE; } - p = sf->preset; + preset_list = sf->preset; - while(p) + /* traverse through presets */ + while(preset_list) { - /* traverse through presets */ - p2 = ((SFPreset *)(p->data))->zone; + zone_list = ((SFPreset *)(preset_list->data))->zone; - while(p2) + /* traverse preset's zones */ + while(zone_list) { - /* traverse preset's zones */ if((size -= SF_BAG_SIZE) < 0) { FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); @@ -1170,12 +1094,11 @@ static int load_pbag(SFData *sf, int size) return FALSE; } - p2->data = z; + zone_list->data = z; z->gen = NULL; /* Init gen and mod before possible failure, */ z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */ READW(sf, genndx); /* possible read failure ^ */ READW(sf, modndx); - z->instsamp = NULL; if(pz) { @@ -1210,10 +1133,10 @@ static int load_pbag(SFData *sf, int size) pz = z; /* update previous zone ptr */ pgenndx = genndx; /* update previous zone gen index */ pmodndx = modndx; /* update previous zone mod index */ - p2 = fluid_list_next(p2); + zone_list = fluid_list_next(zone_list); } - p = fluid_list_next(p); + preset_list = fluid_list_next(preset_list); } size -= SF_BAG_SIZE; @@ -1274,22 +1197,24 @@ static int load_pbag(SFData *sf, int size) /* preset modulator loader */ static int load_pmod(SFData *sf, int size) { - fluid_list_t *p, *p2, *p3; + fluid_list_t *preset_list; + fluid_list_t *zone_list; + fluid_list_t *mod_list; SFMod *m; - p = sf->preset; + preset_list = sf->preset; - while(p) + while(preset_list) { /* traverse through all presets */ - p2 = ((SFPreset *)(p->data))->zone; + zone_list = ((SFPreset *)(preset_list->data))->zone; - while(p2) + while(zone_list) { /* traverse this preset's zones */ - p3 = ((SFZone *)(p2->data))->mod; + mod_list = ((SFZone *)(zone_list->data))->mod; - while(p3) + while(mod_list) { /* load zone's modulators */ if((size -= SF_MOD_SIZE) < 0) @@ -1304,19 +1229,19 @@ static int load_pmod(SFData *sf, int size) return FALSE; } - p3->data = m; + mod_list->data = m; READW(sf, m->src); READW(sf, m->dest); READW(sf, m->amount); READW(sf, m->amtsrc); READW(sf, m->trans); - p3 = fluid_list_next(p3); + mod_list = fluid_list_next(mod_list); } - p2 = fluid_list_next(p2); + zone_list = fluid_list_next(zone_list); } - p = fluid_list_next(p); + preset_list = fluid_list_next(preset_list); } /* @@ -1353,37 +1278,37 @@ static int load_pmod(SFData *sf, int size) * if a generator follows an instrument discard it * if a duplicate generator exists replace previous one * ------------------------------------------------------------------- */ -static int load_pgen(SFData *sf, int size) +int load_pgen(SFData *sf, int size) { - fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; - SFZone *z; + fluid_list_t *dup; + fluid_list_t *preset_list; + fluid_list_t *zone_list; + fluid_list_t *gen_list; + SFZone *zone; SFGen *g; + SFPreset *preset; SFGenAmount genval; unsigned short genid; - int level, skip, drop, gzone, discarded; + int level, skip, drop, discarded; - p = sf->preset; + preset_list = sf->preset; - while(p) + while(preset_list) { + preset = fluid_list_get(preset_list); + /* traverse through all presets */ - gzone = FALSE; discarded = FALSE; - p2 = ((SFPreset *)(p->data))->zone; + zone_list = preset->zone; - if(p2) + /* traverse preset's zones */ + while(zone_list) { - hz = &p2; - } - - while(p2) - { - /* traverse preset's zones */ + zone = fluid_list_get(zone_list); level = 0; - z = (SFZone *)(p2->data); - p3 = z->gen; + gen_list = zone->gen; - while(p3) + while(gen_list) { /* load zone's generators */ dup = NULL; @@ -1398,7 +1323,7 @@ static int load_pgen(SFData *sf, int size) READW(sf, genid); - if(genid == Gen_KeyRange) + if(genid == GEN_KEYRANGE) { /* nothing precedes */ if(level == 0) @@ -1412,7 +1337,7 @@ static int load_pgen(SFData *sf, int size) skip = TRUE; } } - else if(genid == Gen_VelRange) + else if(genid == GEN_VELRANGE) { /* only KeyRange precedes */ if(level <= 1) @@ -1426,13 +1351,11 @@ static int load_pgen(SFData *sf, int size) skip = TRUE; } } - else if(genid == Gen_Instrument) + else if(genid == GEN_INSTRUMENT) { /* inst is last gen */ level = 3; READW(sf, genval.uword); - ((SFZone *)(p2->data))->instsamp = FLUID_INT_TO_POINTER(genval.uword + 1); - break; /* break out of generator loop */ } else { @@ -1442,7 +1365,7 @@ static int load_pgen(SFData *sf, int size) { /* generator valid? */ READW(sf, genval.sword); - dup = find_gen_by_id(genid, z->gen); + dup = find_gen_by_id(genid, zone->gen); } else { @@ -1461,7 +1384,7 @@ static int load_pgen(SFData *sf, int size) return FALSE; } - p3->data = g; + gen_list->data = g; g->id = genid; } else @@ -1482,51 +1405,42 @@ static int load_pgen(SFData *sf, int size) if(!drop) { - p3 = fluid_list_next(p3); /* next gen */ + gen_list = fluid_list_next(gen_list); /* next gen */ } else { - SLADVREM(z->gen, p3); /* drop place holder */ + SLADVREM(zone->gen, gen_list); /* drop place holder */ + } + + /* GEN_INSTRUMENT should be the last generator */ + if (level == 3) + { + break; } } /* generator loop */ - if(level == 3) + /* Anything below level 3 means it's a global zone. The global zone + * should always be the first zone in the list, so discard any + * other global zones we encounter */ + if(level < 3 && (zone_list != preset->zone)) { - SLADVREM(z->gen, p3); /* zone has inst? */ - } - else - { - /* congratulations its a global zone */ - if(!gzone) - { - /* Prior global zones? */ - gzone = TRUE; + /* advance to next zone before deleting the current list element */ + zone_list = fluid_list_next(zone_list); - /* if global zone is not 1st zone, relocate */ - if(*hz != p2) - { - void *save = p2->data; - FLUID_LOG(FLUID_WARN, "Preset '%s': Global zone is not first zone", - ((SFPreset *)(p->data))->name); - SLADVREM(*hz, p2); - *hz = fluid_list_prepend(*hz, save); - continue; - } - } - else - { - /* previous global zone exists, discard */ - FLUID_LOG(FLUID_WARN, "Preset '%s': Discarding invalid global zone", - ((SFPreset *)(p->data))->name); - *hz = fluid_list_remove(*hz, p2->data); - delete_zone((SFZone *)fluid_list_get(p2)); - } + FLUID_LOG(FLUID_WARN, "Preset '%s': Discarding invalid global zone", + preset->name); + preset->zone = fluid_list_remove(preset->zone, zone); + delete_zone(zone); + + /* we have already advanced the zone_list pointer, so continue with next zone */ + continue; } - while(p3) + /* All remaining generators are invalid and should be discarded + * (because they come after an instrument generator) */ + while(gen_list) { - /* Kill any zones following an instrument */ discarded = TRUE; if((size -= SF_GEN_SIZE) < 0) @@ -1536,20 +1450,20 @@ static int load_pgen(SFData *sf, int size) } FSKIP(sf, SF_GEN_SIZE); - SLADVREM(z->gen, p3); + SLADVREM(zone->gen, gen_list); } - p2 = fluid_list_next(p2); /* next zone */ + zone_list = fluid_list_next(zone_list); } if(discarded) { FLUID_LOG(FLUID_WARN, "Preset '%s': Some invalid generators were discarded", - ((SFPreset *)(p->data))->name); + preset->name); } - p = fluid_list_next(p); + preset_list = fluid_list_next(preset_list); } /* in case there isn't a terminal record */ @@ -1572,10 +1486,11 @@ static int load_pgen(SFData *sf, int size) } /* instrument header loader */ -static int load_ihdr(SFData *sf, int size) +static int load_ihdr(SFData *sf, unsigned int size) { - int i, i2; - SFInst *p, *pr = NULL; /* ptr to current & previous instrument */ + unsigned int i; + int i2; + SFInst *inst, *prev_inst = NULL; /* ptr to current & previous instrument */ unsigned short zndx, pzndx = 0; if(size % SF_IHDR_SIZE || size == 0) /* chunk size is valid? */ @@ -1597,19 +1512,19 @@ static int load_ihdr(SFData *sf, int size) for(i = 0; i < size; i++) { /* load all instrument headers */ - if((p = FLUID_NEW(SFInst)) == NULL) + if((inst = FLUID_NEW(SFInst)) == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } - sf->inst = fluid_list_append(sf->inst, p); - p->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */ - p->idx = i; - READSTR(sf, &p->name); /* Possible read failure ^ */ + sf->inst = fluid_list_append(sf->inst, inst); + inst->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */ + inst->idx = i; + READSTR(sf, &inst->name); /* Possible read failure ^ */ READW(sf, zndx); - if(pr) + if(prev_inst) { /* not first instrument? */ if(zndx < pzndx) @@ -1622,7 +1537,7 @@ static int load_ihdr(SFData *sf, int size) while(i2--) { - pr->zone = fluid_list_prepend(pr->zone, NULL); + prev_inst->zone = fluid_list_prepend(prev_inst->zone, NULL); } } else if(zndx > 0) /* 1st inst, warn if ofs >0 */ @@ -1631,7 +1546,7 @@ static int load_ihdr(SFData *sf, int size) } pzndx = zndx; - pr = p; /* update instrument ptr */ + prev_inst = inst; /* update instrument ptr */ } FSKIP(sf, 20); @@ -1647,7 +1562,7 @@ static int load_ihdr(SFData *sf, int size) while(i2--) { - pr->zone = fluid_list_prepend(pr->zone, NULL); + prev_inst->zone = fluid_list_prepend(prev_inst->zone, NULL); } return TRUE; @@ -1656,7 +1571,8 @@ static int load_ihdr(SFData *sf, int size) /* instrument bag loader */ static int load_ibag(SFData *sf, int size) { - fluid_list_t *p, *p2; + fluid_list_t *inst_list; + fluid_list_t *zone_list; SFZone *z, *pz = NULL; unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; int i; @@ -1667,14 +1583,14 @@ static int load_ibag(SFData *sf, int size) return FALSE; } - p = sf->inst; + inst_list = sf->inst; - while(p) + while(inst_list) { /* traverse through inst */ - p2 = ((SFInst *)(p->data))->zone; + zone_list = ((SFInst *)(inst_list->data))->zone; - while(p2) + while(zone_list) { /* load this inst's zones */ if((size -= SF_BAG_SIZE) < 0) @@ -1689,12 +1605,11 @@ static int load_ibag(SFData *sf, int size) return FALSE; } - p2->data = z; + zone_list->data = z; z->gen = NULL; /* In case of failure, */ z->mod = NULL; /* fluid_sffile_close can clean up */ READW(sf, genndx); /* READW = possible read failure */ READW(sf, modndx); - z->instsamp = NULL; if(pz) { @@ -1729,10 +1644,10 @@ static int load_ibag(SFData *sf, int size) pz = z; /* update previous zone ptr */ pgenndx = genndx; pmodndx = modndx; - p2 = fluid_list_next(p2); + zone_list = fluid_list_next(zone_list); } - p = fluid_list_next(p); + inst_list = fluid_list_next(inst_list); } size -= SF_BAG_SIZE; @@ -1794,22 +1709,24 @@ static int load_ibag(SFData *sf, int size) /* instrument modulator loader */ static int load_imod(SFData *sf, int size) { - fluid_list_t *p, *p2, *p3; + fluid_list_t *inst_list; + fluid_list_t *zone_list; + fluid_list_t *mod_list; SFMod *m; - p = sf->inst; + inst_list = sf->inst; - while(p) + while(inst_list) { /* traverse through all inst */ - p2 = ((SFInst *)(p->data))->zone; + zone_list = ((SFInst *)(inst_list->data))->zone; - while(p2) + while(zone_list) { /* traverse this inst's zones */ - p3 = ((SFZone *)(p2->data))->mod; + mod_list = ((SFZone *)(zone_list->data))->mod; - while(p3) + while(mod_list) { /* load zone's modulators */ if((size -= SF_MOD_SIZE) < 0) @@ -1824,19 +1741,19 @@ static int load_imod(SFData *sf, int size) return FALSE; } - p3->data = m; + mod_list->data = m; READW(sf, m->src); READW(sf, m->dest); READW(sf, m->amount); READW(sf, m->amtsrc); READW(sf, m->trans); - p3 = fluid_list_next(p3); + mod_list = fluid_list_next(mod_list); } - p2 = fluid_list_next(p2); + zone_list = fluid_list_next(zone_list); } - p = fluid_list_next(p); + inst_list = fluid_list_next(inst_list); } /* @@ -1862,37 +1779,38 @@ static int load_imod(SFData *sf, int size) } /* load instrument generators (see load_pgen for loading rules) */ -static int load_igen(SFData *sf, int size) +int load_igen(SFData *sf, int size) { - fluid_list_t *p, *p2, *p3, *dup, **hz = NULL; - SFZone *z; + fluid_list_t *dup; + fluid_list_t *inst_list; + fluid_list_t *zone_list; + fluid_list_t *gen_list; + SFZone *zone; SFGen *g; + SFInst *inst; SFGenAmount genval; unsigned short genid; - int level, skip, drop, gzone, discarded; + int level, skip, drop, discarded; - p = sf->inst; + inst_list = sf->inst; - while(p) + /* traverse through all instruments */ + while(inst_list) { - /* traverse through all instruments */ - gzone = FALSE; + inst = fluid_list_get(inst_list); + discarded = FALSE; - p2 = ((SFInst *)(p->data))->zone; + zone_list = inst->zone; - if(p2) + /* traverse this instrument's zones */ + while(zone_list) { - hz = &p2; - } + zone = fluid_list_get(zone_list); - while(p2) - { - /* traverse this instrument's zones */ level = 0; - z = (SFZone *)(p2->data); - p3 = z->gen; + gen_list = zone->gen; - while(p3) + while(gen_list) { /* load zone's generators */ dup = NULL; @@ -1907,7 +1825,7 @@ static int load_igen(SFData *sf, int size) READW(sf, genid); - if(genid == Gen_KeyRange) + if(genid == GEN_KEYRANGE) { /* nothing precedes */ if(level == 0) @@ -1921,7 +1839,7 @@ static int load_igen(SFData *sf, int size) skip = TRUE; } } - else if(genid == Gen_VelRange) + else if(genid == GEN_VELRANGE) { /* only KeyRange precedes */ if(level <= 1) @@ -1935,13 +1853,11 @@ static int load_igen(SFData *sf, int size) skip = TRUE; } } - else if(genid == Gen_SampleId) + else if(genid == GEN_SAMPLEID) { /* sample is last gen */ level = 3; READW(sf, genval.uword); - ((SFZone *)(p2->data))->instsamp = FLUID_INT_TO_POINTER(genval.uword + 1); - break; /* break out of generator loop */ } else { @@ -1951,7 +1867,7 @@ static int load_igen(SFData *sf, int size) { /* gen valid? */ READW(sf, genval.sword); - dup = find_gen_by_id(genid, z->gen); + dup = find_gen_by_id(genid, zone->gen); } else { @@ -1970,7 +1886,7 @@ static int load_igen(SFData *sf, int size) return FALSE; } - p3->data = g; + gen_list->data = g; g->id = genid; } else @@ -1991,50 +1907,42 @@ static int load_igen(SFData *sf, int size) if(!drop) { - p3 = fluid_list_next(p3); /* next gen */ + gen_list = fluid_list_next(gen_list); /* next gen */ } else { - SLADVREM(z->gen, p3); + SLADVREM(zone->gen, gen_list); + } + + /* GEN_SAMPLEID should be last generator */ + if (level == 3) + { + break; } } /* generator loop */ - if(level == 3) + /* Anything below level 3 means it's a global zone. The global zone + * should always be the first zone in the list, so discard any + * other global zones we encounter */ + if(level < 3 && (zone_list != inst->zone)) { - SLADVREM(z->gen, p3); /* zone has sample? */ - } - else - { - /* its a global zone */ - if(!gzone) - { - gzone = TRUE; + /* advance to next zone before deleting the current list element */ + zone_list = fluid_list_next(zone_list); - /* if global zone is not 1st zone, relocate */ - if(*hz != p2) - { - void *save = p2->data; - FLUID_LOG(FLUID_WARN, "Instrument '%s': Global zone is not first zone", - ((SFPreset *)(p->data))->name); - SLADVREM(*hz, p2); - *hz = fluid_list_prepend(*hz, save); - continue; - } - } - else - { - /* previous global zone exists, discard */ - FLUID_LOG(FLUID_WARN, "Instrument '%s': Discarding invalid global zone", - ((SFInst *)(p->data))->name); - *hz = fluid_list_remove(*hz, p2->data); - delete_zone((SFZone *)fluid_list_get(p2)); - } + FLUID_LOG(FLUID_WARN, "Instrument '%s': Discarding invalid global zone", + inst->name); + inst->zone = fluid_list_remove(inst->zone, zone); + delete_zone(zone); + + /* we have already advanced the zone_list pointer, so continue with next zone */ + continue; } - while(p3) + /* All remaining generators must be invalid and should be discarded + * (because they come after a sampleid generator) */ + while(gen_list) { - /* Kill any zones following a sample */ discarded = TRUE; if((size -= SF_GEN_SIZE) < 0) @@ -2044,20 +1952,20 @@ static int load_igen(SFData *sf, int size) } FSKIP(sf, SF_GEN_SIZE); - SLADVREM(z->gen, p3); + SLADVREM(zone->gen, gen_list); } - p2 = fluid_list_next(p2); /* next zone */ + zone_list = fluid_list_next(zone_list); /* next zone */ } if(discarded) { FLUID_LOG(FLUID_WARN, "Instrument '%s': Some invalid generators were discarded", - ((SFInst *)(p->data))->name); + inst->name); } - p = fluid_list_next(p); + inst_list = fluid_list_next(inst_list); } /* for those non-terminal record cases, grr! */ @@ -2109,8 +2017,9 @@ static int load_shdr(SFData *sf, unsigned int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + p->idx = i; - sf->sample = fluid_list_append(sf->sample, p); + sf->sample = fluid_list_prepend(sf->sample, p); READSTR(sf, &p->name); READD(sf, p->start); READD(sf, p->end); @@ -2121,7 +2030,6 @@ static int load_shdr(SFData *sf, unsigned int size) READB(sf, p->pitchadj); FSKIPW(sf); /* skip sample link */ READW(sf, p->sampletype); - p->samfile = 0; } FSKIP(sf, SF_SHDR_SIZE); /* skip terminal shdr */ @@ -2129,95 +2037,7 @@ static int load_shdr(SFData *sf, unsigned int size) return TRUE; } -/* "fixup" (inst # -> inst ptr) instrument references in preset list */ -static int fixup_pgen(SFData *sf) -{ - fluid_list_t *p, *p2, *p3; - SFZone *z; - int i; - - p = sf->preset; - - while(p) - { - p2 = ((SFPreset *)(p->data))->zone; - - while(p2) - { - /* traverse this preset's zones */ - z = (SFZone *)(p2->data); - - if((i = FLUID_POINTER_TO_INT(z->instsamp))) - { - /* load instrument # */ - p3 = fluid_list_nth(sf->inst, i - 1); - - if(!p3) - { - FLUID_LOG(FLUID_ERR, "Preset %03d %03d: Invalid instrument reference", - ((SFPreset *)(p->data))->bank, ((SFPreset *)(p->data))->prenum); - return FALSE; - } - - z->instsamp = p3; - } - else - { - z->instsamp = NULL; - } - - p2 = fluid_list_next(p2); - } - - p = fluid_list_next(p); - } - - return TRUE; -} - -/* "fixup" (sample # -> sample ptr) sample references in instrument list */ -static int fixup_igen(SFData *sf) -{ - fluid_list_t *p, *p2, *p3; - SFZone *z; - int i; - - p = sf->inst; - - while(p) - { - p2 = ((SFInst *)(p->data))->zone; - - while(p2) - { - /* traverse instrument's zones */ - z = (SFZone *)(p2->data); - - if((i = FLUID_POINTER_TO_INT(z->instsamp))) - { - /* load sample # */ - p3 = fluid_list_nth(sf->sample, i - 1); - - if(!p3) - { - FLUID_LOG(FLUID_ERR, "Instrument '%s': Invalid sample reference", - ((SFInst *)(p->data))->name); - return FALSE; - } - - z->instsamp = p3; - } - - p2 = fluid_list_next(p2); - } - - p = fluid_list_next(p); - } - - return TRUE; -} - -static void delete_preset(SFPreset *preset) +void delete_preset(SFPreset *preset) { fluid_list_t *entry; SFZone *zone; @@ -2241,7 +2061,7 @@ static void delete_preset(SFPreset *preset) FLUID_FREE(preset); } -static void delete_inst(SFInst *inst) +void delete_inst(SFInst *inst) { fluid_list_t *entry; SFZone *zone; @@ -2267,7 +2087,7 @@ static void delete_inst(SFInst *inst) /* Free all elements of a zone (Preset or Instrument) */ -static void delete_zone(SFZone *zone) +void delete_zone(SFZone *zone) { fluid_list_t *entry; @@ -2342,37 +2162,46 @@ static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist) /* check validity of instrument generator */ static int valid_inst_genid(unsigned short genid) { - int i = 0; + size_t i; - if(genid > Gen_MaxValid) + /* OVERRIDEROOTKEY is the last official generator, everything + * following it are generators internal to FluidSynth and will + * never appear in a SoundFont file. */ + if(genid > GEN_OVERRIDEROOTKEY) { return FALSE; } - while(invalid_inst_gen[i] && invalid_inst_gen[i] != genid) + for(i = 0; i < FLUID_N_ELEMENTS(invalid_inst_gen); i++) { - i++; + if (invalid_inst_gen[i] == genid) + { + return FALSE; + } } - return (invalid_inst_gen[i] == 0); + return TRUE; } /* check validity of preset generator */ static int valid_preset_genid(unsigned short genid) { - int i = 0; + size_t i; if(!valid_inst_genid(genid)) { return FALSE; } - while(invalid_preset_gen[i] && invalid_preset_gen[i] != genid) + for(i = 0; i < FLUID_N_ELEMENTS(invalid_preset_gen); i++) { - i++; + if (invalid_preset_gen[i] == genid) + { + return FALSE; + } } - return (invalid_preset_gen[i] == 0); + return TRUE; } @@ -2380,9 +2209,11 @@ static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int en { short *loaded_data = NULL; char *loaded_data24 = NULL; + unsigned int num_samples; - int num_samples = (end + 1) - start; - fluid_return_val_if_fail(num_samples > 0, -1); + fluid_return_val_if_fail((end + 1) > start , -1); + + num_samples = (end + 1) - start; if((start * sizeof(short) > sf->samplesize) || (end * sizeof(short) > sf->samplesize)) { @@ -2407,6 +2238,14 @@ static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int en if(sf->fcbs->fread(loaded_data, num_samples * sizeof(short), sf->sffd) == FLUID_FAILED) { +#if FLUID_VERSION_CHECK(FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO) < FLUID_VERSION_CHECK(2,2,0) + if((int)(num_samples * sizeof(short)) < 0) + { + FLUID_LOG(FLUID_INFO, + "This SoundFont seems to be bigger than 2GB, which is not supported in this version of fluidsynth. " + "You need to use at least fluidsynth 2.2.0"); + } +#endif FLUID_LOG(FLUID_ERR, "Failed to read sample data"); goto error_exit; } @@ -2414,7 +2253,7 @@ static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int en /* If this machine is big endian, byte swap the 16 bit samples */ if(FLUID_IS_BIG_ENDIAN) { - int i; + unsigned int i; for(i = 0; i < num_samples; i++) { @@ -2476,7 +2315,7 @@ error_exit: /* Ogg Vorbis loading and decompression */ #if LIBSNDFILE_SUPPORT -/* Virtual file access rountines to allow loading individually compressed +/* Virtual file access routines to allow loading individually compressed * samples from the Soundfont sample data chunk using the file callbacks * passing in during opening of the file */ typedef struct _sfvio_data_t @@ -2519,10 +2358,14 @@ static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data) goto fail; /* proper error handling not possible?? */ } - if(sf->fcbs->fseek(sf->sffd, sf->samplepos + data->start + new_offset, SEEK_SET) != FLUID_FAILED) + new_offset += data->start; + fluid_rec_mutex_lock(sf->mtx); + if (data->start <= new_offset && new_offset <= data->end && + sf->fcbs->fseek(sf->sffd, new_offset, SEEK_SET) != FLUID_FAILED) { - data->offset = new_offset; + data->offset = new_offset - data->start; } + fluid_rec_mutex_unlock(sf->mtx); fail: return data->offset; @@ -2546,11 +2389,21 @@ static sf_count_t sfvio_read(void *ptr, sf_count_t count, void *user_data) return count; } - if(sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED) + fluid_rec_mutex_lock(sf->mtx); + if (sf->fcbs->fseek(sf->sffd, data->start + data->offset, SEEK_SET) == FLUID_FAILED) { - FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data"); - return 0; + FLUID_LOG(FLUID_ERR, "This should never happen: fseek failed in sfvoid_read()"); + count = 0; } + else + { + if (sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data"); + count = 0; + } + } + fluid_rec_mutex_unlock(sf->mtx); data->offset += count; @@ -2597,25 +2450,26 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne // Initialize file position indicator and SF_INFO structure sfdata.sffile = sf; - sfdata.start = start_byte; - sfdata.end = end_byte; - sfdata.offset = 0; + sfdata.start = sf->samplepos + start_byte; + sfdata.end = sf->samplepos + end_byte; + sfdata.offset = -1; - FLUID_MEMSET(&sfinfo, 0, sizeof(sfinfo)); - - /* Seek to beginning of Ogg Vorbis data in Soundfont */ - if(sf->fcbs->fseek(sf->sffd, sf->samplepos + start_byte, SEEK_SET) == FLUID_FAILED) + /* Seek to sfdata.start, the beginning of Ogg Vorbis data in Soundfont */ + sfvio_seek(0, SEEK_SET, &sfdata); + if (sfdata.offset != 0) { - FLUID_LOG(FLUID_ERR, "Failed to seek to compressd sample position"); + FLUID_LOG(FLUID_ERR, "Failed to seek to compressed sample position"); return -1; } + FLUID_MEMSET(&sfinfo, 0, sizeof(sfinfo)); + // Open sample as a virtual file sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata); if(!sndfile) { - FLUID_LOG(FLUID_ERR, "%s", sf_strerror(sndfile)); + FLUID_LOG(FLUID_ERR, "sf_open_virtual(): %s", sf_strerror(sndfile)); return -1; } @@ -2635,6 +2489,11 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne goto error_exit; } + if((sfinfo.format & SF_FORMAT_OGG) == 0) + { + FLUID_LOG(FLUID_WARN, "OGG sample is not OGG compressed, this is not officially supported"); + } + wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels); if(!wav_data) @@ -2647,7 +2506,7 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames) { FLUID_LOG(FLUID_DBG, "Decompression failed!"); - FLUID_LOG(FLUID_ERR, "%s", sf_strerror(sndfile)); + FLUID_LOG(FLUID_ERR, "sf_readf_short(): %s", sf_strerror(sndfile)); goto error_exit; } diff --git a/libs/fluidsynth/src/fluid_sffile.h b/libs/fluidsynth/src/fluid_sffile.h index 0c315c73b6..5275c6252b 100644 --- a/libs/fluidsynth/src/fluid_sffile.h +++ b/libs/fluidsynth/src/fluid_sffile.h @@ -29,7 +29,7 @@ #include "fluid_list.h" #include "fluid_mod.h" #include "fluidsynth.h" -#include "fluidsynth_priv.h" +#include "fluid_sys.h" /* Sound Font structure defines */ @@ -45,10 +45,6 @@ typedef struct _SFInst SFInst; typedef struct _SFPreset SFPreset; typedef struct _SFData SFData; typedef struct _SFChunk SFChunk; -typedef struct _SFPhdr SFPhdr; -typedef struct _SFBag SFBag; -typedef struct _SFIhdr SFIhdr; -typedef struct _SFShdr SFShdr; struct _SFVersion @@ -89,7 +85,6 @@ struct _SFGen struct _SFZone { /* Sample/instrument zone structure */ - fluid_list_t *instsamp; /* instrument/sample pointer for zone */ fluid_list_t *gen; /* list of generators */ fluid_list_t *mod; /* list of modulators */ }; @@ -98,7 +93,7 @@ struct _SFSample { /* Sample structure */ char name[21]; /* Name of sample */ - unsigned char samfile; /* Loaded sfont/sample buffer = 0/1 */ + int idx; /* Index of this instrument in the Soundfont */ unsigned int start; /* Offset in sample area to start of sample */ unsigned int end; /* Offset from start to end of sample, this is the last point of the @@ -131,9 +126,6 @@ struct _SFPreset char name[21]; /* preset name */ unsigned short prenum; /* preset number */ unsigned short bank; /* bank number */ - unsigned int libr; /* Not used (preserved) */ - unsigned int genre; /* Not used (preserved) */ - unsigned int morph; /* Not used (preserved) */ fluid_list_t *zone; /* list of preset zones */ }; @@ -160,6 +152,8 @@ struct _SFData FILE *sffd; /* loaded sfont file descriptor */ const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */ + fluid_rec_mutex_t mtx; /* this mutex can be used to synchronize calls to fcbs when using multiple threads (e.g. SF3 loading) */ + fluid_list_t *info; /* linked list of info strings (1st byte is ID) */ fluid_list_t *preset; /* linked list of preset info */ fluid_list_t *inst; /* linked list of instrument info */ @@ -182,44 +176,6 @@ struct _SFChunk unsigned int size; /* size of the following chunk */ }; -struct _SFPhdr -{ - unsigned char name[20]; /* preset name */ - unsigned short preset; /* preset number */ - unsigned short bank; /* bank number */ - unsigned short pbagndx; /* index into preset bag */ - unsigned int library; /* just for preserving them */ - unsigned int genre; /* Not used */ - unsigned int morphology; /* Not used */ -}; - -struct _SFBag -{ - unsigned short genndx; /* index into generator list */ - unsigned short modndx; /* index into modulator list */ -}; - -struct _SFIhdr -{ - char name[20]; /* Name of instrument */ - unsigned short ibagndx; /* Instrument bag index */ -}; - -struct _SFShdr -{ - /* Sample header loading struct */ - char name[20]; /* Sample name */ - unsigned int start; /* Offset to start of sample */ - unsigned int end; /* Offset to end of sample */ - unsigned int loopstart; /* Offset to start of loop */ - unsigned int loopend; /* Offset to end of loop */ - unsigned int samplerate; /* Sample rate recorded at */ - unsigned char origpitch; /* root midi key number */ - signed char pitchadj; /* pitch correction in cents */ - unsigned short samplelink; /* Not used */ - unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ -}; - /* Public functions */ SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs); void fluid_sffile_close(SFData *sf); @@ -227,4 +183,12 @@ int fluid_sffile_parse_presets(SFData *sf); int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, short **data, char **data24); + +/* extern only for unit test purposes */ +int load_igen(SFData *sf, int size); +int load_pgen(SFData *sf, int size); +void delete_preset(SFPreset *preset); +void delete_inst(SFInst *inst); +void delete_zone(SFZone *zone); + #endif /* _FLUID_SFFILE_H */ diff --git a/libs/fluidsynth/src/fluid_sfont.c b/libs/fluidsynth/src/fluid_sfont.c index 35c0f36356..94844f84bf 100644 --- a/libs/fluidsynth/src/fluid_sfont.c +++ b/libs/fluidsynth/src/fluid_sfont.c @@ -22,7 +22,7 @@ #include "fluid_sys.h" -void *default_fopen(const char *path) +static void *default_fopen(const char *path) { const char* msg; FILE* handle = fluid_file_open(path, &msg); @@ -35,23 +35,23 @@ void *default_fopen(const char *path) return handle; } -int default_fclose(void *handle) +static int default_fclose(void *handle) { return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED; } -long default_ftell(void *handle) +static fluid_long_long_t default_ftell(void *handle) { return FLUID_FTELL((FILE *)handle); } -int safe_fread(void *buf, int count, void *fd) +static int safe_fread(void *buf, fluid_long_long_t count, void *fd) { - if(FLUID_FREAD(buf, count, 1, (FILE *)fd) != 1) + if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1) { if(feof((FILE *)fd)) { - FLUID_LOG(FLUID_ERR, "EOF while attemping to read %d bytes", count); + FLUID_LOG(FLUID_ERR, "EOF while attempting to read %lld bytes", count); } else { @@ -64,11 +64,11 @@ int safe_fread(void *buf, int count, void *fd) return FLUID_OK; } -int safe_fseek(void *fd, long ofs, int whence) +static int safe_fseek(void *fd, fluid_long_long_t 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); + FLUID_LOG(FLUID_ERR, "File seek failed with offset = %lld and whence = %d", ofs, whence); return FLUID_FAILED; } @@ -156,8 +156,6 @@ void *fluid_sfloader_get_data(fluid_sfloader_t *loader) /** * 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. @@ -165,6 +163,9 @@ void *fluid_sfloader_get_data(fluid_sfloader_t *loader) * @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. + * + * Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example. + * */ int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, fluid_sfloader_callback_open_t open, @@ -196,6 +197,7 @@ int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, /** * 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 iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed. @@ -285,8 +287,8 @@ const char *fluid_sfont_get_name(fluid_sfont_t *sfont) } /** - * Retrieve the preset assigned the a SoundFont instance - * for the given bank and preset number. + * Retrieve the preset assigned the a SoundFont instance for the given bank and preset number. + * * @param sfont The SoundFont instance. * @param bank bank number of the preset * @param prenum program number of the preset @@ -300,6 +302,7 @@ fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenu /** * Starts / re-starts virtual preset iteration in a SoundFont. + * * @param sfont Virtual SoundFont instance */ void fluid_sfont_iteration_start(fluid_sfont_t *sfont) @@ -329,10 +332,11 @@ fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont) /** * 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. + * + * Implements #fluid_sfont_free_t. + * */ int delete_fluid_sfont(fluid_sfont_t *sfont) { @@ -467,9 +471,10 @@ fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset) /** * Destroys a SoundFont preset instance created with new_fluid_preset(). * + * @param preset The SoundFont preset instance to destroy. + * * Implements #fluid_preset_free_t. * - * @param preset The SoundFont preset instance to destroy. */ void delete_fluid_preset(fluid_preset_t *preset) { @@ -480,6 +485,7 @@ void delete_fluid_preset(fluid_preset_t *preset) /** * Create a new sample instance. + * * @return The sample on success, NULL otherwise. */ fluid_sample_t * @@ -502,6 +508,7 @@ new_fluid_sample() /** * Destroy a sample instance previously created with new_fluid_sample(). + * * @param sample The sample to destroy. */ void @@ -521,10 +528,10 @@ delete_fluid_sample(fluid_sample_t *sample) /** * Returns the size of the fluid_sample_t structure. * - * Useful in low latency scenarios e.g. to allocate a pool of samples. - * * @return Size of fluid_sample_t in bytes * + * Useful in low latency scenarios e.g. to allocate a pool of samples. + * * @note It is recommend to zero initialize the memory before using the object. * * @warning Do NOT allocate samples on the stack and assign them to a voice! @@ -536,6 +543,7 @@ size_t fluid_sample_sizeof() /** * 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 @@ -551,6 +559,7 @@ int fluid_sample_set_name(fluid_sample_t *sample, const char *name) /** * 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 @@ -700,6 +709,9 @@ int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune) */ int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size) { +#define EXCLUSIVE_FLAGS (FLUID_SAMPLETYPE_MONO | FLUID_SAMPLETYPE_RIGHT | FLUID_SAMPLETYPE_LEFT) + static const unsigned int supported_flags = EXCLUSIVE_FLAGS | FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_OGG_VORBIS | FLUID_SAMPLETYPE_ROM; + /* ROM samples are unusable for us by definition */ if(sample->sampletype & FLUID_SAMPLETYPE_ROM) { @@ -707,6 +719,28 @@ int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size) return FLUID_FAILED; } + if(sample->sampletype & ~supported_flags) + { + FLUID_LOG(FLUID_WARN, "Sample '%s' has unknown flags, possibly using an unsupported compression; sample ignored", sample->name); + return FLUID_FAILED; + } + + if((sample->sampletype & EXCLUSIVE_FLAGS) & ((sample->sampletype & EXCLUSIVE_FLAGS) - 1)) + { + FLUID_LOG(FLUID_INFO, "Sample '%s' should be either mono or left or right; using it anyway", sample->name); + } + + if((sample->sampletype & FLUID_SAMPLETYPE_LINKED) && (sample->sampletype & EXCLUSIVE_FLAGS)) + { + FLUID_LOG(FLUID_INFO, "Linked sample '%s' should not be mono, left or right at the same time; using it anyway", sample->name); + } + + if((sample->sampletype & EXCLUSIVE_FLAGS) == 0) + { + FLUID_LOG(FLUID_INFO, "Sample '%s' has no flags set, assuming mono", sample->name); + sample->sampletype = FLUID_SAMPLETYPE_MONO; + } + /* Ogg vorbis compressed samples in the SF3 format use byte indices for * sample start and end pointers before decompression. Standard SF2 samples * use sample word indices for all pointers, so use half the buffer_size @@ -729,6 +763,7 @@ int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size) } return FLUID_OK; +#undef EXCLUSIVE_FLAGS } /* Check the sample loop pointers and optionally convert them to something diff --git a/libs/fluidsynth/src/fluid_sfont.h b/libs/fluidsynth/src/fluid_sfont.h index 28609e96aa..9a42c02eb1 100644 --- a/libs/fluidsynth/src/fluid_sfont.h +++ b/libs/fluidsynth/src/fluid_sfont.h @@ -45,7 +45,7 @@ int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end); (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel) #define fluid_preset_notify(_preset,_reason,_chan) \ - { if ((_preset) && (_preset)->notify) { (*(_preset)->notify)(_preset,_reason,_chan); }} + ( ((_preset) && (_preset)->notify) ? (*(_preset)->notify)(_preset,_reason,_chan) : FLUID_OK ) #define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; } @@ -147,7 +147,7 @@ struct _fluid_sample_t { char name[21]; /**< Sample name */ - /* The following for sample pointers store the original pointers from the Soundfont + /* The following four sample pointers store the original pointers from the Soundfont * file. They are never changed after loading and are used to re-create the * actual sample pointers after a sample has been unloaded and loaded again. The * actual sample pointers get modified during loading for SF3 (compressed) samples diff --git a/libs/fluidsynth/src/fluid_synth.c b/libs/fluidsynth/src/fluid_synth.c index 382979f7f5..27e7022c5b 100644 --- a/libs/fluidsynth/src/fluid_synth.c +++ b/libs/fluidsynth/src/fluid_synth.c @@ -65,6 +65,10 @@ static int fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len, char *response, int *response_len, int avail_response, int *handled, int dryrun); +static int fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); 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); @@ -77,6 +81,11 @@ static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan); static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan); static int fluid_synth_set_preset(fluid_synth_t *synth, int chan, fluid_preset_t *preset); +static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value); +static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value); + static fluid_preset_t * fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, int banknum, int prognum); @@ -277,7 +286,7 @@ fluid_synth_init(void) { #ifdef TRAP_ON_FPE /* Turn on floating point exception traps */ - feenableexcept(FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID); + feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID); #endif init_dither(); @@ -384,7 +393,7 @@ fluid_synth_init(void) ); fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */ fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */ - /* Amount: 500. The SF specs $8.4.6, p. 55 syas: "Amount = 1000 + /* Amount: 500. The SF specs $8.4.6, p. 55 says: "Amount = 1000 tenths of a percent". The center value (64) corresponds to 50%, so it follows that amount = 50% x 1000/% = 500. */ fluid_mod_set_amount(&default_pan_mod, 500.0); @@ -462,10 +471,13 @@ fluid_synth_init(void) fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */ /* Amount: 96 dB of attenuation (on the opposite channel) */ fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ - -#ifdef LIBINSTPATCH_SUPPORT + +#if defined(LIBINSTPATCH_SUPPORT) /* defer libinstpatch init to fluid_instpatch.c to avoid #include "libinstpatch.h" */ - fluid_instpatch_init(); + if(!fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_init(); + } #endif } @@ -531,7 +543,6 @@ fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_c } fluid_sample_timer_reset(synth, result); - result->isfinished = 0; result->data = data; result->callback = callback; result->next = synth->sample_timers; @@ -563,6 +574,7 @@ void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer) { timer->starttick = fluid_synth_get_ticks(synth); + timer->isfinished = 0; } /*************************************************************** @@ -593,10 +605,15 @@ static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_syn * @param settings Configuration parameters to use (used directly). * @return New FluidSynth instance or NULL on error * - * @note The @p settings parameter is used directly and should freed after - * the synth has been deleted. Further note that you may modify FluidSettings of the + * @note The @p settings parameter is used directly, but the synth does not take ownership of it. + * Hence, the caller is responsible for freeing it, when no longer needed. + * Further note that you may modify FluidSettings of the * @p settings instance. However, only those FluidSettings marked as 'realtime' will - * affect the synth immediately. + * affect the synth immediately. See the \ref fluidsettings for more details. + * + * @warning The @p settings object should only be used by a single synth at a time. I.e. creating + * multiple synth instances with a single @p settings object causes undefined behavior. Once the + * "single synth" has been deleted, you may use the @p settings object again for another synth. */ fluid_synth_t * new_fluid_synth(fluid_settings_t *settings) @@ -604,8 +621,9 @@ new_fluid_synth(fluid_settings_t *settings) fluid_synth_t *synth; fluid_sfloader_t *loader; char *important_channels; - int i, nbuf, prio_level = 0; + int i, prio_level = 0; int with_ladspa = 0; + double sample_rate_min, sample_rate_max; /* initialize all the conversion tables and other stuff */ if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1)) @@ -624,6 +642,13 @@ new_fluid_synth(fluid_settings_t *settings) FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); +#if defined(LIBINSTPATCH_SUPPORT) + if(fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_init(); + } +#endif + fluid_rec_mutex_init(synth->mutex); fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex); synth->public_api_count = 0; @@ -636,6 +661,7 @@ new_fluid_synth(fluid_settings_t *settings) fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony); fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); + fluid_settings_getnum_range(settings, "synth.sample-rate", &sample_rate_min, &sample_rate_max); fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels); fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels); fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups); @@ -738,14 +764,25 @@ new_fluid_synth(fluid_settings_t *settings) synth->effects_channels = 2; } - /* The number of buffers is determined by the higher number of nr - * groups / nr audio channels. If LADSPA is unused, they should be - * the same. */ - nbuf = synth->audio_channels; + /* + number of buffers rendered by the mixer is determined by synth->audio_groups. + audio from MIDI channel is rendered, mapped and mixed in these buffers. - if(synth->audio_groups > nbuf) + Typically synth->audio_channels is only used by audio driver and should be set + to the same value that synth->audio_groups. In some situation using LADSPA, + it is best to diminish audio-channels so that the driver will be able to pass + the audio to audio devices in the case these devices have a limited number of + audio channels. + + audio-channels must not be greater then audio-groups, otherwise these + audio output above audio-groups will not be rendered by the mixeur. + */ + if(synth->audio_channels > synth->audio_groups) { - nbuf = synth->audio_groups; + synth->audio_channels = synth->audio_groups; + fluid_settings_setint(settings, "synth.audio-channels", synth->audio_channels); + FLUID_LOG(FLUID_WARN, "Requested audio-channels to high. " + "Limiting this setting to audio-groups."); } if(fluid_settings_dupstr(settings, "synth.overflow.important-channels", @@ -777,7 +814,10 @@ new_fluid_synth(fluid_settings_t *settings) /* Allocate event queue for rvoice mixer */ /* In an overflow situation, a new voice takes about 50 spaces in the queue! */ synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64, - synth->polyphony, nbuf, synth->effects_channels, synth->effects_groups, synth->sample_rate, synth->cores - 1, prio_level); + synth->polyphony, synth->audio_groups, + synth->effects_channels, synth->effects_groups, + (fluid_real_t)sample_rate_max, synth->sample_rate, + synth->cores - 1, prio_level); if(synth->eventhandler == NULL) { @@ -899,44 +939,35 @@ new_fluid_synth(fluid_settings_t *settings) fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, synth->polyphony, 0.0f); - fluid_synth_set_reverb_on(synth, synth->with_reverb); - fluid_synth_set_chorus_on(synth, synth->with_chorus); + fluid_synth_reverb_on(synth, -1, synth->with_reverb); + fluid_synth_chorus_on(synth, -1, synth->with_chorus); synth->cur = FLUID_BUFSIZE; synth->curmax = 0; synth->dither_index = 0; { - double room, damp, width, level; + double values[FLUID_REVERB_PARAM_LAST]; - fluid_settings_getnum(settings, "synth.reverb.room-size", &room); - fluid_settings_getnum(settings, "synth.reverb.damp", &damp); - fluid_settings_getnum(settings, "synth.reverb.width", &width); - fluid_settings_getnum(settings, "synth.reverb.level", &level); + fluid_settings_getnum(settings, "synth.reverb.room-size", &values[FLUID_REVERB_ROOMSIZE]); + fluid_settings_getnum(settings, "synth.reverb.damp", &values[FLUID_REVERB_DAMP]); + fluid_settings_getnum(settings, "synth.reverb.width", &values[FLUID_REVERB_WIDTH]); + fluid_settings_getnum(settings, "synth.reverb.level", &values[FLUID_REVERB_LEVEL]); - fluid_synth_set_reverb_full(synth, - FLUID_REVMODEL_SET_ALL, - room, - damp, - width, - level); + fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); } { - double level, speed, depth; + double values[FLUID_CHORUS_PARAM_LAST]; fluid_settings_getint(settings, "synth.chorus.nr", &i); - fluid_settings_getnum(settings, "synth.chorus.level", &level); - fluid_settings_getnum(settings, "synth.chorus.speed", &speed); - fluid_settings_getnum(settings, "synth.chorus.depth", &depth); + values[FLUID_CHORUS_NR] = (double)i; + fluid_settings_getnum(settings, "synth.chorus.level", &values[FLUID_CHORUS_LEVEL]); + fluid_settings_getnum(settings, "synth.chorus.speed", &values[FLUID_CHORUS_SPEED]); + fluid_settings_getnum(settings, "synth.chorus.depth", &values[FLUID_CHORUS_DEPTH]); + values[FLUID_CHORUS_TYPE] = (double)FLUID_CHORUS_DEFAULT_TYPE; - fluid_synth_set_chorus_full(synth, - FLUID_CHORUS_SET_ALL, - i, - level, - speed, - depth, - FLUID_CHORUS_DEFAULT_TYPE); + fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); } @@ -991,6 +1022,50 @@ delete_fluid_synth(fluid_synth_t *synth) fluid_profiling_print(); + /* unregister all real-time settings callback, to avoid a use-after-free when changing those settings after + * this synth has been deleted*/ + + fluid_settings_callback_num(synth->settings, "synth.gain", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.polyphony", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.device-id", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.percussion", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.sustained", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.released", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.age", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.volume", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.important", + NULL, NULL); + fluid_settings_callback_str(synth->settings, "synth.overflow.important-channels", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.room-size", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.damp", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.width", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.level", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.reverb.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.nr", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.level", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.depth", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.speed", + NULL, NULL); + /* turn off all voices, needed to unload SoundFont data */ if(synth->voice != NULL) { @@ -1003,6 +1078,12 @@ delete_fluid_synth(fluid_synth_t *synth) continue; } + /* WARNING: A this point we must ensure that the reference counter + of any soundfont sample owned by any rvoice belonging to the voice + are correctly decremented. This is the contrary part to + to fluid_voice_init() where the sample's reference counter is + incremented. + */ fluid_voice_unlock_rvoice(voice); fluid_voice_overflow_rvoice_finished(voice); @@ -1055,6 +1136,18 @@ delete_fluid_synth(fluid_synth_t *synth) delete_fluid_list(synth->loaders); + /* wait for and delete all the lazy sfont unloading timers */ + + for(list = synth->fonts_to_be_unloaded; list; list = fluid_list_next(list)) + { + fluid_timer_t* timer = fluid_list_get(list); + // explicitly join to wait for the unload really to happen + fluid_timer_join(timer); + // delete_fluid_timer alone would stop the timer, even if it had not unloaded the soundfont yet + delete_fluid_timer(timer); + } + + delete_fluid_list(synth->fonts_to_be_unloaded); if(synth->channel != NULL) { @@ -1111,6 +1204,13 @@ delete_fluid_synth(fluid_synth_t *synth) fluid_rec_mutex_destroy(synth->mutex); FLUID_FREE(synth); + +#if defined(LIBINSTPATCH_SUPPORT) + if(fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_deinit(); + } +#endif } /** @@ -1868,6 +1968,7 @@ fluid_synth_handle_device_id(void *data, const char *name, int value) * Non-realtime: 0xF0 0x7E [BODY] 0xF7 * Realtime: 0xF0 0x7F [BODY] 0xF7 * Tuning messages: 0xF0 0x7E/0x7F 0x08 [BODY] 0xF7 + * GS DT1 messages: 0xF0 0x41 0x42 0x12 [ADDRESS (3 bytes)] [DATA] 0xF7 */ int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, @@ -1910,6 +2011,21 @@ fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, FLUID_API_RETURN(result); } + /* GS DT1 message */ + if((synth->bank_select == FLUID_BANK_STYLE_GS) + && data[0] == MIDI_SYSEX_MANUF_ROLAND + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_GS_ID + && data[3] == MIDI_SYSEX_GS_DT1) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_gs_dt1(synth, data, len, response, + response_len, avail_response, + handled, dryrun); + FLUID_API_RETURN(result); + } + return FLUID_OK; } @@ -2205,8 +2321,63 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len, return FLUID_OK; } +/* Handler for GS DT1 messages */ +static int +fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int addr; + int len_data; + int checksum = 0, i; + + if(len < 9) // at least one byte of data should be transmitted + { + return FLUID_FAILED; + } + len_data = len - 8; + addr = (data[4] << 16) | (data[5] << 8) | data[6]; + + for (i = 4; i < len - 1; ++i) + { + checksum += data[i]; + } + if (0x80 - (checksum & 0x7F) != data[len - 1]) + { + return FLUID_FAILED; + } + + if ((addr & 0xFFF0FF) == 0x401015) // Use for rhythm part + { + if (len_data > 1 || data[7] > 0x02) + { + return FLUID_FAILED; + } + if (handled) + { + *handled = TRUE; + } + if (!dryrun) + { + int chan = (addr >> 8) & 0x0F; + //See the Patch Part parameters section in SC-88Pro/8850 owner's manual + chan = chan >= 0x0a ? chan : (chan == 0 ? 9 : chan - 1); + synth->channel[chan]->channel_type = + data[7] == 0x00 ? CHANNEL_TYPE_MELODIC : CHANNEL_TYPE_DRUM; + + //Roland synths seem to "remember" the last instrument a channel + //used in the selected mode. This behavior is not replicated here. + fluid_synth_program_change(synth, chan, 0); + } + return FLUID_OK; + } + + //silently ignore + return FLUID_OK; +} + /** - * Turn off all notes on a MIDI channel (put them into release phase). + * Turn off all voices that are playing on the given MIDI channel, by putting them into release phase. * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) * @return #FLUID_OK on success, #FLUID_FAILED otherwise @@ -2256,7 +2427,7 @@ fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan) } /** - * Immediately stop all notes on a MIDI channel (skips release phase). + * Immediately stop all voices on the given MIDI channel (skips release phase). * @param synth FluidSynth instance * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) * @return #FLUID_OK on success, #FLUID_FAILED otherwise @@ -2741,9 +2912,6 @@ fluid_synth_find_preset(fluid_synth_t *synth, int banknum, * @param prognum MIDI program number (0-127) * @return #FLUID_OK on success, #FLUID_FAILED otherwise */ -/* FIXME - Currently not real-time safe, due to preset allocation and mutex lock, - * and may be called from within synthesis context. */ - /* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented * since fluid_synth_unset_program() should be used instead. */ int @@ -2987,6 +3155,101 @@ fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, FLUID_API_RETURN(result); } +/** + * Pins all samples of the given preset. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK if the preset was found, pinned and loaded + * into memory successfully. #FLUID_FAILED otherwise. Note that #FLUID_OK + * is returned, even if synth.dynamic-sample-loading is disabled or + * the preset doesn't support dynamic-sample-loading. + * + * This function will attempt to pin all samples of the given preset and + * load them into memory, if they are currently unloaded. "To pin" in this + * context means preventing them from being unloaded by an upcoming channel + * prog change. + * + * @note This function is only useful if \ref settings_synth_dynamic-sample-loading is enabled. + * By default, dynamic-sample-loading is disabled and all samples are kept in memory. + * Furthermore, this is only useful for presets which support dynamic-sample-loading (currently, + * only preset loaded with the default soundfont loader do). + * + * @since 2.2.0 + */ +int +fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) +{ + int ret; + fluid_preset_t *preset; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + 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); + FLUID_API_RETURN(FLUID_FAILED); + } + + ret = fluid_preset_notify(preset, FLUID_PRESET_PIN, -1); // channel unused for pinning messages + + FLUID_API_RETURN(ret); +} + +/** + * Unpin all samples of the given preset. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK if preset was found, #FLUID_FAILED otherwise + * + * This function undoes the effect of fluid_synth_pin_preset(). If the preset is + * not currently used, its samples will be unloaded. + * + * @note Only useful for presets loaded with the default soundfont loader and + * only if \ref settings_synth_dynamic-sample-loading is enabled. + * + * @since 2.2.0 + */ +int +fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) +{ + int ret; + fluid_preset_t *preset; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + 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); + FLUID_API_RETURN(FLUID_FAILED); + } + + ret = fluid_preset_notify(preset, FLUID_PRESET_UNPIN, -1); // channel unused for pinning messages + + FLUID_API_RETURN(ret); +} + /** * Select an instrument on a MIDI channel by SoundFont name, bank and program numbers. * @param synth FluidSynth instance @@ -3054,6 +3317,21 @@ fluid_synth_update_presets(fluid_synth_t *synth) } } +static void +fluid_synth_set_sample_rate_LOCAL(fluid_synth_t *synth, float sample_rate) +{ + int i; + fluid_clip(sample_rate, 8000.0f, 96000.0f); + synth->sample_rate = sample_rate; + + synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); + + for(i = 0; i < synth->polyphony; i++) + { + fluid_voice_set_output_rate(synth->voice[i], sample_rate); + } +} + /** * Set up an event to change the sample-rate of the synth during the next rendering call. * @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth. @@ -3084,21 +3362,31 @@ fluid_synth_update_presets(fluid_synth_t *synth) void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate) { - int i; fluid_return_if_fail(synth != NULL); fluid_synth_api_enter(synth); - fluid_clip(sample_rate, 8000.0f, 96000.0f); - synth->sample_rate = sample_rate; - synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); - - for(i = 0; i < synth->polyphony; i++) - { - fluid_voice_set_output_rate(synth->voice[i], sample_rate); - } + fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate, - 0, sample_rate); + 0, synth->sample_rate); + fluid_synth_api_exit(synth); +} + +// internal sample rate change function for the jack driver +// executes immediately, therefore, make sure no rendering call is running! +void +fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); + + param[0].i = 0; + param[1].real = synth->sample_rate; + fluid_rvoice_mixer_set_samplerate(synth->eventhandler->mixer, param); + fluid_synth_api_exit(synth); } @@ -3335,7 +3623,8 @@ fluid_synth_program_reset(fluid_synth_t *synth) } /** - * Synthesize a block of floating point audio to separate audio buffers (multichannel rendering). First effect channel used by reverb, second for chorus. + * Synthesize a block of floating point audio to separate audio buffers (multi-channel rendering). + * * @param synth FluidSynth instance * @param len Count of audio frames to synthesize * @param left Array of float buffers to store left channel of planar audio (as many as \c synth.audio-channels buffers, each of \c len in size) @@ -3344,9 +3633,13 @@ fluid_synth_program_reset(fluid_synth_t *synth) * @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito) * @return #FLUID_OK on success, #FLUID_FAILED otherwise * + * First effect channel used by reverb, second for chorus. + * * @note Should only be called from synthesis thread. * - * @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release. It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more powerful and flexible fluid_synth_process(). + * @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release. + * It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more + * powerful and flexible fluid_synth_process(). * * Usage example: * @code{.cpp} @@ -3358,14 +3651,17 @@ fluid_synth_program_reset(fluid_synth_t *synth) // we need twice as many (mono-)buffers channels *= 2; - // fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: each midi channel gets rendered to its own stereo buffer, rather than having one buffer and interleaved PCM + // fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: + // each midi channel gets rendered to its own stereo buffer, rather than having + // one buffer and interleaved PCM float** mix_buf = new float*[channels]; for(int i = 0; i < channels; i++) { mix_buf[i] = new float[FramesToRender]; } - // retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) and chrous (second chan)) + // retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) + // and chrous (second chan)) fluid_settings_getint(settings, "synth.effects-channels", &channels); channels *= 2; @@ -3583,7 +3879,34 @@ static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out } /** - * @brief Synthesize floating point audio to stereo audio channels (implements the default interface #fluid_audio_func_t). + * Synthesize floating point audio to stereo audio channels + * (implements the default interface #fluid_audio_func_t). + * + * @param synth FluidSynth instance + * + * @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx. + * Zero value is permitted, the function does nothing and return FLUID_OK. + * + * @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo) + * and in the range 0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). + * Note that zero value is valid and allows to skip mixing effects in all fx output buffers. + * + * @param fx Array of buffers to store effects audio to. Buffers may + * alias with buffers of \c out. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * + * @param nout Count of arrays in \c out. Must be a multiple of 2 + * (because of stereo) and in the range 0 <= nout/2 <= fluid_synth_count_audio_channels(). + * Note that zero value is valid and allows to skip mixing dry audio in all out output buffers. + * + * @param out Array of buffers to store (dry) audio to. Buffers may + * alias with buffers of \c fx. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * + * @return #FLUID_OK on success, + * #FLUID_FAILED otherwise, + * - fx == NULL while nfx > 0, or out == NULL while nout > 0. + * - \c nfx or \c nout not multiple of 2. + * - len < 0. + * - \c nfx or \c nout exceed the range explained above. * * Synthesize and mix audio to a given number of planar audio buffers. * Therefore pass nout = N*2 float buffers to \p out in order to render @@ -3623,7 +3946,7 @@ fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_bu * 0 <= j < fluid_synth_count_effects_channels() is a zero-based index denoting the effect channel within * unit \p k. * - * Any voice playing is assigned to audio channels based on the MIDI channel its playing on. Let \p chan be the + * Any playing voice is assigned to audio channels based on the MIDI channel it's playing on: Let \p chan be the * zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is * going to be rendered to use: * @@ -3631,18 +3954,6 @@ fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_bu * * k = chan % fluid_synth_count_effects_groups() * - * @param synth FluidSynth instance - * @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx. - * @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo) - * and in the range 0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). - * @param fx Array of buffers to store effects audio to. Buffers may -alias with buffers of \c out. NULL buffers are permitted and will cause to skip mixing any audio into that buffer. - * @param nout Count of arrays in \c out. Must be a multiple of 2 -(because of stereo) and in the range 0 <= nout/2 <= fluid_synth_count_audio_channels(). - * @param out Array of buffers to store (dry) audio to. Buffers may -alias with buffers of \c fx. NULL buffers are permitted and will cause to skip mixing any audio into that buffer. - * @return #FLUID_OK on success, #FLUID_FAILED otherwise. - * * @parblock * @note The owner of the sample buffers must zero them out before calling this * function, because any synthesized audio is mixed (i.e. added) to the buffers. @@ -3671,6 +3982,7 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], return fluid_synth_process_LOCAL(synth, len, nfx, fx, nout, out, fluid_synth_render_blocks); } +/* declared public (instead of static) for testing purpose */ int fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)) @@ -3685,8 +3997,23 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], float cpu_load; fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + /* fx NULL while nfx > 0 is invalid */ + fluid_return_val_if_fail((fx != NULL) || (nfx == 0), FLUID_FAILED); + /* nfx must be multiple of 2. Note that 0 value is valid and + allows to skip mixing in fx output buffers + */ fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED); + + /* out NULL while nout > 0 is invalid */ + fluid_return_val_if_fail((out != NULL) || (nout == 0), FLUID_FAILED); + /* nout must be multiple of 2. Note that 0 value is valid and + allows to skip mixing in out output buffers + */ fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED); + + /* check len value. Note that 0 value is valid, the function does nothing and returns FLUID_OK. + */ fluid_return_val_if_fail(len >= 0, FLUID_FAILED); fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below @@ -3697,33 +4024,49 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED); fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED); + /* get internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + /* get internal mixer audio effect buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + /* Conversely to fluid_synth_write_float(),fluid_synth_write_s16() (which handle only one + stereo output) we don't want rendered audio effect mixed in internal audio dry buffers. + FALSE instructs the mixer that internal audio effects will be mixed in respective internal + audio effects buffers. + */ fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE); /* First, take what's still available in the buffer */ count = 0; + /* synth->cur indicates if available samples are still in internal mixer buffer */ num = synth->cur; buffered_blocks = (synth->cur + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; if(synth->cur < buffered_blocks * FLUID_BUFSIZE) { + /* yes, available sample are in internal mixer buffer */ int available = (buffered_blocks * FLUID_BUFSIZE) - synth->cur; num = (available > len) ? len : available; + /* mixing dry samples (or skip if requested by the caller) */ if(nout != 0) { for(i = 0; i < naudchan; i++) { + /* mix num left samples from input mixer buffer (left_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ float *out_buf = out[(i * 2) % nout]; fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num); + /* mix num right samples from input mixer buffer (right_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ out_buf = out[(i * 2 + 1) % nout]; fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num); } } + /* mixing effects samples (or skip if requested by the caller) */ if(nfx != 0) { // loop over all effects units @@ -3734,9 +4077,13 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], { int buf_idx = f * nfxchan + i; + /* mix num left samples from input mixer buffer (fx_left_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ float *out_buf = fx[(buf_idx * 2) % nfx]; fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num); + /* mix num right samples from input mixer buffer (fx_right_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ out_buf = fx[(buf_idx * 2 + 1) % nfx]; fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num); } @@ -3750,23 +4097,31 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], /* Then, render blocks and copy till we have 'len' samples */ while(count < len) { + /* always render full bloc multiple of FLUID_BUFSIZE */ int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + /* render audio (dry and effect) to respective internal dry and effect buffers */ int blockcount = block_render_func(synth, blocksleft); num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE; + /* mixing dry samples (or skip if requested by the caller) */ if(nout != 0) { for(i = 0; i < naudchan; i++) { + /* mix num left samples from input mixer buffer (left_in) at input offset + 0 to output buffer (out_buf) at offset count */ float *out_buf = out[(i * 2) % nout]; fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num); + /* mix num right samples from input mixer buffer (right_in) at input offset + 0 to output buffer (out_buf) at offset count */ out_buf = out[(i * 2 + 1) % nout]; fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num); } } + /* mixing effects samples (or skip if requested by the caller) */ if(nfx != 0) { // loop over all effects units @@ -3777,9 +4132,13 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], { int buf_idx = f * nfxchan + i; + /* mix num left samples from input mixer buffer (fx_left_in) at input offset + 0 to output buffer (out_buf) at offset count */ float *out_buf = fx[(buf_idx * 2) % nfx]; fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num); + /* mix num right samples from input mixer buffer (fx_right_in) at input offset + 0 to output buffer (out_buf) at offset count */ out_buf = fx[(buf_idx * 2 + 1) % nfx]; fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num); } @@ -3798,6 +4157,7 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], return FLUID_OK; } + /** * Synthesize a block of floating point audio samples to audio buffers. * @param synth FluidSynth instance @@ -3821,45 +4181,133 @@ fluid_synth_write_float(fluid_synth_t *synth, int len, void *lout, int loff, int lincr, void *rout, int roff, int rincr) { - return fluid_synth_write_float_LOCAL(synth, len, lout, loff, lincr, rout, roff, rincr, fluid_synth_render_blocks); + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_float_channels(synth, len, 2, channels_out, + channels_off, channels_incr); +} + +/** + * Synthesize a block of float audio samples channels to audio buffers. + * The function is convenient for audio driver to render multiple stereo + * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). + * + * @param synth FluidSynth instance. + * @param len Count of audio frames to synthesize. + * @param channels_count Count of channels in a frame. + * must be multiple of 2 and channel_count/2 must not exceed the number + * of internal mixer buffers (synth->audio_groups) + * @param channels_out Array of channels_count pointers on 16 bit words to + * store sample channels. Modified on return. + * @param channels_off Array of channels_count offset index to add to respective pointer + * in channels_out for first sample. + * @param channels_incr Array of channels_count increment between consecutive + * samples channels. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + * + * Useful for storing: + * - interleaved channels in a unique buffer. + * - non interleaved channels in an unique buffer (or in distinct buffers). + * + * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) + * in a unique buffer: + * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,... + * sn:c1, sn:c2, sn:c3, sn:c4 }. + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + */ +int +fluid_synth_write_float_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]) +{ + return fluid_synth_write_float_channels_LOCAL(synth, len, channels_count, + channels_out, channels_off, channels_incr, + fluid_synth_render_blocks); } int -fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, - void *lout, int loff, int lincr, - void *rout, int roff, int rincr, - int (*block_render_func)(fluid_synth_t *, int) - ) +fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[], + int (*block_render_func)(fluid_synth_t *, int)) { + float **chan_out = (float **)channels_out; int n, cur, size; - float *left_out = (float *) lout + loff; - float *right_out = (float *) rout + roff; + + /* pointers on first input mixer buffer */ fluid_real_t *left_in; fluid_real_t *right_in; + int bufs_in_count; /* number of stereo input buffers */ + int i; + + /* start average cpu load probe */ double time = fluid_utime(); float cpu_load; + /* start profiling duration probe (if profiling is enabled) */ fluid_profile_ref_var(prof_ref); + /* check parameters */ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail(lout != NULL, FLUID_FAILED); - fluid_return_val_if_fail(rout != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below - fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); + /* check for valid channel_count: must be multiple of 2 and + channel_count/2 must not exceed the number of internal mixer buffers + (synth->audio_groups) + */ + fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ + && channels_count >= 2, FLUID_FAILED); + + bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ + fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); + + fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); + + /* initialize output channels buffers on first sample position */ + i = channels_count; + do + { + i--; + chan_out[i] += channels_off[i]; + } + while(i); + + /* Conversely to fluid_synth_process(), + we want rendered audio effect mixed in internal audio dry buffers. + TRUE instructs the mixer that internal audio effects will be mixed in internal + audio dry buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); size = len; - cur = synth->cur; + + /* synth->cur indicates if available samples are still in internal mixer buffer */ + cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ do { /* fill up the buffers as needed */ if(cur >= synth->curmax) { + /* render audio (dry and effect) to internal dry buffers */ + /* always render full blocs multiple of FLUID_BUFSIZE */ int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); cur = 0; } @@ -3887,28 +4335,61 @@ fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, do { - *left_out = (float) left_in[n]; - *right_out = (float) right_in[n]; + i = bufs_in_count; + do + { + /* input sample index in stereo buffer i */ + int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; + int c = i << 1; /* channel index c to write */ - left_out += lincr; - right_out += rincr; + /* write left input sample to channel sample */ + *chan_out[c] = (float) left_in[in_idx]; + + /* write right input sample to next channel sample */ + *chan_out[c+1] = (float) right_in[in_idx]; + + /* advance output pointers */ + chan_out[c] += channels_incr[c]; + chan_out[c+1] += channels_incr[c+1]; + } + while(i); } while(++n < 0); } while(size); - synth->cur = cur; + synth->cur = cur; /* save current sample position. It will be used on next call */ + /* save average cpu load, use by API for real time cpu load meter */ time = fluid_utime() - time; cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set(&synth->cpu_load, cpu_load); + /* stop duration probe and save performance measurement (if profiling is enabled) */ fluid_profile_write(FLUID_PROF_WRITE, prof_ref, fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), len); return FLUID_OK; } +/* for testing purpose */ +int +fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr, + int (*block_render_func)(fluid_synth_t *, int) + ) +{ + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_float_channels_LOCAL(synth, len, 2, channels_out, + channels_off, channels_incr, + block_render_func); +} + + #define DITHER_SIZE 48000 #define DITHER_CHANNELS 2 @@ -3989,27 +4470,109 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, void *lout, int loff, int lincr, void *rout, int roff, int rincr) { + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_s16_channels(synth, len, 2, channels_out, + channels_off, channels_incr); +} + +/** + * Synthesize a block of 16 bit audio samples channels to audio buffers. + * The function is convenient for audio driver to render multiple stereo + * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). + * + * @param synth FluidSynth instance. + * @param len Count of audio frames to synthesize. + * @param channels_count Count of channels in a frame. + * must be multiple of 2 and channel_count/2 must not exceed the number + * of internal mixer buffers (synth->audio_groups) + * @param channels_out Array of channels_count pointers on 16 bit words to + * store sample channels. Modified on return. + * @param channels_off Array of channels_count offset index to add to respective pointer + * in channels_out for first sample. + * @param channels_incr Array of channels_count increment between consecutive + * samples channels. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + * + * Useful for storing: + * - interleaved channels in a unique buffer. + * - non interleaved channels in an unique buffer (or in distinct buffers). + * + * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) + * in a unique buffer: + * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4, .... + * sn:c1, sn:c2, sn:c3, sn:c4 }. + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + * @note Dithering is performed when converting from internal floating point to + * 16 bit audio. + */ +int +fluid_synth_write_s16_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]) +{ + int16_t **chan_out = (int16_t **)channels_out; int di, n, cur, size; - int16_t *left_out = (int16_t *)lout + loff; - int16_t *right_out = (int16_t *)rout + roff; + + /* pointers on first input mixer buffer */ fluid_real_t *left_in; fluid_real_t *right_in; + int bufs_in_count; /* number of stereo input buffers */ + int i; + + /* start average cpu load probe */ double time = fluid_utime(); float cpu_load; + /* start profiling duration probe (if profiling is enabled) */ fluid_profile_ref_var(prof_ref); + /* check parameters */ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); - fluid_return_val_if_fail(lout != NULL, FLUID_FAILED); - fluid_return_val_if_fail(rout != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below - fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1); + /* check for valid channel_count: must be multiple of 2 and + channel_count/2 must not exceed the number of internal mixer buffers + (synth->audio_groups) + */ + fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ + && channels_count >= 2, FLUID_FAILED); + + bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ + fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); + + fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); + + /* initialize output channels buffers on first sample position */ + i = channels_count; + do + { + i--; + chan_out[i] += channels_off[i]; + } + while(i); + + /* Conversely to fluid_synth_process(), + we want rendered audio effect mixed in internal audio dry buffers. + TRUE instructs the mixer that internal audio effects will be mixed in internal + audio dry buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); size = len; - cur = synth->cur; + /* synth->cur indicates if available samples are still in internal mixer buffer */ + cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ di = synth->dither_index; do @@ -4017,8 +4580,12 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, /* fill up the buffers as needed */ if(cur >= synth->curmax) { + /* render audio (dry and effect) to internal dry buffers */ + /* always render full blocs multiple of FLUID_BUFSIZE */ int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); cur = 0; } @@ -4046,11 +4613,25 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, do { - *left_out = round_clip_to_i16(left_in[n] * 32766.0f + rand_table[0][di]); - *right_out = round_clip_to_i16(right_in[n] * 32766.0f + rand_table[1][di]); + i = bufs_in_count; + do + { + /* input sample index in stereo buffer i */ + int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; + int c = i << 1; /* channel index c to write */ - left_out += lincr; - right_out += rincr; + /* write left input sample to channel sample */ + *chan_out[c] = round_clip_to_i16(left_in[in_idx] * 32766.0f + + rand_table[0][di]); + + /* write right input sample to next channel sample */ + *chan_out[c+1] = round_clip_to_i16(right_in[in_idx] * 32766.0f + + rand_table[1][di]); + /* advance output pointers */ + chan_out[c] += channels_incr[c]; + chan_out[c+1] += channels_incr[c+1]; + } + while(i); if(++di >= DITHER_SIZE) { @@ -4061,17 +4642,19 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, } while(size); - synth->cur = cur; - synth->dither_index = di; /* keep dither buffer continous */ + synth->cur = cur; /* save current sample position. It will be used on next call */ + synth->dither_index = di; /* keep dither buffer continuous */ + /* save average cpu load, used by API for real time cpu load meter */ time = fluid_utime() - time; cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); fluid_atomic_float_set(&synth->cpu_load, cpu_load); + /* stop duration probe and save performance measurement (if profiling is enabled) */ fluid_profile_write(FLUID_PROF_WRITE, prof_ref, fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), len); - return 0; + return FLUID_OK; } /** @@ -4113,7 +4696,7 @@ fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float } } - *dither_index = di; /* keep dither buffer continous */ + *dither_index = di; /* keep dither buffer continuous */ fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len); } @@ -4136,7 +4719,21 @@ fluid_synth_check_finished_voices(fluid_synth_t *synth) } else if(synth->voice[j]->overflow_rvoice == fv) { + /* Unlock the overflow_rvoice of the voice. + Decrement the reference count of the sample owned by this + rvoice. + */ fluid_voice_overflow_rvoice_finished(synth->voice[j]); + + /* Decrement synth active voice count. Must not be incorporated + in fluid_voice_overflow_rvoice_finished() because + fluid_voice_overflow_rvoice_finished() is called also + at synth destruction and in this case the variable should be + accessed via voice->channel->synth->active_voice_count. + And for certain voices which are not playing, the field + voice->channel is NULL. + */ + synth->active_voice_count--; break; } } @@ -4228,31 +4825,31 @@ static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, d if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0) { - fluid_synth_set_reverb_roomsize(synth, value); + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, value); } else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0) { - fluid_synth_set_reverb_damp(synth, value); + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, value); } else if(FLUID_STRCMP(name, "synth.reverb.width") == 0) { - fluid_synth_set_reverb_width(synth, value); + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, value); } else if(FLUID_STRCMP(name, "synth.reverb.level") == 0) { - fluid_synth_set_reverb_level(synth, value); + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, value); } else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0) { - fluid_synth_set_chorus_depth(synth, value); + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, value); } else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0) { - fluid_synth_set_chorus_speed(synth, value); + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, value); } else if(FLUID_STRCMP(name, "synth.chorus.level") == 0) { - fluid_synth_set_chorus_level(synth, value); + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, value); } } @@ -4266,15 +4863,15 @@ static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, i if(FLUID_STRCMP(name, "synth.reverb.active") == 0) { - fluid_synth_set_reverb_on(synth, value); + fluid_synth_reverb_on(synth, -1, value); } else if(FLUID_STRCMP(name, "synth.chorus.active") == 0) { - fluid_synth_set_chorus_on(synth, value); + fluid_synth_chorus_on(synth, -1, value); } else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0) { - fluid_synth_set_chorus_nr(synth, value); + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, (double)value); } } @@ -4384,6 +4981,7 @@ fluid_synth_alloc_voice(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel) { fluid_return_val_if_fail(sample != NULL, NULL); + fluid_return_val_if_fail(sample->data != NULL, NULL); FLUID_API_ENTRY_CHAN(NULL); FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL)); @@ -4507,14 +5105,13 @@ fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, for(i = 0; i < synth->polyphony; i++) { fluid_voice_t *existing_voice = synth->voice[i]; - int existing_excl_class = fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS); /* 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 */ if(fluid_voice_is_playing(existing_voice) && fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice) - && existing_excl_class == excl_class + && fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS) == excl_class && fluid_voice_get_id(existing_voice) != fluid_voice_get_id(new_voice)) { fluid_voice_kill_excl(existing_voice); @@ -4592,6 +5189,10 @@ fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader) * @param filename File to load * @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset()) * @return SoundFont ID on success, #FLUID_FAILED on error + * + * @note Since FluidSynth 2.2.0 @c filename is treated as an UTF8 encoded string on Windows. FluidSynth will convert it + * to wide-char internally and then pass it to _wfopen(). Before FluidSynth 2.2.0, @c filename was treated as ANSI string + * on Windows. All other platforms directly pass it to fopen() without any conversion (usually, UTF8 is accepted). */ int fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets) @@ -4640,11 +5241,25 @@ fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets } /** - * Unload a SoundFont. + * Schedule a SoundFont for unloading. + * + * If the SoundFont isn't used anymore by any playing voices, it will be unloaded immediately. + * + * If any samples of the given SoundFont are still required by active voices, + * the SoundFont will be unloaded in a lazy manner, once those voices have finished synthesizing. + * If you call delete_fluid_synth(), all voices will be destroyed and the SoundFont + * will be unloaded in any case. + * Once this function returned, fluid_synth_sfcount() and similar functions will behave as if + * the SoundFont has already been unloaded, even though the lazy-unloading is still pending. + * + * @note This lazy-unloading mechanism was broken between FluidSynth 1.1.4 and 2.1.5 . As a + * consequence, SoundFonts scheduled for lazy-unloading may be never freed under certain + * conditions. Calling delete_fluid_synth() does not recover this situation either. + * * @param synth FluidSynth 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 + * @return #FLUID_OK if the given @p id was found, #FLUID_FAILED otherwise. */ int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets) @@ -4705,7 +5320,8 @@ fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont) } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ else { - new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, TRUE, FALSE); + fluid_timer_t* timer = new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, FALSE, FALSE); + synth->fonts_to_be_unloaded = fluid_list_prepend(synth->fonts_to_be_unloaded, timer); } } } @@ -5043,13 +5659,13 @@ fluid_synth_get_voicelist(fluid_synth_t *synth, fluid_voice_t *buf[], int bufsiz /** * Enable or disable reverb effect. * @param synth FluidSynth instance - * @param on TRUE to enable reverb, FALSE to disable + * @param on TRUE to enable chorus, FALSE to disable + * @deprecated Use fluid_synth_reverb_on() instead. */ void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on) { fluid_return_if_fail(synth != NULL); - fluid_synth_api_enter(synth); synth->with_reverb = (on != 0); @@ -5058,6 +5674,44 @@ fluid_synth_set_reverb_on(fluid_synth_t *synth, int on) fluid_synth_api_exit(synth); } +/** + * Enable or disable reverb on one fx group unit. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param on TRUE to enable reverb, FALSE to disable + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(fx_group < 0 ) + { + synth->with_reverb = (on != 0); + } + + param[0].i = fx_group; + param[1].i = on; + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_reverb_enable, + synth->eventhandler->mixer, + param); + + FLUID_API_RETURN(ret); +} + /** * Activate a reverb preset. * @param synth FluidSynth instance @@ -5069,201 +5723,418 @@ fluid_synth_set_reverb_on(fluid_synth_t *synth, int on) int fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num) { + double values[FLUID_REVERB_PARAM_LAST]; + fluid_return_val_if_fail( num < FLUID_N_ELEMENTS(revmodel_preset), FLUID_FAILED ); - fluid_synth_set_reverb(synth, revmodel_preset[num].roomsize, - revmodel_preset[num].damp, revmodel_preset[num].width, - revmodel_preset[num].level); + values[FLUID_REVERB_ROOMSIZE] = revmodel_preset[num].roomsize; + values[FLUID_REVERB_DAMP] = revmodel_preset[num].damp; + values[FLUID_REVERB_WIDTH] = revmodel_preset[num].width; + values[FLUID_REVERB_LEVEL] = revmodel_preset[num].level; + fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); return FLUID_OK; } /** - * Set reverb parameters. + * Set reverb parameters to all groups. + * * @param synth FluidSynth instance * @param roomsize Reverb room size value (0.0-1.0) * @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 - * - * @note Not realtime safe and therefore should not be called from synthesis - * context at the risk of stalling audio output. + * @deprecated Use the individual reverb setter functions in new code instead. */ int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping, double width, double level) { - return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_ALL, - roomsize, damping, width, level); + double values[FLUID_REVERB_PARAM_LAST]; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + values[FLUID_REVERB_ROOMSIZE] = roomsize; + values[FLUID_REVERB_DAMP] = damping; + values[FLUID_REVERB_WIDTH] = width; + values[FLUID_REVERB_LEVEL] = level; + return fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); } /** - * Set reverb roomsize. See fluid_synth_set_reverb() for further info. + * Set reverb roomsize of all groups. + * + * @param synth FluidSynth instance + * @param roomsize Reverb room size value (0.0-1.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_roomsize() in new code instead. */ int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize) { - return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_ROOMSIZE, roomsize, 0, 0, 0); + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, roomsize); } /** - * Set reverb damping. See fluid_synth_set_reverb() for further info. + * Set reverb damping of all groups. + * + * @param synth FluidSynth instance + * @param damping Reverb damping value (0.0-1.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_damp() in new code instead. */ int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping) { - return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_DAMPING, 0, damping, 0, 0); + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, damping); } /** - * Set reverb width. See fluid_synth_set_reverb() for further info. + * Set reverb width of all groups. + * + * @param synth FluidSynth instance + * @param width Reverb width value (0.0-100.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_width() in new code instead. */ int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width) { - return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_WIDTH, 0, 0, width, 0); + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, width); } /** - * Set reverb level. See fluid_synth_set_reverb() for further info. + * Set reverb level of all groups. + * + * @param synth FluidSynth instance + * @param level Reverb level value (0.0-1.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_level() in new code instead. */ int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level) { - return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_LEVEL, 0, 0, 0, level); + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, 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) + * Set reverb roomsize to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param roomsize roomsize value to set. Must be in the range indicated by + * synth.reverb.room-size setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, + double roomsize) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * Set reverb damp to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param damping damping value to set. Must be in the range indicated by + * synth.reverb.damp setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, + double damping) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_DAMP, damping); +} + +/** + * Set reverb width to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param width width value to set. Must be in the range indicated by + * synth.reverb.width setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, + double width) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_WIDTH, width); +} + +/** + * Set reverb level to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param level output level to set. Must be in the range indicated by + * synth.reverb.level setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, + double level) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_LEVEL, level); +} + +/** + * Set one reverb parameter to one fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param enum indicating the parameter to set (#fluid_reverb_param). + * FLUID_REVERB_ROOMSIZE, roomsize Reverb room size value (0.0-1.0) + * FLUID_REVERB_DAMP, reverb damping value (0.0-1.0) + * FLUID_REVERB_WIDTH, reverb width value (0.0-100.0) + * FLUID_REVERB_LEVEL, reverb level value (0.0-1.0) + * @param value, parameter value * @return #FLUID_OK on success, #FLUID_FAILED otherwise - * - * @note Not realtime safe and therefore should not be called from synthesis - * context at the risk of stalling audio output. */ int -fluid_synth_set_reverb_full(fluid_synth_t *synth, int set, double roomsize, - double damping, double width, double level) +fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group, + int param, double value) { int ret; - fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + double values[FLUID_REVERB_PARAM_LAST] = {0.0}; + static const char *name[FLUID_REVERB_PARAM_LAST] = + { + "synth.reverb.room-size", "synth.reverb.damp", + "synth.reverb.width", "synth.reverb.level" + }; + double min; /* minimum value */ + double max; /* maximum value */ + + /* check parameters */ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); - /* if non of the flags is set, fail */ - fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED); - - /* Synth shadow values are set here so that they will be returned if querried */ - + fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); fluid_synth_api_enter(synth); - if(set & FLUID_REVMODEL_SET_ROOMSIZE) + if(fx_group < -1 || fx_group >= synth->effects_groups) { - synth->reverb_roomsize = roomsize; + FLUID_API_RETURN(FLUID_FAILED); } - if(set & FLUID_REVMODEL_SET_DAMPING) + /* check if reverb value is in max min range */ + fluid_settings_getnum_range(synth->settings, name[param], &min, &max); + if(value < min || value > max) { - synth->reverb_damping = damping; + FLUID_API_RETURN(FLUID_FAILED); } - if(set & FLUID_REVMODEL_SET_WIDTH) - { - synth->reverb_width = width; - } - - if(set & FLUID_REVMODEL_SET_LEVEL) - { - synth->reverb_level = level; - } - - param[0].i = set; - param[1].real = roomsize; - param[2].real = damping; - param[3].real = width; - param[4].real = level; - /* finally enqueue an rvoice event to the mixer to actual update reverb */ - ret = fluid_rvoice_eventhandler_push(synth->eventhandler, - fluid_rvoice_mixer_set_reverb_params, - synth->eventhandler->mixer, - param); + /* set the value */ + values[param] = value; + ret = fluid_synth_set_reverb_full(synth, fx_group, FLUID_REVPARAM_TO_SETFLAG(param), values); FLUID_API_RETURN(ret); } +int +fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED); + + /* fx group shadow values are set here so that they will be returned if queried */ + fluid_rvoice_mixer_set_reverb_full(synth->eventhandler->mixer, fx_group, set, + values); + + /* Synth shadow values are set here so that they will be returned if queried */ + if (fx_group < 0) + { + int i; + for(i = 0; i < FLUID_REVERB_PARAM_LAST; i++) + { + if(set & FLUID_REVPARAM_TO_SETFLAG(i)) + { + synth->reverb_param[i] = values[i]; + } + } + } + + param[0].i = fx_group; + param[1].i = set; + param[2].real = values[FLUID_REVERB_ROOMSIZE]; + param[3].real = values[FLUID_REVERB_DAMP]; + param[4].real = values[FLUID_REVERB_WIDTH]; + param[5].real = values[FLUID_REVERB_LEVEL]; + /* finally enqueue an rvoice event to the mixer to actual update reverb */ + return fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_reverb_params, + synth->eventhandler->mixer, + param); +} + /** - * Get reverb room size. + * Get reverb room size of all fx groups. * @param synth FluidSynth instance * @return Reverb room size (0.0-1.2) + * @deprecated Use fluid_synth_get_reverb_group_roomsize() in new code instead. */ double fluid_synth_get_reverb_roomsize(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail(synth != NULL, 0.0); - fluid_synth_api_enter(synth); - result = synth->reverb_roomsize; - FLUID_API_RETURN(result); + double roomsize = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_ROOMSIZE, &roomsize); + return roomsize; } /** - * Get reverb damping. + * Get reverb damping of all fx groups. * @param synth FluidSynth instance * @return Reverb damping value (0.0-1.0) + * @deprecated Use fluid_synth_get_reverb_group_damp() in new code instead. */ double fluid_synth_get_reverb_damp(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail(synth != NULL, 0.0); - fluid_synth_api_enter(synth); - - result = synth->reverb_damping; - FLUID_API_RETURN(result); + double damp = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_DAMP, &damp); + return damp; } /** - * Get reverb level. + * Get reverb level of all fx groups. * @param synth FluidSynth instance * @return Reverb level value (0.0-1.0) + * @deprecated Use fluid_synth_get_reverb_group_level() in new code instead. */ double fluid_synth_get_reverb_level(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail(synth != NULL, 0.0); - fluid_synth_api_enter(synth); - - result = synth->reverb_level; - FLUID_API_RETURN(result); + double level = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_LEVEL, &level); + return level; } /** - * Get reverb width. + * Get reverb width of all fx groups. * @param synth FluidSynth instance * @return Reverb width value (0.0-100.0) + * @deprecated Use fluid_synth_get_reverb_group_width() in new code instead. */ double fluid_synth_get_reverb_width(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail(synth != NULL, 0.0); - fluid_synth_api_enter(synth); - - result = synth->reverb_width; - FLUID_API_RETURN(result); + double width = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_WIDTH, &width); + return width; } /** - * Enable or disable chorus effect. + * get reverb roomsize of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param roomsize valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, + double *roomsize) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * get reverb damp of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param damping valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, + double *damping) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_DAMP, damping); +} + +/** + * get reverb width of one or all groups + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param width valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, + double *width) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_WIDTH, width); +} + +/** + * get reverb level of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param level valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, + double *level) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_LEVEL, level); +} + + +/** + * Get one reverb parameter value of one fx groups. + * @param synth FluidSynth instance + * @param fx_group index of the fx group to get parameter value from. + * Must be in the range -1 to synth->effects_groups-1. If -1 get the + * parameter common to all fx groups. + * @param enum indicating the parameter to get (#fluid_reverb_param). + * FLUID_REVERB_ROOMSIZE, reverb room size value. + * FLUID_REVERB_DAMP, reverb damping value. + * FLUID_REVERB_WIDTH, reverb width value. + * FLUID_REVERB_LEVEL, reverb level value. + * @param value pointer on the value to return. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); + fluid_return_val_if_fail(value != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if (fx_group < 0) + { + /* return reverb param common to all fx groups */ + *value = synth->reverb_param[param]; + } + else + { + /* return reverb param of fx group at index fx_group */ + *value = fluid_rvoice_mixer_reverb_get_param(synth->eventhandler->mixer, + fx_group, param); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Enable or disable all chorus groups. * @param synth FluidSynth instance * @param on TRUE to enable chorus, FALSE to disable + * @deprecated Use fluid_synth_chorus_on() in new code instead. */ void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) @@ -5278,7 +6149,45 @@ fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) } /** - * Set chorus parameters. It should be turned on with fluid_synth_set_chorus_on(). + * Enable or disable chorus on one or all groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param on TRUE to enable chorus, FALSE to disable + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(fx_group < 0 ) + { + synth->with_chorus = (on != 0); + } + + param[0].i = fx_group; + param[1].i = on; + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_chorus_enable, + synth->eventhandler->mixer, + param); + + FLUID_API_RETURN(ret); +} + +/** + * Set chorus parameters to all fx groups. * Keep in mind, that the needed CPU time is proportional to 'nr'. * @param synth FluidSynth instance * @param nr Chorus voice count (0-99, CPU time consumption proportional to @@ -5289,190 +6198,471 @@ fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) * 0.0-21.0 is safe for sample-rate values up to 96KHz) * @param type Chorus waveform type (#fluid_chorus_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use the individual chorus setter functions in new code instead. + * + * Keep in mind, that the needed CPU time is proportional to 'nr'. */ int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, double speed, double depth_ms, int type) { - return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_ALL, nr, level, speed, - depth_ms, type); + double values[FLUID_CHORUS_PARAM_LAST]; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + values[FLUID_CHORUS_NR] = nr; + values[FLUID_CHORUS_LEVEL] = level; + values[FLUID_CHORUS_SPEED] = speed; + values[FLUID_CHORUS_DEPTH] = depth_ms; + values[FLUID_CHORUS_TYPE] = type; + return fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); } /** - * Set the chorus voice count. See fluid_synth_set_chorus() for further info. + * Set the chorus voice count of all groups. + * + * @param synth FluidSynth instance + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_nr() in new code instead. */ int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr) { - return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_NR, nr, 0, 0, 0, 0); + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, nr); } /** - * Set the chorus level. See fluid_synth_set_chorus() for further info. + * Set the chorus level of all groups. + * + * @param synth FluidSynth instance + * @param level Chorus level (0.0-10.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_level() in new code instead. */ int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level) { - return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_LEVEL, 0, level, 0, 0, 0); + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, level); } /** - * Set the chorus speed. See fluid_synth_set_chorus() for further info. + * Set the chorus speed of all groups. + * + * @param synth FluidSynth instance + * @param speed Chorus speed in Hz (0.1-5.0) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_level() in new code instead. */ int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed) { - return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_SPEED, 0, 0, speed, 0, 0); + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, speed); } /** - * Set the chorus depth. See fluid_synth_set_chorus() for further info. + * Set the chorus depth of all groups. + * + * @param synth FluidSynth instance + * @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) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_depth() in new code instead. */ int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms) { - return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_DEPTH, 0, 0, 0, depth_ms, 0); + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, depth_ms); } /** - * Set the chorus type. See fluid_synth_set_chorus() for further info. + * Set the chorus type of all groups. + * + * @param synth FluidSynth instance + * @param type Chorus waveform type (#fluid_chorus_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_type() in new code instead. */ int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type) { - return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_TYPE, 0, 0, 0, 0, type); -} - -int -fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level, - double speed, double depth_ms, int type) -{ - int ret; - fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; - - fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); - /* if non of the flags is set, fail */ - fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED); - - /* Synth shadow values are set here so that they will be returned if queried */ - fluid_synth_api_enter(synth); - - if(set & FLUID_CHORUS_SET_NR) - { - synth->chorus_nr = nr; - } - - if(set & FLUID_CHORUS_SET_LEVEL) - { - synth->chorus_level = level; - } - - if(set & FLUID_CHORUS_SET_SPEED) - { - synth->chorus_speed = speed; - } - - if(set & FLUID_CHORUS_SET_DEPTH) - { - synth->chorus_depth = depth_ms; - } - - if(set & FLUID_CHORUS_SET_TYPE) - { - synth->chorus_type = type; - } - - param[0].i = set; - param[1].i = nr; - param[2].real = level; - param[3].real = speed; - param[4].real = depth_ms; - param[5].i = type; - ret = fluid_rvoice_eventhandler_push(synth->eventhandler, - fluid_rvoice_mixer_set_chorus_params, - synth->eventhandler->mixer, - param); - - FLUID_API_RETURN(ret); + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_TYPE, type); } /** - * Get chorus voice number (delay line count) value. + * Set chorus voice count nr to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param nr Voice count to set. Must be in the range indicated by \setting{synth_chorus_nr} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_NR, (double)nr); +} + +/** + * Set chorus output level to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param level Output level to set. Must be in the range indicated by \setting{synth_chorus_level} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); +} + +/** + * Set chorus lfo speed to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param speed Lfo speed to set. Must be in the range indicated by \setting{synth_chorus_speed} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); +} + +/** + * Set chorus lfo depth to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param depth_ms lfo depth to set. Must be in the range indicated by \setting{synth_chorus_depth} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Set chorus lfo waveform type to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param type Lfo waveform type to set. (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_TYPE, (double)type); +} + +/** + * Set one chorus parameter to one fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param enum indicating the parameter to set (#fluid_chorus_param). + * FLUID_CHORUS_NR, chorus voice count (0-99, CPU time consumption proportional to + * this value). + * FLUID_CHORUS_LEVEL, chorus level (0.0-10.0). + * FLUID_CHORUS_SPEED, chorus speed in Hz (0.1-5.0). + * FLUID_CHORUS_DEPTH, chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz). + * FLUID_CHORUS_TYPE, chorus waveform type (#fluid_chorus_mod) + * @param value, parameter value + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, int param, + double value) +{ + int ret; + double values[FLUID_CHORUS_PARAM_LAST] = {0.0}; + + /* setting name (except lfo waveform type) */ + static const char *name[FLUID_CHORUS_PARAM_LAST-1] = + { + "synth.chorus.nr", "synth.chorus.level", + "synth.chorus.speed", "synth.chorus.depth" + }; + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* check if chorus value is in max min range */ + if(param == FLUID_CHORUS_TYPE || param == FLUID_CHORUS_NR) /* integer value */ + { + int min = FLUID_CHORUS_MOD_SINE; + int max = FLUID_CHORUS_MOD_TRIANGLE; + if(param == FLUID_CHORUS_NR) + { + fluid_settings_getint_range(synth->settings, name[param], &min, &max); + } + if((int)value < min || (int)value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + } + else /* float value */ + { + double min; + double max; + fluid_settings_getnum_range(synth->settings, name[param], &min, &max); + if(value < min || value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + } + + /* set the value */ + values[param] = value; + ret = fluid_synth_set_chorus_full(synth, fx_group, + FLUID_CHORPARAM_TO_SETFLAG(param), values); + FLUID_API_RETURN(ret); +} + +int +fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED); + + /* fx group shadow values are set here so that they will be returned if queried */ + fluid_rvoice_mixer_set_chorus_full(synth->eventhandler->mixer, fx_group, + set, values); + + /* Synth shadow values are set here so that they will be returned if queried */ + if (fx_group < 0) + { + int i; + for(i = 0; i < FLUID_CHORUS_PARAM_LAST; i++) + { + if(set & FLUID_CHORPARAM_TO_SETFLAG(i)) + { + synth->chorus_param[i] = values[i]; + } + } + } + + param[0].i = fx_group; + param[1].i = set; + param[2].i = (int)values[FLUID_CHORUS_NR]; + param[3].real = values[FLUID_CHORUS_LEVEL]; + param[4].real = values[FLUID_CHORUS_SPEED]; + param[5].real = values[FLUID_CHORUS_DEPTH]; + param[6].i = (int)values[FLUID_CHORUS_TYPE]; + return fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_chorus_params, + synth->eventhandler->mixer, + param); +} + +/** + * Get chorus voice number (delay line count) value of all fx groups. * @param synth FluidSynth instance * @return Chorus voice count + * @deprecated Use fluid_synth_get_chorus_group_nr() in new code instead. */ int fluid_synth_get_chorus_nr(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail(synth != NULL, 0); - fluid_synth_api_enter(synth); - - result = synth->chorus_nr; - FLUID_API_RETURN(result); + double nr = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_NR, &nr); + return (int)nr; } /** - * Get chorus level. + * Get chorus level of all fx groups. * @param synth FluidSynth instance * @return Chorus level value + * @deprecated Use fluid_synth_get_chorus_group_level() in new code instead. */ double fluid_synth_get_chorus_level(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail(synth != NULL, 0.0); - fluid_synth_api_enter(synth); - - result = synth->chorus_level; - FLUID_API_RETURN(result); + double level = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_LEVEL, &level); + return level; } /** - * Get chorus speed in Hz. + * Get chorus speed in Hz of all fx groups. * @param synth FluidSynth instance * @return Chorus speed in Hz + * @deprecated Use fluid_synth_get_chorus_group_speed() in new code instead. */ double fluid_synth_get_chorus_speed(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail(synth != NULL, 0.0); - fluid_synth_api_enter(synth); - - result = synth->chorus_speed; - FLUID_API_RETURN(result); + double speed = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_SPEED, &speed); + return speed; } /** - * Get chorus depth. + * Get chorus depth of all fx groups. * @param synth FluidSynth instance * @return Chorus depth + * @deprecated Use fluid_synth_get_chorus_group_depth() in new code instead. */ double fluid_synth_get_chorus_depth(fluid_synth_t *synth) { - double result; - fluid_return_val_if_fail(synth != NULL, 0.0); - fluid_synth_api_enter(synth); - - result = synth->chorus_depth; - FLUID_API_RETURN(result); + double depth = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_DEPTH, &depth); + return depth; } /** - * Get chorus waveform type. + * Get chorus waveform type of all fx groups. * @param synth FluidSynth instance * @return Chorus waveform type (#fluid_chorus_mod) + * @deprecated Use fluid_synth_get_chorus_group_type() in new code instead. */ int fluid_synth_get_chorus_type(fluid_synth_t *synth) { - int result; - fluid_return_val_if_fail(synth != NULL, 0); + double type = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_TYPE, &type); + return (int)type; +} + +/** + * Get chorus count nr of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which to fetch the chorus voice count. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param nr valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr) +{ + double num_nr = 0.0; + int status; + status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_NR, &num_nr); + *nr = (int)num_nr; + return status; +} + +/** + * Get chorus output level of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which chorus level to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param level valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); +} + +/** + * Get chorus waveform lfo speed of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which lfo speed to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param speed valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); +} + +/** + * Get chorus lfo depth of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group from which lfo depth to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param depth_ms valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Get chorus waveform type of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group from which to fetch the waveform type. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param type valid pointer on waveform type to return (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type) +{ + double num_type = 0.0; + int status; + status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_TYPE, &num_type); + *type = (int)num_type; + return status; +} + +/** + * Get chorus parameter value of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group index of the fx group + * @param enum indicating the parameter to get. + * FLUID_CHORUS_NR, chorus voice count. + * FLUID_CHORUS_LEVEL, chorus level. + * FLUID_CHORUS_SPEED, chorus speed. + * FLUID_CHORUS_DEPTH, chorus depth. + * FLUID_CHORUS_TYPE, chorus waveform type. + * @param value pointer on the value to return. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); + fluid_return_val_if_fail(value != NULL, FLUID_FAILED); fluid_synth_api_enter(synth); - result = synth->chorus_type; - FLUID_API_RETURN(result); + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if (fx_group < 0) + { + /* return chorus param common to all fx groups */ + *value = synth->chorus_param[param]; + } + else + { + /* return chorus param of fx group at index group */ + *value = fluid_rvoice_mixer_chorus_get_param(synth->eventhandler->mixer, + fx_group, param); + } + + FLUID_API_RETURN(FLUID_OK); } /* @@ -5627,6 +6817,8 @@ fluid_synth_count_effects_channels(fluid_synth_t *synth) /** * Get the total number of allocated effects units. + * + * This is the same number as initially provided by the setting \setting{synth_effects-groups}. * @param synth FluidSynth instance * @return Count of allocated effects units */ @@ -6228,7 +7420,7 @@ fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value } /** - * Retrive the generator NRPN offset assigned to a MIDI channel. + * Retrieve the generator NRPN offset assigned to a MIDI channel. * * The value returned is in native units of the generator. By default, the offset is zero. * @param synth FluidSynth instance @@ -6305,7 +7497,9 @@ fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event) } /** - * Create and start voices using a preset and a MIDI note on event. + * Create and start voices using an arbitrary preset and a MIDI note on event. + * + * Using this function is only supported when the setting @c synth.dynamic-sample-loading is false! * @param synth FluidSynth instance * @param id Voice group ID to use (can be used with fluid_synth_stop()). * @param preset Preset to synthesize @@ -6322,13 +7516,32 @@ int fluid_synth_start(fluid_synth_t *synth, unsigned int id, fluid_preset_t *preset, int audio_chan, int chan, int key, int vel) { - int result; + int result, dynamic_samples; fluid_return_val_if_fail(preset != NULL, 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_API_ENTRY_CHAN(FLUID_FAILED); - synth->storeid = id; - result = fluid_preset_noteon(preset, synth, chan, key, vel); + + fluid_settings_getint(fluid_synth_get_settings(synth), "synth.dynamic-sample-loading", &dynamic_samples); + if(dynamic_samples) + { + // The preset might not be currently used, thus its sample data may not be loaded. + // This guard is to avoid a NULL deref in rvoice_write(). + FLUID_LOG(FLUID_ERR, "Calling fluid_synth_start() while synth.dynamic-sample-loading is enabled is not supported."); + // Although we would be able to select the preset (and load it's samples) we have no way to + // unselect the preset again in fluid_synth_stop(). Also dynamic sample loading was intended + // to be used only when presets have been selected on a MIDI channel. + // Note that even if the preset is currently selected on a channel, it could be unselected at + // any time. And we would end up with a NULL sample->data again, because we are not referencing + // the preset here. Thus failure is our only option. + result = FLUID_FAILED; + } + else + { + synth->storeid = id; + result = fluid_preset_noteon(preset, synth, chan, key, vel); + } + FLUID_API_RETURN(result); } @@ -6513,14 +7726,13 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) /** * Configure a general-purpose IIR biquad filter. * - * This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard. - * By default this filter is off (#FLUID_IIR_DISABLED). - * * @param synth FluidSynth instance * @param type Type of the IIR filter to use (see #fluid_iir_filter_type) * @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags) - * * @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED + * + * This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard. + * By default this filter is off (#FLUID_IIR_DISABLED). */ int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags) { @@ -6626,7 +7838,7 @@ static void fluid_synth_handle_important_channels(void *data, const char *name, } -/** API legato mode *********************************************************/ +/* API legato mode *********************************************************/ /** * Sets the legato mode of a channel. @@ -6679,7 +7891,7 @@ int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode) FLUID_API_RETURN(FLUID_OK); } -/** API portamento mode *********************************************************/ +/* API portamento mode *********************************************************/ /** * Sets the portamento mode of a channel. @@ -6732,7 +7944,7 @@ int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan, FLUID_API_RETURN(FLUID_OK); } -/** API breath mode *********************************************************/ +/* API breath mode *********************************************************/ /** * Sets the breath mode of a channel. @@ -6896,7 +8108,7 @@ fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mo { /* A value of 0 for val means all possible channels from basicchan to to the next basic channel -1 (if any). - When i reachs the next basic channel group, real_val will be + When i reaches the next basic channel group, real_val will be limited if it is possible */ if(val == 0) { @@ -7010,7 +8222,7 @@ fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mod } /** - * Searchs a previous basic channel starting from chan. + * Searches a previous basic channel starting from chan. * * @param synth the synth instance. * @param chan starting index of the search (including chan). @@ -7020,7 +8232,7 @@ static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan { for(; chan >= 0; chan--) { - /* searchs previous basic channel */ + /* searches previous basic channel */ if(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC) { /* chan is the previous basic channel */ @@ -7068,7 +8280,7 @@ int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, val = synth->channel[basic_chan]->mode_val; } - /* returns the informations if they are requested */ + /* returns the information if they are requested */ if(basic_chan_out) { * basic_chan_out = basic_chan; diff --git a/libs/fluidsynth/src/fluid_synth.h b/libs/fluidsynth/src/fluid_synth.h index 955b3fa12e..9b0078f04b 100644 --- a/libs/fluidsynth/src/fluid_synth.h +++ b/libs/fluidsynth/src/fluid_synth.h @@ -125,6 +125,7 @@ struct _fluid_synth_t fluid_list_t *loaders; /**< the SoundFont loaders */ fluid_list_t *sfont; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ + fluid_list_t *fonts_to_be_unloaded; /**< list of timers that try to unload a soundfont */ float gain; /**< master gain */ fluid_channel_t **channel; /**< the channels */ @@ -136,16 +137,11 @@ struct _fluid_synth_t int fromkey_portamento; /**< fromkey portamento */ fluid_rvoice_eventhandler_t *eventhandler; - double reverb_roomsize; /**< Shadow of reverb roomsize */ - double reverb_damping; /**< Shadow of reverb damping */ - double reverb_width; /**< Shadow of reverb width */ - double reverb_level; /**< Shadow of reverb level */ + /**< Shadow of reverb parameter: roomsize, damping, width, level */ + double reverb_param[FLUID_REVERB_PARAM_LAST]; - int chorus_nr; /**< Shadow of chorus number */ - double chorus_level; /**< Shadow of chorus level */ - double chorus_speed; /**< Shadow of chorus speed */ - double chorus_depth; /**< Shadow of chorus depth */ - int chorus_type; /**< Shadow of chorus type */ + /**< Shadow of chorus parameter: chorus number, level, speed, depth, type */ + double chorus_param[FLUID_CHORUS_PARAM_LAST]; int cur; /**< the current sample in the audio buffers to be output */ int curmax; /**< current amount of samples present in the audio buffers */ @@ -185,6 +181,29 @@ typedef int (*fluid_audio_callback_t)(fluid_synth_t *synth, int len, void *out1, int loff, int lincr, void *out2, int roff, int rincr); +typedef int (*fluid_audio_channels_callback_t)(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); + +int +fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[], + int (*block_render_func)(fluid_synth_t *, int)); + +int +fluid_synth_write_s16_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); +int +fluid_synth_write_float_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); + fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth, int banknum, int prognum); @@ -196,12 +215,17 @@ void fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const int fluid_synth_reset_reverb(fluid_synth_t *synth); int fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned 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_reverb_set_param(fluid_synth_t *synth, int fx_group, + int param, + double value); +int fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]); int fluid_synth_reset_chorus(fluid_synth_t *synth); -int fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level, - double speed, double depth_ms, int type); +int fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, + int param, double value); +int fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]); fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data); void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer); @@ -215,16 +239,17 @@ int fluid_synth_set_gen2(fluid_synth_t *synth, int chan, int fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], - int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)); + int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)); int fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, - void *lout, int loff, int lincr, - void *rout, int roff, int rincr, - int (*block_render_func)(fluid_synth_t *, int)); + void *lout, int loff, int lincr, + void *rout, int roff, int rincr, + int (*block_render_func)(fluid_synth_t *, int)); /* * misc */ void fluid_synth_settings(fluid_settings_t *settings); +void fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate); /* extern declared in fluid_synth_monopoly.c */ diff --git a/libs/fluidsynth/src/fluid_synth_monopoly.c b/libs/fluidsynth/src/fluid_synth_monopoly.c index 23e63bacb6..7791cd40fb 100644 --- a/libs/fluidsynth/src/fluid_synth_monopoly.c +++ b/libs/fluidsynth/src/fluid_synth_monopoly.c @@ -86,7 +86,7 @@ note 'tokey'. Portamento fromkey note choice is determined at noteOn by fluid_synth_get_fromkey_portamento_legato() (see below). - More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). ******************************************************************************/ @@ -95,7 +95,7 @@ ******************************************************************************/ /** - * fluid_synth_get_fromkey_portamento_legato returns two informations: + * fluid_synth_get_fromkey_portamento_legato returns two information: * - fromkey note for portamento. * - fromkey note for legato. * +-----> fromkey_portamento @@ -120,7 +120,7 @@ * - default_fromkey if valid * - otherwise prev_note(prev_note is the note prior the most recent * note played). - * Then portamento mode is applied to validate the value choosen. + * Then portamento mode is applied to validate the value chosen. * Where portamento mode is: * - each note, a portamento occurs on each note. * - legato only, portamento only on notes played legato. @@ -143,7 +143,7 @@ * * On input * @param chan fluid_channel_t. - * @param defaultFromkey, the defaut 'fromkey portamento' note or 'fromkey legato' + * @param defaultFromkey, the default 'fromkey portamento' note or 'fromkey legato' * note (see description above). * * @return @@ -254,7 +254,7 @@ static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan, * polyphonic mode and legato pedal is On during the playing. * When a channel is in "monophonic playing" state, only one note at a time can be * played in a staccato or legato manner (with or without portamento). - * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices). + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). * _______________ * ________________ | noteon | * | legato detector| O-->| mono_staccato |--*-> preset_noteon @@ -521,7 +521,7 @@ fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int ve * * The function has the same behaviour when the noteoff is poly of mono, except * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the - * key is memorized. This is neccessary when the next mono note will be played + * key is memorized. This is necessary when the next mono note will be played * staccato, as any current mono note currently sustained will need to be released * (see fluid_synth_noteon_mono_staccato()). * Note also that for a monophonic legato passage, the function is called only when @@ -647,7 +647,7 @@ int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, * When key tokey is outside the current Instrument Zone, Preset Zone, * current 'fromkey' voices are released. If necessary new voices * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s). - * More informations in FluidPolyMono-0004.pdf chapter 4.7 (Appendices). + * More information in FluidPolyMono-0004.pdf chapter 4.7 (Appendices). */ int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, int fromkey, int tokey, int vel) diff --git a/libs/fluidsynth/src/fluid_sys.c b/libs/fluidsynth/src/fluid_sys.c index c6300b90c2..609af21f36 100644 --- a/libs/fluidsynth/src/fluid_sys.c +++ b/libs/fluidsynth/src/fluid_sys.c @@ -60,6 +60,10 @@ typedef struct struct _fluid_timer_t { long msec; + + // Pointer to a function to be executed by the timer. + // This field is set to NULL once the timer is finished to indicate completion. + // This allows for timed waits, rather than waiting forever as fluid_timer_join() does. fluid_timer_callback_t callback; void *data; fluid_thread_t *thread; @@ -85,7 +89,11 @@ static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] = fluid_default_log_function, fluid_default_log_function, fluid_default_log_function, +#ifdef DEBUG fluid_default_log_function +#else + NULL +#endif }; static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL }; @@ -149,9 +157,7 @@ fluid_default_log_function(int level, const char *message, void *data) break; case FLUID_DBG: -#if DEBUG FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); -#endif break; default: @@ -216,9 +222,73 @@ void* fluid_alloc(size_t len) } /** - * Convenience wrapper for free() that satisfies at least C90 requirements. - * Especially useful when using fluidsynth with programming languages that do not provide malloc() and free(). - * @note Only use this function when the API documentation explicitly says so. Otherwise use adequate \c delete_fluid_* functions. + * Open a file with a UTF-8 string, even in Windows + * @param filename The name of the file to open + * @param mode The mode to open the file in + */ +FILE *fluid_fopen(const char *filename, const char *mode) +{ +#if defined(WIN32) + wchar_t *wpath = NULL, *wmode = NULL; + FILE *file = NULL; + int length; + if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, NULL, 0)) == 0) + { + FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for filename '%s'. Error was: '%s'", filename, fluid_get_windows_error()); + errno = EINVAL; + goto error_recovery; + } + + wpath = FLUID_MALLOC(length * sizeof(wchar_t)); + if (wpath == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory."); + errno = EINVAL; + goto error_recovery; + } + + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, wpath, length); + + if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, NULL, 0)) == 0) + { + FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for mode '%s'. Error was: '%s'", mode, fluid_get_windows_error()); + errno = EINVAL; + goto error_recovery; + } + + wmode = FLUID_MALLOC(length * sizeof(wchar_t)); + if (wmode == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory."); + errno = EINVAL; + goto error_recovery; + } + + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, wmode, length); + + file = _wfopen(wpath, wmode); + +error_recovery: + FLUID_FREE(wpath); + FLUID_FREE(wmode); + + return file; +#else + return fopen(filename, mode); +#endif +} + +/** + * Wrapper for free() that satisfies at least C90 requirements. + * + * @param ptr Pointer to memory region that should be freed + * + * @note Only use this function when the API documentation explicitly says so. Otherwise use + * adequate \c delete_fluid_* functions. + * + * @warning Calling ::free() on memory that is advised to be freed with fluid_free() results in undefined behaviour! + * (cf.: "Potential Errors Passing CRT Objects Across DLL Boundaries" found in MS Docs) + * * @since 2.0.7 */ void fluid_free(void* ptr) @@ -234,7 +304,7 @@ void fluid_free(void* ptr) * @internal * @param str Pointer to a string pointer of source to tokenize. Pointer gets * updated on each invocation to point to beginning of next token. Note that - * token char get's overwritten with a 0 byte. String pointer is set to NULL + * token char gets overwritten with a 0 byte. String pointer is set to NULL * when final token is returned. * @param delim String of delimiter chars. * @return Pointer to the next token or NULL if no more tokens. @@ -441,7 +511,7 @@ fluid_thread_self_set_prio(int prio_level) * Floating point exceptions * * The floating point exception functions were taken from Ircam's - * jMax source code. http://www.ircam.fr/jmax + * jMax source code. https://www.ircam.fr/jmax * * FIXME: check in config for i386 machine * @@ -514,7 +584,7 @@ void fluid_clear_fpe_i386(void) */ #if WITH_PROFILING -/* Profiling interface beetween profiling command shell and audio rendering API +/* Profiling interface between profiling command shell and audio rendering API (FluidProfile_0004.pdf- 3.2.2). Macros are in defined in fluid_sys.h. */ @@ -693,8 +763,8 @@ static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out) * @param sample_rate the sample rate of audio output. * @param out output stream device. * -* When print mode is 1, the function prints all the informations (see below). -* When print mode is 0, the fonction prints only the cpu loads. +* When print mode is 1, the function prints all the information (see below). +* When print mode is 0, the function prints only the cpu loads. * * ------------------------------------------------------------------------------ * Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) @@ -975,7 +1045,7 @@ new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int pri #if OLD_GLIB_THREAD_API /* Make sure g_thread_init has been called. - * FIXME - Probably not a good idea in a shared library, + * Probably not a good idea in a shared library, * but what can we do *and* remain backwards compatible? */ if(!g_thread_supported()) { @@ -1095,6 +1165,7 @@ fluid_timer_run(void *data) } FLUID_LOG(FLUID_DBG, "Timer thread finished"); + timer->callback = NULL; if(timer->auto_destroy) { @@ -1188,6 +1259,19 @@ fluid_timer_join(fluid_timer_t *timer) return FLUID_OK; } +int +fluid_timer_is_running(const fluid_timer_t *timer) +{ + // for unit test usage only + return timer->callback != NULL; +} + +long fluid_timer_get_interval(const fluid_timer_t * timer) +{ + // for unit test usage only + return timer->msec; +} + /*************************************************************** * @@ -1199,7 +1283,7 @@ fluid_timer_join(fluid_timer_t *timer) * Get standard in stream handle. * @return Standard in stream. */ -fluid_istream_t +static fluid_istream_t fluid_get_stdin(void) { return STDIN_FILENO; @@ -1209,7 +1293,7 @@ fluid_get_stdin(void) * Get standard output stream handle. * @return Standard out stream. */ -fluid_ostream_t +static fluid_ostream_t fluid_get_stdout(void) { return STDOUT_FILENO; @@ -1669,3 +1753,50 @@ FILE* fluid_file_open(const char* path, const char** errMsg) return handle; } + +fluid_long_long_t fluid_file_tell(FILE* f) +{ +#ifdef WIN32 + // On Windows, long is only a 32 bit integer. Thus ftell() does not support to handle files >2GiB. + // We should use _ftelli64() in this case, however its availability depends on MS CRT and might not be + // availble on WindowsXP, Win98, etc. + // + // The web recommends to fallback to _telli64() in this case. However, it's return value differs from + // _ftelli64() on Win10: https://github.com/FluidSynth/fluidsynth/pull/629#issuecomment-602238436 + // + // Thus, we use fgetpos(). + fpos_t pos; + if(fgetpos(f, &pos) != 0) + { + return (fluid_long_long_t)-1L; + } + return pos; +#else + return ftell(f); +#endif +} + +#ifdef WIN32 +// not thread-safe! +char* fluid_get_windows_error(void) +{ + static TCHAR err[1024]; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + err, + sizeof(err)/sizeof(err[0]), + NULL); + +#ifdef _UNICODE + static char ascii_err[sizeof(err)]; + + WideCharToMultiByte(CP_UTF8, 0, err, -1, ascii_err, sizeof(ascii_err)/sizeof(ascii_err[0]), 0, 0); + return ascii_err; +#else + return err; +#endif +} +#endif diff --git a/libs/fluidsynth/src/fluid_sys.h b/libs/fluidsynth/src/fluid_sys.h index b747e5676a..05d6c6f200 100644 --- a/libs/fluidsynth/src/fluid_sys.h +++ b/libs/fluidsynth/src/fluid_sys.h @@ -168,9 +168,14 @@ typedef gintptr intptr_t; */ #define fluid_gerror_message(err) ((err) ? err->message : "No error details") +#ifdef WIN32 +char* fluid_get_windows_error(void); +#endif #define FLUID_INLINE inline +#define FLUID_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) + /* Integer<->pointer conversion */ #define FLUID_POINTER_TO_UINT(x) ((unsigned int)(uintptr_t)(x)) #define FLUID_UINT_TO_POINTER(x) ((void *)(uintptr_t)(x)) @@ -231,10 +236,12 @@ fluid_timer_t *new_fluid_timer(int msec, fluid_timer_callback_t callback, void delete_fluid_timer(fluid_timer_t *timer); int fluid_timer_join(fluid_timer_t *timer); int fluid_timer_stop(fluid_timer_t *timer); +int fluid_timer_is_running(const fluid_timer_t *timer); +long fluid_timer_get_interval(const fluid_timer_t * timer); // Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32) -#define NEW_GLIB_THREAD_API (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 32)) -#define OLD_GLIB_THREAD_API (GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32)) +#define NEW_GLIB_THREAD_API GLIB_CHECK_VERSION(2,32,0) +#define OLD_GLIB_THREAD_API !GLIB_CHECK_VERSION(2,32,0) /* Muteces */ @@ -403,19 +410,19 @@ typedef GStaticPrivate fluid_private_t; g_atomic_pointer_compare_and_exchange(_pp, _old, _new) static FLUID_INLINE void -fluid_atomic_float_set(volatile float *fptr, float val) +fluid_atomic_float_set(fluid_atomic_float_t *fptr, float val) { int32_t ival; memcpy(&ival, &val, 4); - fluid_atomic_int_set((volatile int *)fptr, ival); + fluid_atomic_int_set((fluid_atomic_int_t *)fptr, ival); } static FLUID_INLINE float -fluid_atomic_float_get(volatile float *fptr) +fluid_atomic_float_get(fluid_atomic_float_t *fptr) { int32_t ival; float fval; - ival = fluid_atomic_int_get((volatile int *)fptr); + ival = fluid_atomic_int_get((fluid_atomic_int_t *)fptr); memcpy(&fval, &ival, 4); return fval; } @@ -465,7 +472,7 @@ typedef SOCKET fluid_socket_t; typedef int fluid_socket_t; #endif -/* The function should return 0 if no error occured, non-zero +/* The function should return 0 if no error occurred, non-zero otherwise. If the function return non-zero, the socket will be closed by the server. */ typedef int (*fluid_server_func_t)(void *data, fluid_socket_t client_socket, char *addr); @@ -498,10 +505,12 @@ typedef GStatBuf fluid_stat_buf_t; #endif FILE* fluid_file_open(const char* filename, const char** errMsg); +fluid_long_long_t fluid_file_tell(FILE* f); + /* Profiling */ #if WITH_PROFILING -/** profiling interface beetween Profiling command shell and Audio +/** profiling interface between Profiling command shell and Audio rendering API (FluidProfile_0004.pdf- 3.2.2) */ @@ -734,7 +743,7 @@ void fluid_msleep(unsigned int msecs); * Make sure you've allocated an extra of \c alignment bytes to avoid a buffer overflow. * * @note \c alignment must be a power of two - * @return Returned pointer is guarenteed to be aligned to \c alignment boundary and in range \f[ ptr <= returned_ptr < ptr + alignment \f]. + * @return Returned pointer is guaranteed to be aligned to \c alignment boundary and in range \f[ ptr <= returned_ptr < ptr + alignment \f]. */ static FLUID_INLINE void *fluid_align_ptr(const void *ptr, unsigned int alignment) { diff --git a/libs/fluidsynth/src/fluid_tuning.h b/libs/fluidsynth/src/fluid_tuning.h index 3afe2c65ad..542d2ced68 100644 --- a/libs/fluidsynth/src/fluid_tuning.h +++ b/libs/fluidsynth/src/fluid_tuning.h @@ -23,9 +23,9 @@ More information about micro tuning can be found at: - http://www.midi.org/about-midi/tuning.htm - http://www.midi.org/about-midi/tuning-scale.htm - http://www.midi.org/about-midi/tuning_extens.htm + https://www.midi.org/about-midi/tuning.htm + https://www.midi.org/about-midi/tuning-scale.htm + https://www.midi.org/about-midi/tuning_extens.htm */ diff --git a/libs/fluidsynth/src/fluid_voice.c b/libs/fluidsynth/src/fluid_voice.c index d961ae4a7c..2ef0aee066 100644 --- a/libs/fluidsynth/src/fluid_voice.c +++ b/libs/fluidsynth/src/fluid_voice.c @@ -174,6 +174,7 @@ static void fluid_voice_swap_rvoice(fluid_voice_t *voice) voice->can_access_rvoice = voice->can_access_overflow_rvoice; voice->overflow_rvoice = rtemp; voice->can_access_overflow_rvoice = ctemp; + voice->overflow_sample = voice->sample; } static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate) @@ -242,6 +243,7 @@ new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate) voice->eventhandler = handler; voice->channel = NULL; voice->sample = NULL; + voice->overflow_sample = NULL; voice->output_rate = output_rate; /* Initialize both the rvoice and overflow_rvoice */ @@ -321,12 +323,13 @@ fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, voice->has_noteoff = 0; UPDATE_RVOICE0(fluid_rvoice_reset); - /* Increment the reference count of the sample to prevent the - unloading of the soundfont while this voice is playing, - once for us and once for the rvoice. */ + /* + We increment the reference count of the sample to indicate that this + sample is about to be owned by the rvoice. This will prevent the + unloading of the soundfont while this rvoice is playing. + */ fluid_sample_incr_ref(sample); fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample); - fluid_sample_incr_ref(sample); voice->sample = sample; i = fluid_channel_get_interp_method(channel); @@ -367,6 +370,7 @@ fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, /** * Update sample rate. + * * @note If the voice is active, it will be turned off. */ void @@ -385,6 +389,7 @@ fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value) /** * Set the value of a generator. + * * @param voice Voice instance * @param i Generator ID (#fluid_gen_type) * @param val Generator value @@ -403,6 +408,7 @@ fluid_voice_gen_set(fluid_voice_t *voice, int i, float val) /** * Offset the value of a generator. + * * @param voice Voice instance * @param i Generator ID (#fluid_gen_type) * @param val Value to add to the existing value @@ -416,6 +422,7 @@ fluid_voice_gen_incr(fluid_voice_t *voice, int i, float val) /** * Get the value of a generator. + * * @param voice Voice instance * @param gen Generator ID (#fluid_gen_type) * @return Current generator value @@ -473,7 +480,7 @@ fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t ga This is useful to set the value of GEN_PITCH generator on noteOn. This is useful to get the beginning/ending pitch for portamento. */ -fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) +static fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) { fluid_tuning_t *tuning; fluid_real_t x, pitch; @@ -734,13 +741,14 @@ calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, * NRPN system. fluid_voice_gen_value(voice, generator_enumerator) returns the sum * of all three. */ + /** - * Update all the synthesis parameters, which depend on generator \a gen. + * Update all the synthesis parameters which depend on generator \a gen. + * * @param voice Voice instance * @param gen Generator id (#fluid_gen_type) * - * This is only necessary after changing a generator of an already operating voice. - * Most applications will not need this function. + * Calling this function is only necessary after changing a generator of an already playing voice. */ void fluid_voice_update_param(fluid_voice_t *voice, int gen) @@ -1140,8 +1148,9 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen) /** * Recalculate voice parameters for a given control. + * * @param voice the synthesis voice - * @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...) + * @param cc flag to distinguish between a continuous control and a channel control (pitch bend, ...) * @param ctrl the control number: * when >=0, only modulators's destination having ctrl as source are updated. * when -1, all modulators's destination are updated (regardless of ctrl). @@ -1236,17 +1245,18 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) } /** - * Update all the modulators. This function is called after a - * ALL_CTRL_OFF MIDI message has been received (CC 121). + * Update all the modulators. * - * All destination of all modulators must be updated. + * This function is called after a ALL_CTRL_OFF MIDI message has been received (CC 121). + * All destinations of all modulators will be updated. */ int fluid_voice_modulate_all(fluid_voice_t *voice) { return fluid_voice_modulate(voice, 0, -1); } -/** legato update functions --------------------------------------------------*/ +/* legato update functions --------------------------------------------------*/ + /* Updates voice portamento parameters * * @voice voice the synthesis voice @@ -1324,7 +1334,7 @@ fluid_voice_release(fluid_voice_t *voice) { unsigned int at_tick = fluid_channel_get_min_note_length_ticks(voice->channel); UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); - voice->has_noteoff = 1; // voice is marked as noteoff occured + voice->has_noteoff = 1; // voice is marked as noteoff occurred } /* @@ -1406,12 +1416,20 @@ fluid_voice_kill_excl(fluid_voice_t *voice) } /* - * Called by fluid_synth when the overflow rvoice can be reclaimed. + * Unlock the overflow rvoice of the voice. + * Decrement the reference count of the sample owned by this rvoice. + * + * Called by fluid_synth when the overflow rvoice has finished by itself. + * Must be called also explicitly at synth destruction to ensure that + * the soundfont be unloaded successfully. */ void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice) { voice->can_access_overflow_rvoice = 1; - fluid_voice_sample_unref(&voice->overflow_rvoice->dsp.sample); + + /* Decrement the reference count of the sample to indicate + that this sample isn't owned by the rvoice anymore */ + fluid_voice_sample_unref(&voice->overflow_sample); } /* @@ -1439,23 +1457,21 @@ fluid_voice_stop(fluid_voice_t *voice) voice->chan = NO_CHANNEL; - if(voice->can_access_rvoice) - { - fluid_voice_sample_unref(&voice->rvoice->dsp.sample); - } + /* Decrement the reference count of the sample, to indicate + that this sample isn't owned by the rvoice anymore. + */ + fluid_voice_sample_unref(&voice->sample); voice->status = FLUID_VOICE_OFF; voice->has_noteoff = 1; - /* Decrement the reference count of the sample. */ - fluid_voice_sample_unref(&voice->sample); - /* Decrement voice count */ voice->channel->synth->active_voice_count--; } /** * Adds a modulator to the voice if the modulator has valid sources. + * * @param voice Voice instance. * @param mod Modulator info (copied). * @param mode Determines how to handle an existing identical modulator. @@ -1484,7 +1500,7 @@ fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode) * are checked for identity. * - When check_count_limit is below the actual number of voices modulators * (voice->mod_count), this will restrict identity check to this number, - * This is usefull when we know by advance that there is no duplicate with + * This is useful when we know by advance that there is no duplicate with * modulators at index above this limit. This avoid wasting cpu cycles at noteon. */ void @@ -1543,15 +1559,16 @@ fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int /** * Get the unique ID of the noteon-event. + * * @param voice Voice instance * @return Note on unique ID * - * A SoundFont loader may store the voice processes it has created for + * A SoundFont loader may store pointers to voices it has created for * real-time control during the operation of a voice (for example: parameter - * changes in SoundFont editor). The synth uses a pool of voices, which are + * changes in SoundFont editor). The synth uses a pool of voices internally which are * 'recycled' and never deallocated. * - * Before modifying an existing voice, check + * However, before modifying an existing voice, check * - that its state is still 'playing' * - that the ID is still the same * @@ -1563,7 +1580,14 @@ unsigned int fluid_voice_get_id(const fluid_voice_t *voice) } /** - * Check if a voice is producing sound. This is also true after a voice received a noteoff as it may be playing in release phase. + * Check if a voice is producing sound. + * + * Like fluid_voice_is_on() this will return TRUE once a call to + * fluid_synth_start_voice() has been made. Contrary to fluid_voice_is_on(), + * this might also return TRUE after the voice received a noteoff event, as it may + * still be playing in release phase, or because it has been sustained or + * sostenuto'ed. + * * @param voice Voice instance * @return TRUE if playing, FALSE otherwise */ @@ -1576,9 +1600,15 @@ int fluid_voice_is_playing(const fluid_voice_t *voice) } /** - * Check if a voice is ON. A voice is ON, if it has not yet received a noteoff event. + * Check if a voice is ON. + * + * A voice is in ON state as soon as a call to fluid_synth_start_voice() has been made + * (which is typically done in a fluid_preset_t's noteon function). + * A voice stays ON as long as it has not received a noteoff event. + * * @param voice Voice instance * @return TRUE if on, FALSE otherwise + * * @since 1.1.7 */ int fluid_voice_is_on(const fluid_voice_t *voice) @@ -1588,8 +1618,10 @@ int fluid_voice_is_on(const fluid_voice_t *voice) /** * Check if a voice keeps playing after it has received a noteoff due to being held by sustain. + * * @param voice Voice instance * @return TRUE if sustained, FALSE otherwise + * * @since 1.1.7 */ int fluid_voice_is_sustained(const fluid_voice_t *voice) @@ -1599,8 +1631,10 @@ int fluid_voice_is_sustained(const fluid_voice_t *voice) /** * Check if a voice keeps playing after it has received a noteoff due to being held by sostenuto. + * * @param voice Voice instance * @return TRUE if sostenuto, FALSE otherwise + * * @since 1.1.7 */ int fluid_voice_is_sostenuto(const fluid_voice_t *voice) @@ -1609,9 +1643,13 @@ int fluid_voice_is_sostenuto(const fluid_voice_t *voice) } /** - * If the voice is playing, gets the midi channel the voice is playing on. Else the result is undefined. + * Return the MIDI channel the voice is playing on. + * * @param voice Voice instance * @return The channel assigned to this voice + * + * @note The result of this function is only valid if the voice is playing. + * * @since 1.1.7 */ int fluid_voice_get_channel(const fluid_voice_t *voice) @@ -1620,11 +1658,16 @@ int fluid_voice_get_channel(const fluid_voice_t *voice) } /** - * If the voice is playing, gets the midi key the voice is actually playing at. Else the result is undefined. - * If the voice was started from an instrument which uses a fixed key generator, it returns that. - * Else returns the same as \c fluid_voice_get_key. + * Return the effective MIDI key of the playing voice. + * * @param voice Voice instance - * @return The midi key this voice is playing at + * @return The MIDI key this voice is playing at + * + * If the voice was started from an instrument which uses a fixed key generator, it returns that. + * Otherwise returns the same value as \c fluid_voice_get_key. + * + * @note The result of this function is only valid if the voice is playing. + * * @since 1.1.7 */ int fluid_voice_get_actual_key(const fluid_voice_t *voice) @@ -1642,10 +1685,13 @@ int fluid_voice_get_actual_key(const fluid_voice_t *voice) } /** - * If the voice is playing, gets the midi key from the noteon event, by which the voice was initially turned on with. - * Else the result is undefined. + * Return the MIDI key from the starting noteon event. + * * @param voice Voice instance - * @return The midi key of the noteon event that originally turned on this voice + * @return The MIDI key of the noteon event that originally turned on this voice + * + * @note The result of this function is only valid if the voice is playing. + * * @since 1.1.7 */ int fluid_voice_get_key(const fluid_voice_t *voice) @@ -1654,11 +1700,16 @@ int fluid_voice_get_key(const fluid_voice_t *voice) } /** - * If the voice is playing, gets the midi velocity the voice is actually playing at. Else the result is undefined. - * If the voice was started from an instrument which uses a fixed velocity generator, it returns that. - * Else returns the same as \c fluid_voice_get_velocity. + * Return the effective MIDI velocity of the playing voice. + * * @param voice Voice instance - * @return The midi velocity this voice is playing at + * @return The MIDI velocity this voice is playing at + * + * If the voice was started from an instrument which uses a fixed velocity generator, it returns that. + * Otherwise it returns the same value as \c fluid_voice_get_velocity. + * + * @note The result of this function is only valid if the voice is playing. + * * @since 1.1.7 */ int fluid_voice_get_actual_velocity(const fluid_voice_t *voice) @@ -1676,10 +1727,13 @@ int fluid_voice_get_actual_velocity(const fluid_voice_t *voice) } /** - * If the voice is playing, gets the midi velocity from the noteon event, by which the voice was initially - * turned on with. Else the result is undefined. + * Return the MIDI velocity from the starting noteon event. + * * @param voice Voice instance - * @return The midi velocity which originally turned on this voice + * @return The MIDI velocity which originally turned on this voice + * + * @note The result of this function is only valid if the voice is playing. + * * @since 1.1.7 */ int fluid_voice_get_velocity(const fluid_voice_t *voice) @@ -1819,8 +1873,10 @@ int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain) * - Calculate, what factor will make the loop inaudible * - Store in sample */ + /** * Calculate the peak volume of a sample for voice off optimization. + * * @param s Sample to optimize * @return #FLUID_OK on success, #FLUID_FAILED otherwise * diff --git a/libs/fluidsynth/src/fluid_voice.h b/libs/fluidsynth/src/fluid_voice.h index 599a0e28a5..4ce6c2b7ab 100644 --- a/libs/fluidsynth/src/fluid_voice.h +++ b/libs/fluidsynth/src/fluid_voice.h @@ -71,7 +71,8 @@ struct _fluid_voice_t fluid_channel_t *channel; fluid_rvoice_eventhandler_t *eventhandler; fluid_zone_range_t *zone_range; /* instrument zone range*/ - fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ + fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ + fluid_sample_t *overflow_sample; /* Pointer to sample (dupe in overflow_rvoice) */ unsigned int start_time; int mod_count; diff --git a/libs/fluidsynth/src/fluidsynth_priv.h b/libs/fluidsynth/src/fluidsynth_priv.h index 71569bd045..f8f36381ff 100644 --- a/libs/fluidsynth/src/fluidsynth_priv.h +++ b/libs/fluidsynth/src/fluidsynth_priv.h @@ -98,7 +98,7 @@ typedef union _fluid_rvoice_param_t int i; fluid_real_t real; } fluid_rvoice_param_t; -enum { MAX_EVENT_PARAMS = 6 }; /**< Maximum number of #fluid_rvoice_param_t to be passed to an #fluid_rvoice_function_t */ +enum { MAX_EVENT_PARAMS = 7 }; /**< Maximum number of #fluid_rvoice_param_t to be passed to an #fluid_rvoice_function_t */ typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); /* Macro for declaring an rvoice event function (#fluid_rvoice_function_t). The functions may only access @@ -191,11 +191,19 @@ typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t pa void* fluid_alloc(size_t len); /* File access */ -#define FLUID_FOPEN(_f,_m) fopen(_f,_m) +#define FLUID_FOPEN(_f,_m) fluid_fopen(_f,_m) #define FLUID_FCLOSE(_f) fclose(_f) #define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) + +FILE *fluid_fopen(const char *filename, const char *mode); + +#ifdef WIN32 +#define FLUID_FSEEK(_f,_n,_set) _fseeki64(_f,_n,_set) +#else #define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set) -#define FLUID_FTELL(_f) ftell(_f) +#endif + +#define FLUID_FTELL(_f) fluid_file_tell(_f) /* Memory functions */ #define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) @@ -206,9 +214,10 @@ void* fluid_alloc(size_t len); #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_STRTOL(_s,_e,_b) strtol(_s,_e,_b) #define FLUID_STRNCPY(_dst,_src,_n) \ -do { strncpy(_dst,_src,_n); \ +do { strncpy(_dst,_src,_n-1); \ (_dst)[(_n)-1]='\0'; \ }while(0) @@ -226,8 +235,8 @@ do { strncpy(_dst,_src,_n); \ #if (defined(WIN32) && _MSC_VER < 1900) || defined(MINGW32) /* need to make sure we use a C99 compliant implementation of (v)snprintf(), - * i.e. not microsofts non compliant extension _snprintf() as it doesnt - * reliably null-terminates the buffer + * i.e. not microsofts non compliant extension _snprintf() as it doesn't + * reliably null-terminate the buffer */ #define FLUID_SNPRINTF g_snprintf #else diff --git a/tools/fluid-patches/ardour_fluidsynth.diff b/tools/fluid-patches/ardour_fluidsynth.diff index 8aaf597687..88d08c175c 100644 --- a/tools/fluid-patches/ardour_fluidsynth.diff +++ b/tools/fluid-patches/ardour_fluidsynth.diff @@ -1,49 +1,44 @@ +diff --git b/libs/fluidsynth/fluidsynth/settings.h a/libs/fluidsynth/fluidsynth/settings.h +index a8b3cb85ec..aba86e3379 100644 +--- b/libs/fluidsynth/fluidsynth/settings.h ++++ a/libs/fluidsynth/fluidsynth/settings.h +@@ -123,7 +123,7 @@ FLUIDSYNTH_API + int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str); + + FLUIDSYNTH_API +-int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def); ++int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char const **def); + + FLUIDSYNTH_API + int fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *value); diff --git b/libs/fluidsynth/fluidsynth/synth.h a/libs/fluidsynth/fluidsynth/synth.h -index f4802ee5b9..3003972542 100644 +index b8d0b0ab1e..126532554f 100644 --- b/libs/fluidsynth/fluidsynth/synth.h +++ a/libs/fluidsynth/fluidsynth/synth.h -@@ -176,7 +176,7 @@ FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth); +@@ -21,6 +21,7 @@ + #ifndef _FLUIDSYNTH_SYNTH_H + #define _FLUIDSYNTH_SYNTH_H - /* Synthesis parameters */ ++#define FLUID_DEPRECATED --FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate); -+FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate); - FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t *synth, float gain); - FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t *synth); - FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony); -@@ -233,7 +233,7 @@ FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int p - /* Misc */ - - FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); --FLUID_DEPRECATED FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); -+const char *fluid_synth_error(fluid_synth_t *synth); - - - /* Default modulators */ -@@ -265,7 +265,7 @@ FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t *synth, int len, - 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); --FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len, -+FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len, - float **left, float **right, - float **fx_left, float **fx_right); - FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t *synth, int len, -@@ -310,7 +310,9 @@ FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int - - /* LADSPA */ + #ifdef __cplusplus + extern "C" { +@@ -530,8 +531,10 @@ int fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int + FLUIDSYNTH_API + int fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); +#ifdef LADSPA + /** @ingroup ladspa */ FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); +#endif - - /* API: Poly mono mode */ + #ifdef __cplusplus + } diff --git b/libs/fluidsynth/fluidsynth/types.h a/libs/fluidsynth/fluidsynth/types.h -index 47ef18336a..5ad29281ad 100644 +index 4352b4c573..6c7994fe83 100644 --- b/libs/fluidsynth/fluidsynth/types.h +++ a/libs/fluidsynth/fluidsynth/types.h -@@ -56,7 +56,9 @@ typedef struct _fluid_sequencer_t fluid_sequencer_t; /**< Sequencer i +@@ -58,7 +58,9 @@ typedef struct _fluid_sequencer_t fluid_sequencer_t; /**< Sequencer i typedef struct _fluid_ramsfont_t fluid_ramsfont_t; /**< RAM SoundFont */ typedef struct _fluid_rampreset_t fluid_rampreset_t; /**< RAM SoundFont preset */ typedef struct _fluid_cmd_handler_t fluid_cmd_handler_t; /**< Shell Command Handler */ @@ -66,8 +61,21 @@ index 60f441c49f..e6455186eb 100644 fluid_real_t fluid_ct2hz_real(fluid_real_t cents); fluid_real_t fluid_ct2hz(fluid_real_t cents); +diff --git b/libs/fluidsynth/src/fluid_gen.h a/libs/fluidsynth/src/fluid_gen.h +index b87e8d8a8c..75a4f39e8a 100644 +--- b/libs/fluidsynth/src/fluid_gen.h ++++ a/libs/fluidsynth/src/fluid_gen.h +@@ -27,7 +27,7 @@ + typedef struct _fluid_gen_info_t + { + char num; /* Generator number */ +- char *name; ++ char const *name; + char init; /* Does the generator need to be initialized (not used) */ + char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ + float min; /* The minimum value */ diff --git b/libs/fluidsynth/src/fluid_hash.c a/libs/fluidsynth/src/fluid_hash.c -index 946a873bbf..79f83a4583 100644 +index 7efd0dedda..46f701c4ba 100644 --- b/libs/fluidsynth/src/fluid_hash.c +++ a/libs/fluidsynth/src/fluid_hash.c @@ -991,6 +991,7 @@ fluid_hashtable_remove_all(fluid_hashtable_t *hashtable) @@ -87,10 +95,10 @@ index 946a873bbf..79f83a4583 100644 /* * fluid_hashtable_foreach_remove_or_steal: diff --git b/libs/fluidsynth/src/fluid_midi.c a/libs/fluidsynth/src/fluid_midi.c -index ea1aff5202..844de01c8f 100644 +index 796b278fea..830aada199 100644 --- b/libs/fluidsynth/src/fluid_midi.c +++ a/libs/fluidsynth/src/fluid_midi.c -@@ -77,7 +77,7 @@ static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); +@@ -72,7 +72,7 @@ static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); static int fluid_midi_file_eot(fluid_midi_file *mf); static int fluid_midi_file_get_division(fluid_midi_file *midifile); @@ -99,7 +107,7 @@ index ea1aff5202..844de01c8f 100644 /*************************************************************** * * MIDIFILE -@@ -1048,6 +1048,7 @@ fluid_midi_file_get_division(fluid_midi_file *midifile) +@@ -1042,6 +1042,7 @@ fluid_midi_file_get_division(fluid_midi_file *midifile) { return midifile->division; } @@ -107,7 +115,7 @@ index ea1aff5202..844de01c8f 100644 /****************************************************** * -@@ -1414,7 +1415,7 @@ static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **dat +@@ -1408,7 +1409,7 @@ static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **dat * * fluid_track_t */ @@ -116,16 +124,16 @@ index ea1aff5202..844de01c8f 100644 /* * new_fluid_track */ -@@ -2504,3 +2505,4 @@ fluid_midi_event_length(unsigned char event) +@@ -2728,3 +2729,4 @@ fluid_midi_event_length(unsigned char event) return 1; } +#endif diff --git b/libs/fluidsynth/src/fluid_mod.c a/libs/fluidsynth/src/fluid_mod.c -index 47547b5b5e..57313caf42 100644 +index effa202750..3b2a827814 100644 --- b/libs/fluidsynth/src/fluid_mod.c +++ a/libs/fluidsynth/src/fluid_mod.c -@@ -603,7 +603,7 @@ fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select) +@@ -620,7 +620,7 @@ fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select) * @param name,if not NULL, pointer on a string displayed as a warning. * @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise. */ @@ -148,7 +156,7 @@ index 3e7661741f..ec8e967a35 100644 #ifdef DEBUG void fluid_dump_modulator(fluid_mod_t *mod); diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.c a/libs/fluidsynth/src/fluid_rvoice_mixer.c -index 257f0fbdec..d7fd2f541f 100644 +index 0b2d16066f..9bf7aec7bf 100644 --- b/libs/fluidsynth/src/fluid_rvoice_mixer.c +++ a/libs/fluidsynth/src/fluid_rvoice_mixer.c @@ -23,7 +23,6 @@ @@ -160,7 +168,7 @@ index 257f0fbdec..d7fd2f541f 100644 diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.h a/libs/fluidsynth/src/fluid_rvoice_mixer.h -index 4ee072e4b9..1b3fceb342 100644 +index 63a456ce19..6139081185 100644 --- b/libs/fluidsynth/src/fluid_rvoice_mixer.h +++ a/libs/fluidsynth/src/fluid_rvoice_mixer.h @@ -24,7 +24,6 @@ @@ -172,7 +180,7 @@ index 4ee072e4b9..1b3fceb342 100644 typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t; diff --git b/libs/fluidsynth/src/fluid_settings.c a/libs/fluidsynth/src/fluid_settings.c -index 78532ad2a3..a825603a44 100644 +index 2d9f7b10aa..d5c6b940f3 100644 --- b/libs/fluidsynth/src/fluid_settings.c +++ a/libs/fluidsynth/src/fluid_settings.c @@ -21,9 +21,6 @@ @@ -185,7 +193,7 @@ index 78532ad2a3..a825603a44 100644 #include "fluid_settings.h" #include "fluid_midi.h" -@@ -328,11 +325,13 @@ fluid_settings_init(fluid_settings_t *settings) +@@ -330,11 +327,13 @@ fluid_settings_init(fluid_settings_t *settings) fluid_return_if_fail(settings != NULL); fluid_synth_settings(settings); @@ -199,8 +207,64 @@ index 78532ad2a3..a825603a44 100644 } static int +@@ -1215,10 +1214,10 @@ fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const cha + * @note The returned string is not owned by the caller and should not be modified or freed. + */ + int +-fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def) ++fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char const **def) + { + fluid_setting_node_t *node; +- char *retval = NULL; ++ char const *retval = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); +diff --git b/libs/fluidsynth/src/fluid_sfont.c a/libs/fluidsynth/src/fluid_sfont.c +index f5de0a5bdf..94844f84bf 100644 +--- b/libs/fluidsynth/src/fluid_sfont.c ++++ a/libs/fluidsynth/src/fluid_sfont.c +@@ -22,7 +22,7 @@ + #include "fluid_sys.h" + + +-void *default_fopen(const char *path) ++static void *default_fopen(const char *path) + { + const char* msg; + FILE* handle = fluid_file_open(path, &msg); +@@ -35,17 +35,17 @@ void *default_fopen(const char *path) + return handle; + } + +-int default_fclose(void *handle) ++static int default_fclose(void *handle) + { + return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED; + } + +-fluid_long_long_t default_ftell(void *handle) ++static fluid_long_long_t default_ftell(void *handle) + { + return FLUID_FTELL((FILE *)handle); + } + +-int safe_fread(void *buf, fluid_long_long_t count, void *fd) ++static int safe_fread(void *buf, fluid_long_long_t count, void *fd) + { + if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1) + { +@@ -64,7 +64,7 @@ int safe_fread(void *buf, fluid_long_long_t count, void *fd) + return FLUID_OK; + } + +-int safe_fseek(void *fd, fluid_long_long_t ofs, int whence) ++static int safe_fseek(void *fd, fluid_long_long_t ofs, int whence) + { + if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0) + { diff --git b/libs/fluidsynth/src/fluid_synth.c a/libs/fluidsynth/src/fluid_synth.c -index e03c64089f..382979f7f5 100644 +index 89def2fd03..27e7022c5b 100644 --- b/libs/fluidsynth/src/fluid_synth.c +++ a/libs/fluidsynth/src/fluid_synth.c @@ -25,7 +25,6 @@ @@ -211,7 +275,7 @@ index e03c64089f..382979f7f5 100644 #ifdef TRAP_ON_FPE #define _GNU_SOURCE -@@ -262,7 +261,7 @@ void fluid_version(int *major, int *minor, int *micro) +@@ -271,7 +270,7 @@ void fluid_version(int *major, int *minor, int *micro) * @return FluidSynth version string, which is internal and should not be * modified or freed. */ @@ -220,7 +284,7 @@ index e03c64089f..382979f7f5 100644 fluid_version_str(void) { return FLUIDSYNTH_VERSION; -@@ -5558,7 +5557,7 @@ fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method) +@@ -6748,7 +6747,7 @@ fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method) } FLUID_API_RETURN(FLUID_OK); @@ -229,7 +293,7 @@ index e03c64089f..382979f7f5 100644 /** * Get the total count of MIDI channels. -@@ -6496,6 +6495,7 @@ int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type) +@@ -7709,6 +7708,7 @@ int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type) FLUID_API_RETURN(FLUID_OK); } @@ -237,7 +301,7 @@ index e03c64089f..382979f7f5 100644 /** * Return the LADSPA effects instance used by FluidSynth * -@@ -6508,6 +6508,7 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) +@@ -7721,6 +7721,7 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) return synth->ladspa_fx; } @@ -246,7 +310,7 @@ index e03c64089f..382979f7f5 100644 /** * Configure a general-purpose IIR biquad filter. diff --git b/libs/fluidsynth/src/fluid_synth.h a/libs/fluidsynth/src/fluid_synth.h -index b649bcf340..955b3fa12e 100644 +index 132a98ddae..9b0078f04b 100644 --- b/libs/fluidsynth/src/fluid_synth.h +++ a/libs/fluidsynth/src/fluid_synth.h @@ -33,8 +33,6 @@ @@ -258,7 +322,7 @@ index b649bcf340..955b3fa12e 100644 #include "fluid_rvoice_event.h" /*************************************************************** -@@ -165,7 +163,9 @@ struct _fluid_synth_t +@@ -161,7 +159,9 @@ struct _fluid_synth_t fluid_mod_t *default_mod; /**< the (dynamic) list of default modulators */ @@ -269,10 +333,10 @@ index b649bcf340..955b3fa12e 100644 enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */ }; diff --git b/libs/fluidsynth/src/fluid_sys.c a/libs/fluidsynth/src/fluid_sys.c -index 7454217dd2..c6300b90c2 100644 +index d5a8452963..609af21f36 100644 --- b/libs/fluidsynth/src/fluid_sys.c +++ a/libs/fluidsynth/src/fluid_sys.c -@@ -239,9 +239,10 @@ void fluid_free(void* ptr) +@@ -309,9 +309,10 @@ void fluid_free(void* ptr) * @param delim String of delimiter chars. * @return Pointer to the next token or NULL if no more tokens. */ @@ -285,8 +349,26 @@ index 7454217dd2..c6300b90c2 100644 char c; if(str == NULL || delim == NULL || !*delim) +@@ -1282,7 +1283,7 @@ long fluid_timer_get_interval(const fluid_timer_t * timer) + * Get standard in stream handle. + * @return Standard in stream. + */ +-fluid_istream_t ++static fluid_istream_t + fluid_get_stdin(void) + { + return STDIN_FILENO; +@@ -1292,7 +1293,7 @@ fluid_get_stdin(void) + * Get standard output stream handle. + * @return Standard out stream. + */ +-fluid_ostream_t ++static fluid_ostream_t + fluid_get_stdout(void) + { + return STDOUT_FILENO; diff --git b/libs/fluidsynth/src/fluid_sys.h a/libs/fluidsynth/src/fluid_sys.h -index 24df6edb5b..b747e5676a 100644 +index 86a47f32dc..05d6c6f200 100644 --- b/libs/fluidsynth/src/fluid_sys.h +++ a/libs/fluidsynth/src/fluid_sys.h @@ -130,8 +130,9 @@ typedef gintptr intptr_t; @@ -301,7 +383,7 @@ index 24df6edb5b..b747e5676a 100644 /* WIN32 special defines */ #define STDIN_FILENO 0 -@@ -193,7 +194,7 @@ typedef gintptr intptr_t; +@@ -198,7 +199,7 @@ char* fluid_get_windows_error(void); /* * Utility functions */ @@ -310,3 +392,16 @@ index 24df6edb5b..b747e5676a 100644 #if defined(__OS2__) +diff --git b/libs/fluidsynth/src/fluid_voice.c a/libs/fluidsynth/src/fluid_voice.c +index 47f28d2a50..2ef0aee066 100644 +--- b/libs/fluidsynth/src/fluid_voice.c ++++ a/libs/fluidsynth/src/fluid_voice.c +@@ -480,7 +480,7 @@ fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t ga + This is useful to set the value of GEN_PITCH generator on noteOn. + This is useful to get the beginning/ending pitch for portamento. + */ +-fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) ++static fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) + { + fluid_tuning_t *tuning; + fluid_real_t x, pitch; diff --git a/tools/fluid-patches/fluid_conv_tables.c b/tools/fluid-patches/fluid_conv_tables.inc.h similarity index 100% rename from tools/fluid-patches/fluid_conv_tables.c rename to tools/fluid-patches/fluid_conv_tables.inc.h diff --git a/tools/fluid-patches/fluid_rvoice_dsp_tables.c b/tools/fluid-patches/fluid_rvoice_dsp_tables.inc.h similarity index 100% rename from tools/fluid-patches/fluid_rvoice_dsp_tables.c rename to tools/fluid-patches/fluid_rvoice_dsp_tables.inc.h diff --git a/tools/update_fluidsynth.sh b/tools/update_fluidsynth.sh index 1fc43f50b0..a5e33b8107 100755 --- a/tools/update_fluidsynth.sh +++ b/tools/update_fluidsynth.sh @@ -115,5 +115,5 @@ cd "$ASRC" patch -p1 < tools/fluid-patches/ardour_fluidsynth.diff # auto-generated files -cp tools/fluid-patches/fluid_conv_tables.c libs/fluidsynth/src/ -cp tools/fluid-patches/fluid_rvoice_dsp_tables.c libs/fluidsynth/src/ +cp tools/fluid-patches/fluid_conv_tables.inc.h libs/fluidsynth/src/ +cp tools/fluid-patches/fluid_rvoice_dsp_tables.inc.h libs/fluidsynth/src/