Update to fluidsynth-2.2

This commit is contained in:
Robin Gareus 2021-04-18 21:25:20 +02:00
parent dd060b04dc
commit 71788ecfe4
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
62 changed files with 4719 additions and 2064 deletions

View File

@ -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.

View File

@ -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 <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1
@ -47,42 +47,39 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* whether or not we are supporting ladcca */
/* #undef HAVE_LADCCA */
/* Define to 1 if you have the <io.h> 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 <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define to 1 if you have the <linux/soundcard.h> header file. */
/* #undef HAVE_LINUX_SOUNDCARD_H */
/* Define to 1 if you have the <machine/soundcard.h> header file. */
/* #undef HAVE_MACHINE_SOUNDCARD_H */
/* Define to 1 if you have the <math.h> header file. */
#define HAVE_MATH_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <MidiShare.h> header file. */
/* #undef HAVE_MIDISHARE_H */
/* Define to 1 if you have the <netinet/in.h> header file. */
/* #undef HAVE_NETINET_IN_H */
/* Define to 1 if you have the <netinet/tcp.h> 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 <pthread.h> header file. */
#define HAVE_PTHREAD_H 1
@ -143,12 +140,27 @@
/* Define to 1 if you have the <getopt.h> 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 */

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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]);
}
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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,

View File

@ -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)

View File

@ -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];
};

View File

@ -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:
*

View File

@ -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)
{

View File

@ -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 *);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 *

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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)

View File

@ -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

View File

@ -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)
{

View File

@ -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
*/

View File

@ -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
*

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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/