summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/fluidsynth/README8
-rw-r--r--libs/fluidsynth/config.h225
-rw-r--r--libs/fluidsynth/fluidsynth/event.h135
-rw-r--r--libs/fluidsynth/fluidsynth/fluidsynth.h38
-rw-r--r--libs/fluidsynth/fluidsynth/gen.h135
-rw-r--r--libs/fluidsynth/fluidsynth/log.h84
-rw-r--r--libs/fluidsynth/fluidsynth/midi.h141
-rw-r--r--libs/fluidsynth/fluidsynth/misc.h77
-rw-r--r--libs/fluidsynth/fluidsynth/mod.h112
-rw-r--r--libs/fluidsynth/fluidsynth/settings.h219
-rw-r--r--libs/fluidsynth/fluidsynth/sfont.h281
-rw-r--r--libs/fluidsynth/fluidsynth/synth.h315
-rw-r--r--libs/fluidsynth/fluidsynth/types.h68
-rw-r--r--libs/fluidsynth/fluidsynth/voice.h64
-rw-r--r--libs/fluidsynth/src/fluid_adsr_env.c38
-rw-r--r--libs/fluidsynth/src/fluid_adsr_env.h162
-rw-r--r--libs/fluidsynth/src/fluid_chan.c291
-rw-r--r--libs/fluidsynth/src/fluid_chan.h149
-rw-r--r--libs/fluidsynth/src/fluid_chorus.c506
-rw-r--r--libs/fluidsynth/src/fluid_chorus.h61
-rw-r--r--libs/fluidsynth/src/fluid_conv.c320
-rw-r--r--libs/fluidsynth/src/fluid_conv.h63
-rw-r--r--libs/fluidsynth/src/fluid_defsfont.c3432
-rw-r--r--libs/fluidsynth/src/fluid_defsfont.h530
-rw-r--r--libs/fluidsynth/src/fluid_event.c781
-rw-r--r--libs/fluidsynth/src/fluid_event_priv.h83
-rw-r--r--libs/fluidsynth/src/fluid_event_queue.h195
-rw-r--r--libs/fluidsynth/src/fluid_gen.c149
-rw-r--r--libs/fluidsynth/src/fluid_gen.h44
-rw-r--r--libs/fluidsynth/src/fluid_hash.c1310
-rw-r--r--libs/fluidsynth/src/fluid_hash.h131
-rw-r--r--libs/fluidsynth/src/fluid_iir_filter.c301
-rw-r--r--libs/fluidsynth/src/fluid_iir_filter.h75
-rw-r--r--libs/fluidsynth/src/fluid_lfo.c13
-rw-r--r--libs/fluidsynth/src/fluid_lfo.h72
-rw-r--r--libs/fluidsynth/src/fluid_list.c268
-rw-r--r--libs/fluidsynth/src/fluid_list.h62
-rw-r--r--libs/fluidsynth/src/fluid_midi.c1942
-rw-r--r--libs/fluidsynth/src/fluid_midi.h382
-rw-r--r--libs/fluidsynth/src/fluid_mod.c488
-rw-r--r--libs/fluidsynth/src/fluid_mod.h40
-rw-r--r--libs/fluidsynth/src/fluid_phase.h117
-rw-r--r--libs/fluidsynth/src/fluid_rev.c544
-rw-r--r--libs/fluidsynth/src/fluid_rev.h73
-rw-r--r--libs/fluidsynth/src/fluid_ringbuffer.c89
-rw-r--r--libs/fluidsynth/src/fluid_ringbuffer.h128
-rw-r--r--libs/fluidsynth/src/fluid_rvoice.c664
-rw-r--r--libs/fluidsynth/src/fluid_rvoice.h200
-rw-r--r--libs/fluidsynth/src/fluid_rvoice_dsp.c675
-rw-r--r--libs/fluidsynth/src/fluid_rvoice_event.c293
-rw-r--r--libs/fluidsynth/src/fluid_rvoice_event.h115
-rw-r--r--libs/fluidsynth/src/fluid_rvoice_mixer.c974
-rw-r--r--libs/fluidsynth/src/fluid_rvoice_mixer.h74
-rw-r--r--libs/fluidsynth/src/fluid_settings.c1602
-rw-r--r--libs/fluidsynth/src/fluid_settings.h56
-rw-r--r--libs/fluidsynth/src/fluid_sfont.h68
-rw-r--r--libs/fluidsynth/src/fluid_synth.c5053
-rw-r--r--libs/fluidsynth/src/fluid_synth.h237
-rw-r--r--libs/fluidsynth/src/fluid_sys.c1296
-rw-r--r--libs/fluidsynth/src/fluid_sys.h448
-rw-r--r--libs/fluidsynth/src/fluid_tuning.c174
-rw-r--r--libs/fluidsynth/src/fluid_tuning.h68
-rw-r--r--libs/fluidsynth/src/fluid_voice.c1626
-rw-r--r--libs/fluidsynth/src/fluid_voice.h228
-rw-r--r--libs/fluidsynth/src/fluidsynth_priv.h267
-rw-r--r--libs/fluidsynth/wscript79
-rw-r--r--tools/ardour_fluidsynth.diff252
-rwxr-xr-xtools/update_fluidsynth.sh99
-rw-r--r--wscript1
69 files changed, 29290 insertions, 0 deletions
diff --git a/libs/fluidsynth/README b/libs/fluidsynth/README
new file mode 100644
index 0000000000..6032b9c299
--- /dev/null
+++ b/libs/fluidsynth/README
@@ -0,0 +1,8 @@
+This is a stripped down version of fluidsynth (library only)
+
+from git://git.code.sf.net/p/fluidsynth/code-git
+revisition f52597be038a5a045fc74b6f96d5f9b0bbbbc044 from May/2015
+imported into Ardour Aug/2016
+
+fluidsynth is licensed in terms of the LGPL-2+, see individual source
+files for (C) holders.
diff --git a/libs/fluidsynth/config.h b/libs/fluidsynth/config.h
new file mode 100644
index 0000000000..3df4483053
--- /dev/null
+++ b/libs/fluidsynth/config.h
@@ -0,0 +1,225 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define FLUIDSYNTH_VERSION_MAJOR 1
+#define FLUIDSYNTH_VERSION_MINOR 0
+#define FLUIDSYNTH_VERSION_MICRO 6
+#define FLUIDSYNTH_VERSION "1.0.6"
+
+/* Define to enable ALSA driver */
+/* #undef ALSA_SUPPORT */
+
+/* Define to activate sound output to files */
+/* #undef AUFILE_SUPPORT */
+
+/* whether or not we are supporting CoreAudio */
+/* #undef COREAUDIO_SUPPORT */
+
+/* whether or not we are supporting CoreMIDI */
+/* #undef COREMIDI_SUPPORT */
+
+/* whether or not we are supporting DART */
+/* #undef DART_SUPPORT */
+
+/* Define if building for Mac OS X Darwin */
+/* #undef DARWIN */
+
+/* Define if D-Bus support is enabled */
+/* #undef DBUS_SUPPORT 1 */
+
+/* Define to enable FPE checks */
+/* #undef FPE_CHECK */
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#ifndef _WIN32
+# 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
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* 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 */
+
+/* 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 to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* 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 to 1 if you have the <pthread.h> header file. */
+#define HAVE_PTHREAD_H 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+/* #undef HAVE_SIGNAL_H */
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+/* #undef HAVE_STDARG_H */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#ifndef _WIN32
+# define HAVE_SYS_MMAN_H 1
+#endif
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#ifndef _WIN32
+# define HAVE_SYS_SOCKET_H 1
+#endif
+
+/* Define to 1 if you have the <sys/soundcard.h> header file. */
+/* #undef HAVE_SYS_SOUNDCARD_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+#ifdef _WIN32
+# define HAVE_WINDOWS_H 1
+#endif
+
+/* Define to 1 if you have the <getopt.h> header file. */
+/* #undef HAVE_GETOPT_H */
+
+/* Define to enable JACK driver */
+/* #undef JACK_SUPPORT */
+
+/* Include the LADSPA Fx unit */
+/* #undef LADSPA */
+
+/* libsndfile has ogg vorbis support */
+/* #undef LIBSNDFILE_HASVORBIS */
+
+/* Define to enable libsndfile support */
+/* #undef LIBSNDFILE_SUPPORT */
+
+/* Define to enable MidiShare driver */
+/* #undef MIDISHARE_SUPPORT */
+
+/* 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 */
+
+/* Name of package */
+#define PACKAGE "fluidsynth"
+
+/* Define to the address where bug reports for this package should be sent. */
+/* #undef PACKAGE_BUGREPORT */
+
+/* Define to the full name of this package. */
+/* #undef PACKAGE_NAME */
+
+/* Define to the full name and version of this package. */
+/* #undef PACKAGE_STRING */
+
+/* Define to the one symbol short name of this package. */
+/* #undef PACKAGE_TARNAME */
+
+/* Define to the version of this package. */
+/* #undef PACKAGE_VERSION */
+
+/* Define to enable PortAudio driver */
+/* #undef PORTAUDIO_SUPPORT */
+
+/* Define to enable PulseAudio driver */
+/* #undef PULSE_SUPPORT */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to enable SIGFPE assertions */
+/* #undef TRAP_ON_FPE */
+
+/* Version number of package */
+#define VERSION "1.1.6"
+
+/* Define to do all DSP in single floating point precision */
+/* #undef WITH_FLOAT */
+
+/* Define to profile the DSP code */
+/* #undef WITH_PROFILING */
+
+/* Define to use the readline library for line editing */
+/* #undef WITH_READLINE */
+
+/* Define if the compiler supports VLA */
+#define SUPPORTS_VLA 1
+
+/* Define to 1 if your processor stores words with the most significant byte
+ first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+#endif /* CONFIG_H */
diff --git a/libs/fluidsynth/fluidsynth/event.h b/libs/fluidsynth/fluidsynth/event.h
new file mode 100644
index 0000000000..b154304515
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/event.h
@@ -0,0 +1,135 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_EVENT_H
+#define _FLUIDSYNTH_EVENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file event.h
+ * @brief Sequencer event functions and defines.
+ *
+ * Functions and constants for creating/processing sequencer events.
+ */
+
+/**
+ * Sequencer event type enumeration.
+ */
+enum fluid_seq_event_type {
+ FLUID_SEQ_NOTE = 0, /**< Note event with duration */
+ FLUID_SEQ_NOTEON, /**< Note on event */
+ FLUID_SEQ_NOTEOFF, /**< Note off event */
+ FLUID_SEQ_ALLSOUNDSOFF, /**< All sounds off event */
+ FLUID_SEQ_ALLNOTESOFF, /**< All notes off event */
+ FLUID_SEQ_BANKSELECT, /**< Bank select message */
+ FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */
+ FLUID_SEQ_PROGRAMSELECT, /**< Program select message (DOCME) */
+ FLUID_SEQ_PITCHBEND, /**< Pitch bend message */
+ FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was mispelled previously */
+ FLUID_SEQ_MODULATION, /**< Modulation controller event */
+ FLUID_SEQ_SUSTAIN, /**< Sustain controller event */
+ FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */
+ FLUID_SEQ_PAN, /**< Stereo pan set event */
+ FLUID_SEQ_VOLUME, /**< Volume set event */
+ FLUID_SEQ_REVERBSEND, /**< Reverb send set event */
+ FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */
+ FLUID_SEQ_TIMER, /**< Timer event (DOCME) */
+ FLUID_SEQ_ANYCONTROLCHANGE, /**< DOCME (used for remove_events only) */
+ FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.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 */
+ FLUID_SEQ_LASTEVENT /**< Defines the count of event enums */
+};
+
+#define FLUID_SEQ_PITCHWHHELSENS FLUID_SEQ_PITCHWHEELSENS /**< Old deprecated misspelling of #FLUID_SEQ_PITCHWHEELSENS */
+
+/* Event alloc/free */
+FLUIDSYNTH_API fluid_event_t* new_fluid_event(void);
+FLUIDSYNTH_API void delete_fluid_event(fluid_event_t* evt);
+
+/* Initializing events */
+FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t* evt, short src);
+FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t* evt, short dest);
+
+/* Timer events */
+FLUIDSYNTH_API void fluid_event_timer(fluid_event_t* evt, void* data);
+
+/* Note events */
+FLUIDSYNTH_API void fluid_event_note(fluid_event_t* evt, int channel,
+ short key, short vel,
+ unsigned int duration);
+
+FLUIDSYNTH_API void fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel);
+FLUIDSYNTH_API void fluid_event_noteoff(fluid_event_t* evt, int channel, short key);
+FLUIDSYNTH_API void fluid_event_all_sounds_off(fluid_event_t* evt, int channel);
+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_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);
+
+/* 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_channel_pressure(fluid_event_t* evt, int channel, short 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);
+
+/* Accessing event data */
+FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t* evt);
+FLUIDSYNTH_API short fluid_event_get_source(fluid_event_t* evt);
+FLUIDSYNTH_API short fluid_event_get_dest(fluid_event_t* evt);
+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 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 unsigned int fluid_event_get_sfont_id(fluid_event_t* evt);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FLUIDSYNTH_EVENT_H */
diff --git a/libs/fluidsynth/fluidsynth/fluidsynth.h b/libs/fluidsynth/fluidsynth/fluidsynth.h
new file mode 100644
index 0000000000..8c599b5be7
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/fluidsynth.h
@@ -0,0 +1,38 @@
+#ifndef _FLUIDSYNTH_H
+#define _FLUIDSYNTH_H
+
+#include <stdio.h>
+
+#if defined(COMPILER_MSVC)
+# define FLUIDSYNTH_API
+#else
+# define FLUIDSYNTH_API __attribute__ ((visibility ("hidden")))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro);
+FLUIDSYNTH_API char* fluid_version_str(void);
+
+
+#include "types.h"
+#include "settings.h"
+#include "synth.h"
+#include "sfont.h"
+#include "event.h"
+#include "midi.h"
+#include "log.h"
+#include "misc.h"
+#include "mod.h"
+#include "gen.h"
+#include "voice.h"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_H */
diff --git a/libs/fluidsynth/fluidsynth/gen.h b/libs/fluidsynth/fluidsynth/gen.h
new file mode 100644
index 0000000000..e4bbc8ef69
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/gen.h
@@ -0,0 +1,135 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_GEN_H
+#define _FLUIDSYNTH_GEN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file gen.h
+ * @brief Functions and defines for SoundFont generator effects.
+ */
+
+/**
+ * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3)
+ */
+enum fluid_gen_type {
+ GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */
+ GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */
+ GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */
+ GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */
+ GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */
+ GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */
+ GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */
+ GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */
+ GEN_FILTERFC, /**< Filter cutoff */
+ GEN_FILTERQ, /**< Filter Q */
+ GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */
+ GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */
+ GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */
+ GEN_MODLFOTOVOL, /**< Modulation LFO to volume */
+ GEN_UNUSED1, /**< Unused */
+ GEN_CHORUSSEND, /**< Chorus send amount */
+ GEN_REVERBSEND, /**< Reverb send amount */
+ GEN_PAN, /**< Stereo panning */
+ GEN_UNUSED2, /**< Unused */
+ GEN_UNUSED3, /**< Unused */
+ GEN_UNUSED4, /**< Unused */
+ GEN_MODLFODELAY, /**< Modulation LFO delay */
+ GEN_MODLFOFREQ, /**< Modulation LFO frequency */
+ GEN_VIBLFODELAY, /**< Vibrato LFO delay */
+ GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */
+ GEN_MODENVDELAY, /**< Modulation envelope delay */
+ GEN_MODENVATTACK, /**< Modulation envelope attack */
+ GEN_MODENVHOLD, /**< Modulation envelope hold */
+ GEN_MODENVDECAY, /**< Modulation envelope decay */
+ GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */
+ GEN_MODENVRELEASE, /**< Modulation envelope release */
+ GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */
+ GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */
+ GEN_VOLENVDELAY, /**< Volume envelope delay */
+ GEN_VOLENVATTACK, /**< Volume envelope attack */
+ GEN_VOLENVHOLD, /**< Volume envelope hold */
+ GEN_VOLENVDECAY, /**< Volume envelope decay */
+ GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */
+ GEN_VOLENVRELEASE, /**< Volume envelope release */
+ GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */
+ GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */
+ GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */
+ GEN_RESERVED1, /**< Reserved */
+ GEN_KEYRANGE, /**< MIDI note range */
+ GEN_VELRANGE, /**< MIDI velocity range */
+ GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */
+ GEN_KEYNUM, /**< Fixed MIDI note number */
+ GEN_VELOCITY, /**< Fixed MIDI velocity value */
+ GEN_ATTENUATION, /**< Initial volume attenuation */
+ GEN_RESERVED2, /**< Reserved */
+ GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */
+ GEN_COARSETUNE, /**< Coarse tuning */
+ GEN_FINETUNE, /**< Fine tuning */
+ GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */
+ GEN_SAMPLEMODE, /**< Sample mode flags */
+ GEN_RESERVED3, /**< Reserved */
+ GEN_SCALETUNE, /**< Scale tuning */
+ GEN_EXCLUSIVECLASS, /**< Exclusive class number */
+ GEN_OVERRIDEROOTKEY, /**< Sample root note override */
+
+ /* the initial pitch is not a "standard" generator. It is not
+ * mentioned in the list of generator in the SF2 specifications. It
+ * is used, however, as the destination for the default pitch wheel
+ * modulator. */
+ GEN_PITCH, /**< Pitch (NOTE: Not a real SoundFont generator) */
+ GEN_LAST /**< Value defines the count of generators (#fluid_gen_type) */
+};
+
+
+/**
+ * SoundFont generator structure.
+ */
+typedef struct _fluid_gen_t
+{
+ unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */
+ double val; /**< The nominal value */
+ double mod; /**< Change by modulators */
+ double nrpn; /**< Change by NRPN messages */
+} fluid_gen_t;
+
+/**
+ * Enum value for 'flags' field of #fluid_gen_t (not really flags).
+ */
+enum fluid_gen_flags
+{
+ GEN_UNUSED, /**< Generator value is not set */
+ GEN_SET, /**< Generator value is set */
+ GEN_ABS_NRPN /**< Generator is an absolute value */
+};
+
+FLUIDSYNTH_API int fluid_gen_set_default_values(fluid_gen_t* gen);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FLUIDSYNTH_GEN_H */
+
diff --git a/libs/fluidsynth/fluidsynth/log.h b/libs/fluidsynth/fluidsynth/log.h
new file mode 100644
index 0000000000..85db03e1cb
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/log.h
@@ -0,0 +1,84 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_LOG_H
+#define _FLUIDSYNTH_LOG_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @file log.h
+ * @brief 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:
+ *
+ * @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);
+ * @endcode
+ */
+
+/**
+ * FluidSynth log levels.
+ */
+enum fluid_log_level {
+ FLUID_PANIC, /**< The synth can't function correctly any more */
+ FLUID_ERR, /**< Serious error occurred */
+ FLUID_WARN, /**< Warning */
+ FLUID_INFO, /**< Verbose informational messages */
+ FLUID_DBG, /**< Debugging messages */
+ LAST_LOG_LEVEL
+};
+
+/**
+ * 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().
+ */
+typedef void (*fluid_log_function_t)(int level, char* message, void* data);
+
+FLUIDSYNTH_API
+fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data);
+
+FLUIDSYNTH_API void fluid_default_log_function(int level, char* message, void* data);
+
+FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_LOG_H */
diff --git a/libs/fluidsynth/fluidsynth/midi.h b/libs/fluidsynth/fluidsynth/midi.h
new file mode 100644
index 0000000000..ab1e6a198b
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/midi.h
@@ -0,0 +1,141 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_MIDI_H
+#define _FLUIDSYNTH_MIDI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file midi.h
+ * @brief Functions for MIDI events, drivers and MIDI file playback.
+ */
+
+FLUIDSYNTH_API fluid_midi_event_t* new_fluid_midi_event(void);
+FLUIDSYNTH_API int delete_fluid_midi_event(fluid_midi_event_t* event);
+
+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);
+FLUIDSYNTH_API int fluid_midi_event_set_channel(fluid_midi_event_t* evt, int chan);
+FLUIDSYNTH_API int fluid_midi_event_get_channel(fluid_midi_event_t* evt);
+FLUIDSYNTH_API int fluid_midi_event_get_key(fluid_midi_event_t* evt);
+FLUIDSYNTH_API int fluid_midi_event_set_key(fluid_midi_event_t* evt, int key);
+FLUIDSYNTH_API int fluid_midi_event_get_velocity(fluid_midi_event_t* evt);
+FLUIDSYNTH_API int fluid_midi_event_set_velocity(fluid_midi_event_t* evt, int vel);
+FLUIDSYNTH_API int fluid_midi_event_get_control(fluid_midi_event_t* evt);
+FLUIDSYNTH_API int fluid_midi_event_set_control(fluid_midi_event_t* evt, int ctrl);
+FLUIDSYNTH_API int fluid_midi_event_get_value(fluid_midi_event_t* evt);
+FLUIDSYNTH_API int fluid_midi_event_set_value(fluid_midi_event_t* evt, int val);
+FLUIDSYNTH_API int fluid_midi_event_get_program(fluid_midi_event_t* evt);
+FLUIDSYNTH_API int fluid_midi_event_set_program(fluid_midi_event_t* evt, int val);
+FLUIDSYNTH_API int fluid_midi_event_get_pitch(fluid_midi_event_t* evt);
+FLUIDSYNTH_API int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val);
+FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t* evt, void *data,
+ int size, int dynamic);
+
+/**
+ * MIDI router rule type.
+ * @since 1.1.0
+ */
+typedef enum
+{
+ FLUID_MIDI_ROUTER_RULE_NOTE, /**< MIDI note rule */
+ FLUID_MIDI_ROUTER_RULE_CC, /**< MIDI controller rule */
+ FLUID_MIDI_ROUTER_RULE_PROG_CHANGE, /**< MIDI program change rule */
+ 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 */
+ FLUID_MIDI_ROUTER_RULE_COUNT /**< Total count of rule types */
+} 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);
+
+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 int delete_fluid_midi_router(fluid_midi_router_t* handler);
+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);
+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);
+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,
+ int min, int max, float mul, int add);
+FLUIDSYNTH_API void fluid_midi_router_rule_set_param2 (fluid_midi_router_rule_t *rule,
+ int min, int max, float mul, int add);
+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);
+
+
+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);
+
+
+/**
+ * MIDI 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_DONE /**< Player is finished playing */
+};
+
+FLUIDSYNTH_API fluid_player_t* new_fluid_player(fluid_synth_t* synth);
+FLUIDSYNTH_API int delete_fluid_player(fluid_player_t* player);
+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_get_status(fluid_player_t* player);
+FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t* player, handle_midi_event_func_t handler, void* handler_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_MIDI_H */
diff --git a/libs/fluidsynth/fluidsynth/misc.h b/libs/fluidsynth/fluidsynth/misc.h
new file mode 100644
index 0000000000..4f97d8437d
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/misc.h
@@ -0,0 +1,77 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_MISC_H
+#define _FLUIDSYNTH_MISC_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @file misc.h
+ * @brief 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
+ * like the following can be used:
+ *
+ * @code
+ * #include <fluidsynth.h>
+ *
+ * #ifndef FLUID_OK
+ * #define FLUID_OK (0)
+ * #define FLUID_FAILED (-1)
+ * #endif
+ * @endcode
+ */
+#define FLUID_OK (0)
+
+/**
+ * Value that indicates failure, used by most libfluidsynth functions.
+ * @since 1.1.0
+ *
+ * NOTE: See #FLUID_OK for more details.
+ */
+#define FLUID_FAILED (-1)
+
+
+FLUIDSYNTH_API int fluid_is_soundfont (const char *filename);
+FLUIDSYNTH_API int fluid_is_midifile (const char *filename);
+
+
+#ifdef WIN32
+FLUIDSYNTH_API void* fluid_get_hinstance(void);
+FLUIDSYNTH_API void fluid_set_hinstance(void* hinstance);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_MISC_H */
diff --git a/libs/fluidsynth/fluidsynth/mod.h b/libs/fluidsynth/fluidsynth/mod.h
new file mode 100644
index 0000000000..e34309546b
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/mod.h
@@ -0,0 +1,112 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_MOD_H
+#define _FLUIDSYNTH_MOD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file mod.h
+ * @brief SoundFont modulator functions and constants.
+ */
+
+#define FLUID_NUM_MOD 64 /**< Maximum number of modulators in a voice */
+
+/**
+ * Modulator structure. See SoundFont 2.04 PDF section 8.2.
+ */
+struct _fluid_mod_t
+{
+ unsigned char dest; /**< Destination generator to control */
+ unsigned char src1; /**< Source controller 1 */
+ unsigned char flags1; /**< Source controller 1 flags */
+ unsigned char src2; /**< Source controller 2 */
+ unsigned char flags2; /**< Source controller 2 flags */
+ double amount; /**< Multiplier amount */
+ /* The 'next' field allows to link modulators into a list. It is
+ * not used in fluid_voice.c, there each voice allocates memory for a
+ * fixed number of modulators. Since there may be a huge number of
+ * different zones, this is more efficient.
+ */
+ fluid_mod_t * next;
+};
+
+/**
+ * Flags defining the polarity, mapping function and type of a modulator source.
+ * Compare with SoundFont 2.04 PDF section 8.2.
+ *
+ * Note: Bit values do not correspond to the SoundFont spec! Also note that
+ * #FLUID_MOD_GC and #FLUID_MOD_CC are in the flags field instead of the source field.
+ */
+enum fluid_mod_flags
+{
+ FLUID_MOD_POSITIVE = 0, /**< Mapping function is positive */
+ FLUID_MOD_NEGATIVE = 1, /**< Mapping function is negative */
+ FLUID_MOD_UNIPOLAR = 0, /**< Mapping function is unipolar */
+ FLUID_MOD_BIPOLAR = 2, /**< Mapping function is bipolar */
+ FLUID_MOD_LINEAR = 0, /**< Linear mapping function */
+ FLUID_MOD_CONCAVE = 4, /**< Concave mapping function */
+ FLUID_MOD_CONVEX = 8, /**< Convex mapping function */
+ FLUID_MOD_SWITCH = 12, /**< Switch (on/off) mapping function */
+ FLUID_MOD_GC = 0, /**< General controller source type (#fluid_mod_src) */
+ FLUID_MOD_CC = 16 /**< MIDI CC controller (source will be a MIDI CC number) */
+};
+
+/**
+ * General controller (if #FLUID_MOD_GC in flags). This
+ * corresponds to SoundFont 2.04 PDF section 8.2.1
+ */
+enum fluid_mod_src
+{
+ FLUID_MOD_NONE = 0, /**< No source controller */
+ FLUID_MOD_VELOCITY = 2, /**< MIDI note-on velocity */
+ FLUID_MOD_KEY = 3, /**< MIDI note-on note number */
+ FLUID_MOD_KEYPRESSURE = 10, /**< MIDI key pressure */
+ FLUID_MOD_CHANNELPRESSURE = 13, /**< MIDI channel pressure */
+ FLUID_MOD_PITCHWHEEL = 14, /**< Pitch wheel */
+ FLUID_MOD_PITCHWHEELSENS = 16 /**< Pitch wheel sensitivity */
+};
+
+FLUIDSYNTH_API fluid_mod_t* fluid_mod_new(void);
+FLUIDSYNTH_API void fluid_mod_delete(fluid_mod_t * mod);
+
+FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags);
+FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags);
+FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t* mod, int dst);
+FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t* mod, double amount);
+
+FLUIDSYNTH_API int fluid_mod_get_source1(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_flags1(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_source2(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_flags2(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_dest(fluid_mod_t* mod);
+FLUIDSYNTH_API double fluid_mod_get_amount(fluid_mod_t* mod);
+
+FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FLUIDSYNTH_MOD_H */
+
diff --git a/libs/fluidsynth/fluidsynth/settings.h b/libs/fluidsynth/fluidsynth/settings.h
new file mode 100644
index 0000000000..3a0502a920
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/settings.h
@@ -0,0 +1,219 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_SETTINGS_H
+#define _FLUIDSYNTH_SETTINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file settings.h
+ * @brief Synthesizer settings
+ * @defgroup SettingsFunctions 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.
+ * @code
+ * void
+ * my_synthesizer ()
+ * {
+ * fluid_settings_t *settings;
+ * fluid_synth_t *synth;
+ * fluid_audio_driver_t *adriver;
+ *
+ * settings = new_fluid_settings ();
+ * fluid_settings_setstr(settings, "audio.driver", "alsa");
+ * // ... change settings ...
+ * synth = new_fluid_synth (settings);
+ * adriver = new_fluid_audio_driver (settings, synth);
+ * // ...
+ * }
+ * @endcode
+ * @sa @ref CreatingSettings
+ */
+
+/**
+ * Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field
+ * of the FLUID_PortRangeHint should be considered meaningful. The
+ * value in this field should be considered the (inclusive) lower
+ * bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also
+ * specified then the value of LowerBound should be multiplied by the
+ * sample rate.
+ */
+#define FLUID_HINT_BOUNDED_BELOW 0x1
+
+/** Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+ of the FLUID_PortRangeHint should be considered meaningful. The
+ value in this field should be considered the (inclusive) upper
+ bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also
+ specified then the value of UpperBound should be multiplied by the
+ sample rate. */
+#define FLUID_HINT_BOUNDED_ABOVE 0x2
+
+/**
+ * Hint FLUID_HINT_TOGGLED indicates that the data item should be
+ * considered a Boolean toggle. Data less than or equal to zero should
+ * be considered `off' or `false,' and data above zero should be
+ * considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in
+ * conjunction with any other hint.
+ */
+#define FLUID_HINT_TOGGLED 0x4
+
+/**
+ * Hint FLUID_HINT_SAMPLE_RATE indicates that any bounds specified
+ * should be interpreted as multiples of the sample rate. For
+ * instance, a frequency range from 0Hz to the Nyquist frequency (half
+ * the sample rate) could be requested by this hint in conjunction
+ * with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
+ * at all must support this hint to retain meaning.
+ */
+#define FLUID_HINT_SAMPLE_RATE 0x8
+
+/**
+ * Hint FLUID_HINT_LOGARITHMIC indicates that it is likely that the
+ * user will find it more intuitive to view values using a logarithmic
+ * scale. This is particularly useful for frequencies and gains.
+ */
+#define FLUID_HINT_LOGARITHMIC 0x10
+
+/**
+ * Hint FLUID_HINT_INTEGER indicates that a user interface would
+ * probably wish to provide a stepped control taking only integer
+ * values.
+ * @deprecated
+ *
+ * As there is an integer setting type, this hint is not used.
+ */
+#define FLUID_HINT_INTEGER 0x20
+
+
+#define FLUID_HINT_FILENAME 0x01 /**< String setting is a file name */
+#define FLUID_HINT_OPTIONLIST 0x02 /**< Setting is a list of string options */
+
+
+/**
+ * Settings type
+ *
+ * Each setting has a defined type: numeric (double), integer, string or a
+ * set of values. The type of each setting can be retrieved using the
+ * function fluid_settings_get_type()
+ */
+enum fluid_types_enum {
+ FLUID_NO_TYPE = -1, /**< Undefined type */
+ FLUID_NUM_TYPE, /**< Numeric (double) */
+ FLUID_INT_TYPE, /**< Integer */
+ FLUID_STR_TYPE, /**< String */
+ FLUID_SET_TYPE /**< Set of values */
+};
+
+
+FLUIDSYNTH_API fluid_settings_t* new_fluid_settings(void);
+FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t* settings);
+
+FLUIDSYNTH_API
+int fluid_settings_get_type(fluid_settings_t* settings, const char *name);
+
+FLUIDSYNTH_API
+int fluid_settings_get_hints(fluid_settings_t* settings, const char *name);
+
+FLUIDSYNTH_API
+int fluid_settings_is_realtime(fluid_settings_t* settings, const char *name);
+
+FLUIDSYNTH_API
+int fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str);
+
+FLUIDSYNTH_API
+int fluid_settings_copystr(fluid_settings_t* settings, const char *name, char *str, int len);
+
+FLUIDSYNTH_API
+int fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str);
+
+FLUIDSYNTH_API
+int fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str);
+
+FLUIDSYNTH_API
+char* fluid_settings_getstr_default(fluid_settings_t* settings, const char *name);
+
+FLUIDSYNTH_API
+int fluid_settings_str_equal(fluid_settings_t* settings, const char *name, const char *value);
+
+FLUIDSYNTH_API
+int fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val);
+
+FLUIDSYNTH_API
+int fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val);
+
+FLUIDSYNTH_API
+double fluid_settings_getnum_default(fluid_settings_t* settings, const char *name);
+
+FLUIDSYNTH_API
+void fluid_settings_getnum_range(fluid_settings_t* settings, const char *name,
+ double* min, double* max);
+
+FLUIDSYNTH_API
+int fluid_settings_setint(fluid_settings_t* settings, const char *name, int val);
+
+FLUIDSYNTH_API
+int fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val);
+
+FLUIDSYNTH_API
+int fluid_settings_getint_default(fluid_settings_t* settings, const char *name);
+
+FLUIDSYNTH_API
+void fluid_settings_getint_range(fluid_settings_t* settings, const char *name,
+ int* min, int* max);
+
+/**
+ * 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)
+ */
+typedef void (*fluid_settings_foreach_option_t)(void *data, char *name, char *option);
+
+FLUIDSYNTH_API
+void fluid_settings_foreach_option(fluid_settings_t* settings,
+ const char* name, void* data,
+ fluid_settings_foreach_option_t func);
+FLUIDSYNTH_API
+int fluid_settings_option_count (fluid_settings_t* settings, const char* name);
+FLUIDSYNTH_API char *fluid_settings_option_concat (fluid_settings_t* settings,
+ const char* name,
+ const char* separator);
+
+/**
+ * Callback function type used with fluid_settings_foreach()
+ * @param data User defined data pointer
+ * @param name Setting name
+ * @param type Setting type (#fluid_types_enum)
+ */
+typedef void (*fluid_settings_foreach_t)(void *data, char *name, int type);
+
+FLUIDSYNTH_API
+void fluid_settings_foreach(fluid_settings_t* settings, void* data,
+ fluid_settings_foreach_t func);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_SETTINGS_H */
diff --git a/libs/fluidsynth/fluidsynth/sfont.h b/libs/fluidsynth/fluidsynth/sfont.h
new file mode 100644
index 0000000000..30aebfd126
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/sfont.h
@@ -0,0 +1,281 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_SFONT_H
+#define _FLUIDSYNTH_SFONT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @file sfont.h
+ * @brief SoundFont plugins
+ *
+ * It is possible to add new SoundFont loaders to the
+ * synthesizer. The API uses a couple of "interfaces" (structures
+ * with callback functions): #fluid_sfloader_t, #fluid_sfont_t, and
+ * #fluid_preset_t. This API allows for virtual SoundFont files to be loaded
+ * and synthesized, which may not actually be SoundFont files, as long as they
+ * can be represented by the SoundFont synthesis model.
+ *
+ * To add a new SoundFont loader to the synthesizer, call
+ * fluid_synth_add_sfloader() and pass a pointer to an
+ * fluid_sfloader_t structure. The important callback function in
+ * this structure is "load", which should try to load a file and
+ * returns a #fluid_sfont_t structure, or NULL if it fails.
+ *
+ * The #fluid_sfont_t structure contains a callback to obtain the
+ * name of the SoundFont. It contains two functions to iterate
+ * though the contained presets, and one function to obtain a
+ * preset corresponding to a bank and preset number. This
+ * function should return a #fluid_preset_t structure.
+ *
+ * The #fluid_preset_t structure contains some functions to obtain
+ * information from the preset (name, bank, number). The most
+ * important callback is the noteon function. The noteon function
+ * should call fluid_synth_alloc_voice() for every sample that has
+ * to be played. fluid_synth_alloc_voice() expects a pointer to a
+ * #fluid_sample_t structure and returns a pointer to the opaque
+ * #fluid_voice_t structure. To set or increment the values of a
+ * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are
+ * finished initializing the voice call fluid_voice_start() to
+ * start playing the synthesis voice.
+ */
+
+/**
+ * Some notification enums for presets and samples.
+ */
+enum {
+ FLUID_PRESET_SELECTED, /**< Preset selected notify */
+ FLUID_PRESET_UNSELECTED, /**< Preset unselected notify */
+ FLUID_SAMPLE_DONE /**< Sample no longer needed notify */
+};
+
+
+/**
+ * SoundFont loader structure.
+ */
+struct _fluid_sfloader_t {
+ void* data; /**< User defined data pointer */
+
+ /**
+ * The free method should free the memory allocated for the loader in
+ * addition to any private data.
+ * @param loader SoundFont loader
+ * @return Should return 0 if no error occured, non-zero otherwise
+ */
+ int (*free)(fluid_sfloader_t* loader);
+
+ /**
+ * Method to load an instrument file (does not actually need to be a real file name,
+ * could be another type of string identifier that the \a loader understands).
+ * @param loader SoundFont loader
+ * @param filename File name or other string identifier
+ * @return The loaded instrument file (SoundFont) or NULL if an error occured.
+ */
+ fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename);
+};
+
+/**
+ * Virtual SoundFont instance structure.
+ */
+struct _fluid_sfont_t {
+ void* data; /**< User defined data */
+ unsigned int id; /**< SoundFont ID */
+
+ /**
+ * Method to free a virtual SoundFont bank.
+ * @param sfont Virtual SoundFont to free.
+ * @return Should return 0 when it was able to free all resources or non-zero
+ * if some of the samples could not be freed because they are still in use,
+ * in which case the free will be tried again later, until success.
+ */
+ int (*free)(fluid_sfont_t* sfont);
+
+ /**
+ * Method to return the name of a virtual SoundFont.
+ * @param sfont Virtual SoundFont
+ * @return The name of the virtual SoundFont.
+ */
+ char* (*get_name)(fluid_sfont_t* sfont);
+
+ /**
+ * Get a virtual SoundFont preset by bank and program numbers.
+ * @param sfont Virtual SoundFont
+ * @param bank MIDI bank number (0-16384)
+ * @param prenum MIDI preset number (0-127)
+ * @return Should return an allocated virtual preset or NULL if it could not
+ * be found.
+ */
+ fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
+
+ /**
+ * Start virtual SoundFont preset iteration method.
+ * @param sfont Virtual SoundFont
+ *
+ * Starts/re-starts virtual preset iteration in a SoundFont.
+ */
+ void (*iteration_start)(fluid_sfont_t* sfont);
+
+ /**
+ * Virtual SoundFont preset iteration function.
+ * @param sfont Virtual SoundFont
+ * @param preset Caller supplied preset to fill in with current preset information
+ * @return 0 when no more presets are available, 1 otherwise
+ *
+ * Should store preset information to the caller supplied \a preset structure
+ * and advance the internal iteration state to the next preset for subsequent
+ * calls.
+ */
+ int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset);
+};
+
+#define fluid_sfont_get_id(_sf) ((_sf)->id)
+
+/**
+ * Virtual SoundFont preset.
+ */
+struct _fluid_preset_t {
+ void* data; /**< User supplied data */
+ fluid_sfont_t* sfont; /**< Parent virtual SoundFont */
+
+ /**
+ * Method to free a virtual SoundFont preset.
+ * @param preset Virtual SoundFont preset
+ * @return Should return 0
+ */
+ int (*free)(fluid_preset_t* preset);
+
+ /**
+ * Method to get a virtual SoundFont preset name.
+ * @param preset Virtual SoundFont preset
+ * @return Should return the name of the preset. The returned string must be
+ * valid for the duration of the virtual preset (or the duration of the
+ * SoundFont, in the case of preset iteration).
+ */
+ char* (*get_name)(fluid_preset_t* preset);
+
+ /**
+ * Method to get a virtual SoundFont preset MIDI bank number.
+ * @param preset Virtual SoundFont preset
+ * @param return The bank number of the preset
+ */
+ int (*get_banknum)(fluid_preset_t* preset);
+
+ /**
+ * Method to get a virtual SoundFont preset MIDI program number.
+ * @param preset Virtual SoundFont preset
+ * @param return The program number of the preset
+ */
+ int (*get_num)(fluid_preset_t* preset);
+
+ /**
+ * Method to handle a noteon event (synthesize the instrument).
+ * @param preset Virtual SoundFont preset
+ * @param synth Synthesizer instance
+ * @param chan MIDI channel number of the note on event
+ * @param key MIDI note number (0-127)
+ * @param vel MIDI velocity (0-127)
+ * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise
+ *
+ * This method may be called from within synthesis context and therefore
+ * should be as efficient as possible and not perform any operations considered
+ * bad for realtime audio output (memory allocations and other OS calls).
+ *
+ * Call fluid_synth_alloc_voice() for every sample that has
+ * to be played. fluid_synth_alloc_voice() expects a pointer to a
+ * #fluid_sample_t structure and returns a pointer to the opaque
+ * #fluid_voice_t structure. To set or increment the values of a
+ * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are
+ * finished initializing the voice call fluid_voice_start() to
+ * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices
+ * created will be started at the same time.
+ */
+ int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+ /**
+ * Virtual SoundFont preset notify method.
+ * @param preset Virtual SoundFont preset
+ * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED
+ * @param chan MIDI channel number
+ * @return Should return #FLUID_OK
+ *
+ * Implement this optional method if the preset needs to be notified about
+ * preset select and unselect events.
+ *
+ * This method may be called from within synthesis context and therefore
+ * should be as efficient as possible and not perform any operations considered
+ * bad for realtime audio output (memory allocations and other OS calls).
+ */
+ int (*notify)(fluid_preset_t* preset, int reason, int chan);
+};
+
+/**
+ * Virtual SoundFont sample.
+ */
+struct _fluid_sample_t
+{
+ char name[21]; /**< Sample name */
+ unsigned int start; /**< Start index */
+ unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */
+ unsigned int loopstart; /**< Loop start index */
+ unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */
+ unsigned int samplerate; /**< Sample rate */
+ int origpitch; /**< Original pitch (MIDI note number, 0-127) */
+ int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */
+ int sampletype; /**< Values: #FLUID_SAMPLETYPE_MONO, FLUID_SAMPLETYPE_RIGHT, FLUID_SAMPLETYPE_LEFT, FLUID_SAMPLETYPE_ROM */
+ int valid; /**< Should be TRUE if sample data is valid, FALSE otherwise (in which case it will not be synthesized) */
+ short* data; /**< Pointer to the sample's data */
+
+ int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */
+ double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */
+
+ unsigned int refcount; /**< Count of voices using this sample (use #fluid_sample_refcount to access this field) */
+
+ /**
+ * Implement this function to receive notification when sample is no longer used.
+ * @param sample Virtual SoundFont sample
+ * @param reason #FLUID_SAMPLE_DONE only currently
+ * @return Should return #FLUID_OK
+ */
+ int (*notify)(fluid_sample_t* sample, int reason);
+
+ void* userdata; /**< User defined data */
+};
+
+
+#define fluid_sample_refcount(_sample) ((_sample)->refcount) /**< Get the reference count of a sample. Should only be called from within synthesis context (noteon method for example) */
+
+
+#define FLUID_SAMPLETYPE_MONO 1 /**< Flag for #fluid_sample_t \a sampletype field for mono samples */
+#define FLUID_SAMPLETYPE_RIGHT 2 /**< Flag for #fluid_sample_t \a sampletype field for right samples of a stereo pair */
+#define FLUID_SAMPLETYPE_LEFT 4 /**< Flag for #fluid_sample_t \a sampletype field for left samples of a stereo pair */
+#define FLUID_SAMPLETYPE_LINKED 8 /**< Flag for #fluid_sample_t \a sampletype field, not used currently */
+#define FLUID_SAMPLETYPE_ROM 0x8000 /**< Flag for #fluid_sample_t \a sampletype field, ROM sample, causes sample to be ignored */
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_SFONT_H */
diff --git a/libs/fluidsynth/fluidsynth/synth.h b/libs/fluidsynth/fluidsynth/synth.h
new file mode 100644
index 0000000000..f62e60cd1b
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/synth.h
@@ -0,0 +1,315 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_SYNTH_H
+#define _FLUIDSYNTH_SYNTH_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @file synth.h
+ * @brief Embeddable SoundFont synthesizer
+ *
+ * You create a new synthesizer with new_fluid_synth() and you destroy
+ * if with delete_fluid_synth(). Use the settings structure to specify
+ * the synthesizer characteristics.
+ *
+ * You have to load a SoundFont in order to hear any sound. For that
+ * you use the fluid_synth_sfload() function.
+ *
+ * You can use the audio driver functions described below to open
+ * the audio device and create a background audio thread.
+ *
+ * The API for sending MIDI events is probably what you expect:
+ * fluid_synth_noteon(), fluid_synth_noteoff(), ...
+ */
+
+#define FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE 32 /**< Length of channel info name field (including zero terminator) */
+
+/**
+ * Channel information structure for fluid_synth_get_channel_info().
+ * @since 1.1.1
+ */
+struct _fluid_synth_channel_info_t
+{
+ int assigned : 1; /**< TRUE if a preset is assigned, FALSE otherwise */
+ /* Reserved flag bits (at the least 31) */
+ int sfont_id; /**< ID of parent SoundFont */
+ int bank; /**< MIDI bank number (0-16383) */
+ int program; /**< MIDI program number (0-127) */
+ char name[FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE]; /**< Channel preset name */
+ char reserved[32]; /**< Reserved data for future expansion */
+};
+
+FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings);
+FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth);
+FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth);
+
+
+/* MIDI channel messages */
+
+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);
+FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval);
+FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len,
+ char *response, int *response_len, int *handled, int dryrun);
+FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val);
+FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend);
+FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val);
+FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval);
+FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program);
+FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val);
+FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank);
+FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id);
+FLUIDSYNTH_API
+int fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id,
+ unsigned int bank_num, unsigned int preset_num);
+FLUIDSYNTH_API int
+fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan,
+ const char *sfont_name, unsigned int bank_num,
+ unsigned int preset_num);
+FLUIDSYNTH_API
+int fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id,
+ unsigned int* bank_num, unsigned int* preset_num);
+FLUIDSYNTH_API int fluid_synth_unset_program (fluid_synth_t *synth, int chan);
+FLUIDSYNTH_API int fluid_synth_get_channel_info (fluid_synth_t *synth, int chan,
+ fluid_synth_channel_info_t *info);
+FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth);
+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);
+
+enum fluid_midi_channel_type
+{
+ CHANNEL_TYPE_MELODIC = 0,
+ CHANNEL_TYPE_DRUM = 1
+};
+
+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);
+
+
+/* SoundFont management */
+
+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, unsigned int id);
+FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets);
+FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont);
+FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont);
+FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth);
+FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num);
+FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id);
+FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name (fluid_synth_t* synth,
+ 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);
+
+
+/* Reverb */
+
+FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize,
+ double damping, double width, 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);
+
+#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f /**< Default reverb room size */
+#define FLUID_REVERB_DEFAULT_DAMP 0.0f /**< Default reverb damping */
+#define FLUID_REVERB_DEFAULT_WIDTH 0.5f /**< Default reverb width */
+#define FLUID_REVERB_DEFAULT_LEVEL 0.9f /**< Default reverb level */
+
+
+/* Chorus */
+
+/**
+ * Chorus modulation waveform type.
+ */
+enum fluid_chorus_mod {
+ FLUID_CHORUS_MOD_SINE = 0, /**< Sine wave chorus modulation */
+ FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */
+};
+
+FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
+ double speed, double depth_ms, 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_Hz(fluid_synth_t* synth);
+FLUIDSYNTH_API double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth);
+FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t* synth); /* see fluid_chorus_mod */
+
+#define FLUID_CHORUS_DEFAULT_N 3 /**< Default chorus voice count */
+#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f /**< Default chorus level */
+#define FLUID_CHORUS_DEFAULT_SPEED 0.3f /**< Default chorus speed */
+#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f /**< Default chorus depth */
+#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE /**< Default chorus waveform type */
+
+
+/* Audio and MIDI channels */
+
+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);
+
+
+/* Synthesis parameters */
+
+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);
+FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth);
+FLUIDSYNTH_API int fluid_synth_get_active_voice_count(fluid_synth_t* synth);
+FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth);
+
+FLUIDSYNTH_API
+int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method);
+
+/**
+ * Synthesis interpolation method.
+ */
+enum fluid_interp {
+ FLUID_INTERP_NONE = 0, /**< No interpolation: Fastest, but questionable audio quality */
+ FLUID_INTERP_LINEAR = 1, /**< Straight-line interpolation: A bit slower, reasonable audio quality */
+ FLUID_INTERP_4THORDER = 4, /**< Fourth-order interpolation, good quality, the default */
+ FLUID_INTERP_7THORDER = 7 /**< Seventh-order interpolation */
+};
+
+#define FLUID_INTERP_DEFAULT FLUID_INTERP_4THORDER /**< Default interpolation method from #fluid_interp. */
+#define FLUID_INTERP_HIGHEST FLUID_INTERP_7THORDER /**< Highest interpolation method from #fluid_interp. */
+
+
+/* Generator interface */
+
+FLUIDSYNTH_API
+int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value);
+FLUIDSYNTH_API int fluid_synth_set_gen2 (fluid_synth_t* synth, int chan,
+ int param, float value,
+ int absolute, int normalized);
+FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param);
+
+
+/* Tuning */
+
+FLUIDSYNTH_API
+int fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch);
+FLUIDSYNTH_API
+int fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch, int apply);
+FLUIDSYNTH_API
+int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch);
+FLUIDSYNTH_API
+int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch, int apply);
+FLUIDSYNTH_API
+int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog,
+ int len, const int *keys, const double* pitch, int apply);
+FLUIDSYNTH_API
+int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog);
+FLUIDSYNTH_API
+int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog,
+ int apply);
+FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan);
+FLUIDSYNTH_API
+int fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply);
+FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth);
+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);
+
+/* Misc */
+
+FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t* synth);
+FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth);
+
+
+/*
+ * Synthesizer plugin
+ *
+ * To create a synthesizer plugin, create the synthesizer as
+ * explained above. Once the synthesizer is created you can call
+ * any of the functions below to get the audio.
+ */
+
+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,
+ float** left, float** right,
+ float** fx_left, float** fx_right);
+FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len,
+ int nin, float** in,
+ int nout, float** out);
+
+/**
+ * Type definition of the synthesizer's audio callback function.
+ * @param synth FluidSynth instance
+ * @param len Count of audio frames to synthesize
+ * @param out1 Array to store left channel of audio to
+ * @param loff Offset index in 'out1' for first sample
+ * @param lincr Increment between samples stored to 'out1'
+ * @param out2 Array to store right channel of audio to
+ * @param roff Offset index in 'out2' for first sample
+ * @param rincr Increment between samples stored to 'out2'
+ */
+typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len,
+ void* out1, int loff, int lincr,
+ void* out2, int roff, int rincr);
+
+/* Synthesizer's interface to handle SoundFont loaders */
+
+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);
+FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth,
+ fluid_midi_router_t* router);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_SYNTH_H */
diff --git a/libs/fluidsynth/fluidsynth/types.h b/libs/fluidsynth/fluidsynth/types.h
new file mode 100644
index 0000000000..e956d818db
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/types.h
@@ -0,0 +1,68 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_TYPES_H
+#define _FLUIDSYNTH_TYPES_H
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @file types.h
+ * @brief Type declarations
+ */
+
+typedef struct _fluid_hashtable_t fluid_settings_t; /**< Configuration settings instance */
+typedef struct _fluid_synth_t fluid_synth_t; /**< Synthesizer instance */
+typedef struct _fluid_synth_channel_info_t fluid_synth_channel_info_t; /**< SoundFont channel info */
+typedef struct _fluid_voice_t fluid_voice_t; /**< Synthesis voice instance */
+typedef struct _fluid_sfloader_t fluid_sfloader_t; /**< SoundFont loader plugin */
+typedef struct _fluid_sfont_t fluid_sfont_t; /**< SoundFont */
+typedef struct _fluid_preset_t fluid_preset_t; /**< SoundFont preset */
+typedef struct _fluid_sample_t fluid_sample_t; /**< SoundFont sample */
+typedef struct _fluid_mod_t fluid_mod_t; /**< SoundFont modulator */
+typedef struct _fluid_audio_driver_t fluid_audio_driver_t; /**< Audio driver instance */
+typedef struct _fluid_file_renderer_t fluid_file_renderer_t; /**< Audio file renderer instance */
+typedef struct _fluid_player_t fluid_player_t; /**< MIDI player instance */
+typedef struct _fluid_midi_event_t fluid_midi_event_t; /**< MIDI event */
+typedef struct _fluid_midi_driver_t fluid_midi_driver_t; /**< MIDI driver instance */
+typedef struct _fluid_midi_router_t fluid_midi_router_t; /**< MIDI router instance */
+typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t; /**< MIDI router rule */
+typedef struct _fluid_hashtable_t fluid_cmd_handler_t; /**< Command handler */
+typedef struct _fluid_shell_t fluid_shell_t; /**< Command shell */
+typedef struct _fluid_server_t fluid_server_t; /**< TCP/IP shell server instance */
+typedef struct _fluid_event_t fluid_event_t; /**< Sequencer event */
+typedef struct _fluid_sequencer_t fluid_sequencer_t; /**< Sequencer instance */
+typedef struct _fluid_ramsfont_t fluid_ramsfont_t; /**< RAM SoundFont */
+typedef struct _fluid_rampreset_t fluid_rampreset_t; /**< RAM SoundFont preset */
+
+typedef int fluid_istream_t; /**< Input stream descriptor */
+typedef int fluid_ostream_t; /**< Output stream descriptor */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_TYPES_H */
diff --git a/libs/fluidsynth/fluidsynth/voice.h b/libs/fluidsynth/fluidsynth/voice.h
new file mode 100644
index 0000000000..fe7ad8c1ab
--- /dev/null
+++ b/libs/fluidsynth/fluidsynth/voice.h
@@ -0,0 +1,64 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUIDSYNTH_VOICE_H
+#define _FLUIDSYNTH_VOICE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file voice.h
+ * @brief Synthesis voice manipulation functions.
+ *
+ * The interface to the synthesizer's voices.
+ * Examples on using them can be found in fluid_defsfont.c.
+ * Most of these functions should only be called from within synthesis context,
+ * such as the SoundFont loader's noteon method.
+ */
+
+
+FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t* voice, int gen);
+
+/**
+ * Enum used with fluid_voice_add_mod() to specify how to handle duplicate modulators.
+ */
+enum fluid_voice_add_mod {
+ FLUID_VOICE_OVERWRITE, /**< Overwrite any existing matching modulator */
+ FLUID_VOICE_ADD, /**< Add (sum) modulator amounts */
+ FLUID_VOICE_DEFAULT /**< For default modulators only, no need to check for duplicates */
+};
+
+FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode);
+FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val);
+FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen);
+FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val);
+
+FLUIDSYNTH_API unsigned int fluid_voice_get_id(fluid_voice_t* voice);
+FLUIDSYNTH_API int fluid_voice_is_playing(fluid_voice_t* voice);
+FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FLUIDSYNTH_VOICE_H */
+
diff --git a/libs/fluidsynth/src/fluid_adsr_env.c b/libs/fluidsynth/src/fluid_adsr_env.c
new file mode 100644
index 0000000000..1d31fdb5e6
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_adsr_env.c
@@ -0,0 +1,38 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_adsr_env.h"
+
+void
+fluid_adsr_env_set_data(fluid_adsr_env_t* env,
+ fluid_adsr_env_section_t section,
+ unsigned int count,
+ fluid_real_t coeff,
+ fluid_real_t increment,
+ fluid_real_t min,
+ fluid_real_t max)
+{
+ env->data[section].count = count;
+ env->data[section].coeff = coeff;
+ env->data[section].increment = increment;
+ env->data[section].min = min;
+ env->data[section].max = max;
+}
+
diff --git a/libs/fluidsynth/src/fluid_adsr_env.h b/libs/fluidsynth/src/fluid_adsr_env.h
new file mode 100644
index 0000000000..31303a9ce9
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_adsr_env.h
@@ -0,0 +1,162 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_ADSR_ENVELOPE_H
+#define _FLUID_ADSR_ENVELOPE_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_sys.h"
+
+/*
+ * envelope data
+ */
+struct _fluid_env_data_t {
+ unsigned int count;
+ fluid_real_t coeff;
+ fluid_real_t increment;
+ fluid_real_t min;
+ fluid_real_t max;
+};
+
+/* Indices for envelope tables */
+enum fluid_voice_envelope_index_t{
+ FLUID_VOICE_ENVDELAY,
+ FLUID_VOICE_ENVATTACK,
+ FLUID_VOICE_ENVHOLD,
+ FLUID_VOICE_ENVDECAY,
+ FLUID_VOICE_ENVSUSTAIN,
+ FLUID_VOICE_ENVRELEASE,
+ FLUID_VOICE_ENVFINISHED,
+ FLUID_VOICE_ENVLAST
+};
+
+typedef enum fluid_voice_envelope_index_t fluid_adsr_env_section_t;
+
+typedef struct _fluid_adsr_env_t fluid_adsr_env_t;
+
+struct _fluid_adsr_env_t {
+ fluid_env_data_t data[FLUID_VOICE_ENVLAST];
+ unsigned int count;
+ int section;
+ fluid_real_t val; /* the current value of the envelope */
+};
+
+/* For performance, all functions are inlined */
+
+static FLUID_INLINE void
+fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
+{
+ fluid_env_data_t* env_data;
+ fluid_real_t x;
+
+ env_data = &env->data[env->section];
+
+ /* skip to the next section of the envelope if necessary */
+ while (env->count >= env_data->count)
+ {
+ // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage
+ // Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH]
+ if (env->section == FLUID_VOICE_ENVDECAY && is_volenv)
+ env->val = env_data->min * env_data->coeff;
+
+ env_data = &env->data[++env->section];
+ env->count = 0;
+ }
+
+ /* calculate the envelope value and check for valid range */
+ x = env_data->coeff * env->val + env_data->increment;
+
+ if (x < env_data->min)
+ {
+ x = env_data->min;
+ env->section++;
+ env->count = 0;
+ }
+ else if (x > env_data->max)
+ {
+ x = env_data->max;
+ env->section++;
+ env->count = 0;
+ }
+
+ env->val = x;
+ env->count++;
+}
+
+/* This one cannot be inlined since it is referenced in
+ the event queue */
+void
+fluid_adsr_env_set_data(fluid_adsr_env_t* env,
+ fluid_adsr_env_section_t section,
+ unsigned int count,
+ fluid_real_t coeff,
+ fluid_real_t increment,
+ fluid_real_t min,
+ fluid_real_t max);
+
+static inline void
+fluid_adsr_env_reset(fluid_adsr_env_t* env)
+{
+ env->count = 0;
+ env->section = 0;
+ env->val = 0.0f;
+}
+
+static inline fluid_real_t
+fluid_adsr_env_get_val(fluid_adsr_env_t* env)
+{
+ return env->val;
+}
+
+static inline void
+fluid_adsr_env_set_val(fluid_adsr_env_t* env, fluid_real_t val)
+{
+ env->val = val;
+}
+
+static inline fluid_adsr_env_section_t
+fluid_adsr_env_get_section(fluid_adsr_env_t* env)
+{
+ return env->section;
+}
+
+static inline void
+fluid_adsr_env_set_section(fluid_adsr_env_t* env,
+ fluid_adsr_env_section_t section)
+{
+ env->section = section;
+ env->count = 0;
+}
+
+/* Used for determining which voice to kill.
+ Returns max amplitude from now, and forward in time.
+*/
+static inline fluid_real_t
+fluid_adsr_env_get_max_val(fluid_adsr_env_t* env)
+{
+ if (env->section > FLUID_VOICE_ENVATTACK){
+ return env->val * 1000;
+ } else {
+ return env->data[FLUID_VOICE_ENVATTACK].max;
+ }
+}
+
+#endif
+
diff --git a/libs/fluidsynth/src/fluid_chan.c b/libs/fluidsynth/src/fluid_chan.c
new file mode 100644
index 0000000000..c6eb723146
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_chan.c
@@ -0,0 +1,291 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_chan.h"
+#include "fluid_mod.h"
+#include "fluid_synth.h"
+#include "fluid_sfont.h"
+
+/* Field shift amounts for sfont_bank_prog bit field integer */
+#define PROG_SHIFTVAL 0
+#define BANK_SHIFTVAL 8
+#define SFONT_SHIFTVAL 22
+
+/* Field mask values for sfont_bank_prog bit field integer */
+#define PROG_MASKVAL 0x000000FF /* Bit 7 is used to indicate unset state */
+#define BANK_MASKVAL 0x003FFF00
+#define BANKLSB_MASKVAL 0x00007F00
+#define BANKMSB_MASKVAL 0x003F8000
+#define SFONT_MASKVAL 0xFFC00000
+
+
+static void fluid_channel_init(fluid_channel_t* chan);
+
+
+fluid_channel_t*
+new_fluid_channel(fluid_synth_t* synth, int num)
+{
+ fluid_channel_t* chan;
+
+ chan = FLUID_NEW(fluid_channel_t);
+ if (chan == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ chan->synth = synth;
+ chan->channum = num;
+ chan->preset = NULL;
+ chan->tuning = NULL;
+
+ fluid_channel_init(chan);
+ fluid_channel_init_ctrl(chan, 0);
+
+ return chan;
+}
+
+static void
+fluid_channel_init(fluid_channel_t* chan)
+{
+ fluid_preset_t *newpreset;
+ int prognum, banknum;
+
+ chan->sostenuto_orderid = 0;
+
+ chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC;
+ prognum = 0;
+ banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0;
+
+ chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL
+ | prognum << PROG_SHIFTVAL;
+
+ newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum);
+ fluid_channel_set_preset(chan, newpreset);
+
+ chan->interp_method = FLUID_INTERP_DEFAULT;
+ chan->tuning_bank = 0;
+ chan->tuning_prog = 0;
+ chan->nrpn_select = 0;
+ chan->nrpn_active = 0;
+
+ if (chan->tuning)
+ {
+ fluid_tuning_unref (chan->tuning, 1);
+ chan->tuning = NULL;
+ }
+}
+
+/*
+ @param is_all_ctrl_off if nonzero, only resets some controllers, according to
+ http://www.midi.org/techspecs/rp15.php
+*/
+void
+fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off)
+{
+ int i;
+
+ chan->key_pressure = 0;
+ chan->channel_pressure = 0;
+ chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */
+
+ for (i = 0; i < GEN_LAST; i++) {
+ chan->gen[i] = 0.0f;
+ chan->gen_abs[i] = 0;
+ }
+
+ if (is_all_ctrl_off) {
+ for (i = 0; i < ALL_SOUND_OFF; i++) {
+ if (i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) {
+ continue;
+ }
+ if (i >= SOUND_CTRL1 && i <= SOUND_CTRL10) {
+ continue;
+ }
+ if (i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB ||
+ i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB) {
+ continue;
+ }
+
+ fluid_channel_set_cc (chan, i, 0);
+ }
+ }
+ else {
+ for (i = 0; i < 128; i++) {
+ fluid_channel_set_cc (chan, i, 0);
+ }
+ }
+
+ /* Set RPN controllers to NULL state */
+ fluid_channel_set_cc (chan, RPN_LSB, 127);
+ fluid_channel_set_cc (chan, RPN_MSB, 127);
+
+ /* Set NRPN controllers to NULL state */
+ fluid_channel_set_cc (chan, NRPN_LSB, 127);
+ fluid_channel_set_cc (chan, NRPN_MSB, 127);
+
+ /* Expression (MSB & LSB) */
+ fluid_channel_set_cc (chan, EXPRESSION_MSB, 127);
+ fluid_channel_set_cc (chan, EXPRESSION_LSB, 127);
+
+ if (!is_all_ctrl_off) {
+
+ chan->pitch_wheel_sensitivity = 2; /* two semi-tones */
+
+ /* Just like panning, a value of 64 indicates no change for sound ctrls */
+ for (i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) {
+ fluid_channel_set_cc (chan, i, 64);
+ }
+
+ /* Volume / initial attenuation (MSB & LSB) */
+ fluid_channel_set_cc (chan, VOLUME_MSB, 100);
+ fluid_channel_set_cc (chan, VOLUME_LSB, 0);
+
+ /* Pan (MSB & LSB) */
+ fluid_channel_set_cc (chan, PAN_MSB, 64);
+ fluid_channel_set_cc (chan, PAN_LSB, 0);
+
+ /* Reverb */
+ /* 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 */
+ }
+}
+
+/* Only called by delete_fluid_synth(), so no need to queue a preset free event */
+int
+delete_fluid_channel(fluid_channel_t* chan)
+{
+ if (chan->preset) delete_fluid_preset (chan->preset);
+ FLUID_FREE(chan);
+ return FLUID_OK;
+}
+
+/* FIXME - Calls fluid_channel_init() potentially in synthesis context */
+void
+fluid_channel_reset(fluid_channel_t* chan)
+{
+ fluid_channel_init(chan);
+ fluid_channel_init_ctrl(chan, 0);
+}
+
+/* Should only be called from synthesis context */
+int
+fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset)
+{
+
+ fluid_preset_notify (chan->preset, FLUID_PRESET_UNSELECTED, chan->channum);
+
+ if (chan->preset) {
+ fluid_sfont_t *sfont;
+ sfont = chan->preset->sfont;
+ delete_fluid_preset (chan->preset);
+ fluid_synth_sfont_unref (chan->synth, sfont); /* -- unref preset's SoundFont */
+ }
+
+ chan->preset = preset;
+
+ fluid_preset_notify (preset, FLUID_PRESET_SELECTED, chan->channum);
+
+ return FLUID_OK;
+}
+
+/* Set SoundFont ID, MIDI bank and/or program. Use -1 to use current value. */
+void
+fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfontnum,
+ int banknum, int prognum)
+{
+ int oldval, newval, oldmask;
+
+ newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0)
+ | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0)
+ | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0);
+
+ oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL)
+ | ((banknum != -1) ? 0 : BANK_MASKVAL)
+ | ((prognum != -1) ? 0 : PROG_MASKVAL);
+
+ oldval = chan->sfont_bank_prog;
+ newval = (newval & ~oldmask) | (oldval & oldmask);
+ chan->sfont_bank_prog = newval;
+}
+
+/* Set bank LSB 7 bits */
+void
+fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb)
+{
+ int oldval, newval, style;
+
+ style = chan->synth->bank_select;
+ if (style == FLUID_BANK_STYLE_GM ||
+ style == FLUID_BANK_STYLE_GS)
+ return; /* ignored */
+
+ oldval = chan->sfont_bank_prog;
+ if (style == FLUID_BANK_STYLE_XG)
+ newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL);
+ else /* style == FLUID_BANK_STYLE_MMA */
+ newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL);
+ chan->sfont_bank_prog = newval;
+}
+
+/* Set bank MSB 7 bits */
+void
+fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb)
+{
+ int oldval, newval, style;
+
+ style = chan->synth->bank_select;
+
+ if (style == FLUID_BANK_STYLE_XG)
+ {
+ /* 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 */
+ chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC;
+ return;
+ }
+
+ if (style == FLUID_BANK_STYLE_GM ||
+ chan->channel_type == CHANNEL_TYPE_DRUM)
+ return; /* ignored */
+
+ oldval = chan->sfont_bank_prog;
+ if (style == FLUID_BANK_STYLE_GS)
+ newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL);
+ else /* style == FLUID_BANK_STYLE_MMA */
+ newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7));
+ chan->sfont_bank_prog = newval;
+
+}
+
+/* Get SoundFont ID, MIDI bank and/or program. Use NULL to ignore a value. */
+void
+fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont,
+ int *bank, int *prog)
+{
+ int sfont_bank_prog;
+
+ sfont_bank_prog = chan->sfont_bank_prog;
+
+ if (sfont) *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL;
+ if (bank) *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL;
+ if (prog) *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL;
+}
diff --git a/libs/fluidsynth/src/fluid_chan.h b/libs/fluidsynth/src/fluid_chan.h
new file mode 100644
index 0000000000..85aa1ef00c
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_chan.h
@@ -0,0 +1,149 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_CHAN_H
+#define _FLUID_CHAN_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_midi.h"
+#include "fluid_tuning.h"
+
+/*
+ * fluid_channel_t
+ *
+ * Mutual exclusion notes (as of 1.1.2):
+ * None - everything should have been synchronized by the synth.
+ */
+struct _fluid_channel_t
+{
+ fluid_mutex_t mutex; /* Lock for thread sensitive parameters */
+
+ fluid_synth_t* synth; /**< Parent synthesizer instance */
+ int channum; /**< MIDI channel number */
+
+ int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */
+ fluid_preset_t* preset; /**< Selected preset */
+
+ int key_pressure; /**< MIDI key pressure */
+ int channel_pressure; /**< MIDI channel pressure */
+ int pitch_bend; /**< Current pitch bend value */
+ int pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */
+
+ int cc[128]; /**< MIDI controller values */
+
+ /* Sostenuto order id gives the order of SostenutoOn event.
+ This value is useful to known when the sostenuto pedal is depressed
+ (before or after a key note). We need to compare SostenutoOrderId with voice id.
+ */
+ unsigned int sostenuto_orderid;
+ int interp_method; /**< Interpolation method (enum fluid_interp) */
+ fluid_tuning_t* tuning; /**< Micro tuning */
+ int tuning_bank; /**< Current tuning bank number */
+ int tuning_prog; /**< Current tuning program number */
+
+ /* NRPN system */
+ int nrpn_select; /* Generator ID of SoundFont NRPN message */
+ int nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */
+
+ /* The values of the generators, set by NRPN messages, or by
+ * fluid_synth_set_gen(), are cached in the channel so they can be
+ * applied to future notes. They are copied to a voice's generators
+ * in fluid_voice_init(), which calls fluid_gen_init(). */
+ fluid_real_t gen[GEN_LAST];
+
+ /* By default, the NRPN values are relative to the values of the
+ * generators set in the SoundFont. For example, if the NRPN
+ * specifies an attack of 100 msec then 100 msec will be added to the
+ * combined attack time of the sound font and the modulators.
+ *
+ * However, it is useful to be able to specify the generator value
+ * absolutely, completely ignoring the generators of the SoundFont
+ * and the values of modulators. The gen_abs field, is a boolean
+ * flag indicating whether the NRPN value is absolute or not.
+ */
+ char gen_abs[GEN_LAST];
+
+ /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */
+ int channel_type;
+
+};
+
+fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num);
+void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off);
+int delete_fluid_channel(fluid_channel_t* chan);
+void fluid_channel_reset(fluid_channel_t* chan);
+int fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset);
+fluid_preset_t* fluid_channel_get_preset(fluid_channel_t* chan);
+void fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfont,
+ int bank, int prog);
+void fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb);
+void fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb);
+void fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont,
+ int *bank, int *prog);
+int fluid_channel_get_num(fluid_channel_t* chan);
+void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method);
+int fluid_channel_get_interp_method(fluid_channel_t* chan);
+
+#define fluid_channel_get_preset(chan) ((chan)->preset)
+#define fluid_channel_set_cc(chan, num, val) \
+ ((chan)->cc[num] = (val))
+#define fluid_channel_get_cc(chan, num) \
+ ((chan)->cc[num])
+#define fluid_channel_get_key_pressure(chan) \
+ ((chan)->key_pressure)
+#define fluid_channel_set_key_pressure(chan, val) \
+ ((chan)->key_pressure = (val))
+#define fluid_channel_get_channel_pressure(chan) \
+ ((chan)->channel_pressure)
+#define fluid_channel_set_channel_pressure(chan, val) \
+ ((chan)->channel_pressure = (val))
+#define fluid_channel_get_pitch_bend(chan) \
+ ((chan)->pitch_bend)
+#define fluid_channel_set_pitch_bend(chan, val) \
+ ((chan)->pitch_bend = (val))
+#define fluid_channel_get_pitch_wheel_sensitivity(chan) \
+ ((chan)->pitch_wheel_sensitivity)
+#define fluid_channel_set_pitch_wheel_sensitivity(chan, val) \
+ ((chan)->pitch_wheel_sensitivity = (val))
+#define fluid_channel_get_num(chan) ((chan)->channum)
+#define fluid_channel_set_interp_method(chan, new_method) \
+ ((chan)->interp_method = (new_method))
+#define fluid_channel_get_interp_method(chan) \
+ ((chan)->interp_method);
+#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; }
+#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL)
+#define fluid_channel_get_tuning(_c) ((_c)->tuning)
+#define fluid_channel_get_tuning_bank(chan) \
+ ((chan)->tuning_bank)
+#define fluid_channel_set_tuning_bank(chan, bank) \
+ ((chan)->tuning_bank = (bank))
+#define fluid_channel_get_tuning_prog(chan) \
+ ((chan)->tuning_prog)
+#define fluid_channel_set_tuning_prog(chan, prog) \
+ ((chan)->tuning_prog = (prog))
+#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64)
+#define fluid_channel_sostenuto(_c) ((_c)->cc[SOSTENUTO_SWITCH] >= 64)
+#define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; }
+#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n])
+#define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n])
+#define fluid_channel_get_min_note_length_ticks(chan) \
+ ((chan)->synth->min_note_length_ticks)
+
+#endif /* _FLUID_CHAN_H */
diff --git a/libs/fluidsynth/src/fluid_chorus.c b/libs/fluidsynth/src/fluid_chorus.c
new file mode 100644
index 0000000000..4bead5ce2d
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_chorus.c
@@ -0,0 +1,506 @@
+/*
+ * August 24, 1998
+ * Copyright (C) 1998 Juergen Mueller And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Juergen Mueller And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+/*
+
+ CHANGES
+
+ - Adapted for fluidsynth, Peter Hanappe, March 2002
+
+ - Variable delay line implementation using bandlimited
+ interpolation, code reorganization: Markus Nentwig May 2002
+
+ */
+
+
+/*
+ * Chorus effect.
+ *
+ * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ):
+ *
+ * * gain-in ___
+ * ibuff -----+--------------------------------------------->| |
+ * | _________ | |
+ * | | | * level 1 | |
+ * +---->| delay 1 |----------------------------->| |
+ * | |_________| | |
+ * | /|\ | |
+ * : | | |
+ * : +-----------------+ +--------------+ | + |
+ * : | Delay control 1 |<--| mod. speed 1 | | |
+ * : +-----------------+ +--------------+ | |
+ * | _________ | |
+ * | | | * level n | |
+ * +---->| delay n |----------------------------->| |
+ * |_________| | |
+ * /|\ |___|
+ * | |
+ * +-----------------+ +--------------+ | * gain-out
+ * | Delay control n |<--| mod. speed n | |
+ * +-----------------+ +--------------+ +----->obuff
+ *
+ *
+ * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n).
+ *
+ * The delay of each block is modulated between 0..depth ms
+ *
+ */
+
+
+/* Variable delay line implementation
+ * ==================================
+ *
+ * The modulated delay needs the value of the delayed signal between
+ * samples. A lowpass filter is used to obtain intermediate values
+ * between samples (bandlimited interpolation). The sample pulse
+ * train is convoluted with the impulse response of the low pass
+ * filter (sinc function). To make it work with a small number of
+ * samples, the sinc function is windowed (Hamming window).
+ *
+ */
+
+#include "fluid_chorus.h"
+#include "fluid_sys.h"
+
+#define MAX_CHORUS 99
+#define MAX_DELAY 100
+#define MAX_DEPTH 10
+#define MIN_SPEED_HZ 0.29
+#define MAX_SPEED_HZ 5
+
+/* Length of one delay line in samples:
+ * Set through MAX_SAMPLES_LN2.
+ * For example:
+ * MAX_SAMPLES_LN2=12
+ * => MAX_SAMPLES=pow(2,12)=4096
+ * => MAX_SAMPLES_ANDMASK=4095
+ */
+#define MAX_SAMPLES_LN2 12
+
+#define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1))
+#define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1)
+
+
+/* Interpolate how many steps between samples? Must be power of two
+ For example: 8 => use a resolution of 256 steps between any two
+ samples
+*/
+#define INTERPOLATION_SUBSAMPLES_LN2 8
+#define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1))
+#define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1)
+
+/* Use how many samples for interpolation? Must be odd. '7' sounds
+ relatively clean, when listening to the modulated delay signal
+ alone. For a demo on aliasing try '1' With '3', the aliasing is
+ still quite pronounced for some input frequencies
+*/
+#define INTERPOLATION_SAMPLES 5
+
+/* Private data for SKEL file */
+struct _fluid_chorus_t {
+ int type;
+ fluid_real_t depth_ms;
+ fluid_real_t level;
+ fluid_real_t speed_Hz;
+ int number_blocks;
+
+ fluid_real_t *chorusbuf;
+ int counter;
+ long phase[MAX_CHORUS];
+ long modulation_period_samples;
+ int *lookup_tab;
+ fluid_real_t sample_rate;
+
+ /* sinc lookup table */
+ fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES];
+};
+
+static void fluid_chorus_triangle(int *buf, int len, int depth);
+static void fluid_chorus_sine(int *buf, int len, int depth);
+
+
+fluid_chorus_t*
+new_fluid_chorus(fluid_real_t sample_rate)
+{
+ int i; int ii;
+ fluid_chorus_t* chorus;
+
+ chorus = FLUID_NEW(fluid_chorus_t);
+ if (chorus == NULL) {
+ fluid_log(FLUID_PANIC, "chorus: Out of memory");
+ return NULL;
+ }
+
+ FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t));
+
+ chorus->sample_rate = sample_rate;
+
+ /* Lookup table for the SI function (impulse response of an ideal low pass) */
+
+ /* i: Offset in terms of whole samples */
+ for (i = 0; i < INTERPOLATION_SAMPLES; i++){
+
+ /* ii: Offset in terms of fractional samples ('subsamples') */
+ for (ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++){
+ /* Move the origin into the center of the table */
+ double i_shifted = ((double) i- ((double) INTERPOLATION_SAMPLES) / 2.
+ + (double) ii / (double) INTERPOLATION_SUBSAMPLES);
+ if (fabs(i_shifted) < 0.000001) {
+ /* sinc(0) cannot be calculated straightforward (limit needed
+ for 0/0) */
+ chorus->sinc_table[i][ii] = (fluid_real_t)1.;
+
+ } else {
+ chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted);
+ /* Hamming window */
+ chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES));
+ };
+ };
+ };
+
+ /* allocate lookup tables */
+ chorus->lookup_tab = FLUID_ARRAY(int, (int) (chorus->sample_rate / MIN_SPEED_HZ));
+ if (chorus->lookup_tab == NULL) {
+ fluid_log(FLUID_PANIC, "chorus: Out of memory");
+ goto error_recovery;
+ }
+
+ /* allocate sample buffer */
+
+ chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES);
+ if (chorus->chorusbuf == NULL) {
+ fluid_log(FLUID_PANIC, "chorus: Out of memory");
+ goto error_recovery;
+ }
+
+ if (fluid_chorus_init(chorus) != FLUID_OK){
+ goto error_recovery;
+ };
+
+ return chorus;
+
+ error_recovery:
+ delete_fluid_chorus(chorus);
+ return NULL;
+}
+
+void
+delete_fluid_chorus(fluid_chorus_t* chorus)
+{
+ if (chorus == NULL) {
+ return;
+ }
+
+ if (chorus->chorusbuf != NULL) {
+ FLUID_FREE(chorus->chorusbuf);
+ }
+
+ if (chorus->lookup_tab != NULL) {
+ FLUID_FREE(chorus->lookup_tab);
+ }
+
+ FLUID_FREE(chorus);
+}
+
+int
+fluid_chorus_init(fluid_chorus_t* chorus)
+{
+ int i;
+
+ for (i = 0; i < MAX_SAMPLES; i++) {
+ chorus->chorusbuf[i] = 0.0;
+ }
+
+ /* initialize the chorus with the default settings */
+ fluid_chorus_set (chorus, FLUID_CHORUS_SET_ALL, FLUID_CHORUS_DEFAULT_N,
+ FLUID_CHORUS_DEFAULT_LEVEL, FLUID_CHORUS_DEFAULT_SPEED,
+ FLUID_CHORUS_DEFAULT_DEPTH, FLUID_CHORUS_MOD_SINE);
+ return FLUID_OK;
+}
+
+void
+fluid_chorus_reset(fluid_chorus_t* chorus)
+{
+ fluid_chorus_init(chorus);
+}
+
+/**
+ * Set one or more chorus parameters.
+ * @param chorus Chorus instance
+ * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t)
+ * @param nr Chorus voice count (0-99, CPU time consumption proportional to
+ * this value)
+ * @param level Chorus level (0.0-10.0)
+ * @param speed Chorus speed in Hz (0.29-5.0)
+ * @param depth_ms Chorus depth (max value depends on synth sample rate,
+ * 0.0-21.0 is safe for sample rate values up to 96KHz)
+ * @param type Chorus waveform type (#fluid_chorus_mod)
+ */
+void
+fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level,
+ float speed, float depth_ms, int type)
+{
+ int modulation_depth_samples;
+ int i;
+
+ if (set & FLUID_CHORUS_SET_NR) chorus->number_blocks = nr;
+ if (set & FLUID_CHORUS_SET_LEVEL) chorus->level = level;
+ if (set & FLUID_CHORUS_SET_SPEED) chorus->speed_Hz = speed;
+ if (set & FLUID_CHORUS_SET_DEPTH) chorus->depth_ms = depth_ms;
+ if (set & FLUID_CHORUS_SET_TYPE) chorus->type = type;
+
+ if (chorus->number_blocks < 0) {
+ fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0.");
+ chorus->number_blocks = 0;
+ } else if (chorus->number_blocks > MAX_CHORUS) {
+ fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.",
+ MAX_CHORUS);
+ chorus->number_blocks = MAX_CHORUS;
+ }
+
+ if (chorus->speed_Hz < MIN_SPEED_HZ) {
+ fluid_log(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.",
+ (double) MIN_SPEED_HZ);
+ chorus->speed_Hz = MIN_SPEED_HZ;
+ } else if (chorus->speed_Hz > MAX_SPEED_HZ) {
+ fluid_log(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.",
+ (double) MAX_SPEED_HZ);
+ chorus->speed_Hz = MAX_SPEED_HZ;
+ }
+
+ if (chorus->depth_ms < 0.0) {
+ fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0.");
+ chorus->depth_ms = 0.0;
+ }
+ /* Depth: Check for too high value through modulation_depth_samples. */
+
+ if (chorus->level < 0.0) {
+ fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0.");
+ chorus->level = 0.0;
+ } else if (chorus->level > 10) {
+ fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! "
+ "Setting it to 0.1.");
+ chorus->level = 0.1;
+ }
+
+ /* The modulating LFO goes through a full period every x samples: */
+ chorus->modulation_period_samples = chorus->sample_rate / chorus->speed_Hz;
+
+ /* The variation in delay time is x: */
+ modulation_depth_samples = (int)
+ (chorus->depth_ms / 1000.0 /* convert modulation depth in ms to s*/
+ * chorus->sample_rate);
+
+ if (modulation_depth_samples > MAX_SAMPLES) {
+ fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES);
+ modulation_depth_samples = MAX_SAMPLES;
+ }
+
+ /* initialize LFO table */
+ if (chorus->type == FLUID_CHORUS_MOD_SINE) {
+ fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
+ modulation_depth_samples);
+ } else if (chorus->type == FLUID_CHORUS_MOD_TRIANGLE) {
+ fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples,
+ modulation_depth_samples);
+ } else {
+ fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave.");
+ chorus->type = FLUID_CHORUS_MOD_SINE;
+ fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
+ modulation_depth_samples);
+ }
+
+ for (i = 0; i < chorus->number_blocks; i++) {
+ /* Set the phase of the chorus blocks equally spaced */
+ chorus->phase[i] = (int) ((double) chorus->modulation_period_samples
+ * (double) i / (double) chorus->number_blocks);
+ }
+
+ /* Start of the circular buffer */
+ chorus->counter = 0;
+}
+
+
+void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int sample_index;
+ int i;
+ fluid_real_t d_in, d_out;
+
+ for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
+
+ d_in = in[sample_index];
+ d_out = 0.0f;
+
+# if 0
+ /* Debug: Listen to the chorus signal only */
+ left_out[sample_index]=0;
+ right_out[sample_index]=0;
+#endif
+
+ /* Write the current sample into the circular buffer */
+ chorus->chorusbuf[chorus->counter] = d_in;
+
+ for (i = 0; i < chorus->number_blocks; i++) {
+ int ii;
+ /* Calculate the delay in subsamples for the delay line of chorus block nr. */
+
+ /* The value in the lookup table is so, that this expression
+ * will always be positive. It will always include a number of
+ * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
+ * remain positive at all times. */
+ int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
+ - chorus->lookup_tab[chorus->phase[i]]);
+
+ int pos_samples = pos_subsamples/INTERPOLATION_SUBSAMPLES;
+
+ /* modulo divide by INTERPOLATION_SUBSAMPLES */
+ pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+
+ for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
+ /* Add the delayed signal to the chorus sum d_out Note: The
+ * delay in the delay line moves backwards for increasing
+ * delay!*/
+
+ /* The & in chorusbuf[...] is equivalent to a division modulo
+ MAX_SAMPLES, only faster. */
+ d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
+ * chorus->sinc_table[ii][pos_subsamples];
+
+ pos_samples--;
+ };
+ /* Cycle the phase of the modulating LFO */
+ chorus->phase[i]++;
+ chorus->phase[i] %= (chorus->modulation_period_samples);
+ } /* foreach chorus block */
+
+ d_out *= chorus->level;
+
+ /* Add the chorus sum d_out to output */
+ left_out[sample_index] += d_out;
+ right_out[sample_index] += d_out;
+
+ /* Move forward in circular buffer */
+ chorus->counter++;
+ chorus->counter %= MAX_SAMPLES;
+
+ } /* foreach sample */
+}
+
+/* Duplication of code ... (replaces sample data instead of mixing) */
+void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int sample_index;
+ int i;
+ fluid_real_t d_in, d_out;
+
+ for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
+
+ d_in = in[sample_index];
+ d_out = 0.0f;
+
+# if 0
+ /* Debug: Listen to the chorus signal only */
+ left_out[sample_index]=0;
+ right_out[sample_index]=0;
+#endif
+
+ /* Write the current sample into the circular buffer */
+ chorus->chorusbuf[chorus->counter] = d_in;
+
+ for (i = 0; i < chorus->number_blocks; i++) {
+ int ii;
+ /* Calculate the delay in subsamples for the delay line of chorus block nr. */
+
+ /* The value in the lookup table is so, that this expression
+ * will always be positive. It will always include a number of
+ * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
+ * remain positive at all times. */
+ int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
+ - chorus->lookup_tab[chorus->phase[i]]);
+
+ int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
+
+ /* modulo divide by INTERPOLATION_SUBSAMPLES */
+ pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+
+ for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
+ /* Add the delayed signal to the chorus sum d_out Note: The
+ * delay in the delay line moves backwards for increasing
+ * delay!*/
+
+ /* The & in chorusbuf[...] is equivalent to a division modulo
+ MAX_SAMPLES, only faster. */
+ d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
+ * chorus->sinc_table[ii][pos_subsamples];
+
+ pos_samples--;
+ };
+ /* Cycle the phase of the modulating LFO */
+ chorus->phase[i]++;
+ chorus->phase[i] %= (chorus->modulation_period_samples);
+ } /* foreach chorus block */
+
+ d_out *= chorus->level;
+
+ /* Store the chorus sum d_out to output */
+ left_out[sample_index] = d_out;
+ right_out[sample_index] = d_out;
+
+ /* Move forward in circular buffer */
+ chorus->counter++;
+ chorus->counter %= MAX_SAMPLES;
+
+ } /* foreach sample */
+}
+
+/* Purpose:
+ *
+ * Calculates a modulation waveform (sine) Its value ( modulo
+ * MAXSAMPLES) varies between 0 and depth*INTERPOLATION_SUBSAMPLES.
+ * Its period length is len. The waveform data will be used modulo
+ * MAXSAMPLES only. Since MAXSAMPLES is substracted from the waveform
+ * a couple of times here, the resulting (current position in
+ * buffer)-(waveform sample) will always be positive.
+ */
+static void
+fluid_chorus_sine(int *buf, int len, int depth)
+{
+ int i;
+ double val;
+
+ for (i = 0; i < len; i++) {
+ val = sin((double) i / (double)len * 2.0 * M_PI);
+ buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES);
+ buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
+ // printf("%i %i\n",i,buf[i]);
+ }
+}
+
+/* Purpose:
+ * Calculates a modulation waveform (triangle)
+ * See fluid_chorus_sine for comments.
+ */
+static void
+fluid_chorus_triangle(int *buf, int len, int depth)
+{
+ int i=0;
+ int ii=len-1;
+ double val;
+ double val2;
+
+ while (i <= ii){
+ val = i * 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES;
+ val2= (int) (val + 0.5) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
+ buf[i++] = (int) val2;
+ buf[ii--] = (int) val2;
+ }
+}
diff --git a/libs/fluidsynth/src/fluid_chorus.h b/libs/fluidsynth/src/fluid_chorus.h
new file mode 100644
index 0000000000..3422fa94b2
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_chorus.h
@@ -0,0 +1,61 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_CHORUS_H
+#define _FLUID_CHORUS_H
+
+#include "fluidsynth_priv.h"
+
+
+typedef struct _fluid_chorus_t fluid_chorus_t;
+
+/** Flags for fluid_chorus_set() */
+typedef enum
+{
+ FLUID_CHORUS_SET_NR = 1 << 0,
+ FLUID_CHORUS_SET_LEVEL = 1 << 1,
+ FLUID_CHORUS_SET_SPEED = 1 << 2,
+ FLUID_CHORUS_SET_DEPTH = 1 << 3,
+ FLUID_CHORUS_SET_TYPE = 1 << 4,
+} fluid_chorus_set_t;
+
+/** Value for fluid_chorus_set() which sets all chorus parameters. */
+#define FLUID_CHORUS_SET_ALL 0x1F
+
+/*
+ * chorus
+ */
+fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate);
+void delete_fluid_chorus(fluid_chorus_t* chorus);
+int fluid_chorus_init(fluid_chorus_t* chorus);
+void fluid_chorus_reset(fluid_chorus_t* chorus);
+
+void fluid_chorus_set(fluid_chorus_t* chorus, int set, int nr, float level,
+ float speed, float depth_ms, int type);
+
+void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out);
+void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out);
+
+
+
+#endif /* _FLUID_CHORUS_H */
diff --git a/libs/fluidsynth/src/fluid_conv.c b/libs/fluidsynth/src/fluid_conv.c
new file mode 100644
index 0000000000..1a790cfbfb
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_conv.c
@@ -0,0 +1,320 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_conv.h"
+
+
+/* conversion tables */
+fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
+fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
+fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE];
+fluid_real_t fluid_posbp_tab[128];
+fluid_real_t fluid_concave_tab[128];
+fluid_real_t fluid_convex_tab[128];
+fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
+
+/*
+ * void fluid_synth_init
+ *
+ * Does all the initialization for this module.
+ */
+void
+fluid_conversion_config(void)
+{
+ int i;
+ double x;
+
+ for (i = 0; i < FLUID_CENTS_HZ_SIZE; i++) {
+ fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0);
+ }
+
+ /* centibels to amplitude conversion
+ * Note: SF2.01 section 8.1.3: Initial attenuation range is
+ * between 0 and 144 dB. Therefore a negative attenuation is
+ * not allowed.
+ */
+ for (i = 0; i < FLUID_CB_AMP_SIZE; i++) {
+ fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0);
+ }
+
+ /* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont
+ * specification in regards to volume attenuation. The below calculation
+ * is an approx. equation for generating a table equivelant to the
+ * cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told
+ * was generated from device testing. By the spec this should be centibels.
+ */
+ for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) {
+ fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR);
+ }
+
+ /* initialize the conversion tables (see fluid_mod.c
+ fluid_mod_get_value cases 4 and 8) */
+
+ /* concave unipolar positive transform curve */
+ fluid_concave_tab[0] = 0.0;
+ fluid_concave_tab[127] = 1.0;
+
+ /* convex unipolar positive transform curve */
+ fluid_convex_tab[0] = 0;
+ fluid_convex_tab[127] = 1.0;
+ x = log10(128.0 / 127.0);
+
+ /* There seems to be an error in the specs. The equations are
+ implemented according to the pictures on SF2.01 page 73. */
+
+ for (i = 1; i < 127; i++) {
+ x = -20.0 / 96.0 * log((i * i) / (127.0 * 127.0)) / log(10.0);
+ fluid_convex_tab[i] = (fluid_real_t) (1.0 - x);
+ fluid_concave_tab[127 - i] = (fluid_real_t) x;
+ }
+
+ /* initialize the pan conversion table */
+ x = PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
+ for (i = 0; i < FLUID_PAN_SIZE; i++) {
+ fluid_pan_tab[i] = (fluid_real_t) sin(i * x);
+ }
+}
+
+/*
+ * fluid_ct2hz
+ */
+fluid_real_t
+fluid_ct2hz_real(fluid_real_t cents)
+{
+ if (cents < 0)
+ return (fluid_real_t) 1.0;
+ else if (cents < 900) {
+ return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int) (cents + 300)];
+ } else if (cents < 2100) {
+ return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int) (cents - 900)];
+ } else if (cents < 3300) {
+ return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int) (cents - 2100)];
+ } else if (cents < 4500) {
+ return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int) (cents - 3300)];
+ } else if (cents < 5700) {
+ return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int) (cents - 4500)];
+ } else if (cents < 6900) {
+ return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int) (cents - 5700)];
+ } else if (cents < 8100) {
+ return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int) (cents - 6900)];
+ } else if (cents < 9300) {
+ return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int) (cents - 8100)];
+ } else if (cents < 10500) {
+ return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int) (cents - 9300)];
+ } else if (cents < 11700) {
+ return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int) (cents - 10500)];
+ } else if (cents < 12900) {
+ return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int) (cents - 11700)];
+ } else if (cents < 14100) {
+ return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int) (cents - 12900)];
+ } else {
+ return (fluid_real_t) 1.0; /* some loony trying to make you deaf */
+ }
+}
+
+/*
+ * fluid_ct2hz
+ */
+fluid_real_t
+fluid_ct2hz(fluid_real_t cents)
+{
+ /* Filter fc limit: SF2.01 page 48 # 8 */
+ if (cents >= 13500){
+ cents = 13500; /* 20 kHz */
+ } else if (cents < 1500){
+ cents = 1500; /* 20 Hz */
+ }
+ return fluid_ct2hz_real(cents);
+}
+
+/*
+ * fluid_cb2amp
+ *
+ * in: a value between 0 and 960, 0 is no attenuation
+ * out: a value between 1 and 0
+ */
+fluid_real_t
+fluid_cb2amp(fluid_real_t cb)
+{
+ /*
+ * cb: an attenuation in 'centibels' (1/10 dB)
+ * SF2.01 page 49 # 48 limits it to 144 dB.
+ * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit.
+ */
+
+ /* minimum attenuation: 0 dB */
+ if (cb < 0) {
+ return 1.0;
+ }
+ if (cb >= FLUID_CB_AMP_SIZE) {
+ return 0.0;
+ }
+ return fluid_cb2amp_tab[(int) cb];
+}
+
+/*
+ * fluid_atten2amp
+ *
+ * in: a value between 0 and 1440, 0 is no attenuation
+ * out: a value between 1 and 0
+ *
+ * Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't
+ * follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp.
+ */
+fluid_real_t
+fluid_atten2amp(fluid_real_t atten)
+{
+ if (atten < 0) return 1.0;
+ else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0;
+ else return fluid_atten2amp_tab[(int) atten];
+}
+
+/*
+ * fluid_tc2sec_delay
+ */
+fluid_real_t
+fluid_tc2sec_delay(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 21, 23, 25, 33
+ * SF2.01 section 8.1.3 items 21, 23, 25, 33
+ *
+ * The most negative number indicates a delay of 0. Range is limited
+ * from -12000 to 5000 */
+ if (tc <= -32768.0f) {
+ return (fluid_real_t) 0.0f;
+ };
+ if (tc < -12000.) {
+ tc = (fluid_real_t) -12000.0f;
+ }
+ if (tc > 5000.0f) {
+ tc = (fluid_real_t) 5000.0f;
+ }
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec_attack
+ */
+fluid_real_t
+fluid_tc2sec_attack(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 26, 34
+ * SF2.01 section 8.1.3 items 26, 34
+ * The most negative number indicates a delay of 0
+ * Range is limited from -12000 to 8000 */
+ if (tc<=-32768.){return (fluid_real_t) 0.0;};
+ if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
+ if (tc>8000.){tc=(fluid_real_t) 8000.0;};
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec
+ */
+fluid_real_t
+fluid_tc2sec(fluid_real_t tc)
+{
+ /* No range checking here! */
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec_release
+ */
+fluid_real_t
+fluid_tc2sec_release(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 30, 38
+ * SF2.01 section 8.1.3 items 30, 38
+ * No 'most negative number' rule here!
+ * Range is limited from -12000 to 8000 */
+ if (tc<=-32768.){return (fluid_real_t) 0.0;};
+ if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
+ if (tc>8000.){tc=(fluid_real_t) 8000.0;};
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_act2hz
+ *
+ * Convert from absolute cents to Hertz
+ */
+fluid_real_t
+fluid_act2hz(fluid_real_t c)
+{
+ return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0));
+}
+
+/*
+ * fluid_hz2ct
+ *
+ * Convert from Hertz to cents
+ */
+fluid_real_t
+fluid_hz2ct(fluid_real_t f)
+{
+ return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0));
+}
+
+/*
+ * fluid_pan
+ */
+fluid_real_t
+fluid_pan(fluid_real_t c, int left)
+{
+ if (left) {
+ c = -c;
+ }
+ if (c < -500) {
+ return (fluid_real_t) 0.0;
+ } else if (c > 500) {
+ return (fluid_real_t) 1.0;
+ } else {
+ return fluid_pan_tab[(int) (c + 500)];
+ }
+}
+
+/*
+ * fluid_concave
+ */
+fluid_real_t
+fluid_concave(fluid_real_t val)
+{
+ if (val < 0) {
+ return 0;
+ } else if (val > 127) {
+ return 1;
+ }
+ return fluid_concave_tab[(int) val];
+}
+
+/*
+ * fluid_convex
+ */
+fluid_real_t
+fluid_convex(fluid_real_t val)
+{
+ if (val < 0) {
+ return 0;
+ } else if (val > 127) {
+ return 1;
+ }
+ return fluid_convex_tab[(int) val];
+}
diff --git a/libs/fluidsynth/src/fluid_conv.h b/libs/fluidsynth/src/fluid_conv.h
new file mode 100644
index 0000000000..29793c3359
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_conv.h
@@ -0,0 +1,63 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_CONV_H
+#define _FLUID_CONV_H
+
+#include "fluidsynth_priv.h"
+
+#define FLUID_CENTS_HZ_SIZE 1200
+#define FLUID_VEL_CB_SIZE 128
+#define FLUID_CB_AMP_SIZE 961
+#define FLUID_ATTEN_AMP_SIZE 1441
+#define FLUID_PAN_SIZE 1002
+
+/* EMU 8k/10k don't follow spec in regards to volume attenuation.
+ * This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR).
+ * By the standard this should be -200.0. */
+/* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity. Now it equals the response of EMU10K1 programming.*/
+#define FLUID_ATTEN_POWER_FACTOR (-200.0) /* was (-531.509)*/
+
+void fluid_conversion_config(void);
+
+fluid_real_t fluid_ct2hz_real(fluid_real_t cents);
+fluid_real_t fluid_ct2hz(fluid_real_t cents);
+fluid_real_t fluid_cb2amp(fluid_real_t cb);
+fluid_real_t fluid_atten2amp(fluid_real_t atten);
+fluid_real_t fluid_tc2sec(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_delay(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_attack(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_release(fluid_real_t tc);
+fluid_real_t fluid_act2hz(fluid_real_t c);
+fluid_real_t fluid_hz2ct(fluid_real_t c);
+fluid_real_t fluid_pan(fluid_real_t c, int left);
+fluid_real_t fluid_concave(fluid_real_t val);
+fluid_real_t fluid_convex(fluid_real_t val);
+
+extern fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
+extern fluid_real_t fluid_vel2cb_tab[FLUID_VEL_CB_SIZE];
+extern fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
+extern fluid_real_t fluid_posbp_tab[128];
+extern fluid_real_t fluid_concave_tab[128];
+extern fluid_real_t fluid_convex_tab[128];
+extern fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
+
+
+#endif /* _FLUID_CONV_H */
diff --git a/libs/fluidsynth/src/fluid_defsfont.c b/libs/fluidsynth/src/fluid_defsfont.c
new file mode 100644
index 0000000000..c395218411
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_defsfont.c
@@ -0,0 +1,3432 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * SoundFont file loading code borrowed from Smurf SoundFont Editor
+ * Copyright (C) 1999-2001 Josh Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#include "fluid_defsfont.h"
+/* Todo: Get rid of that 'include' */
+#include "fluid_sys.h"
+
+/***************************************************************
+ *
+ * SFONT LOADER
+ */
+
+fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings)
+{
+ fluid_sfloader_t* loader;
+
+ loader = FLUID_NEW(fluid_sfloader_t);
+ if (loader == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ loader->data = settings;
+ loader->free = delete_fluid_defsfloader;
+ loader->load = fluid_defsfloader_load;
+
+ return loader;
+}
+
+int delete_fluid_defsfloader(fluid_sfloader_t* loader)
+{
+ if (loader) {
+ FLUID_FREE(loader);
+ }
+ return FLUID_OK;
+}
+
+fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename)
+{
+ fluid_defsfont_t* defsfont;
+ fluid_sfont_t* sfont;
+
+ defsfont = new_fluid_defsfont(loader->data);
+
+ if (defsfont == NULL) {
+ return NULL;
+ }
+
+ if (fluid_defsfont_load(defsfont, filename) == FLUID_FAILED) {
+ delete_fluid_defsfont(defsfont);
+ return NULL;
+ }
+
+ sfont = FLUID_NEW(fluid_sfont_t);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont->data = defsfont;
+ sfont->free = fluid_defsfont_sfont_delete;
+ sfont->get_name = fluid_defsfont_sfont_get_name;
+ sfont->get_preset = fluid_defsfont_sfont_get_preset;
+ sfont->iteration_start = fluid_defsfont_sfont_iteration_start;
+ sfont->iteration_next = fluid_defsfont_sfont_iteration_next;
+
+ return sfont;
+}
+
+
+
+/***************************************************************
+ *
+ * PUBLIC INTERFACE
+ */
+
+int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont)
+{
+ if (delete_fluid_defsfont(sfont->data) != 0) {
+ return -1;
+ }
+ FLUID_FREE(sfont);
+ return 0;
+}
+
+char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont)
+{
+ return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data);
+}
+
+#if 0
+fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s)
+{
+ /* This function is here just to avoid an ABI/SONAME bump, see ticket #98. Should never be used. */
+ return NULL;
+}
+#endif
+
+fluid_preset_t*
+fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_defpreset_t* defpreset;
+ fluid_defsfont_t* defsfont = sfont->data;
+
+ defpreset = fluid_defsfont_get_preset(defsfont, bank, prenum);
+
+ if (defpreset == NULL) {
+ return NULL;
+ }
+
+ if (defsfont->preset_stack_size > 0) {
+ defsfont->preset_stack_size--;
+ preset = defsfont->preset_stack[defsfont->preset_stack_size];
+ }
+ if (!preset)
+ preset = FLUID_NEW(fluid_preset_t);
+ if (!preset) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ preset->sfont = sfont;
+ preset->data = defpreset;
+ preset->free = fluid_defpreset_preset_delete;
+ preset->get_name = fluid_defpreset_preset_get_name;
+ preset->get_banknum = fluid_defpreset_preset_get_banknum;
+ preset->get_num = fluid_defpreset_preset_get_num;
+ preset->noteon = fluid_defpreset_preset_noteon;
+ preset->notify = NULL;
+
+ return preset;
+}
+
+void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont)
+{
+ fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data);
+}
+
+int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset)
+{
+ preset->free = fluid_defpreset_preset_delete;
+ preset->get_name = fluid_defpreset_preset_get_name;
+ preset->get_banknum = fluid_defpreset_preset_get_banknum;
+ preset->get_num = fluid_defpreset_preset_get_num;
+ preset->noteon = fluid_defpreset_preset_noteon;
+ preset->notify = NULL;
+
+ return fluid_defsfont_iteration_next((fluid_defsfont_t*) sfont->data, preset);
+}
+
+int fluid_defpreset_preset_delete(fluid_preset_t* preset)
+{
+ fluid_defpreset_t* defpreset = preset ? preset->data : NULL;
+ fluid_defsfont_t* sfont = defpreset ? defpreset->sfont : NULL;
+
+ if (sfont && sfont->preset_stack_size < sfont->preset_stack_capacity) {
+ sfont->preset_stack[sfont->preset_stack_size] = preset;
+ sfont->preset_stack_size++;
+ }
+ else
+ FLUID_FREE(preset);
+
+ return 0;
+}
+
+char* fluid_defpreset_preset_get_name(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_get_num(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth,
+ int chan, int key, int vel)
+{
+ return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel);
+}
+
+
+
+
+/***************************************************************
+ *
+ * CACHED SAMPLEDATA LOADER
+ */
+
+typedef struct _fluid_cached_sampledata_t {
+ struct _fluid_cached_sampledata_t *next;
+
+ char* filename;
+ time_t modification_time;
+ int num_references;
+ int mlock;
+
+ const short* sampledata;
+ unsigned int samplesize;
+} fluid_cached_sampledata_t;
+
+static fluid_cached_sampledata_t* all_cached_sampledata = NULL;
+static fluid_mutex_t cached_sampledata_mutex = FLUID_MUTEX_INIT;
+
+static int fluid_get_file_modification_time(char *filename, time_t *modification_time)
+{
+#if defined(WIN32) || defined(__OS2__)
+ *modification_time = 0;
+ return FLUID_OK;
+#else
+ struct stat buf;
+
+ if (stat(filename, &buf) == -1) {
+ return FLUID_FAILED;
+ }
+
+ *modification_time = buf.st_mtime;
+ return FLUID_OK;
+#endif
+}
+
+static int fluid_cached_sampledata_load(char *filename, unsigned int samplepos,
+ unsigned int samplesize, short **sampledata, int try_mlock)
+{
+ fluid_file fd = NULL;
+ short *loaded_sampledata = NULL;
+ fluid_cached_sampledata_t* cached_sampledata = NULL;
+ time_t modification_time;
+
+ fluid_mutex_lock(cached_sampledata_mutex);
+
+ if (fluid_get_file_modification_time(filename, &modification_time) == FLUID_FAILED) {
+ FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
+ modification_time = 0;
+ }
+
+ for (cached_sampledata = all_cached_sampledata; cached_sampledata; cached_sampledata = cached_sampledata->next) {
+ if (strcmp(filename, cached_sampledata->filename))
+ continue;
+ if (cached_sampledata->modification_time != modification_time)
+ continue;
+ if (cached_sampledata->samplesize != samplesize) {
+ FLUID_LOG(FLUID_ERR, "Cached size of soundfont doesn't match actual size of soundfont (cached: %u. actual: %u)",
+ cached_sampledata->samplesize, samplesize);
+ continue;
+ }
+
+ if (try_mlock && !cached_sampledata->mlock) {
+ if (fluid_mlock(cached_sampledata->sampledata, samplesize) != 0)
+ FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
+ else
+ cached_sampledata->mlock = try_mlock;
+ }
+
+ cached_sampledata->num_references++;
+ loaded_sampledata = (short*) cached_sampledata->sampledata;
+ goto success_exit;
+ }
+
+ fd = FLUID_FOPEN(filename, "rb");
+ if (fd == NULL) {
+ FLUID_LOG(FLUID_ERR, "Can't open soundfont file");
+ goto error_exit;
+ }
+ if (FLUID_FSEEK(fd, samplepos, SEEK_SET) == -1) {
+ perror("error");
+ FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
+ goto error_exit;
+ }
+
+
+ loaded_sampledata = (short*) FLUID_MALLOC(samplesize);
+ if (loaded_sampledata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_exit;
+ }
+ if (FLUID_FREAD(loaded_sampledata, 1, samplesize, fd) < samplesize) {
+ FLUID_LOG(FLUID_ERR, "Failed to read sample data");
+ goto error_exit;
+ }
+
+ FLUID_FCLOSE(fd);
+ fd = NULL;
+
+
+ cached_sampledata = (fluid_cached_sampledata_t*) FLUID_MALLOC(sizeof(fluid_cached_sampledata_t));
+ if (cached_sampledata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory.");
+ goto error_exit;
+ }
+
+ /* Lock the memory to disable paging. It's okay if this fails. It
+ probably means that the user doesn't have to required permission. */
+ cached_sampledata->mlock = 0;
+ if (try_mlock) {
+ if (fluid_mlock(loaded_sampledata, samplesize) != 0)
+ FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
+ else
+ cached_sampledata->mlock = try_mlock;
+ }
+
+ /* If this machine is big endian, the sample have to byte swapped */
+ if (FLUID_IS_BIG_ENDIAN) {
+ unsigned char* cbuf;
+ unsigned char hi, lo;
+ unsigned int i, j;
+ short s;
+ cbuf = (unsigned char*)loaded_sampledata;
+ for (i = 0, j = 0; j < samplesize; i++) {
+ lo = cbuf[j++];
+ hi = cbuf[j++];
+ s = (hi << 8) | lo;
+ loaded_sampledata[i] = s;
+ }
+ }
+
+ cached_sampledata->filename = (char*) FLUID_MALLOC(strlen(filename) + 1);
+ if (cached_sampledata->filename == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory.");
+ goto error_exit;
+ }
+
+ sprintf(cached_sampledata->filename, "%s", filename);
+ cached_sampledata->modification_time = modification_time;
+ cached_sampledata->num_references = 1;
+ cached_sampledata->sampledata = loaded_sampledata;
+ cached_sampledata->samplesize = samplesize;
+
+ cached_sampledata->next = all_cached_sampledata;
+ all_cached_sampledata = cached_sampledata;
+
+
+ success_exit:
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ *sampledata = loaded_sampledata;
+ return FLUID_OK;
+
+ error_exit:
+ if (fd != NULL) {
+ FLUID_FCLOSE(fd);
+ }
+ if (loaded_sampledata != NULL) {
+ FLUID_FREE(loaded_sampledata);
+ }
+
+ if (cached_sampledata != NULL) {
+ if (cached_sampledata->filename != NULL) {
+ FLUID_FREE(cached_sampledata->filename);
+ }
+ FLUID_FREE(cached_sampledata);
+ }
+
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ *sampledata = NULL;
+ return FLUID_FAILED;
+}
+
+static int fluid_cached_sampledata_unload(const short *sampledata)
+{
+ fluid_cached_sampledata_t* prev = NULL;
+ fluid_cached_sampledata_t* cached_sampledata;
+
+ fluid_mutex_lock(cached_sampledata_mutex);
+ cached_sampledata = all_cached_sampledata;
+
+ while (cached_sampledata != NULL) {
+ if (sampledata == cached_sampledata->sampledata) {
+
+ cached_sampledata->num_references--;
+
+ if (cached_sampledata->num_references == 0) {
+ if (cached_sampledata->mlock)
+ fluid_munlock(cached_sampledata->sampledata, cached_sampledata->samplesize);
+ FLUID_FREE((short*) cached_sampledata->sampledata);
+ FLUID_FREE(cached_sampledata->filename);
+
+ if (prev != NULL) {
+ prev->next = cached_sampledata->next;
+ } else {
+ all_cached_sampledata = cached_sampledata->next;
+ }
+
+ FLUID_FREE(cached_sampledata);
+ }
+
+ goto success_exit;
+ }
+
+ prev = cached_sampledata;
+ cached_sampledata = cached_sampledata->next;
+ }
+
+ FLUID_LOG(FLUID_ERR, "Trying to free sampledata not found in cache.");
+ goto error_exit;
+
+ success_exit:
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ return FLUID_OK;
+
+ error_exit:
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ return FLUID_FAILED;
+}
+
+
+
+
+/***************************************************************
+ *
+ * SFONT
+ */
+
+/*
+ * new_fluid_defsfont
+ */
+fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings)
+{
+ fluid_defsfont_t* sfont;
+ int i;
+
+ sfont = FLUID_NEW(fluid_defsfont_t);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont->filename = NULL;
+ sfont->samplepos = 0;
+ sfont->samplesize = 0;
+ sfont->sample = NULL;
+ sfont->sampledata = NULL;
+ sfont->preset = NULL;
+ fluid_settings_getint(settings, "synth.lock-memory", &sfont->mlock);
+
+ /* Initialise preset cache, so we don't have to call malloc on program changes.
+ Usually, we have at most one preset per channel plus one temporarily used,
+ so optimise for that case. */
+ fluid_settings_getint(settings, "synth.midi-channels", &sfont->preset_stack_capacity);
+ sfont->preset_stack_capacity++;
+ sfont->preset_stack_size = 0;
+ sfont->preset_stack = FLUID_ARRAY(fluid_preset_t*, sfont->preset_stack_capacity);
+ if (!sfont->preset_stack) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(sfont);
+ return NULL;
+ }
+
+ for (i = 0; i < sfont->preset_stack_capacity; i++) {
+ sfont->preset_stack[i] = FLUID_NEW(fluid_preset_t);
+ if (!sfont->preset_stack[i]) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ delete_fluid_defsfont(sfont);
+ return NULL;
+ }
+ sfont->preset_stack_size++;
+ }
+
+ return sfont;
+}
+
+/*
+ * delete_fluid_defsfont
+ */
+int delete_fluid_defsfont(fluid_defsfont_t* sfont)
+{
+ fluid_list_t *list;
+ fluid_defpreset_t* preset;
+ fluid_sample_t* sample;
+
+ /* Check that no samples are currently used */
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ sample = (fluid_sample_t*) fluid_list_get(list);
+ if (fluid_sample_refcount(sample) != 0) {
+ return -1;
+ }
+ }
+
+ if (sfont->filename != NULL) {
+ FLUID_FREE(sfont->filename);
+ }
+
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ delete_fluid_sample((fluid_sample_t*) fluid_list_get(list));
+ }
+
+ if (sfont->sample) {
+ delete_fluid_list(sfont->sample);
+ }
+
+ if (sfont->sampledata != NULL) {
+ fluid_cached_sampledata_unload(sfont->sampledata);
+ }
+
+ while (sfont->preset_stack_size > 0)
+ FLUID_FREE(sfont->preset_stack[--sfont->preset_stack_size]);
+ FLUID_FREE(sfont->preset_stack);
+
+ preset = sfont->preset;
+ while (preset != NULL) {
+ sfont->preset = preset->next;
+ delete_fluid_defpreset(preset);
+ preset = sfont->preset;
+ }
+
+ FLUID_FREE(sfont);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defsfont_get_name
+ */
+char* fluid_defsfont_get_name(fluid_defsfont_t* sfont)
+{
+ return sfont->filename;
+}
+
+
+/*
+ * fluid_defsfont_load
+ */
+int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file)
+{
+ SFData* sfdata;
+ fluid_list_t *p;
+ SFPreset* sfpreset;
+ SFSample* sfsample;
+ fluid_sample_t* sample;
+ fluid_defpreset_t* preset = NULL;
+
+ sfont->filename = FLUID_MALLOC(1 + FLUID_STRLEN(file));
+ if (sfont->filename == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_STRCPY(sfont->filename, file);
+
+ /* The actual loading is done in the sfont and sffile files */
+ sfdata = sfload_file(file);
+ if (sfdata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file");
+ return FLUID_FAILED;
+ }
+
+ /* Keep track of the position and size of the sample data because
+ it's loaded separately (and might be unoaded/reloaded in future) */
+ sfont->samplepos = sfdata->samplepos;
+ sfont->samplesize = sfdata->samplesize;
+
+ /* load sample data in one block */
+ if (fluid_defsfont_load_sampledata(sfont) != FLUID_OK)
+ goto err_exit;
+
+ /* Create all the sample headers */
+ p = sfdata->sample;
+ while (p != NULL) {
+ sfsample = (SFSample *) p->data;
+
+ sample = new_fluid_sample();
+ if (sample == NULL) goto err_exit;
+
+ if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK)
+ goto err_exit;
+
+ /* Store reference to FluidSynth sample in SFSample for later IZone fixups */
+ sfsample->fluid_sample = sample;
+
+ fluid_defsfont_add_sample(sfont, sample);
+ fluid_voice_optimize_sample(sample);
+ p = fluid_list_next(p);
+ }
+
+ /* Load all the presets */
+ p = sfdata->preset;
+ while (p != NULL) {
+ sfpreset = (SFPreset *) p->data;
+ preset = new_fluid_defpreset(sfont);
+ if (preset == NULL) goto err_exit;
+
+ if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK)
+ goto err_exit;
+
+ fluid_defsfont_add_preset(sfont, preset);
+ p = fluid_list_next(p);
+ }
+ sfont_close (sfdata);
+
+ return FLUID_OK;
+
+err_exit:
+ sfont_close (sfdata);
+ if (preset != NULL)
+ delete_fluid_defpreset(preset);
+ return FLUID_FAILED;
+}
+
+/* fluid_defsfont_add_sample
+ *
+ * Add a sample to the SoundFont
+ */
+int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample)
+{
+ sfont->sample = fluid_list_append(sfont->sample, sample);
+ return FLUID_OK;
+}
+
+/* fluid_defsfont_add_preset
+ *
+ * Add a preset to the SoundFont
+ */
+int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset)
+{
+ fluid_defpreset_t *cur, *prev;
+ if (sfont->preset == NULL) {
+ preset->next = NULL;
+ sfont->preset = preset;
+ } else {
+ /* sort them as we go along. very basic sorting trick. */
+ cur = sfont->preset;
+ prev = NULL;
+ while (cur != NULL) {
+ if ((preset->bank < cur->bank)
+ || ((preset->bank == cur->bank) && (preset->num < cur->num))) {
+ if (prev == NULL) {
+ preset->next = cur;
+ sfont->preset = preset;
+ } else {
+ preset->next = cur;
+ prev->next = preset;
+ }
+ return FLUID_OK;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ preset->next = NULL;
+ prev->next = preset;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defsfont_load_sampledata
+ */
+int
+fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont)
+{
+ return fluid_cached_sampledata_load(sfont->filename, sfont->samplepos,
+ sfont->samplesize, &sfont->sampledata, sfont->mlock);
+}
+
+/*
+ * fluid_defsfont_get_preset
+ */
+fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num)
+{
+ fluid_defpreset_t* preset = sfont->preset;
+ while (preset != NULL) {
+ if ((preset->bank == bank) && ((preset->num == num))) {
+ return preset;
+ }
+ preset = preset->next;
+ }
+ return NULL;
+}
+
+/*
+ * fluid_defsfont_iteration_start
+ */
+void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont)
+{
+ sfont->iter_cur = sfont->preset;
+}
+
+/*
+ * fluid_defsfont_iteration_next
+ */
+int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset)
+{
+ if (sfont->iter_cur == NULL) {
+ return 0;
+ }
+
+ preset->data = (void*) sfont->iter_cur;
+ sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur);
+ return 1;
+}
+
+/***************************************************************
+ *
+ * PRESET
+ */
+
+/*
+ * new_fluid_defpreset
+ */
+fluid_defpreset_t*
+new_fluid_defpreset(fluid_defsfont_t* sfont)
+{
+ fluid_defpreset_t* preset = FLUID_NEW(fluid_defpreset_t);
+ if (preset == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ preset->next = NULL;
+ preset->sfont = sfont;
+ preset->name[0] = 0;
+ preset->bank = 0;
+ preset->num = 0;
+ preset->global_zone = NULL;
+ preset->zone = NULL;
+ return preset;
+}
+
+/*
+ * delete_fluid_defpreset
+ */
+int
+delete_fluid_defpreset(fluid_defpreset_t* preset)
+{
+ int err = FLUID_OK;
+ fluid_preset_zone_t* zone;
+ if (preset->global_zone != NULL) {
+ if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ preset->global_zone = NULL;
+ }
+ zone = preset->zone;
+ while (zone != NULL) {
+ preset->zone = zone->next;
+ if (delete_fluid_preset_zone(zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ zone = preset->zone;
+ }
+ FLUID_FREE(preset);
+ return err;
+}
+
+int
+fluid_defpreset_get_banknum(fluid_defpreset_t* preset)
+{
+ return preset->bank;
+}
+
+int
+fluid_defpreset_get_num(fluid_defpreset_t* preset)
+{
+ return preset->num;
+}
+
+char*
+fluid_defpreset_get_name(fluid_defpreset_t* preset)
+{
+ return preset->name;
+}
+
+/*
+ * fluid_defpreset_next
+ */
+fluid_defpreset_t*
+fluid_defpreset_next(fluid_defpreset_t* preset)
+{
+ return preset->next;
+}
+
+
+/*
+ * fluid_defpreset_noteon
+ */
+int
+fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel)
+{
+ fluid_preset_zone_t *preset_zone, *global_preset_zone;
+ fluid_inst_t* inst;
+ fluid_inst_zone_t *inst_zone, *global_inst_zone;
+ fluid_sample_t* sample;
+ fluid_voice_t* voice;
+ fluid_mod_t * mod;
+ fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */
+ int mod_list_count;
+ int i;
+
+ global_preset_zone = fluid_defpreset_get_global_zone(preset);
+
+ /* run thru all the zones of this preset */
+ preset_zone = fluid_defpreset_get_zone(preset);
+ while (preset_zone != NULL) {
+
+ /* check if the note falls into the key and velocity range of this
+ preset */
+ if (fluid_preset_zone_inside_range(preset_zone, key, vel)) {
+
+ inst = fluid_preset_zone_get_inst(preset_zone);
+ global_inst_zone = fluid_inst_get_global_zone(inst);
+
+ /* run thru all the zones of this instrument */
+ inst_zone = fluid_inst_get_zone(inst);
+ while (inst_zone != NULL) {
+
+ /* make sure this instrument zone has a valid sample */
+ sample = fluid_inst_zone_get_sample(inst_zone);
+ if ((sample == NULL) || fluid_sample_in_rom(sample)) {
+ inst_zone = fluid_inst_zone_next(inst_zone);
+ continue;
+ }
+
+ /* check if the note falls into the key and velocity range of this
+ instrument */
+
+ if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) {
+
+ /* this is a good zone. allocate a new synthesis process and
+ initialize it */
+
+ voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel);
+ if (voice == NULL) {
+ return FLUID_FAILED;
+ }
+
+
+ /* Instrument level, generators */
+
+ for (i = 0; i < GEN_LAST; i++) {
+
+ /* SF 2.01 section 9.4 'bullet' 4:
+ *
+ * A generator in a local instrument zone supersedes a
+ * global instrument zone generator. Both cases supersede
+ * the default generator -> voice_gen_set */
+
+ if (inst_zone->gen[i].flags){
+ fluid_voice_gen_set(voice, i, inst_zone->gen[i].val);
+
+ } else if ((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) {
+ fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val);
+
+ } else {
+ /* The generator has not been defined in this instrument.
+ * Do nothing, leave it at the default.
+ */
+ }
+
+ } /* for all generators */
+
+ /* global instrument zone, modulators: Put them all into a
+ * list. */
+
+ mod_list_count = 0;
+
+ if (global_inst_zone){
+ mod = global_inst_zone->mod;
+ while (mod){
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+ }
+
+ /* local instrument zone, modulators.
+ * Replace modulators with the same definition in the list:
+ * SF 2.01 page 69, 'bullet' 8
+ */
+ mod = inst_zone->mod;
+
+ while (mod){
+
+ /* 'Identical' modulators will be deleted by setting their
+ * list entry to NULL. The list length is known, NULL
+ * entries will be ignored later. SF2.01 section 9.5.1
+ * page 69, 'bullet' 3 defines 'identical'. */
+
+ for (i = 0; i < mod_list_count; i++){
+ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){
+ mod_list[i] = NULL;
+ }
+ }
+
+ /* Finally add the new modulator to to the list. */
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+
+ /* Add instrument modulators (global / local) to the voice. */
+ for (i = 0; i < mod_list_count; i++){
+
+ mod = mod_list[i];
+
+ if (mod != NULL){ /* disabled modulators CANNOT be skipped. */
+
+ /* Instrument modulators -supersede- existing (default)
+ * modulators. SF 2.01 page 69, 'bullet' 6 */
+ fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE);
+ }
+ }
+
+ /* Preset level, generators */
+
+ for (i = 0; i < GEN_LAST; i++) {
+
+ /* SF 2.01 section 8.5 page 58: If some generators are
+ * encountered at preset level, they should be ignored */
+ if ((i != GEN_STARTADDROFS)
+ && (i != GEN_ENDADDROFS)
+ && (i != GEN_STARTLOOPADDROFS)
+ && (i != GEN_ENDLOOPADDROFS)
+ && (i != GEN_STARTADDRCOARSEOFS)
+ && (i != GEN_ENDADDRCOARSEOFS)
+ && (i != GEN_STARTLOOPADDRCOARSEOFS)
+ && (i != GEN_KEYNUM)
+ && (i != GEN_VELOCITY)
+ && (i != GEN_ENDLOOPADDRCOARSEOFS)
+ && (i != GEN_SAMPLEMODE)
+ && (i != GEN_EXCLUSIVECLASS)
+ && (i != GEN_OVERRIDEROOTKEY)) {
+
+ /* SF 2.01 section 9.4 'bullet' 9: A generator in a
+ * local preset zone supersedes a global preset zone
+ * generator. The effect is -added- to the destination
+ * summing node -> voice_gen_incr */
+
+ if (preset_zone->gen[i].flags) {
+ fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val);
+ } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) {
+ fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val);
+ } else {
+ /* The generator has not been defined in this preset
+ * Do nothing, leave it unchanged.
+ */
+ }
+ } /* if available at preset level */
+ } /* for all generators */
+
+
+ /* Global preset zone, modulators: put them all into a
+ * list. */
+ mod_list_count = 0;
+ if (global_preset_zone){
+ mod = global_preset_zone->mod;
+ while (mod){
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+ }
+
+ /* Process the modulators of the local preset zone. Kick
+ * out all identical modulators from the global preset zone
+ * (SF 2.01 page 69, second-last bullet) */
+
+ mod = preset_zone->mod;
+ while (mod){
+ for (i = 0; i < mod_list_count; i++){
+ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){
+ mod_list[i] = NULL;
+ }
+ }
+
+ /* Finally add the new modulator to the list. */
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+
+ /* Add preset modulators (global / local) to the voice. */
+ for (i = 0; i < mod_list_count; i++){
+ mod = mod_list[i];
+ if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */
+
+ /* Preset modulators -add- to existing instrument /
+ * default modulators. SF2.01 page 70 first bullet on
+ * page */
+ fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD);
+ }
+ }
+
+ /* add the synthesis process to the synthesis loop. */
+ fluid_synth_start_voice(synth, voice);
+
+ /* Store the ID of the first voice that was created by this noteon event.
+ * Exclusive class may only terminate older voices.
+ * That avoids killing voices, which have just been created.
+ * (a noteon event can create several voice processes with the same exclusive
+ * class - for example when using stereo samples)
+ */
+ }
+
+ inst_zone = fluid_inst_zone_next(inst_zone);
+ }
+ }
+ preset_zone = fluid_preset_zone_next(preset_zone);
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_set_global_zone
+ */
+int
+fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone)
+{
+ preset->global_zone = zone;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_import_sfont
+ */
+int
+fluid_defpreset_import_sfont(fluid_defpreset_t* preset,
+ SFPreset* sfpreset,
+ fluid_defsfont_t* sfont)
+{
+ fluid_list_t *p;
+ SFZone* sfzone;
+ fluid_preset_zone_t* zone;
+ int count;
+ char zone_name[256];
+ if ((sfpreset->name != NULL) && (FLUID_STRLEN(sfpreset->name) > 0)) {
+ FLUID_STRCPY(preset->name, sfpreset->name);
+ } else {
+ FLUID_SPRINTF(preset->name, "Bank%d,Preset%d", sfpreset->bank, sfpreset->prenum);
+ }
+ preset->bank = sfpreset->bank;
+ preset->num = sfpreset->prenum;
+ p = sfpreset->zone;
+ count = 0;
+ while (p != NULL) {
+ sfzone = (SFZone *) p->data;
+ FLUID_SPRINTF(zone_name, "%s/%d", preset->name, count);
+ zone = new_fluid_preset_zone(zone_name);
+ if (zone == NULL) {
+ return FLUID_FAILED;
+ }
+ if (fluid_preset_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) {
+ delete_fluid_preset_zone(zone);
+ return FLUID_FAILED;
+ }
+ if ((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) {
+ fluid_defpreset_set_global_zone(preset, zone);
+ } else if (fluid_defpreset_add_zone(preset, zone) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ p = fluid_list_next(p);
+ count++;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_add_zone
+ */
+int
+fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone)
+{
+ if (preset->zone == NULL) {
+ zone->next = NULL;
+ preset->zone = zone;
+ } else {
+ zone->next = preset->zone;
+ preset->zone = zone;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_get_zone
+ */
+fluid_preset_zone_t*
+fluid_defpreset_get_zone(fluid_defpreset_t* preset)
+{
+ return preset->zone;
+}
+
+/*
+ * fluid_defpreset_get_global_zone
+ */
+fluid_preset_zone_t*
+fluid_defpreset_get_global_zone(fluid_defpreset_t* preset)
+{
+ return preset->global_zone;
+}
+
+/*
+ * fluid_preset_zone_next
+ */
+fluid_preset_zone_t*
+fluid_preset_zone_next(fluid_preset_zone_t* preset)
+{
+ return preset->next;
+}
+
+/*
+ * new_fluid_preset_zone
+ */
+fluid_preset_zone_t*
+new_fluid_preset_zone(char *name)
+{
+ int size;
+ fluid_preset_zone_t* zone = NULL;
+ zone = FLUID_NEW(fluid_preset_zone_t);
+ if (zone == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ zone->next = NULL;
+ size = 1 + FLUID_STRLEN(name);
+ zone->name = FLUID_MALLOC(size);
+ if (zone->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(zone);
+ return NULL;
+ }
+ FLUID_STRCPY(zone->name, name);
+ zone->inst = NULL;
+ zone->keylo = 0;
+ zone->keyhi = 128;
+ zone->vello = 0;
+ zone->velhi = 128;
+
+ /* Flag all generators as unused (default, they will be set when they are found
+ * in the sound font).
+ * This also sets the generator values to default, but that is of no concern here.*/
+ fluid_gen_set_default_values(&zone->gen[0]);
+ zone->mod = NULL; /* list of modulators */
+ return zone;
+}
+
+/***************************************************************
+ *
+ * PRESET_ZONE
+ */
+
+/*
+ * delete_fluid_preset_zone
+ */
+int
+delete_fluid_preset_zone(fluid_preset_zone_t* zone)
+{
+ fluid_mod_t *mod, *tmp;
+
+ mod = zone->mod;
+ while (mod) /* delete the modulators */
+ {
+ tmp = mod;
+ mod = mod->next;
+ fluid_mod_delete (tmp);
+ }
+
+ if (zone->name) FLUID_FREE (zone->name);
+ if (zone->inst) delete_fluid_inst (zone->inst);
+ FLUID_FREE(zone);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_preset_zone_import_sfont
+ */
+int
+fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *r;
+ SFGen* sfgen;
+ int count;
+ for (count = 0, r = sfzone->gen; r != NULL; count++) {
+ sfgen = (SFGen *) r->data;
+ switch (sfgen->id) {
+ case GEN_KEYRANGE:
+ zone->keylo = (int) sfgen->amount.range.lo;
+ zone->keyhi = (int) sfgen->amount.range.hi;
+ break;
+ case GEN_VELRANGE:
+ zone->vello = (int) sfgen->amount.range.lo;
+ zone->velhi = (int) sfgen->amount.range.hi;
+ break;
+ default:
+ /* FIXME: some generators have an unsigne word amount value but i don't know which ones */
+ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+ zone->gen[sfgen->id].flags = GEN_SET;
+ break;
+ }
+ r = fluid_list_next(r);
+ }
+ if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) {
+ zone->inst = (fluid_inst_t*) new_fluid_inst();
+ if (zone->inst == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+
+ /* Import the modulators (only SF2.1 and higher) */
+ for (count = 0, r = sfzone->mod; r != NULL; count++) {
+
+ SFMod* mod_src = (SFMod *)r->data;
+ fluid_mod_t * mod_dest = fluid_mod_new();
+ int type;
+
+ if (mod_dest == NULL){
+ return FLUID_FAILED;
+ }
+ mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
+
+ /* *** Amount *** */
+ mod_dest->amount = mod_src->amount;
+
+ /* *** Source *** */
+ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags1 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->src & (1<<7)){
+ mod_dest->flags1 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->src & (1<<8)){
+ mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->src & (1<<9)){
+ mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type=(mod_src->src) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags1 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags1 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags1 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags1 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount=0;
+ }
+
+ /* *** Dest *** */
+ mod_dest->dest = mod_src->dest; /* index of controlled generator */
+
+ /* *** Amount source *** */
+ mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */
+ mod_dest->flags2 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->amtsrc & (1<<7)){
+ mod_dest->flags2 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->amtsrc & (1<<8)){
+ mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->amtsrc & (1<<9)){
+ mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type = (mod_src->amtsrc) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags2 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags2 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags2 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags2 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount=0;
+ }
+
+ /* *** Transform *** */
+ /* SF2.01 only uses the 'linear' transform (0).
+ * Deactivate the modulator by setting the amount to 0 in any other case.
+ */
+ if (mod_src->trans !=0){
+ mod_dest->amount = 0;
+ }
+
+ /* Store the new modulator in the zone The order of modulators
+ * will make a difference, at least in an instrument context: The
+ * second modulator overwrites the first one, if they only differ
+ * in amount. */
+ if (count == 0){
+ zone->mod = mod_dest;
+ } else {
+ fluid_mod_t * last_mod = zone->mod;
+
+ /* Find the end of the list */
+ while (last_mod->next != NULL){
+ last_mod=last_mod->next;
+ }
+
+ last_mod->next = mod_dest;
+ }
+
+ r = fluid_list_next(r);
+ } /* foreach modulator */
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_preset_zone_get_inst
+ */
+fluid_inst_t*
+fluid_preset_zone_get_inst(fluid_preset_zone_t* zone)
+{
+ return zone->inst;
+}
+
+/*
+ * fluid_preset_zone_inside_range
+ */
+int
+fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel)
+{
+ return ((zone->keylo <= key) &&
+ (zone->keyhi >= key) &&
+ (zone->vello <= vel) &&
+ (zone->velhi >= vel));
+}
+
+/***************************************************************
+ *
+ * INST
+ */
+
+/*
+ * new_fluid_inst
+ */
+fluid_inst_t*
+new_fluid_inst()
+{
+ fluid_inst_t* inst = FLUID_NEW(fluid_inst_t);
+ if (inst == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ inst->name[0] = 0;
+ inst->global_zone = NULL;
+ inst->zone = NULL;
+ return inst;
+}
+
+/*
+ * delete_fluid_inst
+ */
+int
+delete_fluid_inst(fluid_inst_t* inst)
+{
+ fluid_inst_zone_t* zone;
+ int err = FLUID_OK;
+ if (inst->global_zone != NULL) {
+ if (delete_fluid_inst_zone(inst->global_zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ inst->global_zone = NULL;
+ }
+ zone = inst->zone;
+ while (zone != NULL) {
+ inst->zone = zone->next;
+ if (delete_fluid_inst_zone(zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ zone = inst->zone;
+ }
+ FLUID_FREE(inst);
+ return err;
+}
+
+/*
+ * fluid_inst_set_global_zone
+ */
+int
+fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone)
+{
+ inst->global_zone = zone;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_import_sfont
+ */
+int
+fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *p;
+ SFZone* sfzone;
+ fluid_inst_zone_t* zone;
+ char zone_name[256];
+ int count;
+
+ p = sfinst->zone;
+ if ((sfinst->name != NULL) && (FLUID_STRLEN(sfinst->name) > 0)) {
+ FLUID_STRCPY(inst->name, sfinst->name);
+ } else {
+ FLUID_STRCPY(inst->name, "<untitled>");
+ }
+
+ count = 0;
+ while (p != NULL) {
+
+ sfzone = (SFZone *) p->data;
+ FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count);
+
+ zone = new_fluid_inst_zone(zone_name);
+ if (zone == NULL) {
+ return FLUID_FAILED;
+ }
+
+ if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) {
+ delete_fluid_inst_zone(zone);
+ return FLUID_FAILED;
+ }
+
+ if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) {
+ fluid_inst_set_global_zone(inst, zone);
+
+ } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ p = fluid_list_next(p);
+ count++;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_add_zone
+ */
+int
+fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone)
+{
+ if (inst->zone == NULL) {
+ zone->next = NULL;
+ inst->zone = zone;
+ } else {
+ zone->next = inst->zone;
+ inst->zone = zone;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_get_zone
+ */
+fluid_inst_zone_t*
+fluid_inst_get_zone(fluid_inst_t* inst)
+{
+ return inst->zone;
+}
+
+/*
+ * fluid_inst_get_global_zone
+ */
+fluid_inst_zone_t*
+fluid_inst_get_global_zone(fluid_inst_t* inst)
+{
+ return inst->global_zone;
+}
+
+/***************************************************************
+ *
+ * INST_ZONE
+ */
+
+/*
+ * new_fluid_inst_zone
+ */
+fluid_inst_zone_t*
+new_fluid_inst_zone(char* name)
+{
+ int size;
+ fluid_inst_zone_t* zone = NULL;
+ zone = FLUID_NEW(fluid_inst_zone_t);
+ if (zone == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ zone->next = NULL;
+ size = 1 + FLUID_STRLEN(name);
+ zone->name = FLUID_MALLOC(size);
+ if (zone->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(zone);
+ return NULL;
+ }
+ FLUID_STRCPY(zone->name, name);
+ zone->sample = NULL;
+ zone->keylo = 0;
+ zone->keyhi = 128;
+ zone->vello = 0;
+ zone->velhi = 128;
+
+ /* Flag the generators as unused.
+ * This also sets the generator values to default, but they will be overwritten anyway, if used.*/
+ fluid_gen_set_default_values(&zone->gen[0]);
+ zone->mod=NULL; /* list of modulators */
+ return zone;
+}
+
+/*
+ * delete_fluid_inst_zone
+ */
+int
+delete_fluid_inst_zone(fluid_inst_zone_t* zone)
+{
+ fluid_mod_t *mod, *tmp;
+
+ mod = zone->mod;
+ while (mod) /* delete the modulators */
+ {
+ tmp = mod;
+ mod = mod->next;
+ fluid_mod_delete (tmp);
+ }
+
+ if (zone->name) FLUID_FREE (zone->name);
+ FLUID_FREE(zone);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_zone_next
+ */
+fluid_inst_zone_t*
+fluid_inst_zone_next(fluid_inst_zone_t* zone)
+{
+ return zone->next;
+}
+
+/*
+ * fluid_inst_zone_import_sfont
+ */
+int
+fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *r;
+ SFGen* sfgen;
+ int count;
+
+ for (count = 0, r = sfzone->gen; r != NULL; count++) {
+ sfgen = (SFGen *) r->data;
+ switch (sfgen->id) {
+ case GEN_KEYRANGE:
+ zone->keylo = (int) sfgen->amount.range.lo;
+ zone->keyhi = (int) sfgen->amount.range.hi;
+ break;
+ case GEN_VELRANGE:
+ zone->vello = (int) sfgen->amount.range.lo;
+ zone->velhi = (int) sfgen->amount.range.hi;
+ break;
+ default:
+ /* FIXME: some generators have an unsigned word amount value but
+ i don't know which ones */
+ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+ zone->gen[sfgen->id].flags = GEN_SET;
+ break;
+ }
+ r = fluid_list_next(r);
+ }
+
+ /* FIXME */
+/* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */
+/* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */
+/* } */
+
+ /* fixup sample pointer */
+ if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL))
+ zone->sample = ((SFSample *)(sfzone->instsamp->data))->fluid_sample;
+
+ /* Import the modulators (only SF2.1 and higher) */
+ for (count = 0, r = sfzone->mod; r != NULL; count++) {
+ SFMod* mod_src = (SFMod *) r->data;
+ int type;
+ fluid_mod_t* mod_dest;
+
+ mod_dest = fluid_mod_new();
+ if (mod_dest == NULL){
+ return FLUID_FAILED;
+ }
+
+ mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
+
+ /* *** Amount *** */
+ mod_dest->amount = mod_src->amount;
+
+ /* *** Source *** */
+ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags1 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->src & (1<<7)){
+ mod_dest->flags1 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->src & (1<<8)){
+ mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->src & (1<<9)){
+ mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type = (mod_src->src) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags1 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags1 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags1 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags1 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount = 0;
+ }
+
+ /* *** Dest *** */
+ mod_dest->dest=mod_src->dest; /* index of controlled generator */
+
+ /* *** Amount source *** */
+ mod_dest->src2=mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags2 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->amtsrc & (1<<7)){
+ mod_dest->flags2 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->amtsrc & (1<<8)){
+ mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->amtsrc & (1<<9)){
+ mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type=(mod_src->amtsrc) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags2 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags2 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags2 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags2 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount = 0;
+ }
+
+ /* *** Transform *** */
+ /* SF2.01 only uses the 'linear' transform (0).
+ * Deactivate the modulator by setting the amount to 0 in any other case.
+ */
+ if (mod_src->trans !=0){
+ mod_dest->amount = 0;
+ }
+
+ /* Store the new modulator in the zone
+ * The order of modulators will make a difference, at least in an instrument context:
+ * The second modulator overwrites the first one, if they only differ in amount. */
+ if (count == 0){
+ zone->mod=mod_dest;
+ } else {
+ fluid_mod_t * last_mod=zone->mod;
+ /* Find the end of the list */
+ while (last_mod->next != NULL){
+ last_mod=last_mod->next;
+ }
+ last_mod->next=mod_dest;
+ }
+
+ r = fluid_list_next(r);
+ } /* foreach modulator */
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_zone_get_sample
+ */
+fluid_sample_t*
+fluid_inst_zone_get_sample(fluid_inst_zone_t* zone)
+{
+ return zone->sample;
+}
+
+/*
+ * fluid_inst_zone_inside_range
+ */
+int
+fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel)
+{
+ return ((zone->keylo <= key) &&
+ (zone->keyhi >= key) &&
+ (zone->vello <= vel) &&
+ (zone->velhi >= vel));
+}
+
+/***************************************************************
+ *
+ * SAMPLE
+ */
+
+/*
+ * new_fluid_sample
+ */
+fluid_sample_t*
+new_fluid_sample()
+{
+ fluid_sample_t* sample = NULL;
+
+ sample = FLUID_NEW(fluid_sample_t);
+ if (sample == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ memset(sample, 0, sizeof(fluid_sample_t));
+ sample->valid = 1;
+
+ return sample;
+}
+
+/*
+ * delete_fluid_sample
+ */
+int
+delete_fluid_sample(fluid_sample_t* sample)
+{
+ FLUID_FREE(sample);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_sample_in_rom
+ */
+int
+fluid_sample_in_rom(fluid_sample_t* sample)
+{
+ return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
+}
+
+/*
+ * fluid_sample_import_sfont
+ */
+int
+fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont)
+{
+ FLUID_STRCPY(sample->name, sfsample->name);
+ sample->data = sfont->sampledata;
+ sample->start = sfsample->start;
+ sample->end = sfsample->start + sfsample->end;
+ sample->loopstart = sfsample->start + sfsample->loopstart;
+ sample->loopend = sfsample->start + sfsample->loopend;
+ sample->samplerate = sfsample->samplerate;
+ sample->origpitch = sfsample->origpitch;
+ sample->pitchadj = sfsample->pitchadj;
+ sample->sampletype = sfsample->sampletype;
+
+ if (sample->sampletype & FLUID_SAMPLETYPE_ROM) {
+ sample->valid = 0;
+ FLUID_LOG(FLUID_WARN, "Ignoring sample %s: can't use ROM samples", sample->name);
+ }
+ if (sample->end - sample->start < 8) {
+ sample->valid = 0;
+ FLUID_LOG(FLUID_WARN, "Ignoring sample %s: too few sample data points", sample->name);
+ } else {
+/* if (sample->loopstart < sample->start + 8) { */
+/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required before loop start", sample->name); */
+/* sample->loopstart = sample->start + 8; */
+/* } */
+/* if (sample->loopend > sample->end - 8) { */
+/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required after loop end", sample->name); */
+/* sample->loopend = sample->end - 8; */
+/* } */
+ }
+ return FLUID_OK;
+}
+
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+
+
+/*=================================sfload.c========================
+ Borrowed from Smurf SoundFont Editor by Josh Green
+ =================================================================*/
+
+/*
+ functions for loading data from sfont files, with appropriate byte swapping
+ on big endian machines. Sfont IDs are not swapped because the ID read is
+ equivalent to the matching ID list in memory regardless of LE/BE machine
+*/
+
+#if FLUID_IS_BIG_ENDIAN
+
+#define READCHUNK(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 8, fd)) \
+ return(FAIL); \
+ ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \
+} G_STMT_END
+
+#define READD(var,fd) G_STMT_START { \
+ unsigned int _temp; \
+ if (!safe_fread(&_temp, 4, fd)) \
+ return(FAIL); \
+ var = GINT32_FROM_LE(_temp); \
+} G_STMT_END
+
+#define READW(var,fd) G_STMT_START { \
+ unsigned short _temp; \
+ if (!safe_fread(&_temp, 2, fd)) \
+ return(FAIL); \
+ var = GINT16_FROM_LE(_temp); \
+} G_STMT_END
+
+#else
+
+#define READCHUNK(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 8, fd)) \
+ return(FAIL); \
+ ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \
+} G_STMT_END
+
+#define READD(var,fd) G_STMT_START { \
+ unsigned int _temp; \
+ if (!safe_fread(&_temp, 4, fd)) \
+ return(FAIL); \
+ var = GINT32_FROM_LE(_temp); \
+} G_STMT_END
+
+#define READW(var,fd) G_STMT_START { \
+ unsigned short _temp; \
+ if (!safe_fread(&_temp, 2, fd)) \
+ return(FAIL); \
+ var = GINT16_FROM_LE(_temp); \
+} G_STMT_END
+
+#endif
+
+
+#define READID(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 4, fd)) \
+ return(FAIL); \
+} G_STMT_END
+
+#define READSTR(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 20, fd)) \
+ return(FAIL); \
+ (*var)[20] = '\0'; \
+} G_STMT_END
+
+#define READB(var,fd) G_STMT_START { \
+ if (!safe_fread(&var, 1, fd)) \
+ return(FAIL); \
+} G_STMT_END
+
+#define FSKIP(size,fd) G_STMT_START { \
+ if (!safe_fseek(fd, size, SEEK_CUR)) \
+ return(FAIL); \
+} G_STMT_END
+
+#define FSKIPW(fd) G_STMT_START { \
+ if (!safe_fseek(fd, 2, SEEK_CUR)) \
+ return(FAIL); \
+} G_STMT_END
+
+/* removes and advances a fluid_list_t pointer */
+#define SLADVREM(list, item) G_STMT_START { \
+ fluid_list_t *_temp = item; \
+ item = fluid_list_next(item); \
+ list = fluid_list_remove_link(list, _temp); \
+ delete1_fluid_list(_temp); \
+} G_STMT_END
+
+static int chunkid (unsigned int id);
+static int load_body (unsigned int size, SFData * sf, FILE * fd);
+static int read_listchunk (SFChunk * chunk, FILE * fd);
+static int process_info (int size, SFData * sf, FILE * fd);
+static int process_sdta (unsigned int size, SFData * sf, FILE * fd);
+static int pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk,
+ int * size, FILE * fd);
+static int process_pdta (int size, SFData * sf, FILE * fd);
+static int load_phdr (int size, SFData * sf, FILE * fd);
+static int load_pbag (int size, SFData * sf, FILE * fd);
+static int load_pmod (int size, SFData * sf, FILE * fd);
+static int load_pgen (int size, SFData * sf, FILE * fd);
+static int load_ihdr (int size, SFData * sf, FILE * fd);
+static int load_ibag (int size, SFData * sf, FILE * fd);
+static int load_imod (int size, SFData * sf, FILE * fd);
+static int load_igen (int size, SFData * sf, FILE * fd);
+static int load_shdr (unsigned int size, SFData * sf, FILE * fd);
+static int fixup_pgen (SFData * sf);
+static int fixup_igen (SFData * sf);
+static int fixup_sample (SFData * sf);
+
+char idlist[] = {
+ "RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
+ "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdr"
+};
+
+static unsigned int sdtachunk_size;
+
+/* sound font file load functions */
+static int
+chunkid (unsigned int id)
+{
+ unsigned int i;
+ unsigned int *p;
+
+ p = (unsigned int *) & idlist;
+ for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1)
+ if (*p == id)
+ return (i + 1);
+
+ return (UNKN_ID);
+}
+
+SFData *
+sfload_file (const char * fname)
+{
+ SFData *sf = NULL;
+ FILE *fd;
+ int fsize = 0;
+ int err = FALSE;
+
+ if (!(fd = fopen (fname, "rb")))
+ {
+ FLUID_LOG (FLUID_ERR, _("Unable to open file \"%s\""), fname);
+ return (NULL);
+ }
+
+ if (!(sf = FLUID_NEW (SFData)))
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ fclose(fd);
+ err = TRUE;
+ }
+
+ if (!err)
+ {
+ memset (sf, 0, sizeof (SFData)); /* zero sfdata */
+ sf->fname = FLUID_STRDUP (fname); /* copy file name */
+ sf->sffd = fd;
+ }
+
+ /* get size of file */
+ if (!err && fseek (fd, 0L, SEEK_END) == -1)
+ { /* seek to end of file */
+ err = TRUE;
+ FLUID_LOG (FLUID_ERR, _("Seek to end of file failed"));
+ }
+ if (!err && (fsize = ftell (fd)) == -1)
+ { /* position = size */
+ err = TRUE;
+ FLUID_LOG (FLUID_ERR, _("Get end of file position failed"));
+ }
+ if (!err)
+ rewind (fd);
+
+ if (!err && !load_body (fsize, sf, fd))
+ err = TRUE; /* load the sfont */
+
+ if (err)
+ {
+ if (sf)
+ sfont_close (sf);
+ return (NULL);
+ }
+
+ return (sf);
+}
+
+static int
+load_body (unsigned int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+
+ READCHUNK (&chunk, fd); /* load RIFF chunk */
+ if (chunkid (chunk.id) != RIFF_ID) { /* error if not RIFF */
+ FLUID_LOG (FLUID_ERR, _("Not a RIFF file"));
+ return (FAIL);
+ }
+
+ READID (&chunk.id, fd); /* load file ID */
+ if (chunkid (chunk.id) != SFBK_ID) { /* error if not SFBK_ID */
+ FLUID_LOG (FLUID_ERR, _("Not a SoundFont file"));
+ return (FAIL);
+ }
+
+ if (chunk.size != size - 8) {
+ gerr (ErrCorr, _("SoundFont file size mismatch"));
+ return (FAIL);
+ }
+
+ /* Process INFO block */
+ if (!read_listchunk (&chunk, fd))
+ return (FAIL);
+ if (chunkid (chunk.id) != INFO_ID)
+ return (gerr (ErrCorr, _("Invalid ID found when expecting INFO chunk")));
+ if (!process_info (chunk.size, sf, fd))
+ return (FAIL);
+
+ /* Process sample chunk */
+ if (!read_listchunk (&chunk, fd))
+ return (FAIL);
+ if (chunkid (chunk.id) != SDTA_ID)
+ return (gerr (ErrCorr,
+ _("Invalid ID found when expecting SAMPLE chunk")));
+ if (!process_sdta (chunk.size, sf, fd))
+ return (FAIL);
+
+ /* process HYDRA chunk */
+ if (!read_listchunk (&chunk, fd))
+ return (FAIL);
+ if (chunkid (chunk.id) != PDTA_ID)
+ return (gerr (ErrCorr, _("Invalid ID found when expecting HYDRA chunk")));
+ if (!process_pdta (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!fixup_pgen (sf))
+ return (FAIL);
+ if (!fixup_igen (sf))
+ return (FAIL);
+ if (!fixup_sample (sf))
+ return (FAIL);
+
+ /* sort preset list by bank, preset # */
+ sf->preset = fluid_list_sort (sf->preset,
+ (fluid_compare_func_t) sfont_preset_compare_func);
+
+ return (OK);
+}
+
+static int
+read_listchunk (SFChunk * chunk, FILE * fd)
+{
+ READCHUNK (chunk, fd); /* read list chunk */
+ if (chunkid (chunk->id) != LIST_ID) /* error if ! list chunk */
+ return (gerr (ErrCorr, _("Invalid chunk id in level 0 parse")));
+ READID (&chunk->id, fd); /* read id string */
+ chunk->size -= 4;
+ return (OK);
+}
+
+static int
+process_info (int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+ unsigned char id;
+ char *item;
+ unsigned short ver;
+
+ while (size > 0)
+ {
+ READCHUNK (&chunk, fd);
+ size -= 8;
+
+ id = chunkid (chunk.id);
+
+ if (id == IFIL_ID)
+ { /* sound font version chunk? */
+ if (chunk.size != 4)
+ return (gerr (ErrCorr,
+ _("Sound font version info chunk has invalid size")));
+
+ READW (ver, fd);
+ sf->version.major = ver;
+ READW (ver, fd);
+ sf->version.minor = ver;
+
+ if (sf->version.major < 2) {
+ FLUID_LOG (FLUID_ERR,
+ _("Sound font version is %d.%d which is not"
+ " supported, convert to version 2.0x"),
+ sf->version.major,
+ sf->version.minor);
+ return (FAIL);
+ }
+
+ if (sf->version.major > 2) {
+ FLUID_LOG (FLUID_WARN,
+ _("Sound font version is %d.%d which is newer than"
+ " what this version of FLUID Synth was designed for (v2.0x)"),
+ sf->version.major,
+ sf->version.minor);
+ return (FAIL);
+ }
+ }
+ else if (id == IVER_ID)
+ { /* ROM version chunk? */
+ if (chunk.size != 4)
+ return (gerr (ErrCorr,
+ _("ROM version info chunk has invalid size")));
+
+ READW (ver, fd);
+ sf->romver.major = ver;
+ READW (ver, fd);
+ sf->romver.minor = ver;
+ }
+ else if (id != UNKN_ID)
+ {
+ if ((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536)
+ || (chunk.size % 2))
+ return (gerr (ErrCorr,
+ _("INFO sub chunk %.4s has invalid chunk size"
+ " of %d bytes"), &chunk.id, chunk.size));
+
+ /* alloc for chunk id and da chunk */
+ if (!(item = FLUID_MALLOC (chunk.size + 1)))
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return (FAIL);
+ }
+
+ /* attach to INFO list, sfont_close will cleanup if FAIL occurs */
+ sf->info = fluid_list_append (sf->info, item);
+
+ *(unsigned char *) item = id;
+ if (!safe_fread (&item[1], chunk.size, fd))
+ return (FAIL);
+
+ /* force terminate info item (don't forget uint8 info ID) */
+ *(item + chunk.size) = '\0';
+ }
+ else
+ return (gerr (ErrCorr, _("Invalid chunk id in INFO chunk")));
+ size -= chunk.size;
+ }
+
+ if (size < 0)
+ return (gerr (ErrCorr, _("INFO chunk size mismatch")));
+
+ return (OK);
+}
+
+static int
+process_sdta (unsigned int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+
+ if (size == 0)
+ return (OK); /* no sample data? */
+
+ /* read sub chunk */
+ READCHUNK (&chunk, fd);
+ size -= 8;
+
+ if (chunkid (chunk.id) != SMPL_ID)
+ return (gerr (ErrCorr,
+ _("Expected SMPL chunk found invalid id instead")));
+
+ /* SDTA chunk may also contain sm24 chunk for 24 bit samples
+ * (not yet supported), only an error if SMPL chunk size is
+ * greater than SDTA. */
+ if (chunk.size > size)
+ return (gerr (ErrCorr, _("SDTA chunk size mismatch")));
+
+ /* sample data follows */
+ sf->samplepos = ftell (fd);
+
+ /* used in fixup_sample() to check validity of sample headers */
+ sdtachunk_size = chunk.size;
+ sf->samplesize = chunk.size;
+
+ FSKIP (size, fd);
+
+ return (OK);
+}
+
+static int
+pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk,
+ int * size, FILE * fd)
+{
+ unsigned int id;
+ char *expstr;
+
+ expstr = CHNKIDSTR (expid); /* in case we need it */
+
+ READCHUNK (chunk, fd);
+ *size -= 8;
+
+ if ((id = chunkid (chunk->id)) != expid)
+ return (gerr (ErrCorr, _("Expected"
+ " PDTA sub-chunk \"%.4s\" found invalid id instead"), expstr));
+
+ if (chunk->size % reclen) /* valid chunk size? */
+ return (gerr (ErrCorr,
+ _("\"%.4s\" chunk size is not a multiple of %d bytes"), expstr,
+ reclen));
+ if ((*size -= chunk->size) < 0)
+ return (gerr (ErrCorr,
+ _("\"%.4s\" chunk size exceeds remaining PDTA chunk size"), expstr));
+ return (OK);
+}
+
+static int
+process_pdta (int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+
+ if (!pdtahelper (PHDR_ID, SFPHDRSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_phdr (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (PBAG_ID, SFBAGSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_pbag (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (PMOD_ID, SFMODSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_pmod (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (PGEN_ID, SFGENSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_pgen (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IHDR_ID, SFIHDRSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_ihdr (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IBAG_ID, SFBAGSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_ibag (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IMOD_ID, SFMODSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_imod (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IGEN_ID, SFGENSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_igen (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (SHDR_ID, SFSHDRSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_shdr (chunk.size, sf, fd))
+ return (FAIL);
+
+ return (OK);
+}
+
+/* preset header loader */
+static int
+load_phdr (int size, SFData * sf, FILE * fd)
+{
+ int i, i2;
+ SFPreset *p, *pr = NULL; /* ptr to current & previous preset */
+ unsigned short zndx, pzndx = 0;
+
+ if (size % SFPHDRSIZE || size == 0)
+ return (gerr (ErrCorr, _("Preset header chunk size is invalid")));
+
+ i = size / SFPHDRSIZE - 1;
+ if (i == 0)
+ { /* at least one preset + term record */
+ FLUID_LOG (FLUID_WARN, _("File contains no presets"));
+ FSKIP (SFPHDRSIZE, fd);
+ return (OK);
+ }
+
+ for (; i > 0; i--)
+ { /* load all preset headers */
+ p = FLUID_NEW (SFPreset);
+ sf->preset = fluid_list_append (sf->preset, p);
+ p->zone = NULL; /* In case of failure, sfont_close can cleanup */
+ READSTR (&p->name, fd); /* possible read failure ^ */
+ READW (p->prenum, fd);
+ READW (p->bank, fd);
+ READW (zndx, fd);
+ READD (p->libr, fd);
+ READD (p->genre, fd);
+ READD (p->morph, fd);
+
+ if (pr)
+ { /* not first preset? */
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Preset header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ {
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+ }
+ }
+ else if (zndx > 0) /* 1st preset, warn if ofs >0 */
+ FLUID_LOG (FLUID_WARN, _("%d preset zones not referenced, discarding"), zndx);
+ pr = p; /* update preset ptr */
+ pzndx = zndx;
+ }
+
+ FSKIP (24, fd);
+ READW (zndx, fd); /* Read terminal generator index */
+ FSKIP (12, fd);
+
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Preset header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ {
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+ }
+
+ return (OK);
+}
+
+/* preset bag loader */
+static int
+load_pbag (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2;
+ SFZone *z, *pz = NULL;
+ unsigned short genndx, modndx;
+ unsigned short pgenndx = 0, pmodndx = 0;
+ unsigned short i;
+
+ if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */
+ return (gerr (ErrCorr, _("Preset bag chunk size is invalid")));
+
+ p = sf->preset;
+ while (p)
+ { /* traverse through presets */
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* traverse preset's zones */
+ if ((size -= SFBAGSIZE) < 0)
+ return (gerr (ErrCorr, _("Preset bag chunk size mismatch")));
+ z = FLUID_NEW (SFZone);
+ p2->data = z;
+ z->gen = NULL; /* Init gen and mod before possible failure, */
+ z->mod = NULL; /* to ensure proper cleanup (sfont_close) */
+ READW (genndx, fd); /* possible read failure ^ */
+ READW (modndx, fd);
+ z->instsamp = NULL;
+
+ if (pz)
+ { /* if not first zone */
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr,
+ _("Preset bag generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr,
+ _("Preset bag modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+ }
+ pz = z; /* update previous zone ptr */
+ pgenndx = genndx; /* update previous zone gen index */
+ pmodndx = modndx; /* update previous zone mod index */
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ size -= SFBAGSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Preset bag chunk size mismatch")));
+
+ READW (genndx, fd);
+ READW (modndx, fd);
+
+ if (!pz)
+ {
+ if (genndx > 0)
+ FLUID_LOG (FLUID_WARN, _("No preset generators and terminal index not 0"));
+ if (modndx > 0)
+ FLUID_LOG (FLUID_WARN, _("No preset modulators and terminal index not 0"));
+ return (OK);
+ }
+
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr, _("Preset bag generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr, _("Preset bag modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+
+ return (OK);
+}
+
+/* preset modulator loader */
+static int
+load_pmod (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFMod *m;
+
+ p = sf->preset;
+ while (p)
+ { /* traverse through all presets */
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* traverse this preset's zones */
+ p3 = ((SFZone *) (p2->data))->mod;
+ while (p3)
+ { /* load zone's modulators */
+ if ((size -= SFMODSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Preset modulator chunk size mismatch")));
+ m = FLUID_NEW (SFMod);
+ p3->data = m;
+ READW (m->src, fd);
+ READW (m->dest, fd);
+ READW (m->amount, fd);
+ READW (m->amtsrc, fd);
+ READW (m->trans, fd);
+ p3 = fluid_list_next (p3);
+ }
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ /*
+ If there isn't even a terminal record
+ Hmmm, the specs say there should be one, but..
+ */
+ if (size == 0)
+ return (OK);
+
+ size -= SFMODSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Preset modulator chunk size mismatch")));
+ FSKIP (SFMODSIZE, fd); /* terminal mod */
+
+ return (OK);
+}
+
+/* -------------------------------------------------------------------
+ * preset generator loader
+ * generator (per preset) loading rules:
+ * Zones with no generators or modulators shall be annihilated
+ * Global zone must be 1st zone, discard additional ones (instrumentless zones)
+ *
+ * generator (per zone) loading rules (in order of decreasing precedence):
+ * KeyRange is 1st in list (if exists), else discard
+ * if a VelRange exists only preceded by a KeyRange, else discard
+ * if a generator follows an instrument discard it
+ * if a duplicate generator exists replace previous one
+ * ------------------------------------------------------------------- */
+static int
+load_pgen (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3, *dup, **hz = NULL;
+ SFZone *z;
+ SFGen *g;
+ SFGenAmount genval;
+ unsigned short genid;
+ int level, skip, drop, gzone, discarded;
+
+ p = sf->preset;
+ while (p)
+ { /* traverse through all presets */
+ gzone = FALSE;
+ discarded = FALSE;
+ p2 = ((SFPreset *) (p->data))->zone;
+ if (p2)
+ hz = &p2;
+ while (p2)
+ { /* traverse preset's zones */
+ level = 0;
+ z = (SFZone *) (p2->data);
+ p3 = z->gen;
+ while (p3)
+ { /* load zone's generators */
+ dup = NULL;
+ skip = FALSE;
+ drop = FALSE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Preset generator chunk size mismatch")));
+
+ READW (genid, fd);
+
+ if (genid == Gen_KeyRange)
+ { /* nothing precedes */
+ if (level == 0)
+ {
+ level = 1;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_VelRange)
+ { /* only KeyRange precedes */
+ if (level <= 1)
+ {
+ level = 2;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_Instrument)
+ { /* inst is last gen */
+ level = 3;
+ READW (genval.uword, fd);
+ ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1);
+ break; /* break out of generator loop */
+ }
+ else
+ {
+ level = 2;
+ if (gen_validp (genid))
+ { /* generator valid? */
+ READW (genval.sword, fd);
+ dup = gen_inlist (genid, z->gen);
+ }
+ else
+ skip = TRUE;
+ }
+
+ if (!skip)
+ {
+ if (!dup)
+ { /* if gen ! dup alloc new */
+ g = FLUID_NEW (SFGen);
+ p3->data = g;
+ g->id = genid;
+ }
+ else
+ {
+ g = (SFGen *) (dup->data); /* ptr to orig gen */
+ drop = TRUE;
+ }
+ g->amount = genval;
+ }
+ else
+ { /* Skip this generator */
+ discarded = TRUE;
+ drop = TRUE;
+ FSKIPW (fd);
+ }
+
+ if (!drop)
+ p3 = fluid_list_next (p3); /* next gen */
+ else
+ SLADVREM (z->gen, p3); /* drop place holder */
+
+ } /* generator loop */
+
+ if (level == 3)
+ SLADVREM (z->gen, p3); /* zone has inst? */
+ else
+ { /* congratulations its a global zone */
+ if (!gzone)
+ { /* Prior global zones? */
+ gzone = TRUE;
+
+ /* if global zone is not 1st zone, relocate */
+ if (*hz != p2)
+ {
+ void* save = p2->data;
+ FLUID_LOG (FLUID_WARN,
+ _("Preset \"%s\": Global zone is not first zone"),
+ ((SFPreset *) (p->data))->name);
+ SLADVREM (*hz, p2);
+ *hz = fluid_list_prepend (*hz, save);
+ continue;
+ }
+ }
+ else
+ { /* previous global zone exists, discard */
+ FLUID_LOG (FLUID_WARN,
+ _("Preset \"%s\": Discarding invalid global zone"),
+ ((SFPreset *) (p->data))->name);
+ sfont_zone_delete (sf, hz, (SFZone *) (p2->data));
+ }
+ }
+
+ while (p3)
+ { /* Kill any zones following an instrument */
+ discarded = TRUE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Preset generator chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd);
+ SLADVREM (z->gen, p3);
+ }
+
+ p2 = fluid_list_next (p2); /* next zone */
+ }
+ if (discarded)
+ FLUID_LOG(FLUID_WARN,
+ _("Preset \"%s\": Some invalid generators were discarded"),
+ ((SFPreset *) (p->data))->name);
+ p = fluid_list_next (p);
+ }
+
+ /* in case there isn't a terminal record */
+ if (size == 0)
+ return (OK);
+
+ size -= SFGENSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Preset generator chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd); /* terminal gen */
+
+ return (OK);
+}
+
+/* instrument header loader */
+static int
+load_ihdr (int size, SFData * sf, FILE * fd)
+{
+ int i, i2;
+ SFInst *p, *pr = NULL; /* ptr to current & previous instrument */
+ unsigned short zndx, pzndx = 0;
+
+ if (size % SFIHDRSIZE || size == 0) /* chunk size is valid? */
+ return (gerr (ErrCorr, _("Instrument header has invalid size")));
+
+ size = size / SFIHDRSIZE - 1;
+ if (size == 0)
+ { /* at least one preset + term record */
+ FLUID_LOG (FLUID_WARN, _("File contains no instruments"));
+ FSKIP (SFIHDRSIZE, fd);
+ return (OK);
+ }
+
+ for (i = 0; i < size; i++)
+ { /* load all instrument headers */
+ p = FLUID_NEW (SFInst);
+ sf->inst = fluid_list_append (sf->inst, p);
+ p->zone = NULL; /* For proper cleanup if fail (sfont_close) */
+ READSTR (&p->name, fd); /* Possible read failure ^ */
+ READW (zndx, fd);
+
+ if (pr)
+ { /* not first instrument? */
+ if (zndx < pzndx)
+ return (gerr (ErrCorr,
+ _("Instrument header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+ }
+ else if (zndx > 0) /* 1st inst, warn if ofs >0 */
+ FLUID_LOG (FLUID_WARN, _("%d instrument zones not referenced, discarding"),
+ zndx);
+ pzndx = zndx;
+ pr = p; /* update instrument ptr */
+ }
+
+ FSKIP (20, fd);
+ READW (zndx, fd);
+
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Instrument header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+
+ return (OK);
+}
+
+/* instrument bag loader */
+static int
+load_ibag (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2;
+ SFZone *z, *pz = NULL;
+ unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0;
+ int i;
+
+ if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */
+ return (gerr (ErrCorr, _("Instrument bag chunk size is invalid")));
+
+ p = sf->inst;
+ while (p)
+ { /* traverse through inst */
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* load this inst's zones */
+ if ((size -= SFBAGSIZE) < 0)
+ return (gerr (ErrCorr, _("Instrument bag chunk size mismatch")));
+ z = FLUID_NEW (SFZone);
+ p2->data = z;
+ z->gen = NULL; /* In case of failure, */
+ z->mod = NULL; /* sfont_close can clean up */
+ READW (genndx, fd); /* READW = possible read failure */
+ READW (modndx, fd);
+ z->instsamp = NULL;
+
+ if (pz)
+ { /* if not first zone */
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr,
+ _("Instrument generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr,
+ _("Instrument modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+ }
+ pz = z; /* update previous zone ptr */
+ pgenndx = genndx;
+ pmodndx = modndx;
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ size -= SFBAGSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Instrument chunk size mismatch")));
+
+ READW (genndx, fd);
+ READW (modndx, fd);
+
+ if (!pz)
+ { /* in case that all are no zoners */
+ if (genndx > 0)
+ FLUID_LOG (FLUID_WARN,
+ _("No instrument generators and terminal index not 0"));
+ if (modndx > 0)
+ FLUID_LOG (FLUID_WARN,
+ _("No instrument modulators and terminal index not 0"));
+ return (OK);
+ }
+
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr, _("Instrument generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr, _("Instrument modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+
+ return (OK);
+}
+
+/* instrument modulator loader */
+static int
+load_imod (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFMod *m;
+
+ p = sf->inst;
+ while (p)
+ { /* traverse through all inst */
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* traverse this inst's zones */
+ p3 = ((SFZone *) (p2->data))->mod;
+ while (p3)
+ { /* load zone's modulators */
+ if ((size -= SFMODSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Instrument modulator chunk size mismatch")));
+ m = FLUID_NEW (SFMod);
+ p3->data = m;
+ READW (m->src, fd);
+ READW (m->dest, fd);
+ READW (m->amount, fd);
+ READW (m->amtsrc, fd);
+ READW (m->trans, fd);
+ p3 = fluid_list_next (p3);
+ }
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ /*
+ If there isn't even a terminal record
+ Hmmm, the specs say there should be one, but..
+ */
+ if (size == 0)
+ return (OK);
+
+ size -= SFMODSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Instrument modulator chunk size mismatch")));
+ FSKIP (SFMODSIZE, fd); /* terminal mod */
+
+ return (OK);
+}
+
+/* load instrument generators (see load_pgen for loading rules) */
+static int
+load_igen (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3, *dup, **hz = NULL;
+ SFZone *z;
+ SFGen *g;
+ SFGenAmount genval;
+ unsigned short genid;
+ int level, skip, drop, gzone, discarded;
+
+ p = sf->inst;
+ while (p)
+ { /* traverse through all instruments */
+ gzone = FALSE;
+ discarded = FALSE;
+ p2 = ((SFInst *) (p->data))->zone;
+ if (p2)
+ hz = &p2;
+ while (p2)
+ { /* traverse this instrument's zones */
+ level = 0;
+ z = (SFZone *) (p2->data);
+ p3 = z->gen;
+ while (p3)
+ { /* load zone's generators */
+ dup = NULL;
+ skip = FALSE;
+ drop = FALSE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr, _("IGEN chunk size mismatch")));
+
+ READW (genid, fd);
+
+ if (genid == Gen_KeyRange)
+ { /* nothing precedes */
+ if (level == 0)
+ {
+ level = 1;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_VelRange)
+ { /* only KeyRange precedes */
+ if (level <= 1)
+ {
+ level = 2;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_SampleId)
+ { /* sample is last gen */
+ level = 3;
+ READW (genval.uword, fd);
+ ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1);
+ break; /* break out of generator loop */
+ }
+ else
+ {
+ level = 2;
+ if (gen_valid (genid))
+ { /* gen valid? */
+ READW (genval.sword, fd);
+ dup = gen_inlist (genid, z->gen);
+ }
+ else
+ skip = TRUE;
+ }
+
+ if (!skip)
+ {
+ if (!dup)
+ { /* if gen ! dup alloc new */
+ g = FLUID_NEW (SFGen);
+ p3->data = g;
+ g->id = genid;
+ }
+ else
+ {
+ g = (SFGen *) (dup->data);
+ drop = TRUE;
+ }
+ g->amount = genval;
+ }
+ else
+ { /* skip this generator */
+ discarded = TRUE;
+ drop = TRUE;
+ FSKIPW (fd);
+ }
+
+ if (!drop)
+ p3 = fluid_list_next (p3); /* next gen */
+ else
+ SLADVREM (z->gen, p3);
+
+ } /* generator loop */
+
+ if (level == 3)
+ SLADVREM (z->gen, p3); /* zone has sample? */
+ else
+ { /* its a global zone */
+ if (!gzone)
+ {
+ gzone = TRUE;
+
+ /* if global zone is not 1st zone, relocate */
+ if (*hz != p2)
+ {
+ void* save = p2->data;
+ FLUID_LOG (FLUID_WARN,
+ _("Instrument \"%s\": Global zone is not first zone"),
+ ((SFPreset *) (p->data))->name);
+ SLADVREM (*hz, p2);
+ *hz = fluid_list_prepend (*hz, save);
+ continue;
+ }
+ }
+ else
+ { /* previous global zone exists, discard */
+ FLUID_LOG (FLUID_WARN,
+ _("Instrument \"%s\": Discarding invalid global zone"),
+ ((SFInst *) (p->data))->name);
+ sfont_zone_delete (sf, hz, (SFZone *) (p2->data));
+ }
+ }
+
+ while (p3)
+ { /* Kill any zones following a sample */
+ discarded = TRUE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Instrument generator chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd);
+ SLADVREM (z->gen, p3);
+ }
+
+ p2 = fluid_list_next (p2); /* next zone */
+ }
+ if (discarded)
+ FLUID_LOG(FLUID_WARN,
+ _("Instrument \"%s\": Some invalid generators were discarded"),
+ ((SFInst *) (p->data))->name);
+ p = fluid_list_next (p);
+ }
+
+ /* for those non-terminal record cases, grr! */
+ if (size == 0)
+ return (OK);
+
+ size -= SFGENSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("IGEN chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd); /* terminal gen */
+
+ return (OK);
+}
+
+/* sample header loader */
+static int
+load_shdr (unsigned int size, SFData * sf, FILE * fd)
+{
+ unsigned int i;
+ SFSample *p;
+
+ if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */
+ return (gerr (ErrCorr, _("Sample header has invalid size")));
+
+ size = size / SFSHDRSIZE - 1;
+ if (size == 0)
+ { /* at least one sample + term record? */
+ FLUID_LOG (FLUID_WARN, _("File contains no samples"));
+ FSKIP (SFSHDRSIZE, fd);
+ return (OK);
+ }
+
+ /* load all sample headers */
+ for (i = 0; i < size; i++)
+ {
+ p = FLUID_NEW (SFSample);
+ sf->sample = fluid_list_append (sf->sample, p);
+ READSTR (&p->name, fd);
+ READD (p->start, fd);
+ READD (p->end, fd); /* - end, loopstart and loopend */
+ READD (p->loopstart, fd); /* - will be checked and turned into */
+ READD (p->loopend, fd); /* - offsets in fixup_sample() */
+ READD (p->samplerate, fd);
+ READB (p->origpitch, fd);
+ READB (p->pitchadj, fd);
+ FSKIPW (fd); /* skip sample link */
+ READW (p->sampletype, fd);
+ p->samfile = 0;
+ }
+
+ FSKIP (SFSHDRSIZE, fd); /* skip terminal shdr */
+
+ return (OK);
+}
+
+/* "fixup" (inst # -> inst ptr) instrument references in preset list */
+static int
+fixup_pgen (SFData * sf)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFZone *z;
+ int i;
+
+ p = sf->preset;
+ while (p)
+ {
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* traverse this preset's zones */
+ z = (SFZone *) (p2->data);
+ if ((i = GPOINTER_TO_INT (z->instsamp)))
+ { /* load instrument # */
+ p3 = fluid_list_nth (sf->inst, i - 1);
+ if (!p3)
+ return (gerr (ErrCorr,
+ _("Preset %03d %03d: Invalid instrument reference"),
+ ((SFPreset *) (p->data))->bank,
+ ((SFPreset *) (p->data))->prenum));
+ z->instsamp = p3;
+ }
+ else
+ z->instsamp = NULL;
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/* "fixup" (sample # -> sample ptr) sample references in instrument list */
+static int
+fixup_igen (SFData * sf)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFZone *z;
+ int i;
+
+ p = sf->inst;
+ while (p)
+ {
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* traverse instrument's zones */
+ z = (SFZone *) (p2->data);
+ if ((i = GPOINTER_TO_INT (z->instsamp)))
+ { /* load sample # */
+ p3 = fluid_list_nth (sf->sample, i - 1);
+ if (!p3)
+ return (gerr (ErrCorr,
+ _("Instrument \"%s\": Invalid sample reference"),
+ ((SFInst *) (p->data))->name));
+ z->instsamp = p3;
+ }
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/* convert sample end, loopstart and loopend to offsets and check if valid */
+static int
+fixup_sample (SFData * sf)
+{
+ fluid_list_t *p;
+ SFSample *sam;
+
+ p = sf->sample;
+ while (p)
+ {
+ sam = (SFSample *) (p->data);
+
+ /* if sample is not a ROM sample and end is over the sample data chunk
+ or sam start is greater than 4 less than the end (at least 4 samples) */
+ if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM)
+ && sam->end > sdtachunk_size) || sam->start > (sam->end - 4))
+ {
+ FLUID_LOG (FLUID_WARN, _("Sample '%s' start/end file positions are invalid,"
+ " disabling and will not be saved"), sam->name);
+
+ /* disable sample by setting all sample markers to 0 */
+ sam->start = sam->end = sam->loopstart = sam->loopend = 0;
+
+ return (OK);
+ }
+ else if (sam->loopend > sam->end || sam->loopstart >= sam->loopend
+ || sam->loopstart <= sam->start)
+ { /* loop is fowled?? (cluck cluck :) */
+ /* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */
+ if ((sam->end - sam->start) >= 20)
+ {
+ sam->loopstart = sam->start + 8;
+ sam->loopend = sam->end - 8;
+ }
+ else
+ { /* loop is fowled, sample is tiny (can't pad 8 samples) */
+ sam->loopstart = sam->start + 1;
+ sam->loopend = sam->end - 1;
+ }
+ }
+
+ /* convert sample end, loopstart, loopend to offsets from sam->start */
+ sam->end -= sam->start + 1; /* marks last sample, contrary to SF spec. */
+ sam->loopstart -= sam->start;
+ sam->loopend -= sam->start;
+
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/*=================================sfont.c========================
+ Smurf SoundFont Editor
+ ================================================================*/
+
+
+/* optimum chunk area sizes (could be more optimum) */
+#define PRESET_CHUNK_OPTIMUM_AREA 256
+#define INST_CHUNK_OPTIMUM_AREA 256
+#define SAMPLE_CHUNK_OPTIMUM_AREA 256
+#define ZONE_CHUNK_OPTIMUM_AREA 256
+#define MOD_CHUNK_OPTIMUM_AREA 256
+#define GEN_CHUNK_OPTIMUM_AREA 256
+
+unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4,
+ Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0
+};
+
+unsigned short badpgen[] = { Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs,
+ Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_EndAddrCoarseOfs,
+ Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity,
+ Gen_EndLoopAddrCoarseOfs, Gen_SampleModes, Gen_ExclusiveClass,
+ Gen_OverrideRootKey, 0
+};
+
+/* close SoundFont file and delete a SoundFont structure */
+void
+sfont_close (SFData * sf)
+{
+ fluid_list_t *p, *p2;
+
+ if (sf->sffd)
+ fclose (sf->sffd);
+
+ if (sf->fname)
+ free (sf->fname);
+
+ p = sf->info;
+ while (p)
+ {
+ free (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list(sf->info);
+ sf->info = NULL;
+
+ p = sf->preset;
+ while (p)
+ { /* loop over presets */
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* loop over preset's zones */
+ sfont_free_zone (p2->data);
+ p2 = fluid_list_next (p2);
+ } /* free preset's zone list */
+ delete_fluid_list (((SFPreset *) (p->data))->zone);
+ FLUID_FREE (p->data); /* free preset chunk */
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->preset);
+ sf->preset = NULL;
+
+ p = sf->inst;
+ while (p)
+ { /* loop over instruments */
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* loop over inst's zones */
+ sfont_free_zone (p2->data);
+ p2 = fluid_list_next (p2);
+ } /* free inst's zone list */
+ delete_fluid_list (((SFInst *) (p->data))->zone);
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->inst);
+ sf->inst = NULL;
+
+ p = sf->sample;
+ while (p)
+ {
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->sample);
+ sf->sample = NULL;
+
+ FLUID_FREE (sf);
+}
+
+/* free all elements of a zone (Preset or Instrument) */
+void
+sfont_free_zone (SFZone * zone)
+{
+ fluid_list_t *p;
+
+ if (!zone)
+ return;
+
+ p = zone->gen;
+ while (p)
+ { /* Free gen chunks for this zone */
+ if (p->data)
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (zone->gen); /* free genlist */
+
+ p = zone->mod;
+ while (p)
+ { /* Free mod chunks for this zone */
+ if (p->data)
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (zone->mod); /* free modlist */
+
+ FLUID_FREE (zone); /* free zone chunk */
+}
+
+/* preset sort function, first by bank, then by preset # */
+int
+sfont_preset_compare_func (void* a, void* b)
+{
+ int aval, bval;
+
+ aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum;
+ bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum;
+
+ return (aval - bval);
+}
+
+/* delete zone from zone list */
+void
+sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone)
+{
+ *zlist = fluid_list_remove (*zlist, (void*) zone);
+ sfont_free_zone (zone);
+}
+
+/* Find generator in gen list */
+fluid_list_t *
+gen_inlist (int gen, fluid_list_t * genlist)
+{ /* is generator in gen list? */
+ fluid_list_t *p;
+
+ p = genlist;
+ while (p)
+ {
+ if (p->data == NULL)
+ return (NULL);
+ if (gen == ((SFGen *) p->data)->id)
+ break;
+ p = fluid_list_next (p);
+ }
+ return (p);
+}
+
+/* check validity of instrument generator */
+int
+gen_valid (int gen)
+{ /* is generator id valid? */
+ int i = 0;
+
+ if (gen > Gen_MaxValid)
+ return (FALSE);
+ while (badgen[i] && badgen[i] != gen)
+ i++;
+ return (badgen[i] == 0);
+}
+
+/* check validity of preset generator */
+int
+gen_validp (int gen)
+{ /* is preset generator valid? */
+ int i = 0;
+
+ if (!gen_valid (gen))
+ return (FALSE);
+ while (badpgen[i] && badpgen[i] != (unsigned short) gen)
+ i++;
+ return (badpgen[i] == 0);
+}
+
+/*================================util.c===========================*/
+
+/* Logging function, returns FAIL to use as a return value in calling funcs */
+int
+gerr (int ev, char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vprintf(fmt, args);
+ va_end (args);
+
+ printf("\n");
+
+ return (FAIL);
+}
+
+int
+safe_fread (void *buf, int count, FILE * fd)
+{
+ if (fread (buf, count, 1, fd) != 1)
+ { /* size_t = count, nmemb = 1 */
+ if (feof (fd))
+ gerr (ErrEof, _("EOF while attemping to read %d bytes"), count);
+ else
+ FLUID_LOG (FLUID_ERR, _("File read failed"));
+ return (FAIL);
+ }
+ return (OK);
+}
+
+int
+safe_fseek (FILE * fd, long ofs, int whence)
+{
+ if (fseek (fd, ofs, whence) == -1) {
+ FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence);
+ return (FAIL);
+ }
+ return (OK);
+}
diff --git a/libs/fluidsynth/src/fluid_defsfont.h b/libs/fluidsynth/src/fluid_defsfont.h
new file mode 100644
index 0000000000..29f3fd9e86
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_defsfont.h
@@ -0,0 +1,530 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_DEFSFONT_H
+#define _FLUID_DEFSFONT_H
+
+
+#include "fluidsynth.h"
+#include "fluidsynth_priv.h"
+#include "fluid_list.h"
+
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+/*-----------------------------------sfont.h----------------------------*/
+
+#define SF_SAMPMODES_LOOP 1
+#define SF_SAMPMODES_UNROLL 2
+
+#define SF_MIN_SAMPLERATE 400
+#define SF_MAX_SAMPLERATE 50000
+
+#define SF_MIN_SAMPLE_LENGTH 32
+
+/* Sound Font structure defines */
+
+typedef struct _SFVersion
+{ /* version structure */
+ unsigned short major;
+ unsigned short minor;
+}
+SFVersion;
+
+typedef struct _SFMod
+{ /* Modulator structure */
+ unsigned short src; /* source modulator */
+ unsigned short dest; /* destination generator */
+ signed short amount; /* signed, degree of modulation */
+ unsigned short amtsrc; /* second source controls amnt of first */
+ unsigned short trans; /* transform applied to source */
+}
+SFMod;
+
+typedef union _SFGenAmount
+{ /* Generator amount structure */
+ signed short sword; /* signed 16 bit value */
+ unsigned short uword; /* unsigned 16 bit value */
+ struct
+ {
+ unsigned char lo; /* low value for ranges */
+ unsigned char hi; /* high value for ranges */
+ }
+ range;
+}
+SFGenAmount;
+
+typedef struct _SFGen
+{ /* Generator structure */
+ unsigned short id; /* generator ID */
+ SFGenAmount amount; /* generator value */
+}
+SFGen;
+
+typedef 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 */
+}
+SFZone;
+
+typedef struct _SFSample
+{ /* Sample structure */
+ char name[21]; /* Name of sample */
+ unsigned char samfile; /* Loaded sfont/sample buffer = 0/1 */
+ 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
+ sample, the SF spec has this as the
+ 1st point after, corrected on
+ load/save */
+ unsigned int loopstart; /* Offset from start to start of loop */
+ unsigned int loopend; /* Offset from start to end of loop,
+ marks the first point after loop,
+ whose sample value is ideally
+ equivalent to loopstart */
+ unsigned int samplerate; /* Sample rate recorded at */
+ unsigned char origpitch; /* root midi key number */
+ signed char pitchadj; /* pitch correction in cents */
+ unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */
+ fluid_sample_t *fluid_sample; /* Imported sample (fixed up in fluid_defsfont_load) */
+}
+SFSample;
+
+typedef struct _SFInst
+{ /* Instrument structure */
+ char name[21]; /* Name of instrument */
+ fluid_list_t *zone; /* list of instrument zones */
+}
+SFInst;
+
+typedef struct _SFPreset
+{ /* Preset structure */
+ 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 */
+}
+SFPreset;
+
+/* NOTE: sffd is also used to determine if sound font is new (NULL) */
+typedef struct _SFData
+{ /* Sound font data structure */
+ SFVersion version; /* sound font version */
+ SFVersion romver; /* ROM version */
+ unsigned int samplepos; /* position within sffd of the sample chunk */
+ unsigned int samplesize; /* length within sffd of the sample chunk */
+ char *fname; /* file name */
+ FILE *sffd; /* loaded sfont file descriptor */
+ 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 */
+ fluid_list_t *sample; /* linked list of sample info */
+}
+SFData;
+
+/* sf file chunk IDs */
+enum
+{ UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID,
+ INFO_ID, SDTA_ID, PDTA_ID, /* info/sample/preset */
+
+ IFIL_ID, ISNG_ID, INAM_ID, IROM_ID, /* info ids (1st byte of info strings) */
+ IVER_ID, ICRD_ID, IENG_ID, IPRD_ID, /* more info ids */
+ ICOP_ID, ICMT_ID, ISFT_ID, /* and yet more info ids */
+
+ SNAM_ID, SMPL_ID, /* sample ids */
+ PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID, /* preset ids */
+ IHDR_ID, IBAG_ID, IMOD_ID, IGEN_ID, /* instrument ids */
+ SHDR_ID /* sample info */
+};
+
+/* generator types */
+typedef enum
+{ Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs,
+ Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_ModLFO2Pitch,
+ Gen_VibLFO2Pitch, Gen_ModEnv2Pitch, Gen_FilterFc, Gen_FilterQ,
+ Gen_ModLFO2FilterFc, Gen_ModEnv2FilterFc, Gen_EndAddrCoarseOfs,
+ Gen_ModLFO2Vol, Gen_Unused1, Gen_ChorusSend, Gen_ReverbSend, Gen_Pan,
+ Gen_Unused2, Gen_Unused3, Gen_Unused4,
+ Gen_ModLFODelay, Gen_ModLFOFreq, Gen_VibLFODelay, Gen_VibLFOFreq,
+ Gen_ModEnvDelay, Gen_ModEnvAttack, Gen_ModEnvHold, Gen_ModEnvDecay,
+ Gen_ModEnvSustain, Gen_ModEnvRelease, Gen_Key2ModEnvHold,
+ Gen_Key2ModEnvDecay, Gen_VolEnvDelay, Gen_VolEnvAttack,
+ Gen_VolEnvHold, Gen_VolEnvDecay, Gen_VolEnvSustain, Gen_VolEnvRelease,
+ Gen_Key2VolEnvHold, Gen_Key2VolEnvDecay, Gen_Instrument,
+ Gen_Reserved1, Gen_KeyRange, Gen_VelRange,
+ Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity,
+ Gen_Attenuation, Gen_Reserved2, Gen_EndLoopAddrCoarseOfs,
+ Gen_CoarseTune, Gen_FineTune, Gen_SampleId, Gen_SampleModes,
+ Gen_Reserved3, Gen_ScaleTune, Gen_ExclusiveClass, Gen_OverrideRootKey,
+ Gen_Dummy
+}
+Gen_Type;
+
+#define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */
+#define Gen_Count Gen_Dummy /* count of generators */
+#define GenArrSize sizeof(SFGenAmount)*Gen_Count /* gen array size */
+
+/* generator unit type */
+typedef enum
+{
+ None, /* No unit type */
+ Unit_Smpls, /* in samples */
+ Unit_32kSmpls, /* in 32k samples */
+ Unit_Cent, /* in cents (1/100th of a semitone) */
+ Unit_HzCent, /* in Hz Cents */
+ Unit_TCent, /* in Time Cents */
+ Unit_cB, /* in centibels (1/100th of a decibel) */
+ Unit_Percent, /* in percentage */
+ Unit_Semitone, /* in semitones */
+ Unit_Range /* a range of values */
+}
+Gen_Unit;
+
+/* global data */
+
+extern unsigned short badgen[]; /* list of bad generators */
+extern unsigned short badpgen[]; /* list of bad preset generators */
+
+/* functions */
+void sfont_init_chunks (void);
+
+void sfont_close (SFData * sf);
+void sfont_free_zone (SFZone * zone);
+int sfont_preset_compare_func (void* a, void* b);
+
+void sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone);
+
+fluid_list_t *gen_inlist (int gen, fluid_list_t * genlist);
+int gen_valid (int gen);
+int gen_validp (int gen);
+
+
+/*-----------------------------------sffile.h----------------------------*/
+/*
+ File structures and routines (used to be in sffile.h)
+*/
+
+#define CHNKIDSTR(id) &idlist[(id - 1) * 4]
+
+/* sfont file chunk sizes */
+#define SFPHDRSIZE 38
+#define SFBAGSIZE 4
+#define SFMODSIZE 10
+#define SFGENSIZE 4
+#define SFIHDRSIZE 22
+#define SFSHDRSIZE 46
+
+/* sfont file data structures */
+typedef struct _SFChunk
+{ /* RIFF file chunk structure */
+ unsigned int id; /* chunk id */
+ unsigned int size; /* size of the following chunk */
+}
+SFChunk;
+
+typedef 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 */
+}
+SFPhdr;
+
+typedef struct _SFBag
+{
+ unsigned short genndx; /* index into generator list */
+ unsigned short modndx; /* index into modulator list */
+}
+SFBag;
+
+typedef struct _SFIhdr
+{
+ char name[20]; /* Name of instrument */
+ unsigned short ibagndx; /* Instrument bag index */
+}
+SFIhdr;
+
+typedef 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 */
+}
+SFShdr;
+
+/* data */
+extern char idlist[];
+
+/* functions */
+SFData *sfload_file (const char * fname);
+
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+
+
+/*-----------------------------------util.h----------------------------*/
+/*
+ Utility functions (formerly in util.h)
+ */
+#define FAIL 0
+#define OK 1
+
+enum
+{ ErrWarn, ErrFatal, ErrStatus, ErrCorr, ErrEof, ErrMem, Errno,
+ ErrRead, ErrWrite
+};
+
+#define ErrMax ErrWrite
+#define ErrnoStart Errno
+#define ErrnoEnd ErrWrite
+
+int gerr (int ev, char * fmt, ...);
+int safe_fread (void *buf, int count, FILE * fd);
+int safe_fwrite (void *buf, int count, FILE * fd);
+int safe_fseek (FILE * fd, long ofs, int whence);
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+
+
+/***************************************************************
+ *
+ * FORWARD DECLARATIONS
+ */
+typedef struct _fluid_defsfont_t fluid_defsfont_t;
+typedef struct _fluid_defpreset_t fluid_defpreset_t;
+typedef struct _fluid_preset_zone_t fluid_preset_zone_t;
+typedef struct _fluid_inst_t fluid_inst_t;
+typedef struct _fluid_inst_zone_t fluid_inst_zone_t;
+
+/*
+
+ Public interface
+
+ */
+
+fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings);
+int delete_fluid_defsfloader(fluid_sfloader_t* loader);
+fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename);
+
+
+int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont);
+char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont);
+fluid_preset_t* fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
+void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont);
+int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset);
+
+
+int fluid_defpreset_preset_delete(fluid_preset_t* preset);
+char* fluid_defpreset_preset_get_name(fluid_preset_t* preset);
+int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset);
+int fluid_defpreset_preset_get_num(fluid_preset_t* preset);
+int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+
+/*
+ * fluid_defsfont_t
+ */
+struct _fluid_defsfont_t
+{
+ char* filename; /* the filename of this soundfont */
+ unsigned int samplepos; /* the position in the file at which the sample data starts */
+ unsigned int samplesize; /* the size of the sample data */
+ short* sampledata; /* the sample data, loaded in ram */
+ fluid_list_t* sample; /* the samples in this soundfont */
+ fluid_defpreset_t* preset; /* the presets of this soundfont */
+ int mlock; /* Should we try memlock (avoid swapping)? */
+
+ fluid_preset_t iter_preset; /* preset interface used in the iteration */
+ fluid_defpreset_t* iter_cur; /* the current preset in the iteration */
+
+ fluid_preset_t** preset_stack; /* List of presets that are available to use */
+ int preset_stack_capacity; /* Length of preset_stack array */
+ int preset_stack_size; /* Current number of items in the stack */
+};
+
+
+fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings);
+int delete_fluid_defsfont(fluid_defsfont_t* sfont);
+int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file);
+char* fluid_defsfont_get_name(fluid_defsfont_t* sfont);
+fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int prenum);
+void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont);
+int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset);
+int fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont);
+int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample);
+int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset);
+
+
+/*
+ * fluid_preset_t
+ */
+struct _fluid_defpreset_t
+{
+ fluid_defpreset_t* next;
+ fluid_defsfont_t* sfont; /* the soundfont this preset belongs to */
+ char name[21]; /* the name of the preset */
+ unsigned int bank; /* the bank number */
+ 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 */
+};
+
+fluid_defpreset_t* new_fluid_defpreset(fluid_defsfont_t* sfont);
+int delete_fluid_defpreset(fluid_defpreset_t* preset);
+fluid_defpreset_t* fluid_defpreset_next(fluid_defpreset_t* preset);
+int fluid_defpreset_import_sfont(fluid_defpreset_t* preset, SFPreset* sfpreset, fluid_defsfont_t* sfont);
+int fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone);
+int fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone);
+fluid_preset_zone_t* fluid_defpreset_get_zone(fluid_defpreset_t* preset);
+fluid_preset_zone_t* fluid_defpreset_get_global_zone(fluid_defpreset_t* preset);
+int fluid_defpreset_get_banknum(fluid_defpreset_t* preset);
+int fluid_defpreset_get_num(fluid_defpreset_t* preset);
+char* fluid_defpreset_get_name(fluid_defpreset_t* preset);
+int fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+/*
+ * fluid_preset_zone
+ */
+struct _fluid_preset_zone_t
+{
+ fluid_preset_zone_t* next;
+ char* name;
+ fluid_inst_t* inst;
+ int keylo;
+ int keyhi;
+ int vello;
+ int velhi;
+ fluid_gen_t gen[GEN_LAST];
+ fluid_mod_t * mod; /* List of modulators */
+};
+
+fluid_preset_zone_t* new_fluid_preset_zone(char* name);
+int delete_fluid_preset_zone(fluid_preset_zone_t* zone);
+fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset);
+int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone* sfzone, fluid_defsfont_t* sfont);
+int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel);
+fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone);
+
+/*
+ * fluid_inst_t
+ */
+struct _fluid_inst_t
+{
+ char name[21];
+ fluid_inst_zone_t* global_zone;
+ fluid_inst_zone_t* zone;
+};
+
+fluid_inst_t* new_fluid_inst(void);
+int delete_fluid_inst(fluid_inst_t* inst);
+int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont);
+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);
+fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst);
+fluid_inst_zone_t* fluid_inst_get_global_zone(fluid_inst_t* inst);
+
+/*
+ * fluid_inst_zone_t
+ */
+struct _fluid_inst_zone_t
+{
+ fluid_inst_zone_t* next;
+ char* name;
+ fluid_sample_t* sample;
+ int keylo;
+ int keyhi;
+ int vello;
+ int velhi;
+ fluid_gen_t gen[GEN_LAST];
+ fluid_mod_t * mod; /* List of modulators */
+};
+
+fluid_inst_zone_t* new_fluid_inst_zone(char* name);
+int 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* zone, SFZone *sfzone, fluid_defsfont_t* sfont);
+int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel);
+fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone);
+
+
+
+fluid_sample_t* new_fluid_sample(void);
+int delete_fluid_sample(fluid_sample_t* sample);
+int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont);
+int fluid_sample_in_rom(fluid_sample_t* sample);
+
+
+#endif /* _FLUID_SFONT_H */
diff --git a/libs/fluidsynth/src/fluid_event.c b/libs/fluidsynth/src/fluid_event.c
new file mode 100644
index 0000000000..b3b0608345
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_event.c
@@ -0,0 +1,781 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+/*
+ 2002 : API design by Peter Hanappe and Antoine Schmitt
+ August 2002 : Implementation by Antoine Schmitt as@gratin.org
+ as part of the infiniteCD author project
+ http://www.infiniteCD.org/
+ Oct4.2002 : AS : corrected bug in heap allocation, that caused a crash during sequencer free.
+*/
+
+
+#include "fluid_event_priv.h"
+#include "fluidsynth_priv.h"
+
+/***************************************************************
+ *
+ * SEQUENCER EVENTS
+ */
+
+/* Event alloc/free */
+
+void
+fluid_event_clear(fluid_event_t* evt)
+{
+ FLUID_MEMSET(evt, 0, sizeof(fluid_event_t));
+
+ // by default, no type
+ evt->dest = -1;
+ evt->src = -1;
+ evt->type = -1;
+}
+
+/**
+ * Create a new sequencer event structure.
+ * @return New sequencer event structure or NULL if out of memory
+ */
+fluid_event_t*
+new_fluid_event()
+{
+ fluid_event_t* evt;
+
+ evt = FLUID_NEW(fluid_event_t);
+ if (evt == NULL) {
+ fluid_log(FLUID_PANIC, "event: Out of memory\n");
+ return NULL;
+ }
+ fluid_event_clear(evt);
+
+ return(evt);
+}
+
+/**
+ * Delete a sequencer event structure.
+ * @param evt Sequencer event structure created by new_fluid_event().
+ */
+void
+delete_fluid_event(fluid_event_t* evt)
+{
+
+ if (evt == NULL) {
+ return;
+ }
+
+ FLUID_FREE(evt);
+}
+
+/**
+ * Set the time field of a sequencer event.
+ * @internal
+ * @param evt Sequencer event structure
+ * @param time Time value to assign
+ */
+void
+fluid_event_set_time(fluid_event_t* evt, unsigned int time)
+{
+ evt->time = time;
+}
+
+/**
+ * Set source of a sequencer event (DOCME).
+ * @param evt Sequencer event structure
+ * @param src DOCME
+ */
+void
+fluid_event_set_source(fluid_event_t* evt, short src)
+{
+ evt->src = src;
+}
+
+/**
+ * Set destination of a sequencer event (DOCME).
+ * @param evt Sequencer event structure
+ * @param dest DOCME
+ */
+void
+fluid_event_set_dest(fluid_event_t* evt, short dest)
+{
+ evt->dest = dest;
+}
+
+/**
+ * Set a sequencer event to be a timer event.
+ * @param evt Sequencer event structure
+ * @param data DOCME
+ */
+void
+fluid_event_timer(fluid_event_t* evt, void* data)
+{
+ evt->type = FLUID_SEQ_TIMER;
+ evt->data = data;
+}
+
+/**
+ * Set a sequencer event to be a note on event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param key MIDI note number (0-127)
+ * @param vel MIDI velocity value (0-127)
+ */
+void
+fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel)
+{
+ evt->type = FLUID_SEQ_NOTEON;
+ evt->channel = channel;
+ evt->key = key;
+ evt->vel = vel;
+}
+
+/**
+ * Set a sequencer event to be a note off event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param key MIDI note number (0-127)
+ */
+void
+fluid_event_noteoff(fluid_event_t* evt, int channel, short key)
+{
+ evt->type = FLUID_SEQ_NOTEOFF;
+ evt->channel = channel;
+ evt->key = key;
+}
+
+/**
+ * Set a sequencer event to be a note duration event.
+ * @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 (DOCME units?)
+ */
+void
+fluid_event_note(fluid_event_t* evt, int channel, short key, short vel, unsigned int duration)
+{
+ evt->type = FLUID_SEQ_NOTE;
+ evt->channel = channel;
+ evt->key = key;
+ evt->vel = vel;
+ evt->duration = duration;
+}
+
+/**
+ * Set a sequencer event to be an all sounds off event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ */
+void
+fluid_event_all_sounds_off(fluid_event_t* evt, int channel)
+{
+ evt->type = FLUID_SEQ_ALLSOUNDSOFF;
+ evt->channel = channel;
+}
+
+/**
+ * Set a sequencer event to be a all notes off event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ */
+void
+fluid_event_all_notes_off(fluid_event_t* evt, int channel)
+{
+ evt->type = FLUID_SEQ_ALLNOTESOFF;
+ evt->channel = channel;
+}
+
+/**
+ * Set a sequencer event to be a bank select event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param bank_num MIDI bank number (0-16383)
+ */
+void
+fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num)
+{
+ evt->type = FLUID_SEQ_BANKSELECT;
+ evt->channel = channel;
+ evt->control = bank_num;
+}
+
+/**
+ * Set a sequencer event to be a program change event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param val MIDI program number (0-127)
+ */
+void
+fluid_event_program_change(fluid_event_t* evt, int channel, short val)
+{
+ evt->type = FLUID_SEQ_PROGRAMCHANGE;
+ evt->channel = channel;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a program select event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param sfont_id SoundFont ID number
+ * @param bank_num MIDI bank number (0-16383)
+ * @param preset_num MIDI preset number (0-127)
+ */
+void
+fluid_event_program_select(fluid_event_t* evt, int channel,
+ unsigned int sfont_id, short bank_num, short preset_num)
+{
+ evt->type = FLUID_SEQ_PROGRAMSELECT;
+ evt->channel = channel;
+ evt->duration = sfont_id;
+ evt->value = preset_num;
+ evt->control = bank_num;
+}
+
+/**
+ * Set a sequencer event to be an any control change event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * DOCME
+ */
+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
+ * @param channel MIDI channel number
+ * @param pitch MIDI pitch bend value (0-16383, 8192 = no bend)
+ */
+void
+fluid_event_pitch_bend(fluid_event_t* evt, int channel, int pitch)
+{
+ evt->type = FLUID_SEQ_PITCHBEND;
+ evt->channel = channel;
+ if (pitch < 0) pitch = 0;
+ if (pitch > 16383) pitch = 16383;
+ evt->pitch = pitch;
+}
+
+/**
+ * Set a sequencer event to be a pitch wheel sensitivity event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param value MIDI pitch wheel sensitivity value (DOCME units?)
+ */
+void
+fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short value)
+{
+ evt->type = FLUID_SEQ_PITCHWHHELSENS;
+ evt->channel = channel;
+ evt->value = value;
+}
+
+/**
+ * Set a sequencer event to be a modulation event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param val MIDI modulation value (0-127)
+ */
+void
+fluid_event_modulation(fluid_event_t* evt, int channel, short val)
+{
+ evt->type = FLUID_SEQ_MODULATION;
+ evt->channel = channel;
+ if (val < 0) val = 0;
+ if (val > 127) val = 127;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a MIDI sustain event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param val MIDI sustain value (0-127)
+ */
+void
+fluid_event_sustain(fluid_event_t* evt, int channel, short val)
+{
+ evt->type = FLUID_SEQ_SUSTAIN;
+ evt->channel = channel;
+ if (val < 0) val = 0;
+ if (val > 127) val = 127;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a MIDI control change event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param control MIDI control number (0-127)
+ * @param val MIDI control value (0-16383 DOCME is that true?)
+ */
+void
+fluid_event_control_change(fluid_event_t* evt, int channel, short control, short val)
+{
+ evt->type = FLUID_SEQ_CONTROLCHANGE;
+ evt->channel = channel;
+ evt->control = control;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a stereo pan event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @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)
+{
+ evt->type = FLUID_SEQ_PAN;
+ evt->channel = channel;
+ if (val < 0) val = 0;
+ if (val > 127) val = 127;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a volume event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param val Volume value (0-127)
+ */
+void
+fluid_event_volume(fluid_event_t* evt, int channel, short val)
+{
+ evt->type = FLUID_SEQ_VOLUME;
+ evt->channel = channel;
+ if (val < 0) val = 0;
+ if (val > 127) val = 127;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a reverb send event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param val Reverb amount (0-127)
+ */
+void
+fluid_event_reverb_send(fluid_event_t* evt, int channel, short val)
+{
+ evt->type = FLUID_SEQ_REVERBSEND;
+ evt->channel = channel;
+ if (val < 0) val = 0;
+ if (val > 127) val = 127;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a chorus send event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param val Chorus amount (0-127)
+ */
+void
+fluid_event_chorus_send(fluid_event_t* evt, int channel, short val)
+{
+ evt->type = FLUID_SEQ_CHORUSSEND;
+ evt->channel = channel;
+ if (val < 0) val = 0;
+ if (val > 127) val = 127;
+ evt->value = val;
+}
+
+
+/**
+ * Set a sequencer event to be an unregistering event.
+ * @param evt Sequencer event structure
+ * @since 1.1.0
+ */
+void
+fluid_event_unregistering(fluid_event_t* evt)
+{
+ evt->type = FLUID_SEQ_UNREGISTERING;
+}
+
+/**
+ * Set a sequencer event to be a channel-wide aftertouch event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param val Aftertouch amount (0-127)
+ * @since 1.1.0
+ */
+void
+fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val)
+{
+ evt->type = FLUID_SEQ_CHANNELPRESSURE;
+ evt->channel = channel;
+ if (val < 0) val = 0;
+ if (val > 127) val = 127;
+ evt->value = val;
+}
+
+/**
+ * Set a sequencer event to be a midi system reset event.
+ * @param evt Sequencer event structure
+ * @since 1.1.0
+ */
+void
+fluid_event_system_reset(fluid_event_t* evt)
+{
+ evt->type = FLUID_SEQ_SYSTEMRESET;
+}
+
+
+
+/*
+ * Accessing event data
+ */
+
+/**
+ * Get the event type (#fluid_seq_event_type) field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return Event type (#fluid_seq_event_type).
+ */
+int fluid_event_get_type(fluid_event_t* evt)
+{
+ return evt->type;
+}
+
+/**
+ * Get the time field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return Time value (DOCME units?)
+ */
+unsigned int fluid_event_get_time(fluid_event_t* evt)
+{
+ return evt->time;
+}
+
+/**
+ * Get the source field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return DOCME
+ */
+short fluid_event_get_source(fluid_event_t* evt)
+{
+ return evt->src;
+}
+
+/**
+ * Get the dest field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return DOCME
+ */
+short fluid_event_get_dest(fluid_event_t* evt)
+{
+ return evt->dest;
+}
+
+/**
+ * Get the MIDI channel field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return MIDI channel number (DOCME 0-15 or more?)
+ */
+int fluid_event_get_channel(fluid_event_t* evt)
+{
+ return evt->channel;
+}
+
+/**
+ * Get the MIDI note field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return MIDI note number (0-127)
+ */
+short fluid_event_get_key(fluid_event_t* evt)
+{
+ return evt->key;
+}
+
+/**
+ * Get the MIDI velocity field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return MIDI velocity value (0-127)
+ */
+short fluid_event_get_velocity(fluid_event_t* evt)
+
+{
+ return evt->vel;
+}
+
+/**
+ * Get the MIDI control number field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return MIDI control number (0-127)
+ */
+short fluid_event_get_control(fluid_event_t* evt)
+{
+ return evt->control;
+}
+
+/**
+ * Get the value field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return Value field of event.
+ *
+ * The Value field is used by the following event types:
+ * #FLUID_SEQ_PROGRAMCHANGE, #FLUID_SEQ_PROGRAMSELECT (preset_num),
+ * #FLUID_SEQ_PITCHWHHELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN,
+ * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME,
+ * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND.
+ */
+short fluid_event_get_value(fluid_event_t* evt)
+{
+ return evt->value;
+}
+
+/**
+ * Get the data field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return Data field of event.
+ *
+ * Used by the #FLUID_SEQ_TIMER event type.
+ */
+void* fluid_event_get_data(fluid_event_t* evt)
+{
+ return evt->data;
+}
+
+/**
+ * Get the duration field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return Note duration value (DOCME units?)
+ *
+ * Used by the #FLUID_SEQ_NOTE event type.
+ */
+unsigned int fluid_event_get_duration(fluid_event_t* evt)
+{
+ return evt->duration;
+}
+
+/**
+ * Get the MIDI bank field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return MIDI bank number (0-16383)
+ *
+ * Used by the #FLUID_SEQ_BANKSELECT and #FLUID_SEQ_PROGRAMSELECT
+ * event types.
+ */
+short fluid_event_get_bank(fluid_event_t* evt)
+{
+ return evt->control;
+}
+
+/**
+ * Get the pitch field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return MIDI pitch bend pitch value (0-16383, 8192 = no bend)
+ *
+ * Used by the #FLUID_SEQ_PITCHBEND event type.
+ */
+int fluid_event_get_pitch(fluid_event_t* evt)
+{
+ return evt->pitch;
+}
+
+/**
+ * Get the MIDI program field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return MIDI program number (0-127)
+ *
+ * Used by the #FLUID_SEQ_PROGRAMCHANGE and #FLUID_SEQ_PROGRAMSELECT
+ * event types.
+ */
+short
+fluid_event_get_program(fluid_event_t* evt)
+{
+ return evt->value;
+}
+
+/**
+ * Get the SoundFont ID field from a sequencer event structure.
+ * @param evt Sequencer event structure
+ * @return SoundFont identifier value.
+ *
+ * Used by the #FLUID_SEQ_PROGRAMSELECT event type.
+ */
+unsigned int
+fluid_event_get_sfont_id(fluid_event_t* evt)
+{
+ return evt->duration;
+}
+
+
+
+/********************/
+/* heap management */
+/********************/
+
+fluid_evt_heap_t*
+_fluid_evt_heap_init(int nbEvents)
+{
+#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
+}
diff --git a/libs/fluidsynth/src/fluid_event_priv.h b/libs/fluidsynth/src/fluid_event_priv.h
new file mode 100644
index 0000000000..c0a4c1af4e
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_event_priv.h
@@ -0,0 +1,83 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_EVENT_PRIV_H
+#define _FLUID_EVENT_PRIV_H
+
+#include "fluidsynth.h"
+#include "fluid_sys.h"
+
+/* Private data for event */
+/* ?? should be optimized in size, using unions */
+struct _fluid_event_t {
+ unsigned int time;
+ int type;
+ short src;
+ short dest;
+ int channel;
+ short key;
+ short vel;
+ short control;
+ short value;
+ short id; //?? unused ?
+ int pitch;
+ unsigned int duration;
+ void* data;
+};
+
+unsigned int fluid_event_get_time(fluid_event_t* evt);
+void fluid_event_set_time(fluid_event_t* evt, unsigned int time);
+
+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;
+#endif
+} fluid_evt_heap_t;
+
+fluid_evt_heap_t* _fluid_evt_heap_init(int nbEvents);
+void _fluid_evt_heap_free(fluid_evt_heap_t* heap);
+fluid_evt_entry* _fluid_seq_heap_get_free(fluid_evt_heap_t* heap);
+void _fluid_seq_heap_set_free(fluid_evt_heap_t* heap, fluid_evt_entry* evt);
+
+#endif /* _FLUID_EVENT_PRIV_H */
diff --git a/libs/fluidsynth/src/fluid_event_queue.h b/libs/fluidsynth/src/fluid_event_queue.h
new file mode 100644
index 0000000000..a5c24da754
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_event_queue.h
@@ -0,0 +1,195 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_EVENT_QUEUE_H
+#define _FLUID_EVENT_QUEUE_H
+
+#include "fluid_sys.h"
+#include "fluid_midi.h"
+#include "fluid_ringbuffer.h"
+
+/**
+ * Type of queued event.
+ */
+enum fluid_event_queue_elem
+{
+ FLUID_EVENT_QUEUE_ELEM_MIDI, /**< MIDI event. Uses midi field of event value */
+ FLUID_EVENT_QUEUE_ELEM_UPDATE_GAIN, /**< Update synthesizer gain. No payload value */
+ FLUID_EVENT_QUEUE_ELEM_POLYPHONY, /**< Synth polyphony event. No payload value */
+ FLUID_EVENT_QUEUE_ELEM_GEN, /**< Generator event. Uses gen field of event value */
+ FLUID_EVENT_QUEUE_ELEM_PRESET, /**< Preset set event. Uses preset field of event value */
+ FLUID_EVENT_QUEUE_ELEM_STOP_VOICES, /**< Stop voices event. Uses ival field of event value */
+ FLUID_EVENT_QUEUE_ELEM_FREE_PRESET, /**< Free preset return event. Uses pval field of event value */
+ FLUID_EVENT_QUEUE_ELEM_SET_TUNING, /**< Set tuning event. Uses set_tuning field of event value */
+ FLUID_EVENT_QUEUE_ELEM_REPL_TUNING, /**< Replace tuning event. Uses repl_tuning field of event value */
+ FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING /**< Unref tuning return event. Uses unref_tuning field of event value */
+};
+
+/**
+ * SoundFont generator set event structure.
+ */
+typedef struct
+{
+ int channel; /**< MIDI channel number */
+ int param; /**< FluidSynth generator ID */
+ float value; /**< Value for the generator (absolute or relative) */
+ int absolute; /**< 1 if value is absolute, 0 if relative */
+} fluid_event_gen_t;
+
+/**
+ * Preset channel assignment event structure.
+ */
+typedef struct
+{
+ int channel; /**< MIDI channel number */
+ fluid_preset_t *preset; /**< Preset to assign (synth thread owns) */
+} fluid_event_preset_t;
+
+/**
+ * Tuning assignment event structure.
+ */
+typedef struct
+{
+ char apply; /**< TRUE to set tuning in realtime */
+ int channel; /**< MIDI channel number */
+ fluid_tuning_t *tuning; /**< Tuning to assign */
+} fluid_event_set_tuning_t;
+
+/**
+ * Tuning replacement event structure.
+ */
+typedef struct
+{
+ char apply; /**< TRUE if tuning change should be applied in realtime */
+ fluid_tuning_t *old_tuning; /**< Old tuning pointer to replace */
+ fluid_tuning_t *new_tuning; /**< New tuning to assign */
+} fluid_event_repl_tuning_t;
+
+/**
+ * Tuning unref event structure.
+ */
+typedef struct
+{
+ fluid_tuning_t *tuning; /**< Tuning to unref */
+ int count; /**< Number of times to unref */
+} fluid_event_unref_tuning_t;
+
+/**
+ * Structure for an integer parameter sent to a MIDI channel (bank or SoundFont ID for example).
+ */
+typedef struct
+{
+ int channel;
+ int val;
+} fluid_event_channel_int_t;
+
+/**
+ * Event queue element structure.
+ */
+typedef struct
+{
+ char type; /**< fluid_event_queue_elem */
+
+ union
+ {
+ fluid_midi_event_t midi; /**< If type == FLUID_EVENT_QUEUE_ELEM_MIDI */
+ fluid_event_gen_t gen; /**< If type == FLUID_EVENT_QUEUE_ELEM_GEN */
+ fluid_event_preset_t preset; /**< If type == FLUID_EVENT_QUEUE_ELEM_PRESET */
+ fluid_event_set_tuning_t set_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_SET_TUNING */
+ fluid_event_repl_tuning_t repl_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_REPL_TUNING */
+ fluid_event_unref_tuning_t unref_tuning; /**< If type == FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING */
+ double dval; /**< A floating point payload value */
+ int ival; /**< An integer payload value */
+ void *pval; /**< A pointer payload value */
+ };
+} fluid_event_queue_elem_t;
+
+typedef struct _fluid_ringbuffer_t fluid_event_queue_t;
+
+static FLUID_INLINE fluid_event_queue_t *
+fluid_event_queue_new (int count)
+{
+ return (fluid_event_queue_t *) new_fluid_ringbuffer(count, sizeof(fluid_event_queue_elem_t));
+}
+
+static FLUID_INLINE void fluid_event_queue_free (fluid_event_queue_t *queue)
+{
+ delete_fluid_ringbuffer(queue);
+}
+
+/**
+ * Get pointer to next input array element in queue.
+ * @param queue Lockless queue instance
+ * @return Pointer to array element in queue to store data to or NULL if queue is full
+ *
+ * This function along with fluid_queue_next_inptr() form a queue "push"
+ * operation and is split into 2 functions to avoid an element copy. Note that
+ * the returned array element pointer may contain the data of a previous element
+ * if the queue has wrapped around. This can be used to reclaim pointers to
+ * allocated memory, etc.
+ */
+static FLUID_INLINE fluid_event_queue_elem_t *
+fluid_event_queue_get_inptr (fluid_event_queue_t *queue)
+{
+ return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_inptr(queue, 0);
+}
+
+/**
+ * Advance the input queue index to complete a "push" operation.
+ * @param queue Lockless queue instance
+ *
+ * This function along with fluid_queue_get_inptr() form a queue "push"
+ * operation and is split into 2 functions to avoid element copy.
+ */
+static FLUID_INLINE void
+fluid_event_queue_next_inptr (fluid_event_queue_t *queue)
+{
+ fluid_ringbuffer_next_inptr(queue, 1);
+}
+
+/**
+ * Get pointer to next output array element in queue.
+ * @param queue Lockless queue instance
+ * @return Pointer to array element data in the queue or NULL if empty, can only
+ * be used up until fluid_queue_next_outptr() is called.
+ *
+ * This function along with fluid_queue_next_outptr() form a queue "pop"
+ * operation and is split into 2 functions to avoid an element copy.
+ */
+static FLUID_INLINE fluid_event_queue_elem_t *
+fluid_event_queue_get_outptr (fluid_event_queue_t *queue)
+{
+ return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_outptr(queue);
+}
+
+/**
+ * Advance the output queue index to complete a "pop" operation.
+ * @param queue Lockless queue instance
+ *
+ * This function along with fluid_queue_get_outptr() form a queue "pop"
+ * operation and is split into 2 functions to avoid an element copy.
+ */
+static FLUID_INLINE void
+fluid_event_queue_next_outptr (fluid_event_queue_t *queue)
+{
+ fluid_ringbuffer_next_outptr(queue);
+}
+
+#endif /* _FLUID_EVENT_QUEUE_H */
diff --git a/libs/fluidsynth/src/fluid_gen.c b/libs/fluidsynth/src/fluid_gen.c
new file mode 100644
index 0000000000..0f1413eab2
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_gen.c
@@ -0,0 +1,149 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#include "fluid_gen.h"
+#include "fluid_chan.h"
+
+
+/* See SFSpec21 $8.1.3 */
+fluid_gen_info_t fluid_gen_info[] = {
+ /* number/name init 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 }
+};
+
+
+/**
+ * Set an array of generators to their default values.
+ * @param gen Array of generators (should be #GEN_LAST in size).
+ * @return Always returns 0
+ */
+int
+fluid_gen_set_default_values(fluid_gen_t* gen)
+{
+ int i;
+
+ for (i = 0; i < GEN_LAST; i++) {
+ gen[i].flags = GEN_UNUSED;
+ gen[i].mod = 0.0;
+ gen[i].nrpn = 0.0;
+ gen[i].val = fluid_gen_info[i].def;
+ }
+
+ return FLUID_OK;
+}
+
+
+/* fluid_gen_init
+ *
+ * Set an array of generators to their initial value
+ */
+int
+fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel)
+{
+ int i;
+
+ fluid_gen_set_default_values(gen);
+
+ for (i = 0; i < GEN_LAST; i++) {
+ gen[i].nrpn = fluid_channel_get_gen(channel, i);
+
+ /* This is an extension to the SoundFont standard. More
+ * documentation is available at the fluid_synth_set_gen2()
+ * function. */
+ if (fluid_channel_get_gen_abs(channel, i)) {
+ gen[i].flags = GEN_ABS_NRPN;
+ }
+ }
+
+ return FLUID_OK;
+}
+
+fluid_real_t fluid_gen_scale(int gen, float value)
+{
+ return (fluid_gen_info[gen].min
+ + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min));
+}
+
+fluid_real_t fluid_gen_scale_nrpn(int gen, int data)
+{
+ fluid_real_t value = (float) data - 8192.0f;
+ fluid_clip(value, -8192, 8192);
+ return value * (float) fluid_gen_info[gen].nrpn_scale;
+}
diff --git a/libs/fluidsynth/src/fluid_gen.h b/libs/fluidsynth/src/fluid_gen.h
new file mode 100644
index 0000000000..f54d0490fc
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_gen.h
@@ -0,0 +1,44 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_GEN_H
+#define _FLUID_GEN_H
+
+#include "fluidsynth_priv.h"
+
+typedef struct _fluid_gen_info_t {
+ char num; /* Generator number */
+ char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */
+ char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */
+ float min; /* The minimum value */
+ float max; /* The maximum value */
+ float def; /* The default value (cfr. fluid_gen_set_default_values()) */
+} fluid_gen_info_t;
+
+#define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); }
+#define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); }
+
+fluid_real_t fluid_gen_scale(int gen, float value);
+fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn);
+int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel);
+
+
+#endif /* _FLUID_GEN_H */
diff --git a/libs/fluidsynth/src/fluid_hash.c b/libs/fluidsynth/src/fluid_hash.c
new file mode 100644
index 0000000000..9d5a92009e
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_hash.c
@@ -0,0 +1,1310 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ *
+ * Adapted for FluidSynth use by Josh Green <jgreen@users.sourceforge.net>
+ * September 8, 2009 from glib 2.18.4
+ */
+
+/*
+ * MT safe
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_hash.h"
+#include "fluid_list.h"
+
+
+#define HASH_TABLE_MIN_SIZE 11
+#define HASH_TABLE_MAX_SIZE 13845163
+
+
+typedef struct
+{
+ fluid_hashtable_t *hashtable;
+ fluid_hashnode_t *prev_node;
+ fluid_hashnode_t *node;
+ int position;
+ int pre_advanced; // Boolean
+ int version;
+} RealIter;
+
+
+/* Excerpt from glib gprimes.c */
+
+static const guint primes[] =
+{
+ 11,
+ 19,
+ 37,
+ 73,
+ 109,
+ 163,
+ 251,
+ 367,
+ 557,
+ 823,
+ 1237,
+ 1861,
+ 2777,
+ 4177,
+ 6247,
+ 9371,
+ 14057,
+ 21089,
+ 31627,
+ 47431,
+ 71143,
+ 106721,
+ 160073,
+ 240101,
+ 360163,
+ 540217,
+ 810343,
+ 1215497,
+ 1823231,
+ 2734867,
+ 4102283,
+ 6153409,
+ 9230113,
+ 13845163,
+};
+
+static const unsigned int nprimes = sizeof (primes) / sizeof (primes[0]);
+
+static unsigned int
+spaced_primes_closest (unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 0; i < nprimes; i++)
+ if (primes[i] > num)
+ return primes[i];
+
+ return primes[nprimes - 1];
+}
+
+/* End excerpt from glib gprimes.c */
+
+
+/*
+ * @hashtable: our #fluid_hashtable_t
+ * @key: the key to lookup against
+ * @hash_return: optional key hash return location
+ * Return value: a pointer to the described #fluid_hashnode_t pointer
+ *
+ * Performs a lookup in the hash table. Virtually all hash operations
+ * will use this function internally.
+ *
+ * This function first computes the hash value of the key using the
+ * user's hash function.
+ *
+ * If an entry in the table matching @key is found then this function
+ * returns a pointer to the pointer to that entry in the table. In
+ * the case that the entry is at the head of a chain, this pointer
+ * will be an item in the nodes[] array. In the case that the entry
+ * is not at the head of a chain, this pointer will be the ->next
+ * pointer on the node that preceeds it.
+ *
+ * In the case that no matching entry exists in the table, a pointer
+ * to a %NULL pointer will be returned. To insert a item, this %NULL
+ * pointer should be updated to point to the new #fluid_hashnode_t.
+ *
+ * If @hash_return is a pass-by-reference parameter. If it is
+ * non-%NULL then the computed hash value is returned. This is to
+ * save insertions from having to compute the hash record again for
+ * the new record.
+ */
+static inline fluid_hashnode_t **
+fluid_hashtable_lookup_node (fluid_hashtable_t *hashtable, const void *key,
+ unsigned int *hash_return)
+{
+ fluid_hashnode_t **node_ptr, *node;
+ unsigned int hash_value;
+
+ hash_value = (* hashtable->hash_func)(key);
+ node_ptr = &hashtable->nodes[hash_value % hashtable->size];
+
+ if (hash_return)
+ *hash_return = hash_value;
+
+ /* Hash table lookup needs to be fast.
+ * We therefore remove the extra conditional of testing
+ * whether to call the key_equal_func or not from
+ * the inner loop.
+ *
+ * Additional optimisation: first check if our full hash
+ * values are equal so we can avoid calling the full-blown
+ * key equality function in most cases.
+ */
+ if (hashtable->key_equal_func)
+ {
+ while ((node = *node_ptr))
+ {
+ if (node->key_hash == hash_value &&
+ hashtable->key_equal_func (node->key, key))
+ break;
+
+ node_ptr = &(*node_ptr)->next;
+ }
+ }
+ else
+ {
+ while ((node = *node_ptr))
+ {
+ if (node->key == key)
+ break;
+
+ node_ptr = &(*node_ptr)->next;
+ }
+ }
+
+ return node_ptr;
+}
+
+/*
+ * @hashtable: our #fluid_hashtable_t
+ * @node_ptr_ptr: a pointer to the return value from
+ * fluid_hashtable_lookup_node()
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ *
+ * Removes a node from the hash table and updates the node count. The
+ * node is freed. No table resize is performed.
+ *
+ * If @notify is %TRUE then the destroy notify functions are called
+ * for the key and value of the hash node.
+ *
+ * @node_ptr_ptr is a pass-by-reference in/out parameter. When the
+ * function is called, it should point to the pointer to the node to
+ * remove. This level of indirection is required so that the pointer
+ * may be updated appropriately once the node has been removed.
+ *
+ * Before the function returns, the pointer at @node_ptr_ptr will be
+ * updated to point to the position in the table that contains the
+ * pointer to the "next" node in the chain. This makes this function
+ * convenient to use from functions that iterate over the entire
+ * table. If there is no further item in the chain then the
+ * #fluid_hashnode_t pointer will be %NULL (ie: **node_ptr_ptr == %NULL).
+ *
+ * Since the pointer in the table to the removed node is replaced with
+ * either a pointer to the next node or a %NULL pointer as
+ * appropriate, the pointer at the end of @node_ptr_ptr will never be
+ * modified at all. Stay tuned. :)
+ */
+static void
+fluid_hashtable_remove_node (fluid_hashtable_t *hashtable,
+ fluid_hashnode_t ***node_ptr_ptr, int notify)
+{
+ fluid_hashnode_t **node_ptr, *node;
+
+ node_ptr = *node_ptr_ptr;
+ node = *node_ptr;
+
+ *node_ptr = node->next;
+
+ if (notify && hashtable->key_destroy_func)
+ hashtable->key_destroy_func (node->key);
+
+ if (notify && hashtable->value_destroy_func)
+ hashtable->value_destroy_func (node->value);
+
+ FLUID_FREE (node);
+
+ hashtable->nnodes--;
+}
+
+/*
+ * fluid_hashtable_remove_all_nodes:
+ * @hashtable: our #fluid_hashtable_t
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ *
+ * Removes all nodes from the table. Since this may be a precursor to
+ * freeing the table entirely, no resize is performed.
+ *
+ * If @notify is %TRUE then the destroy notify functions are called
+ * for the key and value of the hash node.
+ */
+static void
+fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify)
+{
+ fluid_hashnode_t **node_ptr;
+ int i;
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;)
+ fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
+
+ hashtable->nnodes = 0;
+}
+
+/*
+ * fluid_hashtable_resize:
+ * @hashtable: our #fluid_hashtable_t
+ *
+ * Resizes the hash table to the optimal size based on the number of
+ * nodes currently held. If you call this function then a resize will
+ * occur, even if one does not need to occur. Use
+ * fluid_hashtable_maybe_resize() instead.
+ */
+static void
+fluid_hashtable_resize (fluid_hashtable_t *hashtable)
+{
+ fluid_hashnode_t **new_nodes;
+ fluid_hashnode_t *node;
+ fluid_hashnode_t *next;
+ unsigned int hash_val;
+ int new_size;
+ int i;
+
+ new_size = spaced_primes_closest (hashtable->nnodes);
+ new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE :
+ ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size);
+
+ new_nodes = FLUID_ARRAY (fluid_hashnode_t *, new_size);
+
+ if (!new_nodes)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return;
+ }
+
+ FLUID_MEMSET (new_nodes, 0, new_size * sizeof (fluid_hashnode_t *));
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = next)
+ {
+ next = node->next;
+
+ hash_val = node->key_hash % new_size;
+
+ node->next = new_nodes[hash_val];
+ new_nodes[hash_val] = node;
+ }
+
+ FLUID_FREE (hashtable->nodes);
+ hashtable->nodes = new_nodes;
+ hashtable->size = new_size;
+}
+
+/*
+ * fluid_hashtable_maybe_resize:
+ * @hashtable: our #fluid_hashtable_t
+ *
+ * Resizes the hash table, if needed.
+ *
+ * Essentially, calls fluid_hashtable_resize() if the table has strayed
+ * too far from its ideal size for its number of nodes.
+ */
+static inline void
+fluid_hashtable_maybe_resize (fluid_hashtable_t *hashtable)
+{
+ int nnodes = hashtable->nnodes;
+ int size = hashtable->size;
+
+ if ((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) ||
+ (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE))
+ fluid_hashtable_resize (hashtable);
+}
+
+/**
+ * new_fluid_hashtable:
+ * @hash_func: a function to create a hash value from a key.
+ * Hash values are used to determine where keys are stored within the
+ * #fluid_hashtable_t data structure. The fluid_direct_hash(), fluid_int_hash() and
+ * fluid_str_hash() functions are provided for some common types of keys.
+ * If hash_func is %NULL, fluid_direct_hash() is used.
+ * @key_equal_func: a function to check two keys for equality. This is
+ * used when looking up keys in the #fluid_hashtable_t. The fluid_direct_equal(),
+ * fluid_int_equal() and fluid_str_equal() functions are provided for the most
+ * common types of keys. If @key_equal_func is %NULL, keys are compared
+ * directly in a similar fashion to fluid_direct_equal(), but without the
+ * overhead of a function call.
+ *
+ * Creates a new #fluid_hashtable_t with a reference count of 1.
+ *
+ * Return value: a new #fluid_hashtable_t.
+ **/
+fluid_hashtable_t*
+new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func)
+{
+ return new_fluid_hashtable_full (hash_func, key_equal_func, NULL, NULL);
+}
+
+
+/**
+ * new_fluid_hashtable_full:
+ * @hash_func: a function to create a hash value from a key.
+ * @key_equal_func: a function to check two keys for equality.
+ * @key_destroy_func: a function to free the memory allocated for the key
+ * used when removing the entry from the #fluid_hashtable_t or %NULL if you
+ * don't want to supply such a function.
+ * @value_destroy_func: a function to free the memory allocated for the
+ * value used when removing the entry from the #fluid_hashtable_t or %NULL if
+ * you don't want to supply such a function.
+ *
+ * Creates a new #fluid_hashtable_t like fluid_hashtable_new() with a reference count
+ * of 1 and allows to specify functions to free the memory allocated for the
+ * key and value that get called when removing the entry from the #fluid_hashtable_t.
+ *
+ * Return value: a new #fluid_hashtable_t.
+ **/
+fluid_hashtable_t*
+new_fluid_hashtable_full (fluid_hash_func_t hash_func,
+ fluid_equal_func_t key_equal_func,
+ fluid_destroy_notify_t key_destroy_func,
+ fluid_destroy_notify_t value_destroy_func)
+{
+ fluid_hashtable_t *hashtable;
+
+ hashtable = FLUID_NEW (fluid_hashtable_t);
+
+ if (!hashtable)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ hashtable->size = HASH_TABLE_MIN_SIZE;
+ hashtable->nnodes = 0;
+ hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash;
+ hashtable->key_equal_func = key_equal_func;
+ hashtable->ref_count = 1;
+ hashtable->key_destroy_func = key_destroy_func;
+ hashtable->value_destroy_func = value_destroy_func;
+ hashtable->nodes = FLUID_ARRAY (fluid_hashnode_t*, hashtable->size);
+ FLUID_MEMSET (hashtable->nodes, 0, hashtable->size * sizeof (fluid_hashnode_t *));
+
+ return hashtable;
+}
+
+/**
+ * fluid_hashtable_iter_init:
+ * @iter: an uninitialized #fluid_hashtable_iter_t.
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Initializes a key/value pair iterator and associates it with
+ * @hashtable. Modifying the hash table after calling this function
+ * invalidates the returned iterator.
+ * |[
+ * fluid_hashtable_iter_t iter;
+ * gpointer key, value;
+ *
+ * fluid_hashtable_iter_init (&iter, hashtable);
+ * while (fluid_hashtable_iter_next (&iter, &key, &value))
+ * {
+ * /&ast; do something with key and value &ast;/
+ * }
+ * ]|
+ *
+ * Since: 2.16
+ **/
+void
+fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter,
+ fluid_hashtable_t *hashtable)
+{
+ RealIter *ri = (RealIter *) iter;
+
+ fluid_return_if_fail (iter != NULL);
+ fluid_return_if_fail (hashtable != NULL);
+
+ ri->hashtable = hashtable;
+ ri->prev_node = NULL;
+ ri->node = NULL;
+ ri->position = -1;
+ ri->pre_advanced = FALSE;
+}
+
+/**
+ * fluid_hashtable_iter_next:
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ * @key: a location to store the key, or %NULL.
+ * @value: a location to store the value, or %NULL.
+ *
+ * Advances @iter and retrieves the key and/or value that are now
+ * pointed to as a result of this advancement. If %FALSE is returned,
+ * @key and @value are not set, and the iterator becomes invalid.
+ *
+ * Return value: %FALSE if the end of the #fluid_hashtable_t has been reached.
+ *
+ * Since: 2.16
+ **/
+int
+fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key,
+ void **value)
+{
+ RealIter *ri = (RealIter *) iter;
+
+ fluid_return_val_if_fail (iter != NULL, FALSE);
+
+ if (ri->pre_advanced)
+ {
+ ri->pre_advanced = FALSE;
+
+ if (ri->node == NULL)
+ return FALSE;
+ }
+ else
+ {
+ if (ri->node != NULL)
+ {
+ ri->prev_node = ri->node;
+ ri->node = ri->node->next;
+ }
+
+ while (ri->node == NULL)
+ {
+ ri->position++;
+ if (ri->position >= ri->hashtable->size)
+ return FALSE;
+
+ ri->prev_node = NULL;
+ ri->node = ri->hashtable->nodes[ri->position];
+ }
+ }
+
+ if (key != NULL)
+ *key = ri->node->key;
+ if (value != NULL)
+ *value = ri->node->value;
+
+ return TRUE;
+}
+
+/**
+ * fluid_hashtable_iter_get_hash_table:
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ *
+ * Returns the #fluid_hashtable_t associated with @iter.
+ *
+ * Return value: the #fluid_hashtable_t associated with @iter.
+ *
+ * Since: 2.16
+ **/
+fluid_hashtable_t *
+fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter)
+{
+ fluid_return_val_if_fail (iter != NULL, NULL);
+
+ return ((RealIter *) iter)->hashtable;
+}
+
+static void
+iter_remove_or_steal (RealIter *ri, int notify)
+{
+ fluid_hashnode_t *prev;
+ fluid_hashnode_t *node;
+ int position;
+
+ fluid_return_if_fail (ri != NULL);
+ fluid_return_if_fail (ri->node != NULL);
+
+ prev = ri->prev_node;
+ node = ri->node;
+ position = ri->position;
+
+ /* pre-advance the iterator since we will remove the node */
+
+ ri->node = ri->node->next;
+ /* ri->prev_node is still the correct previous node */
+
+ while (ri->node == NULL)
+ {
+ ri->position++;
+ if (ri->position >= ri->hashtable->size)
+ break;
+
+ ri->prev_node = NULL;
+ ri->node = ri->hashtable->nodes[ri->position];
+ }
+
+ ri->pre_advanced = TRUE;
+
+ /* remove the node */
+
+ if (prev != NULL)
+ prev->next = node->next;
+ else
+ ri->hashtable->nodes[position] = node->next;
+
+ if (notify)
+ {
+ if (ri->hashtable->key_destroy_func)
+ ri->hashtable->key_destroy_func(node->key);
+ if (ri->hashtable->value_destroy_func)
+ ri->hashtable->value_destroy_func(node->value);
+ }
+
+ FLUID_FREE (node);
+
+ ri->hashtable->nnodes--;
+}
+
+/**
+ * fluid_hashtable_iter_remove():
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ *
+ * Removes the key/value pair currently pointed to by the iterator
+ * from its associated #fluid_hashtable_t. Can only be called after
+ * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more
+ * than once for the same key/value pair.
+ *
+ * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the
+ * key and value are freed using the supplied destroy functions, otherwise
+ * you have to make sure that any dynamically allocated values are freed
+ * yourself.
+ *
+ * Since: 2.16
+ **/
+void
+fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter)
+{
+ iter_remove_or_steal ((RealIter *) iter, TRUE);
+}
+
+/**
+ * fluid_hashtable_iter_steal():
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ *
+ * Removes the key/value pair currently pointed to by the iterator
+ * from its associated #fluid_hashtable_t, without calling the key and value
+ * destroy functions. Can only be called after
+ * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more
+ * than once for the same key/value pair.
+ *
+ * Since: 2.16
+ **/
+void
+fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter)
+{
+ iter_remove_or_steal ((RealIter *) iter, FALSE);
+}
+
+
+/**
+ * fluid_hashtable_ref:
+ * @hashtable: a valid #fluid_hashtable_t.
+ *
+ * Atomically increments the reference count of @hashtable by one.
+ * This function is MT-safe and may be called from any thread.
+ *
+ * Return value: the passed in #fluid_hashtable_t.
+ *
+ * Since: 2.10
+ **/
+fluid_hashtable_t*
+fluid_hashtable_ref (fluid_hashtable_t *hashtable)
+{
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+ fluid_return_val_if_fail (hashtable->ref_count > 0, hashtable);
+
+ fluid_atomic_int_add (&hashtable->ref_count, 1);
+ return hashtable;
+}
+
+/**
+ * fluid_hashtable_unref:
+ * @hashtable: a valid #fluid_hashtable_t.
+ *
+ * Atomically decrements the reference count of @hashtable by one.
+ * If the reference count drops to 0, all keys and values will be
+ * destroyed, and all memory allocated by the hash table is released.
+ * This function is MT-safe and may be called from any thread.
+ *
+ * Since: 2.10
+ **/
+void
+fluid_hashtable_unref (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (hashtable->ref_count > 0);
+
+ if (fluid_atomic_int_exchange_and_add (&hashtable->ref_count, -1) - 1 == 0)
+ {
+ fluid_hashtable_remove_all_nodes (hashtable, TRUE);
+ FLUID_FREE (hashtable->nodes);
+ FLUID_FREE (hashtable);
+ }
+}
+
+/**
+ * delete_fluid_hashtable:
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Destroys all keys and values in the #fluid_hashtable_t and decrements its
+ * reference count by 1. If keys and/or values are dynamically allocated,
+ * you should either free them first or create the #fluid_hashtable_t with destroy
+ * notifiers using fluid_hashtable_new_full(). In the latter case the destroy
+ * functions you supplied will be called on all keys and values during the
+ * destruction phase.
+ **/
+void
+delete_fluid_hashtable (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (hashtable->ref_count > 0);
+
+ fluid_hashtable_remove_all (hashtable);
+ fluid_hashtable_unref (hashtable);
+}
+
+/**
+ * fluid_hashtable_lookup:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: the key to look up.
+ *
+ * Looks up a key in a #fluid_hashtable_t. Note that this function cannot
+ * distinguish between a key that is not present and one which is present
+ * and has the value %NULL. If you need this distinction, use
+ * fluid_hashtable_lookup_extended().
+ *
+ * Return value: the associated value, or %NULL if the key is not found.
+ **/
+void *
+fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key)
+{
+ fluid_hashnode_t *node;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+
+ node = *fluid_hashtable_lookup_node (hashtable, key, NULL);
+
+ return node ? node->value : NULL;
+}
+
+/**
+ * fluid_hashtable_lookup_extended:
+ * @hashtable: a #fluid_hashtable_t.
+ * @lookup_key: the key to look up.
+ * @orig_key: returns the original key.
+ * @value: returns the value associated with the key.
+ *
+ * Looks up a key in the #fluid_hashtable_t, returning the original key and the
+ * associated value and a #gboolean which is %TRUE if the key was found. This
+ * is useful if you need to free the memory allocated for the original key,
+ * for example before calling fluid_hashtable_remove().
+ *
+ * Return value: %TRUE if the key was found in the #fluid_hashtable_t.
+ **/
+int
+fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable,
+ const void *lookup_key,
+ void **orig_key, void **value)
+{
+ fluid_hashnode_t *node;
+
+ fluid_return_val_if_fail (hashtable != NULL, FALSE);
+
+ node = *fluid_hashtable_lookup_node (hashtable, lookup_key, NULL);
+
+ if (node == NULL)
+ return FALSE;
+
+ if (orig_key)
+ *orig_key = node->key;
+
+ if (value)
+ *value = node->value;
+
+ return TRUE;
+}
+
+/*
+ * fluid_hashtable_insert_internal:
+ * @hashtable: our #fluid_hashtable_t
+ * @key: the key to insert
+ * @value: the value to insert
+ * @keep_new_key: if %TRUE and this key already exists in the table
+ * then call the destroy notify function on the old key. If %FALSE
+ * then call the destroy notify function on the new key.
+ *
+ * Implements the common logic for the fluid_hashtable_insert() and
+ * fluid_hashtable_replace() functions.
+ *
+ * Do a lookup of @key. If it is found, replace it with the new
+ * @value (and perhaps the new @key). If it is not found, create a
+ * new node.
+ */
+static void
+fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key,
+ void *value, int keep_new_key)
+{
+ fluid_hashnode_t **node_ptr, *node;
+ unsigned int key_hash;
+
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (hashtable->ref_count > 0);
+
+ node_ptr = fluid_hashtable_lookup_node (hashtable, key, &key_hash);
+
+ if ((node = *node_ptr))
+ {
+ if (keep_new_key)
+ {
+ if (hashtable->key_destroy_func)
+ hashtable->key_destroy_func (node->key);
+ node->key = key;
+ }
+ else
+ {
+ if (hashtable->key_destroy_func)
+ hashtable->key_destroy_func (key);
+ }
+
+ if (hashtable->value_destroy_func)
+ hashtable->value_destroy_func (node->value);
+
+ node->value = value;
+ }
+ else
+ {
+ node = FLUID_NEW (fluid_hashnode_t);
+
+ if (!node)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return;
+ }
+
+ node->key = key;
+ node->value = value;
+ node->key_hash = key_hash;
+ node->next = NULL;
+
+ *node_ptr = node;
+ hashtable->nnodes++;
+ fluid_hashtable_maybe_resize (hashtable);
+ }
+}
+
+/**
+ * fluid_hashtable_insert:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: a key to insert.
+ * @value: the value to associate with the key.
+ *
+ * Inserts a new key and value into a #fluid_hashtable_t.
+ *
+ * If the key already exists in the #fluid_hashtable_t its current value is replaced
+ * with the new value. If you supplied a @value_destroy_func when creating the
+ * #fluid_hashtable_t, the old value is freed using that function. If you supplied
+ * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed
+ * using that function.
+ **/
+void
+fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value)
+{
+ fluid_hashtable_insert_internal (hashtable, key, value, FALSE);
+}
+
+/**
+ * fluid_hashtable_replace:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: a key to insert.
+ * @value: the value to associate with the key.
+ *
+ * Inserts a new key and value into a #fluid_hashtable_t similar to
+ * fluid_hashtable_insert(). The difference is that if the key already exists
+ * in the #fluid_hashtable_t, it gets replaced by the new key. If you supplied a
+ * @value_destroy_func when creating the #fluid_hashtable_t, the old value is freed
+ * using that function. If you supplied a @key_destroy_func when creating the
+ * #fluid_hashtable_t, the old key is freed using that function.
+ **/
+void
+fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value)
+{
+ fluid_hashtable_insert_internal (hashtable, key, value, TRUE);
+}
+
+/*
+ * fluid_hashtable_remove_internal:
+ * @hashtable: our #fluid_hashtable_t
+ * @key: the key to remove
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ * Return value: %TRUE if a node was found and removed, else %FALSE
+ *
+ * Implements the common logic for the fluid_hashtable_remove() and
+ * fluid_hashtable_steal() functions.
+ *
+ * Do a lookup of @key and remove it if it is found, calling the
+ * destroy notify handlers only if @notify is %TRUE.
+ */
+static int
+fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key,
+ int notify)
+{
+ fluid_hashnode_t **node_ptr;
+
+ fluid_return_val_if_fail (hashtable != NULL, FALSE);
+
+ node_ptr = fluid_hashtable_lookup_node (hashtable, key, NULL);
+ if (*node_ptr == NULL)
+ return FALSE;
+
+ fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
+ fluid_hashtable_maybe_resize (hashtable);
+
+ return TRUE;
+}
+
+/**
+ * fluid_hashtable_remove:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: the key to remove.
+ *
+ * Removes a key and its associated value from a #fluid_hashtable_t.
+ *
+ * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the
+ * key and value are freed using the supplied destroy functions, otherwise
+ * you have to make sure that any dynamically allocated values are freed
+ * yourself.
+ *
+ * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.
+ **/
+int
+fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key)
+{
+ return fluid_hashtable_remove_internal (hashtable, key, TRUE);
+}
+
+/**
+ * fluid_hashtable_steal:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: the key to remove.
+ *
+ * Removes a key and its associated value from a #fluid_hashtable_t without
+ * calling the key and value destroy functions.
+ *
+ * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.
+ **/
+int
+fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key)
+{
+ return fluid_hashtable_remove_internal (hashtable, key, FALSE);
+}
+
+/**
+ * fluid_hashtable_remove_all:
+ * @hashtable: a #fluid_hashtable_t
+ *
+ * Removes all keys and their associated values from a #fluid_hashtable_t.
+ *
+ * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the keys
+ * and values are freed using the supplied destroy functions, otherwise you
+ * have to make sure that any dynamically allocated values are freed
+ * yourself.
+ *
+ * Since: 2.12
+ **/
+void
+fluid_hashtable_remove_all (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+
+ fluid_hashtable_remove_all_nodes (hashtable, TRUE);
+ fluid_hashtable_maybe_resize (hashtable);
+}
+
+/**
+ * fluid_hashtable_steal_all:
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Removes all keys and their associated values from a #fluid_hashtable_t
+ * without calling the key and value destroy functions.
+ *
+ * Since: 2.12
+ **/
+void
+fluid_hashtable_steal_all (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+
+ fluid_hashtable_remove_all_nodes (hashtable, FALSE);
+ fluid_hashtable_maybe_resize (hashtable);
+}
+
+/*
+ * fluid_hashtable_foreach_remove_or_steal:
+ * @hashtable: our #fluid_hashtable_t
+ * @func: the user's callback function
+ * @user_data: data for @func
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ *
+ * Implements the common logic for fluid_hashtable_foreach_remove() and
+ * fluid_hashtable_foreach_steal().
+ *
+ * Iterates over every node in the table, calling @func with the key
+ * and value of the node (and @user_data). If @func returns %TRUE the
+ * node is removed from the table.
+ *
+ * If @notify is true then the destroy notify handlers will be called
+ * for each removed node.
+ */
+static unsigned int
+fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data,
+ int notify)
+{
+ fluid_hashnode_t *node, **node_ptr;
+ unsigned int deleted = 0;
+ int i;
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;)
+ if ((* func) (node->key, node->value, user_data))
+ {
+ fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
+ deleted++;
+ }
+ else
+ node_ptr = &node->next;
+
+ fluid_hashtable_maybe_resize (hashtable);
+
+ return deleted;
+}
+
+#if 0
+/**
+ * fluid_hashtable_foreach_remove:
+ * @hashtable: a #fluid_hashtable_t.
+ * @func: the function to call for each key/value pair.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for each key/value pair in the #fluid_hashtable_t.
+ * If the function returns %TRUE, then the key/value pair is removed from the
+ * #fluid_hashtable_t. If you supplied key or value destroy functions when creating
+ * the #fluid_hashtable_t, they are used to free the memory allocated for the removed
+ * keys and values.
+ *
+ * See #fluid_hashtable_iter_t for an alternative way to loop over the
+ * key/value pairs in the hash table.
+ *
+ * Return value: the number of key/value pairs removed.
+ **/
+static unsigned int
+fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data)
+{
+ fluid_return_val_if_fail (hashtable != NULL, 0);
+ fluid_return_val_if_fail (func != NULL, 0);
+
+ return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, TRUE);
+}
+#endif
+
+/**
+ * fluid_hashtable_foreach_steal:
+ * @hashtable: a #fluid_hashtable_t.
+ * @func: the function to call for each key/value pair.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for each key/value pair in the #fluid_hashtable_t.
+ * If the function returns %TRUE, then the key/value pair is removed from the
+ * #fluid_hashtable_t, but no key or value destroy functions are called.
+ *
+ * See #fluid_hashtable_iter_t for an alternative way to loop over the
+ * key/value pairs in the hash table.
+ *
+ * Return value: the number of key/value pairs removed.
+ **/
+unsigned int
+fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data)
+{
+ fluid_return_val_if_fail (hashtable != NULL, 0);
+ fluid_return_val_if_fail (func != NULL, 0);
+
+ return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, FALSE);
+}
+
+/**
+ * fluid_hashtable_foreach:
+ * @hashtable: a #fluid_hashtable_t.
+ * @func: the function to call for each key/value pair.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for each of the key/value pairs in the
+ * #fluid_hashtable_t. The function is passed the key and value of each
+ * pair, and the given @user_data parameter. The hash table may not
+ * be modified while iterating over it (you can't add/remove
+ * items). To remove all items matching a predicate, use
+ * fluid_hashtable_foreach_remove().
+ *
+ * See fluid_hashtable_find() for performance caveats for linear
+ * order searches in contrast to fluid_hashtable_lookup().
+ **/
+void
+fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func,
+ void *user_data)
+{
+ fluid_hashnode_t *node;
+ int i;
+
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (func != NULL);
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ (* func) (node->key, node->value, user_data);
+}
+
+/**
+ * fluid_hashtable_find:
+ * @hashtable: a #fluid_hashtable_t.
+ * @predicate: function to test the key/value pairs for a certain property.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for key/value pairs in the #fluid_hashtable_t until
+ * @predicate returns %TRUE. The function is passed the key and value of
+ * each pair, and the given @user_data parameter. The hash table may not
+ * be modified while iterating over it (you can't add/remove items).
+ *
+ * Note, that hash tables are really only optimized for forward lookups,
+ * i.e. fluid_hashtable_lookup().
+ * So code that frequently issues fluid_hashtable_find() or
+ * fluid_hashtable_foreach() (e.g. in the order of once per every entry in a
+ * hash table) should probably be reworked to use additional or different
+ * data structures for reverse lookups (keep in mind that an O(n) find/foreach
+ * operation issued for all n values in a hash table ends up needing O(n*n)
+ * operations).
+ *
+ * Return value: The value of the first key/value pair is returned, for which
+ * func evaluates to %TRUE. If no pair with the requested property is found,
+ * %NULL is returned.
+ *
+ * Since: 2.4
+ **/
+void *
+fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,
+ void *user_data)
+{
+ fluid_hashnode_t *node;
+ int i;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+ fluid_return_val_if_fail (predicate != NULL, NULL);
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ if (predicate (node->key, node->value, user_data))
+ return node->value;
+ return NULL;
+}
+
+/**
+ * fluid_hashtable_size:
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Returns the number of elements contained in the #fluid_hashtable_t.
+ *
+ * Return value: the number of key/value pairs in the #fluid_hashtable_t.
+ **/
+unsigned int
+fluid_hashtable_size (fluid_hashtable_t *hashtable)
+{
+ fluid_return_val_if_fail (hashtable != NULL, 0);
+
+ return hashtable->nnodes;
+}
+
+/**
+ * fluid_hashtable_get_keys:
+ * @hashtable: a #fluid_hashtable_t
+ *
+ * Retrieves every key inside @hashtable. The returned data is valid
+ * until @hashtable is modified.
+ *
+ * Return value: a #GList containing all the keys inside the hash
+ * table. The content of the list is owned by the hash table and
+ * should not be modified or freed. Use delete_fluid_list() when done
+ * using the list.
+ *
+ * Since: 2.14
+ */
+fluid_list_t *
+fluid_hashtable_get_keys (fluid_hashtable_t *hashtable)
+{
+ fluid_hashnode_t *node;
+ int i;
+ fluid_list_t *retval;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+
+ retval = NULL;
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ retval = fluid_list_prepend (retval, node->key);
+
+ return retval;
+}
+
+/**
+ * fluid_hashtable_get_values:
+ * @hashtable: a #fluid_hashtable_t
+ *
+ * Retrieves every value inside @hashtable. The returned data is
+ * valid until @hashtable is modified.
+ *
+ * Return value: a #GList containing all the values inside the hash
+ * table. The content of the list is owned by the hash table and
+ * should not be modified or freed. Use delete_fluid_list() when done
+ * using the list.
+ *
+ * Since: 2.14
+ */
+fluid_list_t *
+fluid_hashtable_get_values (fluid_hashtable_t *hashtable)
+{
+ fluid_hashnode_t *node;
+ int i;
+ fluid_list_t *retval;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+
+ retval = NULL;
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ retval = fluid_list_prepend (retval, node->value);
+
+ return retval;
+}
+
+
+/* Extracted from glib/gstring.c */
+
+
+/**
+ * fluid_str_equal:
+ * @v1: a key
+ * @v2: a key to compare with @v1
+ *
+ * Compares two strings for byte-by-byte equality and returns %TRUE
+ * if they are equal. It can be passed to new_fluid_hashtable() as the
+ * @key_equal_func parameter, when using strings as keys in a #Ghashtable.
+ *
+ * Returns: %TRUE if the two keys match
+ */
+int
+fluid_str_equal (const void *v1, const void *v2)
+{
+ const char *string1 = v1;
+ const char *string2 = v2;
+
+ return strcmp (string1, string2) == 0;
+}
+
+/**
+ * fluid_str_hash:
+ * @v: a string key
+ *
+ * Converts a string to a hash value.
+ * It can be passed to new_fluid_hashtable() as the @hash_func
+ * parameter, when using strings as keys in a #fluid_hashtable_t.
+ *
+ * Returns: a hash value corresponding to the key
+ */
+unsigned int
+fluid_str_hash (const void *v)
+{
+ /* 31 bit hash function */
+ const signed char *p = v;
+ uint32 h = *p;
+
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+
+/* Extracted from glib/gutils.c */
+
+
+/**
+ * fluid_direct_equal:
+ * @v1: a key.
+ * @v2: a key to compare with @v1.
+ *
+ * Compares two #gpointer arguments and returns %TRUE if they are equal.
+ * It can be passed to new_fluid_hashtable() as the @key_equal_func
+ * parameter, when using pointers as keys in a #fluid_hashtable_t.
+ *
+ * Returns: %TRUE if the two keys match.
+ */
+int
+fluid_direct_equal (const void *v1, const void *v2)
+{
+ return v1 == v2;
+}
+
+/**
+ * fluid_direct_hash:
+ * @v: a void * key
+ *
+ * Converts a gpointer to a hash value.
+ * It can be passed to g_hashtable_new() as the @hash_func parameter,
+ * when using pointers as keys in a #fluid_hashtable_t.
+ *
+ * Returns: a hash value corresponding to the key.
+ */
+unsigned int
+fluid_direct_hash (const void *v)
+{
+ return FLUID_POINTER_TO_UINT (v);
+}
+
+/**
+ * fluid_int_equal:
+ * @v1: a pointer to a int key.
+ * @v2: a pointer to a int key to compare with @v1.
+ *
+ * Compares the two #gint values being pointed to and returns
+ * %TRUE if they are equal.
+ * It can be passed to g_hashtable_new() as the @key_equal_func
+ * parameter, when using pointers to integers as keys in a #fluid_hashtable_t.
+ *
+ * Returns: %TRUE if the two keys match.
+ */
+int
+fluid_int_equal (const void *v1, const void *v2)
+{
+ return *((const int*) v1) == *((const int*) v2);
+}
+
+/**
+ * fluid_int_hash:
+ * @v: a pointer to a int key
+ *
+ * Converts a pointer to a #gint to a hash value.
+ * It can be passed to g_hashtable_new() as the @hash_func parameter,
+ * when using pointers to integers values as keys in a #fluid_hashtable_t.
+ *
+ * Returns: a hash value corresponding to the key.
+ */
+unsigned int
+fluid_int_hash (const void *v)
+{
+ return *(const int*) v;
+}
diff --git a/libs/fluidsynth/src/fluid_hash.h b/libs/fluidsynth/src/fluid_hash.h
new file mode 100644
index 0000000000..3beff0607e
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_hash.h
@@ -0,0 +1,131 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * Adapted for FluidSynth use by Josh Green <jgreen@users.sourceforge.net>
+ * September 8, 2009 from glib 2.18.4
+ *
+ * - Self contained (no dependencies on glib)
+ * - changed names to fluid_hashtable_...
+ */
+
+#ifndef _FLUID_HASH_H
+#define _FLUID_HASH_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_list.h"
+#include "fluid_sys.h"
+
+/* Extracted from gtypes.h */
+typedef void (*fluid_destroy_notify_t)(void *data);
+typedef unsigned int (*fluid_hash_func_t)(const void *key);
+typedef int (*fluid_equal_func_t)(const void *a, const void *b);
+/* End gtypes.h extraction */
+
+typedef int (*fluid_hr_func_t)(void *key, void *value, void *user_data);
+typedef struct _fluid_hashtable_iter_t fluid_hashtable_iter_t;
+
+typedef struct _fluid_hashnode_t fluid_hashnode_t;
+
+struct _fluid_hashnode_t
+{
+ void *key;
+ void *value;
+ fluid_hashnode_t *next;
+ unsigned int key_hash;
+};
+
+struct _fluid_hashtable_t
+{
+ int size;
+ int nnodes;
+ fluid_hashnode_t **nodes;
+ fluid_hash_func_t hash_func;
+ fluid_equal_func_t key_equal_func;
+ volatile int ref_count;
+ fluid_destroy_notify_t key_destroy_func;
+ fluid_destroy_notify_t value_destroy_func;
+ fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example)
+};
+
+struct _fluid_hashtable_iter_t
+{
+ /*< private >*/
+ void * dummy1;
+ void * dummy2;
+ void * dummy3;
+ int dummy4;
+ int dummy5; // Bool
+ void * dummy6;
+};
+
+fluid_hashtable_t* new_fluid_hashtable (fluid_hash_func_t hash_func,
+ fluid_equal_func_t key_equal_func);
+fluid_hashtable_t* new_fluid_hashtable_full (fluid_hash_func_t hash_func,
+ fluid_equal_func_t key_equal_func,
+ fluid_destroy_notify_t key_destroy_func,
+ fluid_destroy_notify_t value_destroy_func);
+void delete_fluid_hashtable(fluid_hashtable_t *hashtable);
+
+void fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable);
+int fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, void **value);
+fluid_hashtable_t *fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter);
+void fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter);
+void fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter);
+
+fluid_hashtable_t* fluid_hashtable_ref (fluid_hashtable_t *hashtable);
+void fluid_hashtable_unref (fluid_hashtable_t *hashtable);
+
+void *fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key);
+int fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, const void *lookup_key,
+ void **orig_key, void **value);
+
+void fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value);
+void fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value);
+
+int fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key);
+int fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key);
+void fluid_hashtable_remove_all (fluid_hashtable_t *hashtable);
+void fluid_hashtable_steal_all (fluid_hashtable_t *hashtable);
+unsigned int fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data);
+void fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func,
+ void *user_data);
+void *fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,
+ void *user_data);
+unsigned int fluid_hashtable_size (fluid_hashtable_t *hashtable);
+fluid_list_t *fluid_hashtable_get_keys (fluid_hashtable_t *hashtable);
+fluid_list_t *fluid_hashtable_get_values (fluid_hashtable_t *hashtable);
+
+int fluid_str_equal (const void *v1, const void *v2);
+unsigned int fluid_str_hash (const void *v);
+int fluid_direct_equal (const void *v1, const void *v2);
+unsigned int fluid_direct_hash (const void *v);
+int fluid_int_equal (const void *v1, const void *v2);
+unsigned int fluid_int_hash (const void *v);
+
+#endif /* _FLUID_HASH_H */
+
diff --git a/libs/fluidsynth/src/fluid_iir_filter.c b/libs/fluidsynth/src/fluid_iir_filter.c
new file mode 100644
index 0000000000..5b474692ae
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_iir_filter.c
@@ -0,0 +1,301 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_iir_filter.h"
+#include "fluid_sys.h"
+#include "fluid_conv.h"
+
+/**
+ * Applies a lowpass filter with variable cutoff frequency and quality factor.
+ * Also modifies filter state accordingly.
+ * @param iir_filter Filter parameter
+ * @param dsp_buf Pointer to the synthesized audio data
+ * @param count Count of samples in dsp_buf
+ */
+/*
+ * Variable description:
+ * - dsp_a1, dsp_a2, dsp_b0, dsp_b1, dsp_b2: Filter coefficients
+ *
+ * A couple of variables are used internally, their results are discarded:
+ * - dsp_i: Index through the output buffer
+ * - dsp_phase_fractional: The fractional part of dsp_phase
+ * - dsp_coeff: A table of four coefficients, depending on the fractional phase.
+ * Used to interpolate between samples.
+ * - dsp_process_buffer: Holds the processed signal between stages
+ * - dsp_centernode: delay line for the IIR filter
+ * - dsp_hist1: same
+ * - dsp_hist2: same
+ */
+void
+fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter,
+ fluid_real_t *dsp_buf, int count)
+{
+ /* IIR filter sample history */
+ fluid_real_t dsp_hist1 = iir_filter->hist1;
+ fluid_real_t dsp_hist2 = iir_filter->hist2;
+
+ /* IIR filter coefficients */
+ fluid_real_t dsp_a1 = iir_filter->a1;
+ fluid_real_t dsp_a2 = iir_filter->a2;
+ fluid_real_t dsp_b02 = iir_filter->b02;
+ fluid_real_t dsp_b1 = iir_filter->b1;
+ int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count;
+
+ fluid_real_t dsp_centernode;
+ int dsp_i;
+
+ /* filter (implement the voice filter according to SoundFont standard) */
+
+ /* Check for denormal number (too close to zero). */
+ if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */
+
+ /* Two versions of the filter loop. One, while the filter is
+ * changing towards its new setting. The other, if the filter
+ * doesn't change.
+ */
+
+ if (dsp_filter_coeff_incr_count > 0)
+ {
+ fluid_real_t dsp_a1_incr = iir_filter->a1_incr;
+ fluid_real_t dsp_a2_incr = iir_filter->a2_incr;
+ fluid_real_t dsp_b02_incr = iir_filter->b02_incr;
+ fluid_real_t dsp_b1_incr = iir_filter->b1_incr;
+
+
+ /* Increment is added to each filter coefficient filter_coeff_incr_count times. */
+ for (dsp_i = 0; dsp_i < count; dsp_i++)
+ {
+ /* The filter is implemented in Direct-II form. */
+ dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
+ dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
+ dsp_hist2 = dsp_hist1;
+ dsp_hist1 = dsp_centernode;
+
+ if (dsp_filter_coeff_incr_count-- > 0)
+ {
+ fluid_real_t old_b02 = dsp_b02;
+ dsp_a1 += dsp_a1_incr;
+ dsp_a2 += dsp_a2_incr;
+ dsp_b02 += dsp_b02_incr;
+ dsp_b1 += dsp_b1_incr;
+
+ /* Compensate history to avoid the filter going havoc with large frequency changes */
+ if (iir_filter->compensate_incr && fabs(dsp_b02) > 0.001) {
+ fluid_real_t compensate = old_b02 / dsp_b02;
+ dsp_centernode *= compensate;
+ dsp_hist1 *= compensate;
+ dsp_hist2 *= compensate;
+ }
+ }
+ } /* for dsp_i */
+ }
+ else /* The filter parameters are constant. This is duplicated to save time. */
+ {
+ for (dsp_i = 0; dsp_i < count; dsp_i++)
+ { /* The filter is implemented in Direct-II form. */
+ dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
+ dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
+ dsp_hist2 = dsp_hist1;
+ dsp_hist1 = dsp_centernode;
+ }
+ }
+
+ iir_filter->hist1 = dsp_hist1;
+ iir_filter->hist2 = dsp_hist2;
+ iir_filter->a1 = dsp_a1;
+ iir_filter->a2 = dsp_a2;
+ iir_filter->b02 = dsp_b02;
+ iir_filter->b1 = dsp_b1;
+ iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count;
+
+ fluid_check_fpe ("voice_filter");
+}
+
+
+void
+fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter)
+{
+ iir_filter->hist1 = 0;
+ iir_filter->hist2 = 0;
+ iir_filter->last_fres = -1.;
+ iir_filter->filter_startup = 1;
+}
+
+void
+fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter,
+ fluid_real_t fres)
+{
+ iir_filter->fres = fres;
+ iir_filter->last_fres = -1.;
+}
+
+
+void
+fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter,
+ fluid_real_t q_dB)
+{
+ /* The 'sound font' Q is defined in dB. The filter needs a linear
+ q. Convert. */
+ iir_filter->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f));
+
+ /* SF 2.01 page 59:
+ *
+ * The SoundFont specs ask for a gain reduction equal to half the
+ * height of the resonance peak (Q). For example, for a 10 dB
+ * resonance peak, the gain is reduced by 5 dB. This is done by
+ * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB
+ * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc)
+ * The gain is later factored into the 'b' coefficients
+ * (numerator of the filter equation). This gain factor depends
+ * only on Q, so this is the right place to calculate it.
+ */
+ iir_filter->filter_gain = (fluid_real_t) (1.0 / sqrt(iir_filter->q_lin));
+
+ /* The synthesis loop will have to recalculate the filter coefficients. */
+ iir_filter->last_fres = -1.;
+
+}
+
+
+static inline void
+fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t* iir_filter,
+ int transition_samples,
+ fluid_real_t output_rate)
+{
+
+ /*
+ * Those equations from Robert Bristow-Johnson's `Cookbook
+ * formulae for audio EQ biquad filter coefficients', obtained
+ * from Harmony-central.com / Computer / Programming. They are
+ * the result of the bilinear transform on an analogue filter
+ * prototype. To quote, `BLT frequency warping has been taken
+ * into account for both significant frequency relocation and for
+ * bandwidth readjustment'. */
+
+ fluid_real_t omega = (fluid_real_t) (2.0 * M_PI *
+ (iir_filter->last_fres / ((float) output_rate)));
+ fluid_real_t sin_coeff = (fluid_real_t) sin(omega);
+ fluid_real_t cos_coeff = (fluid_real_t) cos(omega);
+ fluid_real_t alpha_coeff = sin_coeff / (2.0f * iir_filter->q_lin);
+ fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff);
+
+ /* Calculate the filter coefficients. All coefficients are
+ * normalized by a0. Think of `a1' as `a1/a0'.
+ *
+ * Here a couple of multiplications are saved by reusing common expressions.
+ * The original equations should be:
+ * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain;
+ * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain;
+ * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */
+
+ fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv;
+ fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv;
+ fluid_real_t b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain;
+ /* both b0 -and- b2 */
+ fluid_real_t b02_temp = b1_temp * 0.5f;
+
+ iir_filter->compensate_incr = 0;
+
+ if (iir_filter->filter_startup || (transition_samples == 0))
+ {
+ /* The filter is calculated, because the voice was started up.
+ * In this case set the filter coefficients without delay.
+ */
+ iir_filter->a1 = a1_temp;
+ iir_filter->a2 = a2_temp;
+ iir_filter->b02 = b02_temp;
+ iir_filter->b1 = b1_temp;
+ iir_filter->filter_coeff_incr_count = 0;
+ iir_filter->filter_startup = 0;
+// printf("Setting initial filter coefficients.\n");
+ }
+ else
+ {
+
+ /* The filter frequency is changed. Calculate an increment
+ * factor, so that the new setting is reached after one buffer
+ * length. x_incr is added to the current value FLUID_BUFSIZE
+ * times. The length is arbitrarily chosen. Longer than one
+ * buffer will sacrifice some performance, though. Note: If
+ * the filter is still too 'grainy', then increase this number
+ * at will.
+ */
+
+ iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples;
+ iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples;
+ iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples;
+ iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples;
+ if (fabs(iir_filter->b02) > 0.0001) {
+ fluid_real_t quota = b02_temp / iir_filter->b02;
+ iir_filter->compensate_incr = quota < 0.5 || quota > 2;
+ }
+ /* Have to add the increments filter_coeff_incr_count times. */
+ iir_filter->filter_coeff_incr_count = transition_samples;
+ }
+ fluid_check_fpe ("voice_write filter calculation");
+}
+
+
+void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter,
+ fluid_real_t output_rate,
+ fluid_real_t fres_mod)
+{
+ fluid_real_t fres;
+
+ /* calculate the frequency of the resonant filter in Hz */
+ fres = fluid_ct2hz(iir_filter->fres + fres_mod);
+
+ /* FIXME - Still potential for a click during turn on, can we interpolate
+ between 20khz cutoff and 0 Q? */
+
+ /* I removed the optimization of turning the filter off when the
+ * resonance frequence is above the maximum frequency. Instead, the
+ * filter frequency is set to a maximum of 0.45 times the sampling
+ * rate. For a 44100 kHz sampling rate, this amounts to 19845
+ * Hz. The reason is that there were problems with anti-aliasing when the
+ * synthesizer was run at lower sampling rates. Thanks to Stephan
+ * Tassart for pointing me to this bug. By turning the filter on and
+ * clipping the maximum filter frequency at 0.45*srate, the filter
+ * is used as an anti-aliasing filter. */
+
+ if (fres > 0.45f * output_rate)
+ fres = 0.45f * output_rate;
+ else if (fres < 5)
+ fres = 5;
+
+ /* if filter enabled and there is a significant frequency change.. */
+ if ((abs (fres - iir_filter->last_fres) > 0.01))
+ {
+ /* The filter coefficients have to be recalculated (filter
+ * parameters have changed). Recalculation for various reasons is
+ * forced by setting last_fres to -1. The flag filter_startup
+ * indicates, that the DSP loop runs for the first time, in this
+ * case, the filter is set directly, instead of smoothly fading
+ * between old and new settings. */
+ iir_filter->last_fres = fres;
+ fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE,
+ output_rate);
+ }
+
+
+ fluid_check_fpe ("voice_write DSP coefficients");
+
+}
+
diff --git a/libs/fluidsynth/src/fluid_iir_filter.h b/libs/fluidsynth/src/fluid_iir_filter.h
new file mode 100644
index 0000000000..7dc5de14a5
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_iir_filter.h
@@ -0,0 +1,75 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_IIR_FILTER_H
+#define _FLUID_IIR_FILTER_H
+
+#include "fluidsynth_priv.h"
+
+typedef struct _fluid_iir_filter_t fluid_iir_filter_t;
+
+
+void fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter,
+ fluid_real_t *dsp_buf, int dsp_buf_count);
+
+void fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter);
+
+void fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter,
+ fluid_real_t q_dB);
+
+void fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter,
+ fluid_real_t fres);
+
+void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter,
+ fluid_real_t output_rate,
+ fluid_real_t fres_mod);
+
+/* We can't do information hiding here, as fluid_voice_t includes the struct
+ without a pointer. */
+struct _fluid_iir_filter_t
+{
+ /* filter coefficients */
+ /* The coefficients are normalized to a0. */
+ /* b0 and b2 are identical => b02 */
+ fluid_real_t b02; /* b0 / a0 */
+ fluid_real_t b1; /* b1 / a0 */
+ fluid_real_t a1; /* a0 / a0 */
+ fluid_real_t a2; /* a1 / a0 */
+
+ fluid_real_t b02_incr;
+ fluid_real_t b1_incr;
+ fluid_real_t a1_incr;
+ fluid_real_t a2_incr;
+ int filter_coeff_incr_count;
+ int compensate_incr; /* Flag: If set, must compensate history */
+ fluid_real_t hist1, hist2; /* Sample history for the IIR filter */
+ int filter_startup; /* Flag: If set, the filter will be set directly.
+ Else it changes smoothly. */
+
+ fluid_real_t fres; /* the resonance frequency, in cents (not absolute cents) */
+ fluid_real_t last_fres; /* Current resonance frequency of the IIR filter */
+ /* Serves as a flag: A deviation between fres and last_fres */
+ /* indicates, that the filter has to be recalculated. */
+ fluid_real_t q_lin; /* the q-factor on a linear scale */
+ fluid_real_t filter_gain; /* Gain correction factor, depends on q */
+};
+
+#endif
+
diff --git a/libs/fluidsynth/src/fluid_lfo.c b/libs/fluidsynth/src/fluid_lfo.c
new file mode 100644
index 0000000000..ff178e0012
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_lfo.c
@@ -0,0 +1,13 @@
+#include "fluid_lfo.h"
+
+void
+fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment)
+{
+ lfo->increment = increment;
+}
+
+void
+fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay)
+{
+ lfo->delay = delay;
+}
diff --git a/libs/fluidsynth/src/fluid_lfo.h b/libs/fluidsynth/src/fluid_lfo.h
new file mode 100644
index 0000000000..e9440cf526
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_lfo.h
@@ -0,0 +1,72 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_LFO_H
+#define _FLUID_LFO_H
+
+#include "fluidsynth_priv.h"
+
+typedef struct _fluid_lfo_t fluid_lfo_t;
+
+struct _fluid_lfo_t {
+ fluid_real_t val; /* the current value of the LFO */
+ unsigned int delay; /* the delay of the lfo in samples */
+ fluid_real_t increment; /* the lfo frequency is converted to a per-buffer increment */
+};
+
+static inline void
+fluid_lfo_reset(fluid_lfo_t* lfo)
+{
+ lfo->val = 0.0f;
+}
+
+// These two cannot be inlined since they're used by event_dispatch
+void fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment);
+void fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay);
+
+static inline fluid_real_t
+fluid_lfo_get_val(fluid_lfo_t* lfo)
+{
+ return lfo->val;
+}
+
+static inline void
+fluid_lfo_calc(fluid_lfo_t* lfo, unsigned int cur_delay)
+{
+ if (cur_delay < lfo->delay)
+ return;
+
+ lfo->val += lfo->increment;
+
+ if (lfo->val > (fluid_real_t) 1.0)
+ {
+ lfo->increment = -lfo->increment;
+ lfo->val = (fluid_real_t) 2.0 - lfo->val;
+ }
+ else if (lfo->val < (fluid_real_t) -1.0)
+ {
+ lfo->increment = -lfo->increment;
+ lfo->val = (fluid_real_t) -2.0 - lfo->val;
+ }
+
+}
+
+#endif
+
diff --git a/libs/fluidsynth/src/fluid_list.c b/libs/fluidsynth/src/fluid_list.c
new file mode 100644
index 0000000000..dd47a39e0e
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_list.c
@@ -0,0 +1,268 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-1999. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+
+
+#include "fluid_list.h"
+
+
+fluid_list_t*
+new_fluid_list(void)
+{
+ fluid_list_t* list;
+ list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t));
+ list->data = NULL;
+ list->next = NULL;
+ return list;
+}
+
+void
+delete_fluid_list(fluid_list_t *list)
+{
+ fluid_list_t *next;
+ while (list) {
+ next = list->next;
+ FLUID_FREE(list);
+ list = next;
+ }
+}
+
+void
+delete1_fluid_list(fluid_list_t *list)
+{
+ if (list) {
+ FLUID_FREE(list);
+ }
+}
+
+fluid_list_t*
+fluid_list_append(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *last;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ if (list)
+ {
+ last = fluid_list_last(list);
+ /* g_assert (last != NULL); */
+ last->next = new_list;
+
+ return list;
+ }
+ else
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_prepend(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+ new_list->next = list;
+
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_nth(fluid_list_t *list, int n)
+{
+ while ((n-- > 0) && list) {
+ list = list->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove(fluid_list_t *list, void* data)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp->data == data) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ delete_fluid_list(tmp);
+
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp == link) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+static fluid_list_t*
+fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func)
+{
+ fluid_list_t list, *l;
+
+ l = &list;
+
+ while (l1 && l2) {
+ if (compare_func(l1->data,l2->data) < 0) {
+ l = l->next = l1;
+ l1 = l1->next;
+ } else {
+ l = l->next = l2;
+ l2 = l2->next;
+ }
+ }
+ l->next= l1 ? l1 : l2;
+
+ return list.next;
+}
+
+fluid_list_t*
+fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func)
+{
+ fluid_list_t *l1, *l2;
+
+ if (!list) {
+ return NULL;
+ }
+ if (!list->next) {
+ return list;
+ }
+
+ l1 = list;
+ l2 = list->next;
+
+ while ((l2 = l2->next) != NULL) {
+ if ((l2 = l2->next) == NULL)
+ break;
+ l1=l1->next;
+ }
+ l2 = l1->next;
+ l1->next = NULL;
+
+ return fluid_list_sort_merge(fluid_list_sort(list, compare_func),
+ fluid_list_sort(l2, compare_func),
+ compare_func);
+}
+
+
+fluid_list_t*
+fluid_list_last(fluid_list_t *list)
+{
+ if (list) {
+ while (list->next)
+ list = list->next;
+ }
+
+ return list;
+}
+
+int
+fluid_list_size(fluid_list_t *list)
+{
+ int n = 0;
+ while (list) {
+ n++;
+ list = list->next;
+ }
+ return n;
+}
+
+fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *cur;
+ fluid_list_t *prev = NULL;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ cur = list;
+ while ((n-- > 0) && cur) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ new_list->next = cur;
+
+ if (prev) {
+ prev->next = new_list;
+ return list;
+ } else {
+ return new_list;
+ }
+}
+
+/* Compare function to sort strings alphabetically,
+ * for use with fluid_list_sort(). */
+int
+fluid_list_str_compare_func (void *a, void *b)
+{
+ if (a && b) return FLUID_STRCMP ((char *)a, (char *)b);
+ if (!a && !b) return 0;
+ if (a) return -1;
+ return 1;
+}
diff --git a/libs/fluidsynth/src/fluid_list.h b/libs/fluidsynth/src/fluid_list.h
new file mode 100644
index 0000000000..bdc32918b8
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_list.h
@@ -0,0 +1,62 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _FLUID_LIST_H
+#define _FLUID_LIST_H
+
+#include "fluidsynth_priv.h"
+
+/*
+ *
+ * Lists
+ *
+ * A sound font loader has to pack the data from the .SF2 file into
+ * list structures of this type.
+ *
+ */
+
+typedef struct _fluid_list_t fluid_list_t;
+
+typedef int (*fluid_compare_func_t)(void* a, void* b);
+
+struct _fluid_list_t
+{
+ void* data;
+ fluid_list_t *next;
+};
+
+fluid_list_t* new_fluid_list(void);
+void delete_fluid_list(fluid_list_t *list);
+void delete1_fluid_list(fluid_list_t *list);
+fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func);
+fluid_list_t* fluid_list_append(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data);
+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_size(fluid_list_t *list);
+
+#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL)
+#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL)
+
+int fluid_list_str_compare_func (void *a, void *b);
+
+#endif /* _FLUID_LIST_H */
diff --git a/libs/fluidsynth/src/fluid_midi.c b/libs/fluidsynth/src/fluid_midi.c
new file mode 100644
index 0000000000..1ee3dd24cb
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_midi.c
@@ -0,0 +1,1942 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_midi.h"
+#include "fluid_sys.h"
+#include "fluid_synth.h"
+#include "fluid_settings.h"
+
+
+static int fluid_midi_event_length(unsigned char event);
+
+/* Read the entire contents of a file into memory, allocating enough memory
+ * for the file, and returning the length and the buffer.
+ * Note: This rewinds the file to the start before reading.
+ * Returns NULL if there was an error reading or allocating memory.
+ */
+static char* fluid_file_read_full(fluid_file fp, size_t* length);
+#define READ_FULL_INITIAL_BUFLEN 1024
+
+
+/***************************************************************
+ *
+ * MIDIFILE
+ */
+
+/**
+ * Return a new MIDI file handle for parsing an already-loaded MIDI file.
+ * @internal
+ * @param buffer Pointer to full contents of MIDI file (borrows the pointer).
+ * The caller must not free buffer until after the fluid_midi_file is deleted.
+ * @param length Size of the buffer in bytes.
+ * @return New MIDI file handle or NULL on error.
+ */
+fluid_midi_file *
+new_fluid_midi_file(const char* buffer, size_t length)
+{
+ fluid_midi_file *mf;
+
+ mf = FLUID_NEW(fluid_midi_file);
+ if (mf == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file));
+
+ mf->c = -1;
+ mf->running_status = -1;
+
+ mf->buffer = buffer;
+ mf->buf_len = length;
+ mf->buf_pos = 0;
+ mf->eof = FALSE;
+
+ if (fluid_midi_file_read_mthd(mf) != FLUID_OK) {
+ FLUID_FREE(mf);
+ return NULL;
+ }
+ return mf;
+}
+
+static char*
+fluid_file_read_full(fluid_file fp, size_t* length)
+{
+ size_t buflen;
+ char* buffer;
+ size_t n;
+ /* Work out the length of the file in advance */
+ if (FLUID_FSEEK(fp, 0, SEEK_END) != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");
+ return NULL;
+ }
+ buflen = ftell(fp);
+ if (FLUID_FSEEK(fp, 0, SEEK_SET) != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");
+ return NULL;
+ }
+ FLUID_LOG(FLUID_DBG, "File load: Allocating %d bytes", buflen);
+ buffer = FLUID_MALLOC(buflen);
+ if (buffer == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+ n = FLUID_FREAD(buffer, 1, buflen, fp);
+ if (n != buflen) {
+ FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n,
+ buflen);
+ FLUID_FREE(buffer);
+ return NULL;
+ };
+ *length = n;
+ return buffer;
+}
+
+/**
+ * Delete a MIDI file handle.
+ * @internal
+ * @param mf MIDI file handle to close and free.
+ */
+void
+delete_fluid_midi_file (fluid_midi_file *mf)
+{
+ if (mf == NULL) {
+ return;
+ }
+ FLUID_FREE(mf);
+ return;
+}
+
+/*
+ * Gets the next byte in a MIDI file, taking into account previous running status.
+ *
+ * returns FLUID_FAILED if EOF or read error
+ */
+int
+fluid_midi_file_getc (fluid_midi_file *mf)
+{
+ unsigned char c;
+ if (mf->c >= 0) {
+ c = mf->c;
+ mf->c = -1;
+ } else {
+ if (mf->buf_pos >= mf->buf_len) {
+ mf->eof = TRUE;
+ return FLUID_FAILED;
+ }
+ c = mf->buffer[mf->buf_pos++];
+ mf->trackpos++;
+ }
+ return (int) c;
+}
+
+/*
+ * Saves a byte to be returned the next time fluid_midi_file_getc() is called,
+ * when it is necessary according to running status.
+ */
+int
+fluid_midi_file_push(fluid_midi_file *mf, int c)
+{
+ mf->c = c;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read
+ */
+int
+fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len)
+{
+ int num = len < mf->buf_len - mf->buf_pos
+ ? len : mf->buf_len - mf->buf_pos;
+ if (num != len) {
+ mf->eof = TRUE;
+ }
+ if (num < 0) {
+ num = 0;
+ }
+ /* Note: Read bytes, even if there aren't enough, but only increment
+ * trackpos if successful (emulates old behaviour of fluid_midi_file_read)
+ */
+ FLUID_MEMCPY(buf, mf->buffer+mf->buf_pos, num);
+ mf->buf_pos += num;
+ if (num == len)
+ mf->trackpos += num;
+#if DEBUG
+ else
+ FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes");
+#endif
+ return (num != len) ? FLUID_FAILED : FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_skip
+ */
+int
+fluid_midi_file_skip (fluid_midi_file *mf, int skip)
+{
+ int new_pos = mf->buf_pos + skip;
+ /* Mimic the behaviour of fseek: Error to seek past the start of file, but
+ * OK to seek past end (this just puts it into the EOF state). */
+ if (new_pos < 0) {
+ FLUID_LOG(FLUID_ERR, "Failed to seek position in file");
+ return FLUID_FAILED;
+ }
+ /* Clear the EOF flag, even if moved past the end of the file (this is
+ * consistent with the behaviour of fseek). */
+ mf->eof = FALSE;
+ mf->buf_pos = new_pos;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_eof
+ */
+int fluid_midi_file_eof(fluid_midi_file* mf)
+{
+ /* Note: This does not simply test whether the file read pointer is past
+ * the end of the file. It mimics the behaviour of feof by actually
+ * testing the stateful EOF condition, which is set to TRUE if getc or
+ * fread have attempted to read past the end (but not if they have
+ * precisely reached the end), but reset to FALSE upon a successful seek.
+ */
+ return mf->eof;
+}
+
+/*
+ * fluid_midi_file_read_mthd
+ */
+int
+fluid_midi_file_read_mthd(fluid_midi_file *mf)
+{
+ char mthd[15];
+ if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6)
+ || (mthd[9] > 2)) {
+ FLUID_LOG(FLUID_ERR,
+ "Doesn't look like a MIDI file: invalid MThd header");
+ return FLUID_FAILED;
+ }
+ mf->type = mthd[9];
+ mf->ntracks = (unsigned) mthd[11];
+ mf->ntracks += (unsigned int) (mthd[10]) << 16;
+ if ((mthd[12]) < 0) {
+ mf->uses_smpte = 1;
+ mf->smpte_fps = -mthd[12];
+ mf->smpte_res = (unsigned) mthd[13];
+ FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet");
+ return FLUID_FAILED;
+ } else {
+ mf->uses_smpte = 0;
+ mf->division = (mthd[12] << 8) | (mthd[13] & 0xff);
+ FLUID_LOG(FLUID_DBG, "Division=%d", mf->division);
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_load_tracks
+ */
+int
+fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player)
+{
+ int i;
+ for (i = 0; i < mf->ntracks; i++) {
+ if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_isasciistring
+ */
+int
+fluid_isasciistring(char *s)
+{
+ int i;
+ int len = (int) FLUID_STRLEN(s);
+ for (i = 0; i < len; i++) {
+ if (!fluid_isascii(s[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * fluid_getlength
+ */
+long
+fluid_getlength(unsigned char *s)
+{
+ long i = 0;
+ i = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24);
+ return i;
+}
+
+/*
+ * fluid_midi_file_read_tracklen
+ */
+int
+fluid_midi_file_read_tracklen(fluid_midi_file *mf)
+{
+ unsigned char length[5];
+ if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ mf->tracklen = fluid_getlength(length);
+ mf->trackpos = 0;
+ mf->eot = 0;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_eot
+ */
+int
+fluid_midi_file_eot(fluid_midi_file *mf)
+{
+#if DEBUG
+ if (mf->trackpos > mf->tracklen) {
+ printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen);
+ }
+#endif
+ return mf->eot || (mf->trackpos >= mf->tracklen);
+}
+
+/*
+ * fluid_midi_file_read_track
+ */
+int
+fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num)
+{
+ fluid_track_t *track;
+ unsigned char id[5], length[5];
+ int found_track = 0;
+ int skip;
+
+ if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ id[4] = '\0';
+ mf->dtime = 0;
+
+ while (!found_track) {
+
+ if (fluid_isasciistring((char *) id) == 0) {
+ FLUID_LOG(FLUID_ERR,
+ "An non-ascii track header found, corrupt file");
+ return FLUID_FAILED;
+
+ } else if (strcmp((char *) id, "MTrk") == 0) {
+
+ found_track = 1;
+
+ if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ track = new_fluid_track(num);
+ if (track == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ while (!fluid_midi_file_eot(mf)) {
+ if (fluid_midi_file_read_event(mf, track) != FLUID_OK) {
+ delete_fluid_track(track);
+ return FLUID_FAILED;
+ }
+ }
+
+ /* Skip remaining track data, if any */
+ if (mf->trackpos < mf->tracklen)
+ fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos);
+
+ fluid_player_add_track(player, track);
+
+ } else {
+ found_track = 0;
+ if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ skip = fluid_getlength(length);
+ /* fseek(mf->fp, skip, SEEK_CUR); */
+ if (fluid_midi_file_skip(mf, skip) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+ }
+ if (fluid_midi_file_eof(mf)) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read_varlen
+ */
+int
+fluid_midi_file_read_varlen(fluid_midi_file *mf)
+{
+ int i;
+ int c;
+ mf->varlen = 0;
+ for (i = 0;; i++) {
+ if (i == 4) {
+ FLUID_LOG(FLUID_ERR, "Invalid variable length number");
+ return FLUID_FAILED;
+ }
+ c = fluid_midi_file_getc(mf);
+ if (c < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ if (c & 0x80) {
+ mf->varlen |= (int) (c & 0x7F);
+ mf->varlen <<= 7;
+ } else {
+ mf->varlen += c;
+ break;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read_event
+ */
+int
+fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)
+{
+ int status;
+ int type;
+ int tempo;
+ unsigned char *metadata = NULL;
+ unsigned char *dyn_buf = NULL;
+ unsigned char static_buf[256];
+ int nominator, denominator, clocks, notes;
+ fluid_midi_event_t *evt;
+ int channel = 0;
+ int param1 = 0;
+ int param2 = 0;
+ int size;
+
+ /* read the delta-time of the event */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ mf->dtime += mf->varlen;
+
+ /* read the status byte */
+ status = fluid_midi_file_getc(mf);
+ if (status < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ /* not a valid status byte: use the running status instead */
+ if ((status & 0x80) == 0) {
+ if ((mf->running_status & 0x80) == 0) {
+ FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status");
+ return FLUID_FAILED;
+ }
+ fluid_midi_file_push(mf, status);
+ status = mf->running_status;
+ }
+
+ /* check what message we have */
+
+ mf->running_status = status;
+
+ if ((status == MIDI_SYSEX)) { /* system exclusif */
+ /* read the length of the message */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ if (mf->varlen) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
+ __LINE__, mf->varlen);
+ metadata = FLUID_MALLOC(mf->varlen + 1);
+
+ if (metadata == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ /* read the data of the message */
+ if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
+ FLUID_FREE (metadata);
+ return FLUID_FAILED;
+ }
+
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE (metadata);
+ return FLUID_FAILED;
+ }
+
+ evt->dtime = mf->dtime;
+ size = mf->varlen;
+
+ if (metadata[mf->varlen - 1] == MIDI_EOX)
+ size--;
+
+ /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */
+ fluid_midi_event_set_sysex(evt, metadata, size, TRUE);
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ }
+
+ return FLUID_OK;
+
+ } else if (status == MIDI_META_EVENT) { /* meta events */
+
+ int result = FLUID_OK;
+
+ /* get the type of the meta message */
+ type = fluid_midi_file_getc(mf);
+ if (type < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ /* get the length of the data part */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ if (mf->varlen < 255) {
+ metadata = &static_buf[0];
+ } else {
+ FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
+ __LINE__, mf->varlen);
+ dyn_buf = FLUID_MALLOC(mf->varlen + 1);
+ if (dyn_buf == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+ metadata = dyn_buf;
+ }
+
+ /* read the data */
+ if (mf->varlen) {
+ if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
+ if (dyn_buf) {
+ FLUID_FREE(dyn_buf);
+ }
+ return FLUID_FAILED;
+ }
+ }
+
+ /* handle meta data */
+ switch (type) {
+
+ case MIDI_COPYRIGHT:
+ metadata[mf->varlen] = 0;
+ break;
+
+ case MIDI_TRACK_NAME:
+ metadata[mf->varlen] = 0;
+ fluid_track_set_name(track, (char *) metadata);
+ break;
+
+ case MIDI_INST_NAME:
+ metadata[mf->varlen] = 0;
+ break;
+
+ case MIDI_LYRIC:
+ break;
+
+ case MIDI_MARKER:
+ break;
+
+ case MIDI_CUE_POINT:
+ break; /* don't care much for text events */
+
+ case MIDI_EOT:
+ if (mf->varlen != 0) {
+ FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event");
+ result = FLUID_FAILED;
+ break;
+ }
+ mf->eot = 1;
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ result = FLUID_FAILED;
+ break;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = MIDI_EOT;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ break;
+
+ case MIDI_SET_TEMPO:
+ if (mf->varlen != 3) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for SetTempo meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2];
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ result = FLUID_FAILED;
+ break;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = MIDI_SET_TEMPO;
+ evt->channel = 0;
+ evt->param1 = tempo;
+ evt->param2 = 0;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ break;
+
+ case MIDI_SMPTE_OFFSET:
+ if (mf->varlen != 5) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for SMPTE Offset meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ break; /* we don't use smtp */
+
+ case MIDI_TIME_SIGNATURE:
+ if (mf->varlen != 4) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for TimeSignature meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ nominator = metadata[0];
+ denominator = pow(2.0, (double) metadata[1]);
+ clocks = metadata[2];
+ notes = metadata[3];
+
+ FLUID_LOG(FLUID_DBG,
+ "signature=%d/%d, metronome=%d, 32nd-notes=%d",
+ nominator, denominator, clocks, notes);
+
+ break;
+
+ case MIDI_KEY_SIGNATURE:
+ if (mf->varlen != 2) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for KeySignature meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ /* We don't care about key signatures anyway */
+ /* sf = metadata[0];
+ mi = metadata[1]; */
+ break;
+
+ case MIDI_SEQUENCER_EVENT:
+ break;
+
+ default:
+ break;
+ }
+
+ if (dyn_buf) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__);
+ FLUID_FREE(dyn_buf);
+ }
+
+ return result;
+
+ } else { /* channel messages */
+
+ type = status & 0xf0;
+ channel = status & 0x0f;
+
+ /* all channel message have at least 1 byte of associated data */
+ if ((param1 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ switch (type) {
+
+ case NOTE_ON:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case NOTE_OFF:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case KEY_PRESSURE:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case CONTROL_CHANGE:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case PROGRAM_CHANGE:
+ break;
+
+ case CHANNEL_PRESSURE:
+ break;
+
+ case PITCH_BEND:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f);
+ param2 = 0;
+ break;
+
+ default:
+ /* Can't possibly happen !? */
+ FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event");
+ return FLUID_FAILED;
+ }
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = type;
+ evt->channel = channel;
+ evt->param1 = param1;
+ evt->param2 = param2;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_get_division
+ */
+int
+fluid_midi_file_get_division(fluid_midi_file *midifile)
+{
+ return midifile->division;
+}
+
+/******************************************************
+ *
+ * fluid_track_t
+ */
+
+/**
+ * Create a MIDI event structure.
+ * @return New MIDI event structure or NULL when out of memory.
+ */
+fluid_midi_event_t *
+new_fluid_midi_event ()
+{
+ fluid_midi_event_t* evt;
+ evt = FLUID_NEW(fluid_midi_event_t);
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ evt->dtime = 0;
+ evt->type = 0;
+ evt->channel = 0;
+ evt->param1 = 0;
+ evt->param2 = 0;
+ evt->next = NULL;
+ evt->paramptr = NULL;
+ return evt;
+}
+
+/**
+ * Delete MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Always returns #FLUID_OK
+ */
+int
+delete_fluid_midi_event(fluid_midi_event_t *evt)
+{
+ fluid_midi_event_t *temp;
+
+ while (evt) {
+ temp = evt->next;
+
+ /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */
+ if (evt->type == MIDI_SYSEX && evt->paramptr && evt->param2)
+ FLUID_FREE (evt->paramptr);
+
+ FLUID_FREE(evt);
+ evt = temp;
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Get the event type field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Event type field (MIDI status byte without channel)
+ */
+int
+fluid_midi_event_get_type(fluid_midi_event_t *evt)
+{
+ return evt->type;
+}
+
+/**
+ * Set the event type field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param type Event type field (MIDI status byte without channel)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_type(fluid_midi_event_t *evt, int type)
+{
+ evt->type = type;
+ return FLUID_OK;
+}
+
+/**
+ * Get the channel field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Channel field
+ */
+int
+fluid_midi_event_get_channel(fluid_midi_event_t *evt)
+{
+ return evt->channel;
+}
+
+/**
+ * Set the channel field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param chan MIDI channel field
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan)
+{
+ evt->channel = chan;
+ return FLUID_OK;
+}
+
+/**
+ * Get the key field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI note number (0-127)
+ */
+int
+fluid_midi_event_get_key(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the key field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI note number (0-127)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_key(fluid_midi_event_t *evt, int v)
+{
+ evt->param1 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the velocity field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI velocity number (0-127)
+ */
+int
+fluid_midi_event_get_velocity(fluid_midi_event_t *evt)
+{
+ return evt->param2;
+}
+
+/**
+ * Set the velocity field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI velocity value
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int v)
+{
+ evt->param2 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the control number of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI control number
+ */
+int
+fluid_midi_event_get_control(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the control field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI control number
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_control(fluid_midi_event_t *evt, int v)
+{
+ evt->param1 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the value field from a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Value field
+ */
+int
+fluid_midi_event_get_value(fluid_midi_event_t *evt)
+{
+ return evt->param2;
+}
+
+/**
+ * Set the value field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v Value to assign
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_value(fluid_midi_event_t *evt, int v)
+{
+ evt->param2 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the program field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI program number (0-127)
+ */
+int
+fluid_midi_event_get_program(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the program field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param val MIDI program number (0-127)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_program(fluid_midi_event_t *evt, int val)
+{
+ evt->param1 = val;
+ return FLUID_OK;
+}
+
+/**
+ * Get the pitch field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Pitch value (14 bit value, 0-16383, 8192 is center)
+ */
+int
+fluid_midi_event_get_pitch(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the pitch field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param val Pitch value (14 bit value, 0-16383, 8192 is center)
+ * @return Always returns FLUID_OK
+ */
+int
+fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)
+{
+ evt->param1 = val;
+ return FLUID_OK;
+}
+
+/**
+ * Assign sysex data to a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param data Pointer to SYSEX data
+ * @param size Size of SYSEX data
+ * @param dynamic TRUE if the SYSEX data has been dynamically allocated and
+ * should be freed when the event is freed (only applies if event gets destroyed
+ * with delete_fluid_midi_event())
+ * @return Always returns #FLUID_OK
+ *
+ * NOTE: Unlike the other event assignment functions, this one sets evt->type.
+ */
+int
+fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic)
+{
+ evt->type = MIDI_SYSEX;
+ evt->paramptr = data;
+ evt->param1 = size;
+ evt->param2 = dynamic;
+ return FLUID_OK;
+}
+
+/******************************************************
+ *
+ * fluid_track_t
+ */
+
+/*
+ * new_fluid_track
+ */
+fluid_track_t *
+new_fluid_track(int num)
+{
+ fluid_track_t *track;
+ track = FLUID_NEW(fluid_track_t);
+ if (track == NULL) {
+ return NULL;
+ }
+ track->name = NULL;
+ track->num = num;
+ track->first = NULL;
+ track->cur = NULL;
+ track->last = NULL;
+ track->ticks = 0;
+ return track;
+}
+
+/*
+ * delete_fluid_track
+ */
+int
+delete_fluid_track(fluid_track_t *track)
+{
+ if (track->name != NULL) {
+ FLUID_FREE(track->name);
+ }
+ if (track->first != NULL) {
+ delete_fluid_midi_event(track->first);
+ }
+ FLUID_FREE(track);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_set_name
+ */
+int
+fluid_track_set_name(fluid_track_t *track, char *name)
+{
+ int len;
+ if (track->name != NULL) {
+ FLUID_FREE(track->name);
+ }
+ if (name == NULL) {
+ track->name = NULL;
+ return FLUID_OK;
+ }
+ len = FLUID_STRLEN(name);
+ track->name = FLUID_MALLOC(len + 1);
+ if (track->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_STRCPY(track->name, name);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_get_name
+ */
+char *
+fluid_track_get_name(fluid_track_t *track)
+{
+ return track->name;
+}
+
+/*
+ * fluid_track_get_duration
+ */
+int
+fluid_track_get_duration(fluid_track_t *track)
+{
+ int time = 0;
+ fluid_midi_event_t *evt = track->first;
+ while (evt != NULL) {
+ time += evt->dtime;
+ evt = evt->next;
+ }
+ return time;
+}
+
+#if 0
+/*
+ * fluid_track_count_events
+ */
+static int
+fluid_track_count_events(fluid_track_t *track, int *on, int *off)
+{
+ fluid_midi_event_t *evt = track->first;
+ while (evt != NULL) {
+ if (evt->type == NOTE_ON) {
+ (*on)++;
+ } else if (evt->type == NOTE_OFF) {
+ (*off)++;
+ }
+ evt = evt->next;
+ }
+ return FLUID_OK;
+}
+#endif
+
+/*
+ * fluid_track_add_event
+ */
+int
+fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt)
+{
+ evt->next = NULL;
+ if (track->first == NULL) {
+ track->first = evt;
+ track->cur = evt;
+ track->last = evt;
+ } else {
+ track->last->next = evt;
+ track->last = evt;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_first_event
+ */
+fluid_midi_event_t *
+fluid_track_first_event(fluid_track_t *track)
+{
+ track->cur = track->first;
+ return track->cur;
+}
+
+/*
+ * fluid_track_next_event
+ */
+fluid_midi_event_t *
+fluid_track_next_event(fluid_track_t *track)
+{
+ if (track->cur != NULL) {
+ track->cur = track->cur->next;
+ }
+ return track->cur;
+}
+
+/*
+ * fluid_track_reset
+ */
+int
+fluid_track_reset(fluid_track_t *track)
+{
+ track->ticks = 0;
+ track->cur = track->first;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_send_events
+ */
+int
+fluid_track_send_events(fluid_track_t *track,
+ fluid_synth_t *synth,
+ fluid_player_t *player,
+ unsigned int ticks)
+{
+ int status = FLUID_OK;
+ fluid_midi_event_t *event;
+
+ while (1) {
+
+ event = track->cur;
+ if (event == NULL) {
+ return status;
+ }
+
+ /* printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */
+ /* track->num, */
+ /* ticks, */
+ /* track->ticks, */
+ /* event->dtime, */
+ /* track->ticks + event->dtime); */
+
+ if (track->ticks + event->dtime > ticks) {
+ return status;
+ }
+
+ track->ticks += event->dtime;
+
+ if (!player || event->type == MIDI_EOT) {
+ }
+ else if (event->type == MIDI_SET_TEMPO) {
+ fluid_player_set_midi_tempo(player, event->param1);
+ }
+ else {
+ if (player->playback_callback)
+ player->playback_callback(player->playback_userdata, event);
+ }
+
+ fluid_track_next_event(track);
+
+ }
+ return status;
+}
+
+/******************************************************
+ *
+ * fluid_player
+ */
+
+/**
+ * Create a new MIDI player.
+ * @param synth Fluid synthesizer instance to create player for
+ * @return New MIDI player instance or NULL on error (out of memory)
+ */
+fluid_player_t *
+new_fluid_player(fluid_synth_t *synth)
+{
+ int i;
+ fluid_player_t *player;
+ player = FLUID_NEW(fluid_player_t);
+ if (player == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ player->status = FLUID_PLAYER_READY;
+ player->loop = 1;
+ player->ntracks = 0;
+ for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
+ player->track[i] = NULL;
+ }
+ player->synth = synth;
+ player->system_timer = NULL;
+ player->sample_timer = NULL;
+ player->playlist = NULL;
+ player->currentfile = NULL;
+ player->division = 0;
+ player->send_program_change = 1;
+ player->miditempo = 480000;
+ player->deltatime = 4.0;
+ player->cur_msec = 0;
+ player->cur_ticks = 0;
+ fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth);
+
+ player->use_system_timer = fluid_settings_str_equal(synth->settings,
+ "player.timing-source", "system");
+
+ fluid_settings_getint(synth->settings, "player.reset-synth", &i);
+ player->reset_synth_between_songs = i;
+
+ return player;
+}
+
+/**
+ * Delete a MIDI player instance.
+ * @param player MIDI player instance
+ * @return Always returns #FLUID_OK
+ */
+int
+delete_fluid_player(fluid_player_t *player)
+{
+ fluid_list_t *q;
+ fluid_playlist_item* pi;
+
+ if (player == NULL) {
+ return FLUID_OK;
+ }
+ fluid_player_stop(player);
+ fluid_player_reset(player);
+
+ while (player->playlist != NULL) {
+ q = player->playlist->next;
+ pi = (fluid_playlist_item*) player->playlist->data;
+ FLUID_FREE(pi->filename);
+ FLUID_FREE(pi->buffer);
+ FLUID_FREE(pi);
+ delete1_fluid_list(player->playlist);
+ player->playlist = q;
+ }
+
+ FLUID_FREE(player);
+ return FLUID_OK;
+}
+
+/**
+ * Registers settings related to the MIDI player
+ */
+void
+fluid_player_settings(fluid_settings_t *settings)
+{
+ /* player.timing-source can be either "system" (use system timer)
+ or "sample" (use timer based on number of written samples) */
+ fluid_settings_register_str(settings, "player.timing-source", "sample", 0,
+ NULL, NULL);
+ fluid_settings_add_option(settings, "player.timing-source", "sample");
+ fluid_settings_add_option(settings, "player.timing-source", "system");
+
+ /* Selects whether the player should reset the synth between songs, or not. */
+ fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+}
+
+
+int
+fluid_player_reset(fluid_player_t *player)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
+ if (player->track[i] != NULL) {
+ delete_fluid_track(player->track[i]);
+ player->track[i] = NULL;
+ }
+ }
+ /* player->current_file = NULL; */
+ /* player->status = FLUID_PLAYER_READY; */
+ /* player->loop = 1; */
+ player->ntracks = 0;
+ player->division = 0;
+ player->send_program_change = 1;
+ player->miditempo = 480000;
+ player->deltatime = 4.0;
+ return 0;
+}
+
+/*
+ * fluid_player_add_track
+ */
+int
+fluid_player_add_track(fluid_player_t *player, fluid_track_t *track)
+{
+ if (player->ntracks < MAX_NUMBER_OF_TRACKS) {
+ player->track[player->ntracks++] = track;
+ return FLUID_OK;
+ } else {
+ return FLUID_FAILED;
+ }
+}
+
+/*
+ * fluid_player_count_tracks
+ */
+int
+fluid_player_count_tracks(fluid_player_t *player)
+{
+ return player->ntracks;
+}
+
+/*
+ * fluid_player_get_track
+ */
+fluid_track_t *
+fluid_player_get_track(fluid_player_t *player, int i)
+{
+ if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) {
+ return player->track[i];
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * 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.
+ * @param player MIDI player instance
+ * @param handler Pointer to callback function
+ * @param handler_data Parameter sent to the callback function
+ * @returns FLUID_OK
+ * @since 1.1.4
+ */
+int
+fluid_player_set_playback_callback(fluid_player_t* player,
+ handle_midi_event_func_t handler, void* handler_data)
+{
+ player->playback_callback = handler;
+ player->playback_userdata = handler_data;
+ return FLUID_OK;
+}
+
+/**
+ * Add a MIDI file to a player queue.
+ * @param player MIDI player instance
+ * @param midifile File name of the MIDI file to add
+ * @return #FLUID_OK or #FLUID_FAILED
+ */
+int
+fluid_player_add(fluid_player_t *player, const char *midifile)
+{
+ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));
+ char* f = FLUID_STRDUP(midifile);
+ if (!pi || !f) {
+ FLUID_FREE(pi);
+ FLUID_FREE(f);
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ pi->filename = f;
+ pi->buffer = NULL;
+ pi->buffer_len = 0;
+ player->playlist = fluid_list_append(player->playlist, pi);
+ return FLUID_OK;
+}
+
+/**
+ * Add a MIDI file to a player queue, from a buffer in memory.
+ * @param player MIDI player instance
+ * @param buffer Pointer to memory containing the bytes of a complete MIDI
+ * file. The data is copied, so the caller may free or modify it immediately
+ * without affecting the playlist.
+ * @param len Length of the buffer, in bytes.
+ * @return #FLUID_OK or #FLUID_FAILED
+ */
+int
+fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len)
+{
+ /* Take a copy of the buffer, so the caller can free immediately. */
+ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));
+ void *buf_copy = FLUID_MALLOC(len);
+ if (!pi || !buf_copy) {
+ FLUID_FREE(pi);
+ FLUID_FREE(buf_copy);
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ FLUID_MEMCPY(buf_copy, buffer, len);
+ pi->filename = NULL;
+ pi->buffer = buf_copy;
+ pi->buffer_len = len;
+ player->playlist = fluid_list_append(player->playlist, pi);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_player_load
+ */
+int
+fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
+{
+ fluid_midi_file *midifile;
+ char* buffer;
+ size_t buffer_length;
+ int buffer_owned;
+
+ if (item->filename != NULL)
+ {
+ fluid_file fp;
+ /* This file is specified by filename; load the file from disk */
+ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__,
+ item->filename);
+ /* Read the entire contents of the file into the buffer */
+ fp = FLUID_FOPEN(item->filename, "rb");
+ if (fp == NULL) {
+ FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file");
+ return FLUID_FAILED;
+ }
+ buffer = fluid_file_read_full(fp, &buffer_length);
+ if (buffer == NULL)
+ {
+ FLUID_FCLOSE(fp);
+ return FLUID_FAILED;
+ }
+ buffer_owned = 1;
+ FLUID_FCLOSE(fp);
+ }
+ else
+ {
+ /* This file is specified by a pre-loaded buffer; load from memory */
+ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)",
+ __FILE__, __LINE__, item->buffer);
+ buffer = (char *) item->buffer;
+ buffer_length = item->buffer_len;
+ /* Do not free the buffer (it is owned by the playlist) */
+ buffer_owned = 0;
+ }
+
+ midifile = new_fluid_midi_file(buffer, buffer_length);
+ if (midifile == NULL) {
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ return FLUID_FAILED;
+ }
+ player->division = fluid_midi_file_get_division(midifile);
+ fluid_player_set_midi_tempo(player, player->miditempo); // Update deltatime
+ /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */
+
+ if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) {
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ delete_fluid_midi_file(midifile);
+ return FLUID_FAILED;
+ }
+ delete_fluid_midi_file(midifile);
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ return FLUID_OK;
+}
+
+static void
+fluid_player_advancefile(fluid_player_t *player)
+{
+ if (player->playlist == NULL) {
+ return; /* No files to play */
+ }
+ if (player->currentfile != NULL) {
+ player->currentfile = fluid_list_next(player->currentfile);
+ }
+ if (player->currentfile == NULL) {
+ if (player->loop == 0) {
+ return; /* We're done playing */
+ }
+ if (player->loop > 0) {
+ player->loop--;
+ }
+ player->currentfile = player->playlist;
+ }
+}
+
+static void
+fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)
+{
+ fluid_playlist_item* current_playitem;
+ int i;
+
+ do {
+ fluid_player_advancefile(player);
+ if (player->currentfile == NULL) {
+ /* Failed to find next song, probably since we're finished */
+ player->status = FLUID_PLAYER_DONE;
+ return;
+ }
+
+ fluid_player_reset(player);
+ current_playitem = (fluid_playlist_item *) player->currentfile->data;
+ } while (fluid_player_load(player, current_playitem) != FLUID_OK);
+
+ /* Successfully loaded midi file */
+
+ player->begin_msec = msec;
+ player->start_msec = 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) {
+ fluid_track_reset(player->track[i]);
+ }
+ }
+}
+
+
+/*
+ * fluid_player_callback
+ */
+int
+fluid_player_callback(void *data, unsigned int msec)
+{
+ int i;
+ int loadnextfile;
+ int status = FLUID_PLAYER_DONE;
+ fluid_player_t *player;
+ fluid_synth_t *synth;
+ player = (fluid_player_t *) data;
+ synth = player->synth;
+
+ loadnextfile = player->currentfile == NULL ? 1 : 0;
+ do {
+ if (loadnextfile) {
+ loadnextfile = 0;
+ fluid_player_playlist_load(player, msec);
+ if (player->currentfile == NULL) {
+ return 0;
+ }
+ }
+
+ player->cur_msec = msec;
+ player->cur_ticks = (player->start_ticks
+ + (int) ((double) (player->cur_msec - player->start_msec)
+ / player->deltatime));
+
+ for (i = 0; i < player->ntracks; i++) {
+ if (!fluid_track_eot(player->track[i])) {
+ status = FLUID_PLAYER_PLAYING;
+ if (fluid_track_send_events(player->track[i], synth, player,
+ player->cur_ticks) != FLUID_OK) {
+ /* */
+ }
+ }
+ }
+
+ if (status == FLUID_PLAYER_DONE) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__,
+ __LINE__, (msec - player->begin_msec) / 1000.0);
+ loadnextfile = 1;
+ }
+ } while (loadnextfile);
+
+ player->status = status;
+
+ return 1;
+}
+
+/**
+ * Activates play mode for a MIDI player if not already playing.
+ * @param player MIDI player instance
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int
+fluid_player_play(fluid_player_t *player)
+{
+ if (player->status == FLUID_PLAYER_PLAYING) {
+ return FLUID_OK;
+ }
+
+ if (player->playlist == NULL) {
+ return FLUID_OK;
+ }
+
+ player->status = FLUID_PLAYER_PLAYING;
+
+ if (player->use_system_timer) {
+ player->system_timer = new_fluid_timer((int) player->deltatime,
+ fluid_player_callback, (void *) player, TRUE, FALSE, TRUE);
+ if (player->system_timer == NULL) {
+ return FLUID_FAILED;
+ }
+ } else {
+ player->sample_timer = new_fluid_sample_timer(player->synth,
+ fluid_player_callback, (void *) player);
+
+ if (player->sample_timer == NULL) {
+ return FLUID_FAILED;
+ }
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Stops a MIDI player.
+ * @param player MIDI player instance
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_player_stop(fluid_player_t *player)
+{
+ if (player->system_timer != NULL) {
+ delete_fluid_timer(player->system_timer);
+ }
+ if (player->sample_timer != NULL) {
+ delete_fluid_sample_timer(player->synth, player->sample_timer);
+ }
+ player->status = FLUID_PLAYER_DONE;
+ player->sample_timer = NULL;
+ player->system_timer = NULL;
+ return FLUID_OK;
+}
+
+/**
+ * Get MIDI player status.
+ * @param player MIDI player instance
+ * @return Player status (#fluid_player_status)
+ * @since 1.1.0
+ */
+int
+fluid_player_get_status(fluid_player_t *player)
+{
+ return player->status;
+}
+
+/**
+ * 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.
+ */
+int fluid_player_set_loop(fluid_player_t *player, int loop)
+{
+ player->loop = loop;
+ 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
+ */
+int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)
+{
+ player->miditempo = tempo;
+ player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */
+ player->start_msec = player->cur_msec;
+ player->start_ticks = player->cur_ticks;
+
+ FLUID_LOG(FLUID_DBG,
+ "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d",
+ tempo, player->deltatime, player->cur_msec, player->cur_ticks);
+
+ return FLUID_OK;
+}
+
+/**
+ * Set the tempo of a MIDI player in beats per minute.
+ * @param player MIDI player instance
+ * @param bpm Tempo in beats per minute
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_player_set_bpm(fluid_player_t *player, int bpm)
+{
+ return fluid_player_set_midi_tempo(player, (int) ((double) 60 * 1e6 / bpm));
+}
+
+/**
+ * Wait for a MIDI player to terminate (when done playing).
+ * @param player MIDI player instance
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int
+fluid_player_join(fluid_player_t *player)
+{
+ if (player->system_timer) {
+ return fluid_timer_join(player->system_timer);
+ } else if (player->sample_timer) {
+ /* Busy-wait loop, since there's no thread to wait for... */
+ while (player->status != FLUID_PLAYER_DONE) {
+#if defined(WIN32)
+ Sleep(10);
+#else
+ usleep(10000);
+#endif
+ }
+ }
+ return FLUID_OK;
+}
+
+/************************************************************************
+ * MIDI PARSER
+ *
+ */
+
+/*
+ * new_fluid_midi_parser
+ */
+fluid_midi_parser_t *
+new_fluid_midi_parser ()
+{
+ fluid_midi_parser_t *parser;
+ parser = FLUID_NEW(fluid_midi_parser_t);
+ if (parser == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */
+ return parser;
+}
+
+/*
+ * delete_fluid_midi_parser
+ */
+int
+delete_fluid_midi_parser(fluid_midi_parser_t *parser)
+{
+ FLUID_FREE(parser);
+ return FLUID_OK;
+}
+
+/**
+ * Parse a MIDI stream one character at a time.
+ * @param parser Parser instance
+ * @param c Next character in MIDI stream
+ * @return A parsed MIDI event or NULL if none. Event is internal and should
+ * not be modified or freed and is only valid until next call to this function.
+ */
+fluid_midi_event_t *
+fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
+{
+ fluid_midi_event_t *event;
+
+ /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle
+ * of another message. */
+ if (c >= 0xF8) {
+ if (c == MIDI_SYSTEM_RESET) {
+ parser->event.type = c;
+ parser->status = 0; /* clear the status */
+ return &parser->event;
+ }
+
+ return NULL;
+ }
+
+ /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */
+ if (c & 0x80) {
+ /* Any status byte terminates SYSEX messages (not just 0xF7) */
+ if (parser->status == MIDI_SYSEX && parser->nr_bytes > 0) {
+ event = &parser->event;
+ fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes,
+ FALSE);
+ } else
+ event = NULL;
+
+ if (c < 0xF0) /* Voice category message? */
+ {
+ parser->channel = c & 0x0F;
+ parser->status = c & 0xF0;
+
+ /* The event consumes x bytes of data... (subtract 1 for the status byte) */
+ parser->nr_bytes_total = fluid_midi_event_length(parser->status)
+ - 1;
+
+ parser->nr_bytes = 0; /* 0 bytes read so far */
+ } else if (c == MIDI_SYSEX) {
+ parser->status = MIDI_SYSEX;
+ parser->nr_bytes = 0;
+ } else
+ parser->status = 0; /* Discard other system messages (0xF1-0xF7) */
+
+ return event; /* Return SYSEX event or NULL */
+ }
+
+ /* Data/parameter byte */
+
+ /* Discard data bytes for events we don't care about */
+ if (parser->status == 0)
+ return NULL;
+
+ /* Max data size exceeded? (SYSEX messages only really) */
+ if (parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) {
+ parser->status = 0; /* Discard the rest of the message */
+ return NULL;
+ }
+
+ /* Store next byte */
+ parser->data[parser->nr_bytes++] = c;
+
+ /* Do we still need more data to get this event complete? */
+ if (parser->nr_bytes < parser->nr_bytes_total)
+ return NULL;
+
+ /* Event is complete, return it.
+ * Running status byte MIDI feature is also handled here. */
+ parser->event.type = parser->status;
+ parser->event.channel = parser->channel;
+ parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */
+
+ switch (parser->status) {
+ case NOTE_OFF:
+ case NOTE_ON:
+ case KEY_PRESSURE:
+ case CONTROL_CHANGE:
+ case PROGRAM_CHANGE:
+ case CHANNEL_PRESSURE:
+ parser->event.param1 = parser->data[0]; /* For example key number */
+ parser->event.param2 = parser->data[1]; /* For example velocity */
+ break;
+ case PITCH_BEND:
+ /* Pitch-bend is transmitted with 14-bit precision. */
+ parser->event.param1 = (parser->data[1] << 7) | parser->data[0];
+ break;
+ default: /* Unlikely */
+ return NULL;
+ }
+
+ return &parser->event;
+}
+
+/* Purpose:
+ * Returns the length of a MIDI message. */
+static int
+fluid_midi_event_length(unsigned char event)
+{
+ switch (event & 0xF0) {
+ case NOTE_OFF:
+ case NOTE_ON:
+ case KEY_PRESSURE:
+ case CONTROL_CHANGE:
+ case PITCH_BEND:
+ return 3;
+ case PROGRAM_CHANGE:
+ case CHANNEL_PRESSURE:
+ return 2;
+ }
+ switch (event) {
+ case MIDI_TIME_CODE:
+ case MIDI_SONG_SELECT:
+ case 0xF4:
+ case 0xF5:
+ return 2;
+ case MIDI_TUNE_REQUEST:
+ return 1;
+ case MIDI_SONG_POSITION:
+ return 3;
+ }
+ return 1;
+}
diff --git a/libs/fluidsynth/src/fluid_midi.h b/libs/fluidsynth/src/fluid_midi.h
new file mode 100644
index 0000000000..90fcef7c91
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_midi.h
@@ -0,0 +1,382 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_MIDI_H
+#define _FLUID_MIDI_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_sys.h"
+#include "fluid_list.h"
+
+typedef struct _fluid_midi_parser_t fluid_midi_parser_t;
+
+fluid_midi_parser_t* new_fluid_midi_parser(void);
+int delete_fluid_midi_parser(fluid_midi_parser_t* parser);
+fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c);
+
+
+/***************************************************************
+ *
+ * CONSTANTS & ENUM
+ */
+
+
+#define MAX_NUMBER_OF_TRACKS 128
+
+enum fluid_midi_event_type {
+ /* channel messages */
+ NOTE_OFF = 0x80,
+ NOTE_ON = 0x90,
+ KEY_PRESSURE = 0xa0,
+ CONTROL_CHANGE = 0xb0,
+ PROGRAM_CHANGE = 0xc0,
+ CHANNEL_PRESSURE = 0xd0,
+ PITCH_BEND = 0xe0,
+ /* system exclusive */
+ MIDI_SYSEX = 0xf0,
+ /* system common - never in midi files */
+ MIDI_TIME_CODE = 0xf1,
+ MIDI_SONG_POSITION = 0xf2,
+ MIDI_SONG_SELECT = 0xf3,
+ MIDI_TUNE_REQUEST = 0xf6,
+ MIDI_EOX = 0xf7,
+ /* system real-time - never in midi files */
+ MIDI_SYNC = 0xf8,
+ MIDI_TICK = 0xf9,
+ MIDI_START = 0xfa,
+ MIDI_CONTINUE = 0xfb,
+ MIDI_STOP = 0xfc,
+ MIDI_ACTIVE_SENSING = 0xfe,
+ MIDI_SYSTEM_RESET = 0xff,
+ /* meta event - for midi files only */
+ MIDI_META_EVENT = 0xff
+};
+
+enum fluid_midi_control_change {
+ BANK_SELECT_MSB = 0x00,
+ MODULATION_MSB = 0x01,
+ BREATH_MSB = 0x02,
+ FOOT_MSB = 0x04,
+ PORTAMENTO_TIME_MSB = 0x05,
+ DATA_ENTRY_MSB = 0x06,
+ VOLUME_MSB = 0x07,
+ BALANCE_MSB = 0x08,
+ PAN_MSB = 0x0A,
+ EXPRESSION_MSB = 0x0B,
+ EFFECTS1_MSB = 0x0C,
+ EFFECTS2_MSB = 0x0D,
+ GPC1_MSB = 0x10, /* general purpose controller */
+ GPC2_MSB = 0x11,
+ GPC3_MSB = 0x12,
+ GPC4_MSB = 0x13,
+ BANK_SELECT_LSB = 0x20,
+ MODULATION_WHEEL_LSB = 0x21,
+ BREATH_LSB = 0x22,
+ FOOT_LSB = 0x24,
+ PORTAMENTO_TIME_LSB = 0x25,
+ DATA_ENTRY_LSB = 0x26,
+ VOLUME_LSB = 0x27,
+ BALANCE_LSB = 0x28,
+ PAN_LSB = 0x2A,
+ EXPRESSION_LSB = 0x2B,
+ EFFECTS1_LSB = 0x2C,
+ EFFECTS2_LSB = 0x2D,
+ GPC1_LSB = 0x30,
+ GPC2_LSB = 0x31,
+ GPC3_LSB = 0x32,
+ GPC4_LSB = 0x33,
+ SUSTAIN_SWITCH = 0x40,
+ PORTAMENTO_SWITCH = 0x41,
+ SOSTENUTO_SWITCH = 0x42,
+ SOFT_PEDAL_SWITCH = 0x43,
+ LEGATO_SWITCH = 0x45,
+ HOLD2_SWITCH = 0x45,
+ SOUND_CTRL1 = 0x46,
+ SOUND_CTRL2 = 0x47,
+ SOUND_CTRL3 = 0x48,
+ SOUND_CTRL4 = 0x49,
+ SOUND_CTRL5 = 0x4A,
+ SOUND_CTRL6 = 0x4B,
+ SOUND_CTRL7 = 0x4C,
+ SOUND_CTRL8 = 0x4D,
+ SOUND_CTRL9 = 0x4E,
+ SOUND_CTRL10 = 0x4F,
+ GPC5 = 0x50,
+ GPC6 = 0x51,
+ GPC7 = 0x52,
+ GPC8 = 0x53,
+ PORTAMENTO_CTRL = 0x54,
+ EFFECTS_DEPTH1 = 0x5B,
+ EFFECTS_DEPTH2 = 0x5C,
+ EFFECTS_DEPTH3 = 0x5D,
+ EFFECTS_DEPTH4 = 0x5E,
+ EFFECTS_DEPTH5 = 0x5F,
+ DATA_ENTRY_INCR = 0x60,
+ DATA_ENTRY_DECR = 0x61,
+ NRPN_LSB = 0x62,
+ NRPN_MSB = 0x63,
+ RPN_LSB = 0x64,
+ RPN_MSB = 0x65,
+ ALL_SOUND_OFF = 0x78,
+ ALL_CTRL_OFF = 0x79,
+ LOCAL_CONTROL = 0x7A,
+ ALL_NOTES_OFF = 0x7B,
+ OMNI_OFF = 0x7C,
+ OMNI_ON = 0x7D,
+ POLY_OFF = 0x7E,
+ POLY_ON = 0x7F
+};
+
+/* General MIDI RPN event numbers (LSB, MSB = 0) */
+enum midi_rpn_event {
+ RPN_PITCH_BEND_RANGE = 0x00,
+ RPN_CHANNEL_FINE_TUNE = 0x01,
+ RPN_CHANNEL_COARSE_TUNE = 0x02,
+ RPN_TUNING_PROGRAM_CHANGE = 0x03,
+ RPN_TUNING_BANK_SELECT = 0x04,
+ RPN_MODULATION_DEPTH_RANGE = 0x05
+};
+
+enum midi_meta_event {
+ MIDI_COPYRIGHT = 0x02,
+ MIDI_TRACK_NAME = 0x03,
+ MIDI_INST_NAME = 0x04,
+ MIDI_LYRIC = 0x05,
+ MIDI_MARKER = 0x06,
+ MIDI_CUE_POINT = 0x07,
+ MIDI_EOT = 0x2f,
+ MIDI_SET_TEMPO = 0x51,
+ MIDI_SMPTE_OFFSET = 0x54,
+ MIDI_TIME_SIGNATURE = 0x58,
+ MIDI_KEY_SIGNATURE = 0x59,
+ MIDI_SEQUENCER_EVENT = 0x7f
+};
+
+/* MIDI SYSEX useful manufacturer values */
+enum midi_sysex_manuf {
+ MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */
+ MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */
+ MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */
+};
+
+#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */
+
+/* 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 */
+
+/**
+ * SYSEX tuning message IDs.
+ */
+enum midi_sysex_tuning_msg_id {
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */
+};
+
+/* General MIDI sub-ID #2 */
+#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */
+#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */
+
+enum fluid_driver_status
+{
+ FLUID_MIDI_READY,
+ FLUID_MIDI_LISTENING,
+ FLUID_MIDI_DONE
+};
+
+/***************************************************************
+ *
+ * TYPE DEFINITIONS & FUNCTION DECLARATIONS
+ */
+
+/* From ctype.h */
+#define fluid_isascii(c) (((c) & ~0x7f) == 0)
+
+
+
+/*
+ * fluid_midi_event_t
+ */
+struct _fluid_midi_event_t {
+ fluid_midi_event_t* next; /* Link to next event */
+ void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */
+ unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */
+ unsigned int param1; /* First parameter */
+ unsigned int param2; /* Second parameter */
+ unsigned char type; /* MIDI event type */
+ unsigned char channel; /* MIDI channel */
+};
+
+
+/*
+ * fluid_track_t
+ */
+struct _fluid_track_t {
+ char* name;
+ int num;
+ fluid_midi_event_t *first;
+ fluid_midi_event_t *cur;
+ fluid_midi_event_t *last;
+ unsigned int ticks;
+};
+
+typedef struct _fluid_track_t fluid_track_t;
+
+fluid_track_t* new_fluid_track(int num);
+int delete_fluid_track(fluid_track_t* track);
+int fluid_track_set_name(fluid_track_t* track, char* name);
+char* fluid_track_get_name(fluid_track_t* track);
+int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt);
+fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track);
+fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track);
+int fluid_track_get_duration(fluid_track_t* track);
+int fluid_track_reset(fluid_track_t* track);
+
+int fluid_track_send_events(fluid_track_t* track,
+ fluid_synth_t* synth,
+ fluid_player_t* player,
+ unsigned int ticks);
+
+#define fluid_track_eot(track) ((track)->cur == NULL)
+
+
+/**
+ * fluid_playlist_item
+ * Used as the `data' elements of the fluid_player.playlist.
+ * Represents either a filename or a pre-loaded memory buffer.
+ * Exactly one of `filename' and `buffer' is non-NULL.
+ */
+typedef struct
+{
+ char* filename; /** Name of file (owned); NULL if data pre-loaded */
+ void* buffer; /** The MIDI file data (owned); NULL if filename */
+ size_t buffer_len; /** Number of bytes in buffer; 0 if filename */
+} fluid_playlist_item;
+
+/*
+ * fluid_player
+ */
+struct _fluid_player_t {
+ int status;
+ int ntracks;
+ fluid_track_t *track[MAX_NUMBER_OF_TRACKS];
+ fluid_synth_t* synth;
+ fluid_timer_t* system_timer;
+ fluid_sample_timer_t* sample_timer;
+
+ int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */
+ 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 start_ticks; /* the number of tempo ticks passed at the last tempo change */
+ int cur_ticks; /* the number of tempo ticks passed */
+ 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 */
+ 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 */
+};
+
+int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track);
+int fluid_player_callback(void* data, unsigned int msec);
+int fluid_player_count_tracks(fluid_player_t* player);
+fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i);
+int fluid_player_reset(fluid_player_t* player);
+int fluid_player_load(fluid_player_t* player, fluid_playlist_item *item);
+
+void fluid_player_settings(fluid_settings_t* settings);
+
+
+/*
+ * fluid_midi_file
+ */
+typedef struct {
+ const char* buffer; /* Entire contents of MIDI file (borrowed) */
+ int buf_len; /* Length of buffer, in bytes */
+ int buf_pos; /* Current read position in contents buffer */
+ int eof; /* The "end of file" condition */
+ int running_status;
+ int c;
+ int type;
+ int ntracks;
+ int uses_smpte;
+ unsigned int smpte_fps;
+ unsigned int smpte_res;
+ unsigned int division; /* If uses_SMPTE == 0 then division is
+ ticks per beat (quarter-note) */
+ double tempo; /* Beats per second (SI rules =) */
+ int tracklen;
+ int trackpos;
+ int eot;
+ int varlen;
+ int dtime;
+} fluid_midi_file;
+
+fluid_midi_file* new_fluid_midi_file(const char* buffer, size_t length);
+void delete_fluid_midi_file(fluid_midi_file* mf);
+int fluid_midi_file_read_mthd(fluid_midi_file* midifile);
+int fluid_midi_file_load_tracks(fluid_midi_file* midifile, fluid_player_t* player);
+int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num);
+int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track);
+int fluid_midi_file_read_varlen(fluid_midi_file* mf);
+int fluid_midi_file_getc(fluid_midi_file* mf);
+int fluid_midi_file_push(fluid_midi_file* mf, int c);
+int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len);
+int fluid_midi_file_skip(fluid_midi_file* mf, int len);
+int fluid_midi_file_eof(fluid_midi_file* mf);
+int fluid_midi_file_read_tracklen(fluid_midi_file* mf);
+int fluid_midi_file_eot(fluid_midi_file* mf);
+int fluid_midi_file_get_division(fluid_midi_file* midifile);
+
+
+#define FLUID_MIDI_PARSER_MAX_DATA_SIZE 1024 /**< Maximum size of MIDI parameters/data (largest is SYSEX data) */
+
+/*
+ * fluid_midi_parser_t
+ */
+struct _fluid_midi_parser_t {
+ unsigned char status; /* Identifies the type of event, that is currently received ('Noteon', 'Pitch Bend' etc). */
+ unsigned char channel; /* The channel of the event that is received (in case of a channel event) */
+ unsigned int nr_bytes; /* How many bytes have been read for the current event? */
+ unsigned int nr_bytes_total; /* How many bytes does the current event type include? */
+ unsigned char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */
+ fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */
+};
+
+int fluid_isasciistring(char* s);
+long fluid_getlength(unsigned char *s);
+
+
+#endif /* _FLUID_MIDI_H */
diff --git a/libs/fluidsynth/src/fluid_mod.c b/libs/fluidsynth/src/fluid_mod.c
new file mode 100644
index 0000000000..5931aa52a6
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_mod.c
@@ -0,0 +1,488 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_mod.h"
+#include "fluid_chan.h"
+#include "fluid_voice.h"
+
+/*
+ * fluid_mod_clone
+ */
+void
+fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src)
+{
+ mod->dest = src->dest;
+ mod->src1 = src->src1;
+ mod->flags1 = src->flags1;
+ mod->src2 = src->src2;
+ mod->flags2 = src->flags2;
+ mod->amount = src->amount;
+}
+
+/**
+ * Set a modulator's primary source controller and flags.
+ * @param mod Modulator
+ * @param src Modulator source (#fluid_mod_src or a MIDI controller number)
+ * @param flags Flags determining mapping function and whether the source
+ * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller
+ * (#FLUID_MOD_CC), see #fluid_mod_flags.
+ */
+void
+fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags)
+{
+ mod->src1 = src;
+ mod->flags1 = flags;
+}
+
+/**
+ * Set a modulator's secondary source controller and flags.
+ * @param mod Modulator
+ * @param src Modulator source (#fluid_mod_src or a MIDI controller number)
+ * @param flags Flags determining mapping function and whether the source
+ * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller
+ * (#FLUID_MOD_CC), see #fluid_mod_flags.
+ */
+void
+fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags)
+{
+ mod->src2 = src;
+ mod->flags2 = flags;
+}
+
+/**
+ * Set the destination effect of a modulator.
+ * @param mod Modulator
+ * @param dest Destination generator (#fluid_gen_type)
+ */
+void
+fluid_mod_set_dest(fluid_mod_t* mod, int dest)
+{
+ mod->dest = dest;
+}
+
+/**
+ * Set the scale amount of a modulator.
+ * @param mod Modulator
+ * @param amount Scale amount to assign
+ */
+void
+fluid_mod_set_amount(fluid_mod_t* mod, double amount)
+{
+ mod->amount = (double) amount;
+}
+
+/**
+ * Get the primary source value from a modulator.
+ * @param mod Modulator
+ * @return The primary source value (#fluid_mod_src or a MIDI CC controller value).
+ */
+int
+fluid_mod_get_source1(fluid_mod_t* mod)
+{
+ return mod->src1;
+}
+
+/**
+ * Get primary source flags from a modulator.
+ * @param mod Modulator
+ * @return The primary source flags (#fluid_mod_flags).
+ */
+int
+fluid_mod_get_flags1(fluid_mod_t* mod)
+{
+ return mod->flags1;
+}
+
+/**
+ * Get the secondary source value from a modulator.
+ * @param mod Modulator
+ * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value).
+ */
+int
+fluid_mod_get_source2(fluid_mod_t* mod)
+{
+ return mod->src2;
+}
+
+/**
+ * Get secondary source flags from a modulator.
+ * @param mod Modulator
+ * @return The secondary source flags (#fluid_mod_flags).
+ */
+int
+fluid_mod_get_flags2(fluid_mod_t* mod)
+{
+ return mod->flags2;
+}
+
+/**
+ * Get destination effect from a modulator.
+ * @param mod Modulator
+ * @return Destination generator (#fluid_gen_type)
+ */
+int
+fluid_mod_get_dest(fluid_mod_t* mod)
+{
+ return mod->dest;
+}
+
+/**
+ * Get the scale amount from a modulator.
+ * @param mod Modulator
+ * @return Scale amount
+ */
+double
+fluid_mod_get_amount(fluid_mod_t* mod)
+{
+ return (fluid_real_t) mod->amount;
+}
+
+
+/*
+ * fluid_mod_get_value
+ */
+fluid_real_t
+fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice)
+{
+ fluid_real_t v1 = 0.0, v2 = 1.0;
+ fluid_real_t range1 = 127.0, range2 = 127.0;
+
+ if (chan == NULL) {
+ return 0.0f;
+ }
+
+ /* 'special treatment' for default controller
+ *
+ * Reference: SF2.01 section 8.4.2
+ *
+ * The GM default controller 'vel-to-filter cut off' is not clearly
+ * defined: If implemented according to the specs, the filter
+ * frequency jumps between vel=63 and vel=64. To maintain
+ * compatibility with existing sound fonts, the implementation is
+ * 'hardcoded', it is impossible to implement using only one
+ * modulator otherwise.
+ *
+ * I assume here, that the 'intention' of the paragraph is one
+ * octave (1200 cents) filter frequency shift between vel=127 and
+ * vel=64. 'amount' is (-2400), at least as long as the controller
+ * is set to default.
+ *
+ * Further, the 'appearance' of the modulator (source enumerator,
+ * destination enumerator, flags etc) is different from that
+ * described in section 8.4.2, but it matches the definition used in
+ * several SF2.1 sound fonts (where it is used only to turn it off).
+ * */
+ if ((mod->src2 == FLUID_MOD_VELOCITY) &&
+ (mod->src1 == FLUID_MOD_VELOCITY) &&
+ (mod->flags1 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR
+ | FLUID_MOD_NEGATIVE | FLUID_MOD_LINEAR)) &&
+ (mod->flags2 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR
+ | FLUID_MOD_POSITIVE | FLUID_MOD_SWITCH)) &&
+ (mod->dest == GEN_FILTERFC)) {
+// S. Christian Collins' mod, to stop forcing velocity based filtering
+/*
+ if (voice->vel < 64){
+ return (fluid_real_t) mod->amount / 2.0;
+ } else {
+ return (fluid_real_t) mod->amount * (127 - voice->vel) / 127;
+ }
+*/
+ return 0; // (fluid_real_t) mod->amount / 2.0;
+ }
+// end S. Christian Collins' mod
+
+ /* get the initial value of the first source */
+ if (mod->src1 > 0) {
+ if (mod->flags1 & FLUID_MOD_CC) {
+ v1 = fluid_channel_get_cc(chan, mod->src1);
+ } else { /* source 1 is one of the direct controllers */
+ switch (mod->src1) {
+ case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */
+ v1 = range1;
+ break;
+ case FLUID_MOD_VELOCITY:
+ v1 = voice->vel;
+ break;
+ case FLUID_MOD_KEY:
+ v1 = voice->key;
+ break;
+ case FLUID_MOD_KEYPRESSURE:
+ v1 = fluid_channel_get_key_pressure (chan);
+ break;
+ case FLUID_MOD_CHANNELPRESSURE:
+ v1 = fluid_channel_get_channel_pressure (chan);
+ break;
+ case FLUID_MOD_PITCHWHEEL:
+ v1 = fluid_channel_get_pitch_bend (chan);
+ range1 = 0x4000;
+ break;
+ case FLUID_MOD_PITCHWHEELSENS:
+ v1 = fluid_channel_get_pitch_wheel_sensitivity (chan);
+ break;
+ default:
+ v1 = 0.0;
+ }
+ }
+
+ /* transform the input value */
+ switch (mod->flags1 & 0x0f) {
+ case 0: /* linear, unipolar, positive */
+ v1 /= range1;
+ break;
+ case 1: /* linear, unipolar, negative */
+ v1 = 1.0f - v1 / range1;
+ break;
+ case 2: /* linear, bipolar, positive */
+ v1 = -1.0f + 2.0f * v1 / range1;
+ break;
+ case 3: /* linear, bipolar, negative */
+ v1 = 1.0f - 2.0f * v1 / range1;
+ break;
+ case 4: /* concave, unipolar, positive */
+ v1 = fluid_concave(v1);
+ break;
+ case 5: /* concave, unipolar, negative */
+ v1 = fluid_concave(127 - v1);
+ break;
+ case 6: /* concave, bipolar, positive */
+ v1 = (v1 > 64)? fluid_concave(2 * (v1 - 64)) : -fluid_concave(2 * (64 - v1));
+ break;
+ case 7: /* concave, bipolar, negative */
+ v1 = (v1 > 64)? -fluid_concave(2 * (v1 - 64)) : fluid_concave(2 * (64 - v1));
+ break;
+ case 8: /* convex, unipolar, positive */
+ v1 = fluid_convex(v1);
+ break;
+ case 9: /* convex, unipolar, negative */
+ v1 = fluid_convex(127 - v1);
+ break;
+ case 10: /* convex, bipolar, positive */
+ v1 = (v1 > 64)? fluid_convex(2 * (v1 - 64)) : -fluid_convex(2 * (64 - v1));
+ break;
+ case 11: /* convex, bipolar, negative */
+ v1 = (v1 > 64)? -fluid_convex(2 * (v1 - 64)) : fluid_convex(2 * (64 - v1));
+ break;
+ case 12: /* switch, unipolar, positive */
+ v1 = (v1 >= 64)? 1.0f : 0.0f;
+ break;
+ case 13: /* switch, unipolar, negative */
+ v1 = (v1 >= 64)? 0.0f : 1.0f;
+ break;
+ case 14: /* switch, bipolar, positive */
+ v1 = (v1 >= 64)? 1.0f : -1.0f;
+ break;
+ case 15: /* switch, bipolar, negative */
+ v1 = (v1 >= 64)? -1.0f : 1.0f;
+ break;
+ }
+ } else {
+ return 0.0;
+ }
+
+ /* no need to go further */
+ if (v1 == 0.0f) {
+ return 0.0f;
+ }
+
+ /* get the second input source */
+ if (mod->src2 > 0) {
+ if (mod->flags2 & FLUID_MOD_CC) {
+ v2 = fluid_channel_get_cc(chan, mod->src2);
+ } else {
+ switch (mod->src2) {
+ case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */
+ v2 = range2;
+ break;
+ case FLUID_MOD_VELOCITY:
+ v2 = voice->vel;
+ break;
+ case FLUID_MOD_KEY:
+ v2 = voice->key;
+ break;
+ case FLUID_MOD_KEYPRESSURE:
+ v2 = fluid_channel_get_key_pressure (chan);
+ break;
+ case FLUID_MOD_CHANNELPRESSURE:
+ v2 = fluid_channel_get_channel_pressure (chan);
+ break;
+ case FLUID_MOD_PITCHWHEEL:
+ v2 = fluid_channel_get_pitch_bend (chan);
+ break;
+ case FLUID_MOD_PITCHWHEELSENS:
+ v2 = fluid_channel_get_pitch_wheel_sensitivity (chan);
+ break;
+ default:
+ v1 = 0.0f;
+ }
+ }
+
+ /* transform the second input value */
+ switch (mod->flags2 & 0x0f) {
+ case 0: /* linear, unipolar, positive */
+ v2 /= range2;
+ break;
+ case 1: /* linear, unipolar, negative */
+ v2 = 1.0f - v2 / range2;
+ break;
+ case 2: /* linear, bipolar, positive */
+ v2 = -1.0f + 2.0f * v2 / range2;
+ break;
+ case 3: /* linear, bipolar, negative */
+ v2 = -1.0f + 2.0f * v2 / range2;
+ break;
+ case 4: /* concave, unipolar, positive */
+ v2 = fluid_concave(v2);
+ break;
+ case 5: /* concave, unipolar, negative */
+ v2 = fluid_concave(127 - v2);
+ break;
+ case 6: /* concave, bipolar, positive */
+ v2 = (v2 > 64)? fluid_concave(2 * (v2 - 64)) : -fluid_concave(2 * (64 - v2));
+ break;
+ case 7: /* concave, bipolar, negative */
+ v2 = (v2 > 64)? -fluid_concave(2 * (v2 - 64)) : fluid_concave(2 * (64 - v2));
+ break;
+ case 8: /* convex, unipolar, positive */
+ v2 = fluid_convex(v2);
+ break;
+ case 9: /* convex, unipolar, negative */
+ v2 = 1.0f - fluid_convex(v2);
+ break;
+ case 10: /* convex, bipolar, positive */
+ v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2));
+ break;
+ case 11: /* convex, bipolar, negative */
+ v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2));
+ break;
+ case 12: /* switch, unipolar, positive */
+ v2 = (v2 >= 64)? 1.0f : 0.0f;
+ break;
+ case 13: /* switch, unipolar, negative */
+ v2 = (v2 >= 64)? 0.0f : 1.0f;
+ break;
+ case 14: /* switch, bipolar, positive */
+ v2 = (v2 >= 64)? 1.0f : -1.0f;
+ break;
+ case 15: /* switch, bipolar, negative */
+ v2 = (v2 >= 64)? -1.0f : 1.0f;
+ break;
+ }
+ } else {
+ v2 = 1.0f;
+ }
+
+ /* it's as simple as that: */
+ return (fluid_real_t) mod->amount * v1 * v2;
+}
+
+/**
+ * Create a new uninitialized modulator structure.
+ * @return New allocated modulator or NULL if out of memory
+ */
+fluid_mod_t*
+fluid_mod_new()
+{
+ fluid_mod_t* mod = FLUID_NEW (fluid_mod_t);
+ if (mod == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ return mod;
+}
+
+/**
+ * Free a modulator structure.
+ * @param mod Modulator to free
+ */
+void
+fluid_mod_delete (fluid_mod_t *mod)
+{
+ FLUID_FREE(mod);
+}
+
+/**
+ * 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
+ *
+ * SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'.
+ */
+int
+fluid_mod_test_identity (fluid_mod_t *mod1, fluid_mod_t *mod2)
+{
+ return mod1->dest == mod2->dest
+ && mod1->src1 == mod2->src1
+ && mod1->src2 == mod2->src2
+ && mod1->flags1 == mod2->flags1
+ && mod1->flags2 == mod2->flags2;
+}
+
+/* debug function: Prints the contents of a modulator */
+void fluid_dump_modulator(fluid_mod_t * mod){
+ int src1=mod->src1;
+ int dest=mod->dest;
+ int src2=mod->src2;
+ int flags1=mod->flags1;
+ int flags2=mod->flags2;
+ fluid_real_t amount=(fluid_real_t)mod->amount;
+
+ printf("Src: ");
+ if (flags1 & FLUID_MOD_CC){
+ printf("MIDI CC=%i",src1);
+ } else {
+ switch(src1){
+ case FLUID_MOD_NONE:
+ printf("None"); break;
+ case FLUID_MOD_VELOCITY:
+ printf("note-on velocity"); break;
+ case FLUID_MOD_KEY:
+ printf("Key nr"); break;
+ case FLUID_MOD_KEYPRESSURE:
+ printf("Poly pressure"); break;
+ case FLUID_MOD_CHANNELPRESSURE:
+ printf("Chan pressure"); break;
+ case FLUID_MOD_PITCHWHEEL:
+ printf("Pitch Wheel"); break;
+ case FLUID_MOD_PITCHWHEELSENS:
+ printf("Pitch Wheel sens"); break;
+ default:
+ printf("(unknown: %i)", src1);
+ }; /* switch src1 */
+ }; /* if not CC */
+ if (flags1 & FLUID_MOD_NEGATIVE){printf("- ");} else {printf("+ ");};
+ if (flags1 & FLUID_MOD_BIPOLAR){printf("bip ");} else {printf("unip ");};
+ printf("-> ");
+ switch(dest){
+ case GEN_FILTERQ: printf("Q"); break;
+ case GEN_FILTERFC: printf("fc"); break;
+ case GEN_VIBLFOTOPITCH: printf("VibLFO-to-pitch"); break;
+ case GEN_MODENVTOPITCH: printf("ModEnv-to-pitch"); break;
+ case GEN_MODLFOTOPITCH: printf("ModLFO-to-pitch"); break;
+ case GEN_CHORUSSEND: printf("Chorus send"); break;
+ case GEN_REVERBSEND: printf("Reverb send"); break;
+ case GEN_PAN: printf("pan"); break;
+ case GEN_ATTENUATION: printf("att"); break;
+ default: printf("dest %i",dest);
+ }; /* switch dest */
+ printf(", amount %f flags %i src2 %i flags2 %i\n",amount, flags1, src2, flags2);
+};
+
+
diff --git a/libs/fluidsynth/src/fluid_mod.h b/libs/fluidsynth/src/fluid_mod.h
new file mode 100644
index 0000000000..81c9f76c70
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_mod.h
@@ -0,0 +1,40 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_MOD_H
+#define _FLUID_MOD_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_conv.h"
+
+void fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src);
+fluid_real_t fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice);
+void fluid_dump_modulator(fluid_mod_t * mod);
+
+#define fluid_mod_has_source(mod,cc,ctrl) \
+( ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) \
+ || ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)))) \
+|| ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) \
+ || ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) == 0) && (cc == 0)))))
+
+#define fluid_mod_has_dest(mod,gen) ((mod)->dest == gen)
+
+
+#endif /* _FLUID_MOD_H */
diff --git a/libs/fluidsynth/src/fluid_phase.h b/libs/fluidsynth/src/fluid_phase.h
new file mode 100644
index 0000000000..15f2fa7550
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_phase.h
@@ -0,0 +1,117 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_PHASE_H
+#define _FLUID_PHASE_H
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+ * phase
+ */
+
+#define FLUID_INTERP_BITS 8
+#define FLUID_INTERP_BITS_MASK 0xff000000
+#define FLUID_INTERP_BITS_SHIFT 24
+#define FLUID_INTERP_MAX 256
+
+#define FLUID_FRACT_MAX ((double)4294967296.0)
+
+/* fluid_phase_t
+* Purpose:
+* Playing pointer for voice playback
+*
+* When a sample is played back at a different pitch, the playing pointer in the
+* source sample will not advance exactly one sample per output sample.
+* This playing pointer is implemented using fluid_phase_t.
+* It is a 64 bit number. The higher 32 bits contain the 'index' (number of
+* the current sample), the lower 32 bits the fractional part.
+*/
+typedef unsigned long long fluid_phase_t;
+
+/* Purpose:
+ * Set a to b.
+ * a: fluid_phase_t
+ * b: fluid_phase_t
+ */
+#define fluid_phase_set(a,b) a=b;
+
+#define fluid_phase_set_int(a, b) ((a) = ((unsigned long long)(b)) << 32)
+
+/* Purpose:
+ * Sets the phase a to a phase increment given in b.
+ * For example, assume b is 0.9. After setting a to it, adding a to
+ * the playing pointer will advance it by 0.9 samples. */
+#define fluid_phase_set_float(a, b) \
+ (a) = (((unsigned long long)(b)) << 32) \
+ | (uint32) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX)
+
+/* create a fluid_phase_t from an index and a fraction value */
+#define fluid_phase_from_index_fract(index, fract) \
+ ((((unsigned long long)(index)) << 32) + (fract))
+
+/* Purpose:
+ * Return the index and the fractional part, respectively. */
+#define fluid_phase_index(_x) \
+ ((unsigned int)((_x) >> 32))
+#define fluid_phase_fract(_x) \
+ ((uint32)((_x) & 0xFFFFFFFF))
+
+/* Get the phase index with fractional rounding */
+#define fluid_phase_index_round(_x) \
+ ((unsigned int)(((_x) + 0x80000000) >> 32))
+
+
+/* Purpose:
+ * Takes the fractional part of the argument phase and
+ * calculates the corresponding position in the interpolation table.
+ * The fractional position of the playing pointer is calculated with a quite high
+ * resolution (32 bits). It would be unpractical to keep a set of interpolation
+ * coefficients for each possible fractional part...
+ */
+#define fluid_phase_fract_to_tablerow(_x) \
+ ((unsigned int)(fluid_phase_fract(_x) & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT)
+
+#define fluid_phase_double(_x) \
+ ((double)(fluid_phase_index(_x)) + ((double)fluid_phase_fract(_x) / FLUID_FRACT_MAX))
+
+/* Purpose:
+ * Advance a by a step of b (both are fluid_phase_t).
+ */
+#define fluid_phase_incr(a, b) a += b
+
+/* Purpose:
+ * Subtract b from a (both are fluid_phase_t).
+ */
+#define fluid_phase_decr(a, b) a -= b
+
+/* Purpose:
+ * Subtract b samples from a.
+ */
+#define fluid_phase_sub_int(a, b) ((a) -= (unsigned long long)(b) << 32)
+
+/* Purpose:
+ * Creates the expression a.index++. */
+#define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL)
+
+#endif /* _FLUID_PHASE_H */
diff --git a/libs/fluidsynth/src/fluid_rev.c b/libs/fluidsynth/src/fluid_rev.c
new file mode 100644
index 0000000000..166007da3f
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rev.c
@@ -0,0 +1,544 @@
+/*
+
+ Freeverb
+
+ Written by Jezar at Dreampoint, June 2000
+ http://www.dreampoint.co.uk
+ This code is public domain
+
+ Translated to C by Peter Hanappe, Mai 2001
+*/
+
+#include "fluid_rev.h"
+
+/***************************************************************
+ *
+ * REVERB
+ */
+
+/* Denormalising:
+ *
+ * According to music-dsp thread 'Denormalise', Pentium processors
+ * have a hardware 'feature', that is of interest here, related to
+ * numeric underflow. We have a recursive filter. The output decays
+ * exponentially, if the input stops. So the numbers get smaller and
+ * smaller... At some point, they reach 'denormal' level. This will
+ * lead to drastic spikes in the CPU load. The effect was reproduced
+ * with the reverb - sometimes the average load over 10 s doubles!!.
+ *
+ * The 'undenormalise' macro fixes the problem: As soon as the number
+ * is close enough to denormal level, the macro forces the number to
+ * 0.0f. The original macro is:
+ *
+ * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
+ *
+ * This will zero out a number when it reaches the denormal level.
+ * Advantage: Maximum dynamic range Disadvantage: We'll have to check
+ * every sample, expensive. The alternative macro comes from a later
+ * mail from Jon Watte. It will zap a number before it reaches
+ * denormal level. Jon suggests to run it once per block instead of
+ * every sample.
+ */
+
+# if defined(WITH_FLOATX)
+# define zap_almost_zero(sample) (((*(unsigned int*)&(sample))&0x7f800000) < 0x08000000)?0.0f:(sample)
+# else
+/* 1e-20 was chosen as an arbitrary (small) threshold. */
+#define zap_almost_zero(sample) fabs(sample)<1e-10 ? 0 : sample;
+#endif
+
+/* Denormalising part II:
+ *
+ * Another method fixes the problem cheaper: Use a small DC-offset in
+ * the filter calculations. Now the signals converge not against 0,
+ * but against the offset. The constant offset is invisible from the
+ * outside world (i.e. it does not appear at the output. There is a
+ * very small turn-on transient response, which should not cause
+ * problems.
+ */
+
+
+//#define DC_OFFSET 0
+#define DC_OFFSET 1e-8
+//#define DC_OFFSET 0.001f
+typedef struct _fluid_allpass fluid_allpass;
+typedef struct _fluid_comb fluid_comb;
+
+struct _fluid_allpass {
+ fluid_real_t feedback;
+ fluid_real_t *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+void fluid_allpass_init(fluid_allpass* allpass);
+void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val);
+fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass);
+
+static void
+fluid_allpass_setbuffer(fluid_allpass* allpass, int size)
+{
+ allpass->bufidx = 0;
+ allpass->buffer = FLUID_ARRAY(fluid_real_t,size);
+ allpass->bufsize = size;
+}
+
+static void
+fluid_allpass_release(fluid_allpass* allpass)
+{
+ FLUID_FREE(allpass->buffer);
+}
+
+void
+fluid_allpass_init(fluid_allpass* allpass)
+{
+ int i;
+ int len = allpass->bufsize;
+ fluid_real_t* buf = allpass->buffer;
+ for (i = 0; i < len; i++) {
+ buf[i] = DC_OFFSET; /* this is not 100 % correct. */
+ }
+}
+
+void
+fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val)
+{
+ allpass->feedback = val;
+}
+
+fluid_real_t
+fluid_allpass_getfeedback(fluid_allpass* allpass)
+{
+ return allpass->feedback;
+}
+
+#define fluid_allpass_process(_allpass, _input) \
+{ \
+ fluid_real_t output; \
+ fluid_real_t bufout; \
+ bufout = _allpass.buffer[_allpass.bufidx]; \
+ output = bufout-_input; \
+ _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \
+ if (++_allpass.bufidx >= _allpass.bufsize) { \
+ _allpass.bufidx = 0; \
+ } \
+ _input = output; \
+}
+
+/* fluid_real_t fluid_allpass_process(fluid_allpass* allpass, fluid_real_t input) */
+/* { */
+/* fluid_real_t output; */
+/* fluid_real_t bufout; */
+/* bufout = allpass->buffer[allpass->bufidx]; */
+/* undenormalise(bufout); */
+/* output = -input + bufout; */
+/* allpass->buffer[allpass->bufidx] = input + (bufout * allpass->feedback); */
+/* if (++allpass->bufidx >= allpass->bufsize) { */
+/* allpass->bufidx = 0; */
+/* } */
+/* return output; */
+/* } */
+
+struct _fluid_comb {
+ fluid_real_t feedback;
+ fluid_real_t filterstore;
+ fluid_real_t damp1;
+ fluid_real_t damp2;
+ fluid_real_t *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+void fluid_comb_setbuffer(fluid_comb* comb, int size);
+void fluid_comb_release(fluid_comb* comb);
+void fluid_comb_init(fluid_comb* comb);
+void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val);
+fluid_real_t fluid_comb_getdamp(fluid_comb* comb);
+void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val);
+fluid_real_t fluid_comb_getfeedback(fluid_comb* comb);
+
+void
+fluid_comb_setbuffer(fluid_comb* comb, int size)
+{
+ comb->filterstore = 0;
+ comb->bufidx = 0;
+ comb->buffer = FLUID_ARRAY(fluid_real_t,size);
+ comb->bufsize = size;
+}
+
+void
+fluid_comb_release(fluid_comb* comb)
+{
+ FLUID_FREE(comb->buffer);
+}
+
+void
+fluid_comb_init(fluid_comb* comb)
+{
+ int i;
+ fluid_real_t* buf = comb->buffer;
+ int len = comb->bufsize;
+ for (i = 0; i < len; i++) {
+ buf[i] = DC_OFFSET; /* This is not 100 % correct. */
+ }
+}
+
+void
+fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val)
+{
+ comb->damp1 = val;
+ comb->damp2 = 1 - val;
+}
+
+fluid_real_t
+fluid_comb_getdamp(fluid_comb* comb)
+{
+ return comb->damp1;
+}
+
+void
+fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val)
+{
+ comb->feedback = val;
+}
+
+fluid_real_t
+fluid_comb_getfeedback(fluid_comb* comb)
+{
+ return comb->feedback;
+}
+
+#define fluid_comb_process(_comb, _input, _output) \
+{ \
+ fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \
+ _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \
+ _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \
+ if (++_comb.bufidx >= _comb.bufsize) { \
+ _comb.bufidx = 0; \
+ } \
+ _output += _tmp; \
+}
+
+/* fluid_real_t fluid_comb_process(fluid_comb* comb, fluid_real_t input) */
+/* { */
+/* fluid_real_t output; */
+
+/* output = comb->buffer[comb->bufidx]; */
+/* undenormalise(output); */
+/* comb->filterstore = (output * comb->damp2) + (comb->filterstore * comb->damp1); */
+/* undenormalise(comb->filterstore); */
+/* comb->buffer[comb->bufidx] = input + (comb->filterstore * comb->feedback); */
+/* if (++comb->bufidx >= comb->bufsize) { */
+/* comb->bufidx = 0; */
+/* } */
+
+/* return output; */
+/* } */
+
+#define numcombs 8
+#define numallpasses 4
+#define fixedgain 0.015f
+#define scalewet 3.0f
+#define scaledamp 1.0f
+#define scaleroom 0.28f
+#define offsetroom 0.7f
+#define initialroom 0.5f
+#define initialdamp 0.2f
+#define initialwet 1
+#define initialdry 0
+#define initialwidth 1
+#define stereospread 23
+
+/*
+ These values assume 44.1KHz sample rate
+ they will probably be OK for 48KHz sample rate
+ but would need scaling for 96KHz (or other) sample rates.
+ The values were obtained by listening tests.
+*/
+#define combtuningL1 1116
+#define combtuningR1 (1116 + stereospread)
+#define combtuningL2 1188
+#define combtuningR2 (1188 + stereospread)
+#define combtuningL3 1277
+#define combtuningR3 (1277 + stereospread)
+#define combtuningL4 1356
+#define combtuningR4 (1356 + stereospread)
+#define combtuningL5 1422
+#define combtuningR5 (1422 + stereospread)
+#define combtuningL6 1491
+#define combtuningR6 (1491 + stereospread)
+#define combtuningL7 1557
+#define combtuningR7 (1557 + stereospread)
+#define combtuningL8 1617
+#define combtuningR8 (1617 + stereospread)
+#define allpasstuningL1 556
+#define allpasstuningR1 (556 + stereospread)
+#define allpasstuningL2 441
+#define allpasstuningR2 (441 + stereospread)
+#define allpasstuningL3 341
+#define allpasstuningR3 (341 + stereospread)
+#define allpasstuningL4 225
+#define allpasstuningR4 (225 + stereospread)
+
+struct _fluid_revmodel_t {
+ fluid_real_t roomsize;
+ fluid_real_t damp;
+ fluid_real_t wet, wet1, wet2;
+ fluid_real_t width;
+ fluid_real_t gain;
+ /*
+ The following are all declared inline
+ to remove the need for dynamic allocation
+ with its subsequent error-checking messiness
+ */
+ /* Comb filters */
+ fluid_comb combL[numcombs];
+ fluid_comb combR[numcombs];
+ /* Allpass filters */
+ fluid_allpass allpassL[numallpasses];
+ fluid_allpass allpassR[numallpasses];
+};
+
+static void fluid_revmodel_update(fluid_revmodel_t* rev);
+static void fluid_revmodel_init(fluid_revmodel_t* rev);
+void fluid_set_revmodel_buffers(fluid_revmodel_t* rev, fluid_real_t sample_rate);
+
+fluid_revmodel_t*
+new_fluid_revmodel(fluid_real_t sample_rate)
+{
+ fluid_revmodel_t* rev;
+ rev = FLUID_NEW(fluid_revmodel_t);
+ if (rev == NULL) {
+ return NULL;
+ }
+
+ fluid_set_revmodel_buffers(rev, sample_rate);
+
+ /* Set default values */
+ fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f);
+ fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f);
+ fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f);
+ fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f);
+ fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f);
+ fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f);
+ fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f);
+ fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f);
+
+ rev->gain = fixedgain;
+ fluid_revmodel_set(rev,FLUID_REVMODEL_SET_ALL,initialroom,initialdamp,initialwidth,initialwet);
+
+ return rev;
+}
+
+void
+delete_fluid_revmodel(fluid_revmodel_t* rev)
+{
+ int i;
+ for (i = 0; i < numcombs;i++) {
+ fluid_comb_release(&rev->combL[i]);
+ fluid_comb_release(&rev->combR[i]);
+ }
+ for (i = 0; i < numallpasses; i++) {
+ fluid_allpass_release(&rev->allpassL[i]);
+ fluid_allpass_release(&rev->allpassR[i]);
+ }
+
+ FLUID_FREE(rev);
+}
+
+void
+fluid_set_revmodel_buffers(fluid_revmodel_t* rev, fluid_real_t sample_rate) {
+
+ float srfactor = sample_rate/44100.0f;
+
+ fluid_comb_setbuffer(&rev->combL[0], combtuningL1*srfactor);
+ fluid_comb_setbuffer(&rev->combR[0], combtuningR1*srfactor);
+ fluid_comb_setbuffer(&rev->combL[1], combtuningL2*srfactor);
+ fluid_comb_setbuffer(&rev->combR[1], combtuningR2*srfactor);
+ fluid_comb_setbuffer(&rev->combL[2], combtuningL3*srfactor);
+ fluid_comb_setbuffer(&rev->combR[2], combtuningR3*srfactor);
+ fluid_comb_setbuffer(&rev->combL[3], combtuningL4*srfactor);
+ fluid_comb_setbuffer(&rev->combR[3], combtuningR4*srfactor);
+ fluid_comb_setbuffer(&rev->combL[4], combtuningL5*srfactor);
+ fluid_comb_setbuffer(&rev->combR[4], combtuningR5*srfactor);
+ fluid_comb_setbuffer(&rev->combL[5], combtuningL6*srfactor);
+ fluid_comb_setbuffer(&rev->combR[5], combtuningR6*srfactor);
+ fluid_comb_setbuffer(&rev->combL[6], combtuningL7*srfactor);
+ fluid_comb_setbuffer(&rev->combR[6], combtuningR7*srfactor);
+ fluid_comb_setbuffer(&rev->combL[7], combtuningL8*srfactor);
+ fluid_comb_setbuffer(&rev->combR[7], combtuningR8*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassL[0], allpasstuningL1*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassR[0], allpasstuningR1*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassL[1], allpasstuningL2*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassR[1], allpasstuningR2*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassL[2], allpasstuningL3*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassR[2], allpasstuningR3*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassL[3], allpasstuningL4*srfactor);
+ fluid_allpass_setbuffer(&rev->allpassR[3], allpasstuningR4*srfactor);
+
+ /* Clear all buffers */
+ fluid_revmodel_init(rev);
+}
+
+
+static void
+fluid_revmodel_init(fluid_revmodel_t* rev)
+{
+ int i;
+ for (i = 0; i < numcombs;i++) {
+ fluid_comb_init(&rev->combL[i]);
+ fluid_comb_init(&rev->combR[i]);
+ }
+ for (i = 0; i < numallpasses; i++) {
+ fluid_allpass_init(&rev->allpassL[i]);
+ fluid_allpass_init(&rev->allpassR[i]);
+ }
+}
+
+void
+fluid_revmodel_reset(fluid_revmodel_t* rev)
+{
+ fluid_revmodel_init(rev);
+}
+
+void
+fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int i, k = 0;
+ fluid_real_t outL, outR, input;
+
+ for (k = 0; k < FLUID_BUFSIZE; k++) {
+
+ outL = outR = 0;
+
+ /* The original Freeverb code expects a stereo signal and 'input'
+ * is set to the sum of the left and right input sample. Since
+ * this code works on a mono signal, 'input' is set to twice the
+ * input sample. */
+ input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
+
+ /* Accumulate comb filters in parallel */
+ for (i = 0; i < numcombs; i++) {
+ fluid_comb_process(rev->combL[i], input, outL);
+ fluid_comb_process(rev->combR[i], input, outR);
+ }
+ /* Feed through allpasses in series */
+ for (i = 0; i < numallpasses; i++) {
+ fluid_allpass_process(rev->allpassL[i], outL);
+ fluid_allpass_process(rev->allpassR[i], outR);
+ }
+
+ /* Remove the DC offset */
+ outL -= DC_OFFSET;
+ outR -= DC_OFFSET;
+
+ /* Calculate output REPLACING anything already there */
+ left_out[k] = outL * rev->wet1 + outR * rev->wet2;
+ right_out[k] = outR * rev->wet1 + outL * rev->wet2;
+ }
+}
+
+void
+fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int i, k = 0;
+ fluid_real_t outL, outR, input;
+
+ for (k = 0; k < FLUID_BUFSIZE; k++) {
+
+ outL = outR = 0;
+
+ /* The original Freeverb code expects a stereo signal and 'input'
+ * is set to the sum of the left and right input sample. Since
+ * this code works on a mono signal, 'input' is set to twice the
+ * input sample. */
+ input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
+
+ /* Accumulate comb filters in parallel */
+ for (i = 0; i < numcombs; i++) {
+ fluid_comb_process(rev->combL[i], input, outL);
+ fluid_comb_process(rev->combR[i], input, outR);
+ }
+ /* Feed through allpasses in series */
+ for (i = 0; i < numallpasses; i++) {
+ fluid_allpass_process(rev->allpassL[i], outL);
+ fluid_allpass_process(rev->allpassR[i], outR);
+ }
+
+ /* Remove the DC offset */
+ outL -= DC_OFFSET;
+ outR -= DC_OFFSET;
+
+ /* Calculate output MIXING with anything already there */
+ left_out[k] += outL * rev->wet1 + outR * rev->wet2;
+ right_out[k] += outR * rev->wet1 + outL * rev->wet2;
+ }
+}
+
+static void
+fluid_revmodel_update(fluid_revmodel_t* rev)
+{
+ /* Recalculate internal values after parameter change */
+ int i;
+
+ rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f);
+ rev->wet2 = rev->wet * ((1.0f - rev->width) / 2.0f);
+
+ for (i = 0; i < numcombs; i++) {
+ fluid_comb_setfeedback(&rev->combL[i], rev->roomsize);
+ fluid_comb_setfeedback(&rev->combR[i], rev->roomsize);
+ }
+
+ for (i = 0; i < numcombs; i++) {
+ fluid_comb_setdamp(&rev->combL[i], rev->damp);
+ fluid_comb_setdamp(&rev->combR[i], rev->damp);
+ }
+}
+
+/**
+ * Set one or more reverb parameters.
+ * @param rev Reverb instance
+ * @param set One or more flags from #fluid_revmodel_set_t indicating what
+ * parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters)
+ * @param roomsize Reverb room size
+ * @param damping Reverb damping
+ * @param width Reverb width
+ * @param level Reverb level
+ */
+void
+fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize,
+ float damping, float width, float level)
+{
+ if (set & FLUID_REVMODEL_SET_ROOMSIZE)
+ rev->roomsize = (roomsize * scaleroom) + offsetroom;
+
+ if (set & FLUID_REVMODEL_SET_DAMPING)
+ rev->damp = damping * scaledamp;
+
+ if (set & FLUID_REVMODEL_SET_WIDTH)
+ rev->width = width;
+
+ if (set & FLUID_REVMODEL_SET_LEVEL)
+ {
+ fluid_clip(level, 0.0f, 1.0f);
+ rev->wet = level * scalewet;
+ }
+
+ fluid_revmodel_update (rev);
+}
+
+void
+fluid_revmodel_samplerate_change(fluid_revmodel_t* rev, fluid_real_t sample_rate) {
+ int i;
+ for (i = 0; i < numcombs;i++) {
+ fluid_comb_release(&rev->combL[i]);
+ fluid_comb_release(&rev->combR[i]);
+ }
+ for (i = 0; i < numallpasses; i++) {
+ fluid_allpass_release(&rev->allpassL[i]);
+ fluid_allpass_release(&rev->allpassR[i]);
+ }
+ fluid_set_revmodel_buffers(rev, sample_rate);
+}
diff --git a/libs/fluidsynth/src/fluid_rev.h b/libs/fluidsynth/src/fluid_rev.h
new file mode 100644
index 0000000000..f977352cf5
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rev.h
@@ -0,0 +1,73 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_REV_H
+#define _FLUID_REV_H
+
+#include "fluidsynth_priv.h"
+
+typedef struct _fluid_revmodel_t fluid_revmodel_t;
+
+
+/** Flags for fluid_revmodel_set() */
+typedef enum
+{
+ FLUID_REVMODEL_SET_ROOMSIZE = 1 << 0,
+ FLUID_REVMODEL_SET_DAMPING = 1 << 1,
+ FLUID_REVMODEL_SET_WIDTH = 1 << 2,
+ FLUID_REVMODEL_SET_LEVEL = 1 << 3
+} fluid_revmodel_set_t;
+
+/** Value for fluid_revmodel_set() which sets all reverb parameters. */
+#define FLUID_REVMODEL_SET_ALL 0x0F
+
+/*
+ * reverb preset
+ */
+typedef struct _fluid_revmodel_presets_t {
+ char* name;
+ fluid_real_t roomsize;
+ fluid_real_t damp;
+ fluid_real_t width;
+ fluid_real_t level;
+} fluid_revmodel_presets_t;
+
+
+/*
+ * reverb
+ */
+fluid_revmodel_t* new_fluid_revmodel(fluid_real_t sample_rate);
+void delete_fluid_revmodel(fluid_revmodel_t* rev);
+
+void fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out);
+
+void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out);
+
+void fluid_revmodel_reset(fluid_revmodel_t* rev);
+
+void fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize,
+ float damping, float width, float level);
+
+void fluid_revmodel_samplerate_change(fluid_revmodel_t* rev, fluid_real_t sample_rate);
+
+#endif /* _FLUID_REV_H */
diff --git a/libs/fluidsynth/src/fluid_ringbuffer.c b/libs/fluidsynth/src/fluid_ringbuffer.c
new file mode 100644
index 0000000000..f6c06dd76d
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_ringbuffer.c
@@ -0,0 +1,89 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+/*
+ * Josh Green <josh@resonance.org>
+ * 2009-05-28
+ */
+
+#include "fluid_ringbuffer.h"
+#include "fluidsynth_priv.h"
+
+
+/**
+ * Create a lock free queue with a fixed maximum count and size of elements.
+ * @param count Count of elements in queue (fixed max number of queued elements)
+ * @return New lock free queue or NULL if out of memory (error message logged)
+ *
+ * Lockless FIFO queues don't use any locking mechanisms and can therefore be
+ * advantageous in certain situations, such as passing data between a lower
+ * priority thread and a higher "real time" thread, without potential lock
+ * contention which could stall the high priority thread. Note that there may
+ * only be one producer thread and one consumer thread.
+ */
+fluid_ringbuffer_t *
+new_fluid_ringbuffer (int count, int elementsize)
+{
+ fluid_ringbuffer_t *queue;
+
+ fluid_return_val_if_fail (count > 0, NULL);
+
+ queue = FLUID_NEW (fluid_ringbuffer_t);
+
+ if (!queue)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ queue->array = FLUID_MALLOC (elementsize * count);
+
+ if (!queue->array)
+ {
+ FLUID_FREE (queue);
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ /* Clear array, in case dynamic pointer reclaiming is being done */
+ FLUID_MEMSET (queue->array, 0, elementsize * count);
+
+ queue->totalcount = count;
+ queue->elementsize = elementsize;
+ queue->count = 0;
+ queue->in = 0;
+ queue->out = 0;
+
+ return (queue);
+}
+
+/**
+ * Free an event queue.
+ * @param queue Lockless queue instance
+ *
+ * Care must be taken when freeing a queue, to ensure that the consumer and
+ * producer threads will no longer access it.
+ */
+void
+delete_fluid_ringbuffer (fluid_ringbuffer_t *queue)
+{
+ FLUID_FREE (queue->array);
+ FLUID_FREE (queue);
+}
diff --git a/libs/fluidsynth/src/fluid_ringbuffer.h b/libs/fluidsynth/src/fluid_ringbuffer.h
new file mode 100644
index 0000000000..bd43f8a250
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_ringbuffer.h
@@ -0,0 +1,128 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_RINGBUFFER_H
+#define _FLUID_RINGBUFFER_H
+
+#include "fluid_sys.h"
+
+/**
+ * Lockless event queue instance.
+ */
+struct _fluid_ringbuffer_t
+{
+ char *array; /**< Queue array of arbitrary size elements */
+ int totalcount; /**< Total count of elements in array */
+ int count; /**< Current count of elements */
+ int in; /**< Index in queue to store next pushed element */
+ int out; /**< Index in queue of next popped element */
+ int elementsize; /**< Size of each element */
+ void* userdata;
+};
+
+typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t;
+
+
+fluid_ringbuffer_t *new_fluid_ringbuffer (int count, int elementsize);
+void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue);
+
+/**
+ * Get pointer to next input array element in queue.
+ * @param queue Lockless queue instance
+ * @param count Normally zero, or more if you need to push several items at once
+ * @return Pointer to array element in queue to store data to or NULL if queue is full
+ *
+ * This function along with fluid_ringbuffer_next_inptr() form a queue "push"
+ * operation and is split into 2 functions to avoid an element copy. Note that
+ * the returned array element pointer may contain the data of a previous element
+ * if the queue has wrapped around. This can be used to reclaim pointers to
+ * allocated memory, etc.
+ */
+static FLUID_INLINE void*
+fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset)
+{
+ return fluid_atomic_int_get (&queue->count) + offset >= queue->totalcount ? NULL
+ : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount);
+}
+
+/**
+ * Advance the input queue index to complete a "push" operation.
+ * @param queue Lockless queue instance
+ * @param count Normally one, or more if you need to push several items at once
+ *
+ * This function along with fluid_ringbuffer_get_inptr() form a queue "push"
+ * operation and is split into 2 functions to avoid element copy.
+ */
+static FLUID_INLINE void
+fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count)
+{
+ fluid_atomic_int_add (&queue->count, count);
+
+ queue->in += count;
+ if (queue->in >= queue->totalcount)
+ queue->in -= queue->totalcount;
+}
+
+/**
+ * Get amount of items currently in queue
+ * @param queue Lockless queue instance
+ * @return amount of items currently in queue
+ */
+static FLUID_INLINE int
+fluid_ringbuffer_get_count (fluid_ringbuffer_t *queue)
+{
+ return fluid_atomic_int_get (&queue->count);
+}
+
+
+/**
+ * Get pointer to next output array element in queue.
+ * @param queue Lockless queue instance
+ * @return Pointer to array element data in the queue or NULL if empty, can only
+ * be used up until fluid_ringbuffer_next_outptr() is called.
+ *
+ * This function along with fluid_ringbuffer_next_outptr() form a queue "pop"
+ * operation and is split into 2 functions to avoid an element copy.
+ */
+static FLUID_INLINE void*
+fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue)
+{
+ return fluid_ringbuffer_get_count(queue) == 0 ? NULL
+ : queue->array + queue->elementsize * queue->out;
+}
+
+
+/**
+ * Advance the output queue index to complete a "pop" operation.
+ * @param queue Lockless queue instance
+ *
+ * This function along with fluid_ringbuffer_get_outptr() form a queue "pop"
+ * operation and is split into 2 functions to avoid an element copy.
+ */
+static FLUID_INLINE void
+fluid_ringbuffer_next_outptr (fluid_ringbuffer_t *queue)
+{
+ fluid_atomic_int_add (&queue->count, -1);
+
+ if (++queue->out == queue->totalcount)
+ queue->out = 0;
+}
+
+#endif /* _FLUID_ringbuffer_H */
diff --git a/libs/fluidsynth/src/fluid_rvoice.c b/libs/fluidsynth/src/fluid_rvoice.c
new file mode 100644
index 0000000000..ba8da98333
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rvoice.c
@@ -0,0 +1,664 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_rvoice.h"
+#include "fluid_conv.h"
+#include "fluid_sys.h"
+
+/**
+ * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise
+ */
+static inline int
+fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
+{
+ fluid_real_t target_amp; /* target amplitude */
+
+ if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY)
+ return -1; /* The volume amplitude is in hold phase. No sound is produced. */
+
+ if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK)
+ {
+ /* the envelope is in the attack section: ramp linearly to max value.
+ * A positive modlfo_to_vol should increase volume (negative attenuation).
+ */
+ target_amp = fluid_atten2amp (voice->dsp.attenuation)
+ * fluid_cb2amp (fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol)
+ * fluid_adsr_env_get_val(&voice->envlfo.volenv);
+ }
+ else
+ {
+ fluid_real_t amplitude_that_reaches_noise_floor;
+ fluid_real_t amp_max;
+
+ target_amp = fluid_atten2amp (voice->dsp.attenuation)
+ * fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv))
+ + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol);
+
+ /* We turn off a voice, if the volume has dropped low enough. */
+
+ /* A voice can be turned off, when an estimate for the volume
+ * (upper bound) falls below that volume, that will drop the
+ * sample below the noise floor.
+ */
+
+ /* If the loop amplitude is known, we can use it if the voice loop is within
+ * the sample loop
+ */
+
+ /* Is the playing pointer already in the loop? */
+ if (voice->dsp.has_looped)
+ amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop;
+ else
+ amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop;
+
+ /* voice->attenuation_min is a lower boundary for the attenuation
+ * now and in the future (possibly 0 in the worst case). Now the
+ * amplitude of sample and volenv cannot exceed amp_max (since
+ * volenv_val can only drop):
+ */
+
+ amp_max = fluid_atten2amp (voice->dsp.min_attenuation_cB) *
+ fluid_adsr_env_get_val(&voice->envlfo.volenv);
+
+ /* And if amp_max is already smaller than the known amplitude,
+ * which will attenuate the sample below the noise floor, then we
+ * can safely turn off the voice. Duh. */
+ if (amp_max < amplitude_that_reaches_noise_floor)
+ {
+ return 0;
+ }
+ }
+
+ /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */
+ voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE;
+
+ fluid_check_fpe ("voice_write amplitude calculation");
+
+ /* no volume and not changing? - No need to process */
+ if ((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f))
+ return -1;
+
+ return 1;
+}
+
+
+/* these should be the absolute minimum that FluidSynth can deal with */
+#define FLUID_MIN_LOOP_SIZE 2
+#define FLUID_MIN_LOOP_PAD 0
+
+#define FLUID_SAMPLESANITY_CHECK (1 << 0)
+#define FLUID_SAMPLESANITY_STARTUP (1 << 1)
+
+/* Purpose:
+ *
+ * Make sure, that sample start / end point and loop points are in
+ * proper order. When starting up, calculate the initial phase.
+ * TODO: Investigate whether this can be moved from rvoice to voice.
+ */
+static void
+fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice)
+{
+ int min_index_nonloop=(int) voice->dsp.sample->start;
+ int max_index_nonloop=(int) voice->dsp.sample->end;
+
+ /* make sure we have enough samples surrounding the loop */
+ int min_index_loop=(int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD;
+ int max_index_loop=(int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */
+ fluid_check_fpe("voice_check_sample_sanity start");
+
+ if (!voice->dsp.check_sample_sanity_flag){
+ return;
+ }
+
+#if 0
+ printf("Sample from %i to %i\n",voice->dsp.sample->start, voice->dsp.sample->end);
+ printf("Sample loop from %i %i\n",voice->dsp.sample->loopstart, voice->dsp.sample->loopend);
+ printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end);
+ printf("Playback loop from %i to %i\n",voice->dsp.loopstart, voice->dsp.loopend);
+#endif
+
+ /* Keep the start point within the sample data */
+ if (voice->dsp.start < min_index_nonloop){
+ voice->dsp.start = min_index_nonloop;
+ } else if (voice->dsp.start > max_index_nonloop){
+ voice->dsp.start = max_index_nonloop;
+ }
+
+ /* Keep the end point within the sample data */
+ if (voice->dsp.end < min_index_nonloop){
+ voice->dsp.end = min_index_nonloop;
+ } else if (voice->dsp.end > max_index_nonloop){
+ voice->dsp.end = max_index_nonloop;
+ }
+
+ /* Keep start and end point in the right order */
+ if (voice->dsp.start > voice->dsp.end){
+ int temp = voice->dsp.start;
+ voice->dsp.start = voice->dsp.end;
+ voice->dsp.end = temp;
+ /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */
+ }
+
+ /* Zero length? */
+ if (voice->dsp.start == voice->dsp.end){
+ fluid_rvoice_voiceoff(voice);
+ return;
+ }
+
+ if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)
+ || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) {
+ /* Keep the loop start point within the sample data */
+ if (voice->dsp.loopstart < min_index_loop){
+ voice->dsp.loopstart = min_index_loop;
+ } else if (voice->dsp.loopstart > max_index_loop){
+ voice->dsp.loopstart = max_index_loop;
+ }
+
+ /* Keep the loop end point within the sample data */
+ if (voice->dsp.loopend < min_index_loop){
+ voice->dsp.loopend = min_index_loop;
+ } else if (voice->dsp.loopend > max_index_loop){
+ voice->dsp.loopend = max_index_loop;
+ }
+
+ /* Keep loop start and end point in the right order */
+ if (voice->dsp.loopstart > voice->dsp.loopend){
+ int temp = voice->dsp.loopstart;
+ voice->dsp.loopstart = voice->dsp.loopend;
+ voice->dsp.loopend = temp;
+ /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */
+ }
+
+ /* Loop too short? Then don't loop. */
+ if (voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE){
+ voice->dsp.samplemode = FLUID_UNLOOPED;
+ }
+
+ /* The loop points may have changed. Obtain a new estimate for the loop volume. */
+ /* Is the voice loop within the sample loop? */
+ if ((int)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart
+ && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend){
+ /* Is there a valid peak amplitude available for the loop, and can we use it? */
+ if (voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE){
+ voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain;
+ } else {
+ /* Worst case */
+ voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.amplitude_that_reaches_noise_floor_nonloop;
+ };
+ };
+
+ } /* if sample mode is looped */
+
+ /* Run startup specific code (only once, when the voice is started) */
+ if (voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){
+ if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){
+ if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)
+ || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)){
+ voice->dsp.samplemode = FLUID_UNLOOPED;
+ }
+ }
+
+ /* Set the initial phase of the voice (using the result from the
+ start offset modulators). */
+ fluid_phase_set_int(voice->dsp.phase, voice->dsp.start);
+ } /* if startup */
+
+ /* Is this voice run in loop mode, or does it run straight to the
+ end of the waveform data? */
+ if (((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) &&
+ (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE))
+ || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) {
+ /* Yes, it will loop as soon as it reaches the loop point. In
+ * this case we must prevent, that the playback pointer (phase)
+ * happens to end up beyond the 2nd loop point, because the
+ * point has moved. The DSP algorithm is unable to cope with
+ * that situation. So if the phase is beyond the 2nd loop
+ * point, set it to the start of the loop. No way to avoid some
+ * noise here. Note: If the sample pointer ends up -before the
+ * first loop point- instead, then the DSP loop will just play
+ * the sample, enter the loop and proceed as expected => no
+ * actions required.
+ */
+ int index_in_sample = fluid_phase_index(voice->dsp.phase);
+ if (index_in_sample >= voice->dsp.loopend){
+ /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */
+ fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart);
+ }
+ }
+/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */
+
+ /* Sample sanity has been assured. Don't check again, until some
+ sample parameter is changed by modulation. */
+ voice->dsp.check_sample_sanity_flag=0;
+#if 0
+ printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend);
+#endif
+ fluid_check_fpe("voice_check_sample_sanity");
+}
+
+
+/**
+ * Synthesize a voice to a buffer.
+ *
+ * @param voice rvoice to synthesize
+ * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length)
+ * @return Count of samples written to dsp_buf. (-1 means voice is currently
+ * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.)
+ *
+ * Panning, reverb and chorus are processed separately. The dsp interpolation
+ * routine is in (fluid_dsp_float.c).
+ */
+int
+fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
+{
+ int ticks = voice->envlfo.ticks;
+ int count;
+
+ /******************* sample sanity check **********/
+
+ if (!voice->dsp.sample)
+ return 0;
+ if (voice->dsp.check_sample_sanity_flag)
+ fluid_rvoice_check_sample_sanity(voice);
+
+ /******************* noteoff check ****************/
+
+ if (voice->envlfo.noteoff_ticks != 0 &&
+ voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) {
+ fluid_rvoice_noteoff(voice, 0);
+ }
+
+ voice->envlfo.ticks += FLUID_BUFSIZE;
+
+ /******************* vol env **********************/
+
+ fluid_adsr_env_calc(&voice->envlfo.volenv, 1);
+ fluid_check_fpe ("voice_write vol env");
+ if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED)
+ return 0;
+
+ /******************* mod env **********************/
+
+ fluid_adsr_env_calc(&voice->envlfo.modenv, 0);
+ fluid_check_fpe ("voice_write mod env");
+
+ /******************* lfo **********************/
+
+ fluid_lfo_calc(&voice->envlfo.modlfo, ticks);
+ fluid_check_fpe ("voice_write mod LFO");
+ fluid_lfo_calc(&voice->envlfo.viblfo, ticks);
+ fluid_check_fpe ("voice_write vib LFO");
+
+ /******************* amplitude **********************/
+
+ count = fluid_rvoice_calc_amp(voice);
+ if (count <= 0)
+ return count;
+
+ /******************* phase **********************/
+
+ /* Calculate the number of samples, that the DSP loop advances
+ * through the original waveform with each step in the output
+ * buffer. It is the ratio between the frequencies of original
+ * waveform and output waveform.*/
+ voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch +
+ fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch
+ + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch
+ + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch)
+ / voice->dsp.root_pitch_hz;
+
+ fluid_check_fpe ("voice_write phase calculation");
+
+ /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */
+ if (voice->dsp.phase_incr == 0) voice->dsp.phase_incr = 1;
+
+ voice->dsp.is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE
+ || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE
+ && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE);
+
+ /*********************** run the dsp chain ************************
+ * The sample is mixed with the output buffer.
+ * The buffer has to be filled from 0 to FLUID_BUFSIZE-1.
+ * Depending on the position in the loop and the loop size, this
+ * may require several runs. */
+ voice->dsp.dsp_buf = dsp_buf;
+
+ switch (voice->dsp.interp_method)
+ {
+ case FLUID_INTERP_NONE:
+ count = fluid_rvoice_dsp_interpolate_none (&voice->dsp);
+ break;
+ case FLUID_INTERP_LINEAR:
+ count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp);
+ break;
+ case FLUID_INTERP_4THORDER:
+ default:
+ count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp);
+ break;
+ case FLUID_INTERP_7THORDER:
+ count = fluid_rvoice_dsp_interpolate_7th_order (&voice->dsp);
+ break;
+ }
+ fluid_check_fpe ("voice_write interpolation");
+ if (count == 0)
+ return count;
+
+ /*************** resonant filter ******************/
+ fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,
+ fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
+ fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc);
+
+ fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
+
+ return count;
+}
+
+
+static inline fluid_real_t*
+get_dest_buf(fluid_rvoice_buffers_t* buffers, int index,
+ fluid_real_t** dest_bufs, int dest_bufcount)
+{
+ int j = buffers->bufs[index].mapping;
+ if (j >= dest_bufcount || j < 0) return NULL;
+ return dest_bufs[j];
+}
+
+/**
+ * Mix data down to buffers
+ *
+ * @param buffers Destination buffer(s)
+ * @param dsp_buf Mono sample source
+ * @param samplecount Number of samples to process (no FLUID_BUFSIZE restriction)
+ * @param dest_bufs Array of buffers to mixdown to
+ * @param dest_bufcount Length of dest_bufs
+ */
+void
+fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
+ fluid_real_t* dsp_buf, int samplecount,
+ fluid_real_t** dest_bufs, int dest_bufcount)
+{
+ int bufcount = buffers->count;
+ int i, dsp_i;
+ if (!samplecount || !bufcount || !dest_bufcount)
+ return;
+
+ for (i=0; i < bufcount; i++) {
+ fluid_real_t* buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);
+ fluid_real_t* next_buf;
+ fluid_real_t amp = buffers->bufs[i].amp;
+ if (buf == NULL || amp == 0.0f)
+ continue;
+
+ /* Optimization for centered stereo samples - we can save one
+ multiplication per sample */
+ next_buf = (i+1 >= bufcount ? NULL : get_dest_buf(buffers, i+1, dest_bufs, dest_bufcount));
+ if (next_buf && buffers->bufs[i+1].amp == amp) {
+ for (dsp_i = 0; dsp_i < samplecount; dsp_i++) {
+ fluid_real_t samp = amp * dsp_buf[dsp_i];
+ buf[dsp_i] += samp;
+ next_buf[dsp_i] += samp;
+ }
+ i++;
+ }
+ else {
+ for (dsp_i = 0; dsp_i < samplecount; dsp_i++)
+ buf[dsp_i] += amp * dsp_buf[dsp_i];
+ }
+ }
+}
+
+/**
+ * Initialize buffers up to (and including) bufnum
+ */
+static int
+fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t* buffers, unsigned int bufnum)
+{
+ unsigned int i;
+
+ if (bufnum < buffers->count) return FLUID_OK;
+ if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED;
+
+ for (i = buffers->count; i <= bufnum; i++) {
+ buffers->bufs[bufnum].amp = 0.0f;
+ buffers->bufs[bufnum].mapping = i;
+ }
+ buffers->count = bufnum+1;
+ return FLUID_OK;
+}
+
+
+void
+fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers,
+ unsigned int bufnum, fluid_real_t value)
+{
+ if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
+ return;
+ buffers->bufs[bufnum].amp = value;
+}
+
+void
+fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers,
+ unsigned int bufnum, int mapping)
+{
+ if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
+ return;
+ buffers->bufs[bufnum].mapping = mapping;
+}
+
+
+void
+fluid_rvoice_reset(fluid_rvoice_t* voice)
+{
+ voice->dsp.has_looped = 0;
+ voice->envlfo.ticks = 0;
+ voice->envlfo.noteoff_ticks = 0;
+ voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to
+ calculate the volume increment during
+ processing */
+
+ /* mod env initialization*/
+ fluid_adsr_env_reset(&voice->envlfo.modenv);
+
+ /* vol env initialization */
+ fluid_adsr_env_reset(&voice->envlfo.volenv);
+
+ /* Fixme: Retrieve from any other existing
+ voice on this channel to keep LFOs in
+ unison? */
+ fluid_lfo_reset(&voice->envlfo.viblfo);
+ fluid_lfo_reset(&voice->envlfo.modlfo);
+
+ /* Clear sample history in filter */
+ fluid_iir_filter_reset(&voice->resonant_filter);
+
+ /* Force setting of the phase at the first DSP loop run
+ * This cannot be done earlier, because it depends on modulators.
+ [DH] Is that comment really true? */
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
+}
+
+
+void
+fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks)
+{
+ if (min_ticks > voice->envlfo.ticks) {
+ /* Delay noteoff */
+ voice->envlfo.noteoff_ticks = min_ticks;
+ return;
+ }
+ voice->envlfo.noteoff_ticks = 0;
+
+ if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) {
+ /* A voice is turned off during the attack section of the volume
+ * envelope. The attack section ramps up linearly with
+ * amplitude. The other sections use logarithmic scaling. Calculate new
+ * volenv_val to achieve equievalent amplitude during the release phase
+ * for seamless volume transition.
+ */
+ if (fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0){
+ fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol;
+ fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * pow (10.0, lfo / -200);
+ fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1);
+ fluid_clip (env_value, 0.0, 1.0);
+ fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);
+ }
+ }
+ fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
+ fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
+}
+
+
+void
+fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->dsp.output_rate = value;
+}
+
+void
+fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value)
+{
+ voice->dsp.interp_method = value;
+}
+
+void
+fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->dsp.root_pitch_hz = value;
+}
+
+void
+fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->dsp.pitch = value;
+}
+
+
+void
+fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->dsp.attenuation = value;
+}
+
+void
+fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->dsp.min_attenuation_cB = value;
+}
+
+void
+fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->envlfo.viblfo_to_pitch = value;
+}
+
+void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->envlfo.modlfo_to_pitch = value;
+}
+
+void
+fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->envlfo.modlfo_to_vol = value;
+}
+
+void
+fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->envlfo.modlfo_to_fc = value;
+}
+
+void
+fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->envlfo.modenv_to_fc = value;
+}
+
+void
+fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->envlfo.modenv_to_pitch = value;
+}
+
+void
+fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value)
+{
+ voice->dsp.synth_gain = value;
+
+ /* For a looped sample, this value will be overwritten as soon as the
+ * loop parameters are initialized (they may depend on modulators).
+ * This value can be kept, it is a worst-case estimate.
+ */
+ voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value;
+ voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value;
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+}
+
+void
+fluid_rvoice_set_start(fluid_rvoice_t* voice, int value)
+{
+ voice->dsp.start = value;
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+}
+
+void
+fluid_rvoice_set_end(fluid_rvoice_t* voice, int value)
+{
+ voice->dsp.end = value;
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+}
+
+void
+fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value)
+{
+ voice->dsp.loopstart = value;
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+}
+
+void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value)
+{
+ voice->dsp.loopend = value;
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+}
+
+void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value)
+{
+ voice->dsp.samplemode = value;
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+}
+
+
+void
+fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value)
+{
+ voice->dsp.sample = value;
+ if (value) {
+ voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
+ }
+}
+
+void
+fluid_rvoice_voiceoff(fluid_rvoice_t* voice)
+{
+ fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);
+ fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);
+}
+
+
diff --git a/libs/fluidsynth/src/fluid_rvoice.h b/libs/fluidsynth/src/fluid_rvoice.h
new file mode 100644
index 0000000000..4566cb1de3
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rvoice.h
@@ -0,0 +1,200 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_RVOICE_H
+#define _FLUID_RVOICE_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_iir_filter.h"
+#include "fluid_adsr_env.h"
+#include "fluid_lfo.h"
+#include "fluid_phase.h"
+#include "fluid_sfont.h"
+
+typedef struct _fluid_rvoice_envlfo_t fluid_rvoice_envlfo_t;
+typedef struct _fluid_rvoice_dsp_t fluid_rvoice_dsp_t;
+typedef struct _fluid_rvoice_buffers_t fluid_rvoice_buffers_t;
+typedef struct _fluid_rvoice_t fluid_rvoice_t;
+
+/* Smallest amplitude that can be perceived (full scale is +/- 0.5)
+ * 16 bits => 96+4=100 dB dynamic range => 0.00001
+ * 0.00001 * 2 is approximately 0.00003 :)
+ */
+#define FLUID_NOISE_FLOOR 0.00003
+
+
+enum fluid_loop {
+ FLUID_UNLOOPED = 0,
+ FLUID_LOOP_DURING_RELEASE = 1,
+ FLUID_NOTUSED = 2,
+ FLUID_LOOP_UNTIL_RELEASE = 3
+};
+
+/**
+ * rvoice ticks-based parameters
+ * These parameters must be updated even if the voice is currently quiet.
+ */
+struct _fluid_rvoice_envlfo_t
+{
+ /* Note-off minimum length */
+ unsigned int ticks;
+ unsigned int noteoff_ticks;
+
+ /* vol env */
+ fluid_adsr_env_t volenv;
+
+ /* mod env */
+ fluid_adsr_env_t modenv;
+ fluid_real_t modenv_to_fc;
+ fluid_real_t modenv_to_pitch;
+
+ /* mod lfo */
+ fluid_lfo_t modlfo;
+ fluid_real_t modlfo_to_fc;
+ fluid_real_t modlfo_to_pitch;
+ fluid_real_t modlfo_to_vol;
+
+ /* vib lfo */
+ fluid_lfo_t viblfo;
+ fluid_real_t viblfo_to_pitch;
+};
+
+/**
+ * rvoice parameters needed for dsp interpolation
+ */
+struct _fluid_rvoice_dsp_t
+{
+ /* interpolation method, as in fluid_interp in fluidsynth.h */
+ int interp_method;
+ fluid_sample_t* sample;
+ int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters
+ have to be checked. */
+
+ /* sample and loop start and end points (offset in sample memory). */
+ int start;
+ int end;
+ int loopstart;
+ int loopend; /* Note: first point following the loop (superimposed on loopstart) */
+ enum fluid_loop samplemode;
+
+ /* Stuff needed for phase calculations */
+
+ fluid_real_t pitch; /* the pitch in midicents */
+ fluid_real_t root_pitch_hz;
+ fluid_real_t output_rate;
+
+ /* Stuff needed for amplitude calculations */
+
+ int has_looped; /* Flag that is set as soon as the first loop is completed. */
+ fluid_real_t attenuation; /* the attenuation in centibels */
+ fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation
+ * during the lifetime of the voice */
+ fluid_real_t amplitude_that_reaches_noise_floor_nonloop;
+ fluid_real_t amplitude_that_reaches_noise_floor_loop;
+ fluid_real_t synth_gain; /* master gain */
+
+
+ /* Dynamic input to the interpolator below */
+
+ fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */
+
+ fluid_real_t amp; /* current linear amplitude */
+ fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */
+
+ fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */
+ fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */
+ int is_looping;
+
+};
+
+/* Currently left, right, reverb, chorus. To be changed if we
+ ever add surround positioning, or stereo reverb/chorus */
+#define FLUID_RVOICE_MAX_BUFS (4)
+
+/**
+ * rvoice mixer-related parameters
+ */
+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 */
+ } bufs[FLUID_RVOICE_MAX_BUFS];
+};
+
+
+/**
+ * Parameters needed to synthesize a voice
+ */
+struct _fluid_rvoice_t
+{
+ fluid_rvoice_envlfo_t envlfo;
+ fluid_rvoice_dsp_t dsp;
+ fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */
+ fluid_rvoice_buffers_t buffers;
+};
+
+
+int fluid_rvoice_write(fluid_rvoice_t* voice, fluid_real_t *dsp_buf);
+
+void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
+ fluid_real_t* dsp_buf, int samplecount,
+ fluid_real_t** dest_bufs, int dest_bufcount);
+void fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers,
+ unsigned int bufnum, fluid_real_t value);
+void fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers,
+ unsigned int bufnum, int mapping);
+
+/* Dynamic update functions */
+
+void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks);
+void fluid_rvoice_voiceoff(fluid_rvoice_t* voice);
+void fluid_rvoice_reset(fluid_rvoice_t* voice);
+void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t output_rate);
+void fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int interp_method);
+void fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t root_pitch_hz);
+void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
+void fluid_rvoice_set_start(fluid_rvoice_t* voice, int value);
+void fluid_rvoice_set_end(fluid_rvoice_t* voice, int value);
+void fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value);
+void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value);
+void fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value);
+void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value);
+
+/* defined in fluid_rvoice_dsp.c */
+
+void fluid_rvoice_dsp_config (void);
+int fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice);
+int fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice);
+int fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice);
+int fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice);
+
+#endif
diff --git a/libs/fluidsynth/src/fluid_rvoice_dsp.c b/libs/fluidsynth/src/fluid_rvoice_dsp.c
new file mode 100644
index 0000000000..df7da5022d
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rvoice_dsp.c
@@ -0,0 +1,675 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_phase.h"
+#include "fluid_rvoice.h"
+#include "fluid_sys.h"
+
+/* Purpose:
+ *
+ * Interpolates audio data (obtains values between the samples of the original
+ * waveform data).
+ *
+ * Variables loaded from the voice structure (assigned in fluid_voice_write()):
+ * - dsp_data: Pointer to the original waveform data
+ * - dsp_phase: The position in the original waveform data.
+ * This has an integer and a fractional part (between samples).
+ * - dsp_phase_incr: For each output sample, the position in the original
+ * waveform advances by dsp_phase_incr. This also has an integer
+ * part and a fractional part.
+ * If a sample is played at root pitch (no pitch change),
+ * dsp_phase_incr is integer=1 and fractional=0.
+ * - dsp_amp: The current amplitude envelope value.
+ * - dsp_amp_incr: The changing rate of the amplitude envelope.
+ *
+ * A couple of variables are used internally, their results are discarded:
+ * - dsp_i: Index through the output buffer
+ * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length)
+ */
+
+/* Interpolation (find a value between two samples of the original waveform) */
+
+/* Linear interpolation table (2 coefficients centered on 1st) */
+static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2];
+
+/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
+static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4];
+
+/* 7th order interpolation (7 coefficients centered on 3rd) */
+static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7];
+
+
+#define SINC_INTERP_ORDER 7 /* 7th order constant */
+
+
+/* Initializes interpolation tables */
+void fluid_rvoice_dsp_config (void)
+{
+ int i, i2;
+ double x, v;
+ double i_shifted;
+
+ /* Initialize the coefficients for the interpolation. The math comes
+ * from a mail, posted by Olli Niemitalo to the music-dsp mailing
+ * list (I found it in the music-dsp archives
+ * http://www.smartelectronix.com/musicdsp/). */
+
+ for (i = 0; i < FLUID_INTERP_MAX; i++)
+ {
+ x = (double) i / (double) FLUID_INTERP_MAX;
+
+ interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x)));
+ interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5));
+ interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x)));
+ interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0));
+
+ interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x);
+ interp_coeff_linear[i][1] = (fluid_real_t)x;
+ }
+
+ /* i: Offset in terms of whole samples */
+ for (i = 0; i < SINC_INTERP_ORDER; i++)
+ { /* i2: Offset in terms of fractional samples ('subsamples') */
+ for (i2 = 0; i2 < FLUID_INTERP_MAX; i2++)
+ {
+ /* center on middle of table */
+ i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0)
+ + (double)i2 / (double)FLUID_INTERP_MAX;
+
+ /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */
+ if (fabs (i_shifted) > 0.000001)
+ {
+ v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted);
+ /* Hamming window */
+ v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (fluid_real_t)SINC_INTERP_ORDER));
+ }
+ else v = 1.0;
+
+ sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
+ }
+ }
+
+#if 0
+ for (i = 0; i < FLUID_INTERP_MAX; i++)
+ {
+ printf ("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n",
+ i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i],
+ sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]);
+ }
+#endif
+
+ fluid_check_fpe("interpolation table calculation");
+}
+
+/* No interpolation. Just take the sample, which is closest to
+ * the playback pointer. Questionable quality, but very
+ * efficient. */
+int
+fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int end_index;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* voice is currently looping? */
+ looping = voice->is_looping;
+
+ end_index = looping ? voice->loopend - 1 : voice->end;
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
+
+ /* interpolate sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index];
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if not looping (buffer may not be full) */
+ if (!looping) break;
+
+ /* go back to loop start */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+ voice->has_looped = 1;
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+ }
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
+
+/* Straight line interpolation.
+ * Returns number of samples processed (usually FLUID_BUFSIZE but could be
+ * smaller if end of sample occurs).
+ */
+int
+fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int end_index;
+ short int point;
+ fluid_real_t *coeffs;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* voice is currently looping? */
+ looping = voice->is_looping;
+
+ /* last index before 2nd interpolation point must be specially handled */
+ end_index = (looping ? voice->loopend - 1 : voice->end) - 1;
+
+ /* 2nd interpolation point to use at end of loop or sample */
+ if (looping) point = dsp_data[voice->loopstart]; /* loop start */
+ else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+
+ /* interpolate the sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ + coeffs[1] * dsp_data[dsp_phase_index+1]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if buffer filled */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index++; /* we're now interpolating the last point */
+
+ /* interpolate within last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ + coeffs[1] * point);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr; /* increment amplitude */
+ }
+
+ if (!looping) break; /* break out if not looping (end of sample) */
+
+ /* go back to loop start (if past */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+ voice->has_looped = 1;
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index--; /* set end back to second to last sample point */
+ }
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
+
+/* 4th order (cubic) interpolation.
+ * Returns number of samples processed (usually FLUID_BUFSIZE but could be
+ * smaller if end of sample occurs).
+ */
+int
+fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int start_index, end_index;
+ short int start_point, end_point1, end_point2;
+ fluid_real_t *coeffs;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* voice is currently looping? */
+ looping = voice->is_looping;
+
+ /* last index before 4th interpolation point must be specially handled */
+ end_index = (looping ? voice->loopend - 1 : voice->end) - 2;
+
+ if (voice->has_looped) /* set start_index and start point if looped or not */
+ {
+ start_index = voice->loopstart;
+ start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */
+ }
+ else
+ {
+ start_index = voice->start;
+ start_point = dsp_data[voice->start]; /* just duplicate the point */
+ }
+
+ /* get points off the end (loop start if looping, duplicate point if end) */
+ if (looping)
+ {
+ end_point1 = dsp_data[voice->loopstart];
+ end_point2 = dsp_data[voice->loopstart + 1];
+ }
+ else
+ {
+ end_point1 = dsp_data[voice->end];
+ end_point2 = end_point1;
+ }
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+
+ /* interpolate first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * dsp_data[dsp_phase_index+1]
+ + coeffs[3] * dsp_data[dsp_phase_index+2]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* interpolate the sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * dsp_data[dsp_phase_index+1]
+ + coeffs[3] * dsp_data[dsp_phase_index+2]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if buffer filled */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index++; /* we're now interpolating the 2nd to last point */
+
+ /* interpolate within 2nd to last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * dsp_data[dsp_phase_index+1]
+ + coeffs[3] * end_point1);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ end_index++; /* we're now interpolating the last point */
+
+ /* interpolate within the last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * end_point1
+ + coeffs[3] * end_point2);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ if (!looping) break; /* break out if not looping (end of sample) */
+
+ /* go back to loop start */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+
+ if (!voice->has_looped)
+ {
+ voice->has_looped = 1;
+ start_index = voice->loopstart;
+ start_point = dsp_data[voice->loopend - 1];
+ }
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index -= 2; /* set end back to third to last sample point */
+ }
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
+
+/* 7th order interpolation.
+ * Returns number of samples processed (usually FLUID_BUFSIZE but could be
+ * smaller if end of sample occurs).
+ */
+int
+fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int start_index, end_index;
+ short int start_points[3];
+ short int end_points[3];
+ fluid_real_t *coeffs;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on
+ * the 4th sample point */
+ fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000);
+
+ /* voice is currently looping? */
+ looping = voice->is_looping;
+
+ /* last index before 7th interpolation point must be specially handled */
+ end_index = (looping ? voice->loopend - 1 : voice->end) - 3;
+
+ if (voice->has_looped) /* set start_index and start point if looped or not */
+ {
+ start_index = voice->loopstart;
+ start_points[0] = dsp_data[voice->loopend - 1];
+ start_points[1] = dsp_data[voice->loopend - 2];
+ start_points[2] = dsp_data[voice->loopend - 3];
+ }
+ else
+ {
+ start_index = voice->start;
+ start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */
+ start_points[1] = start_points[0];
+ start_points[2] = start_points[0];
+ }
+
+ /* get the 3 points off the end (loop start if looping, duplicate point if end) */
+ if (looping)
+ {
+ end_points[0] = dsp_data[voice->loopstart];
+ end_points[1] = dsp_data[voice->loopstart + 1];
+ end_points[2] = dsp_data[voice->loopstart + 2];
+ }
+ else
+ {
+ end_points[0] = dsp_data[voice->end];
+ end_points[1] = end_points[0];
+ end_points[2] = end_points[0];
+ }
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+
+ /* interpolate first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)start_points[2]
+ + coeffs[1] * (fluid_real_t)start_points[1]
+ + coeffs[2] * (fluid_real_t)start_points[0]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ start_index++;
+
+ /* interpolate 2nd to first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)start_points[1]
+ + coeffs[1] * (fluid_real_t)start_points[0]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ start_index++;
+
+ /* interpolate 3rd to first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)start_points[0]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ start_index -= 2; /* set back to original start index */
+
+
+ /* interpolate the sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if buffer filled */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index++; /* we're now interpolating the 3rd to last point */
+
+ /* interpolate within 3rd to last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)end_points[0]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ end_index++; /* we're now interpolating the 2nd to last point */
+
+ /* interpolate within 2nd to last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)end_points[0]
+ + coeffs[6] * (fluid_real_t)end_points[1]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ end_index++; /* we're now interpolating the last point */
+
+ /* interpolate within last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)end_points[0]
+ + coeffs[5] * (fluid_real_t)end_points[1]
+ + coeffs[6] * (fluid_real_t)end_points[2]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ if (!looping) break; /* break out if not looping (end of sample) */
+
+ /* go back to loop start */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+
+ if (!voice->has_looped)
+ {
+ voice->has_looped = 1;
+ start_index = voice->loopstart;
+ start_points[0] = dsp_data[voice->loopend - 1];
+ start_points[1] = dsp_data[voice->loopend - 2];
+ start_points[2] = dsp_data[voice->loopend - 3];
+ }
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index -= 3; /* set end back to 4th to last sample point */
+ }
+
+ /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on
+ * the 4th sample point (correct back to real value) */
+ fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000);
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
diff --git a/libs/fluidsynth/src/fluid_rvoice_event.c b/libs/fluidsynth/src/fluid_rvoice_event.c
new file mode 100644
index 0000000000..65edb9dacb
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rvoice_event.c
@@ -0,0 +1,293 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_rvoice_event.h"
+#include "fluid_rvoice.h"
+#include "fluid_rvoice_mixer.h"
+#include "fluid_iir_filter.h"
+#include "fluid_lfo.h"
+#include "fluid_adsr_env.h"
+
+#define EVENTFUNC_0(proc, type) \
+ if (event->method == proc) { \
+ proc((type) event->object); \
+ return; }
+
+#define EVENTFUNC_R1(proc, type) \
+ if (event->method == proc) { \
+ if(event->intparam != 0) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \
+ proc((type) event->object, event->realparams[0]); \
+ return; }
+
+#define EVENTFUNC_PTR(proc, type, type2) \
+ if (event->method == proc) { \
+ proc((type) event->object, (type2) event->ptr); \
+ return; }
+
+#define EVENTFUNC_I1(proc, type) \
+ if (event->method == proc) { \
+ if(event->realparams[0] != 0.0f) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \
+ proc((type) event->object, event->intparam); \
+ return; }
+
+#define EVENTFUNC_IR(proc, type) \
+ if (event->method == proc) { \
+ proc((type) event->object, event->intparam, event->realparams[0]); \
+ return; }
+
+#define EVENTFUNC_ALL(proc, type) \
+ if (event->method == proc) { \
+ proc((type) event->object, event->intparam, event->realparams[0], \
+ event->realparams[1], event->realparams[2], event->realparams[3], \
+ event->realparams[4]); \
+ return; }
+
+#define EVENTFUNC_R4(proc, type) \
+ if (event->method == proc) { \
+ proc((type) event->object, event->intparam, event->realparams[0], \
+ event->realparams[1], event->realparams[2], event->realparams[3]); \
+ return; }
+
+void
+fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
+{
+ EVENTFUNC_PTR(fluid_rvoice_mixer_add_voice, fluid_rvoice_mixer_t*, fluid_rvoice_t*);
+ EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*);
+ EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*);
+ EVENTFUNC_0(fluid_rvoice_reset, fluid_rvoice_t*);
+
+ EVENTFUNC_ALL(fluid_adsr_env_set_data, fluid_adsr_env_t*);
+
+ EVENTFUNC_I1(fluid_lfo_set_delay, fluid_lfo_t*);
+ EVENTFUNC_R1(fluid_lfo_set_incr, fluid_lfo_t*);
+
+ EVENTFUNC_R1(fluid_iir_filter_set_fres, fluid_iir_filter_t*);
+ EVENTFUNC_R1(fluid_iir_filter_set_q_dB, fluid_iir_filter_t*);
+
+ EVENTFUNC_IR(fluid_rvoice_buffers_set_mapping, fluid_rvoice_buffers_t*);
+ EVENTFUNC_IR(fluid_rvoice_buffers_set_amp, fluid_rvoice_buffers_t*);
+
+ EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_output_rate, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_root_pitch_hz, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_synth_gain, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_pitch, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_attenuation, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_min_attenuation_cB, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_viblfo_to_pitch, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_pitch, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_vol, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_fc, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_modenv_to_fc, fluid_rvoice_t*);
+ EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*);
+ EVENTFUNC_I1(fluid_rvoice_set_interp_method, fluid_rvoice_t*);
+ EVENTFUNC_I1(fluid_rvoice_set_start, fluid_rvoice_t*);
+ EVENTFUNC_I1(fluid_rvoice_set_end, fluid_rvoice_t*);
+ EVENTFUNC_I1(fluid_rvoice_set_loopstart, fluid_rvoice_t*);
+ EVENTFUNC_I1(fluid_rvoice_set_loopend, fluid_rvoice_t*);
+ EVENTFUNC_I1(fluid_rvoice_set_samplemode, fluid_rvoice_t*);
+ EVENTFUNC_PTR(fluid_rvoice_set_sample, fluid_rvoice_t*, fluid_sample_t*);
+
+ EVENTFUNC_R1(fluid_rvoice_mixer_set_samplerate, fluid_rvoice_mixer_t*);
+ EVENTFUNC_I1(fluid_rvoice_mixer_set_polyphony, fluid_rvoice_mixer_t*);
+ EVENTFUNC_I1(fluid_rvoice_mixer_set_reverb_enabled, fluid_rvoice_mixer_t*);
+ EVENTFUNC_I1(fluid_rvoice_mixer_set_chorus_enabled, fluid_rvoice_mixer_t*);
+ EVENTFUNC_I1(fluid_rvoice_mixer_set_mix_fx, fluid_rvoice_mixer_t*);
+ EVENTFUNC_0(fluid_rvoice_mixer_reset_fx, fluid_rvoice_mixer_t*);
+ EVENTFUNC_0(fluid_rvoice_mixer_reset_reverb, fluid_rvoice_mixer_t*);
+ EVENTFUNC_0(fluid_rvoice_mixer_reset_chorus, fluid_rvoice_mixer_t*);
+ EVENTFUNC_IR(fluid_rvoice_mixer_set_threads, fluid_rvoice_mixer_t*);
+
+ EVENTFUNC_ALL(fluid_rvoice_mixer_set_chorus_params, fluid_rvoice_mixer_t*);
+ EVENTFUNC_R4(fluid_rvoice_mixer_set_reverb_params, fluid_rvoice_mixer_t*);
+
+ FLUID_LOG(FLUID_ERR, "fluid_rvoice_event_dispatch: Unknown method %p to dispatch!", event->method);
+}
+
+
+/**
+ * In order to be able to push more than one event atomically,
+ * use push for all events, then use flush to commit them to the
+ * queue. If threadsafe is false, all events are processed immediately. */
+int
+fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler,
+ void* method, void* object, int intparam,
+ fluid_real_t realparam)
+{
+ fluid_rvoice_event_t* event;
+ fluid_rvoice_event_t local_event;
+ event = handler->is_threadsafe ?
+ fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
+
+ if (event == NULL) {
+ FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
+ return FLUID_FAILED; // Buffer full...
+ }
+
+ event->method = method;
+ event->object = object;
+ event->intparam = intparam;
+ event->realparams[0] = realparam;
+ if (handler->is_threadsafe)
+ handler->queue_stored++;
+ else
+ fluid_rvoice_event_dispatch(event);
+ return FLUID_OK;
+}
+
+
+int
+fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler,
+ void* method, void* object, void* ptr)
+{
+ fluid_rvoice_event_t* event;
+ fluid_rvoice_event_t local_event;
+ event = handler->is_threadsafe ?
+ fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
+
+ if (event == NULL) {
+ FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
+ return FLUID_FAILED; // Buffer full...
+ }
+
+ event->method = method;
+ event->object = object;
+ event->ptr = ptr;
+ if (handler->is_threadsafe)
+ handler->queue_stored++;
+ else
+ fluid_rvoice_event_dispatch(event);
+ return FLUID_OK;
+}
+
+
+int
+fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler,
+ void* method, void* object, int intparam,
+ fluid_real_t r1, fluid_real_t r2,
+ fluid_real_t r3, fluid_real_t r4, fluid_real_t r5)
+{
+ fluid_rvoice_event_t* event;
+ fluid_rvoice_event_t local_event;
+ event = handler->is_threadsafe ?
+ fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
+
+ if (event == NULL) {
+ FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
+ return FLUID_FAILED; // Buffer full...
+ }
+
+ event->method = method;
+ event->object = object;
+ event->intparam = intparam;
+ event->realparams[0] = r1;
+ event->realparams[1] = r2;
+ event->realparams[2] = r3;
+ event->realparams[3] = r4;
+ event->realparams[4] = r5;
+ if (handler->is_threadsafe)
+ handler->queue_stored++;
+ else
+ fluid_rvoice_event_dispatch(event);
+ return FLUID_OK;
+}
+
+
+static void
+finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice)
+{
+ fluid_rvoice_eventhandler_t* eventhandler = userdata;
+ fluid_rvoice_t** vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0);
+ if (vptr == NULL)
+ return; // Buffer full
+ *vptr = rvoice;
+ fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1);
+}
+
+fluid_rvoice_eventhandler_t*
+new_fluid_rvoice_eventhandler(int is_threadsafe, int queuesize,
+ int finished_voices_size, int bufs, int fx_bufs, fluid_real_t sample_rate)
+{
+ fluid_rvoice_eventhandler_t* eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t);
+ if (eventhandler == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ eventhandler->mixer = NULL;
+ eventhandler->queue = NULL;
+ eventhandler->finished_voices = NULL;
+ eventhandler->is_threadsafe = is_threadsafe;
+ eventhandler->queue_stored = 0;
+
+ eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size,
+ sizeof(fluid_rvoice_t*));
+ if (eventhandler->finished_voices == NULL)
+ goto error_recovery;
+
+ eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t));
+ if (eventhandler->queue == NULL)
+ goto error_recovery;
+
+ eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, sample_rate);
+ if (eventhandler->mixer == NULL)
+ goto error_recovery;
+ fluid_rvoice_mixer_set_finished_voices_callback(eventhandler->mixer,
+ finished_voice_callback, eventhandler);
+ return eventhandler;
+
+error_recovery:
+ delete_fluid_rvoice_eventhandler(eventhandler);
+ return NULL;
+}
+
+int
+fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t* handler)
+{
+ return fluid_ringbuffer_get_count(handler->queue);
+}
+
+
+/**
+ * Call fluid_rvoice_event_dispatch for all events in queue
+ * @return number of events dispatched
+ */
+int
+fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t* handler)
+{
+ fluid_rvoice_event_t* event;
+ int result = 0;
+ while (NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) {
+ fluid_rvoice_event_dispatch(event);
+ result++;
+ fluid_ringbuffer_next_outptr(handler->queue);
+ }
+ return result;
+}
+
+
+void
+delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t* handler)
+{
+ if (handler == NULL) return;
+ delete_fluid_rvoice_mixer(handler->mixer);
+ delete_fluid_ringbuffer(handler->queue);
+ delete_fluid_ringbuffer(handler->finished_voices);
+ FLUID_FREE(handler);
+}
diff --git a/libs/fluidsynth/src/fluid_rvoice_event.h b/libs/fluidsynth/src/fluid_rvoice_event.h
new file mode 100644
index 0000000000..e8693bcd21
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rvoice_event.h
@@ -0,0 +1,115 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_RVOICE_EVENT_H
+#define _FLUID_RVOICE_EVENT_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_rvoice_mixer.h"
+#include "fluid_ringbuffer.h"
+
+#define EVENT_REAL_PARAMS (5)
+
+typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t;
+typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t;
+
+struct _fluid_rvoice_event_t {
+ void* method;
+ void* object;
+ void* ptr;
+ int intparam;
+ fluid_real_t realparams[EVENT_REAL_PARAMS];
+};
+
+void fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event);
+
+
+/**
+ * Bridge between the renderer thread and the midi state thread.
+ * If is_threadsafe is true, that means fluid_rvoice_eventhandler_fetch_all
+ * can be called in parallell with fluid_rvoice_eventhandler_push/flush
+ */
+struct _fluid_rvoice_eventhandler_t {
+ int is_threadsafe; /* False for optimal performance, true for atomic operations */
+ fluid_ringbuffer_t* queue; /**< List of fluid_rvoice_event_t */
+ int queue_stored; /**< Extras pushed but not flushed */
+ fluid_ringbuffer_t* finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */
+ fluid_rvoice_mixer_t* mixer;
+};
+
+fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
+ int is_threadsafe, int queuesize, int finished_voices_size, int bufs,
+ int fx_bufs, fluid_real_t sample_rate);
+
+void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*);
+
+int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t*);
+int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t*);
+
+static FLUID_INLINE void
+fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t* handler)
+{
+ if (handler->queue_stored > 0) {
+ fluid_ringbuffer_next_inptr(handler->queue, handler->queue_stored);
+ handler->queue_stored = 0;
+ }
+}
+
+/**
+ * @return next finished voice, or NULL if nothing in queue
+ */
+static FLUID_INLINE fluid_rvoice_t*
+fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t* handler)
+{
+ void* result = fluid_ringbuffer_get_outptr(handler->finished_voices);
+ if (result == NULL) return NULL;
+ result = * (fluid_rvoice_t**) result;
+ fluid_ringbuffer_next_outptr(handler->finished_voices);
+ return result;
+}
+
+
+int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler,
+ void* method, void* object, int intparam,
+ fluid_real_t realparam);
+
+int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler,
+ void* method, void* object, void* ptr);
+
+int fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler,
+ void* method, void* object, int intparam,
+ fluid_real_t r1, fluid_real_t r2,
+ fluid_real_t r3, fluid_real_t r4, fluid_real_t r5);
+
+static FLUID_INLINE void
+fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t* handler,
+ fluid_rvoice_t* rvoice)
+{
+ if (handler->is_threadsafe)
+ fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice,
+ handler->mixer, rvoice);
+ else
+ fluid_rvoice_mixer_add_voice(handler->mixer, rvoice);
+}
+
+
+
+#endif
diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.c b/libs/fluidsynth/src/fluid_rvoice_mixer.c
new file mode 100644
index 0000000000..cc633f510b
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rvoice_mixer.c
@@ -0,0 +1,974 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_rvoice_mixer.h"
+#include "fluid_rvoice.h"
+#include "fluid_sys.h"
+#include "fluid_rev.h"
+#include "fluid_chorus.h"
+#include "fluidsynth_priv.h"
+//#include "fluid_ladspa.h"
+
+#define SYNTH_REVERB_CHANNEL 0
+#define SYNTH_CHORUS_CHANNEL 1
+
+#define ENABLE_MIXER_THREADS 1
+
+// If less than x voices, the thread overhead is larger than the gain,
+// so don't activate the thread(s).
+#define VOICES_PER_THREAD 8
+
+typedef struct _fluid_mixer_buffers_t fluid_mixer_buffers_t;
+
+struct _fluid_mixer_buffers_t {
+ fluid_rvoice_mixer_t* mixer; /**< Owner of object */
+#ifdef ENABLE_MIXER_THREADS
+ fluid_thread_t* thread; /**< Thread object */
+#endif
+
+ fluid_rvoice_t** finished_voices; /* List of voices who have finished */
+ int finished_voice_count;
+
+ int ready; /**< Atomic: buffers are ready for mixing */
+
+ int buf_blocks; /**< Number of blocks allocated in the buffers */
+
+ int buf_count;
+ fluid_real_t** left_buf;
+ fluid_real_t** right_buf;
+
+ int fx_buf_count;
+ fluid_real_t** fx_left_buf;
+ fluid_real_t** fx_right_buf;
+};
+
+typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t;
+
+struct _fluid_mixer_fx_t {
+ fluid_revmodel_t* reverb; /**< Reverb unit */
+ fluid_chorus_t* chorus; /**< Chorus unit */
+ int with_reverb; /**< Should the synth use the built-in reverb unit? */
+ int with_chorus; /**< Should the synth use the built-in chorus unit? */
+ int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */
+};
+
+struct _fluid_rvoice_mixer_t {
+ fluid_mixer_fx_t fx;
+
+ fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */
+ void (*remove_voice_callback)(void*, fluid_rvoice_t*); /**< Used by mixer only: Receive this callback every time a voice is removed */
+ void* remove_voice_callback_userdata;
+
+ fluid_rvoice_t** rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */
+ int polyphony; /**< Read-only: Length of voices array */
+ int active_voices; /**< Read-only: Number of non-null voices */
+ int current_blockcount; /**< Read-only: how many blocks to process this time */
+
+#ifdef LADSPA
+ fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */
+#endif
+
+#ifdef ENABLE_MIXER_THREADS
+// int sleeping_threads; /**< Atomic: number of threads currently asleep */
+// int active_threads; /**< Atomic: number of threads in the thread loop */
+ int threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */
+ int current_rvoice; /**< Atomic: for the threads to know next voice to */
+ fluid_cond_t* wakeup_threads; /**< Signalled when the threads should wake up */
+ fluid_cond_mutex_t* wakeup_threads_m; /**< wakeup_threads mutex companion */
+ fluid_cond_t* thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */
+ fluid_cond_mutex_t* thread_ready_m; /**< thread_ready mutex companion */
+
+ int thread_count; /**< Number of extra mixer threads for multi-core rendering */
+ fluid_mixer_buffers_t* threads; /**< Array of mixer threads (thread_count in length) */
+#endif
+};
+
+static FLUID_INLINE void
+fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer)
+{
+ int i;
+ fluid_profile_ref_var(prof_ref);
+ if (mixer->fx.with_reverb) {
+ if (mixer->fx.mix_fx_to_out) {
+ for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+ fluid_revmodel_processmix(mixer->fx.reverb,
+ &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i],
+ &mixer->buffers.left_buf[0][i],
+ &mixer->buffers.right_buf[0][i]);
+ }
+ else {
+ for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+ fluid_revmodel_processreplace(mixer->fx.reverb,
+ &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i],
+ &mixer->buffers.fx_left_buf[SYNTH_REVERB_CHANNEL][i],
+ &mixer->buffers.fx_right_buf[SYNTH_REVERB_CHANNEL][i]);
+ }
+ fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref);
+ }
+
+ if (mixer->fx.with_chorus) {
+ if (mixer->fx.mix_fx_to_out) {
+ for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+ fluid_chorus_processmix(mixer->fx.chorus,
+ &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i],
+ &mixer->buffers.left_buf[0][i],
+ &mixer->buffers.right_buf[0][i]);
+ }
+ else {
+ for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+ fluid_chorus_processreplace(mixer->fx.chorus,
+ &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i],
+ &mixer->buffers.fx_left_buf[SYNTH_CHORUS_CHANNEL][i],
+ &mixer->buffers.fx_right_buf[SYNTH_CHORUS_CHANNEL][i]);
+ }
+ fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref);
+ }
+
+#ifdef LADSPA
+ /* Run the signal through the LADSPA Fx unit */
+ if (mixer->LADSPA_FxUnit) {
+ int j;
+ FLUID_DECLARE_VLA(fluid_real_t*, left_buf, mixer->buffers.buf_count);
+ FLUID_DECLARE_VLA(fluid_real_t*, right_buf, mixer->buffers.buf_count);
+ FLUID_DECLARE_VLA(fluid_real_t*, fx_left_buf, mixer->buffers.fx_buf_count);
+ FLUID_DECLARE_VLA(fluid_real_t*, fx_right_buf, mixer->buffers.fx_buf_count);
+ for (j=0; j < mixer->buffers.buf_count; j++) {
+ left_buf[j] = mixer->buffers.left_buf[j];
+ right_buf[j] = mixer->buffers.right_buf[j];
+ }
+ for (j=0; j < mixer->buffers.fx_buf_count; j++) {
+ fx_left_buf[j] = mixer->buffers.fx_left_buf[j];
+ fx_right_buf[j] = mixer->buffers.fx_right_buf[j];
+ }
+ for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) {
+ fluid_LADSPA_run(mixer->LADSPA_FxUnit, left_buf, right_buf, fx_left_buf,
+ fx_right_buf);
+ for (j=0; j < mixer->buffers.buf_count; j++) {
+ left_buf[j] += FLUID_BUFSIZE;
+ right_buf[j] += FLUID_BUFSIZE;
+ }
+ for (j=0; j < mixer->buffers.fx_buf_count; j++) {
+ fx_left_buf[j] += FLUID_BUFSIZE;
+ fx_right_buf[j] += FLUID_BUFSIZE;
+ }
+ }
+ fluid_check_fpe("LADSPA");
+ }
+#endif
+}
+
+/**
+ * During rendering, rvoices might be finished. Set this callback
+ * for getting a callback any time the rvoice is finished.
+ */
+void fluid_rvoice_mixer_set_finished_voices_callback(
+ fluid_rvoice_mixer_t* mixer,
+ void (*func)(void*, fluid_rvoice_t*),
+ void* userdata)
+{
+ mixer->remove_voice_callback_userdata = userdata;
+ mixer->remove_voice_callback = func;
+}
+
+
+
+/**
+ * Synthesize one voice and add to buffer.
+ * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means
+ * voice has been finished, removed and possibly replaced with another voice.
+ * @return Number of samples written
+ */
+static int
+fluid_mix_one(fluid_rvoice_t* rvoice, fluid_real_t** bufs, unsigned int bufcount, int blockcount)
+{
+ int i, result = 0;
+
+ FLUID_DECLARE_VLA(fluid_real_t, local_buf, FLUID_BUFSIZE*blockcount);
+
+ for (i=0; i < blockcount; i++) {
+ int s = fluid_rvoice_write(rvoice, &local_buf[FLUID_BUFSIZE*i]);
+ if (s == -1) {
+ s = FLUID_BUFSIZE; /* Voice is quiet, TODO: optimize away memset/mix */
+ FLUID_MEMSET(&local_buf[FLUID_BUFSIZE*i], 0, FLUID_BUFSIZE*sizeof(fluid_real_t));
+ }
+ result += s;
+ if (s < FLUID_BUFSIZE) {
+ break;
+ }
+ }
+ fluid_rvoice_buffers_mix(&rvoice->buffers, local_buf, result, bufs, bufcount);
+
+ return result;
+}
+
+/**
+ * Glue to get fluid_rvoice_buffers_mix what it wants
+ * Note: Make sure outbufs has 2 * (buf_count + fx_buf_count) elements before calling
+ */
+static FLUID_INLINE int
+fluid_mixer_buffers_prepare(fluid_mixer_buffers_t* buffers, fluid_real_t** outbufs)
+{
+ fluid_real_t* reverb_buf, *chorus_buf;
+ int i;
+
+ /* Set up the reverb / chorus buffers only, when the effect is
+ * enabled on synth level. Nonexisting buffers are detected in the
+ * DSP loop. Not sending the reverb / chorus signal saves some time
+ * in that case. */
+ reverb_buf = buffers->mixer->fx.with_reverb ? buffers->fx_left_buf[SYNTH_REVERB_CHANNEL] : NULL;
+ chorus_buf = buffers->mixer->fx.with_chorus ? buffers->fx_left_buf[SYNTH_CHORUS_CHANNEL] : NULL;
+ outbufs[buffers->buf_count*2 + SYNTH_REVERB_CHANNEL] = reverb_buf;
+ outbufs[buffers->buf_count*2 + SYNTH_CHORUS_CHANNEL] = chorus_buf;
+
+ /* 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',
+ * as long as the LADSPA Fx unit is not used. In case of LADSPA
+ * unit, think of it as subgroups on a mixer.
+ *
+ * For example: Assume that the number of groups is set to 2.
+ * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2,
+ * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI
+ * channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to
+ * output 2, 3, 6, 9, 12 etc to output 3.
+ */
+
+ for (i = 0; i < buffers->buf_count; i++) {
+ outbufs[i*2] = buffers->left_buf[i];
+ outbufs[i*2+1] = buffers->right_buf[i];
+ }
+ return buffers->buf_count*2 + 2;
+}
+
+
+static FLUID_INLINE void
+fluid_finish_rvoice(fluid_mixer_buffers_t* buffers, fluid_rvoice_t* rvoice)
+{
+ if (buffers->finished_voice_count < buffers->mixer->polyphony)
+ buffers->finished_voices[buffers->finished_voice_count++] = rvoice;
+ else
+ FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony");
+}
+
+static void
+fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t* buffers)
+{
+ int i,j;
+ for (i=0; i < buffers->finished_voice_count; i++) {
+ fluid_rvoice_t* v = buffers->finished_voices[i];
+ int* av = &buffers->mixer->active_voices;
+ for (j=0; j < *av; j++) {
+ if (v == buffers->mixer->rvoices[j]) {
+ (*av)--;
+ /* Pack the array */
+ if (j < *av)
+ buffers->mixer->rvoices[j] = buffers->mixer->rvoices[*av];
+ }
+ }
+ if (buffers->mixer->remove_voice_callback)
+ buffers->mixer->remove_voice_callback(
+ buffers->mixer->remove_voice_callback_userdata, v);
+ }
+ buffers->finished_voice_count = 0;
+}
+
+static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t* mixer)
+{
+#ifdef ENABLE_MIXER_THREADS
+ int i;
+ for (i=0; i < mixer->thread_count; i++)
+ fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]);
+#endif
+ fluid_mixer_buffer_process_finished_voices(&mixer->buffers);
+}
+
+static FLUID_INLINE void
+fluid_mixer_buffers_render_one(fluid_mixer_buffers_t* buffers,
+ fluid_rvoice_t* voice, fluid_real_t** bufs,
+ unsigned int bufcount)
+{
+ int s = fluid_mix_one(voice, bufs, bufcount, buffers->mixer->current_blockcount);
+ if (s < buffers->mixer->current_blockcount * FLUID_BUFSIZE) {
+ fluid_finish_rvoice(buffers, voice);
+ }
+}
+/*
+static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers,
+ fluid_rvoice_t* voice)
+{
+ int i, retval=0;
+ int fvc = buffers->finished_voice_count;
+ for (i=0; i < fvc; i++)
+ if (buffers->finished_voices[i] == voice) {
+ fvc--;
+ if (i < fvc)
+ buffers->finished_voices[i] = buffers->finished_voices[fvc];
+ retval++;
+ }
+ fvc = buffers->finished_voice_count;
+ return retval;
+}
+*/
+
+int
+fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice)
+{
+ int i;
+
+ if (mixer->active_voices < mixer->polyphony) {
+ mixer->rvoices[mixer->active_voices++] = voice;
+ return FLUID_OK;
+ }
+
+ /* See if any voices just finished, if so, take its place.
+ This can happen in voice overflow conditions. */
+ for (i=0; i < mixer->active_voices; i++) {
+ if (mixer->rvoices[i] == voice) {
+ FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!");
+ return FLUID_FAILED;
+ }
+ if (mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) {
+ fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]);
+ mixer->rvoices[i] = voice;
+ return FLUID_OK;
+ }
+ }
+
+ /* This should never happen */
+ FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");
+ return FLUID_FAILED;
+}
+
+static int
+fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t* buffers, int value)
+{
+ void* newptr;
+
+ if (buffers->finished_voice_count > value)
+ return FLUID_FAILED;
+
+ newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t*));
+ if (newptr == NULL && value > 0)
+ return FLUID_FAILED;
+ buffers->finished_voices = newptr;
+ return FLUID_OK;
+}
+
+/**
+ * Update polyphony - max number of voices (NOTE: not hard real-time capable)
+ * @return FLUID_OK or FLUID_FAILED
+ */
+int
+fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value)
+{
+ void* newptr;
+ if (handler->active_voices > value)
+ return FLUID_FAILED;
+
+ newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t*));
+ if (newptr == NULL)
+ return FLUID_FAILED;
+ handler->rvoices = newptr;
+
+ if (fluid_mixer_buffers_update_polyphony(&handler->buffers, value)
+ == FLUID_FAILED)
+ return FLUID_FAILED;
+
+#ifdef ENABLE_MIXER_THREADS
+ {
+ int i;
+ for (i=0; i < handler->thread_count; i++)
+ if (fluid_mixer_buffers_update_polyphony(&handler->threads[i], value)
+ == FLUID_FAILED)
+ return FLUID_FAILED;
+ }
+#endif
+
+ handler->polyphony = value;
+ return FLUID_OK;
+}
+
+
+static void
+fluid_render_loop_singlethread(fluid_rvoice_mixer_t* mixer)
+{
+ int i;
+ FLUID_DECLARE_VLA(fluid_real_t*, bufs,
+ mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);
+ int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);
+ fluid_profile_ref_var(prof_ref);
+ for (i=0; i < mixer->active_voices; i++) {
+ fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs,
+ bufcount);
+ fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref);
+ }
+}
+
+
+static FLUID_INLINE void
+fluid_mixer_buffers_zero(fluid_mixer_buffers_t* buffers)
+{
+ int i;
+ int size = buffers->mixer->current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t);
+ /* TODO: Optimize by only zero out the buffers we actually use later on. */
+ for (i=0; i < buffers->buf_count; i++) {
+ FLUID_MEMSET(buffers->left_buf[i], 0, size);
+ FLUID_MEMSET(buffers->right_buf[i], 0, size);
+ }
+ for (i=0; i < buffers->fx_buf_count; i++) {
+ FLUID_MEMSET(buffers->fx_left_buf[i], 0, size);
+ FLUID_MEMSET(buffers->fx_right_buf[i], 0, size);
+ }
+}
+
+
+
+static int
+fluid_mixer_buffers_init(fluid_mixer_buffers_t* buffers, fluid_rvoice_mixer_t* mixer)
+{
+ int i, samplecount;
+
+ buffers->mixer = mixer;
+ buffers->buf_count = buffers->mixer->buffers.buf_count;
+ buffers->fx_buf_count = buffers->mixer->buffers.fx_buf_count;
+ buffers->buf_blocks = buffers->mixer->buffers.buf_blocks;
+ samplecount = FLUID_BUFSIZE * buffers->buf_blocks;
+
+
+ /* Left and right audio buffers */
+
+ buffers->left_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
+ buffers->right_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
+
+ if ((buffers->left_buf == NULL) || (buffers->right_buf == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+
+ FLUID_MEMSET(buffers->left_buf, 0, buffers->buf_count * sizeof(fluid_real_t*));
+ FLUID_MEMSET(buffers->right_buf, 0, buffers->buf_count * sizeof(fluid_real_t*));
+
+ for (i = 0; i < buffers->buf_count; i++) {
+
+ buffers->left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
+ buffers->right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
+
+ if ((buffers->left_buf[i] == NULL) || (buffers->right_buf[i] == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+ }
+
+ /* Effects audio buffers */
+
+ buffers->fx_left_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count);
+ buffers->fx_right_buf = FLUID_ARRAY(fluid_real_t*, buffers->fx_buf_count);
+
+ if ((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+
+ FLUID_MEMSET(buffers->fx_left_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*));
+ FLUID_MEMSET(buffers->fx_right_buf, 0, buffers->fx_buf_count * sizeof(fluid_real_t*));
+
+ for (i = 0; i < buffers->fx_buf_count; i++) {
+ buffers->fx_left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
+ buffers->fx_right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
+
+ if ((buffers->fx_left_buf[i] == NULL) || (buffers->fx_right_buf[i] == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+ }
+
+ buffers->finished_voices = NULL;
+ if (fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony)
+ == FLUID_FAILED) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Note: Not hard real-time capable (calls malloc)
+ */
+void
+fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate)
+{
+ int i;
+ if (mixer->fx.chorus)
+ delete_fluid_chorus(mixer->fx.chorus);
+ mixer->fx.chorus = new_fluid_chorus(samplerate);
+ if (mixer->fx.reverb)
+ fluid_revmodel_samplerate_change(mixer->fx.reverb, samplerate);
+ for (i=0; i < mixer->active_voices; i++)
+ fluid_rvoice_set_output_rate(mixer->rvoices[i], samplerate);
+}
+
+
+/**
+ * @param buf_count number of primary stereo buffers
+ * @param fx_buf_count number of stereo effect buffers
+ */
+fluid_rvoice_mixer_t*
+new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, fluid_real_t sample_rate)
+{
+ fluid_rvoice_mixer_t* mixer = FLUID_NEW(fluid_rvoice_mixer_t);
+ if (mixer == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t));
+ mixer->buffers.buf_count = buf_count;
+ mixer->buffers.fx_buf_count = fx_buf_count;
+ mixer->buffers.buf_blocks = FLUID_MIXER_MAX_BUFFERS_DEFAULT;
+
+ /* allocate the reverb module */
+ mixer->fx.reverb = new_fluid_revmodel(sample_rate);
+ mixer->fx.chorus = new_fluid_chorus(sample_rate);
+ if (mixer->fx.reverb == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ delete_fluid_rvoice_mixer(mixer);
+ return NULL;
+ }
+
+ if (!fluid_mixer_buffers_init(&mixer->buffers, mixer)) {
+ delete_fluid_rvoice_mixer(mixer);
+ return NULL;
+ }
+
+#ifdef ENABLE_MIXER_THREADS
+ mixer->thread_ready = new_fluid_cond();
+ mixer->wakeup_threads = new_fluid_cond();
+ mixer->thread_ready_m = new_fluid_cond_mutex();
+ mixer->wakeup_threads_m = new_fluid_cond_mutex();
+ if (!mixer->thread_ready || !mixer->wakeup_threads ||
+ !mixer->thread_ready_m || !mixer->wakeup_threads_m) {
+ delete_fluid_rvoice_mixer(mixer);
+ return NULL;
+ }
+#endif
+
+ return mixer;
+}
+
+static void
+fluid_mixer_buffers_free(fluid_mixer_buffers_t* buffers)
+{
+ int i;
+
+ FLUID_FREE(buffers->finished_voices);
+
+ /* free all the sample buffers */
+ if (buffers->left_buf != NULL) {
+ for (i = 0; i < buffers->buf_count; i++) {
+ if (buffers->left_buf[i] != NULL) {
+ FLUID_FREE(buffers->left_buf[i]);
+ }
+ }
+ FLUID_FREE(buffers->left_buf);
+ }
+
+ if (buffers->right_buf != NULL) {
+ for (i = 0; i < buffers->buf_count; i++) {
+ if (buffers->right_buf[i] != NULL) {
+ FLUID_FREE(buffers->right_buf[i]);
+ }
+ }
+ FLUID_FREE(buffers->right_buf);
+ }
+
+ if (buffers->fx_left_buf != NULL) {
+ for (i = 0; i < buffers->fx_buf_count; i++) {
+ if (buffers->fx_left_buf[i] != NULL) {
+ FLUID_FREE(buffers->fx_left_buf[i]);
+ }
+ }
+ FLUID_FREE(buffers->fx_left_buf);
+ }
+
+ if (buffers->fx_right_buf != NULL) {
+ for (i = 0; i < buffers->fx_buf_count; i++) {
+ if (buffers->fx_right_buf[i] != NULL) {
+ FLUID_FREE(buffers->fx_right_buf[i]);
+ }
+ }
+ FLUID_FREE(buffers->fx_right_buf);
+ }
+}
+
+void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t* mixer)
+{
+ if (!mixer)
+ return;
+ fluid_rvoice_mixer_set_threads(mixer, 0, 0);
+#ifdef ENABLE_MIXER_THREADS
+ if (mixer->thread_ready)
+ delete_fluid_cond(mixer->thread_ready);
+ if (mixer->wakeup_threads)
+ delete_fluid_cond(mixer->wakeup_threads);
+ if (mixer->thread_ready_m)
+ delete_fluid_cond_mutex(mixer->thread_ready_m);
+ if (mixer->wakeup_threads_m)
+ delete_fluid_cond_mutex(mixer->wakeup_threads_m);
+#endif
+ fluid_mixer_buffers_free(&mixer->buffers);
+ if (mixer->fx.reverb)
+ delete_fluid_revmodel(mixer->fx.reverb);
+ if (mixer->fx.chorus)
+ delete_fluid_chorus(mixer->fx.chorus);
+ FLUID_FREE(mixer->rvoices);
+ FLUID_FREE(mixer);
+}
+
+
+#ifdef LADSPA
+void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer,
+ fluid_LADSPA_FxUnit_t* ladspa)
+{
+ mixer->LADSPA_FxUnit = ladspa;
+}
+#endif
+
+void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on)
+{
+ mixer->fx.with_reverb = on;
+}
+
+void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on)
+{
+ mixer->fx.with_chorus = on;
+}
+
+void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on)
+{
+ mixer->fx.mix_fx_to_out = on;
+}
+
+void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set,
+ int nr, double level, double speed,
+ double depth_ms, int type)
+{
+ fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type);
+}
+void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set,
+ double roomsize, double damping,
+ double width, double level)
+{
+ fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level);
+}
+
+void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer)
+{
+ fluid_revmodel_reset(mixer->fx.reverb);
+ fluid_chorus_reset(mixer->fx.chorus);
+}
+
+void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer)
+{
+ fluid_revmodel_reset(mixer->fx.reverb);
+}
+
+void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer)
+{
+ fluid_chorus_reset(mixer->fx.chorus);
+}
+
+int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
+ fluid_real_t*** left, fluid_real_t*** right)
+{
+ *left = mixer->buffers.left_buf;
+ *right = mixer->buffers.right_buf;
+ return mixer->buffers.buf_count;
+}
+
+
+#ifdef ENABLE_MIXER_THREADS
+
+static FLUID_INLINE fluid_rvoice_t*
+fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t* mixer)
+{
+ int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1);
+ if (i >= mixer->active_voices)
+ return NULL;
+ return mixer->rvoices[i];
+}
+
+#define THREAD_BUF_PROCESSING 0
+#define THREAD_BUF_VALID 1
+#define THREAD_BUF_NODATA 2
+#define THREAD_BUF_TERMINATE 3
+
+/* Core thread function (processes voices in parallel to primary synthesis thread) */
+static void
+fluid_mixer_thread_func (void* data)
+{
+ fluid_mixer_buffers_t* buffers = data;
+ fluid_rvoice_mixer_t* mixer = buffers->mixer;
+ int hasValidData = 0;
+ FLUID_DECLARE_VLA(fluid_real_t*, bufs, buffers->buf_count*2 + buffers->fx_buf_count*2);
+ int bufcount = 0;
+
+ while (!fluid_atomic_int_get(&mixer->threads_should_terminate)) {
+ fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer);
+ if (rvoice == NULL) {
+ // if no voices: signal rendered buffers, sleep
+ fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA);
+ fluid_cond_mutex_lock(mixer->thread_ready_m);
+ fluid_cond_signal(mixer->thread_ready);
+ fluid_cond_mutex_unlock(mixer->thread_ready_m);
+
+ fluid_cond_mutex_lock(mixer->wakeup_threads_m);
+ while (1) {
+ int j = fluid_atomic_int_get(&buffers->ready);
+ if (j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE)
+ break;
+ fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m);
+ }
+ fluid_cond_mutex_unlock(mixer->wakeup_threads_m);
+
+ hasValidData = 0;
+ }
+ else {
+ // else: if buffer is not zeroed, zero buffers
+ if (!hasValidData) {
+ fluid_mixer_buffers_zero(buffers);
+ bufcount = fluid_mixer_buffers_prepare(buffers, bufs);
+ hasValidData = 1;
+ }
+ // then render voice to buffers
+ fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount);
+ }
+ }
+
+}
+
+static void
+fluid_mixer_buffers_mix(fluid_mixer_buffers_t* dest, fluid_mixer_buffers_t* src)
+{
+ int i,j;
+ int scount = dest->mixer->current_blockcount * FLUID_BUFSIZE;
+ int minbuf;
+
+ minbuf = dest->buf_count;
+ if (minbuf > src->buf_count)
+ minbuf = src->buf_count;
+ for (i=0; i < minbuf; i++) {
+ for (j=0; j < scount; j++) {
+ dest->left_buf[i][j] += src->left_buf[i][j];
+ dest->right_buf[i][j] += src->right_buf[i][j];
+ }
+ }
+
+ minbuf = dest->fx_buf_count;
+ if (minbuf > src->fx_buf_count)
+ minbuf = src->fx_buf_count;
+ for (i=0; i < minbuf; i++) {
+ for (j=0; j < scount; j++) {
+ dest->fx_left_buf[i][j] += src->fx_left_buf[i][j];
+ dest->fx_right_buf[i][j] += src->fx_right_buf[i][j];
+ }
+ }
+}
+
+
+/**
+ * Go through all threads and see if someone is finished for mixing
+ */
+static FLUID_INLINE int
+fluid_mixer_mix_in(fluid_rvoice_mixer_t* mixer, int extra_threads)
+{
+ int i, result, hasmixed;
+ do {
+ hasmixed = 0;
+ result = 0;
+ for (i=0; i < extra_threads; i++) {
+ int j = fluid_atomic_int_get(&mixer->threads[i].ready);
+ switch (j) {
+ case THREAD_BUF_PROCESSING:
+ result = 1;
+ break;
+ case THREAD_BUF_VALID:
+ fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA);
+ fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i]);
+ hasmixed = 1;
+ break;
+ }
+ }
+ } while (hasmixed);
+ return result;
+}
+
+static void
+fluid_render_loop_multithread(fluid_rvoice_mixer_t* mixer)
+{
+ int i, bufcount;
+ //int scount = mixer->current_blockcount * FLUID_BUFSIZE;
+ FLUID_DECLARE_VLA(fluid_real_t*, bufs,
+ mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);
+ // How many threads should we start this time?
+ int extra_threads = mixer->active_voices / VOICES_PER_THREAD;
+ if (extra_threads > mixer->thread_count)
+ extra_threads = mixer->thread_count;
+ if (extra_threads == 0) {
+ // No extra threads? No thread overhead!
+ fluid_render_loop_singlethread(mixer);
+ return;
+ }
+
+ bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);
+
+ // Prepare voice list
+ fluid_cond_mutex_lock(mixer->wakeup_threads_m);
+ fluid_atomic_int_set(&mixer->current_rvoice, 0);
+ for (i=0; i < extra_threads; i++)
+ fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING);
+ // Signal threads to wake up
+ fluid_cond_broadcast(mixer->wakeup_threads);
+ fluid_cond_mutex_unlock(mixer->wakeup_threads_m);
+
+ // If thread is finished, mix it in
+ while (fluid_mixer_mix_in(mixer, extra_threads)) {
+ // Otherwise get a voice and render it
+ fluid_rvoice_t* rvoice = fluid_mixer_get_mt_rvoice(mixer);
+ if (rvoice != NULL) {
+ fluid_profile_ref_var(prof_ref);
+ fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount);
+ fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref);
+ //test++;
+ }
+ else {
+ // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock
+ int is_processing = 0;
+ //waits++;
+ fluid_cond_mutex_lock(mixer->thread_ready_m);
+ for (i=0; i < extra_threads; i++)
+ if (fluid_atomic_int_get(&mixer->threads[i].ready) ==
+ THREAD_BUF_PROCESSING)
+ is_processing = 1;
+ if (is_processing)
+ fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m);
+ fluid_cond_mutex_unlock(mixer->thread_ready_m);
+ }
+ }
+ //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d",
+ // mixer->current_blockcount, test, mixer->active_voices, waits);
+}
+
+#endif
+
+/**
+ * Update amount of extra mixer threads.
+ * @param thread_count Number of extra mixer threads for multi-core rendering
+ * @param prio_level real-time prio level for the extra mixer threads
+ */
+void
+fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_count,
+ int prio_level)
+{
+#ifdef ENABLE_MIXER_THREADS
+ char name[16];
+ int i;
+
+ // Kill all existing threads first
+ if (mixer->thread_count) {
+ 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++)
+ fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE);
+ 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);
+ mixer->thread_count = 0;
+ mixer->threads = NULL;
+ }
+
+ if (thread_count == 0)
+ return;
+
+ // Now prepare the new threads
+ fluid_atomic_int_set(&mixer->threads_should_terminate, 0);
+ mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count);
+ if (mixer->threads == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return;
+ }
+ FLUID_MEMSET(mixer->threads, 0, thread_count*sizeof(fluid_mixer_buffers_t));
+ mixer->thread_count = thread_count;
+ for (i=0; i < thread_count; i++) {
+ fluid_mixer_buffers_t* b = &mixer->threads[i];
+ if (!fluid_mixer_buffers_init(b, mixer))
+ return;
+ fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA);
+ g_snprintf (name, sizeof (name), "mixer%d", i);
+ b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0);
+ if (!b->thread)
+ return;
+ }
+
+#endif
+}
+
+/**
+ * Synthesize audio into buffers
+ * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples
+ * @return number of blocks rendered
+ */
+int
+fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount)
+{
+ fluid_profile_ref_var(prof_ref);
+
+ mixer->current_blockcount = blockcount > mixer->buffers.buf_blocks ?
+ mixer->buffers.buf_blocks : blockcount;
+
+ // Zero buffers
+ fluid_mixer_buffers_zero(&mixer->buffers);
+ fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref);
+
+#ifdef ENABLE_MIXER_THREADS
+ if (mixer->thread_count > 0)
+ fluid_render_loop_multithread(mixer);
+ else
+#endif
+ fluid_render_loop_singlethread(mixer);
+ fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref);
+
+
+ // Process reverb & chorus
+ fluid_rvoice_mixer_process_fx(mixer);
+
+ // Call the callback and pack active voice array
+ fluid_rvoice_mixer_process_finished_voices(mixer);
+
+ return mixer->current_blockcount;
+}
diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.h b/libs/fluidsynth/src/fluid_rvoice_mixer.h
new file mode 100644
index 0000000000..d4e41ca0a8
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_rvoice_mixer.h
@@ -0,0 +1,74 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_RVOICE_MIXER_H
+#define _FLUID_RVOICE_MIXER_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_rvoice.h"
+//#include "fluid_ladspa.h"
+
+typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t;
+
+#define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE)
+
+
+void fluid_rvoice_mixer_set_finished_voices_callback(
+ fluid_rvoice_mixer_t* mixer,
+ void (*func)(void*, fluid_rvoice_t*),
+ void* userdata);
+
+
+int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t* mixer, int blockcount);
+int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer,
+ fluid_real_t*** left, fluid_real_t*** right);
+
+fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count,
+ fluid_real_t sample_rate);
+
+void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t*);
+
+void fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samplerate);
+void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on);
+void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on);
+void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t* mixer, int on);
+int fluid_rvoice_mixer_set_polyphony(fluid_rvoice_mixer_t* handler, int value);
+int fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice);
+void fluid_rvoice_mixer_set_chorus_params(fluid_rvoice_mixer_t* mixer, int set,
+ int nr, double level, double speed,
+ double depth_ms, int type);
+void fluid_rvoice_mixer_set_reverb_params(fluid_rvoice_mixer_t* mixer, int set,
+ double roomsize, double damping,
+ double width, double level);
+void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer);
+void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer);
+void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer);
+
+void fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_count,
+ int prio_level);
+
+#ifdef LADSPA
+void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer,
+ fluid_LADSPA_FxUnit_t* ladspa);
+#endif
+
+#endif
+
diff --git a/libs/fluidsynth/src/fluid_settings.c b/libs/fluidsynth/src/fluid_settings.c
new file mode 100644
index 0000000000..2061c90f90
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_settings.c
@@ -0,0 +1,1602 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_sys.h"
+#include "fluid_hash.h"
+#include "fluid_synth.h"
+//#include "fluid_cmd.h"
+//#include "fluid_adriver.h"
+//#include "fluid_mdriver.h"
+#include "fluid_settings.h"
+#include "fluid_midi.h"
+
+/* Defined in fluid_filerenderer.c */
+extern void fluid_file_renderer_settings (fluid_settings_t* settings);
+
+/* maximum allowed components of a settings variable (separated by '.') */
+#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */
+#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */
+
+static void fluid_settings_init(fluid_settings_t* settings);
+static void fluid_settings_key_destroy_func(void* value);
+static void fluid_settings_value_destroy_func(void* value);
+static int fluid_settings_tokenize(const char *s, char *buf, char **ptr);
+
+/* Common structure to all settings nodes */
+typedef struct {
+ int type; /**< fluid_types_enum */
+} fluid_setting_node_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ char* value;
+ char* def;
+ int hints;
+ fluid_list_t* options;
+ fluid_str_update_t update;
+ void* data;
+} fluid_str_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ double value;
+ double def;
+ double min;
+ double max;
+ int hints;
+ fluid_num_update_t update;
+ void* data;
+} fluid_num_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ int value;
+ int def;
+ int min;
+ int max;
+ int hints;
+ fluid_int_update_t update;
+ void* data;
+} fluid_int_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ fluid_hashtable_t *hashtable;
+} fluid_set_setting_t;
+
+
+static fluid_str_setting_t*
+new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data)
+{
+ fluid_str_setting_t* str;
+
+ str = FLUID_NEW(fluid_str_setting_t);
+
+ if (!str)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ str->node.type = FLUID_STR_TYPE;
+ str->value = value? FLUID_STRDUP(value) : NULL;
+ str->def = def? FLUID_STRDUP(def) : NULL;
+ str->hints = hints;
+ str->options = NULL;
+ str->update = fun;
+ str->data = data;
+ return str;
+}
+
+static void
+delete_fluid_str_setting(fluid_str_setting_t* str)
+{
+ if (!str) return;
+
+ if (str->value) FLUID_FREE(str->value);
+ if (str->def) FLUID_FREE(str->def);
+
+ if (str->options) {
+ fluid_list_t* list = str->options;
+
+ while (list) {
+ FLUID_FREE (list->data);
+ list = fluid_list_next(list);
+ }
+
+ delete_fluid_list(str->options);
+ }
+
+ FLUID_FREE(str);
+}
+
+
+static fluid_num_setting_t*
+new_fluid_num_setting(double min, double max, double def,
+ int hints, fluid_num_update_t fun, void* data)
+{
+ fluid_num_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_num_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_NUM_TYPE;
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void
+delete_fluid_num_setting(fluid_num_setting_t* setting)
+{
+ if (setting) FLUID_FREE(setting);
+}
+
+static fluid_int_setting_t*
+new_fluid_int_setting(int min, int max, int def,
+ int hints, fluid_int_update_t fun, void* data)
+{
+ fluid_int_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_int_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_INT_TYPE;
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void
+delete_fluid_int_setting(fluid_int_setting_t* setting)
+{
+ if (setting) FLUID_FREE(setting);
+}
+
+static fluid_set_setting_t*
+new_fluid_set_setting(void)
+{
+ fluid_set_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_set_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_SET_TYPE;
+ setting->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
+ fluid_settings_key_destroy_func,
+ fluid_settings_value_destroy_func);
+ if (!setting->hashtable)
+ {
+ FLUID_FREE (setting);
+ return NULL;
+ }
+
+ return setting;
+}
+
+static void
+delete_fluid_set_setting(fluid_set_setting_t* setting)
+{
+ if (setting)
+ {
+ delete_fluid_hashtable(setting->hashtable);
+ FLUID_FREE(setting);
+ }
+}
+
+/**
+ * Create a new settings object
+ * @return the pointer to the settings object
+ */
+fluid_settings_t *
+new_fluid_settings(void)
+{
+ fluid_settings_t* settings;
+
+ settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
+ fluid_settings_key_destroy_func,
+ fluid_settings_value_destroy_func);
+ if (settings == NULL) return NULL;
+
+ fluid_rec_mutex_init (settings->mutex);
+ fluid_settings_init(settings);
+ return settings;
+}
+
+/**
+ * Delete the provided settings object
+ * @param settings a settings object
+ */
+void
+delete_fluid_settings(fluid_settings_t* settings)
+{
+ fluid_return_if_fail (settings != NULL);
+
+ fluid_rec_mutex_destroy (settings->mutex);
+ delete_fluid_hashtable(settings);
+}
+
+/* Settings hash key destroy function */
+static void
+fluid_settings_key_destroy_func(void* value)
+{
+ FLUID_FREE (value); /* Free the string key value */
+}
+
+/* Settings hash value destroy function */
+static void
+fluid_settings_value_destroy_func(void* value)
+{
+ fluid_setting_node_t *node = value;
+
+ switch (node->type) {
+ case FLUID_NUM_TYPE:
+ delete_fluid_num_setting((fluid_num_setting_t*) value);
+ break;
+ case FLUID_INT_TYPE:
+ delete_fluid_int_setting((fluid_int_setting_t*) value);
+ break;
+ case FLUID_STR_TYPE:
+ delete_fluid_str_setting((fluid_str_setting_t*) value);
+ break;
+ case FLUID_SET_TYPE:
+ delete_fluid_set_setting((fluid_set_setting_t*) value);
+ break;
+ }
+}
+
+void
+fluid_settings_init(fluid_settings_t* settings)
+{
+ fluid_return_if_fail (settings != NULL);
+
+ fluid_synth_settings(settings);
+ //fluid_shell_settings(settings);
+ fluid_player_settings(settings);
+#if 0
+ fluid_file_renderer_settings(settings);
+ fluid_audio_driver_settings(settings);
+ fluid_midi_driver_settings(settings);
+#endif
+}
+
+static int
+fluid_settings_tokenize(const char *s, char *buf, char **ptr)
+{
+ char *tokstr, *tok;
+ int n = 0;
+
+ if (strlen (s) > MAX_SETTINGS_LABEL)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars",
+ MAX_SETTINGS_LABEL);
+ return 0;
+ }
+
+ FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */
+ tokstr = buf;
+
+ while ((tok = fluid_strtok (&tokstr, ".")))
+ {
+ if (n >= MAX_SETTINGS_TOKENS)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",
+ MAX_SETTINGS_TOKENS);
+ return 0;
+ } else
+ ptr[n++] = tok;
+ }
+
+ return n;
+}
+
+/**
+ * Get a setting name, value and type
+ *
+ * @param settings a settings object
+ * @param name Settings name
+ * @param value Location to store setting node if found
+ * @return 1 if the node exists, 0 otherwise
+ */
+static int
+fluid_settings_get(fluid_settings_t* settings, const char *name,
+ fluid_setting_node_t **value)
+{
+ fluid_hashtable_t* table = settings;
+ fluid_setting_node_t *node = NULL;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+ int n;
+
+ ntokens = fluid_settings_tokenize (name, buf, tokens);
+
+ if (table == NULL || ntokens <= 0) return 0;
+
+ for (n = 0; n < ntokens; n++) {
+
+ node = fluid_hashtable_lookup(table, tokens[n]);
+ if (!node) return 0;
+
+ table = (node->type == FLUID_SET_TYPE) ? ((fluid_set_setting_t *)node)->hashtable : NULL;
+ }
+
+ if (value) *value = node;
+
+ return 1;
+}
+
+/**
+ * Set a setting name, value and type, replacing it if already exists
+ *
+ * @param settings a settings object
+ * @param name Settings name
+ * @param value Node instance to assign (used directly)
+ * @return 1 if the value has been set, zero otherwise
+ */
+static int
+fluid_settings_set(fluid_settings_t* settings, const char *name, void* value)
+{
+ fluid_hashtable_t* table = settings;
+ fluid_setting_node_t *node;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int n, num;
+ char *dupname;
+
+ num = fluid_settings_tokenize (name, buf, tokens) - 1;
+ if (num == 0)
+ return 0;
+
+ for (n = 0; n < num; n++) {
+
+ node = fluid_hashtable_lookup(table, tokens[n]);
+
+ if (node) {
+
+ if (node->type == FLUID_SET_TYPE) {
+ table = ((fluid_set_setting_t *)node)->hashtable;
+ } else {
+ /* path ends prematurely */
+ FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]);
+ return 0;
+ }
+
+ } else {
+ /* create a new node */
+ fluid_set_setting_t* setnode;
+
+ dupname = FLUID_STRDUP (tokens[n]);
+ setnode = new_fluid_set_setting ();
+
+ if (!dupname || !setnode)
+ {
+ if (dupname) FLUID_FREE (dupname);
+ else FLUID_LOG(FLUID_ERR, "Out of memory");
+
+ if (setnode) delete_fluid_set_setting (setnode);
+
+ return 0;
+ }
+
+ fluid_hashtable_insert(table, dupname, setnode);
+ table = setnode->hashtable;
+ }
+ }
+
+ dupname = FLUID_STRDUP (tokens[num]);
+
+ if (!dupname)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+
+ fluid_hashtable_insert(table, dupname, value);
+
+ return 1;
+}
+
+/** returns 1 if the value has been registered correctly, 0
+ otherwise */
+int
+fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints,
+ fluid_str_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t* setting;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ setting = new_fluid_str_setting(def, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_str_setting (setting);
+ } else {
+ /* if variable already exists, don't change its value. */
+ if (node->type == FLUID_STR_TYPE) {
+ setting = (fluid_str_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->def = def? FLUID_STRDUP(def) : NULL;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise */
+int
+fluid_settings_register_num(fluid_settings_t* settings, char* name, double def,
+ double min, double max, int hints,
+ fluid_num_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ /* For now, all floating point settings are bounded below and above */
+ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(min, max, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_num_setting (setting);
+ } else {
+ if (node->type == FLUID_NUM_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise. */
+int
+fluid_settings_register_int(fluid_settings_t* settings, char* name, int def,
+ int min, int max, int hints,
+ fluid_int_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ /* For now, all integer settings are bounded below and above */
+ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(min, max, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_int_setting (setting);
+ } else {
+ if (node->type == FLUID_INT_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the type of the setting with the given name
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the type for the named setting, or #FLUID_NO_TYPE when it does not exist
+ */
+int
+fluid_settings_get_type(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int type;
+
+ fluid_return_val_if_fail (settings != NULL, FLUID_NO_TYPE);
+ fluid_return_val_if_fail (name != NULL, FLUID_NO_TYPE);
+ fluid_return_val_if_fail (name[0] != '\0', FLUID_NO_TYPE);
+
+ fluid_rec_mutex_lock (settings->mutex);
+ type = fluid_settings_get (settings, name, &node) ? node->type : FLUID_NO_TYPE;
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return (type);
+}
+
+/**
+ * Get the hints for the named setting as an integer bitmap
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the hints associated to the named setting if it exists, zero otherwise
+ */
+int
+fluid_settings_get_hints(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int hints = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ hints = setting->hints;
+ } else if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ hints = setting->hints;
+ } else if (node->type == FLUID_INT_TYPE) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ hints = setting->hints;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return hints;
+}
+
+/**
+ * Ask whether the setting is changeable in real-time.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return non zero if the setting is changeable in real-time
+ */
+int
+fluid_settings_is_realtime(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int isrealtime = FALSE;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ } else if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ } else if (node->type == FLUID_INT_TYPE) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return isrealtime;
+}
+
+/**
+ * Set a string value for a named setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str new string value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node)) {
+ if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value) FLUID_FREE (setting->value);
+ setting->value = str ? FLUID_STRDUP (str) : NULL;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, str);
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle yes/no for boolean values for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ if (FLUID_STRCMP (str, "yes") == 0)
+ {
+ setting->value = TRUE;
+ if (setting->update) (*setting->update)(setting->data, name, TRUE);
+ }
+ else if (FLUID_STRCMP (str, "no") == 0)
+ {
+ setting->value = FALSE;
+ if (setting->update) (*setting->update)(setting->data, name, FALSE);
+ }
+ }
+ }
+ } else {
+ /* insert a new setting */
+ fluid_str_setting_t* setting;
+ setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_str_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Copy the value of a string setting
+ * @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 1 if the value exists, 0 otherwise
+ * @since 1.1.0
+ *
+ * Like fluid_settings_getstr() but is thread safe. A size of 256 should be
+ * more than sufficient for the string buffer.
+ */
+int
+fluid_settings_copystr(fluid_settings_t* settings, const char *name,
+ char *str, int len)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+ fluid_return_val_if_fail (len > 0, 0);
+
+ str[0] = 0;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value)
+ {
+ FLUID_STRNCPY (str, setting->value, len);
+ str[len - 1] = 0; /* Force terminate, in case of truncation */
+ }
+
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ FLUID_STRNCPY (str, setting->value ? "yes" : "no", len);
+ str[len - 1] = 0; /* Force terminate, in case of truncation */
+
+ retval = 1;
+ }
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Duplicate the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Location to store pointer to allocated duplicate string
+ * @return 1 if the value exists and was successfully duplicated, 0 otherwise
+ * @since 1.1.0
+ *
+ * Like fluid_settings_copystr() but allocates a new copy of the string. Caller
+ * owns the string and should free it with free() when done using it.
+ */
+int
+fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value)
+ {
+ *str = FLUID_STRDUP (setting->value);
+ if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
+ }
+
+ if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ *str = FLUID_STRDUP (setting->value ? "yes" : "no");
+ if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
+
+ if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */
+ }
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Location to store pointer to the settings string value
+ * @return 1 if the value exists, 0 otherwise
+ * @deprecated
+ *
+ * If the value does not exists, 'str' is set to NULL. Otherwise, 'str' will
+ * point to the value. The application does not own the returned value and it
+ * is valid only until a new value is assigned to the setting of the given name.
+ *
+ * NOTE: In a multi-threaded environment, caller must ensure that the setting
+ * being read by fluid_settings_getstr() is not assigned during the
+ * duration of callers use of the setting's value. Use fluid_settings_copystr()
+ * or fluid_settings_dupstr() which does not have this restriction.
+ */
+int
+fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+ *str = setting->value;
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ *str = setting->value ? "yes" : "no";
+ retval = 1;
+ }
+ }
+ }
+ else *str = NULL;
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Test a string setting for some value.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s a string to be tested
+ * @return 1 if the value exists and is equal to 's', 0 otherwise
+ */
+int
+fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const char *s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+ if (setting->value) retval = FLUID_STRCMP (setting->value, s) == 0;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ retval = FLUID_STRCMP (setting->value ? "yes" : "no", s) == 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the default value of a string setting. Note that the returned string is
+ * not owned by the caller and should not be modified or freed.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the default string value of the setting if it exists, NULL otherwise
+ */
+char*
+fluid_settings_getstr_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ char *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))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ retval = setting->def;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ retval = setting->def ? "yes" : "no";
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Add an option to a string setting (like an enumeration value).
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s option string to add
+ * @return 1 if the setting exists and option was added, 0 otherwise
+ *
+ * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set.
+ */
+int
+fluid_settings_add_option(fluid_settings_t* settings, const char *name, const char *s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_STR_TYPE)) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ char* copy = FLUID_STRDUP(s);
+ setting->options = fluid_list_append(setting->options, copy);
+ setting->hints |= FLUID_HINT_OPTIONLIST;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Remove an option previously assigned by fluid_settings_add_option().
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s option string to remove
+ * @return 1 if the setting exists and option was removed, 0 otherwise
+ */
+int
+fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const char* s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_STR_TYPE)) {
+
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ fluid_list_t* list = setting->options;
+
+ while (list) {
+ char* option = (char*) fluid_list_get(list);
+ if (FLUID_STRCMP(s, option) == 0) {
+ FLUID_FREE (option);
+ setting->options = fluid_list_remove_link(setting->options, list);
+ retval = 1;
+ break;
+ }
+ list = fluid_list_next(list);
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Set a numeric value for a named setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val new setting's value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val)
+{
+ fluid_setting_node_t *node;
+ fluid_num_setting_t* setting;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ setting = (fluid_num_setting_t*) node;
+
+ if (val < setting->min) val = setting->min;
+ else if (val > setting->max) val = setting->max;
+
+ setting->value = val;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, val);
+ retval = 1;
+ }
+ } else {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL);
+ setting->value = val;
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_num_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the numeric value of a named setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val variable pointer to receive the setting's numeric value
+ * @return 1 if the value exists, 0 otherwise
+ */
+int
+fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (val != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ *val = setting->value;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the range of values of a numeric setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param min setting's range lower limit
+ * @param max setting's range upper limit
+ */
+void
+fluid_settings_getnum_range(fluid_settings_t* settings, const char *name,
+ double* min, double* max)
+{
+ fluid_setting_node_t *node;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (min != NULL);
+ fluid_return_if_fail (max != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ *min = setting->min;
+ *max = setting->max;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+}
+
+/**
+ * Get the default value of a named numeric (double) setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the default value if the named setting exists, 0.0f otherwise
+ */
+double
+fluid_settings_getnum_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ double retval = 0.0;
+
+ fluid_return_val_if_fail (settings != NULL, 0.0);
+ fluid_return_val_if_fail (name != NULL, 0.0);
+ fluid_return_val_if_fail (name[0] != '\0', 0.0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ retval = setting->def;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Set an integer value for a setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val new setting's integer value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setint(fluid_settings_t* settings, const char *name, int val)
+{
+ fluid_setting_node_t *node;
+ fluid_int_setting_t* setting;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_INT_TYPE) {
+ setting = (fluid_int_setting_t*) node;
+
+ if (val < setting->min) val = setting->min;
+ else if (val > setting->max) val = setting->max;
+
+ setting->value = val;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, val);
+ retval = 1;
+ }
+ } else {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL);
+ setting->value = val;
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_int_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get an integer value setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val pointer to a variable to receive the setting's integer value
+ * @return 1 if the value exists, 0 otherwise
+ */
+int
+fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (val != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ *val = setting->value;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * 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
+ * @param max setting's range upper limit
+ */
+void
+fluid_settings_getint_range(fluid_settings_t* settings, const char *name,
+ int* min, int* max)
+{
+ fluid_setting_node_t *node;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (min != NULL);
+ fluid_return_if_fail (max != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ *min = setting->min;
+ *max = setting->max;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+}
+
+/**
+ * Get the default value of an integer setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the setting's default integer value it it exists, zero otherwise
+ */
+int
+fluid_settings_getint_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ retval = setting->def;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Iterate the available options for a named string setting, calling the provided
+ * callback function for each existing option.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param data any user provided pointer
+ * @param func callback function to be called on each iteration
+ *
+ * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each
+ * option in alphabetical order. Sort order was undefined in previous versions.
+ */
+void
+fluid_settings_foreach_option (fluid_settings_t* settings, const char *name,
+ void* data, fluid_settings_foreach_option_t func)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t *setting;
+ fluid_list_t *p, *newlist = NULL;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (func != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex); /* ++ lock */
+
+ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE)
+ {
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+ return;
+ }
+
+ setting = (fluid_str_setting_t*)node;
+
+ /* Duplicate option list */
+ for (p = setting->options; p; p = p->next)
+ newlist = fluid_list_append (newlist, fluid_list_get (p));
+
+ /* Sort by name */
+ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func);
+
+ for (p = newlist; p; p = p->next)
+ (*func)(data, (char *)name, (char *)fluid_list_get (p));
+
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+
+ delete_fluid_list (newlist);
+}
+
+/**
+ * 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
+fluid_settings_option_count (fluid_settings_t *settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int count = -1;
+
+ fluid_return_val_if_fail (settings != NULL, -1);
+ fluid_return_val_if_fail (name != NULL, -1);
+ fluid_return_val_if_fail (name[0] != '\0', -1);
+
+ fluid_rec_mutex_lock (settings->mutex);
+ if (fluid_settings_get(settings, name, &node) && node->type == FLUID_STR_TYPE)
+ count = fluid_list_size (((fluid_str_setting_t *)node)->options);
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return (count);
+}
+
+/**
+ * Concatenate options for a string setting together with a separator between.
+ * @param settings Settings object
+ * @param name Settings name
+ * @param separator String to use between options (NULL to use ", ")
+ * @return Newly allocated string or NULL on error (out of memory, not a valid
+ * setting \a name or not a string setting). Free the string when finished with it.
+ * @since 1.1.0
+ */
+char *
+fluid_settings_option_concat (fluid_settings_t *settings, const char *name,
+ const char *separator)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t *setting;
+ fluid_list_t *p, *newlist = NULL;
+ int count, len;
+ char *str, *option;
+
+ fluid_return_val_if_fail (settings != NULL, NULL);
+ fluid_return_val_if_fail (name != NULL, NULL);
+ fluid_return_val_if_fail (name[0] != '\0', NULL);
+
+ if (!separator) separator = ", ";
+
+ fluid_rec_mutex_lock (settings->mutex); /* ++ lock */
+
+ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE)
+ {
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+ return (NULL);
+ }
+
+ setting = (fluid_str_setting_t*)node;
+
+ /* Duplicate option list, count options and get total string length */
+ for (p = setting->options, count = 0, len = 0; p; p = p->next, count++)
+ {
+ option = fluid_list_get (p);
+
+ if (option)
+ {
+ newlist = fluid_list_append (newlist, option);
+ len += strlen (option);
+ }
+ }
+
+ if (count > 1) len += (count - 1) * strlen (separator);
+ len++; /* For terminator */
+
+ /* Sort by name */
+ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func);
+
+ str = FLUID_MALLOC (len);
+
+ if (str)
+ {
+ str[0] = 0;
+ for (p = newlist; p; p = p->next)
+ {
+ option = fluid_list_get (p);
+ strcat (str, option);
+ if (p->next) strcat (str, separator);
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+
+ delete_fluid_list (newlist);
+
+ if (!str) FLUID_LOG (FLUID_ERR, "Out of memory");
+
+ return (str);
+}
+
+/* Structure passed to fluid_settings_foreach_iter recursive function */
+typedef struct
+{
+ char path[MAX_SETTINGS_LABEL+1]; /* Maximum settings label length */
+ fluid_list_t *names; /* For fluid_settings_foreach() */
+} fluid_settings_foreach_bag_t;
+
+static int
+fluid_settings_foreach_iter (void* key, void* value, void* data)
+{
+ fluid_settings_foreach_bag_t *bag = data;
+ char *keystr = key;
+ fluid_setting_node_t *node = value;
+ int pathlen;
+ char *s;
+
+ pathlen = strlen (bag->path);
+
+ if (pathlen > 0)
+ {
+ bag->path[pathlen] = '.';
+ bag->path[pathlen + 1] = 0;
+ }
+
+ strcat (bag->path, keystr);
+
+ switch (node->type) {
+ case FLUID_NUM_TYPE:
+ case FLUID_INT_TYPE:
+ case FLUID_STR_TYPE:
+ s = FLUID_STRDUP (bag->path);
+ if (s) bag->names = fluid_list_append (bag->names, s);
+ break;
+ case FLUID_SET_TYPE:
+ fluid_hashtable_foreach(((fluid_set_setting_t *)value)->hashtable,
+ fluid_settings_foreach_iter, bag);
+ break;
+ }
+
+ bag->path[pathlen] = 0;
+
+ return 0;
+}
+
+/**
+ * Iterate the existing settings defined in a settings object, calling the
+ * provided callback function for each setting.
+ *
+ * @param settings a settings object
+ * @param data any user provided pointer
+ * @param func callback function to be called on each iteration
+ *
+ * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each
+ * setting in alphabetical order. Sort order was undefined in previous versions.
+ */
+void
+fluid_settings_foreach (fluid_settings_t* settings, void* data,
+ fluid_settings_foreach_t func)
+{
+ fluid_settings_foreach_bag_t bag;
+ fluid_setting_node_t *node;
+ fluid_list_t *p;
+ int r;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (func != NULL);
+
+ bag.path[0] = 0;
+ bag.names = NULL;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ /* Add all node names to the bag.names list */
+ fluid_hashtable_foreach (settings, fluid_settings_foreach_iter, &bag);
+
+ /* Sort names */
+ bag.names = fluid_list_sort (bag.names, fluid_list_str_compare_func);
+
+ /* Loop over names and call the callback */
+ for (p = bag.names; p; p = p->next)
+ {
+ r = fluid_settings_get (settings, (char *)(p->data), &node);
+ if (r && node) (*func) (data, (char *)(p->data), node->type);
+ FLUID_FREE (p->data); /* -- Free name */
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ delete_fluid_list (bag.names); /* -- Free names list */
+}
diff --git a/libs/fluidsynth/src/fluid_settings.h b/libs/fluidsynth/src/fluid_settings.h
new file mode 100644
index 0000000000..0eb1f97286
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_settings.h
@@ -0,0 +1,56 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_SETTINGS_H
+#define _FLUID_SETTINGS_H
+
+
+
+/** returns 1 if the option was added, 0 otherwise */
+int fluid_settings_add_option(fluid_settings_t* settings, const char* name, const char* s);
+
+/** returns 1 if the option was added, 0 otherwise */
+int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, const char* s);
+
+
+typedef int (*fluid_num_update_t)(void* data, const char* name, double value);
+typedef int (*fluid_str_update_t)(void* data, const char* name, const char* value);
+typedef int (*fluid_int_update_t)(void* data, const char* name, int value);
+
+/** returns 0 if the value has been registered correctly, non-zero
+ otherwise */
+int fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints,
+ fluid_str_update_t fun, void* data);
+
+/** returns 0 if the value has been registered correctly, non-zero
+ otherwise */
+int fluid_settings_register_num(fluid_settings_t* settings, char* name, double def,
+ double min, double max, int hints,
+ fluid_num_update_t fun, void* data);
+
+/** returns 0 if the value has been registered correctly, non-zero
+ otherwise */
+int fluid_settings_register_int(fluid_settings_t* settings, char* name, int def,
+ int min, int max, int hints,
+ fluid_int_update_t fun, void* data);
+
+
+#endif /* _FLUID_SETTINGS_H */
diff --git a/libs/fluidsynth/src/fluid_sfont.h b/libs/fluidsynth/src/fluid_sfont.h
new file mode 100644
index 0000000000..51e941e440
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_sfont.h
@@ -0,0 +1,68 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _PRIV_FLUID_SFONT_H
+#define _PRIV_FLUID_SFONT_H
+
+
+/*
+ * Utility macros to access soundfonts, presets, and samples
+ */
+
+#define fluid_sfloader_delete(_loader) { if ((_loader) && (_loader)->free) (*(_loader)->free)(_loader); }
+#define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename)
+
+
+#define delete_fluid_sfont(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0)
+#define fluid_sfont_get_name(_sf) (*(_sf)->get_name)(_sf)
+#define fluid_sfont_get_preset(_sf,_bank,_prenum) (*(_sf)->get_preset)(_sf,_bank,_prenum)
+#define fluid_sfont_iteration_start(_sf) (*(_sf)->iteration_start)(_sf)
+#define fluid_sfont_iteration_next(_sf,_pr) (*(_sf)->iteration_next)(_sf,_pr)
+#define fluid_sfont_get_data(_sf) (_sf)->data
+#define fluid_sfont_set_data(_sf,_p) { (_sf)->data = (void*) (_p); }
+
+
+#define delete_fluid_preset(_preset) \
+ { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }}
+
+#define fluid_preset_get_data(_preset) (_preset)->data
+#define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); }
+#define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset)
+#define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset)
+#define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset)
+
+#define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \
+ (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel)
+
+#define fluid_preset_notify(_preset,_reason,_chan) \
+ { if ((_preset) && (_preset)->notify) { (*(_preset)->notify)(_preset,_reason,_chan); }}
+
+
+#define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; }
+
+#define fluid_sample_decr_ref(_sample) \
+ (_sample)->refcount--; \
+ if (((_sample)->refcount == 0) && ((_sample)->notify)) \
+ (*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE);
+
+
+
+#endif /* _PRIV_FLUID_SFONT_H */
diff --git a/libs/fluidsynth/src/fluid_synth.c b/libs/fluidsynth/src/fluid_synth.c
new file mode 100644
index 0000000000..a12260c7b0
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_synth.c
@@ -0,0 +1,5053 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include <math.h>
+
+#include "fluid_synth.h"
+#include "fluid_sys.h"
+#include "fluid_chan.h"
+#include "fluid_tuning.h"
+#include "fluid_settings.h"
+#include "fluid_sfont.h"
+#include "fluid_hash.h"
+#include "fluid_defsfont.h"
+
+#ifdef TRAP_ON_FPE
+#define _GNU_SOURCE
+#include <fenv.h>
+
+/* seems to not be declared in fenv.h */
+extern int feenableexcept (int excepts);
+#endif
+
+static void fluid_synth_init(void);
+
+static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key,
+ int vel);
+static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key);
+static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num);
+static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name,
+ int value);
+static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name,
+ fluid_real_t value);
+static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data,
+ int len, char *response,
+ int *response_len, int avail_response,
+ int *handled, int dryrun);
+static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan);
+static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan);
+static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth);
+static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan,
+ int is_cc, int ctrl);
+static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan);
+static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int channum);
+static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan);
+static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan);
+static int fluid_synth_set_preset (fluid_synth_t *synth, int chan,
+ fluid_preset_t *preset);
+static fluid_preset_t*
+fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum,
+ unsigned int banknum, unsigned int prognum);
+static fluid_preset_t*
+fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname,
+ unsigned int banknum, unsigned int prognum);
+
+static void fluid_synth_update_presets(fluid_synth_t* synth);
+static int fluid_synth_update_sample_rate(fluid_synth_t* synth,
+ char* name, double value);
+static int fluid_synth_update_gain(fluid_synth_t* synth,
+ char* name, double value);
+static void fluid_synth_update_gain_LOCAL(fluid_synth_t* synth);
+static int fluid_synth_update_polyphony(fluid_synth_t* synth,
+ char* name, int value);
+static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth, int new_polyphony);
+static void init_dither(void);
+static inline int roundi (float x);
+static int fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount);
+//static void fluid_synth_core_thread_func (void* data);
+//static FLUID_INLINE void fluid_synth_process_event_queue_LOCAL
+// (fluid_synth_t *synth, fluid_event_queue_t *queue);
+static fluid_voice_t* fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth);
+static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth,
+ fluid_voice_t* new_voice);
+static fluid_sfont_info_t *new_fluid_sfont_info (fluid_synth_t *synth,
+ fluid_sfont_t *sfont);
+static int fluid_synth_sfunload_callback(void* data, unsigned int msec);
+static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth,
+ int chan, int key);
+static fluid_tuning_t* fluid_synth_get_tuning(fluid_synth_t* synth,
+ int bank, int prog);
+static int fluid_synth_replace_tuning_LOCK (fluid_synth_t* synth,
+ fluid_tuning_t *tuning,
+ int bank, int prog, int apply);
+static void fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth,
+ fluid_tuning_t *old_tuning,
+ fluid_tuning_t *new_tuning,
+ int apply, int unref_new);
+static void fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth,
+ fluid_channel_t *channel);
+static int fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan,
+ fluid_tuning_t *tuning, int apply);
+static void fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan,
+ int param, float value, int absolute);
+static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id);
+
+
+
+/***************************************************************
+ *
+ * GLOBAL
+ */
+
+/* has the synth module been initialized? */
+static int fluid_synth_initialized = 0;
+static void fluid_synth_init(void);
+static void init_dither(void);
+
+/* default modulators
+ * SF2.01 page 52 ff:
+ *
+ * There is a set of predefined default modulators. They have to be
+ * explicitly overridden by the sound font in order to turn them off.
+ */
+
+fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */
+fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */
+fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */
+fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */
+fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */
+fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */
+fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */
+fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */
+fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */
+fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */
+
+/* reverb presets */
+static fluid_revmodel_presets_t revmodel_preset[] = {
+ /* name */ /* roomsize */ /* damp */ /* width */ /* level */
+ { "Test 1", 0.2f, 0.0f, 0.5f, 0.9f },
+ { "Test 2", 0.4f, 0.2f, 0.5f, 0.8f },
+ { "Test 3", 0.6f, 0.4f, 0.5f, 0.7f },
+ { "Test 4", 0.8f, 0.7f, 0.5f, 0.6f },
+ { "Test 5", 0.8f, 1.0f, 0.5f, 0.5f },
+ { NULL, 0.0f, 0.0f, 0.0f, 0.0f }
+};
+
+
+/***************************************************************
+ *
+ * INITIALIZATION & UTILITIES
+ */
+
+static void fluid_synth_register_overflow(fluid_settings_t* settings,
+ fluid_num_update_t update_func,
+ void* update_data)
+{
+ fluid_settings_register_num(settings, "synth.overflow.percussion",
+ 4000, -10000, 10000, 0, update_func, update_data);
+ fluid_settings_register_num(settings, "synth.overflow.sustained",
+ -1000, -10000, 10000, 0, update_func, update_data);
+ fluid_settings_register_num(settings, "synth.overflow.released",
+ -2000, -10000, 10000, 0, update_func, update_data);
+ fluid_settings_register_num(settings, "synth.overflow.age",
+ 1000, -10000, 10000, 0, update_func, update_data);
+ fluid_settings_register_num(settings, "synth.overflow.volume",
+ 500, -10000, 10000, 0, update_func, update_data);
+}
+
+void fluid_synth_settings(fluid_settings_t* settings)
+{
+ fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.dump", 0, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+ fluid_settings_register_str(settings, "midi.portname", "", 0, NULL, NULL);
+
+ fluid_settings_register_str(settings, "synth.default-soundfont",
+ DEFAULT_SOUNDFONT, 0, NULL, NULL);
+
+ fluid_settings_register_int(settings, "synth.polyphony",
+ 256, 1, 65535, 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.midi-channels",
+ 16, 16, 256, 0, NULL, NULL);
+ fluid_settings_register_num(settings, "synth.gain",
+ 0.2f, 0.0f, 10.0f,
+ 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.audio-channels",
+ 1, 1, 128, 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.audio-groups",
+ 1, 1, 128, 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.effects-channels",
+ 2, 2, 2, 0, NULL, NULL);
+ fluid_settings_register_num(settings, "synth.sample-rate",
+ 44100.0f, 8000.0f, 96000.0f,
+ 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.device-id",
+ 0, 0, 126, 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0, NULL, NULL);
+
+ fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0, NULL, NULL);
+
+ fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.parallel-render", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+
+ fluid_synth_register_overflow(settings, NULL, NULL);
+
+ fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0, NULL, NULL);
+ fluid_settings_add_option(settings, "synth.midi-bank-select", "gm");
+ fluid_settings_add_option(settings, "synth.midi-bank-select", "gs");
+ fluid_settings_add_option(settings, "synth.midi-bank-select", "xg");
+ fluid_settings_add_option(settings, "synth.midi-bank-select", "mma");
+
+}
+
+/**
+ * Get FluidSynth runtime version.
+ * @param major Location to store major number
+ * @param minor Location to store minor number
+ * @param micro Location to store micro number
+ */
+void fluid_version(int *major, int *minor, int *micro)
+{
+ *major = FLUIDSYNTH_VERSION_MAJOR;
+ *minor = FLUIDSYNTH_VERSION_MINOR;
+ *micro = FLUIDSYNTH_VERSION_MICRO;
+}
+
+/**
+ * Get FluidSynth runtime version as a string.
+ * @return FluidSynth version string, which is internal and should not be
+ * modified or freed.
+ */
+char *
+fluid_version_str (void)
+{
+ return FLUIDSYNTH_VERSION;
+}
+
+#define FLUID_API_ENTRY_CHAN(fail_value) \
+ fluid_return_val_if_fail (synth != NULL, fail_value); \
+ fluid_return_val_if_fail (chan >= 0, fail_value); \
+ fluid_synth_api_enter(synth); \
+ if (chan >= synth->midi_channels) { \
+ fluid_synth_api_exit(synth); \
+ return fail_value; \
+ } \
+
+#define FLUID_API_RETURN(return_value) \
+ do { fluid_synth_api_exit(synth); \
+ return return_value; } while (0)
+
+/*
+ * void fluid_synth_init
+ *
+ * Does all the initialization for this module.
+ */
+static void
+fluid_synth_init(void)
+{
+ fluid_synth_initialized++;
+
+#ifdef TRAP_ON_FPE
+ /* Turn on floating point exception traps */
+ feenableexcept (FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID);
+#endif
+
+ fluid_conversion_config();
+
+ fluid_rvoice_dsp_config();
+
+ fluid_sys_config();
+
+ init_dither();
+
+
+ /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */
+ fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */
+ FLUID_MOD_VELOCITY, /* Source. VELOCITY corresponds to 'index=2'. */
+ FLUID_MOD_GC /* Not a MIDI continuous controller */
+ | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */
+ | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */
+ | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */
+ );
+ fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */
+ fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */
+ fluid_mod_set_amount(&default_vel2att_mod, 960.0); /* Modulation amount: 960 */
+
+
+
+ /* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff
+ * Have to make a design decision here. The specs don't make any sense this way or another.
+ * One sound font, 'Kingston Piano', which has been praised for its quality, tries to
+ * override this modulator with an amount of 0 and positive polarity (instead of what
+ * the specs say, D=1) for the secondary source.
+ * So if we change the polarity to 'positive', one of the best free sound fonts works...
+ */
+ fluid_mod_set_source1(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */
+ FLUID_MOD_GC /* CC=0 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_NEGATIVE /* D=1 */
+ );
+ fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */
+ FLUID_MOD_GC /* CC=0 */
+ | FLUID_MOD_SWITCH /* type=3 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ // do not remove | FLUID_MOD_NEGATIVE /* D=1 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */
+ fluid_mod_set_amount(&default_vel2filter_mod, -2400);
+
+
+
+ /* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */
+ fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */
+ FLUID_MOD_GC /* CC=0 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_source2(&default_at2viblfo_mod, 0,0); /* no second source */
+ fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */
+ fluid_mod_set_amount(&default_at2viblfo_mod, 50);
+
+
+
+ /* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */
+ fluid_mod_set_source1(&default_mod2viblfo_mod, 1, /* Index=1 */
+ FLUID_MOD_CC /* CC=1 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_source2(&default_mod2viblfo_mod, 0,0); /* no second source */
+ fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */
+ fluid_mod_set_amount(&default_mod2viblfo_mod, 50);
+
+
+
+ /* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/
+ fluid_mod_set_source1(&default_att_mod, 7, /* index=7 */
+ FLUID_MOD_CC /* CC=1 */
+ | FLUID_MOD_CONCAVE /* type=1 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_NEGATIVE /* D=1 */
+ );
+ fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */
+ fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */
+ fluid_mod_set_amount(&default_att_mod, 960.0); /* Amount: 960 */
+
+
+
+ /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */
+ fluid_mod_set_source1(&default_pan_mod, 10, /* index=10 */
+ FLUID_MOD_CC /* CC=1 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_BIPOLAR /* P=1 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */
+ fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */
+ /* Amount: 500. The SF specs $8.4.6, p. 55 syas: "Amount = 1000
+ tenths of a percent". The center value (64) corresponds to 50%,
+ so it follows that amount = 50% x 1000/% = 500. */
+ fluid_mod_set_amount(&default_pan_mod, 500.0);
+
+
+ /* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/
+ fluid_mod_set_source1(&default_expr_mod, 11, /* index=11 */
+ FLUID_MOD_CC /* CC=1 */
+ | FLUID_MOD_CONCAVE /* type=1 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_NEGATIVE /* D=1 */
+ );
+ fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */
+ fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */
+ fluid_mod_set_amount(&default_expr_mod, 960.0); /* Amount: 960 */
+
+
+
+ /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */
+ fluid_mod_set_source1(&default_reverb_mod, 91, /* index=91 */
+ FLUID_MOD_CC /* CC=1 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_source2(&default_reverb_mod, 0, 0); /* No second source */
+ fluid_mod_set_dest(&default_reverb_mod, GEN_REVERBSEND); /* Target: Reverb send */
+ fluid_mod_set_amount(&default_reverb_mod, 200); /* Amount: 200 ('tenths of a percent') */
+
+
+
+ /* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Chorus send */
+ fluid_mod_set_source1(&default_chorus_mod, 93, /* index=93 */
+ FLUID_MOD_CC /* CC=1 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_source2(&default_chorus_mod, 0, 0); /* No second source */
+ fluid_mod_set_dest(&default_chorus_mod, GEN_CHORUSSEND); /* Target: Chorus */
+ fluid_mod_set_amount(&default_chorus_mod, 200); /* Amount: 200 ('tenths of a percent') */
+
+
+
+ /* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */
+ fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */
+ FLUID_MOD_GC /* CC =0 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_BIPOLAR /* P=1 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */
+ FLUID_MOD_GC /* CC=0 */
+ | FLUID_MOD_LINEAR /* type=0 */
+ | FLUID_MOD_UNIPOLAR /* P=0 */
+ | FLUID_MOD_POSITIVE /* D=0 */
+ );
+ fluid_mod_set_dest(&default_pitch_bend_mod, GEN_PITCH); /* Destination: Initial pitch */
+ fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */
+}
+
+static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t* synth)
+{
+ if (synth->eventhandler->is_threadsafe)
+ return fluid_atomic_int_get(&synth->ticks_since_start);
+ else
+ return synth->ticks_since_start;
+}
+
+static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t* synth, int val)
+{
+ if (synth->eventhandler->is_threadsafe)
+ fluid_atomic_int_add((int*) &synth->ticks_since_start, val);
+ else
+ synth->ticks_since_start += val;
+}
+
+
+/***************************************************************
+ * FLUID SAMPLE TIMERS
+ * Timers that use written audio data as timing reference
+ */
+struct _fluid_sample_timer_t
+{
+ fluid_sample_timer_t* next; /* Single linked list of timers */
+ unsigned long starttick;
+ fluid_timer_callback_t callback;
+ void* data;
+ int isfinished;
+};
+
+/*
+ * fluid_sample_timer_process - called when synth->ticks is updated
+ */
+static void fluid_sample_timer_process(fluid_synth_t* synth)
+{
+ fluid_sample_timer_t* st;
+ long msec;
+ int cont;
+ unsigned int ticks = fluid_synth_get_ticks(synth);
+
+ for (st=synth->sample_timers; st; st=st->next) {
+ if (st->isfinished) {
+ continue;
+ }
+
+ msec = (long) (1000.0*((double) (ticks - st->starttick))/synth->sample_rate);
+ cont = (*st->callback)(st->data, msec);
+ if (cont == 0) {
+ st->isfinished = 1;
+ }
+ }
+}
+
+fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data)
+{
+ fluid_sample_timer_t* result = FLUID_NEW(fluid_sample_timer_t);
+ if (result == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ result->starttick = fluid_synth_get_ticks(synth);
+ result->isfinished = 0;
+ result->data = data;
+ result->callback = callback;
+ result->next = synth->sample_timers;
+ synth->sample_timers = result;
+ return result;
+}
+
+int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer)
+{
+ fluid_sample_timer_t** ptr = &synth->sample_timers;
+ while (*ptr) {
+ if (*ptr == timer) {
+ *ptr = timer->next;
+ FLUID_FREE(timer);
+ return FLUID_OK;
+ }
+ ptr = &((*ptr)->next);
+ }
+ FLUID_LOG(FLUID_ERR,"delete_fluid_sample_timer failed, no timer found");
+ return FLUID_FAILED;
+}
+
+
+/***************************************************************
+ *
+ * FLUID SYNTH
+ */
+
+static FLUID_INLINE void
+fluid_synth_update_mixer(fluid_synth_t* synth, void* method, int intparam,
+ fluid_real_t realparam)
+{
+ fluid_return_if_fail(synth != NULL || synth->eventhandler != NULL);
+ fluid_return_if_fail(synth->eventhandler->mixer != NULL);
+ fluid_rvoice_eventhandler_push(synth->eventhandler, method,
+ synth->eventhandler->mixer,
+ intparam, realparam);
+}
+
+
+/**
+ * Create new FluidSynth instance.
+ * @param settings Configuration parameters to use (used directly).
+ * @return New FluidSynth instance or NULL on error
+ *
+ * NOTE: The settings parameter is used directly and should not be modified
+ * or freed independently.
+ */
+fluid_synth_t*
+new_fluid_synth(fluid_settings_t *settings)
+{
+ fluid_synth_t* synth;
+ fluid_sfloader_t* loader;
+ double gain;
+ int i, nbuf;
+
+ /* initialize all the conversion tables and other stuff */
+ if (fluid_synth_initialized == 0) {
+ fluid_synth_init();
+ }
+
+ /* allocate a new synthesizer object */
+ synth = FLUID_NEW(fluid_synth_t);
+ if (synth == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t));
+
+ fluid_rec_mutex_init(synth->mutex);
+ fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex);
+ synth->public_api_count = 0;
+
+ synth->settings = settings;
+
+ fluid_settings_getint(settings, "synth.reverb.active", &synth->with_reverb);
+ fluid_settings_getint(settings, "synth.chorus.active", &synth->with_chorus);
+ fluid_settings_getint(settings, "synth.verbose", &synth->verbose);
+ fluid_settings_getint(settings, "synth.dump", &synth->dump);
+
+ fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony);
+ fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate);
+ fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels);
+ fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels);
+ fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups);
+ fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels);
+ fluid_settings_getnum(settings, "synth.gain", &gain);
+ synth->gain = gain;
+ fluid_settings_getint(settings, "synth.device-id", &synth->device_id);
+ fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores);
+
+ /* register the callbacks */
+ fluid_settings_register_num(settings, "synth.sample-rate",
+ 44100.0f, 8000.0f, 96000.0f, 0,
+ (fluid_num_update_t) fluid_synth_update_sample_rate, synth);
+ fluid_settings_register_num(settings, "synth.gain",
+ 0.2f, 0.0f, 10.0f, 0,
+ (fluid_num_update_t) fluid_synth_update_gain, synth);
+ fluid_settings_register_int(settings, "synth.polyphony",
+ synth->polyphony, 1, 65535, 0,
+ (fluid_int_update_t) fluid_synth_update_polyphony,
+ synth);
+ fluid_settings_register_int(settings, "synth.device-id",
+ synth->device_id, 126, 0, 0,
+ (fluid_int_update_t) fluid_synth_update_device_id, synth);
+
+ fluid_synth_register_overflow(settings,
+ (fluid_num_update_t) fluid_synth_update_overflow, synth);
+
+ /* do some basic sanity checking on the settings */
+
+ if (synth->midi_channels % 16 != 0) {
+ int n = synth->midi_channels / 16;
+ synth->midi_channels = (n + 1) * 16;
+ fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels);
+ FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. "
+ "I'll increase the number of channels to the next multiple.");
+ }
+
+ if (synth->audio_channels < 1) {
+ FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. "
+ "Changing this setting to 1.");
+ synth->audio_channels = 1;
+ } else if (synth->audio_channels > 128) {
+ FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). "
+ "Limiting this setting to 128.", synth->audio_channels);
+ synth->audio_channels = 128;
+ }
+
+ if (synth->audio_groups < 1) {
+ FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. "
+ "Changing this setting to 1.");
+ synth->audio_groups = 1;
+ } else if (synth->audio_groups > 128) {
+ FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). "
+ "Limiting this setting to 128.", synth->audio_groups);
+ synth->audio_groups = 128;
+ }
+
+ if (synth->effects_channels < 2) {
+ FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)."
+ "Setting effects channels to 2.", synth->effects_channels);
+ synth->effects_channels = 2;
+ }
+
+
+ /* The number of buffers is determined by the higher number of nr
+ * groups / nr audio channels. If LADSPA is unused, they should be
+ * the same. */
+ nbuf = synth->audio_channels;
+ if (synth->audio_groups > nbuf) {
+ nbuf = synth->audio_groups;
+ }
+
+ /* as soon as the synth is created it starts playing. */
+ synth->state = FLUID_SYNTH_PLAYING;
+ synth->sfont_info = NULL;
+ synth->sfont_hash = new_fluid_hashtable (NULL, NULL);
+ synth->noteid = 0;
+ synth->ticks_since_start = 0;
+ synth->tuning = NULL;
+ fluid_private_init(synth->tuning_iter);
+
+ /* Allocate event queue for rvoice mixer */
+ fluid_settings_getint(settings, "synth.parallel-render", &i);
+ /* In an overflow situation, a new voice takes about 50 spaces in the queue! */
+ synth->eventhandler = new_fluid_rvoice_eventhandler(i, synth->polyphony*64,
+ synth->polyphony, nbuf, synth->effects_channels, synth->sample_rate);
+
+ if (synth->eventhandler == NULL)
+ goto error_recovery;
+
+#ifdef LADSPA
+ /* Create and initialize the Fx unit.*/
+ synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth);
+ fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->LADSPA_FxUnit);
+#endif
+
+ /* allocate and add the default sfont loader */
+ loader = new_fluid_defsfloader(settings);
+
+ if (loader == NULL) {
+ FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader");
+ } else {
+ fluid_synth_add_sfloader(synth, loader);
+ }
+
+ /* allocate all channel objects */
+ synth->channel = FLUID_ARRAY(fluid_channel_t*, synth->midi_channels);
+ if (synth->channel == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+ for (i = 0; i < synth->midi_channels; i++) {
+ synth->channel[i] = new_fluid_channel(synth, i);
+ if (synth->channel[i] == NULL) {
+ goto error_recovery;
+ }
+ }
+
+ /* allocate all synthesis processes */
+ synth->nvoice = synth->polyphony;
+ synth->voice = FLUID_ARRAY(fluid_voice_t*, synth->nvoice);
+ if (synth->voice == NULL) {
+ goto error_recovery;
+ }
+ for (i = 0; i < synth->nvoice; i++) {
+ synth->voice[i] = new_fluid_voice(synth->sample_rate);
+ if (synth->voice[i] == NULL) {
+ goto error_recovery;
+ }
+ }
+
+ fluid_synth_set_sample_rate(synth, synth->sample_rate);
+
+ fluid_synth_update_overflow(synth, "", 0.0f);
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony,
+ synth->polyphony, 0.0f);
+ fluid_synth_set_reverb_on(synth, synth->with_reverb);
+ fluid_synth_set_chorus_on(synth, synth->with_chorus);
+
+ synth->cur = FLUID_BUFSIZE;
+ synth->curmax = 0;
+ synth->dither_index = 0;
+
+ synth->reverb_roomsize = FLUID_REVERB_DEFAULT_ROOMSIZE;
+ synth->reverb_damping = FLUID_REVERB_DEFAULT_DAMP;
+ synth->reverb_width = FLUID_REVERB_DEFAULT_WIDTH;
+ synth->reverb_level = FLUID_REVERB_DEFAULT_LEVEL;
+
+ fluid_rvoice_eventhandler_push5(synth->eventhandler,
+ fluid_rvoice_mixer_set_reverb_params,
+ synth->eventhandler->mixer,
+ FLUID_REVMODEL_SET_ALL, synth->reverb_roomsize,
+ synth->reverb_damping, synth->reverb_width,
+ synth->reverb_level, 0.0f);
+
+ /* Initialize multi-core variables if multiple cores enabled */
+ if (synth->cores > 1)
+ {
+ int prio_level = 0;
+ fluid_settings_getint (synth->settings, "audio.realtime-prio", &prio_level);
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_threads,
+ synth->cores-1, prio_level);
+ }
+
+ synth->bank_select = FLUID_BANK_STYLE_GS;
+ if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "gm") == 1)
+ synth->bank_select = FLUID_BANK_STYLE_GM;
+ else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "gs") == 1)
+ synth->bank_select = FLUID_BANK_STYLE_GS;
+ else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "xg") == 1)
+ synth->bank_select = FLUID_BANK_STYLE_XG;
+ else if (fluid_settings_str_equal (settings, "synth.midi-bank-select", "mma") == 1)
+ synth->bank_select = FLUID_BANK_STYLE_MMA;
+
+ fluid_synth_process_event_queue(synth);
+
+ /* FIXME */
+ synth->start = fluid_curtime();
+
+ return synth;
+
+ error_recovery:
+ delete_fluid_synth(synth);
+ return NULL;
+}
+
+
+/**
+ * Delete a FluidSynth instance.
+ * @param synth FluidSynth instance to delete
+ * @return FLUID_OK
+ *
+ * NOTE: Other users of a synthesizer instance, such as audio and MIDI drivers,
+ * should be deleted prior to freeing the FluidSynth instance.
+ */
+int
+delete_fluid_synth(fluid_synth_t* synth)
+{
+ int i, k;
+ fluid_list_t *list;
+ fluid_sfont_info_t* sfont_info;
+// fluid_event_queue_t* queue;
+ fluid_sfloader_t* loader;
+
+ if (synth == NULL) {
+ return FLUID_OK;
+ }
+
+ fluid_profiling_print();
+
+ /* turn off all voices, needed to unload SoundFont data */
+ if (synth->voice != NULL) {
+ for (i = 0; i < synth->nvoice; i++) {
+ fluid_voice_t* voice = synth->voice[i];
+ if (!voice)
+ continue;
+ fluid_voice_unlock_rvoice(voice);
+ fluid_voice_overflow_rvoice_finished(voice);
+ if (fluid_voice_is_playing(voice))
+ fluid_voice_off(voice);
+ }
+ }
+
+ /* also unset all presets for clean SoundFont unload */
+ if (synth->channel != NULL)
+ for (i = 0; i < synth->midi_channels; i++)
+ if (synth->channel[i] != NULL)
+ fluid_channel_set_preset(synth->channel[i], NULL);
+
+ if (synth->eventhandler)
+ delete_fluid_rvoice_eventhandler(synth->eventhandler);
+
+ /* delete all the SoundFonts */
+ for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
+ sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+ delete_fluid_sfont (sfont_info->sfont);
+ FLUID_FREE (sfont_info);
+ }
+
+ delete_fluid_list(synth->sfont_info);
+
+
+ /* Delete the SoundFont info hash */
+ if (synth->sfont_hash) delete_fluid_hashtable (synth->sfont_hash);
+
+
+ /* delete all the SoundFont loaders */
+
+ for (list = synth->loaders; list; list = fluid_list_next(list)) {
+ loader = (fluid_sfloader_t*) fluid_list_get(list);
+ fluid_sfloader_delete(loader);
+ }
+
+ delete_fluid_list(synth->loaders);
+
+
+ if (synth->channel != NULL) {
+ for (i = 0; i < synth->midi_channels; i++) {
+ if (synth->channel[i] != NULL) {
+ delete_fluid_channel(synth->channel[i]);
+ }
+ }
+ FLUID_FREE(synth->channel);
+ }
+
+ if (synth->voice != NULL) {
+ for (i = 0; i < synth->nvoice; i++) {
+ if (synth->voice[i] != NULL) {
+ delete_fluid_voice(synth->voice[i]);
+ }
+ }
+ FLUID_FREE(synth->voice);
+ }
+
+
+ /* free the tunings, if any */
+ if (synth->tuning != NULL) {
+ for (i = 0; i < 128; i++) {
+ if (synth->tuning[i] != NULL) {
+ for (k = 0; k < 128; k++) {
+ if (synth->tuning[i][k] != NULL) {
+ delete_fluid_tuning(synth->tuning[i][k]);
+ }
+ }
+ FLUID_FREE(synth->tuning[i]);
+ }
+ }
+ FLUID_FREE(synth->tuning);
+ }
+
+ fluid_private_free (synth->tuning_iter);
+
+#ifdef LADSPA
+ /* Release the LADSPA Fx unit */
+ fluid_LADSPA_shutdown(synth->LADSPA_FxUnit);
+ FLUID_FREE(synth->LADSPA_FxUnit);
+#endif
+
+ fluid_rec_mutex_destroy(synth->mutex);
+
+ FLUID_FREE(synth);
+
+ return FLUID_OK;
+}
+
+/**
+ * Get a textual representation of the last error
+ * @param synth FluidSynth instance
+ * @return Pointer to string of last error message. Valid until the same
+ * calling thread calls another FluidSynth function which fails. String is
+ * internal and should not be modified or freed.
+ */
+/* FIXME - The error messages are not thread-safe, yet. They are still stored
+ * in a global message buffer (see fluid_sys.c). */
+char*
+fluid_synth_error(fluid_synth_t* synth)
+{
+ return fluid_error();
+}
+
+/**
+ * Send a note-on event to a FluidSynth object.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param key MIDI note number (0-127)
+ * @param vel MIDI velocity (0-127, 0=noteoff)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
+{
+ int result;
+ fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED);
+ fluid_return_val_if_fail (vel >= 0 && vel <= 127, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ result = fluid_synth_noteon_LOCAL (synth, chan, key, vel);
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of fluid_synth_noteon */
+static int
+fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel)
+{
+ fluid_channel_t* channel;
+
+ /* notes with velocity zero go to noteoff */
+ if (vel == 0) return fluid_synth_noteoff_LOCAL(synth, chan, key);
+
+ channel = synth->channel[chan];
+
+ /* make sure this channel has a preset */
+ if (channel->preset == NULL) {
+ if (synth->verbose) {
+ FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s",
+ chan, key, vel, 0,
+ fluid_synth_get_ticks(synth) / 44100.0f,
+ (fluid_curtime() - synth->start) / 1000.0f,
+ 0.0f, 0, "channel has no preset");
+ }
+ return FLUID_FAILED;
+ }
+
+ /* If there is another voice process on the same channel and key,
+ advance it to the release phase. */
+ fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key);
+
+
+ return fluid_preset_noteon(channel->preset, synth, chan, key, vel);
+}
+
+/**
+ * Send a note-off event to a FluidSynth object.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param key MIDI note number (0-127)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise (may just mean that no
+ * voices matched the note off event)
+ */
+int
+fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key)
+{
+ int result;
+ fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ result = fluid_synth_noteoff_LOCAL (synth, chan, key);
+
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of fluid_synth_noteoff */
+static int
+fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key)
+{
+ fluid_voice_t* voice;
+ int status = FLUID_FAILED;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (_ON(voice) && (voice->chan == chan) && (voice->key == key)) {
+ if (synth->verbose) {
+ int used_voices = 0;
+ int k;
+ for (k = 0; k < synth->polyphony; k++) {
+ if (!_AVAILABLE(synth->voice[k])) {
+ used_voices++;
+ }
+ }
+ FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d",
+ voice->chan, voice->key, 0, voice->id,
+ (fluid_curtime() - synth->start) / 1000.0f,
+ used_voices);
+ } /* if verbose */
+
+ fluid_voice_noteoff(voice);
+ status = FLUID_OK;
+ } /* if voice on */
+ } /* for all voices */
+ return status;
+}
+
+/* Damp voices on a channel (turn notes off), if they're sustained by
+ sustain pedal */
+static int
+fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t* synth, int chan)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if ((voice->chan == chan) && _SUSTAINED(voice))
+ fluid_voice_release(voice);
+ }
+
+ return FLUID_OK;
+}
+
+/* Damp voices on a channel (turn notes off), if they're sustained by
+ sostenuto pedal */
+static int
+fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t* synth, int chan)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if ((voice->chan == chan) && _HELD_BY_SOSTENUTO(voice))
+ fluid_voice_release(voice);
+ }
+
+ return FLUID_OK;
+}
+
+
+/**
+ * Send a MIDI controller event on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param num MIDI controller number (0-127)
+ * @param val MIDI controller value (0-127)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val)
+{
+ int result;
+ fluid_return_val_if_fail (num >= 0 && num <= 127, FLUID_FAILED);
+ fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ if (synth->verbose)
+ FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val);
+
+ fluid_channel_set_cc (synth->channel[chan], num, val);
+ result = fluid_synth_cc_LOCAL (synth, chan, num);
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of MIDI CC set function. */
+static int
+fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num)
+{
+ fluid_channel_t* chan = synth->channel[channum];
+ int nrpn_select;
+ int value;
+
+ value = fluid_channel_get_cc (chan, num);
+
+ switch (num) {
+ case SUSTAIN_SWITCH:
+ /* Release voices if Sustain switch is released */
+ if (value < 64) /* Sustain is released */
+ fluid_synth_damp_voices_by_sustain_LOCAL (synth, channum);
+ break;
+
+ case SOSTENUTO_SWITCH:
+ /* Release voices if Sostetuno switch is released */
+ if (value < 64) /* Sostenuto is released */
+ fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum);
+ else /* Sostenuto is depressed */
+ /* Update sostenuto order id when pedaling on Sostenuto */
+ chan->sostenuto_orderid = synth->noteid; /* future voice id value */
+ break;
+
+ case BANK_SELECT_MSB:
+ fluid_channel_set_bank_msb (chan, value & 0x7F);
+ break;
+ case BANK_SELECT_LSB:
+ fluid_channel_set_bank_lsb (chan, value & 0x7F);
+ break;
+ case ALL_NOTES_OFF:
+ fluid_synth_all_notes_off_LOCAL (synth, channum);
+ break;
+ case ALL_SOUND_OFF:
+ fluid_synth_all_sounds_off_LOCAL (synth, channum);
+ break;
+ case ALL_CTRL_OFF:
+ fluid_channel_init_ctrl (chan, 1);
+ fluid_synth_modulate_voices_all_LOCAL (synth, channum);
+ break;
+ case DATA_ENTRY_MSB:
+ {
+ int data = (value << 7) + fluid_channel_get_cc (chan, DATA_ENTRY_LSB);
+
+ if (chan->nrpn_active) /* NRPN is active? */
+ { /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
+ if ((fluid_channel_get_cc (chan, NRPN_MSB) == 120)
+ && (fluid_channel_get_cc (chan, NRPN_LSB) < 100))
+ {
+ nrpn_select = chan->nrpn_select;
+
+ if (nrpn_select < GEN_LAST)
+ {
+ float val = fluid_gen_scale_nrpn (nrpn_select, data);
+ fluid_synth_set_gen_LOCAL (synth, channum, nrpn_select, val, FALSE);
+ }
+
+ chan->nrpn_select = 0; /* Reset to 0 */
+ }
+ }
+ else if (fluid_channel_get_cc (chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */
+ {
+ switch (fluid_channel_get_cc (chan, RPN_LSB))
+ {
+ case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones */
+ fluid_channel_set_pitch_wheel_sensitivity (synth->channel[channum], value);
+ fluid_synth_update_pitch_wheel_sens_LOCAL (synth, channum); /* Update bend range */
+ /* FIXME - Handle LSB? (Fine bend range in cents) */
+ break;
+ case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over 1 semitone (+/- 50 cents, 8192 = center) */
+ fluid_synth_set_gen_LOCAL (synth, channum, GEN_FINETUNE,
+ (data - 8192) / 8192.0 * 50.0, FALSE);
+ break;
+ case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */
+ fluid_synth_set_gen_LOCAL (synth, channum, GEN_COARSETUNE,
+ value - 64, FALSE);
+ break;
+ case RPN_TUNING_PROGRAM_CHANGE:
+ fluid_channel_set_tuning_prog (chan, value);
+ fluid_synth_activate_tuning (synth, channum,
+ fluid_channel_get_tuning_bank (chan),
+ value, TRUE);
+ break;
+ case RPN_TUNING_BANK_SELECT:
+ fluid_channel_set_tuning_bank (chan, value);
+ break;
+ case RPN_MODULATION_DEPTH_RANGE:
+ break;
+ }
+ }
+ break;
+ }
+ case NRPN_MSB:
+ fluid_channel_set_cc (chan, NRPN_LSB, 0);
+ chan->nrpn_select = 0;
+ chan->nrpn_active = 1;
+ break;
+ case NRPN_LSB:
+ /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
+ if (fluid_channel_get_cc (chan, NRPN_MSB) == 120) {
+ if (value == 100) {
+ chan->nrpn_select += 100;
+ } else if (value == 101) {
+ chan->nrpn_select += 1000;
+ } else if (value == 102) {
+ chan->nrpn_select += 10000;
+ } else if (value < 100) {
+ chan->nrpn_select += value;
+ }
+ }
+
+ chan->nrpn_active = 1;
+ break;
+ case RPN_MSB:
+ case RPN_LSB:
+ chan->nrpn_active = 0;
+ break;
+ default:
+ return fluid_synth_modulate_voices_LOCAL (synth, channum, 1, num);
+ }
+
+ return FLUID_OK;
+}
+
+/**
+ * Get current MIDI controller value on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param num MIDI controller number (0-127)
+ * @param pval Location to store MIDI controller value (0-127)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval)
+{
+ fluid_return_val_if_fail (num >= 0 && num < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (pval != NULL, FLUID_FAILED);
+
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ *pval = fluid_channel_get_cc (synth->channel[chan], num);
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/*
+ * Handler for synth.device-id setting.
+ */
+static int
+fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value)
+{
+ fluid_synth_api_enter(synth);
+ synth->device_id = value;
+ fluid_synth_api_exit(synth);
+ return 0;
+}
+
+/**
+ * Process a MIDI SYSEX (system exclusive) message.
+ * @param synth FluidSynth instance
+ * @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7)
+ * @param len Length of data in buffer
+ * @param response Buffer to store response to or NULL to ignore
+ * @param response_len IN/OUT parameter, in: size of response buffer, out:
+ * amount of data written to response buffer (if FLUID_FAILED is returned and
+ * this value is non-zero, it indicates the response buffer is too small)
+ * @param handled Optional location to store boolean value if message was
+ * recognized and handled or not (set to TRUE if it was handled)
+ * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX
+ * command (useful for checking if a SYSEX message would be handled)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ */
+/* SYSEX format (0xF0 and 0xF7 not passed to this function):
+ * Non-realtime: 0xF0 0x7E <DeviceId> [BODY] 0xF7
+ * Realtime: 0xF0 0x7F <DeviceId> [BODY] 0xF7
+ * Tuning messages: 0xF0 0x7E/0x7F <DeviceId> 0x08 <sub ID2> [BODY] <ChkSum> 0xF7
+ */
+int
+fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len,
+ char *response, int *response_len, int *handled, int dryrun)
+{
+ int avail_response = 0;
+
+ if (handled) *handled = FALSE;
+
+ if (response_len)
+ {
+ avail_response = *response_len;
+ *response_len = 0;
+ }
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (data != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (len > 0, FLUID_FAILED);
+ fluid_return_val_if_fail (!response || response_len, FLUID_FAILED);
+
+ if (len < 4) return FLUID_OK;
+
+ /* MIDI tuning SYSEX message? */
+ if ((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME)
+ && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL)
+ && data[2] == MIDI_SYSEX_MIDI_TUNING_ID)
+ {
+ int result;
+ fluid_synth_api_enter(synth);
+ result = fluid_synth_sysex_midi_tuning (synth, data, len, response,
+ response_len, avail_response,
+ handled, dryrun);
+
+ FLUID_API_RETURN(result);
+ }
+ return FLUID_OK;
+}
+
+/* Handler for MIDI tuning SYSEX messages */
+static int
+fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, int len,
+ char *response, int *response_len, int avail_response,
+ int *handled, int dryrun)
+{
+ int realtime, msgid;
+ int bank = 0, prog, channels;
+ double tunedata[128];
+ int keys[128];
+ char name[17];
+ int note, frac, frac2;
+ uint8 chksum;
+ int i, count, index;
+ const char *dataptr;
+ char *resptr;;
+
+ realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME;
+ msgid = data[3];
+
+ switch (msgid)
+ {
+ case MIDI_SYSEX_TUNING_BULK_DUMP_REQ:
+ case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK:
+ if (data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)
+ {
+ if (len != 5 || data[4] & 0x80 || !response)
+ return FLUID_OK;
+
+ *response_len = 406;
+ prog = data[4];
+ }
+ else
+ {
+ if (len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response)
+ return FLUID_OK;
+
+ *response_len = 407;
+ bank = data[4];
+ prog = data[5];
+ }
+
+ if (dryrun)
+ {
+ if (handled) *handled = TRUE;
+ return FLUID_OK;
+ }
+
+ if (avail_response < *response_len) return FLUID_FAILED;
+
+ /* Get tuning data, return if tuning not found */
+ if (fluid_synth_tuning_dump (synth, bank, prog, name, 17, tunedata) == FLUID_FAILED)
+ {
+ *response_len = 0;
+ return FLUID_OK;
+ }
+
+ resptr = response;
+
+ *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME;
+ *resptr++ = synth->device_id;
+ *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID;
+ *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP;
+
+ if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK)
+ *resptr++ = bank;
+
+ *resptr++ = prog;
+ FLUID_STRNCPY (resptr, name, 16);
+ resptr += 16;
+
+ for (i = 0; i < 128; i++)
+ {
+ note = tunedata[i] / 100.0;
+ fluid_clip (note, 0, 127);
+
+ frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0;
+ fluid_clip (frac, 0, 16383);
+
+ *resptr++ = note;
+ *resptr++ = frac >> 7;
+ *resptr++ = frac & 0x7F;
+ }
+
+ if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)
+ { /* NOTE: Checksum is not as straight forward as the bank based messages */
+ chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID
+ ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog;
+
+ for (i = 21; i < 128 * 3 + 21; i++)
+ chksum ^= response[i];
+ }
+ else
+ {
+ for (i = 1, chksum = 0; i < 406; i++)
+ chksum ^= response[i];
+ }
+
+ *resptr++ = chksum & 0x7F;
+
+ if (handled) *handled = TRUE;
+ break;
+ case MIDI_SYSEX_TUNING_NOTE_TUNE:
+ case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK:
+ dataptr = data + 4;
+
+ if (msgid == MIDI_SYSEX_TUNING_NOTE_TUNE)
+ {
+ if (len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6)
+ return FLUID_OK;
+ }
+ else
+ {
+ if (len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80
+ || len != data[5] * 4 + 7)
+ return FLUID_OK;
+
+ bank = *dataptr++;
+ }
+
+ if (dryrun)
+ {
+ if (handled) *handled = TRUE;
+ return FLUID_OK;
+ }
+
+ prog = *dataptr++;
+ count = *dataptr++;
+
+ for (i = 0, index = 0; i < count; i++)
+ {
+ note = *dataptr++;
+ if (note & 0x80) return FLUID_OK;
+ keys[index] = note;
+
+ note = *dataptr++;
+ frac = *dataptr++;
+ frac2 = *dataptr++;
+
+ if (note & 0x80 || frac & 0x80 || frac2 & 0x80)
+ return FLUID_OK;
+
+ frac = frac << 7 | frac2;
+
+ /* No change pitch value? Doesn't really make sense to send that, but.. */
+ if (note == 0x7F && frac == 16383) continue;
+
+ tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0);
+ index++;
+ }
+
+ if (index > 0)
+ {
+ if (fluid_synth_tune_notes (synth, bank, prog, index, keys, tunedata,
+ realtime) == FLUID_FAILED)
+ return FLUID_FAILED;
+ }
+
+ if (handled) *handled = TRUE;
+ break;
+ case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE:
+ case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE:
+ if ((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19)
+ || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31))
+ return FLUID_OK;
+
+ if (data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80)
+ return FLUID_OK;
+
+ if (dryrun)
+ {
+ if (handled) *handled = TRUE;
+ return FLUID_OK;
+ }
+
+ channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6];
+
+ if (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE)
+ {
+ for (i = 0; i < 12; i++)
+ {
+ frac = data[i + 7];
+ if (frac & 0x80) return FLUID_OK;
+ tunedata[i] = (int)frac - 64;
+ }
+ }
+ else
+ {
+ for (i = 0; i < 12; i++)
+ {
+ frac = data[i * 2 + 7];
+ frac2 = data[i * 2 + 8];
+ if (frac & 0x80 || frac2 & 0x80) return FLUID_OK;
+ tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0);
+ }
+ }
+
+ if (fluid_synth_activate_octave_tuning (synth, 0, 0, "SYSEX",
+ tunedata, realtime) == FLUID_FAILED)
+ return FLUID_FAILED;
+
+ if (channels)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ if (channels & (1 << i))
+ fluid_synth_activate_tuning (synth, i, 0, 0, realtime);
+ }
+ }
+
+ if (handled) *handled = TRUE;
+ break;
+ }
+
+ return FLUID_OK;
+}
+
+/**
+ * Turn off all notes on a MIDI channel (put them into release phase).
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.4
+ */
+int
+fluid_synth_all_notes_off(fluid_synth_t* synth, int chan)
+{
+ int result;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (chan >= -1, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ if (chan >= synth->midi_channels)
+ result = FLUID_FAILED;
+ else
+ result = fluid_synth_all_notes_off_LOCAL (synth, chan);
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */
+static int
+fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (_PLAYING(voice) && ((-1 == chan) || (chan == voice->chan)))
+ fluid_voice_noteoff(voice);
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Immediately stop all notes on a MIDI channel (skips release phase).
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.4
+ */
+int
+fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan)
+{
+ int result;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (chan >= -1, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ if (chan >= synth->midi_channels)
+ result = FLUID_FAILED;
+ else
+ result = fluid_synth_all_sounds_off_LOCAL (synth, chan);
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */
+static int
+fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (_PLAYING(voice) && ((-1 == chan) || (chan == voice->chan)))
+ fluid_voice_off(voice);
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Reset reverb engine
+ * @param synth FluidSynth instance
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_reset_reverb(fluid_synth_t* synth)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f);
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Reset chorus engine
+ * @param synth FluidSynth instance
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_reset_chorus(fluid_synth_t* synth)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f);
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+
+/**
+ * Send MIDI system reset command (big red 'panic' button), turns off notes and
+ * resets controllers.
+ * @param synth FluidSynth instance
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_system_reset(fluid_synth_t* synth)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ result = fluid_synth_system_reset_LOCAL (synth);
+ FLUID_API_RETURN(result);
+}
+
+/* Local variant of the system reset command */
+static int
+fluid_synth_system_reset_LOCAL(fluid_synth_t* synth)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (_PLAYING(voice))
+ fluid_voice_off(voice);
+ }
+
+ for (i = 0; i < synth->midi_channels; i++)
+ fluid_channel_reset(synth->channel[i]);
+
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_fx, 0, 0.0f);
+
+ return FLUID_OK;
+}
+
+/**
+ * Update voices on a MIDI channel after a MIDI control change.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param is_cc Boolean value indicating if ctrl is a CC controller or not
+ * @param ctrl MIDI controller value
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+static int
+fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (voice->chan == chan)
+ fluid_voice_modulate(voice, is_cc, ctrl);
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Update voices on a MIDI channel after all MIDI controllers have been changed.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+static int
+fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (voice->chan == chan)
+ fluid_voice_modulate_all(voice);
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Set the MIDI channel pressure controller value.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param val MIDI channel pressure value (0-127)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val)
+{
+ int result;
+ fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED);
+
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ if (synth->verbose)
+ FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val);
+
+ fluid_channel_set_channel_pressure (synth->channel[chan], val);
+
+ result = fluid_synth_update_channel_pressure_LOCAL (synth, chan);
+ FLUID_API_RETURN(result);
+}
+
+/* Updates channel pressure from within synthesis thread */
+static int
+fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int chan)
+{
+ return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_CHANNELPRESSURE);
+}
+
+/**
+ * Set the MIDI pitch bend controller value on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param val MIDI pitch bend value (0-16383 with 8192 being center)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val)
+{
+ int result;
+ fluid_return_val_if_fail (val >= 0 && val <= 16383, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ if (synth->verbose)
+ FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val);
+
+ fluid_channel_set_pitch_bend (synth->channel[chan], val);
+
+ result = fluid_synth_update_pitch_bend_LOCAL (synth, chan);
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of pitch bend */
+static int
+fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan)
+{
+ return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEEL);
+}
+
+/**
+ * Get the MIDI pitch bend controller value on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with
+ * 8192 being center)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend)
+{
+ fluid_return_val_if_fail (ppitch_bend != NULL, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ *ppitch_bend = fluid_channel_get_pitch_bend (synth->channel[chan]);
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Set MIDI pitch wheel sensitivity on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param val Pitch wheel sensitivity value in semitones
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val)
+{
+ int result;
+ fluid_return_val_if_fail (val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ if (synth->verbose)
+ FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val);
+
+ fluid_channel_set_pitch_wheel_sensitivity (synth->channel[chan], val);
+
+ result = fluid_synth_update_pitch_wheel_sens_LOCAL (synth, chan);
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of set pitch wheel sensitivity */
+static int
+fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan)
+{
+ return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEELSENS);
+}
+
+/**
+ * Get MIDI pitch wheel sensitivity on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param pval Location to store pitch wheel sensitivity value in semitones
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since Sometime AFTER v1.0 API freeze.
+ */
+int
+fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval)
+{
+ fluid_return_val_if_fail (pval != NULL, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ *pval = fluid_channel_get_pitch_wheel_sensitivity (synth->channel[chan]);
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Assign a preset to a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param preset Preset to assign to channel or NULL to clear (ownership is taken over)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+static int
+fluid_synth_set_preset (fluid_synth_t *synth, int chan, fluid_preset_t *preset)
+{
+ fluid_channel_t *channel;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
+
+ channel = synth->channel[chan];
+
+ return fluid_channel_set_preset (channel, preset);
+}
+
+/* Get a preset by SoundFont, bank and program numbers.
+ * Returns preset pointer or NULL.
+ *
+ * NOTE: The returned preset has been allocated, caller owns it and should
+ * free it when finished using it.
+ */
+static fluid_preset_t*
+fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum,
+ unsigned int banknum, unsigned int prognum)
+{
+ fluid_preset_t *preset = NULL;
+ fluid_sfont_info_t *sfont_info;
+ fluid_list_t *list;
+
+ /* 128 indicates an "unset" operation" */
+ if (prognum == FLUID_UNSET_PROGRAM) return NULL;
+
+ for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
+ sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+
+ if (fluid_sfont_get_id (sfont_info->sfont) == sfontnum)
+ {
+ preset = fluid_sfont_get_preset (sfont_info->sfont,
+ banknum - sfont_info->bankofs, prognum);
+ if (preset) sfont_info->refcount++; /* Add reference to SoundFont */
+ break;
+ }
+ }
+
+ return preset;
+}
+
+/* Get a preset by SoundFont name, bank and program.
+ * Returns preset pointer or NULL.
+ *
+ * NOTE: The returned preset has been allocated, caller owns it and should
+ * free it when finished using it.
+ */
+static fluid_preset_t*
+fluid_synth_get_preset_by_sfont_name(fluid_synth_t* synth, const char *sfontname,
+ unsigned int banknum, unsigned int prognum)
+{
+ fluid_preset_t *preset = NULL;
+ fluid_sfont_info_t *sfont_info;
+ fluid_list_t *list;
+
+ for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
+ sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+
+ if (FLUID_STRCMP (fluid_sfont_get_name (sfont_info->sfont), sfontname) == 0)
+ {
+ preset = fluid_sfont_get_preset (sfont_info->sfont,
+ banknum - sfont_info->bankofs, prognum);
+ if (preset) sfont_info->refcount++; /* Add reference to SoundFont */
+ break;
+ }
+ }
+
+ return preset;
+}
+
+/* Find a preset by bank and program numbers.
+ * Returns preset pointer or NULL.
+ *
+ * NOTE: The returned preset has been allocated, caller owns it and should
+ * free it when finished using it. */
+fluid_preset_t*
+fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum,
+ unsigned int prognum)
+{
+ fluid_preset_t *preset = NULL;
+ fluid_sfont_info_t *sfont_info;
+ fluid_list_t *list;
+
+ for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
+ sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+
+ preset = fluid_sfont_get_preset (sfont_info->sfont,
+ banknum - sfont_info->bankofs, prognum);
+ if (preset)
+ {
+ sfont_info->refcount++; /* Add reference to SoundFont */
+ break;
+ }
+ }
+
+ return preset;
+}
+
+/**
+ * Send a program change event on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param prognum MIDI program number (0-127)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+/* FIXME - Currently not real-time safe, due to preset allocation and mutex lock,
+ * and may be called from within synthesis context. */
+
+/* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented
+ * since fluid_synth_unset_program() should be used instead. */
+int
+fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_channel_t* channel;
+ int subst_bank, subst_prog, banknum = 0, result;
+
+ fluid_return_val_if_fail (prognum >= 0 && prognum <= 128, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ channel = synth->channel[chan];
+ if (channel->channel_type == CHANNEL_TYPE_DRUM)
+ banknum = DRUM_INST_BANK;
+ else
+ fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL);
+
+ if (synth->verbose)
+ FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum);
+
+ /* I think this is a hack for MIDI files that do bank changes in GM mode.
+ * Proper way to handle this would probably be to ignore bank changes when in
+ * GM mode. - JG
+ * This is now possible by setting synth.midi-bank-select=gm, but let the hack
+ * stay for the time being. - DH
+ */
+ if (prognum != FLUID_UNSET_PROGRAM)
+ {
+ subst_bank = banknum;
+ subst_prog = prognum;
+
+ preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);
+
+ /* Fallback to another preset if not found */
+ if (!preset) {
+ /* Percussion: Fallback to preset 0 in percussion bank */
+ if (subst_bank == DRUM_INST_BANK) {
+ subst_prog = 0;
+ preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);
+ }
+ /* Melodic instrument */
+ else {
+ /* Fallback first to bank 0:prognum */
+ subst_bank = 0;
+ preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);
+
+ /* Fallback to first preset in bank 0 (usually piano...) */
+ if (!preset)
+ {
+ subst_prog = 0;
+ preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);
+ }
+ }
+
+ if (preset)
+ FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]",
+ chan, banknum, prognum, subst_bank, subst_prog);
+ else
+ FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]",
+ chan, banknum, prognum);
+ }
+ }
+
+ /* Assign the SoundFont ID and program number to the channel */
+ fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0,
+ -1, prognum);
+ result = fluid_synth_set_preset (synth, chan, preset);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Set instrument bank number on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param bank MIDI bank number
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank)
+{
+ fluid_return_val_if_fail (bank <= 16383, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1);
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Set SoundFont ID on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param sfont_id ID of a loaded SoundFont
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id)
+{
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Set the preset of a MIDI channel to an unassigned state.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ * @since 1.1.1
+ *
+ * Note: Channel retains its SoundFont ID and bank numbers, while the program
+ * number is set to an "unset" state. MIDI program changes may re-assign a
+ * preset if one matches.
+ */
+int
+fluid_synth_unset_program (fluid_synth_t *synth, int chan)
+{
+ int result;
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ result = fluid_synth_program_change (synth, chan, FLUID_UNSET_PROGRAM);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get current SoundFont ID, bank number and program number for a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param sfont_id Location to store SoundFont ID
+ * @param bank_num Location to store MIDI bank number
+ * @param preset_num Location to store MIDI program number
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id,
+ unsigned int* bank_num, unsigned int* preset_num)
+{
+ fluid_channel_t* channel;
+
+ fluid_return_val_if_fail (sfont_id != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (bank_num != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (preset_num != NULL, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ channel = synth->channel[chan];
+ fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num,
+ (int *)preset_num);
+
+ /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */
+ if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0;
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Select an instrument on a MIDI channel by SoundFont ID, bank and program numbers.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param sfont_id ID of a loaded SoundFont
+ * @param bank_num MIDI bank number
+ * @param preset_num MIDI program number
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id,
+ unsigned int bank_num, unsigned int preset_num)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_channel_t* channel;
+ int result;
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ channel = synth->channel[chan];
+
+ /* ++ Allocate preset */
+ preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num);
+
+ if (preset == NULL) {
+ FLUID_LOG(FLUID_ERR,
+ "There is no preset with bank number %d and preset number %d in SoundFont %d",
+ bank_num, preset_num, sfont_id);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ /* Assign the new SoundFont ID, bank and program number to the channel */
+ fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num);
+ result = fluid_synth_set_preset (synth, chan, preset);
+
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Select an instrument on a MIDI channel by SoundFont name, bank and program numbers.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param sfont_name Name of a loaded SoundFont
+ * @param bank_num MIDI bank number
+ * @param preset_num MIDI program number
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ */
+int
+fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan,
+ const char *sfont_name, unsigned int bank_num,
+ unsigned int preset_num)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_channel_t* channel;
+ int result;
+ fluid_return_val_if_fail (sfont_name != NULL, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ channel = synth->channel[chan];
+
+ /* ++ Allocate preset */
+ preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num,
+ preset_num);
+ if (preset == NULL) {
+ FLUID_LOG(FLUID_ERR,
+ "There is no preset with bank number %d and preset number %d in SoundFont %s",
+ bank_num, preset_num, sfont_name);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ /* Assign the new SoundFont ID, bank and program number to the channel */
+ fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont),
+ bank_num, preset_num);
+ result = fluid_synth_set_preset (synth, chan, preset);
+ FLUID_API_RETURN(result);
+}
+
+/*
+ * This function assures that every MIDI channel has a valid preset
+ * (NULL is okay). This function is called after a SoundFont is
+ * unloaded or reloaded.
+ */
+static void
+fluid_synth_update_presets(fluid_synth_t* synth)
+{
+ fluid_channel_t *channel;
+ fluid_preset_t *preset;
+ int sfont, bank, prog;
+ int chan;
+
+ for (chan = 0; chan < synth->midi_channels; chan++) {
+ channel = synth->channel[chan];
+ fluid_channel_get_sfont_bank_prog (channel, &sfont, &bank, &prog);
+ preset = fluid_synth_get_preset (synth, sfont, bank, prog);
+ fluid_synth_set_preset (synth, chan, preset);
+ }
+}
+
+/* Handler for synth.gain setting. */
+static int
+fluid_synth_update_sample_rate(fluid_synth_t* synth, char* name, double value)
+{
+ fluid_synth_set_sample_rate(synth, (float) value);
+ return 0;
+}
+
+/**
+ * Set sample rate of the synth.
+ * NOTE: This function is currently experimental and should only be
+ * used when no voices or notes are active, and before any rendering calls.
+ * @param synth FluidSynth instance
+ * @param sample_rate New sample rate (Hz)
+ * @since 1.1.2
+ */
+void
+fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate)
+{
+ int i;
+ fluid_return_if_fail (synth != NULL);
+ fluid_synth_api_enter(synth);
+ fluid_clip (sample_rate, 8000.0f, 96000.0f);
+ synth->sample_rate = sample_rate;
+
+ fluid_settings_getint(synth->settings, "synth.min-note-length", &i);
+ synth->min_note_length_ticks = (unsigned int) (i*synth->sample_rate/1000.0f);
+
+ for (i=0; i < synth->polyphony; i++)
+ fluid_voice_set_output_rate(synth->voice[i], sample_rate);
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate,
+ 0, sample_rate);
+ fluid_synth_api_exit(synth);
+}
+
+
+/* Handler for synth.gain setting. */
+static int
+fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value)
+{
+ fluid_synth_set_gain(synth, (float) value);
+ return 0;
+}
+
+/**
+ * Set synth output gain value.
+ * @param synth FluidSynth instance
+ * @param gain Gain value (function clamps value to the range 0.0 to 10.0)
+ */
+void
+fluid_synth_set_gain(fluid_synth_t* synth, float gain)
+{
+ fluid_return_if_fail (synth != NULL);
+ fluid_synth_api_enter(synth);
+
+ fluid_clip (gain, 0.0f, 10.0f);
+
+ synth->gain = gain;
+ fluid_synth_update_gain_LOCAL (synth);
+ fluid_synth_api_exit(synth);
+}
+
+/* Called by synthesis thread to update the gain in all voices */
+static void
+fluid_synth_update_gain_LOCAL(fluid_synth_t* synth)
+{
+ fluid_voice_t *voice;
+ float gain;
+ int i;
+
+ gain = synth->gain;
+
+ for (i = 0; i < synth->polyphony; i++)
+ {
+ voice = synth->voice[i];
+ if (_PLAYING (voice)) fluid_voice_set_gain (voice, gain);
+ }
+}
+
+/**
+ * Get synth output gain value.
+ * @param synth FluidSynth instance
+ * @return Synth gain value (0.0 to 10.0)
+ */
+float
+fluid_synth_get_gain(fluid_synth_t* synth)
+{
+ float result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = synth->gain;
+ FLUID_API_RETURN(result);
+}
+
+/*
+ * Handler for synth.polyphony setting.
+ */
+static int
+fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value)
+{
+ fluid_synth_set_polyphony(synth, value);
+ return 0;
+}
+
+/**
+ * Set synthesizer polyphony (max number of voices).
+ * @param synth FluidSynth instance
+ * @param polyphony Polyphony to assign
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.0.6
+ */
+int
+fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (polyphony >= 1 && polyphony <= 65535, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_synth_update_polyphony_LOCAL(synth, polyphony);
+
+ FLUID_API_RETURN(result);
+}
+
+/* Called by synthesis thread to update the polyphony value */
+static int
+fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth, int new_polyphony)
+{
+ fluid_voice_t *voice;
+ int i;
+
+ if (new_polyphony > synth->nvoice) {
+ /* Create more voices */
+ fluid_voice_t** new_voices = FLUID_REALLOC(synth->voice,
+ sizeof(fluid_voice_t*) * new_polyphony);
+ if (new_voices == NULL)
+ return FLUID_FAILED;
+ synth->voice = new_voices;
+ for (i = synth->nvoice; i < new_polyphony; i++) {
+ synth->voice[i] = new_fluid_voice(synth->sample_rate);
+ if (synth->voice[i] == NULL)
+ return FLUID_FAILED;
+ }
+ synth->nvoice = new_polyphony;
+ }
+
+ synth->polyphony = new_polyphony;
+ /* turn off any voices above the new limit */
+ for (i = synth->polyphony; i < synth->nvoice; i++)
+ {
+ voice = synth->voice[i];
+ if (_PLAYING (voice)) fluid_voice_off (voice);
+ }
+
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony,
+ synth->polyphony, 0.0f);
+
+ return FLUID_OK;
+}
+
+/**
+ * Get current synthesizer polyphony (max number of voices).
+ * @param synth FluidSynth instance
+ * @return Synth polyphony value.
+ * @since 1.0.6
+ */
+int
+fluid_synth_get_polyphony(fluid_synth_t* synth)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ result = synth->polyphony;
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get current number of active voices.
+ * @param synth FluidSynth instance
+ * @return Number of currently active voices.
+ * @since 1.1.0
+ *
+ * Note: To generate accurate continuous statistics of the voice count, caller
+ * should ensure this function is called synchronously with the audio synthesis
+ * process. This can be done in the new_fluid_audio_driver2() audio callback
+ * function for example.
+ */
+int
+fluid_synth_get_active_voice_count(fluid_synth_t* synth)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ result = synth->active_voice_count;
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get the internal synthesis buffer size value.
+ * @param synth FluidSynth instance
+ * @return Internal buffer size in audio frames.
+ *
+ * Audio is synthesized this number of frames at a time. Defaults to 64 frames.
+ */
+int
+fluid_synth_get_internal_bufsize(fluid_synth_t* synth)
+{
+ return FLUID_BUFSIZE;
+}
+
+/**
+ * Resend a bank select and a program change for every channel.
+ * @param synth FluidSynth instance
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * This function is called mainly after a SoundFont has been loaded,
+ * unloaded or reloaded.
+ */
+int
+fluid_synth_program_reset(fluid_synth_t* synth)
+{
+ int i, prog;
+ fluid_synth_api_enter(synth);
+ /* try to set the correct presets */
+ for (i = 0; i < synth->midi_channels; i++){
+ fluid_channel_get_sfont_bank_prog (synth->channel[i], NULL, NULL, &prog);
+ fluid_synth_program_change(synth, i, prog);
+ }
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Synthesize a block of floating point audio to audio buffers.
+ * @param synth FluidSynth instance
+ * @param len Count of audio frames to synthesize
+ * @param left Array of floats to store left channel of audio (len in size)
+ * @param right Array of floats to store right channel of audio (len in size)
+ * @param fx_left Not currently used
+ * @param fx_right Not currently used
+ * @return FLUID_OK on success, FLUID_FAIL otherwise
+ *
+ * NOTE: Should only be called from synthesis thread.
+ */
+int
+fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
+ float** left, float** right,
+ float** fx_left, float** fx_right)
+{
+ fluid_real_t** left_in;
+ fluid_real_t** right_in;
+ double time = fluid_utime();
+ int i, num, available, count;
+#ifdef WITH_FLOAT
+ int bytes;
+#endif
+ float cpu_load;
+
+ if (!synth->eventhandler->is_threadsafe)
+ fluid_synth_api_enter(synth);
+
+ /* First, take what's still available in the buffer */
+ count = 0;
+ num = synth->cur;
+ if (synth->cur < FLUID_BUFSIZE) {
+ available = FLUID_BUFSIZE - synth->cur;
+ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+
+ num = (available > len)? len : available;
+#ifdef WITH_FLOAT
+ bytes = num * sizeof(float);
+#endif
+
+ for (i = 0; i < synth->audio_channels; i++) {
+#ifdef WITH_FLOAT
+ FLUID_MEMCPY(left[i], left_in[i] + synth->cur, bytes);
+ FLUID_MEMCPY(right[i], right_in[i] + synth->cur, bytes);
+#else //WITH_FLOAT
+ int j;
+ for (j = 0; j < num; j++) {
+ left[i][j] = (float) left_in[i][j + synth->cur];
+ right[i][j] = (float) right_in[i][j + synth->cur];
+ }
+#endif //WITH_FLOAT
+ }
+ count += num;
+ num += synth->cur; /* if we're now done, num becomes the new synth->cur below */
+ }
+
+ /* Then, run one_block() and copy till we have 'len' samples */
+ while (count < len) {
+ fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0);
+ fluid_synth_render_blocks(synth, 1); // TODO:
+ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+
+ num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE;
+#ifdef WITH_FLOAT
+ bytes = num * sizeof(float);
+#endif
+
+ for (i = 0; i < synth->audio_channels; i++) {
+#ifdef WITH_FLOAT
+ FLUID_MEMCPY(left[i] + count, left_in[i], bytes);
+ FLUID_MEMCPY(right[i] + count, right_in[i], bytes);
+#else //WITH_FLOAT
+ int j;
+ for (j = 0; j < num; j++) {
+ left[i][j + count] = (float) left_in[i][j];
+ right[i][j + count] = (float) right_in[i][j];
+ }
+#endif //WITH_FLOAT
+ }
+
+ count += num;
+ }
+
+ synth->cur = num;
+
+ time = fluid_utime() - time;
+ cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0);
+ fluid_atomic_float_set (&synth->cpu_load, cpu_load);
+
+ if (!synth->eventhandler->is_threadsafe)
+ fluid_synth_api_exit(synth);
+
+ return FLUID_OK;
+}
+
+/**
+ * Synthesize floating point audio to audio buffers.
+ * @param synth FluidSynth instance
+ * @param len Count of audio frames to synthesize
+ * @param nin Ignored
+ * @param in Ignored
+ * @param nout Count of arrays in 'out'
+ * @param out Array of arrays to store audio to
+ * @return FLUID_OK on success, FLUID_FAIL otherwise
+ *
+ * This function implements the default interface defined in fluidsynth/audio.h.
+ * NOTE: Should only be called from synthesis thread.
+ */
+/*
+ * FIXME: Currently if nout != 2 memory allocation will occur!
+ */
+int
+fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in,
+ int nout, float** out)
+{
+ if (nout==2) {
+ return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1);
+ }
+ else {
+ float **left, **right;
+ int i;
+ left = FLUID_ARRAY(float*, nout/2);
+ right = FLUID_ARRAY(float*, nout/2);
+ for(i=0; i<nout/2; i++) {
+ left[i] = out[2*i];
+ right[i] = out[2*i+1];
+ }
+ fluid_synth_nwrite_float(synth, len, left, right, NULL, NULL);
+ FLUID_FREE(left);
+ FLUID_FREE(right);
+ return FLUID_OK;
+ }
+}
+
+/**
+ * Synthesize a block of floating point audio samples to audio buffers.
+ * @param synth FluidSynth instance
+ * @param len Count of audio frames to synthesize
+ * @param lout Array of floats to store left channel of audio
+ * @param loff Offset index in 'lout' for first sample
+ * @param lincr Increment between samples stored to 'lout'
+ * @param rout Array of floats to store right channel of audio
+ * @param roff Offset index in 'rout' for first sample
+ * @param rincr Increment between samples stored to 'rout'
+ * @return FLUID_OK on success, FLUID_FAIL otherwise
+ *
+ * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,
+ * lincr = 2, rincr = 2).
+ *
+ * NOTE: Should only be called from synthesis thread.
+ */
+int
+fluid_synth_write_float(fluid_synth_t* synth, int len,
+ void* lout, int loff, int lincr,
+ void* rout, int roff, int rincr)
+{
+ int i, j, k, l;
+ float* left_out = (float*) lout;
+ float* right_out = (float*) rout;
+ fluid_real_t** left_in;
+ fluid_real_t** right_in;
+ double time = fluid_utime();
+ float cpu_load;
+
+ fluid_profile_ref_var (prof_ref);
+ if (!synth->eventhandler->is_threadsafe)
+ fluid_synth_api_enter(synth);
+
+ fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
+ l = synth->cur;
+ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+
+ for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) {
+ /* fill up the buffers as needed */
+ if (l >= synth->curmax) {
+ int blocksleft = (len-i+FLUID_BUFSIZE-1) / FLUID_BUFSIZE;
+ synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
+ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+
+ l = 0;
+ }
+
+ left_out[j] = (float) left_in[0][l];
+ right_out[k] = (float) right_in[0][l];
+ }
+
+ synth->cur = l;
+
+ time = fluid_utime() - time;
+ cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0);
+ fluid_atomic_float_set (&synth->cpu_load, cpu_load);
+
+ if (!synth->eventhandler->is_threadsafe)
+ fluid_synth_api_exit(synth);
+ fluid_profile(FLUID_PROF_WRITE, prof_ref);
+
+ return FLUID_OK;
+}
+
+#define DITHER_SIZE 48000
+#define DITHER_CHANNELS 2
+
+static float rand_table[DITHER_CHANNELS][DITHER_SIZE];
+
+/* Init dither table */
+static void
+init_dither(void)
+{
+ float d, dp;
+ int c, i;
+
+ for (c = 0; c < DITHER_CHANNELS; c++) {
+ dp = 0;
+ for (i = 0; i < DITHER_SIZE-1; i++) {
+ d = rand() / (float)RAND_MAX - 0.5f;
+ rand_table[c][i] = d - dp;
+ dp = d;
+ }
+ rand_table[c][DITHER_SIZE-1] = 0 - dp;
+ }
+}
+
+/* A portable replacement for roundf(), seems it may actually be faster too! */
+static inline int
+roundi (float x)
+{
+ if (x >= 0.0f)
+ return (int)(x+0.5f);
+ else
+ return (int)(x-0.5f);
+}
+
+/**
+ * Synthesize a block of 16 bit audio samples to audio buffers.
+ * @param synth FluidSynth instance
+ * @param len Count of audio frames to synthesize
+ * @param lout Array of 16 bit words to store left channel of audio
+ * @param loff Offset index in 'lout' for first sample
+ * @param lincr Increment between samples stored to 'lout'
+ * @param rout Array of 16 bit words to store right channel of audio
+ * @param roff Offset index in 'rout' for first sample
+ * @param rincr Increment between samples stored to 'rout'
+ * @return FLUID_OK on success, FLUID_FAIL otherwise
+ *
+ * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1,
+ * lincr = 2, rincr = 2).
+ *
+ * NOTE: Should only be called from synthesis thread.
+ * NOTE: Dithering is performed when converting from internal floating point to
+ * 16 bit audio.
+ */
+int
+fluid_synth_write_s16(fluid_synth_t* synth, int len,
+ void* lout, int loff, int lincr,
+ void* rout, int roff, int rincr)
+{
+ int i, j, k, cur;
+ signed short* left_out = (signed short*) lout;
+ signed short* right_out = (signed short*) rout;
+ fluid_real_t** left_in;
+ fluid_real_t** right_in;
+ fluid_real_t left_sample;
+ fluid_real_t right_sample;
+ double time = fluid_utime();
+ int di;
+ //double prof_ref_on_block;
+ float cpu_load;
+ fluid_profile_ref_var (prof_ref);
+
+ if (!synth->eventhandler->is_threadsafe)
+ fluid_synth_api_enter(synth);
+
+ fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
+ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+
+ cur = synth->cur;
+ di = synth->dither_index;
+
+ for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) {
+
+ /* fill up the buffers as needed */
+ if (cur >= synth->curmax) {
+ int blocksleft = (len-i+FLUID_BUFSIZE-1) / FLUID_BUFSIZE;
+ //prof_ref_on_block = fluid_profile_ref();
+ synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
+ fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+ cur = 0;
+
+ //fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block);
+ }
+
+ left_sample = roundi (left_in[0][cur] * 32766.0f + rand_table[0][di]);
+ right_sample = roundi (right_in[0][cur] * 32766.0f + rand_table[1][di]);
+
+ di++;
+ if (di >= DITHER_SIZE) di = 0;
+
+ /* digital clipping */
+ if (left_sample > 32767.0f) left_sample = 32767.0f;
+ if (left_sample < -32768.0f) left_sample = -32768.0f;
+ if (right_sample > 32767.0f) right_sample = 32767.0f;
+ if (right_sample < -32768.0f) right_sample = -32768.0f;
+
+ left_out[j] = (signed short) left_sample;
+ right_out[k] = (signed short) right_sample;
+ }
+
+ synth->cur = cur;
+ synth->dither_index = di; /* keep dither buffer continous */
+
+ fluid_profile(FLUID_PROF_WRITE, prof_ref);
+
+ time = fluid_utime() - time;
+ cpu_load = 0.5 * (synth->cpu_load + time * synth->sample_rate / len / 10000.0);
+ fluid_atomic_float_set (&synth->cpu_load, cpu_load);
+
+ if (!synth->eventhandler->is_threadsafe)
+ fluid_synth_api_exit(synth);
+
+ return 0;
+}
+
+/**
+ * Converts stereo floating point sample data to signed 16 bit data with dithering.
+ * @param dither_index Pointer to an integer which should be initialized to 0
+ * before the first call and passed unmodified to additional calls which are
+ * part of the same synthesis output.
+ * @param len Length in frames to convert
+ * @param lin Buffer of left audio samples to convert from
+ * @param rin Buffer of right audio samples to convert from
+ * @param lout Array of 16 bit words to store left channel of audio
+ * @param loff Offset index in 'lout' for first sample
+ * @param lincr Increment between samples stored to 'lout'
+ * @param rout Array of 16 bit words to store right channel of audio
+ * @param roff Offset index in 'rout' for first sample
+ * @param rincr Increment between samples stored to 'rout'
+ *
+ * NOTE: Currently private to libfluidsynth.
+ */
+void
+fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
+ void* lout, int loff, int lincr,
+ void* rout, int roff, int rincr)
+{
+ int i, j, k;
+ signed short* left_out = (signed short*) lout;
+ signed short* right_out = (signed short*) rout;
+ fluid_real_t left_sample;
+ fluid_real_t right_sample;
+ int di = *dither_index;
+ fluid_profile_ref_var (prof_ref);
+
+ for (i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) {
+
+ left_sample = roundi (lin[i] * 32766.0f + rand_table[0][di]);
+ right_sample = roundi (rin[i] * 32766.0f + rand_table[1][di]);
+
+ di++;
+ if (di >= DITHER_SIZE) di = 0;
+
+ /* digital clipping */
+ if (left_sample > 32767.0f) left_sample = 32767.0f;
+ if (left_sample < -32768.0f) left_sample = -32768.0f;
+ if (right_sample > 32767.0f) right_sample = 32767.0f;
+ if (right_sample < -32768.0f) right_sample = -32768.0f;
+
+ left_out[j] = (signed short) left_sample;
+ right_out[k] = (signed short) right_sample;
+ }
+
+ *dither_index = di; /* keep dither buffer continous */
+
+ fluid_profile(FLUID_PROF_WRITE, prof_ref);
+}
+
+static void
+fluid_synth_check_finished_voices(fluid_synth_t* synth)
+{
+ int j;
+ fluid_rvoice_t* fv;
+
+ while (NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) {
+ for (j=0; j < synth->polyphony; j++) {
+ if (synth->voice[j]->rvoice == fv) {
+ fluid_voice_unlock_rvoice(synth->voice[j]);
+ fluid_voice_off(synth->voice[j]);
+ break;
+ }
+ else if (synth->voice[j]->overflow_rvoice == fv) {
+ fluid_voice_overflow_rvoice_finished(synth->voice[j]);
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Process all waiting events in the rvoice queue.
+ * Make sure no (other) rendering is running in parallel when
+ * you call this function!
+ */
+void fluid_synth_process_event_queue(fluid_synth_t* synth)
+{
+ fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
+}
+
+
+/**
+ * Process blocks (FLUID_BUFSIZE) of audio.
+ * Must be called from renderer thread only!
+ * @return number of blocks rendered. Might (often) return less than requested
+ */
+static int
+fluid_synth_render_blocks(fluid_synth_t* synth, int blockcount)
+{
+ int i;
+ fluid_profile_ref_var (prof_ref);
+
+ /* Assign ID of synthesis thread */
+// synth->synth_thread_id = fluid_thread_get_id ();
+
+ fluid_check_fpe("??? Just starting up ???");
+
+ fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
+
+ for (i=0; i < blockcount; i++) {
+ fluid_sample_timer_process(synth);
+ fluid_synth_add_ticks(synth, FLUID_BUFSIZE);
+ if (fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) {
+ // Something has happened, we can't process more
+ blockcount = i+1;
+ break;
+ }
+ }
+
+ fluid_check_fpe("fluid_sample_timer_process");
+
+ blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount);
+
+ /* Testcase, that provokes a denormal floating point error */
+#if 0
+ {float num=1;while (num != 0){num*=0.5;};};
+#endif
+ fluid_check_fpe("??? Remainder of synth_one_block ???");
+ fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref);
+ return blockcount;
+}
+
+
+static int fluid_synth_update_overflow (fluid_synth_t *synth, char *name,
+ fluid_real_t value)
+{
+ double d;
+ fluid_synth_api_enter(synth);
+
+ fluid_settings_getnum(synth->settings, "synth.overflow.percussion", &d);
+ synth->overflow.percussion = d;
+ fluid_settings_getnum(synth->settings, "synth.overflow.released", &d);
+ synth->overflow.released = d;
+ fluid_settings_getnum(synth->settings, "synth.overflow.sustained", &d);
+ synth->overflow.sustained = d;
+ fluid_settings_getnum(synth->settings, "synth.overflow.volume", &d);
+ synth->overflow.volume = d;
+ fluid_settings_getnum(synth->settings, "synth.overflow.age", &d);
+ synth->overflow.age = d;
+
+ FLUID_API_RETURN(0);
+}
+
+
+/* Selects a voice for killing. */
+static fluid_voice_t*
+fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
+{
+ int i;
+ fluid_real_t best_prio = OVERFLOW_PRIO_CANNOT_KILL-1;
+ fluid_real_t this_voice_prio;
+ fluid_voice_t* voice;
+ int best_voice_index=-1;
+ unsigned int ticks = fluid_synth_get_ticks(synth);
+
+ for (i = 0; i < synth->polyphony; i++) {
+
+ voice = synth->voice[i];
+
+ /* safeguard against an available voice. */
+ if (_AVAILABLE(voice)) {
+ return voice;
+ }
+ this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow,
+ ticks);
+
+ /* check if this voice has less priority than the previous candidate. */
+ if (this_voice_prio < best_prio) {
+ best_voice_index = i;
+ best_prio = this_voice_prio;
+ }
+ }
+
+ if (best_voice_index < 0) {
+ return NULL;
+ }
+
+ voice = synth->voice[best_voice_index];
+ FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ",
+ voice->id, best_voice_index, voice->chan, voice->key);
+ fluid_voice_off(voice);
+
+ return voice;
+}
+
+
+/**
+ * Allocate a synthesis voice.
+ * @param synth FluidSynth instance
+ * @param sample Sample to assign to the voice
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param key MIDI note number for the voice (0-127)
+ * @param vel MIDI velocity for the voice (0-127)
+ * @return Allocated synthesis voice or NULL on error
+ *
+ * This function is called by a SoundFont's preset in response to a noteon event.
+ * The returned voice comes with default modulators and generators.
+ * A single noteon event may create any number of voices, when the preset is layered.
+ *
+ * NOTE: Should only be called from within synthesis thread, which includes
+ * SoundFont loader preset noteon method.
+ */
+fluid_voice_t*
+fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel)
+{
+ int i, k;
+ fluid_voice_t* voice = NULL;
+ fluid_channel_t* channel = NULL;
+ unsigned int ticks;
+
+ fluid_return_val_if_fail (sample != NULL, NULL);
+ FLUID_API_ENTRY_CHAN(NULL);
+
+ /* check if there's an available synthesis process */
+ for (i = 0; i < synth->polyphony; i++) {
+ if (_AVAILABLE(synth->voice[i])) {
+ voice = synth->voice[i];
+ break;
+ }
+ }
+
+ /* No success yet? Then stop a running voice. */
+ if (voice == NULL) {
+ FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice");
+ voice = fluid_synth_free_voice_by_kill_LOCAL(synth);
+ }
+
+ if (voice == NULL) {
+ FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key);
+ FLUID_API_RETURN(NULL);
+ }
+ ticks = fluid_synth_get_ticks(synth);
+
+ if (synth->verbose) {
+ k = 0;
+ for (i = 0; i < synth->polyphony; i++) {
+ if (!_AVAILABLE(synth->voice[i])) {
+ k++;
+ }
+ }
+
+ FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d",
+ chan, key, vel, synth->storeid,
+ (float) ticks / 44100.0f,
+ (fluid_curtime() - synth->start) / 1000.0f,
+ 0.0f,
+ k);
+ }
+
+ if (chan >= 0) {
+ channel = synth->channel[chan];
+ }
+
+ if (fluid_voice_init (voice, sample, channel, key, vel,
+ synth->storeid, ticks, synth->gain) != FLUID_OK) {
+ FLUID_LOG(FLUID_WARN, "Failed to initialize voice");
+ FLUID_API_RETURN(NULL);
+ }
+
+ /* add the default modulators to the synthesis process. */
+ fluid_voice_add_mod(voice, &default_vel2att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.1 */
+ fluid_voice_add_mod(voice, &default_vel2filter_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.2 */
+ fluid_voice_add_mod(voice, &default_at2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.3 */
+ fluid_voice_add_mod(voice, &default_mod2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.4 */
+ fluid_voice_add_mod(voice, &default_att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.5 */
+ fluid_voice_add_mod(voice, &default_pan_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.6 */
+ fluid_voice_add_mod(voice, &default_expr_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.7 */
+ fluid_voice_add_mod(voice, &default_reverb_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.8 */
+ fluid_voice_add_mod(voice, &default_chorus_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.9 */
+ fluid_voice_add_mod(voice, &default_pitch_bend_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.10 */
+
+ FLUID_API_RETURN(voice);
+}
+
+/* Kill all voices on a given channel, which have the same exclusive class
+ * generator as new_voice.
+ */
+static void
+fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth,
+ fluid_voice_t* new_voice)
+{
+ int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS);
+ fluid_voice_t* existing_voice;
+ int i;
+
+ /* Excl. class 0: No exclusive class */
+ if (excl_class == 0) return;
+
+ /* Kill all notes on the same channel with the same exclusive class */
+ for (i = 0; i < synth->polyphony; i++) {
+ existing_voice = synth->voice[i];
+
+ /* If voice is playing, on the same channel, has same exclusive
+ * class and is not part of the same noteon event (voice group), then kill it */
+
+ if (_PLAYING(existing_voice)
+ && existing_voice->chan == new_voice->chan
+ && (int)_GEN (existing_voice, GEN_EXCLUSIVECLASS) == excl_class
+ && fluid_voice_get_id (existing_voice) != fluid_voice_get_id(new_voice))
+ fluid_voice_kill_excl(existing_voice);
+ }
+}
+
+/**
+ * Activate a voice previously allocated with fluid_synth_alloc_voice().
+ * @param synth FluidSynth instance
+ * @param voice Voice to activate
+ *
+ * This function is called by a SoundFont's preset in response to a noteon
+ * event. Exclusive classes are processed here.
+ *
+ * NOTE: Should only be called from within synthesis thread, which includes
+ * SoundFont loader preset noteon method.
+ */
+void
+fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice)
+{
+ fluid_return_if_fail (synth != NULL);
+ fluid_return_if_fail (voice != NULL);
+// fluid_return_if_fail (fluid_synth_is_synth_thread (synth));
+ fluid_synth_api_enter(synth);
+
+ /* Find the exclusive class of this voice. If set, kill all voices
+ * that match the exclusive class and are younger than the first
+ * voice process created by this noteon event. */
+ fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice);
+
+ fluid_voice_start(voice); /* Start the new voice */
+ if (synth->eventhandler->is_threadsafe)
+ fluid_voice_lock_rvoice(voice);
+ fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);
+ fluid_synth_api_exit(synth);
+}
+
+/**
+ * Add a SoundFont loader interface.
+ * @param synth FluidSynth instance
+ * @param loader Loader API structure, used directly and should remain allocated
+ * as long as the synth instance is used.
+ *
+ * SoundFont loaders are used to add custom instrument loading to FluidSynth.
+ * The caller supplied functions for loading files, allocating presets,
+ * retrieving information on them and synthesizing note-on events. Using this
+ * method even non SoundFont instruments can be synthesized, although limited
+ * to the SoundFont synthesis model.
+ *
+ * NOTE: Should only be called before any SoundFont files are loaded.
+ */
+void
+fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader)
+{
+ gboolean sfont_already_loaded;
+
+ fluid_return_if_fail (synth != NULL);
+ fluid_return_if_fail (loader != NULL);
+ fluid_synth_api_enter(synth);
+ sfont_already_loaded = synth->sfont_info != NULL;
+ if (!sfont_already_loaded)
+ synth->loaders = fluid_list_prepend(synth->loaders, loader);
+ fluid_synth_api_exit(synth);
+}
+
+/**
+ * Load a SoundFont file (filename is interpreted by SoundFont loaders).
+ * The newly loaded SoundFont will be put on top of the SoundFont
+ * stack. Presets are searched starting from the SoundFont on the
+ * top of the stack, working the way down the stack until a preset is found.
+ *
+ * @param synth SoundFont instance
+ * @param filename File to load
+ * @param reset_presets TRUE to re-assign presets for all MIDI channels
+ * @return SoundFont ID on success, FLUID_FAILED on error
+ */
+int
+fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets)
+{
+ fluid_sfont_info_t *sfont_info;
+ fluid_sfont_t *sfont;
+ fluid_list_t *list;
+ fluid_sfloader_t *loader;
+ unsigned int sfont_id;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (filename != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ /* MT NOTE: Loaders list should not change. */
+
+ for (list = synth->loaders; list; list = fluid_list_next(list)) {
+ loader = (fluid_sfloader_t*) fluid_list_get(list);
+
+ sfont = fluid_sfloader_load(loader, filename);
+
+ if (sfont != NULL) {
+ sfont_info = new_fluid_sfont_info (synth, sfont);
+
+ if (!sfont_info)
+ {
+ delete_fluid_sfont (sfont);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ sfont->id = sfont_id = ++synth->sfont_id;
+ synth->sfont_info = fluid_list_prepend(synth->sfont_info, sfont_info); /* prepend to list */
+ fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */
+
+ /* reset the presets for all channels if requested */
+ if (reset_presets) fluid_synth_program_reset(synth);
+
+ FLUID_API_RETURN((int)sfont_id);
+ }
+ }
+
+ FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
+ FLUID_API_RETURN(FLUID_FAILED);
+}
+
+/* Create a new SoundFont info structure, free with FLUID_FREE */
+static fluid_sfont_info_t *
+new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont)
+{
+ fluid_sfont_info_t *sfont_info;
+
+ sfont_info = FLUID_NEW (fluid_sfont_info_t);
+
+ if (!sfont_info)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont_info->sfont = sfont;
+ sfont_info->synth = synth;
+ sfont_info->refcount = 1; /* Start with refcount of 1 for owning synth */
+ sfont_info->bankofs = 0;
+
+ return (sfont_info);
+}
+
+/**
+ * Unload a SoundFont.
+ * @param synth SoundFont instance
+ * @param id ID of SoundFont to unload
+ * @param reset_presets TRUE to re-assign presets for all MIDI channels
+ * @return FLUID_OK on success, FLUID_FAILED on error
+ */
+int
+fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets)
+{
+ fluid_sfont_info_t *sfont_info = NULL;
+ fluid_list_t *list;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ /* remove the SoundFont from the list */
+ for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
+ sfont_info = (fluid_sfont_info_t*) fluid_list_get(list);
+
+ if (fluid_sfont_get_id (sfont_info->sfont) == id)
+ {
+ synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info);
+ break;
+ }
+ }
+
+ if (!list) {
+ FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ /* reset the presets for all channels (SoundFont will be freed when there are no more references) */
+ if (reset_presets) fluid_synth_program_reset (synth);
+ else fluid_synth_update_presets (synth);
+
+ /* -- Remove synth->sfont_info list's reference to SoundFont */
+ fluid_synth_sfont_unref (synth, sfont_info->sfont);
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/* Unref a SoundFont and destroy if no more references */
+void
+fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont)
+{
+ fluid_sfont_info_t *sfont_info;
+ int refcount = 0;
+
+ sfont_info = fluid_hashtable_lookup (synth->sfont_hash, sfont);
+
+ if (sfont_info)
+ {
+ sfont_info->refcount--; /* -- Remove the sfont_info list's reference */
+ refcount = sfont_info->refcount;
+
+ if (refcount == 0) /* Remove SoundFont from hash if no more references */
+ fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont);
+ }
+
+ fluid_return_if_fail (sfont_info != NULL); /* Shouldn't happen, programming error if so */
+
+ if (refcount == 0) /* No more references? - Attempt delete */
+ {
+ if (delete_fluid_sfont (sfont_info->sfont) == 0) /* SoundFont loader can block SoundFont unload */
+ {
+ FLUID_FREE (sfont_info);
+ FLUID_LOG (FLUID_DBG, "Unloaded SoundFont");
+ } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */
+ else new_fluid_timer (100, fluid_synth_sfunload_callback, sfont_info, TRUE, TRUE, FALSE);
+ }
+}
+
+/* Callback to continually attempt to unload a SoundFont,
+ * only if a SoundFont loader blocked the unload operation */
+static int
+fluid_synth_sfunload_callback(void* data, unsigned int msec)
+{
+ fluid_sfont_info_t *sfont_info = (fluid_sfont_info_t *)data;
+
+ if (delete_fluid_sfont (sfont_info->sfont) == 0)
+ {
+ FLUID_FREE (sfont_info);
+ FLUID_LOG (FLUID_DBG, "Unloaded SoundFont");
+ return FALSE;
+ }
+ else return TRUE;
+}
+
+/**
+ * Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack.
+ * @param synth SoundFont instance
+ * @param id ID of SoundFont to reload
+ * @return SoundFont ID on success, FLUID_FAILED on error
+ */
+int
+fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id)
+{
+ char filename[1024];
+ fluid_sfont_info_t *sfont_info, *old_sfont_info;
+ fluid_sfont_t* sfont;
+ fluid_sfloader_t* loader;
+ fluid_list_t *list;
+ int index;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ /* Search for SoundFont and get its index */
+ for (list = synth->sfont_info, index = 0; list; list = fluid_list_next (list), index++) {
+ old_sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+ if (fluid_sfont_get_id (old_sfont_info->sfont) == id) break;
+ }
+
+ if (!list) {
+ FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ /* keep a copy of the SoundFont's filename */
+ FLUID_STRCPY (filename, fluid_sfont_get_name (old_sfont_info->sfont));
+
+ if (fluid_synth_sfunload (synth, id, FALSE) != FLUID_OK)
+ FLUID_API_RETURN(FLUID_FAILED);
+
+ /* MT Note: SoundFont loader list will not change */
+
+ for (list = synth->loaders; list; list = fluid_list_next(list)) {
+ loader = (fluid_sfloader_t*) fluid_list_get(list);
+
+ sfont = fluid_sfloader_load(loader, filename);
+
+ if (sfont != NULL) {
+ sfont->id = id;
+
+ sfont_info = new_fluid_sfont_info (synth, sfont);
+
+ if (!sfont_info)
+ {
+ delete_fluid_sfont (sfont);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ synth->sfont_info = fluid_list_insert_at(synth->sfont_info, index, sfont_info); /* insert the sfont at the same index */
+ fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */
+
+ /* reset the presets for all channels */
+ fluid_synth_update_presets(synth);
+ FLUID_API_RETURN(sfont->id);
+ }
+ }
+
+ FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
+ FLUID_API_RETURN(FLUID_FAILED);
+}
+
+/**
+ * Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack.
+ * @param synth FluidSynth instance
+ * @param sfont SoundFont to add
+ * @return New assigned SoundFont ID or FLUID_FAILED on error
+ */
+int
+fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
+{
+ fluid_sfont_info_t *sfont_info;
+ unsigned int sfont_id;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (sfont != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ sfont_info = new_fluid_sfont_info (synth, sfont);
+ if (!sfont_info)
+ FLUID_API_RETURN(FLUID_FAILED);
+
+ sfont->id = sfont_id = ++synth->sfont_id;
+ synth->sfont_info = fluid_list_prepend (synth->sfont_info, sfont_info); /* prepend to list */
+ fluid_hashtable_insert (synth->sfont_hash, sfont, sfont_info); /* Hash sfont->sfont_info */
+
+ /* reset the presets for all channels */
+ fluid_synth_program_reset (synth);
+
+ FLUID_API_RETURN(sfont_id);
+}
+
+/**
+ * Remove a SoundFont from the SoundFont stack without deleting it.
+ * @param synth FluidSynth instance
+ * @param sfont SoundFont to remove
+ *
+ * SoundFont is not freed and is left as the responsibility of the caller.
+ *
+ * NOTE: The SoundFont should only be freed after there are no presets
+ * referencing it. This can only be ensured by the SoundFont loader and
+ * therefore this function should not normally be used.
+ */
+void
+fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
+{
+ fluid_sfont_info_t *sfont_info;
+ fluid_list_t *list;
+
+ fluid_return_if_fail (synth != NULL);
+ fluid_return_if_fail (sfont != NULL);
+ fluid_synth_api_enter(synth);
+
+ /* remove the SoundFont from the list */
+ for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
+ sfont_info = (fluid_sfont_info_t*) fluid_list_get(list);
+
+ if (sfont_info->sfont == sfont)
+ {
+ synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info);
+
+ /* Remove from SoundFont hash regardless of refcount (SoundFont delete is up to caller) */
+ fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont);
+ break;
+ }
+ }
+
+ /* reset the presets for all channels */
+ fluid_synth_program_reset (synth);
+ fluid_synth_api_exit(synth);
+}
+
+/**
+ * Count number of loaded SoundFont files.
+ * @param synth FluidSynth instance
+ * @return Count of loaded SoundFont files.
+ */
+int
+fluid_synth_sfcount(fluid_synth_t* synth)
+{
+ int count;
+
+ fluid_return_val_if_fail (synth != NULL, 0);
+ fluid_synth_api_enter(synth);
+ count = fluid_list_size (synth->sfont_info);
+ FLUID_API_RETURN(count);
+}
+
+/**
+ * Get SoundFont by index.
+ * @param synth FluidSynth instance
+ * @param num SoundFont index on the stack (starting from 0 for top of stack).
+ * @return SoundFont instance or NULL if invalid index
+ *
+ * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
+ * the duration of use of the returned pointer.
+ */
+fluid_sfont_t *
+fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num)
+{
+ fluid_sfont_t *sfont = NULL;
+ fluid_list_t *list;
+
+ fluid_return_val_if_fail (synth != NULL, NULL);
+ fluid_synth_api_enter(synth);
+ list = fluid_list_nth (synth->sfont_info, num);
+ if (list) sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont;
+ FLUID_API_RETURN(sfont);
+}
+
+/**
+ * Get SoundFont by ID.
+ * @param synth FluidSynth instance
+ * @param id SoundFont ID
+ * @return SoundFont instance or NULL if invalid ID
+ *
+ * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
+ * the duration of use of the returned pointer.
+ */
+fluid_sfont_t *
+fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id)
+{
+ fluid_sfont_t* sfont = NULL;
+ fluid_list_t* list;
+
+ fluid_return_val_if_fail (synth != NULL, NULL);
+ fluid_synth_api_enter(synth);
+
+ for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
+ sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont;
+ if (fluid_sfont_get_id (sfont) == id)
+ break;
+ }
+
+ FLUID_API_RETURN(list ? sfont : NULL);
+}
+
+/**
+ * Get SoundFont by name.
+ * @param synth FluidSynth instance
+ * @param name Name of SoundFont
+ * @return SoundFont instance or NULL if invalid name
+ * @since 1.1.0
+ *
+ * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
+ * the duration of use of the returned pointer.
+ */
+fluid_sfont_t *
+fluid_synth_get_sfont_by_name(fluid_synth_t* synth, const char *name)
+{
+ fluid_sfont_t* sfont = NULL;
+ fluid_list_t* list;
+
+ fluid_return_val_if_fail (synth != NULL, NULL);
+ fluid_return_val_if_fail (name != NULL, NULL);
+ fluid_synth_api_enter(synth);
+
+ for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
+ sfont = ((fluid_sfont_info_t *)fluid_list_get (list))->sfont;
+ if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0)
+ break;
+ }
+
+ FLUID_API_RETURN(list ? sfont : NULL);
+}
+
+/**
+ * Get active preset on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @return Preset or NULL if no preset active on channel
+ * @deprecated fluid_synth_get_channel_info() should replace most use cases.
+ *
+ * NOTE: Should only be called from within synthesis thread, which includes
+ * SoundFont loader preset noteon methods. Not thread safe otherwise.
+ */
+fluid_preset_t *
+fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan)
+{
+ fluid_preset_t* result;
+ fluid_channel_t *channel;
+ FLUID_API_ENTRY_CHAN(NULL);
+
+ channel = synth->channel[chan];
+ result = channel->preset;
+ fluid_synth_api_exit(synth);
+ return result;
+}
+
+/**
+ * Get information on the currently selected preset on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param info Caller supplied structure to fill with preset information
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ * @since 1.1.1
+ */
+int
+fluid_synth_get_channel_info (fluid_synth_t *synth, int chan,
+ fluid_synth_channel_info_t *info)
+{
+ fluid_channel_t *channel;
+ fluid_preset_t *preset;
+ char *name;
+
+ if (info)
+ {
+ info->assigned = FALSE;
+ info->name[0] = '\0';
+ }
+
+ fluid_return_val_if_fail (info != NULL, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ channel = synth->channel[chan];
+ preset = channel->preset;
+
+ if (preset)
+ {
+ info->assigned = TRUE;
+ name = fluid_preset_get_name (preset);
+
+ if (name)
+ {
+ strncpy (info->name, name, FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE);
+ info->name[FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE - 1] = '\0';
+ }
+ else info->name[0] = '\0';
+
+ info->sfont_id = preset->sfont->id;
+ info->bank = fluid_preset_get_banknum (preset);
+ info->program = fluid_preset_get_num (preset);
+ }
+ else
+ {
+ info->assigned = FALSE;
+ fluid_channel_get_sfont_bank_prog (channel, &info->sfont_id, &info->bank, &info->program);
+ info->name[0] = '\0';
+ }
+
+ fluid_synth_api_exit(synth);
+ return FLUID_OK;
+}
+
+/**
+ * Get list of voices.
+ * @param synth FluidSynth instance
+ * @param buf Array to store voices to (NULL terminated if not filled completely)
+ * @param bufsize Count of indexes in buf
+ * @param id Voice ID to search for or < 0 to return list of all playing voices
+ *
+ * NOTE: Should only be called from within synthesis thread, which includes
+ * SoundFont loader preset noteon methods. Voices are only guaranteed to remain
+ * unchanged until next synthesis process iteration.
+ */
+void
+fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize,
+ int id)
+{
+ int count = 0;
+ int i;
+
+ fluid_return_if_fail (synth != NULL);
+ fluid_return_if_fail (buf != NULL);
+ fluid_synth_api_enter(synth);
+
+ for (i = 0; i < synth->polyphony && count < bufsize; i++) {
+ fluid_voice_t* voice = synth->voice[i];
+
+ if (_PLAYING(voice) && (id < 0 || (int)voice->id == id))
+ buf[count++] = voice;
+ }
+
+ if (count < bufsize) buf[count] = NULL;
+ fluid_synth_api_exit(synth);
+}
+
+/**
+ * Enable or disable reverb effect.
+ * @param synth FluidSynth instance
+ * @param on TRUE to enable reverb, FALSE to disable
+ */
+void
+fluid_synth_set_reverb_on(fluid_synth_t* synth, int on)
+{
+ fluid_return_if_fail (synth != NULL);
+
+ fluid_atomic_int_set (&synth->with_reverb, on != 0);
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled,
+ on != 0, 0.0f);
+}
+
+/**
+ * Activate a reverb preset.
+ * @param synth FluidSynth instance
+ * @param num Reverb preset number
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: Currently private to libfluidsynth.
+ */
+int
+fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num)
+{
+ int i = 0;
+ while (revmodel_preset[i].name != NULL) {
+ if (i == num) {
+ fluid_synth_set_reverb (synth, revmodel_preset[i].roomsize,
+ revmodel_preset[i].damp, revmodel_preset[i].width,
+ revmodel_preset[i].level);
+ return FLUID_OK;
+ }
+ i++;
+ }
+ return FLUID_FAILED;
+}
+
+/**
+ * Set reverb parameters.
+ * @param synth FluidSynth instance
+ * @param roomsize Reverb room size value (0.0-1.2)
+ * @param damping Reverb damping value (0.0-1.0)
+ * @param width Reverb width value (0.0-100.0)
+ * @param level Reverb level value (0.0-1.0)
+ *
+ * NOTE: Not realtime safe and therefore should not be called from synthesis
+ * context at the risk of stalling audio output.
+ */
+void
+fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping,
+ double width, double level)
+{
+ fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ALL,
+ roomsize, damping, width, level);
+}
+
+/**
+ * Set one or more reverb parameters.
+ * @param synth FluidSynth instance
+ * @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t)
+ * @param roomsize Reverb room size value (0.0-1.2)
+ * @param damping Reverb damping value (0.0-1.0)
+ * @param width Reverb width value (0.0-100.0)
+ * @param level Reverb level value (0.0-1.0)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: Not realtime safe and therefore should not be called from synthesis
+ * context at the risk of stalling audio output.
+ */
+int
+fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize,
+ double damping, double width, double level)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+
+ if (!(set & FLUID_REVMODEL_SET_ALL))
+ set = FLUID_REVMODEL_SET_ALL;
+
+ /* Synth shadow values are set here so that they will be returned if querried */
+
+ fluid_synth_api_enter(synth);
+
+ if (set & FLUID_REVMODEL_SET_ROOMSIZE)
+ fluid_atomic_float_set (&synth->reverb_roomsize, roomsize);
+
+ if (set & FLUID_REVMODEL_SET_DAMPING)
+ fluid_atomic_float_set (&synth->reverb_damping, damping);
+
+ if (set & FLUID_REVMODEL_SET_WIDTH)
+ fluid_atomic_float_set (&synth->reverb_width, width);
+
+ if (set & FLUID_REVMODEL_SET_LEVEL)
+ fluid_atomic_float_set (&synth->reverb_level, level);
+
+ fluid_rvoice_eventhandler_push5(synth->eventhandler,
+ fluid_rvoice_mixer_set_reverb_params,
+ synth->eventhandler->mixer, set,
+ roomsize, damping, width, level, 0.0f);
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Get reverb room size.
+ * @param synth FluidSynth instance
+ * @return Reverb room size (0.0-1.2)
+ */
+double
+fluid_synth_get_reverb_roomsize(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+ result = fluid_atomic_float_get (&synth->reverb_roomsize);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get reverb damping.
+ * @param synth FluidSynth instance
+ * @return Reverb damping value (0.0-1.0)
+ */
+double
+fluid_synth_get_reverb_damp(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_float_get (&synth->reverb_damping);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get reverb level.
+ * @param synth FluidSynth instance
+ * @return Reverb level value (0.0-1.0)
+ */
+double
+fluid_synth_get_reverb_level(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_float_get (&synth->reverb_level);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get reverb width.
+ * @param synth FluidSynth instance
+ * @return Reverb width value (0.0-100.0)
+ */
+double
+fluid_synth_get_reverb_width(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_float_get (&synth->reverb_width);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Enable or disable chorus effect.
+ * @param synth FluidSynth instance
+ * @param on TRUE to enable chorus, FALSE to disable
+ */
+void
+fluid_synth_set_chorus_on(fluid_synth_t* synth, int on)
+{
+ fluid_return_if_fail (synth != NULL);
+ fluid_synth_api_enter(synth);
+
+ fluid_atomic_int_set (&synth->with_chorus, on != 0);
+ fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled,
+ on != 0, 0.0f);
+ fluid_synth_api_exit(synth);
+}
+
+/**
+ * Set chorus parameters.
+ * @param synth FluidSynth instance
+ * @param nr Chorus voice count (0-99, CPU time consumption proportional to
+ * this value)
+ * @param level Chorus level (0.0-10.0)
+ * @param speed Chorus speed in Hz (0.29-5.0)
+ * @param depth_ms Chorus depth (max value depends on synth sample rate,
+ * 0.0-21.0 is safe for sample rate values up to 96KHz)
+ * @param type Chorus waveform type (#fluid_chorus_mod)
+ */
+void
+fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
+ double speed, double depth_ms, int type)
+{
+ fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_ALL, nr, level, speed,
+ depth_ms, type);
+}
+
+/**
+ * Set one or more chorus parameters.
+ * @param synth FluidSynth instance
+ * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t)
+ * @param nr Chorus voice count (0-99, CPU time consumption proportional to
+ * this value)
+ * @param level Chorus level (0.0-10.0)
+ * @param speed Chorus speed in Hz (0.29-5.0)
+ * @param depth_ms Chorus depth (max value depends on synth sample rate,
+ * 0.0-21.0 is safe for sample rate values up to 96KHz)
+ * @param type Chorus waveform type (#fluid_chorus_mod)
+ */
+int
+fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level,
+ double speed, double depth_ms, int type)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+
+ if (!(set & FLUID_CHORUS_SET_ALL))
+ set = FLUID_CHORUS_SET_ALL;
+
+ /* Synth shadow values are set here so that they will be returned if queried */
+ fluid_synth_api_enter(synth);
+
+ if (set & FLUID_CHORUS_SET_NR)
+ fluid_atomic_int_set (&synth->chorus_nr, nr);
+
+ if (set & FLUID_CHORUS_SET_LEVEL)
+ fluid_atomic_float_set (&synth->chorus_level, level);
+
+ if (set & FLUID_CHORUS_SET_SPEED)
+ fluid_atomic_float_set (&synth->chorus_speed, speed);
+
+ if (set & FLUID_CHORUS_SET_DEPTH)
+ fluid_atomic_float_set (&synth->chorus_depth, depth_ms);
+
+ if (set & FLUID_CHORUS_SET_TYPE)
+ fluid_atomic_int_set (&synth->chorus_type, type);
+
+ fluid_rvoice_eventhandler_push5(synth->eventhandler,
+ fluid_rvoice_mixer_set_chorus_params,
+ synth->eventhandler->mixer, set,
+ nr, level, speed, depth_ms, type);
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Get chorus voice number (delay line count) value.
+ * @param synth FluidSynth instance
+ * @return Chorus voice count (0-99)
+ */
+int
+fluid_synth_get_chorus_nr(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_int_get (&synth->chorus_nr);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get chorus level.
+ * @param synth FluidSynth instance
+ * @return Chorus level value (0.0-10.0)
+ */
+double
+fluid_synth_get_chorus_level(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_float_get (&synth->chorus_level);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get chorus speed in Hz.
+ * @param synth FluidSynth instance
+ * @return Chorus speed in Hz (0.29-5.0)
+ */
+double
+fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_float_get (&synth->chorus_speed);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get chorus depth.
+ * @param synth FluidSynth instance
+ * @return Chorus depth
+ */
+double
+fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_float_get (&synth->chorus_depth);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get chorus waveform type.
+ * @param synth FluidSynth instance
+ * @return Chorus waveform type (#fluid_chorus_mod)
+ */
+int
+fluid_synth_get_chorus_type(fluid_synth_t* synth)
+{
+ double result;
+ fluid_return_val_if_fail (synth != NULL, 0.0);
+ fluid_synth_api_enter(synth);
+
+ result = fluid_atomic_int_get (&synth->chorus_type);
+ FLUID_API_RETURN(result);
+}
+
+/*
+ * If the same note is hit twice on the same channel, then the older
+ * voice process is advanced to the release stage. Using a mechanical
+ * MIDI controller, the only way this can happen is when the sustain
+ * pedal is held. In this case the behaviour implemented here is
+ * natural for many instruments. Note: One noteon event can trigger
+ * several voice processes, for example a stereo sample. Don't
+ * release those...
+ */
+static void
+fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan,
+ int key)
+{
+ int i;
+ fluid_voice_t* voice;
+
+ synth->storeid = synth->noteid++;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (_PLAYING(voice)
+ && (voice->chan == chan)
+ && (voice->key == key)
+ && (fluid_voice_get_id(voice) != synth->noteid)) {
+ /* Id of voices that was sustained by sostenuto */
+ if(_HELD_BY_SOSTENUTO(voice))
+ synth->storeid = voice->id;
+ /* Force the voice into release stage (pedaling is ignored) */
+ fluid_voice_release(voice);
+ }
+ }
+}
+
+/**
+ * Set synthesis interpolation method on one or all MIDI channels.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel to set interpolation method on or -1 for all channels
+ * @param interp_method Interpolation method (#fluid_interp)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method)
+{
+ int i;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ if (chan < -1 || chan >= synth->midi_channels)
+ FLUID_API_RETURN(FLUID_FAILED);
+
+ if (synth->channel[0] == NULL) {
+ FLUID_LOG (FLUID_ERR, "Channels don't exist (yet)!");
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ for (i = 0; i < synth->midi_channels; i++) {
+ if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan)
+ fluid_channel_set_interp_method(synth->channel[i], interp_method);
+ }
+
+ FLUID_API_RETURN(FLUID_OK);
+};
+
+/**
+ * Get the total count of MIDI channels.
+ * @param synth FluidSynth instance
+ * @return Count of MIDI channels
+ */
+int
+fluid_synth_count_midi_channels(fluid_synth_t* synth)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, 0);
+ fluid_synth_api_enter(synth);
+
+ result = synth->midi_channels;
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get the total count of audio channels.
+ * @param synth FluidSynth instance
+ * @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc)
+ */
+int
+fluid_synth_count_audio_channels(fluid_synth_t* synth)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, 0);
+ fluid_synth_api_enter(synth);
+
+ result = synth->audio_channels;
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get the total number of allocated audio channels. Usually identical to the
+ * number of audio channels. Can be employed by LADSPA effects subsystem.
+ *
+ * @param synth FluidSynth instance
+ * @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc)
+ */
+int
+fluid_synth_count_audio_groups(fluid_synth_t* synth)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, 0);
+ fluid_synth_api_enter(synth);
+
+ result = synth->audio_groups;
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get the total number of allocated effects channels.
+ * @param synth FluidSynth instance
+ * @return Count of allocated effects channels
+ */
+int
+fluid_synth_count_effects_channels(fluid_synth_t* synth)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, 0);
+ fluid_synth_api_enter(synth);
+
+ result = synth->effects_channels;
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Get the synth CPU load value.
+ * @param synth FluidSynth instance
+ * @return Estimated CPU load value in percent (0-100)
+ */
+double
+fluid_synth_get_cpu_load(fluid_synth_t* synth)
+{
+ fluid_return_val_if_fail (synth != NULL, 0);
+ return fluid_atomic_float_get (&synth->cpu_load);
+}
+
+/* Get tuning for a given bank:program */
+static fluid_tuning_t *
+fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog)
+{
+
+ if ((synth->tuning == NULL) ||
+ (synth->tuning[bank] == NULL) ||
+ (synth->tuning[bank][prog] == NULL))
+ return NULL;
+
+ return synth->tuning[bank][prog];
+}
+
+/* Replace tuning on a given bank:program (need not already exist).
+ * Synth mutex should already be locked by caller. */
+static int
+fluid_synth_replace_tuning_LOCK (fluid_synth_t* synth, fluid_tuning_t *tuning,
+ int bank, int prog, int apply)
+{
+ fluid_tuning_t *old_tuning;
+// fluid_event_queue_t *queue;
+// fluid_event_queue_elem_t *event;
+
+ if (synth->tuning == NULL) {
+ synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128);
+ if (synth->tuning == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t**));
+ }
+
+ if (synth->tuning[bank] == NULL) {
+ synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t*, 128);
+ if (synth->tuning[bank] == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*));
+ }
+
+ old_tuning = synth->tuning[bank][prog];
+ synth->tuning[bank][prog] = tuning;
+
+ if (old_tuning) {
+ if (!fluid_tuning_unref (old_tuning, 1)) /* -- unref old tuning */
+ { /* Replace old tuning if present */
+ fluid_synth_replace_tuning_LOCAL (synth, old_tuning, tuning, apply, FALSE);
+ }
+ }
+
+ return FLUID_OK;
+}
+
+/* Replace a tuning with a new one in all MIDI channels. new_tuning can be
+ * NULL, in which case channels are reset to default equal tempered scale. */
+static void
+fluid_synth_replace_tuning_LOCAL (fluid_synth_t *synth, fluid_tuning_t *old_tuning,
+ fluid_tuning_t *new_tuning, int apply, int unref_new)
+{
+// fluid_event_queue_elem_t *event;
+ fluid_channel_t *channel;
+ int old_tuning_unref = 0;
+ int i;
+
+ for (i = 0; i < synth->midi_channels; i++)
+ {
+ channel = synth->channel[i];
+
+ if (fluid_channel_get_tuning (channel) == old_tuning)
+ {
+ old_tuning_unref++;
+ if (new_tuning) fluid_tuning_ref (new_tuning); /* ++ ref new tuning for channel */
+ fluid_channel_set_tuning (channel, new_tuning);
+
+ if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel);
+ }
+ }
+
+ /* Send unref old tuning event if any unrefs */
+ if (old_tuning && old_tuning_unref)
+ fluid_tuning_unref (old_tuning, old_tuning_unref);
+ if (!unref_new || !new_tuning) return;
+
+ fluid_tuning_unref (new_tuning, 1);
+}
+
+/* Update voice tunings in realtime */
+static void
+fluid_synth_update_voice_tuning_LOCAL (fluid_synth_t *synth, fluid_channel_t *channel)
+{
+ fluid_voice_t *voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++)
+ {
+ voice = synth->voice[i];
+
+ if (_ON (voice) && (voice->channel == channel))
+ {
+ fluid_voice_calculate_gen_pitch (voice);
+ fluid_voice_update_param (voice, GEN_PITCH);
+ }
+ }
+}
+
+/**
+ * Set the tuning of the entire MIDI note scale.
+ * @param synth FluidSynth instance
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param name Label name for this tuning
+ * @param pitch Array of pitch values (length of 128, each value is number of
+ * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc).
+ * Pass NULL to create a well-tempered (normal) scale.
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: Tuning is not applied in realtime to existing notes of the replaced
+ * tuning (if any), use fluid_synth_activate_key_tuning() instead to specify
+ * this behavior.
+ */
+int
+fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch)
+{
+ return fluid_synth_activate_key_tuning (synth, bank, prog, name, pitch, FALSE);
+}
+
+/**
+ * Set the tuning of the entire MIDI note scale.
+ * @param synth FluidSynth instance
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param name Label name for this tuning
+ * @param pitch Array of pitch values (length of 128, each value is number of
+ * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc).
+ * Pass NULL to create a well-tempered (normal) scale.
+ * @param apply TRUE to apply new tuning in realtime to existing notes which
+ * are using the replaced tuning (if any), FALSE otherwise
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ */
+int
+fluid_synth_activate_key_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch, int apply)
+{
+ fluid_tuning_t* tuning;
+ int retval = FLUID_OK;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+
+ fluid_synth_api_enter(synth);
+
+ tuning = new_fluid_tuning (name, bank, prog);
+
+ if (tuning)
+ {
+ if (pitch) fluid_tuning_set_all (tuning, pitch);
+ retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, apply);
+ if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1);
+ }
+ else retval = FLUID_FAILED;
+ FLUID_API_RETURN(retval);
+}
+
+/**
+ * Apply an octave tuning to every octave in the MIDI note scale.
+ * @param synth FluidSynth instance
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param name Label name for this tuning
+ * @param pitch Array of pitch values (length of 12 for each note of an octave
+ * starting at note C, values are number of offset cents to add to the normal
+ * tuning amount)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: Tuning is not applied in realtime to existing notes of the replaced
+ * tuning (if any), use fluid_synth_activate_octave_tuning() instead to specify
+ * this behavior.
+ */
+int
+fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch)
+{
+ return fluid_synth_activate_octave_tuning (synth, bank, prog, name, pitch, FALSE);
+}
+
+/**
+ * Activate an octave tuning on every octave in the MIDI note scale.
+ * @param synth FluidSynth instance
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param name Label name for this tuning
+ * @param pitch Array of pitch values (length of 12 for each note of an octave
+ * starting at note C, values are number of offset cents to add to the normal
+ * tuning amount)
+ * @param apply TRUE to apply new tuning in realtime to existing notes which
+ * are using the replaced tuning (if any), FALSE otherwise
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ */
+int
+fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch, int apply)
+{
+ fluid_tuning_t* tuning;
+ int retval = FLUID_OK;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED);
+
+ fluid_synth_api_enter(synth);
+ tuning = new_fluid_tuning (name, bank, prog);
+
+ if (tuning)
+ {
+ fluid_tuning_set_octave (tuning, pitch);
+ retval = fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, apply);
+ if (retval == FLUID_FAILED) fluid_tuning_unref (tuning, 1);
+ }
+ else retval = FLUID_FAILED;
+
+ FLUID_API_RETURN(retval);
+}
+
+/**
+ * Set tuning values for one or more MIDI notes for an existing tuning.
+ * @param synth FluidSynth instance
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param len Number of MIDI notes to assign
+ * @param key Array of MIDI key numbers (length of 'len', values 0-127)
+ * @param pitch Array of pitch values (length of 'len', values are number of
+ * cents from MIDI note 0)
+ * @param apply TRUE to apply tuning change in realtime to existing notes using
+ * the specified tuning, FALSE otherwise
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: Prior to version 1.1.0 it was an error to specify a tuning that didn't
+ * already exist. Starting with 1.1.0, the default equal tempered scale will be
+ * used as a basis, if no tuning exists for the given bank and prog.
+ */
+int
+fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog,
+ int len, const int *key, const double* pitch, int apply)
+{
+ fluid_tuning_t* old_tuning, *new_tuning;
+ int retval = FLUID_OK;
+ int i;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (len > 0, FLUID_FAILED);
+ fluid_return_val_if_fail (key != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (pitch != NULL, FLUID_FAILED);
+
+ fluid_synth_api_enter(synth);
+
+ old_tuning = fluid_synth_get_tuning (synth, bank, prog);
+
+ if (old_tuning)
+ new_tuning = fluid_tuning_duplicate (old_tuning);
+ else new_tuning = new_fluid_tuning ("Unnamed", bank, prog);
+
+ if (new_tuning)
+ {
+ for (i = 0; i < len; i++)
+ fluid_tuning_set_pitch (new_tuning, key[i], pitch[i]);
+
+ retval = fluid_synth_replace_tuning_LOCK (synth, new_tuning, bank, prog, apply);
+ if (retval == FLUID_FAILED) fluid_tuning_unref (new_tuning, 1);
+ }
+ else retval = FLUID_FAILED;
+
+ FLUID_API_RETURN(retval);
+}
+
+/**
+ * Select a tuning scale on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: This function does NOT activate tuning in realtime, use
+ * fluid_synth_activate_tuning() instead to specify whether tuning change
+ * should cause existing notes to update.
+ *
+ * NOTE: Prior to version 1.1.0 it was an error to select a tuning that didn't
+ * already exist. Starting with 1.1.0, a default equal tempered scale will be
+ * created, if no tuning exists for the given bank and prog.
+ */
+int
+fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog)
+{
+ return fluid_synth_activate_tuning (synth, chan, bank, prog, FALSE);
+}
+
+/**
+ * Activate a tuning scale on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param apply TRUE to apply tuning change to active notes, FALSE otherwise
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ *
+ * NOTE: A default equal tempered scale will be created, if no tuning exists
+ * on the given bank and prog.
+ */
+int
+fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog,
+ int apply)
+{
+ //fluid_event_queue_elem_t *event;
+ //fluid_event_queue_t *queue;
+ fluid_tuning_t* tuning;
+ int retval = FLUID_OK;
+
+ //fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ //fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED);
+ fluid_return_val_if_fail (bank >= 0 && bank < 128, FLUID_FAILED);
+ fluid_return_val_if_fail (prog >= 0 && prog < 128, FLUID_FAILED);
+
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ tuning = fluid_synth_get_tuning (synth, bank, prog);
+
+ /* If no tuning exists, create a new default tuning. We do this, so that
+ * it can be replaced later, if any changes are made. */
+ if (!tuning)
+ {
+ tuning = new_fluid_tuning ("Unnamed", bank, prog);
+ if (tuning) fluid_synth_replace_tuning_LOCK (synth, tuning, bank, prog, FALSE);
+ }
+
+ if (tuning) fluid_tuning_ref (tuning); /* ++ ref for outside of lock */
+
+ if (!tuning)
+ FLUID_API_RETURN(FLUID_FAILED);
+
+ fluid_tuning_ref (tuning); /* ++ ref new tuning for following function */
+ retval = fluid_synth_set_tuning_LOCAL (synth, chan, tuning, apply);
+
+ fluid_tuning_unref (tuning, 1); /* -- unref for outside of lock */
+
+ FLUID_API_RETURN(retval);
+}
+
+/* Local synthesis thread set tuning function (takes over tuning reference) */
+static int
+fluid_synth_set_tuning_LOCAL (fluid_synth_t *synth, int chan,
+ fluid_tuning_t *tuning, int apply)
+{
+ fluid_tuning_t *old_tuning;
+ fluid_channel_t *channel;
+
+ channel = synth->channel[chan];
+
+ old_tuning = fluid_channel_get_tuning (channel);
+ fluid_channel_set_tuning (channel, tuning); /* !! Takes over callers reference */
+
+ if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel);
+
+ /* Send unref old tuning event */
+ if (old_tuning)
+ {
+ fluid_tuning_unref (old_tuning, 1);
+ }
+
+
+ return FLUID_OK;
+}
+
+/**
+ * Clear tuning scale on a MIDI channel (set it to the default well-tempered scale).
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: This function does NOT activate tuning change in realtime, use
+ * fluid_synth_deactivate_tuning() instead to specify whether tuning change
+ * should cause existing notes to update.
+ */
+int
+fluid_synth_reset_tuning(fluid_synth_t* synth, int chan)
+{
+ return fluid_synth_deactivate_tuning (synth, chan, FALSE);
+}
+
+/**
+ * Clear tuning scale on a MIDI channel (use default equal tempered scale).
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param apply TRUE to apply tuning change to active notes, FALSE otherwise
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ */
+int
+fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply)
+{
+ int retval = FLUID_OK;
+
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ retval = fluid_synth_set_tuning_LOCAL (synth, chan, NULL, apply);
+
+ FLUID_API_RETURN(retval);
+}
+
+/**
+ * Start tuning iteration.
+ * @param synth FluidSynth instance
+ */
+void
+fluid_synth_tuning_iteration_start(fluid_synth_t* synth)
+{
+ fluid_return_if_fail (synth != NULL);
+ fluid_synth_api_enter(synth);
+ fluid_private_set (synth->tuning_iter, FLUID_INT_TO_POINTER (0));
+ fluid_synth_api_exit(synth);
+}
+
+/**
+ * Advance to next tuning.
+ * @param synth FluidSynth instance
+ * @param bank Location to store MIDI bank number of next tuning scale
+ * @param prog Location to store MIDI program number of next tuning scale
+ * @return 1 if tuning iteration advanced, 0 if no more tunings
+ */
+int
+fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog)
+{
+ void *pval;
+ int b = 0, p = 0;
+
+ fluid_return_val_if_fail (synth != NULL, 0);
+ fluid_return_val_if_fail (bank != NULL, 0);
+ fluid_return_val_if_fail (prog != NULL, 0);
+ fluid_synth_api_enter(synth);
+
+ /* Current tuning iteration stored as: bank << 8 | program */
+ pval = fluid_private_get (synth->tuning_iter);
+ p = FLUID_POINTER_TO_INT (pval);
+ b = (p >> 8) & 0xFF;
+ p &= 0xFF;
+
+ if (!synth->tuning)
+ {
+ FLUID_API_RETURN(0);
+ }
+
+ for (; b < 128; b++, p = 0)
+ {
+ if (synth->tuning[b] == NULL) continue;
+
+ for (; p < 128; p++)
+ {
+ if (synth->tuning[b][p] == NULL) continue;
+
+ *bank = b;
+ *prog = p;
+
+ if (p < 127) fluid_private_set (synth->tuning_iter,
+ FLUID_INT_TO_POINTER (b << 8 | (p + 1)));
+ else fluid_private_set (synth->tuning_iter,
+ FLUID_INT_TO_POINTER ((b + 1) << 8));
+
+ FLUID_API_RETURN(1);
+ }
+ }
+
+ FLUID_API_RETURN(0);
+}
+
+/**
+ * Get the entire note tuning for a given MIDI bank and program.
+ * @param synth FluidSynth instance
+ * @param bank MIDI bank number of tuning
+ * @param prog MIDI program number of tuning
+ * @param name Location to store tuning name or NULL to ignore
+ * @param len Maximum number of chars to store to 'name' (including NULL byte)
+ * @param pitch Array to store tuning scale to or NULL to ignore (len of 128)
+ * @return FLUID_OK if matching tuning was found, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog,
+ char* name, int len, double* pitch)
+{
+ fluid_tuning_t* tuning;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ tuning = fluid_synth_get_tuning (synth, bank, prog);
+
+ if (tuning)
+ {
+ if (name)
+ {
+ snprintf (name, len - 1, "%s", fluid_tuning_get_name (tuning));
+ name[len - 1] = 0; /* make sure the string is null terminated */
+ }
+
+ if (pitch)
+ FLUID_MEMCPY (pitch, fluid_tuning_get_all (tuning), 128 * sizeof (double));
+ }
+
+ FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED);
+}
+
+/**
+ * Get settings assigned to a synth.
+ * @param synth FluidSynth instance
+ * @return FluidSynth settings which are assigned to the synth
+ */
+fluid_settings_t *
+fluid_synth_get_settings(fluid_synth_t* synth)
+{
+ fluid_return_val_if_fail (synth != NULL, NULL);
+
+ return synth->settings;
+}
+
+/**
+ * Convenience function to set a string setting of a synth.
+ * @param synth FluidSynth instance
+ * @param name Name of setting parameter
+ * @param str Value to assign to the setting
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_setstr(fluid_synth_t* synth, const char* name, const char* str)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+
+ return fluid_settings_setstr(synth->settings, name, str);
+}
+
+/**
+ * Convenience function to duplicate a string setting of a synth.
+ * @param synth FluidSynth instance
+ * @param name Name of setting parameter
+ * @param str Location to store a pointer to the newly allocated string value
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * The returned string is owned by the caller and should be freed with free()
+ * when finished with it.
+ */
+int
+fluid_synth_dupstr(fluid_synth_t* synth, const char* name, char** str)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (str != NULL, FLUID_FAILED);
+
+ return fluid_settings_dupstr(synth->settings, name, str);
+}
+
+/**
+ * Convenience function to set a floating point setting of a synth.
+ * @param synth FluidSynth instance
+ * @param name Name of setting parameter
+ * @param val Value to assign to the setting
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_setnum(fluid_synth_t* synth, const char* name, double val)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+
+ return fluid_settings_setnum(synth->settings, name, val);
+}
+
+/**
+ * Convenience function to get a floating point setting of a synth.
+ * @param synth FluidSynth instance
+ * @param name Name of setting parameter
+ * @param val Location to store the current value of the setting
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_getnum(fluid_synth_t* synth, const char* name, double* val)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+
+ return fluid_settings_getnum(synth->settings, name, val);
+}
+
+/**
+ * Convenience function to set an integer setting of a synth.
+ * @param synth FluidSynth instance
+ * @param name Name of setting parameter
+ * @param val Value to assign to the setting
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_setint(fluid_synth_t* synth, const char* name, int val)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+
+ return fluid_settings_setint(synth->settings, name, val);
+}
+
+/**
+ * Convenience function to get an integer setting of a synth.
+ * @param synth FluidSynth instance
+ * @param name Name of setting parameter
+ * @param val Location to store the current value of the setting
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_getint(fluid_synth_t* synth, const char* name, int* val)
+{
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+
+ return fluid_settings_getint(synth->settings, name, val);
+}
+
+/**
+ * Set a SoundFont generator (effect) value on a MIDI channel in real-time.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param param SoundFont generator ID (#fluid_gen_type)
+ * @param value Offset generator value to assign to the MIDI channel
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * Parameter numbers and ranges are described in the SoundFont 2.01
+ * specification PDF, paragraph 8.1.3, page 48. See #fluid_gen_type.
+ */
+int
+fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value)
+{
+ fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ fluid_synth_set_gen_LOCAL (synth, chan, param, value, FALSE);
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/* Synthesis thread local set gen function */
+static void
+fluid_synth_set_gen_LOCAL (fluid_synth_t* synth, int chan, int param, float value,
+ int absolute)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ fluid_channel_set_gen (synth->channel[chan], param, value, absolute);
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (voice->chan == chan)
+ fluid_voice_set_param (voice, param, value, absolute);
+ }
+}
+
+/**
+ * Set a SoundFont generator (effect) value on a MIDI channel in real-time.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param param SoundFont generator ID (#fluid_gen_type)
+ * @param value Offset or absolute generator value to assign to the MIDI channel
+ * @param absolute 0 to assign a relative value, non-zero to assign an absolute value
+ * @param normalized 0 if value is specified in the native units of the generator,
+ * non-zero to take the value as a 0.0-1.0 range and apply it to the valid
+ * generator effect range (scaled and shifted as necessary).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ *
+ * This function allows for setting all effect parameters in real time on a
+ * MIDI channel. Setting absolute to non-zero will cause the value to override
+ * any generator values set in the instruments played on the MIDI channel.
+ * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont
+ * generator parameters and valid ranges.
+ */
+int
+fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param,
+ float value, int absolute, int normalized)
+{
+ float v;
+ fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ v = normalized ? fluid_gen_scale(param, value) : value;
+
+ fluid_synth_set_gen_LOCAL (synth, chan, param, v, absolute);
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Get generator value assigned to a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param param SoundFont generator ID (#fluid_gen_type)
+ * @return Current generator value assigned to MIDI channel
+ */
+float
+fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param)
+{
+ float result;
+ fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ result = fluid_channel_get_gen(synth->channel[chan], param);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Assign a MIDI router to a synth.
+ * @param synth FluidSynth instance
+ * @param router MIDI router to assign to the synth
+ *
+ * NOTE: This should only be done once and prior to using the synth.
+ */
+void
+fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router)
+{
+ fluid_return_if_fail (synth != NULL);
+ fluid_synth_api_enter(synth);
+
+ synth->midi_router = router;
+ fluid_synth_api_exit(synth);
+};
+
+/**
+ * Handle MIDI event from MIDI router, used as a callback function.
+ * @param data FluidSynth instance
+ * @param event MIDI event to handle
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ */
+int
+fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event)
+{
+ fluid_synth_t* synth = (fluid_synth_t*) data;
+ int type = fluid_midi_event_get_type(event);
+ int chan = fluid_midi_event_get_channel(event);
+
+ switch(type) {
+ case NOTE_ON:
+ return fluid_synth_noteon(synth, chan,
+ fluid_midi_event_get_key(event),
+ fluid_midi_event_get_velocity(event));
+
+ case NOTE_OFF:
+ return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event));
+
+ case CONTROL_CHANGE:
+ return fluid_synth_cc(synth, chan,
+ fluid_midi_event_get_control(event),
+ fluid_midi_event_get_value(event));
+
+ case PROGRAM_CHANGE:
+ return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event));
+
+ case CHANNEL_PRESSURE:
+ return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event));
+
+ case PITCH_BEND:
+ return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event));
+
+ case MIDI_SYSTEM_RESET:
+ return fluid_synth_system_reset(synth);
+ case MIDI_SYSEX:
+ return fluid_synth_sysex (synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE);
+ }
+ return FLUID_FAILED;
+}
+
+/**
+ * Create and start voices using a preset and a MIDI note on event.
+ * @param synth FluidSynth instance
+ * @param id Voice group ID to use (can be used with fluid_synth_stop()).
+ * @param preset Preset to synthesize
+ * @param audio_chan Unused currently, set to 0
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param key MIDI note number (0-127)
+ * @param vel MIDI velocity number (1-127)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: Should only be called from within synthesis thread, which includes
+ * SoundFont loader preset noteon method.
+ */
+int
+fluid_synth_start(fluid_synth_t* synth, unsigned int id, fluid_preset_t* preset,
+ int audio_chan, int chan, int key, int vel)
+{
+ int result;
+ fluid_return_val_if_fail (preset != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED);
+ fluid_return_val_if_fail (vel >= 1 && vel <= 127, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+ synth->storeid = id;
+ result = fluid_preset_noteon (preset, synth, chan, key, vel);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Stop notes for a given note event voice ID.
+ * @param synth FluidSynth instance
+ * @param id Voice note event ID
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ *
+ * NOTE: In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned
+ * if no matching voice note event ID was found. Versions after 1.1.0 only
+ * return #FLUID_FAILED if an error occurs.
+ */
+int
+fluid_synth_stop(fluid_synth_t* synth, unsigned int id)
+{
+ int result;
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ fluid_synth_stop_LOCAL (synth, id);
+ result = FLUID_OK;
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of fluid_synth_stop */
+static void
+fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id)
+{
+ fluid_voice_t* voice;
+ int i;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (_ON(voice) && (fluid_voice_get_id (voice) == id))
+ fluid_voice_noteoff(voice);
+ }
+}
+
+/**
+ * Offset the bank numbers of a loaded SoundFont.
+ * @param synth FluidSynth instance
+ * @param sfont_id ID of a loaded SoundFont
+ * @param offset Bank offset value to apply to all instruments
+ */
+int
+fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset)
+{
+ fluid_sfont_info_t *sfont_info;
+ fluid_list_t *list;
+
+ fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
+ sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+
+ if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id)
+ {
+ sfont_info->bankofs = offset;
+ break;
+ }
+ }
+
+ if (!list)
+ {
+ FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Get bank offset of a loaded SoundFont.
+ * @param synth FluidSynth instance
+ * @param sfont_id ID of a loaded SoundFont
+ * @return SoundFont bank offset value
+ */
+int
+fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id)
+{
+ fluid_sfont_info_t *sfont_info;
+ fluid_list_t *list;
+ int offset = 0;
+
+ fluid_return_val_if_fail (synth != NULL, 0);
+ fluid_synth_api_enter(synth);
+
+ for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
+ sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+
+ if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id)
+ {
+ offset = sfont_info->bankofs;
+ break;
+ }
+ }
+
+ if (!list)
+ {
+ FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id);
+ FLUID_API_RETURN(0);
+ }
+
+ FLUID_API_RETURN(offset);
+}
+
+void
+fluid_synth_api_enter(fluid_synth_t* synth)
+{
+ if (synth->use_mutex) {
+ fluid_rec_mutex_lock(synth->mutex);
+ }
+ if (!synth->public_api_count) {
+ fluid_synth_check_finished_voices(synth);
+ }
+ synth->public_api_count++;
+}
+
+void fluid_synth_api_exit(fluid_synth_t* synth)
+{
+ synth->public_api_count--;
+ if (!synth->public_api_count) {
+ fluid_rvoice_eventhandler_flush(synth->eventhandler);
+ }
+
+ if (synth->use_mutex) {
+ fluid_rec_mutex_unlock(synth->mutex);
+ }
+
+}
+
+
+/**
+ * Set midi channel type
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param type CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.4
+ */
+int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type)
+{
+ fluid_return_val_if_fail ((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ synth->channel[chan]->channel_type = type;
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
diff --git a/libs/fluidsynth/src/fluid_synth.h b/libs/fluidsynth/src/fluid_synth.h
new file mode 100644
index 0000000000..019a8e0d55
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_synth.h
@@ -0,0 +1,237 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_SYNTH_H
+#define _FLUID_SYNTH_H
+
+
+/***************************************************************
+ *
+ * INCLUDES
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "fluidsynth_priv.h"
+#include "fluid_event_queue.h"
+#include "fluid_list.h"
+#include "fluid_rev.h"
+#include "fluid_voice.h"
+#include "fluid_chorus.h"
+//#include "fluid_ladspa.h"
+//#include "fluid_midi_router.h"
+#include "fluid_sys.h"
+#include "fluid_rvoice_event.h"
+
+/***************************************************************
+ *
+ * DEFINES
+ */
+#define FLUID_NUM_PROGRAMS 128
+#define DRUM_INST_BANK 128
+
+#define FLUID_UNSET_PROGRAM 128 /* Program number used to unset a preset */
+
+#if defined(WITH_FLOAT)
+#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_FLOAT
+#else
+#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_DOUBLE
+#endif
+
+
+/***************************************************************
+ *
+ * ENUM
+ */
+/*enum fluid_loop {
+ FLUID_UNLOOPED = 0,
+ FLUID_LOOP_DURING_RELEASE = 1,
+ FLUID_NOTUSED = 2,
+ FLUID_LOOP_UNTIL_RELEASE = 3
+};*/
+
+/**
+ * Bank Select MIDI message styles. Default style is GS.
+ */
+enum fluid_midi_bank_select
+{
+ FLUID_BANK_STYLE_GM, /**< GM style, bank = 0 always (CC0/MSB and CC32/LSB ignored) */
+ FLUID_BANK_STYLE_GS, /**< GS style, bank = CC0/MSB (CC32/LSB ignored) */
+ FLUID_BANK_STYLE_XG, /**< XG style, bank = CC32/LSB (CC0/MSB ignored) */
+ FLUID_BANK_STYLE_MMA /**< MMA style bank = 128*MSB+LSB */
+};
+
+enum fluid_synth_status
+{
+ FLUID_SYNTH_CLEAN,
+ FLUID_SYNTH_PLAYING,
+ FLUID_SYNTH_QUIET,
+ FLUID_SYNTH_STOPPED
+};
+
+#define SYNTH_REVERB_CHANNEL 0
+#define SYNTH_CHORUS_CHANNEL 1
+
+/**
+ * Structure used for sfont_info field in #fluid_synth_t for each loaded
+ * SoundFont with the SoundFont instance and additional fields.
+ */
+typedef struct _fluid_sfont_info_t {
+ fluid_sfont_t *sfont; /**< Loaded SoundFont */
+ fluid_synth_t *synth; /**< Parent synth */
+ int refcount; /**< SoundFont reference count (0 if no presets referencing it) */
+ int bankofs; /**< Bank offset */
+} fluid_sfont_info_t;
+
+/*
+ * fluid_synth_t
+ *
+ * Mutual exclusion notes (as of 1.1.2):
+ *
+ * All variables are considered belongning to the "public API" thread,
+ * which processes all MIDI, except for:
+ *
+ * ticks_since_start - atomic, set by rendering thread only
+ * cpu_load - atomic, set by rendering thread only
+ * cur, curmax, dither_index - used by rendering thread only
+ * LADSPA_FxUnit - same instance copied in rendering thread. Synchronising handled internally (I think...?).
+ *
+ */
+
+struct _fluid_synth_t
+{
+ fluid_rec_mutex_t mutex; /**< Lock for public API */
+ int use_mutex; /**< Use mutex for all public API functions? */
+ int public_api_count; /**< How many times the mutex is currently locked */
+
+ fluid_settings_t* settings; /**< the synthesizer settings */
+ int device_id; /**< Device ID used for SYSEX messages */
+ int polyphony; /**< Maximum polyphony */
+ int with_reverb; /**< Should the synth use the built-in reverb unit? */
+ int with_chorus; /**< Should the synth use the built-in chorus unit? */
+ int verbose; /**< Turn verbose mode on? */
+ int dump; /**< Dump events to stdout to hook up a user interface? */
+ double sample_rate; /**< The sample rate */
+ int midi_channels; /**< the number of MIDI channels (>= 16) */
+ int bank_select; /**< the style of Bank Select MIDI messages */
+ int audio_channels; /**< the number of audio channels (1 channel=left+right) */
+ int audio_groups; /**< the number of (stereo) 'sub'groups from the synth.
+ Typically equal to audio_channels. */
+ int effects_channels; /**< the number of effects channels (>= 2) */
+ int state; /**< the synthesizer state */
+ unsigned int ticks_since_start; /**< the number of audio samples since the start */
+ unsigned int start; /**< the start in msec, as returned by system clock */
+ fluid_overflow_prio_t overflow; /**< parameters for overflow priority (aka voice-stealing) */
+
+ fluid_list_t *loaders; /**< the SoundFont loaders */
+ fluid_list_t *sfont_info; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */
+ fluid_hashtable_t *sfont_hash; /**< Hash of fluid_sfont_t->fluid_sfont_info_t (remains until SoundFont is deleted) */
+ unsigned int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */
+
+ float gain; /**< master gain */
+ fluid_channel_t** channel; /**< the channels */
+ int nvoice; /**< the length of the synthesis process array (max polyphony allowed) */
+ fluid_voice_t** voice; /**< the synthesis voices */
+ int active_voice_count; /**< count of active voices */
+ unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */
+ unsigned int storeid;
+ fluid_rvoice_eventhandler_t* eventhandler;
+
+ float reverb_roomsize; /**< Shadow of reverb roomsize */
+ float reverb_damping; /**< Shadow of reverb damping */
+ float reverb_width; /**< Shadow of reverb width */
+ float reverb_level; /**< Shadow of reverb level */
+
+ int chorus_nr; /**< Shadow of chorus number */
+ float chorus_level; /**< Shadow of chorus level */
+ float chorus_speed; /**< Shadow of chorus speed */
+ float chorus_depth; /**< Shadow of chorus depth */
+ int chorus_type; /**< Shadow of chorus type */
+
+ int cur; /**< the current sample in the audio buffers to be output */
+ int curmax; /**< current amount of samples present in the audio buffers */
+ int dither_index; /**< current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */
+
+ char outbuf[256]; /**< buffer for message output */
+ float cpu_load; /**< CPU load in percent (CPU time required / audio synthesized time * 100) */
+
+ fluid_tuning_t*** tuning; /**< 128 banks of 128 programs for the tunings */
+ fluid_private_t tuning_iter; /**< Tuning iterators per each thread */
+
+ fluid_midi_router_t* midi_router; /**< The midi router. Could be done nicer. */
+ fluid_sample_timer_t* sample_timers; /**< List of timers triggered before a block is processed */
+ unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */
+
+ int cores; /**< Number of CPU cores (1 by default) */
+
+#ifdef LADSPA
+ fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Effects unit for LADSPA support */
+#endif
+};
+
+int fluid_synth_setstr(fluid_synth_t* synth, const char* name, const char* str);
+int fluid_synth_dupstr(fluid_synth_t* synth, const char* name, char** str);
+int fluid_synth_setnum(fluid_synth_t* synth, const char* name, double val);
+int fluid_synth_getnum(fluid_synth_t* synth, const char* name, double* val);
+int fluid_synth_setint(fluid_synth_t* synth, const char* name, int val);
+int fluid_synth_getint(fluid_synth_t* synth, const char* name, int* val);
+
+fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth,
+ unsigned int banknum,
+ unsigned int prognum);
+void fluid_synth_sfont_unref (fluid_synth_t *synth, fluid_sfont_t *sfont);
+
+
+int fluid_synth_all_notes_off(fluid_synth_t* synth, int chan);
+int fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan);
+int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice);
+
+void fluid_synth_print_voice(fluid_synth_t* synth);
+
+void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
+ void* lout, int loff, int lincr,
+ void* rout, int roff, int rincr);
+
+int fluid_synth_reset_reverb(fluid_synth_t* synth);
+int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num);
+int fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize,
+ double damping, double width, double level);
+
+int fluid_synth_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);
+
+fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data);
+int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer);
+
+void fluid_synth_api_enter(fluid_synth_t* synth);
+void fluid_synth_api_exit(fluid_synth_t* synth);
+
+void fluid_synth_process_event_queue(fluid_synth_t* synth);
+
+/*
+ * misc
+ */
+
+void fluid_synth_settings(fluid_settings_t* settings);
+
+#endif /* _FLUID_SYNTH_H */
diff --git a/libs/fluidsynth/src/fluid_sys.c b/libs/fluidsynth/src/fluid_sys.c
new file mode 100644
index 0000000000..600b04e88f
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_sys.c
@@ -0,0 +1,1296 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_sys.h"
+
+
+#if WITH_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#ifdef DBUS_SUPPORT
+#include "fluid_rtkit.h"
+#endif
+
+/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket.
+ * Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */
+#define WIN32_SOCKET_FLAG 0x40000000
+
+/* SCHED_FIFO priority for high priority timer threads */
+#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10
+
+
+typedef struct
+{
+ fluid_thread_func_t func;
+ void *data;
+ int prio_level;
+} fluid_thread_info_t;
+
+struct _fluid_timer_t
+{
+ long msec;
+ fluid_timer_callback_t callback;
+ void *data;
+ fluid_thread_t *thread;
+ int cont;
+ int auto_destroy;
+};
+
+struct _fluid_server_socket_t
+{
+ fluid_socket_t socket;
+ fluid_thread_t *thread;
+ int cont;
+ fluid_server_func_t func;
+ void *data;
+};
+
+
+static int fluid_istream_gets(fluid_istream_t in, char* buf, int len);
+
+
+static char fluid_errbuf[512]; /* buffer for error message */
+
+static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL];
+static void* fluid_log_user_data[LAST_LOG_LEVEL];
+static int fluid_log_initialized = 0;
+
+static char* fluid_libname = "fluidsynth";
+
+
+void fluid_sys_config()
+{
+ fluid_log_config();
+}
+
+
+unsigned int fluid_debug_flags = 0;
+
+#if DEBUG
+/*
+ * fluid_debug
+ */
+int fluid_debug(int level, char * fmt, ...)
+{
+ if (fluid_debug_flags & level) {
+ fluid_log_function_t fun;
+ va_list args;
+
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ fun = fluid_log_function[FLUID_DBG];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]);
+ }
+ }
+ return 0;
+}
+#endif
+
+/**
+ * Installs a new log function for a specified log level.
+ * @param level Log level to install handler for.
+ * @param fun Callback function handler to call for logged messages
+ * @param data User supplied data pointer to pass to log function
+ * @return The previously installed function.
+ */
+fluid_log_function_t
+fluid_set_log_function(int level, fluid_log_function_t fun, void* data)
+{
+ fluid_log_function_t old = NULL;
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ old = fluid_log_function[level];
+ fluid_log_function[level] = fun;
+ fluid_log_user_data[level] = data;
+ }
+ return old;
+}
+
+/**
+ * Default log function which prints to the stderr.
+ * @param level Log level
+ * @param message Log message
+ * @param data User supplied data (not used)
+ */
+void
+fluid_default_log_function(int level, char* message, void* data)
+{
+ FILE* out;
+
+#if defined(WIN32)
+ out = stdout;
+#else
+ out = stderr;
+#endif
+
+ if (fluid_log_initialized == 0) {
+ fluid_log_config();
+ }
+
+ switch (level) {
+ case FLUID_PANIC:
+ FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
+ break;
+ case FLUID_ERR:
+ FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
+ break;
+ case FLUID_WARN:
+ FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
+ break;
+ case FLUID_INFO:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ case FLUID_DBG:
+#if DEBUG
+ FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
+#endif
+ break;
+ default:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ }
+ fflush(out);
+}
+
+/*
+ * fluid_init_log
+ */
+void
+fluid_log_config(void)
+{
+ if (fluid_log_initialized == 0) {
+
+ fluid_log_initialized = 1;
+
+ if (fluid_log_function[FLUID_PANIC] == NULL) {
+ fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_ERR] == NULL) {
+ fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_WARN] == NULL) {
+ fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_INFO] == NULL) {
+ fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_DBG] == NULL) {
+ fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL);
+ }
+ }
+}
+
+/**
+ * Print a message to the log.
+ * @param level Log level (#fluid_log_level).
+ * @param fmt Printf style format string for log message
+ * @param ... Arguments for printf 'fmt' message string
+ * @return Always returns #FLUID_FAILED
+ */
+int
+fluid_log(int level, const char* fmt, ...)
+{
+ fluid_log_function_t fun = NULL;
+
+ va_list args;
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ fun = fluid_log_function[level];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[level]);
+ }
+ }
+ return FLUID_FAILED;
+}
+
+/**
+ * An improved strtok, still trashes the input string, but is portable and
+ * thread safe. Also skips token chars at beginning of token string and never
+ * returns an empty token (will return NULL if source ends in token chars though).
+ * NOTE: NOT part of public API
+ * @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
+ * when final token is returned.
+ * @param delim String of delimiter chars.
+ * @return Pointer to the next token or NULL if no more tokens.
+ */
+char *fluid_strtok (char **str, char *delim)
+{
+ char *s, *d, *token;
+ char c;
+
+ if (str == NULL || delim == NULL || !*delim)
+ {
+ FLUID_LOG(FLUID_ERR, "Null pointer");
+ return NULL;
+ }
+
+ s = *str;
+ if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */
+
+ /* skip delimiter chars at beginning of token */
+ do
+ {
+ c = *s;
+ if (!c) /* end of source string? */
+ {
+ *str = NULL;
+ return NULL;
+ }
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ s++; /* advance to next source char */
+ break;
+ }
+ }
+ } while (*d); /* while token char match */
+
+ token = s; /* start of token found */
+
+ /* search for next token char or end of source string */
+ for (s = s+1; *s; s++)
+ {
+ c = *s;
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ *s = '\0'; /* overwrite token char with zero byte to terminate token */
+ *str = s+1; /* update str to point to beginning of next token */
+ return token;
+ }
+ }
+ }
+
+ /* we get here only if source string ended */
+ *str = NULL;
+ return token;
+}
+
+/*
+ * fluid_error
+ */
+char*
+fluid_error()
+{
+ return fluid_errbuf;
+}
+
+/**
+ * Check if a file is a MIDI file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a MIDI file, FALSE otherwise
+ *
+ * The current implementation only checks for the "MThd" header in the file.
+ * It is useful only to distinguish between SoundFont and MIDI files.
+ */
+int
+fluid_is_midifile(const char *filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "MThd", 4) == 0;
+}
+
+/**
+ * Check if a file is a SoundFont file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a SoundFont, FALSE otherwise
+ *
+ * The current implementation only checks for the "RIFF" header in the file.
+ * It is useful only to distinguish between SoundFont and MIDI files.
+ */
+int
+fluid_is_soundfont(const char *filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "RIFF", 4) == 0;
+}
+
+/**
+ * Get time in milliseconds to be used in relative timing operations.
+ * @return Unix time in milliseconds.
+ */
+unsigned int fluid_curtime(void)
+{
+ static glong initial_seconds = 0;
+ GTimeVal timeval;
+
+ if (initial_seconds == 0) {
+ g_get_current_time (&timeval);
+ initial_seconds = timeval.tv_sec;
+ }
+
+ g_get_current_time (&timeval);
+
+ return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0);
+}
+
+/**
+ * Get time in microseconds to be used in relative timing operations.
+ * @return Unix time in microseconds.
+ */
+double
+fluid_utime (void)
+{
+ GTimeVal timeval;
+
+ g_get_current_time (&timeval);
+
+ return (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
+}
+
+
+#if defined(WIN32) /* Windoze specific stuff */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ if (prio_level > 0)
+ SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+}
+
+
+#elif defined(__OS2__) /* OS/2 specific stuff */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ if (prio_level > 0)
+ DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
+}
+
+#else /* POSIX stuff.. Nice POSIX.. Good POSIX. */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ struct sched_param priority;
+
+ if (prio_level > 0)
+ {
+
+ memset(&priority, 0, sizeof(priority));
+ priority.sched_priority = prio_level;
+
+ if (pthread_setschedparam (pthread_self (), SCHED_FIFO, &priority) == 0) {
+ return;
+ }
+#ifdef DBUS_SUPPORT
+/* Try to gain high priority via rtkit */
+
+ if (fluid_rtkit_make_realtime(0, prio_level) == 0) {
+ return;
+ }
+#endif
+ FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority");
+ }
+}
+
+#ifdef FPE_CHECK
+
+/***************************************************************
+ *
+ * Floating point exceptions
+ *
+ * The floating point exception functions were taken from Ircam's
+ * jMax source code. http://www.ircam.fr/jmax
+ *
+ * FIXME: check in config for i386 machine
+ *
+ * Currently not used. I leave the code here in case we want to pick
+ * this up again some time later.
+ */
+
+/* Exception flags */
+#define _FPU_STATUS_IE 0x001 /* Invalid Operation */
+#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */
+#define _FPU_STATUS_ZE 0x004 /* Zero Divide */
+#define _FPU_STATUS_OE 0x008 /* Overflow */
+#define _FPU_STATUS_UE 0x010 /* Underflow */
+#define _FPU_STATUS_PE 0x020 /* Precision */
+#define _FPU_STATUS_SF 0x040 /* Stack Fault */
+#define _FPU_STATUS_ES 0x080 /* Error Summary Status */
+
+/* Macros for accessing the FPU status word. */
+
+/* get the FPU status */
+#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw))
+
+/* clear the FPU status */
+#define _FPU_CLR_SW() __asm__ ("fnclex" : : )
+
+/* Purpose:
+ * Checks, if the floating point unit has produced an exception, print a message
+ * if so and clear the exception.
+ */
+unsigned int fluid_check_fpe_i386(char* explanation)
+{
+ unsigned int s;
+
+ _FPU_GET_SW(s);
+ _FPU_CLR_SW();
+
+ s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE;
+
+ if (s)
+ {
+ FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation,
+ (s & _FPU_STATUS_IE) ? "Invalid operation " : "",
+ (s & _FPU_STATUS_DE) ? "Denormal number " : "",
+ (s & _FPU_STATUS_ZE) ? "Zero divide " : "",
+ (s & _FPU_STATUS_OE) ? "Overflow " : "",
+ (s & _FPU_STATUS_UE) ? "Underflow " : "");
+ }
+
+ return s;
+}
+
+/* Purpose:
+ * Clear floating point exception.
+ */
+void fluid_clear_fpe_i386 (void)
+{
+ _FPU_CLR_SW();
+}
+
+#endif // ifdef FPE_CHECK
+
+
+#endif // #else (its POSIX)
+
+
+/***************************************************************
+ *
+ * Profiling (Linux, i586 only)
+ *
+ */
+
+#if WITH_PROFILING
+
+fluid_profile_data_t fluid_profile_data[] =
+{
+ { FLUID_PROF_WRITE, "fluid_synth_write_* ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0}
+};
+
+
+void fluid_profiling_print(void)
+{
+ int i;
+
+ printf("fluid_profiling_print\n");
+
+ FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");
+
+ for (i = 0; i < FLUID_PROF_LAST; i++) {
+ if (fluid_profile_data[i].count > 0) {
+ FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f",
+ fluid_profile_data[i].description,
+ fluid_profile_data[i].min,
+ fluid_profile_data[i].total / fluid_profile_data[i].count,
+ fluid_profile_data[i].max);
+ } else {
+ FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description);
+ }
+ }
+}
+
+
+#endif /* WITH_PROFILING */
+
+
+
+/***************************************************************
+ *
+ * Threads
+ *
+ */
+
+#if OLD_GLIB_THREAD_API
+
+/* Rather than inline this one, we just declare it as a function, to prevent
+ * GCC warning about inline failure. */
+fluid_cond_t *
+new_fluid_cond (void)
+{
+ if (!g_thread_supported ()) g_thread_init (NULL);
+ return g_cond_new ();
+}
+
+#endif
+
+static gpointer
+fluid_thread_high_prio (gpointer data)
+{
+ fluid_thread_info_t *info = data;
+
+ fluid_thread_self_set_prio (info->prio_level);
+
+ info->func (info->data);
+ FLUID_FREE (info);
+
+ return NULL;
+}
+
+/**
+ * Create a new thread.
+ * @param func Function to execute in new thread context
+ * @param data User defined data to pass to func
+ * @param prio_level Priority level. If greater than 0 then high priority scheduling will
+ * be used, with the given priority level (used by pthreads only). 0 uses normal scheduling.
+ * @param detach If TRUE, 'join' does not work and the thread destroys itself when finished.
+ * @return New thread pointer or NULL on error
+ */
+fluid_thread_t *
+new_fluid_thread (const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach)
+{
+ GThread *thread;
+ fluid_thread_info_t *info;
+ GError *err = NULL;
+
+ g_return_val_if_fail (func != NULL, NULL);
+
+#if OLD_GLIB_THREAD_API
+ /* Make sure g_thread_init has been called.
+ * FIXME - Probably not a good idea in a shared library,
+ * but what can we do *and* remain backwards compatible? */
+ if (!g_thread_supported ()) g_thread_init (NULL);
+#endif
+
+ if (prio_level > 0)
+ {
+ info = FLUID_NEW (fluid_thread_info_t);
+
+ if (!info)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ info->func = func;
+ info->data = data;
+ info->prio_level = prio_level;
+#if NEW_GLIB_THREAD_API
+ thread = g_thread_try_new (name, fluid_thread_high_prio, info, &err);
+#else
+ thread = g_thread_create (fluid_thread_high_prio, info, detach == FALSE, &err);
+#endif
+ }
+#if NEW_GLIB_THREAD_API
+ else thread = g_thread_try_new (name, (GThreadFunc)func, data, &err);
+#else
+ else thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err);
+#endif
+
+ if (!thread)
+ {
+ FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s",
+ fluid_gerror_message (err));
+ g_clear_error (&err);
+ return NULL;
+ }
+
+#if NEW_GLIB_THREAD_API
+ if (detach) g_thread_unref (thread); // Release thread reference, if caller wants to detach
+#endif
+
+ return thread;
+}
+
+/**
+ * Frees data associated with a thread (does not actually stop thread).
+ * @param thread Thread to free
+ */
+void
+delete_fluid_thread(fluid_thread_t* thread)
+{
+ /* Threads free themselves when they quit, nothing to do */
+}
+
+/**
+ * Join a thread (wait for it to terminate).
+ * @param thread Thread to join
+ * @return FLUID_OK
+ */
+int
+fluid_thread_join(fluid_thread_t* thread)
+{
+ g_thread_join (thread);
+
+ return FLUID_OK;
+}
+
+
+static void
+fluid_timer_run (void *data)
+{
+ fluid_timer_t *timer;
+ int count = 0;
+ int cont;
+ long start;
+ long delay;
+
+ timer = (fluid_timer_t *)data;
+
+ /* keep track of the start time for absolute positioning */
+ start = fluid_curtime ();
+
+ while (timer->cont)
+ {
+ cont = (*timer->callback)(timer->data, fluid_curtime() - start);
+
+ count++;
+ if (!cont) break;
+
+ /* to avoid incremental time errors, calculate the delay between
+ two callbacks bringing in the "absolute" time (count *
+ timer->msec) */
+ delay = (count * timer->msec) - (fluid_curtime() - start);
+ if (delay > 0) g_usleep (delay * 1000);
+ }
+
+ FLUID_LOG (FLUID_DBG, "Timer thread finished");
+
+ if (timer->auto_destroy)
+ FLUID_FREE (timer);
+
+ return;
+}
+
+fluid_timer_t*
+new_fluid_timer (int msec, fluid_timer_callback_t callback, void* data,
+ int new_thread, int auto_destroy, int high_priority)
+{
+ fluid_timer_t *timer;
+
+ timer = FLUID_NEW (fluid_timer_t);
+
+ if (timer == NULL)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ timer->msec = msec;
+ timer->callback = callback;
+ timer->data = data;
+ timer->cont = TRUE ;
+ timer->thread = NULL;
+ timer->auto_destroy = auto_destroy;
+
+ if (new_thread)
+ {
+ timer->thread = new_fluid_thread ("timer", fluid_timer_run, timer, high_priority
+ ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE);
+ if (!timer->thread)
+ {
+ FLUID_FREE (timer);
+ return NULL;
+ }
+ }
+ else fluid_timer_run (timer); /* Run directly, instead of as a separate thread */
+
+ return timer;
+}
+
+int
+delete_fluid_timer (fluid_timer_t *timer)
+{
+ int auto_destroy = timer->auto_destroy;
+
+ timer->cont = 0;
+ fluid_timer_join (timer);
+
+ /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */
+
+ if (!auto_destroy) FLUID_FREE (timer);
+
+ return FLUID_OK;
+}
+
+int
+fluid_timer_join (fluid_timer_t *timer)
+{
+ int auto_destroy;
+
+ if (timer->thread)
+ {
+ auto_destroy = timer->auto_destroy;
+ fluid_thread_join (timer->thread);
+
+ if (!auto_destroy) timer->thread = NULL;
+ }
+
+ return FLUID_OK;
+}
+
+
+/***************************************************************
+ *
+ * Sockets and I/O
+ *
+ */
+
+/**
+ * Get standard in stream handle.
+ * @return Standard in stream.
+ */
+fluid_istream_t
+fluid_get_stdin (void)
+{
+ return STDIN_FILENO;
+}
+
+/**
+ * Get standard output stream handle.
+ * @return Standard out stream.
+ */
+fluid_ostream_t
+fluid_get_stdout (void)
+{
+ return STDOUT_FILENO;
+}
+
+/**
+ * Read a line from an input stream.
+ * @return 0 if end-of-stream, -1 if error, non zero otherwise
+ */
+int
+fluid_istream_readline (fluid_istream_t in, fluid_ostream_t out, char* prompt,
+ char* buf, int len)
+{
+#if WITH_READLINE
+ if (in == fluid_get_stdin ())
+ {
+ char *line;
+
+ line = readline (prompt);
+
+ if (line == NULL)
+ return -1;
+
+ snprintf(buf, len, "%s", line);
+ buf[len - 1] = 0;
+
+ free(line);
+ return 1;
+ }
+ else
+#endif
+ {
+ fluid_ostream_printf (out, "%s", prompt);
+ return fluid_istream_gets (in, buf, len);
+ }
+}
+
+/**
+ * Reads a line from an input stream (socket).
+ * @param in The input socket
+ * @param buf Buffer to store data to
+ * @param len Maximum length to store to buf
+ * @return 1 if a line was read, 0 on end of stream, -1 on error
+ */
+static int
+fluid_istream_gets (fluid_istream_t in, char* buf, int len)
+{
+ char c;
+ int n;
+
+ buf[len - 1] = 0;
+
+ while (--len > 0)
+ {
+#ifndef WIN32
+ n = read(in, &c, 1);
+ if (n == -1) return -1;
+#else
+ /* Handle read differently depending on if its a socket or file descriptor */
+ if (!(in & WIN32_SOCKET_FLAG))
+ {
+ n = read(in, &c, 1);
+ if (n == -1) return -1;
+ }
+ else
+ {
+ n = recv(in & ~WIN32_SOCKET_FLAG, &c, 1, 0);
+ if (n == SOCKET_ERROR) return -1;
+ }
+#endif
+
+ if (n == 0)
+ {
+ *buf++ = 0;
+ return 0;
+ }
+
+ if ((c == '\n'))
+ {
+ *buf++ = 0;
+ return 1;
+ }
+
+ /* Store all characters excluding CR */
+ if (c != '\r') *buf++ = c;
+ }
+
+ return -1;
+}
+
+/**
+ * Send a printf style string with arguments to an output stream (socket).
+ * @param out Output stream
+ * @param format printf style format string
+ * @param ... Arguments for the printf format string
+ * @return Number of bytes written or -1 on error
+ */
+int
+fluid_ostream_printf (fluid_ostream_t out, char* format, ...)
+{
+ char buf[4096];
+ va_list args;
+ int len;
+
+ va_start (args, format);
+ len = vsnprintf (buf, 4095, format, args);
+ va_end (args);
+
+ if (len == 0)
+ {
+ return 0;
+ }
+
+ if (len < 0)
+ {
+ printf("fluid_ostream_printf: buffer overflow");
+ return -1;
+ }
+
+ buf[4095] = 0;
+
+#ifndef WIN32
+ return write (out, buf, strlen (buf));
+#else
+ {
+ int retval;
+
+ /* Handle write differently depending on if its a socket or file descriptor */
+ if (!(out & WIN32_SOCKET_FLAG))
+ return write(out, buf, strlen (buf));
+
+ /* Socket */
+ retval = send (out & ~WIN32_SOCKET_FLAG, buf, strlen (buf), 0);
+
+ return retval != SOCKET_ERROR ? retval : -1;
+ }
+#endif
+}
+
+int fluid_server_socket_join(fluid_server_socket_t *server_socket)
+{
+ return fluid_thread_join (server_socket->thread);
+}
+
+
+#ifndef WIN32 // Not win32?
+
+#define SOCKET_ERROR -1
+
+fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
+{
+ return sock;
+}
+
+fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
+{
+ return sock;
+}
+
+void fluid_socket_close(fluid_socket_t sock)
+{
+ if (sock != INVALID_SOCKET)
+ close (sock);
+}
+
+static void
+fluid_server_socket_run (void *data)
+{
+ fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
+ fluid_socket_t client_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+ char straddr[INET6_ADDRSTRLEN];
+#else
+ struct sockaddr_in addr;
+ char straddr[INET_ADDRSTRLEN];
+#endif
+ socklen_t addrlen = sizeof (addr);
+ int retval;
+ FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
+
+ FLUID_LOG (FLUID_DBG, "Server listening for connections");
+
+ while (server_socket->cont)
+ {
+ client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen);
+
+ FLUID_LOG (FLUID_DBG, "New client connection");
+
+ if (client_socket == INVALID_SOCKET)
+ {
+ if (server_socket->cont)
+ FLUID_LOG(FLUID_ERR, "Failed to accept connection");
+
+ server_socket->cont = 0;
+ return;
+ } else {
+#ifdef IPV6
+ inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
+#else
+ inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+#endif
+ retval = server_socket->func (server_socket->data, client_socket,
+ straddr);
+
+ if (retval != 0)
+ fluid_socket_close(client_socket);
+ }
+ }
+
+ FLUID_LOG(FLUID_DBG, "Server closing");
+}
+
+fluid_server_socket_t*
+new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
+{
+ fluid_server_socket_t* server_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+#else
+ struct sockaddr_in addr;
+#endif
+ fluid_socket_t sock;
+
+ g_return_val_if_fail (func != NULL, NULL);
+#ifdef IPV6
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket");
+ return NULL;
+ }
+
+ FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in6));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = in6addr_any;
+ addr.sin6_port = htons(port);
+#else
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket");
+ return NULL;
+ }
+
+ FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+#endif
+ if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ FLUID_LOG(FLUID_ERR, "Failed to bind server socket");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ if (listen(sock, 10) == SOCKET_ERROR) {
+ FLUID_LOG(FLUID_ERR, "Failed listen on server socket");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ server_socket = FLUID_NEW(fluid_server_socket_t);
+ if (server_socket == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ server_socket->socket = sock;
+ server_socket->func = func;
+ server_socket->data = data;
+ server_socket->cont = 1;
+
+ server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
+ 0, FALSE);
+ if (server_socket->thread == NULL) {
+ FLUID_FREE(server_socket);
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ return server_socket;
+}
+
+int delete_fluid_server_socket(fluid_server_socket_t* server_socket)
+{
+ server_socket->cont = 0;
+ if (server_socket->socket != INVALID_SOCKET) {
+ fluid_socket_close(server_socket->socket);
+ }
+ if (server_socket->thread) {
+ delete_fluid_thread(server_socket->thread);
+ }
+ FLUID_FREE(server_socket);
+ return FLUID_OK;
+}
+
+
+#else // Win32 is "special"
+
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
+{
+ return sock | WIN32_SOCKET_FLAG;
+}
+
+fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
+{
+ return sock | WIN32_SOCKET_FLAG;
+}
+
+void fluid_socket_close (fluid_socket_t sock)
+{
+ if (sock != INVALID_SOCKET)
+ closesocket (sock);
+}
+
+static void fluid_server_socket_run (void *data)
+{
+ fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
+ fluid_socket_t client_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+ char straddr[INET6_ADDRSTRLEN];
+#else
+ struct sockaddr_in addr;
+ char straddr[INET_ADDRSTRLEN];
+#endif
+ socklen_t addrlen = sizeof (addr);
+ int r;
+ FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
+
+ FLUID_LOG(FLUID_DBG, "Server listening for connections");
+
+ while (server_socket->cont)
+ {
+ client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen);
+
+ FLUID_LOG (FLUID_DBG, "New client connection");
+
+ if (client_socket == INVALID_SOCKET)
+ {
+ if (server_socket->cont)
+ FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", WSAGetLastError ());
+
+ server_socket->cont = 0;
+ return;
+ }
+ else
+ {
+#ifdef IPV6
+ inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
+#else
+ inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+#endif
+ r = server_socket->func (server_socket->data, client_socket,
+ straddr);
+ if (r != 0)
+ fluid_socket_close (client_socket);
+ }
+ }
+
+ FLUID_LOG (FLUID_DBG, "Server closing");
+}
+
+fluid_server_socket_t*
+new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
+{
+ fluid_server_socket_t* server_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+#else
+ struct sockaddr_in addr;
+#endif
+
+ fluid_socket_t sock;
+ WSADATA wsaData;
+ int retval;
+
+ g_return_val_if_fail (func != NULL, NULL);
+
+ // Win32 requires initialization of winsock
+ retval = WSAStartup (MAKEWORD (2,2), &wsaData);
+
+ if (retval != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", retval);
+ return NULL;
+ }
+#ifdef IPV6
+ sock = socket (AF_INET6, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
+ WSACleanup ();
+ return NULL;
+ }
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons (port);
+ addr.sin6_addr = in6addr_any;
+#else
+
+ sock = socket (AF_INET, SOCK_STREAM, 0);
+
+ if (sock == INVALID_SOCKET)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
+ WSACleanup ();
+ return NULL;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons (port);
+ addr.sin_addr.s_addr = htonl (INADDR_ANY);
+#endif
+ retval = bind (sock, (struct sockaddr *)&addr, sizeof (addr));
+
+ if (retval == SOCKET_ERROR)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", WSAGetLastError ());
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ if (listen (sock, SOMAXCONN) == SOCKET_ERROR)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to listen on server socket: %ld", WSAGetLastError ());
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ server_socket = FLUID_NEW (fluid_server_socket_t);
+
+ if (server_socket == NULL)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ server_socket->socket = sock;
+ server_socket->func = func;
+ server_socket->data = data;
+ server_socket->cont = 1;
+
+ server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
+ 0, FALSE);
+ if (server_socket->thread == NULL)
+ {
+ FLUID_FREE (server_socket);
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ return server_socket;
+}
+
+int delete_fluid_server_socket(fluid_server_socket_t *server_socket)
+{
+ server_socket->cont = 0;
+
+ if (server_socket->socket != INVALID_SOCKET)
+ fluid_socket_close (server_socket->socket);
+
+ if (server_socket->thread)
+ delete_fluid_thread (server_socket->thread);
+
+ FLUID_FREE (server_socket);
+
+ WSACleanup (); // Should be called the same number of times as WSAStartup
+
+ return FLUID_OK;
+}
+
+#endif
diff --git a/libs/fluidsynth/src/fluid_sys.h b/libs/fluidsynth/src/fluid_sys.h
new file mode 100644
index 0000000000..4953515692
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_sys.h
@@ -0,0 +1,448 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+/**
+
+ This header contains a bunch of (mostly) system and machine
+ dependent functions:
+
+ - timers
+ - current time in milliseconds and microseconds
+ - debug logging
+ - profiling
+ - memory locking
+ - checking for floating point exceptions
+
+ */
+
+#ifndef _FLUID_SYS_H
+#define _FLUID_SYS_H
+
+#include <glib.h>
+#include "fluidsynth_priv.h"
+
+
+/**
+ * Macro used for safely accessing a message from a GError and using a default
+ * message if it is NULL.
+ * @param err Pointer to a GError to access the message field of.
+ * @return Message string
+ */
+#define fluid_gerror_message(err) ((err) ? err->message : "No error details")
+
+
+void fluid_sys_config(void);
+void fluid_log_config(void);
+void fluid_time_config(void);
+
+
+/* Misc */
+
+#define fluid_return_val_if_fail g_return_val_if_fail
+#define fluid_return_if_fail g_return_if_fail
+#define FLUID_INLINE inline
+#define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT
+#define FLUID_UINT_TO_POINTER GUINT_TO_POINTER
+#define FLUID_POINTER_TO_INT GPOINTER_TO_INT
+#define FLUID_INT_TO_POINTER GINT_TO_POINTER
+#define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0]))
+
+#define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN)
+
+/*
+ * Utility functions
+ */
+char *fluid_strtok (char **str, char *delim);
+
+
+/**
+
+ Additional debugging system, separate from the log system. This
+ allows to print selected debug messages of a specific subsystem.
+ */
+
+extern unsigned int fluid_debug_flags;
+
+#if DEBUG
+
+enum fluid_debug_level {
+ FLUID_DBG_DRIVER = 1
+};
+
+int fluid_debug(int level, char * fmt, ...);
+
+#else
+#define fluid_debug
+#endif
+
+
+#if defined(__OS2__)
+#define INCL_DOS
+#include <os2.h>
+
+typedef int socklen_t;
+#endif
+
+unsigned int fluid_curtime(void);
+double fluid_utime(void);
+
+
+/**
+ Timers
+
+ */
+
+/* if the callback function returns 1 the timer will continue; if it
+ returns 0 it will stop */
+typedef int (*fluid_timer_callback_t)(void* data, unsigned int msec);
+
+typedef struct _fluid_timer_t fluid_timer_t;
+
+fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback,
+ void* data, int new_thread, int auto_destroy,
+ int high_priority);
+
+int delete_fluid_timer(fluid_timer_t* timer);
+int fluid_timer_join(fluid_timer_t* timer);
+int fluid_timer_stop(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))
+
+/* Muteces */
+
+#if NEW_GLIB_THREAD_API
+
+/* glib 2.32 and newer */
+
+/* Regular mutex */
+typedef GMutex fluid_mutex_t;
+#define FLUID_MUTEX_INIT { 0 }
+#define fluid_mutex_init(_m) g_mutex_init (&(_m))
+#define fluid_mutex_destroy(_m) g_mutex_clear (&(_m))
+#define fluid_mutex_lock(_m) g_mutex_lock(&(_m))
+#define fluid_mutex_unlock(_m) g_mutex_unlock(&(_m))
+
+/* Recursive lock capable mutex */
+typedef GRecMutex fluid_rec_mutex_t;
+#define fluid_rec_mutex_init(_m) g_rec_mutex_init(&(_m))
+#define fluid_rec_mutex_destroy(_m) g_rec_mutex_clear(&(_m))
+#define fluid_rec_mutex_lock(_m) g_rec_mutex_lock(&(_m))
+#define fluid_rec_mutex_unlock(_m) g_rec_mutex_unlock(&(_m))
+
+/* Dynamically allocated mutex suitable for fluid_cond_t use */
+typedef GMutex fluid_cond_mutex_t;
+#define fluid_cond_mutex_lock(m) g_mutex_lock(m)
+#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m)
+
+static FLUID_INLINE fluid_cond_mutex_t *
+new_fluid_cond_mutex (void)
+{
+ GMutex *mutex;
+ mutex = g_new (GMutex, 1);
+ g_mutex_init (mutex);
+ return (mutex);
+}
+
+static FLUID_INLINE void
+delete_fluid_cond_mutex (fluid_cond_mutex_t *m)
+{
+ g_mutex_clear (m);
+ g_free (m);
+}
+
+/* Thread condition signaling */
+typedef GCond fluid_cond_t;
+#define fluid_cond_signal(cond) g_cond_signal(cond)
+#define fluid_cond_broadcast(cond) g_cond_broadcast(cond)
+#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex)
+
+static FLUID_INLINE fluid_cond_t *
+new_fluid_cond (void)
+{
+ GCond *cond;
+ cond = g_new (GCond, 1);
+ g_cond_init (cond);
+ return (cond);
+}
+
+static FLUID_INLINE void
+delete_fluid_cond (fluid_cond_t *cond)
+{
+ g_cond_clear (cond);
+ g_free (cond);
+}
+
+/* Thread private data */
+
+typedef GPrivate fluid_private_t;
+#define fluid_private_init(_priv) memset (&_priv, 0, sizeof (_priv))
+#define fluid_private_free(_priv)
+#define fluid_private_get(_priv) g_private_get(&(_priv))
+#define fluid_private_set(_priv, _data) g_private_set(&(_priv), _data)
+
+#else
+
+/* glib prior to 2.32 */
+
+/* Regular mutex */
+typedef GStaticMutex fluid_mutex_t;
+#define FLUID_MUTEX_INIT G_STATIC_MUTEX_INIT
+#define fluid_mutex_destroy(_m) g_static_mutex_free(&(_m))
+#define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m))
+#define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m))
+
+#define fluid_mutex_init(_m) G_STMT_START { \
+ if (!g_thread_supported ()) g_thread_init (NULL); \
+ g_static_mutex_init (&(_m)); \
+} G_STMT_END;
+
+/* Recursive lock capable mutex */
+typedef GStaticRecMutex fluid_rec_mutex_t;
+#define fluid_rec_mutex_destroy(_m) g_static_rec_mutex_free(&(_m))
+#define fluid_rec_mutex_lock(_m) g_static_rec_mutex_lock(&(_m))
+#define fluid_rec_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m))
+
+#define fluid_rec_mutex_init(_m) G_STMT_START { \
+ if (!g_thread_supported ()) g_thread_init (NULL); \
+ g_static_rec_mutex_init (&(_m)); \
+} G_STMT_END;
+
+/* Dynamically allocated mutex suitable for fluid_cond_t use */
+typedef GMutex fluid_cond_mutex_t;
+#define delete_fluid_cond_mutex(m) g_mutex_free(m)
+#define fluid_cond_mutex_lock(m) g_mutex_lock(m)
+#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m)
+
+static FLUID_INLINE fluid_cond_mutex_t *
+new_fluid_cond_mutex (void)
+{
+ if (!g_thread_supported ()) g_thread_init (NULL);
+ return g_mutex_new ();
+}
+
+/* Thread condition signaling */
+typedef GCond fluid_cond_t;
+fluid_cond_t *new_fluid_cond (void);
+#define delete_fluid_cond(cond) g_cond_free(cond)
+#define fluid_cond_signal(cond) g_cond_signal(cond)
+#define fluid_cond_broadcast(cond) g_cond_broadcast(cond)
+#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex)
+
+/* Thread private data */
+typedef GStaticPrivate fluid_private_t;
+#define fluid_private_get(_priv) g_static_private_get(&(_priv))
+#define fluid_private_set(_priv, _data) g_static_private_set(&(_priv), _data, NULL)
+#define fluid_private_free(_priv) g_static_private_free(&(_priv))
+
+#define fluid_private_init(_priv) G_STMT_START { \
+ if (!g_thread_supported ()) g_thread_init (NULL); \
+ g_static_private_init (&(_priv)); \
+} G_STMT_END;
+
+#endif
+
+
+/* Atomic operations */
+
+#define fluid_atomic_int_inc(_pi) g_atomic_int_inc(_pi)
+#define fluid_atomic_int_add(_pi, _val) g_atomic_int_add(_pi, _val)
+#define fluid_atomic_int_get(_pi) g_atomic_int_get(_pi)
+#define fluid_atomic_int_set(_pi, _val) g_atomic_int_set(_pi, _val)
+#define fluid_atomic_int_dec_and_test(_pi) g_atomic_int_dec_and_test(_pi)
+#define fluid_atomic_int_compare_and_exchange(_pi, _old, _new) \
+ g_atomic_int_compare_and_exchange(_pi, _old, _new)
+
+#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 30)
+#define fluid_atomic_int_exchange_and_add(_pi, _add) \
+ g_atomic_int_add(_pi, _add)
+#else
+#define fluid_atomic_int_exchange_and_add(_pi, _add) \
+ g_atomic_int_exchange_and_add(_pi, _add)
+#endif
+
+#define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp)
+#define fluid_atomic_pointer_set(_pp, val) g_atomic_pointer_set(_pp, val)
+#define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \
+ g_atomic_pointer_compare_and_exchange(_pp, _old, _new)
+
+static FLUID_INLINE void
+fluid_atomic_float_set(volatile float *fptr, float val)
+{
+ sint32 ival;
+ memcpy (&ival, &val, 4);
+ fluid_atomic_int_set ((volatile int *)fptr, ival);
+}
+
+static FLUID_INLINE float
+fluid_atomic_float_get(volatile float *fptr)
+{
+ sint32 ival;
+ float fval;
+ ival = fluid_atomic_int_get ((volatile int *)fptr);
+ memcpy (&fval, &ival, 4);
+ return fval;
+}
+
+
+/* Threads */
+
+typedef GThread fluid_thread_t;
+typedef void (*fluid_thread_func_t)(void* data);
+
+#define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */
+#define fluid_thread_id_t GThread * /* Data type for a thread ID */
+#define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */
+
+fluid_thread_t* new_fluid_thread(const char *name, fluid_thread_func_t func, void *data,
+ int prio_level, int detach);
+void delete_fluid_thread(fluid_thread_t* thread);
+void fluid_thread_self_set_prio (int prio_level);
+int fluid_thread_join(fluid_thread_t* thread);
+
+/* Sockets and I/O */
+
+fluid_istream_t fluid_get_stdin (void);
+fluid_ostream_t fluid_get_stdout (void);
+int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char* prompt, char* buf, int len);
+int fluid_ostream_printf (fluid_ostream_t out, char* format, ...);
+
+/* The function should return 0 if no error occured, 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);
+
+fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data);
+int delete_fluid_server_socket(fluid_server_socket_t* sock);
+int fluid_server_socket_join(fluid_server_socket_t* sock);
+void fluid_socket_close(fluid_socket_t sock);
+fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock);
+fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock);
+
+
+
+/* Profiling */
+
+
+/**
+ * Profile numbers. List all the pieces of code you want to profile
+ * here. Be sure to add an entry in the fluid_profile_data table in
+ * fluid_sys.c
+ */
+enum {
+ FLUID_PROF_WRITE,
+ FLUID_PROF_ONE_BLOCK,
+ FLUID_PROF_ONE_BLOCK_CLEAR,
+ FLUID_PROF_ONE_BLOCK_VOICE,
+ FLUID_PROF_ONE_BLOCK_VOICES,
+ FLUID_PROF_ONE_BLOCK_REVERB,
+ FLUID_PROF_ONE_BLOCK_CHORUS,
+ FLUID_PROF_VOICE_NOTE,
+ FLUID_PROF_VOICE_RELEASE,
+ FLUID_PROF_LAST
+};
+
+
+#if WITH_PROFILING
+
+void fluid_profiling_print(void);
+
+
+/** Profiling data. Keep track of min/avg/max values to execute a
+ piece of code. */
+typedef struct _fluid_profile_data_t {
+ int num;
+ char* description;
+ double min, max, total;
+ unsigned int count;
+} fluid_profile_data_t;
+
+extern fluid_profile_data_t fluid_profile_data[];
+
+/** Macro to obtain a time refence used for the profiling */
+#define fluid_profile_ref() fluid_utime()
+
+/** Macro to create a variable and assign the current reference time for profiling.
+ * So we don't get unused variable warnings when profiling is disabled. */
+#define fluid_profile_ref_var(name) double name = fluid_utime()
+
+/** Macro to calculate the min/avg/max. Needs a time refence and a
+ profile number. */
+#define fluid_profile(_num,_ref) { \
+ double _now = fluid_utime(); \
+ double _delta = _now - _ref; \
+ fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ? _delta : fluid_profile_data[_num].min; \
+ fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ? _delta : fluid_profile_data[_num].max; \
+ fluid_profile_data[_num].total += _delta; \
+ fluid_profile_data[_num].count++; \
+ _ref = _now; \
+}
+
+
+#else
+
+/* No profiling */
+#define fluid_profiling_print()
+#define fluid_profile_ref() 0
+#define fluid_profile_ref_var(name)
+#define fluid_profile(_num,_ref)
+
+#endif
+
+
+
+/**
+
+ Memory locking
+
+ Memory locking is used to avoid swapping of the large block of
+ sample data.
+ */
+
+#if defined(HAVE_SYS_MMAN_H) && !defined(__OS2__)
+#define fluid_mlock(_p,_n) mlock(_p, _n)
+#define fluid_munlock(_p,_n) munlock(_p,_n)
+#else
+#define fluid_mlock(_p,_n) 0
+#define fluid_munlock(_p,_n)
+#endif
+
+
+/**
+
+ Floating point exceptions
+
+ fluid_check_fpe() checks for "unnormalized numbers" and other
+ exceptions of the floating point processsor.
+*/
+#ifdef FPE_CHECK
+#define fluid_check_fpe(expl) fluid_check_fpe_i386(expl)
+#define fluid_clear_fpe() fluid_clear_fpe_i386()
+#else
+#define fluid_check_fpe(expl)
+#define fluid_clear_fpe()
+#endif
+
+unsigned int fluid_check_fpe_i386(char * explanation_in_case_of_fpe);
+void fluid_clear_fpe_i386(void);
+
+#endif /* _FLUID_SYS_H */
diff --git a/libs/fluidsynth/src/fluid_tuning.c b/libs/fluidsynth/src/fluid_tuning.c
new file mode 100644
index 0000000000..8977ed6728
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_tuning.c
@@ -0,0 +1,174 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#include "fluid_tuning.h"
+#include "fluidsynth_priv.h"
+#include "fluid_sys.h"
+
+
+fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog)
+{
+ fluid_tuning_t* tuning;
+ int i;
+
+ tuning = FLUID_NEW(fluid_tuning_t);
+ if (tuning == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+
+ tuning->name = NULL;
+
+ if (name != NULL) {
+ tuning->name = FLUID_STRDUP(name);
+ }
+
+ tuning->bank = bank;
+ tuning->prog = prog;
+
+ for (i = 0; i < 128; i++) {
+ tuning->pitch[i] = i * 100.0;
+ }
+
+ tuning->refcount = 1; /* Start with a refcount of 1 */
+
+ return tuning;
+}
+
+/* Duplicate a tuning */
+fluid_tuning_t *
+fluid_tuning_duplicate (fluid_tuning_t *tuning)
+{
+ fluid_tuning_t *new_tuning;
+ int i;
+
+ new_tuning = FLUID_NEW (fluid_tuning_t);
+
+ if (!new_tuning) {
+ FLUID_LOG (FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+
+ if (tuning->name)
+ {
+ new_tuning->name = FLUID_STRDUP (tuning->name);
+
+ if (!new_tuning->name)
+ {
+ FLUID_FREE (new_tuning);
+ FLUID_LOG (FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+ }
+ else new_tuning->name = NULL;
+
+ new_tuning->bank = tuning->bank;
+ new_tuning->prog = tuning->prog;
+
+ for (i = 0; i < 128; i++)
+ new_tuning->pitch[i] = tuning->pitch[i];
+
+ new_tuning->refcount = 1; /* Start with a refcount of 1 */
+
+ return new_tuning;
+}
+
+void
+delete_fluid_tuning (fluid_tuning_t *tuning)
+{
+ if (tuning->name) FLUID_FREE (tuning->name);
+ FLUID_FREE (tuning);
+}
+
+/* Add a reference to a tuning object */
+void
+fluid_tuning_ref (fluid_tuning_t *tuning)
+{
+ fluid_return_if_fail (tuning != NULL);
+
+ fluid_atomic_int_inc (&tuning->refcount);
+}
+
+/* Unref a tuning object, when it reaches 0 it is deleted, returns TRUE if deleted */
+int
+fluid_tuning_unref (fluid_tuning_t *tuning, int count)
+{
+ fluid_return_val_if_fail (tuning != NULL, FALSE);
+
+ /* Add and compare are separate, but that is OK, since refcount will only
+ * reach 0 when there are no references and therefore no possibility of
+ * another thread adding a reference in between */
+ fluid_atomic_int_add (&tuning->refcount, -count);
+
+ /* Delete when refcount reaches 0 */
+ if (!fluid_atomic_int_get (&tuning->refcount))
+ {
+ delete_fluid_tuning (tuning);
+ return TRUE;
+ }
+ else return FALSE;
+}
+
+void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name)
+{
+ if (tuning->name != NULL) {
+ FLUID_FREE(tuning->name);
+ tuning->name = NULL;
+ }
+ if (name != NULL) {
+ tuning->name = FLUID_STRDUP(name);
+ }
+}
+
+char* fluid_tuning_get_name(fluid_tuning_t* tuning)
+{
+ return tuning->name;
+}
+
+static void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch)
+{
+ tuning->pitch[key] = pitch;
+}
+
+void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv)
+{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12];
+ }
+}
+
+void fluid_tuning_set_all(fluid_tuning_t* tuning, const double* pitch)
+{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ tuning->pitch[i] = pitch[i];
+ }
+}
+
+void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch)
+{
+ if ((key >= 0) && (key < 128)) {
+ tuning->pitch[key] = pitch;
+ }
+}
diff --git a/libs/fluidsynth/src/fluid_tuning.h b/libs/fluidsynth/src/fluid_tuning.h
new file mode 100644
index 0000000000..d974139276
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_tuning.h
@@ -0,0 +1,68 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+/*
+
+ 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
+
+*/
+
+#ifndef _FLUID_TUNING_H
+#define _FLUID_TUNING_H
+
+#include "fluidsynth_priv.h"
+
+struct _fluid_tuning_t {
+ char* name;
+ int bank;
+ int prog;
+ double pitch[128]; /* the pitch of every key, in cents */
+ int refcount; /* Tuning reference count */
+};
+
+fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog);
+void delete_fluid_tuning (fluid_tuning_t *tuning);
+fluid_tuning_t *fluid_tuning_duplicate (fluid_tuning_t *tuning);
+void fluid_tuning_ref (fluid_tuning_t *tuning);
+int fluid_tuning_unref (fluid_tuning_t *tuning, int count);
+
+void fluid_tuning_set_name(fluid_tuning_t* tuning, char* name);
+char* fluid_tuning_get_name(fluid_tuning_t* tuning);
+
+#define fluid_tuning_get_bank(_t) ((_t)->bank)
+#define fluid_tuning_get_prog(_t) ((_t)->prog)
+
+void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch);
+#define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key])
+
+void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv);
+
+void fluid_tuning_set_all(fluid_tuning_t* tuning, const double* pitch);
+#define fluid_tuning_get_all(_t) (&(_t)->pitch[0])
+
+
+
+
+#endif /* _FLUID_TUNING_H */
diff --git a/libs/fluidsynth/src/fluid_voice.c b/libs/fluidsynth/src/fluid_voice.c
new file mode 100644
index 0000000000..e6efbac899
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_voice.c
@@ -0,0 +1,1626 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_voice.h"
+#include "fluid_mod.h"
+#include "fluid_chan.h"
+#include "fluid_conv.h"
+#include "fluid_synth.h"
+#include "fluid_sys.h"
+#include "fluid_sfont.h"
+#include "fluid_rvoice_event.h"
+
+/* used for filter turn off optimization - if filter cutoff is above the
+ specified value and filter q is below the other value, turn filter off */
+#define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f
+#define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f
+
+/* min vol envelope release (to stop clicks) in SoundFont timecents */
+#define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */
+
+static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice);
+static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
+ int gen_key2base, int is_decay);
+static fluid_real_t
+fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
+
+#define UPDATE_RVOICE0(proc) \
+ do { \
+ if (voice->can_access_rvoice) proc(voice->rvoice); \
+ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
+ proc, voice->rvoice, 0, 0.0f); \
+ } while (0)
+
+#define UPDATE_RVOICE_PTR(proc, obj) \
+ do { \
+ if (voice->can_access_rvoice) proc(voice->rvoice, obj); \
+ else fluid_rvoice_eventhandler_push_ptr(voice->channel->synth->eventhandler, \
+ proc, voice->rvoice, obj); \
+ } while (0)
+
+
+#define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \
+ do { \
+ if (voice->can_access_rvoice) proc(obj, rarg); \
+ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
+ proc, obj, 0, rarg); \
+ } while (0)
+
+#define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \
+ do { \
+ if (voice->can_access_rvoice) proc(obj, iarg); \
+ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
+ proc, obj, iarg, 0.0f); \
+ } while (0)
+
+#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \
+ do { \
+ if (voice->can_access_rvoice) proc(obj, iarg, rarg); \
+ else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
+ proc, obj, iarg, rarg); \
+ } while (0)
+
+#define UPDATE_RVOICE_GENERIC_ALL(proc, obj, iarg, r1, r2, r3, r4, r5) \
+ do { \
+ if (voice->can_access_rvoice) proc(obj, iarg, r1, r2, r3, r4, r5); \
+ else fluid_rvoice_eventhandler_push5(voice->channel->synth->eventhandler, \
+ proc, obj, iarg, r1, r2, r3, r4, r5); \
+ } while (0)
+
+
+#define UPDATE_RVOICE_VOLENV(section, arg1, arg2, arg3, arg4, arg5) \
+ do { \
+ fluid_adsr_env_set_data(&voice->volenv, section, arg1, arg2, arg3, arg4, arg5) \
+ UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.volenv, section, arg1, arg2, arg3, arg4, arg5) \
+ } while(0)
+
+#define UPDATE_RVOICE_MODENV(section, arg1, arg2, arg3, arg4, arg5) \
+ UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.modenv, section, arg1, arg2, arg3, arg4, arg5)
+
+#define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1)
+#define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1)
+#define UPDATE_RVOICE_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_filter, arg1)
+
+#define UPDATE_RVOICE2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, voice->rvoice, iarg, rarg)
+#define UPDATE_RVOICE_BUFFERS2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg)
+#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg)
+#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg)
+
+static inline void
+fluid_voice_update_volenv(fluid_voice_t* voice,
+ fluid_adsr_env_section_t section,
+ unsigned int count,
+ fluid_real_t coeff,
+ fluid_real_t increment,
+ fluid_real_t min,
+ fluid_real_t max)
+{
+ fluid_adsr_env_set_data(&voice->volenv, section, count, coeff, increment,
+ min, max);
+ UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data,
+ &voice->rvoice->envlfo.volenv, section, count,
+ coeff, increment, min, max);
+}
+
+static inline void
+fluid_voice_update_modenv(fluid_voice_t* voice,
+ fluid_adsr_env_section_t section,
+ unsigned int count,
+ fluid_real_t coeff,
+ fluid_real_t increment,
+ fluid_real_t min,
+ fluid_real_t max)
+{
+ UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data,
+ &voice->rvoice->envlfo.modenv, section, count,
+ coeff, increment, min, max);
+}
+
+static inline void fluid_sample_null_ptr(fluid_sample_t** sample)
+{
+ if (*sample != NULL) {
+ fluid_sample_decr_ref(*sample);
+ *sample = NULL;
+ }
+}
+
+/*
+ * Swaps the current rvoice with the current overflow_rvoice
+ */
+static void fluid_voice_swap_rvoice(fluid_voice_t* voice)
+{
+ fluid_rvoice_t* rtemp = voice->rvoice;
+ int ctemp = voice->can_access_rvoice;
+ voice->rvoice = voice->overflow_rvoice;
+ voice->can_access_rvoice = voice->can_access_overflow_rvoice;
+ voice->overflow_rvoice = rtemp;
+ voice->can_access_overflow_rvoice = ctemp;
+}
+
+static void fluid_voice_initialize_rvoice(fluid_voice_t* voice)
+{
+ FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t));
+
+ /* The 'sustain' and 'finished' segments of the volume / modulation
+ * envelope are constant. They are never affected by any modulator
+ * or generator. Therefore it is enough to initialize them once
+ * during the lifetime of the synth.
+ */
+ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVSUSTAIN,
+ 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
+ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVFINISHED,
+ 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
+ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVSUSTAIN,
+ 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
+ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVFINISHED,
+ 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
+}
+
+/*
+ * new_fluid_voice
+ */
+fluid_voice_t*
+new_fluid_voice(fluid_real_t output_rate)
+{
+ fluid_voice_t* voice;
+ voice = FLUID_NEW(fluid_voice_t);
+ if (voice == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ voice->rvoice = FLUID_NEW(fluid_rvoice_t);
+ voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t);
+ if (voice->rvoice == NULL || voice->overflow_rvoice == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(voice->rvoice);
+ FLUID_FREE(voice);
+ return NULL;
+ }
+
+ voice->status = FLUID_VOICE_CLEAN;
+ voice->chan = NO_CHANNEL;
+ voice->key = 0;
+ voice->vel = 0;
+ voice->channel = NULL;
+ voice->sample = NULL;
+
+ /* Initialize both the rvoice and overflow_rvoice */
+ voice->can_access_rvoice = 1;
+ voice->can_access_overflow_rvoice = 1;
+ fluid_voice_initialize_rvoice(voice);
+ fluid_voice_swap_rvoice(voice);
+ fluid_voice_initialize_rvoice(voice);
+
+ fluid_voice_set_output_rate(voice, output_rate);
+
+ return voice;
+}
+
+/*
+ * delete_fluid_voice
+ */
+int
+delete_fluid_voice(fluid_voice_t* voice)
+{
+ if (voice == NULL) {
+ return FLUID_OK;
+ }
+ if (!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) {
+ /* stop rvoice before deleting voice! */
+ return FLUID_FAILED;
+ }
+ FLUID_FREE(voice->overflow_rvoice);
+ FLUID_FREE(voice->rvoice);
+ FLUID_FREE(voice);
+ return FLUID_OK;
+}
+
+/* fluid_voice_init
+ *
+ * Initialize the synthesis process
+ */
+int
+fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
+ fluid_channel_t* channel, int key, int vel, unsigned int id,
+ unsigned int start_time, fluid_real_t gain)
+{
+ /* Note: The voice parameters will be initialized later, when the
+ * generators have been retrieved from the sound font. Here, only
+ * the 'working memory' of the voice (position in envelopes, history
+ * of IIR filters, position in sample etc) is initialized. */
+ int i;
+
+ if (!voice->can_access_rvoice) {
+ if (voice->can_access_overflow_rvoice)
+ fluid_voice_swap_rvoice(voice);
+ else {
+ FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!");
+ return FLUID_FAILED;
+ }
+ }
+ /* We are now guaranteed to have access to the rvoice */
+
+ if (voice->sample)
+ fluid_voice_off(voice);
+
+ voice->id = id;
+ voice->chan = fluid_channel_get_num(channel);
+ voice->key = (unsigned char) key;
+ voice->vel = (unsigned char) vel;
+ voice->channel = channel;
+ voice->mod_count = 0;
+ voice->start_time = start_time;
+ voice->debug = 0;
+ 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. */
+ fluid_sample_incr_ref(sample);
+ UPDATE_RVOICE_PTR(fluid_rvoice_set_sample, sample);
+ fluid_sample_incr_ref(sample);
+ voice->sample = sample;
+
+ i = fluid_channel_get_interp_method(channel);
+ UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i);
+
+ /* Set all the generators to their default value, according to SF
+ * 2.01 section 8.1.3 (page 48). The value of NRPN messages are
+ * copied from the channel to the voice's generators. The sound font
+ * loader overwrites them. The generator values are later converted
+ * into voice parameters in
+ * fluid_voice_calculate_runtime_synthesis_parameters. */
+ fluid_gen_init(&voice->gen[0], channel);
+ UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice));
+
+ voice->synth_gain = gain;
+ /* avoid division by zero later*/
+ if (voice->synth_gain < 0.0000001){
+ voice->synth_gain = 0.0000001;
+ }
+ UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain);
+
+ /* Set up buffer mapping, should be done more flexible in the future. */
+ i = channel->synth->audio_groups;
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 2, i*2 + SYNTH_REVERB_CHANNEL);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 3, i*2 + SYNTH_CHORUS_CHANNEL);
+ i = 2 * (voice->chan % i);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 0, i);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 1, i+1);
+
+ return FLUID_OK;
+}
+
+
+/**
+ * Update sample rate.
+ * NOTE: If the voice is active, it will be turned off.
+ */
+int
+fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value)
+{
+ if (_PLAYING(voice))
+ fluid_voice_off(voice);
+
+ voice->output_rate = value;
+ UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value);
+ /* Update the other rvoice as well */
+ fluid_voice_swap_rvoice(voice);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value);
+ fluid_voice_swap_rvoice(voice);
+
+ return FLUID_FAILED;
+}
+
+
+/**
+ * Set the value of a generator.
+ * @param voice Voice instance
+ * @param i Generator ID (#fluid_gen_type)
+ * @param val Generator value
+ */
+void
+fluid_voice_gen_set(fluid_voice_t* voice, int i, float val)
+{
+ voice->gen[i].val = val;
+ voice->gen[i].flags = GEN_SET;
+ if (i == GEN_SAMPLEMODE)
+ UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) 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
+ */
+void
+fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val)
+{
+ voice->gen[i].val += val;
+ voice->gen[i].flags = GEN_SET;
+}
+
+/**
+ * Get the value of a generator.
+ * @param voice Voice instance
+ * @param gen Generator ID (#fluid_gen_type)
+ * @return Current generator value
+ */
+float
+fluid_voice_gen_get(fluid_voice_t* voice, int gen)
+{
+ return voice->gen[gen].val;
+}
+
+fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num)
+{
+ /* This is an extension to the SoundFont standard. More
+ * documentation is available at the fluid_synth_set_gen2()
+ * function. */
+ if (voice->gen[num].flags == GEN_ABS_NRPN) {
+ return (fluid_real_t) voice->gen[num].nrpn;
+ } else {
+ return (fluid_real_t) (voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn);
+ }
+}
+
+
+/**
+ * Synthesize a voice to a buffer.
+ *
+ * @param voice Voice to synthesize
+ * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length)
+ * @return Count of samples written to dsp_buf (can be 0)
+ *
+ * Panning, reverb and chorus are processed separately. The dsp interpolation
+ * routine is in (fluid_dsp_float.c).
+ */
+int
+fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
+{
+ int result;
+ if (!voice->can_access_rvoice)
+ return 0;
+
+ result = fluid_rvoice_write(voice->rvoice, dsp_buf);
+
+ if (result == -1)
+ return 0;
+
+ if ((result < FLUID_BUFSIZE) && _PLAYING(voice)) /* Voice finished by itself */
+ fluid_voice_off(voice);
+
+ return result;
+}
+
+
+/**
+ * Mix voice data to left/right (panning), reverb and chorus buffers.
+ * @param count Number of samples
+ * @param dsp_buf Source buffer
+ * @param voice Voice to mix
+ * @param left_buf Left audio buffer
+ * @param right_buf Right audio buffer
+ * @param reverb_buf Reverb buffer
+ * @param chorus_buf Chorus buffer
+ *
+ */
+void
+fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
+ fluid_real_t* left_buf, fluid_real_t* right_buf,
+ fluid_real_t* reverb_buf, fluid_real_t* chorus_buf)
+{
+ fluid_rvoice_buffers_t buffers;
+ fluid_real_t* dest_buf[4] = {left_buf, right_buf, reverb_buf, chorus_buf};
+
+ fluid_rvoice_buffers_set_amp(&buffers, 0, voice->amp_left);
+ fluid_rvoice_buffers_set_amp(&buffers, 1, voice->amp_right);
+ fluid_rvoice_buffers_set_amp(&buffers, 2, voice->amp_reverb);
+ fluid_rvoice_buffers_set_amp(&buffers, 3, voice->amp_chorus);
+
+ fluid_rvoice_buffers_mix(&buffers, dsp_buf, count, dest_buf, 4);
+
+ fluid_check_fpe ("voice_mix");
+}
+
+
+
+/*
+ * fluid_voice_start
+ */
+void fluid_voice_start(fluid_voice_t* voice)
+{
+ /* The maximum volume of the loop is calculated and cached once for each
+ * sample with its nominal loop settings. This happens, when the sample is used
+ * for the first time.*/
+
+ fluid_voice_calculate_runtime_synthesis_parameters(voice);
+
+ voice->ref = fluid_profile_ref();
+
+ voice->status = FLUID_VOICE_ON;
+
+ /* Increment voice count */
+ voice->channel->synth->active_voice_count++;
+}
+
+void
+fluid_voice_calculate_gen_pitch(fluid_voice_t* voice)
+{
+ fluid_tuning_t* tuning;
+ fluid_real_t x;
+
+ /* The GEN_PITCH is a hack to fit the pitch bend controller into the
+ * modulator paradigm. Now the nominal pitch of the key is set.
+ * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a
+ * non-realtime parameter. So we don't allow modulation (as opposed
+ * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied,
+ * one key remains fixed. Here C3 (MIDI number 60) is used.
+ */
+ if (fluid_channel_has_tuning(voice->channel)) {
+ tuning = fluid_channel_get_tuning (voice->channel);
+ x = fluid_tuning_get_pitch (tuning, (int)(voice->root_pitch / 100.0f));
+ voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val / 100.0f *
+ (fluid_tuning_get_pitch (tuning, voice->key) - x) + x;
+ } else {
+ voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val
+ * (voice->key - voice->root_pitch / 100.0f) + voice->root_pitch;
+ }
+
+}
+
+/*
+ * fluid_voice_calculate_runtime_synthesis_parameters
+ *
+ * in this function we calculate the values of all the parameters. the
+ * parameters are converted to their most useful unit for the DSP
+ * algorithm, for example, number of samples instead of
+ * timecents. Some parameters keep their "perceptual" unit and
+ * conversion will be done in the DSP function. This is the case, for
+ * example, for the pitch since it is modulated by the controllers in
+ * cents. */
+static int
+fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice)
+{
+ int i;
+
+ int list_of_generators_to_initialize[35] = {
+ GEN_STARTADDROFS, /* SF2.01 page 48 #0 */
+ GEN_ENDADDROFS, /* #1 */
+ GEN_STARTLOOPADDROFS, /* #2 */
+ GEN_ENDLOOPADDROFS, /* #3 */
+ /* GEN_STARTADDRCOARSEOFS see comment below [1] #4 */
+ GEN_MODLFOTOPITCH, /* #5 */
+ GEN_VIBLFOTOPITCH, /* #6 */
+ GEN_MODENVTOPITCH, /* #7 */
+ GEN_FILTERFC, /* #8 */
+ GEN_FILTERQ, /* #9 */
+ GEN_MODLFOTOFILTERFC, /* #10 */
+ GEN_MODENVTOFILTERFC, /* #11 */
+ /* GEN_ENDADDRCOARSEOFS [1] #12 */
+ GEN_MODLFOTOVOL, /* #13 */
+ /* not defined #14 */
+ GEN_CHORUSSEND, /* #15 */
+ GEN_REVERBSEND, /* #16 */
+ GEN_PAN, /* #17 */
+ /* not defined #18 */
+ /* not defined #19 */
+ /* not defined #20 */
+ GEN_MODLFODELAY, /* #21 */
+ GEN_MODLFOFREQ, /* #22 */
+ GEN_VIBLFODELAY, /* #23 */
+ GEN_VIBLFOFREQ, /* #24 */
+ GEN_MODENVDELAY, /* #25 */
+ GEN_MODENVATTACK, /* #26 */
+ GEN_MODENVHOLD, /* #27 */
+ GEN_MODENVDECAY, /* #28 */
+ /* GEN_MODENVSUSTAIN [1] #29 */
+ GEN_MODENVRELEASE, /* #30 */
+ /* GEN_KEYTOMODENVHOLD [1] #31 */
+ /* GEN_KEYTOMODENVDECAY [1] #32 */
+ GEN_VOLENVDELAY, /* #33 */
+ GEN_VOLENVATTACK, /* #34 */
+ GEN_VOLENVHOLD, /* #35 */
+ GEN_VOLENVDECAY, /* #36 */
+ /* GEN_VOLENVSUSTAIN [1] #37 */
+ GEN_VOLENVRELEASE, /* #38 */
+ /* GEN_KEYTOVOLENVHOLD [1] #39 */
+ /* GEN_KEYTOVOLENVDECAY [1] #40 */
+ /* GEN_STARTLOOPADDRCOARSEOFS [1] #45 */
+ GEN_KEYNUM, /* #46 */
+ GEN_VELOCITY, /* #47 */
+ GEN_ATTENUATION, /* #48 */
+ /* GEN_ENDLOOPADDRCOARSEOFS [1] #50 */
+ /* GEN_COARSETUNE [1] #51 */
+ /* GEN_FINETUNE [1] #52 */
+ GEN_OVERRIDEROOTKEY, /* #58 */
+ GEN_PITCH, /* --- */
+ -1}; /* end-of-list marker */
+
+ /* When the voice is made ready for the synthesis process, a lot of
+ * voice-internal parameters have to be calculated.
+ *
+ * At this point, the sound font has already set the -nominal- value
+ * for all generators (excluding GEN_PITCH). Most generators can be
+ * modulated - they include a nominal value and an offset (which
+ * changes with velocity, note number, channel parameters like
+ * aftertouch, mod wheel...) Now this offset will be calculated as
+ * follows:
+ *
+ * - Process each modulator once.
+ * - Calculate its output value.
+ * - Find the target generator.
+ * - Add the output value to the modulation value of the generator.
+ *
+ * Note: The generators have been initialized with
+ * fluid_gen_set_default_values.
+ */
+
+ for (i = 0; i < voice->mod_count; i++) {
+ fluid_mod_t* mod = &voice->mod[i];
+ fluid_real_t modval = fluid_mod_get_value(mod, voice->channel, voice);
+ int dest_gen_index = mod->dest;
+ fluid_gen_t* dest_gen = &voice->gen[dest_gen_index];
+ dest_gen->mod += modval;
+ /* fluid_dump_modulator(mod); */
+ }
+
+ /* Now the generators are initialized, nominal and modulation value.
+ * The voice parameters (which depend on generators) are calculated
+ * with fluid_voice_update_param. Processing the list of generator
+ * changes will calculate each voice parameter once.
+ *
+ * Note [1]: Some voice parameters depend on several generators. For
+ * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and
+ * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided
+ * by removing all but one generator from the list of voice
+ * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the
+ * initialisation list contains only GEN_XXX.
+ */
+
+ /* Calculate the voice parameter(s) dependent on each generator. */
+ for (i = 0; list_of_generators_to_initialize[i] != -1; i++) {
+ fluid_voice_update_param(voice, list_of_generators_to_initialize[i]);
+ }
+
+ /* Make an estimate on how loud this voice can get at any time (attenuation). */
+ UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB,
+ fluid_voice_get_lower_boundary_for_attenuation(voice));
+ return FLUID_OK;
+}
+
+/*
+ * calculate_hold_decay_buffers
+ */
+static int
+calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
+ int gen_key2base, int is_decay)
+{
+ /* Purpose:
+ *
+ * Returns the number of DSP loops, that correspond to the hold
+ * (is_decay=0) or decay (is_decay=1) time.
+ * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD,
+ * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD,
+ * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY
+ */
+
+ fluid_real_t timecents;
+ fluid_real_t seconds;
+ int buffers;
+
+ /* SF2.01 section 8.4.3 # 31, 32, 39, 40
+ * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'.
+ * The unit of the generator is timecents per key number.
+ * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72)
+ * will cause (60-72)*100=-1200 timecents of time variation.
+ * The time is cut in half.
+ */
+ timecents = (_GEN(voice, gen_base) + _GEN(voice, gen_key2base) * (60.0 - voice->key));
+
+ /* Range checking */
+ if (is_decay){
+ /* SF 2.01 section 8.1.3 # 28, 36 */
+ if (timecents > 8000.0) {
+ timecents = 8000.0;
+ }
+ } else {
+ /* SF 2.01 section 8.1.3 # 27, 35 */
+ if (timecents > 5000) {
+ timecents = 5000.0;
+ }
+ /* SF 2.01 section 8.1.2 # 27, 35:
+ * The most negative number indicates no hold time
+ */
+ if (timecents <= -32768.) {
+ return 0;
+ }
+ }
+ /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */
+ if (timecents < -12000.0) {
+ timecents = -12000.0;
+ }
+
+ seconds = fluid_tc2sec(timecents);
+ /* Each DSP loop processes FLUID_BUFSIZE samples. */
+
+ /* round to next full number of buffers */
+ buffers = (int)(((fluid_real_t)voice->output_rate * seconds)
+ / (fluid_real_t)FLUID_BUFSIZE
+ +0.5);
+
+ return buffers;
+}
+
+/*
+ * The value of a generator (gen) has changed. (The different
+ * generators are listed in fluidsynth.h, or in SF2.01 page 48-49)
+ * Now the dependent 'voice' parameters are calculated.
+ *
+ * fluid_voice_update_param can be called during the setup of the
+ * voice (to calculate the initial value for a voice parameter), or
+ * during its operation (a generator has been changed due to
+ * real-time parameter modifications like pitch-bend).
+ *
+ * Note: The generator holds three values: The base value .val, an
+ * offset caused by modulators .mod, and an offset caused by the
+ * NRPN system. _GEN(voice, generator_enumerator) returns the sum
+ * of all three.
+ */
+/**
+ * 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.
+ */
+void
+fluid_voice_update_param(fluid_voice_t* voice, int gen)
+{
+ double q_dB;
+ fluid_real_t x;
+ fluid_real_t y;
+ unsigned int count, z;
+ // Alternate attenuation scale used by EMU10K1 cards when setting the attenuation at the preset or instrument level within the SoundFont bank.
+ static const float ALT_ATTENUATION_SCALE = 0.4;
+
+ switch (gen) {
+
+ case GEN_PAN:
+ /* range checking is done in the fluid_pan function */
+ voice->pan = _GEN(voice, GEN_PAN);
+ voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f;
+ voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f;
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
+ break;
+
+ case GEN_ATTENUATION:
+ voice->attenuation = ((fluid_real_t)(voice)->gen[GEN_ATTENUATION].val*ALT_ATTENUATION_SCALE) +
+ (fluid_real_t)(voice)->gen[GEN_ATTENUATION].mod + (fluid_real_t)(voice)->gen[GEN_ATTENUATION].nrpn;
+
+ /* Range: SF2.01 section 8.1.3 # 48
+ * Motivation for range checking:
+ * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */
+ fluid_clip(voice->attenuation, 0.0, 1440.0);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation);
+ break;
+
+ /* The pitch is calculated from three different generators.
+ * Read comment in fluidsynth.h about GEN_PITCH.
+ */
+ case GEN_PITCH:
+ case GEN_COARSETUNE:
+ case GEN_FINETUNE:
+ /* The testing for allowed range is done in 'fluid_ct2hz' */
+ voice->pitch = (_GEN(voice, GEN_PITCH)
+ + 100.0f * _GEN(voice, GEN_COARSETUNE)
+ + _GEN(voice, GEN_FINETUNE));
+ UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch);
+ break;
+
+ case GEN_REVERBSEND:
+ /* The generator unit is 'tenths of a percent'. */
+ voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f;
+ fluid_clip(voice->reverb_send, 0.0, 1.0);
+ voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f;
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
+ break;
+
+ case GEN_CHORUSSEND:
+ /* The generator unit is 'tenths of a percent'. */
+ voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f;
+ fluid_clip(voice->chorus_send, 0.0, 1.0);
+ voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f;
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
+ break;
+
+ case GEN_OVERRIDEROOTKEY:
+ /* This is a non-realtime parameter. Therefore the .mod part of the generator
+ * can be neglected.
+ * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount
+ * which offsets the original rate. This means that the fine tuning is
+ * inverted with respect to the root note (so subtract it, not add).
+ */
+ if (voice->sample != NULL) {
+ if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1
+ voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f
+ - voice->sample->pitchadj;
+ else
+ voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj;
+ x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate));
+ } else {
+ if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1
+ voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f;
+ else
+ voice->root_pitch = 0;
+ x = fluid_ct2hz(voice->root_pitch);
+ }
+ /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
+ fluid_voice_calculate_gen_pitch(voice);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x);
+
+ break;
+
+ case GEN_FILTERFC:
+ /* The resonance frequency is converted from absolute cents to
+ * midicents .val and .mod are both used, this permits real-time
+ * modulation. The allowed range is tested in the 'fluid_ct2hz'
+ * function [PH,20021214]
+ */
+ x = _GEN(voice, GEN_FILTERFC);
+ UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_fres, x);
+ break;
+
+ case GEN_FILTERQ:
+ /* The generator contains 'centibels' (1/10 dB) => divide by 10 to
+ * obtain dB */
+ q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f;
+
+ /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
+ fluid_clip(q_dB, 0.0f, 96.0f);
+
+ /* Short version: Modify the Q definition in a way, that a Q of 0
+ * dB leads to no resonance hump in the freq. response.
+ *
+ * Long version: From SF2.01, page 39, item 9 (initialFilterQ):
+ * "The gain at the cutoff frequency may be less than zero when
+ * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave
+ * q as it is, then this results in a 3 dB hump slightly below
+ * fc. At fc, the gain is exactly the DC gain (0 dB). What is
+ * (probably) meant here is that the filter does not show a
+ * resonance hump for q_dB=0. In this case, the corresponding
+ * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of
+ * attenuation at fc now. In this case Q_dB is the height of the
+ * resonance peak not over the DC gain, but over the frequency
+ * response of a non-resonant filter. This idea is implemented as
+ * follows: */
+ q_dB -= 3.01f;
+ UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_q_dB, q_dB);
+
+ break;
+
+ case GEN_MODLFOTOPITCH:
+ x = _GEN(voice, GEN_MODLFOTOPITCH);
+ fluid_clip(x, -12000.0, 12000.0);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x);
+ break;
+
+ case GEN_MODLFOTOVOL:
+ x = _GEN(voice, GEN_MODLFOTOVOL);
+ fluid_clip(x, -960.0, 960.0);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x);
+ break;
+
+ case GEN_MODLFOTOFILTERFC:
+ x = _GEN(voice, GEN_MODLFOTOFILTERFC);
+ fluid_clip(x, -12000, 12000);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x);
+ break;
+
+ case GEN_MODLFODELAY:
+ x = _GEN(voice, GEN_MODLFODELAY);
+ fluid_clip(x, -12000.0f, 5000.0f);
+ z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
+ UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z);
+ break;
+
+ case GEN_MODLFOFREQ:
+ /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples
+ * - the delay into a sample delay
+ */
+ x = _GEN(voice, GEN_MODLFOFREQ);
+ fluid_clip(x, -16000.0f, 4500.0f);
+ x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
+ UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x);
+ break;
+
+ case GEN_VIBLFOFREQ:
+ /* vib lfo
+ *
+ * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples
+ * - the delay into a sample delay
+ */
+ x = _GEN(voice, GEN_VIBLFOFREQ);
+ fluid_clip(x, -16000.0f, 4500.0f);
+ x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate;
+ UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x);
+ break;
+
+ case GEN_VIBLFODELAY:
+ x = _GEN(voice,GEN_VIBLFODELAY);
+ fluid_clip(x, -12000.0f, 5000.0f);
+ z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
+ UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z);
+ break;
+
+ case GEN_VIBLFOTOPITCH:
+ x = _GEN(voice, GEN_VIBLFOTOPITCH);
+ fluid_clip(x, -12000.0, 12000.0);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x);
+ break;
+
+ case GEN_KEYNUM:
+ /* GEN_KEYNUM: SF2.01 page 46, item 46
+ *
+ * If this generator is active, it forces the key number to its
+ * value. Non-realtime controller.
+ *
+ * There is a flag, which should indicate, whether a generator is
+ * enabled or not. But here we rely on the default value of -1.
+ * */
+ x = _GEN(voice, GEN_KEYNUM);
+ if (x >= 0){
+ voice->key = x;
+ }
+ break;
+
+ case GEN_VELOCITY:
+ /* GEN_VELOCITY: SF2.01 page 46, item 47
+ *
+ * If this generator is active, it forces the velocity to its
+ * value. Non-realtime controller.
+ *
+ * There is a flag, which should indicate, whether a generator is
+ * enabled or not. But here we rely on the default value of -1. */
+ x = _GEN(voice, GEN_VELOCITY);
+ if (x > 0) {
+ voice->vel = x;
+ }
+ break;
+
+ case GEN_MODENVTOPITCH:
+ x = _GEN(voice, GEN_MODENVTOPITCH);
+ fluid_clip(x, -12000.0, 12000.0);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x);
+ break;
+
+ case GEN_MODENVTOFILTERFC:
+ x = _GEN(voice,GEN_MODENVTOFILTERFC);
+
+ /* Range: SF2.01 section 8.1.3 # 1
+ * Motivation for range checking:
+ * Filter is reported to make funny noises now and then
+ */
+ fluid_clip(x, -12000.0, 12000.0);
+ UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x);
+ break;
+
+
+ /* sample start and ends points
+ *
+ * Range checking is initiated via the
+ * voice->check_sample_sanity flag,
+ * because it is impossible to check here:
+ * During the voice setup, all modulators are processed, while
+ * the voice is inactive. Therefore, illegal settings may
+ * occur during the setup (for example: First move the loop
+ * end point ahead of the loop start point => invalid, then
+ * move the loop start point forward => valid again.
+ */
+ case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */
+ case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */
+ if (voice->sample != NULL) {
+ z = (voice->sample->start
+ + (int) _GEN(voice, GEN_STARTADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS));
+ UPDATE_RVOICE_I1(fluid_rvoice_set_start, z);
+ }
+ break;
+ case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */
+ case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */
+ if (voice->sample != NULL) {
+ z = (voice->sample->end
+ + (int) _GEN(voice, GEN_ENDADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS));
+ UPDATE_RVOICE_I1(fluid_rvoice_set_end, z);
+ }
+ break;
+ case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */
+ case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */
+ if (voice->sample != NULL) {
+ z = (voice->sample->loopstart
+ + (int) _GEN(voice, GEN_STARTLOOPADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS));
+ UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z);
+ }
+ break;
+
+ case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */
+ case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */
+ if (voice->sample != NULL) {
+ z = (voice->sample->loopend
+ + (int) _GEN(voice, GEN_ENDLOOPADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS));
+ UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z);
+ }
+ break;
+
+ /* Conversion functions differ in range limit */
+#define NUM_BUFFERS_DELAY(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE)
+#define NUM_BUFFERS_ATTACK(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE)
+#define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE)
+
+ /* volume envelope
+ *
+ * - delay and hold times are converted to absolute number of samples
+ * - sustain is converted to its absolute value
+ * - attack, decay and release are converted to their increment per sample
+ */
+ case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */
+ x = _GEN(voice, GEN_VOLENVDELAY);
+ fluid_clip(x, -12000.0f, 5000.0f);
+ count = NUM_BUFFERS_DELAY(x);
+ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDELAY,
+ count, 0.0f, 0.0f, -1.0f, 1.0f);
+ break;
+
+ case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */
+ x = _GEN(voice, GEN_VOLENVATTACK);
+ fluid_clip(x, -12000.0f, 8000.0f);
+ count = 1 + NUM_BUFFERS_ATTACK(x);
+ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVATTACK,
+ count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f);
+ break;
+
+ case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */
+ case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */
+ count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */
+ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVHOLD,
+ count, 1.0f, 0.0f, -1.0f, 2.0f);
+ break;
+
+ case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */
+ case GEN_VOLENVSUSTAIN: /* SF2.01 section 8.1.3 # 37 */
+ case GEN_KEYTOVOLENVDECAY: /* SF2.01 section 8.1.3 # 40 */
+ y = 1.0f - 0.001f * _GEN(voice, GEN_VOLENVSUSTAIN);
+ fluid_clip(y, 0.0f, 1.0f);
+ count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */
+ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDECAY,
+ count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f);
+ break;
+
+ case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */
+ x = _GEN(voice, GEN_VOLENVRELEASE);
+ fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f);
+ count = 1 + NUM_BUFFERS_RELEASE(x);
+ fluid_voice_update_volenv(voice, FLUID_VOICE_ENVRELEASE,
+ count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 1.0f);
+ break;
+
+ /* Modulation envelope */
+ case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */
+ x = _GEN(voice, GEN_MODENVDELAY);
+ fluid_clip(x, -12000.0f, 5000.0f);
+ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDELAY,
+ NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 1.0f);
+ break;
+
+ case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */
+ x = _GEN(voice, GEN_MODENVATTACK);
+ fluid_clip(x, -12000.0f, 8000.0f);
+ count = 1 + NUM_BUFFERS_ATTACK(x);
+ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVATTACK,
+ count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f);
+ break;
+
+ case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */
+ case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */
+ count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */
+ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVHOLD,
+ count, 1.0f, 0.0f, -1.0f, 2.0f);
+ break;
+
+ case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */
+ case GEN_MODENVSUSTAIN: /* SF 2.01 section 8.1.3 # 29 */
+ case GEN_KEYTOMODENVDECAY: /* SF 2.01 section 8.1.3 # 32 */
+ count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */
+ y = 1.0f - 0.001f * _GEN(voice, GEN_MODENVSUSTAIN);
+ fluid_clip(y, 0.0f, 1.0f);
+ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDECAY,
+ count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f);
+ break;
+
+ case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */
+ x = _GEN(voice, GEN_MODENVRELEASE);
+ fluid_clip(x, -12000.0f, 8000.0f);
+ count = 1 + NUM_BUFFERS_RELEASE(x);
+ fluid_voice_update_modenv(voice, FLUID_VOICE_ENVRELEASE,
+ count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 2.0f);
+
+ break;
+
+ } /* switch 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 ctrl the control number
+ *
+ * In this implementation, I want to make sure that all controllers
+ * are event based: the parameter values of the DSP algorithm should
+ * only be updates when a controller event arrived and not at every
+ * iteration of the audio cycle (which would probably be feasible if
+ * the synth was made in silicon).
+ *
+ * The update is done in three steps:
+ *
+ * - first, we look for all the modulators that have the changed
+ * controller as a source. This will yield a list of generators that
+ * will be changed because of the controller event.
+ *
+ * - For every changed generator, calculate its new value. This is the
+ * sum of its original value plus the values of al the attached
+ * modulators.
+ *
+ * - For every changed generator, convert its value to the correct
+ * unit of the corresponding DSP parameter
+ */
+int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl)
+{
+ int i, k;
+ fluid_mod_t* mod;
+ int gen;
+ fluid_real_t modval;
+
+/* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */
+
+ for (i = 0; i < voice->mod_count; i++) {
+
+ mod = &voice->mod[i];
+
+ /* step 1: find all the modulators that have the changed controller
+ * as input source. */
+ if (fluid_mod_has_source(mod, cc, ctrl)) {
+
+ gen = fluid_mod_get_dest(mod);
+ modval = 0.0;
+
+ /* step 2: for every changed modulator, calculate the modulation
+ * value of its associated generator */
+ for (k = 0; k < voice->mod_count; k++) {
+ if (fluid_mod_has_dest(&voice->mod[k], gen)) {
+ modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice);
+ }
+ }
+
+ fluid_gen_set_mod(&voice->gen[gen], modval);
+
+ /* step 3: now that we have the new value of the generator,
+ * recalculate the parameter values that are derived from the
+ * generator */
+ fluid_voice_update_param(voice, gen);
+ }
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Update all the modulators. This function is called after a
+ * ALL_CTRL_OFF MIDI message has been received (CC 121).
+ *
+ */
+int fluid_voice_modulate_all(fluid_voice_t* voice)
+{
+ fluid_mod_t* mod;
+ int i, k, gen;
+ fluid_real_t modval;
+
+ /* Loop through all the modulators.
+
+ FIXME: we should loop through the set of generators instead of
+ the set of modulators. We risk to call 'fluid_voice_update_param'
+ several times for the same generator if several modulators have
+ that generator as destination. It's not an error, just a wast of
+ energy (think polution, global warming, unhappy musicians,
+ ...) */
+
+ for (i = 0; i < voice->mod_count; i++) {
+
+ mod = &voice->mod[i];
+ gen = fluid_mod_get_dest(mod);
+ modval = 0.0;
+
+ /* Accumulate the modulation values of all the modulators with
+ * destination generator 'gen' */
+ for (k = 0; k < voice->mod_count; k++) {
+ if (fluid_mod_has_dest(&voice->mod[k], gen)) {
+ modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice);
+ }
+ }
+
+ fluid_gen_set_mod(&voice->gen[gen], modval);
+
+ /* Update the parameter values that are depend on the generator
+ * 'gen' */
+ fluid_voice_update_param(voice, gen);
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ Force the voice into release stage. Useful anywhere a voice
+ needs to be damped even if pedals (sustain sostenuto) are depressed.
+ See fluid_synth_damp_voices_by_sustain_LOCAL(),
+ fluid_synth_damp_voices_by_sostenuto_LOCAL,
+ fluid_voice_noteoff().
+*/
+void
+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
+}
+
+/*
+ * fluid_voice_noteoff
+ */
+int
+fluid_voice_noteoff(fluid_voice_t* voice)
+{
+ fluid_channel_t* channel;
+
+ fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref);
+
+ channel = voice->channel;
+
+ /* Sustain a note under Sostenuto pedal */
+ if (fluid_channel_sostenuto(channel) &&
+ channel->sostenuto_orderid > voice->id)
+ { // Sostenuto depressed after note
+ voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO;
+ }
+ /* Or sustain a note under Sustain pedal */
+ else if (fluid_channel_sustained(channel)) {
+ voice->status = FLUID_VOICE_SUSTAINED;
+ }
+ /* Or force the voice to release stage */
+ else
+ fluid_voice_release(voice);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_voice_kill_excl
+ *
+ * Percussion sounds can be mutually exclusive: for example, a 'closed
+ * hihat' sound will terminate an 'open hihat' sound ringing at the
+ * same time. This behaviour is modeled using 'exclusive classes',
+ * turning on a voice with an exclusive class other than 0 will kill
+ * all other voices having that exclusive class within the same preset
+ * or channel. fluid_voice_kill_excl gets called, when 'voice' is to
+ * be killed for that reason.
+ */
+
+int
+fluid_voice_kill_excl(fluid_voice_t* voice){
+
+ unsigned int at_tick;
+
+ if (!_PLAYING(voice)) {
+ return FLUID_OK;
+ }
+
+ /* Turn off the exclusive class information for this voice,
+ so that it doesn't get killed twice
+ */
+ fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0);
+
+ /* Speed up the volume envelope */
+ /* The value was found through listening tests with hi-hat samples. */
+ fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200);
+ fluid_voice_update_param(voice, GEN_VOLENVRELEASE);
+
+ /* Speed up the modulation envelope */
+ fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200);
+ fluid_voice_update_param(voice, GEN_MODENVRELEASE);
+
+ at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
+ UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
+
+
+ return FLUID_OK;
+}
+
+/*
+ * Called by fluid_synth when the overflow rvoice can be reclaimed.
+ */
+void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice)
+{
+ voice->can_access_overflow_rvoice = 1;
+ fluid_sample_null_ptr(&voice->overflow_rvoice->dsp.sample);
+}
+
+
+/*
+ * fluid_voice_off
+ *
+ * Purpose:
+ * Turns off a voice, meaning that it is not processed
+ * anymore by the DSP loop.
+ */
+int
+fluid_voice_off(fluid_voice_t* voice)
+{
+ fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref);
+
+ voice->chan = NO_CHANNEL;
+ UPDATE_RVOICE0(fluid_rvoice_voiceoff);
+
+ if (voice->can_access_rvoice)
+ fluid_sample_null_ptr(&voice->rvoice->dsp.sample);
+
+ voice->status = FLUID_VOICE_OFF;
+ voice->has_noteoff = 1;
+
+ /* Decrement the reference count of the sample. */
+ fluid_sample_null_ptr(&voice->sample);
+
+ /* Decrement voice count */
+ voice->channel->synth->active_voice_count--;
+
+ return FLUID_OK;
+}
+
+/**
+ * Adds a modulator to the voice.
+ * @param voice Voice instance
+ * @param mod Modulator info (copied)
+ * @param mode Determines how to handle an existing identical modulator
+ * #FLUID_VOICE_ADD to add (offset) the modulator amounts,
+ * #FLUID_VOICE_OVERWRITE to replace the modulator,
+ * #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should
+ * exist so don't check.
+ */
+void
+fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode)
+{
+ int i;
+
+ /*
+ * Some soundfonts come with a huge number of non-standard
+ * controllers, because they have been designed for one particular
+ * sound card. Discard them, maybe print a warning.
+ */
+
+ if (((mod->flags1 & FLUID_MOD_CC) == 0)
+ && ((mod->src1 != 0) /* SF2.01 section 8.2.1: Constant value */
+ && (mod->src1 != 2) /* Note-on velocity */
+ && (mod->src1 != 3) /* Note-on key number */
+ && (mod->src1 != 10) /* Poly pressure */
+ && (mod->src1 != 13) /* Channel pressure */
+ && (mod->src1 != 14) /* Pitch wheel */
+ && (mod->src1 != 16))) { /* Pitch wheel sensitivity */
+ FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
+ return;
+ }
+
+ if (mode == FLUID_VOICE_ADD) {
+
+ /* if identical modulator exists, add them */
+ for (i = 0; i < voice->mod_count; i++) {
+ if (fluid_mod_test_identity(&voice->mod[i], mod)) {
+ // printf("Adding modulator...\n");
+ voice->mod[i].amount += mod->amount;
+ return;
+ }
+ }
+
+ } else if (mode == FLUID_VOICE_OVERWRITE) {
+
+ /* if identical modulator exists, replace it (only the amount has to be changed) */
+ for (i = 0; i < voice->mod_count; i++) {
+ if (fluid_mod_test_identity(&voice->mod[i], mod)) {
+ // printf("Replacing modulator...amount is %f\n",mod->amount);
+ voice->mod[i].amount = mod->amount;
+ return;
+ }
+ }
+ }
+
+ /* Add a new modulator (No existing modulator to add / overwrite).
+ Also, default modulators (FLUID_VOICE_DEFAULT) are added without
+ checking, if the same modulator already exists. */
+ if (voice->mod_count < FLUID_NUM_MOD) {
+ fluid_mod_clone(&voice->mod[voice->mod_count++], mod);
+ }
+}
+
+/**
+ * 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
+ * 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
+ * 'recycled' and never deallocated.
+ *
+ * Before modifying an existing voice, check
+ * - that its state is still 'playing'
+ * - that the ID is still the same
+ *
+ * Otherwise the voice has finished playing.
+ */
+unsigned int fluid_voice_get_id(fluid_voice_t* voice)
+{
+ return voice->id;
+}
+
+/**
+ * Check if a voice is still playing.
+ * @param voice Voice instance
+ * @return TRUE if playing, FALSE otherwise
+ */
+int fluid_voice_is_playing(fluid_voice_t* voice)
+{
+ return _PLAYING(voice);
+}
+
+/*
+ * fluid_voice_get_lower_boundary_for_attenuation
+ *
+ * Purpose:
+ *
+ * A lower boundary for the attenuation (as in 'the minimum
+ * attenuation of this voice, with volume pedals, modulators
+ * etc. resulting in minimum attenuation, cannot fall below x cB) is
+ * calculated. This has to be called during fluid_voice_init, after
+ * all modulators have been run on the voice once. Also,
+ * voice->attenuation has to be initialized.
+ */
+static fluid_real_t
+fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice)
+{
+ int i;
+ fluid_mod_t* mod;
+ fluid_real_t possible_att_reduction_cB=0;
+ fluid_real_t lower_bound;
+
+ for (i = 0; i < voice->mod_count; i++) {
+ mod = &voice->mod[i];
+
+ /* Modulator has attenuation as target and can change over time? */
+ if ((mod->dest == GEN_ATTENUATION)
+ && ((mod->flags1 & FLUID_MOD_CC) || (mod->flags2 & FLUID_MOD_CC))) {
+
+ fluid_real_t current_val = fluid_mod_get_value(mod, voice->channel, voice);
+ fluid_real_t v = fabs(mod->amount);
+
+ if ((mod->src1 == FLUID_MOD_PITCHWHEEL)
+ || (mod->flags1 & FLUID_MOD_BIPOLAR)
+ || (mod->flags2 & FLUID_MOD_BIPOLAR)
+ || (mod->amount < 0)) {
+ /* Can this modulator produce a negative contribution? */
+ v *= -1.0;
+ } else {
+ /* No negative value possible. But still, the minimum contribution is 0. */
+ v = 0;
+ }
+
+ /* For example:
+ * - current_val=100
+ * - min_val=-4000
+ * - possible_att_reduction_cB += 4100
+ */
+ if (current_val > v){
+ possible_att_reduction_cB += (current_val - v);
+ }
+ }
+ }
+
+ lower_bound = voice->attenuation-possible_att_reduction_cB;
+
+ /* SF2.01 specs do not allow negative attenuation */
+ if (lower_bound < 0) {
+ lower_bound = 0;
+ }
+ return lower_bound;
+}
+
+
+
+
+int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs)
+{
+ voice->gen[gen].nrpn = nrpn_value;
+ voice->gen[gen].flags = (abs)? GEN_ABS_NRPN : GEN_SET;
+ fluid_voice_update_param(voice, gen);
+ return FLUID_OK;
+}
+
+int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
+{
+ /* avoid division by zero*/
+ if (gain < 0.0000001){
+ gain = 0.0000001;
+ }
+
+ voice->synth_gain = gain;
+ voice->amp_left = fluid_pan(voice->pan, 1) * gain / 32768.0f;
+ voice->amp_right = fluid_pan(voice->pan, 0) * gain / 32768.0f;
+ voice->amp_reverb = voice->reverb_send * gain / 32768.0f;
+ voice->amp_chorus = voice->chorus_send * gain / 32768.0f;
+
+ UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
+ UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
+
+ return FLUID_OK;
+}
+
+/* - Scan the loop
+ * - determine the peak level
+ * - 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
+ *
+ * If the peak volume during the loop is known, then the voice can
+ * be released earlier during the release phase. Otherwise, the
+ * voice will operate (inaudibly), until the envelope is at the
+ * nominal turnoff point. So it's a good idea to call
+ * fluid_voice_optimize_sample() on each sample once.
+ */
+int
+fluid_voice_optimize_sample(fluid_sample_t* s)
+{
+ signed short peak_max = 0;
+ signed short peak_min = 0;
+ signed short peak;
+ fluid_real_t normalized_amplitude_during_loop;
+ double result;
+ int i;
+
+ /* ignore ROM and other(?) invalid samples */
+ if (!s->valid) return (FLUID_OK);
+
+ if (!s->amplitude_that_reaches_noise_floor_is_valid){ /* Only once */
+ /* Scan the loop */
+ for (i = (int)s->loopstart; i < (int) s->loopend; i ++){
+ signed short val = s->data[i];
+ if (val > peak_max) {
+ peak_max = val;
+ } else if (val < peak_min) {
+ peak_min = val;
+ }
+ }
+
+ /* Determine the peak level */
+ if (peak_max >- peak_min){
+ peak = peak_max;
+ } else {
+ peak =- peak_min;
+ };
+ if (peak == 0){
+ /* Avoid division by zero */
+ peak = 1;
+ };
+
+ /* Calculate what factor will make the loop inaudible
+ * For example: Take a peak of 3277 (10 % of 32768). The
+ * normalized amplitude is 0.1 (10 % of 32768). An amplitude
+ * factor of 0.0001 (as opposed to the default 0.00001) will
+ * drop this sample to the noise floor.
+ */
+
+ /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */
+ normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.;
+ result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop;
+
+ /* Store in sample */
+ s->amplitude_that_reaches_noise_floor = (double)result;
+ s->amplitude_that_reaches_noise_floor_is_valid = 1;
+#if 0
+ printf("Sample peak detection: factor %f\n", (double)result);
+#endif
+ };
+ return FLUID_OK;
+}
+
+fluid_real_t
+fluid_voice_get_overflow_prio(fluid_voice_t* voice,
+ fluid_overflow_prio_t* score,
+ unsigned int cur_time)
+{
+ fluid_real_t this_voice_prio = 0;
+
+ /* Are we already overflowing? */
+ if (!voice->can_access_overflow_rvoice) {
+ return OVERFLOW_PRIO_CANNOT_KILL;
+ }
+
+ /* Is this voice on the drum channel?
+ * Then it is very important.
+ * Also skip the released and sustained scores.
+ */
+ if (voice->channel->channel_type == CHANNEL_TYPE_DRUM){
+ this_voice_prio += score->percussion;
+ }
+ else if (voice->has_noteoff) {
+ /* Noteoff has */
+ this_voice_prio += score->released;
+ } else if (_SUSTAINED(voice) || _HELD_BY_SOSTENUTO(voice)) {
+ /* This voice is still active, since the sustain pedal is held down.
+ * Consider it less important than non-sustained channels.
+ * This decision is somehow subjective. But usually the sustain pedal
+ * is used to play 'more-voices-than-fingers', so it shouldn't hurt
+ * if we kill one voice.
+ */
+ this_voice_prio += score->sustained;
+ }
+
+ /* We are not enthusiastic about releasing voices, which have just been started.
+ * Otherwise hitting a chord may result in killing notes belonging to that very same
+ * chord. So give newer voices a higher score. */
+ if (score->age) {
+ cur_time -= voice->start_time;
+ if (cur_time < 1)
+ cur_time = 1; // Avoid div by zero
+ this_voice_prio += (score->age * voice->output_rate) / cur_time;
+ }
+
+ /* take a rough estimate of loudness into account. Louder voices are more important. */
+ if (score->volume) {
+ fluid_real_t a = voice->attenuation;
+ if (voice->has_noteoff) {
+ // FIXME: Should take into account where on the envelope we are...?
+ }
+ if (a < 0.1)
+ a = 0.1; // Avoid div by zero
+ this_voice_prio += score->volume / a;
+ }
+
+ return this_voice_prio;
+}
diff --git a/libs/fluidsynth/src/fluid_voice.h b/libs/fluidsynth/src/fluid_voice.h
new file mode 100644
index 0000000000..c43fe59738
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_voice.h
@@ -0,0 +1,228 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_VOICE_H
+#define _FLUID_VOICE_H
+
+#include "fluid_phase.h"
+#include "fluid_gen.h"
+#include "fluid_mod.h"
+#include "fluid_iir_filter.h"
+#include "fluid_adsr_env.h"
+#include "fluid_lfo.h"
+#include "fluid_rvoice.h"
+#include "fluid_sys.h"
+
+#define NO_CHANNEL 0xff
+
+typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t;
+
+struct _fluid_overflow_prio_t
+{
+ fluid_real_t percussion; /**< Is this voice on the drum channel? Then add this score */
+ fluid_real_t released; /**< Is this voice in release stage? Then add this score (usually negative) */
+ fluid_real_t sustained; /**< Is this voice sustained? Then add this score (usually negative) */
+ fluid_real_t volume; /**< Multiply current (or future) volume (a value between 0 and 1) */
+ fluid_real_t age; /**< This score will be divided by the number of seconds the voice has lasted */
+};
+
+enum fluid_voice_status
+{
+ FLUID_VOICE_CLEAN,
+ FLUID_VOICE_ON,
+ FLUID_VOICE_SUSTAINED, /* Sustained by Sustain pedal */
+ FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */
+ FLUID_VOICE_OFF
+};
+
+
+/*
+ * fluid_voice_t
+ */
+struct _fluid_voice_t
+{
+ unsigned int id; /* the id is incremented for every new noteon.
+ it's used for noteoff's */
+ unsigned char status;
+ unsigned char chan; /* the channel number, quick access for channel messages */
+ unsigned char key; /* the key, quick access for noteoff */
+ unsigned char vel; /* the velocity */
+ fluid_channel_t* channel;
+ fluid_gen_t gen[GEN_LAST];
+ fluid_mod_t mod[FLUID_NUM_MOD];
+ int mod_count;
+ fluid_sample_t* sample; /* Pointer to sample (dupe in rvoice) */
+
+ int has_noteoff; /* Flag set when noteoff has been sent */
+
+ /* basic parameters */
+ fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */
+
+ unsigned int start_time;
+ fluid_adsr_env_t volenv; /* Volume envelope (dupe in rvoice) */
+
+ /* basic parameters */
+ fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */
+ fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */
+ fluid_real_t root_pitch;
+
+ /* master gain (dupe in rvoice) */
+ fluid_real_t synth_gain;
+
+ /* pan */
+ fluid_real_t pan;
+ fluid_real_t amp_left;
+ fluid_real_t amp_right;
+
+ /* reverb */
+ fluid_real_t reverb_send;
+ fluid_real_t amp_reverb;
+
+ /* chorus */
+ fluid_real_t chorus_send;
+ fluid_real_t amp_chorus;
+
+ /* rvoice control */
+ fluid_rvoice_t* rvoice;
+ fluid_rvoice_t* overflow_rvoice; /* Used temporarily and only in overflow situations */
+ int can_access_rvoice; /* False if rvoice is being rendered in separate thread */
+ int can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */
+
+ /* for debugging */
+ int debug;
+ double ref;
+};
+
+
+fluid_voice_t* new_fluid_voice(fluid_real_t output_rate);
+int delete_fluid_voice(fluid_voice_t* voice);
+
+void fluid_voice_start(fluid_voice_t* voice);
+void fluid_voice_calculate_gen_pitch(fluid_voice_t* voice);
+
+int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf);
+
+int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
+ fluid_channel_t* channel, int key, int vel,
+ unsigned int id, unsigned int time, fluid_real_t gain);
+
+int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl);
+int fluid_voice_modulate_all(fluid_voice_t* voice);
+
+/** Set the NRPN value of a generator. */
+int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value, int abs);
+
+
+/** Set the gain. */
+int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain);
+
+int fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value);
+
+
+/** Update all the synthesis parameters, which depend on generator
+ 'gen'. This is only necessary after changing a generator of an
+ already operating voice. Most applications will not need this
+ function.*/
+void fluid_voice_update_param(fluid_voice_t* voice, int gen);
+
+/** fluid_voice_release
+ Force the voice into release stage. Usefuf anywhere a voice
+ needs to be damped even if pedals (sustain sostenuto) are depressed.
+ See fluid_synth_damp_voices_LOCAL(), fluid_synth_damp_voices_by_sostenuto_LOCAL,
+ fluid_voice_noteoff(), fluid_synth_stop_LOCAL().
+*/
+void fluid_voice_release(fluid_voice_t* voice);
+int fluid_voice_noteoff(fluid_voice_t* voice);
+int fluid_voice_off(fluid_voice_t* voice);
+void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice);
+void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
+ fluid_real_t* left_buf, fluid_real_t* right_buf,
+ fluid_real_t* reverb_buf, fluid_real_t* chorus_buf);
+
+int fluid_voice_kill_excl(fluid_voice_t* voice);
+fluid_real_t fluid_voice_get_overflow_prio(fluid_voice_t* voice,
+ fluid_overflow_prio_t* score,
+ unsigned int cur_time);
+
+#define OVERFLOW_PRIO_CANNOT_KILL 999999.
+
+/**
+ * Locks the rvoice for rendering, so it can't be modified directly
+ */
+static FLUID_INLINE fluid_rvoice_t*
+fluid_voice_lock_rvoice(fluid_voice_t* voice)
+{
+ voice->can_access_rvoice = 0;
+ return voice->rvoice;
+}
+
+/**
+ * Unlocks the rvoice for rendering, so it can be modified directly
+ */
+static FLUID_INLINE void
+fluid_voice_unlock_rvoice(fluid_voice_t* voice)
+{
+ voice->can_access_rvoice = 1;
+}
+
+
+#define fluid_voice_get_channel(voice) ((voice)->channel)
+
+
+#define fluid_voice_set_id(_voice, _id) { (_voice)->id = (_id); }
+#define fluid_voice_get_chan(_voice) (_voice)->chan
+
+
+#define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || \
+ _SUSTAINED(voice) || \
+ _HELD_BY_SOSTENUTO(voice) )
+
+/* A voice is 'ON', if it has not yet received a noteoff
+ * event. Sending a noteoff event will advance the envelopes to
+ * section 5 (release). */
+#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && !voice->has_noteoff)
+#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
+#define _HELD_BY_SOSTENUTO(voice) ((voice)->status == FLUID_VOICE_HELD_BY_SOSTENUTO)
+#define _AVAILABLE(voice) ((voice)->can_access_rvoice && \
+ (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)))
+//#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)
+#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val)
+
+
+/* FIXME - This doesn't seem to be used anywhere - JG */
+fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num);
+
+#define fluid_voice_get_loudness(voice) (fluid_adsr_env_get_max_val(&voice->volenv))
+
+#define _GEN(_voice, _n) \
+ ((fluid_real_t)(_voice)->gen[_n].val \
+ + (fluid_real_t)(_voice)->gen[_n].mod \
+ + (fluid_real_t)(_voice)->gen[_n].nrpn)
+
+/* defined in fluid_dsp_float.c */
+
+void fluid_dsp_float_config (void);
+int fluid_dsp_float_interpolate_none (fluid_voice_t *voice);
+int fluid_dsp_float_interpolate_linear (fluid_voice_t *voice);
+int fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice);
+int fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice);
+
+#endif /* _FLUID_VOICE_H */
diff --git a/libs/fluidsynth/src/fluidsynth_priv.h b/libs/fluidsynth/src/fluidsynth_priv.h
new file mode 100644
index 0000000000..74b9f4b9c6
--- /dev/null
+++ b/libs/fluidsynth/src/fluidsynth_priv.h
@@ -0,0 +1,267 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUIDSYNTH_PRIV_H
+#define _FLUIDSYNTH_PRIV_H
+
+#include <glib.h>
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_MATH_H
+#include <math.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#if HAVE_IO_H
+#include <io.h>
+#endif
+
+#if HAVE_WINDOWS_H
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#endif
+
+/* MinGW32 special defines */
+#ifdef MINGW32
+
+#include <stdint.h>
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+#define DSOUND_SUPPORT 1
+#define WINMIDI_SUPPORT 1
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+#endif
+
+/* Darwin special defines (taken from config_macosx.h) */
+#ifdef DARWIN
+#define MACINTOSH
+#define __Types__
+#define WITHOUT_SERVER 1
+#endif
+
+
+#include "fluidsynth.h"
+
+
+/***************************************************************
+ *
+ * BASIC TYPES
+ */
+
+#if defined(WITH_FLOAT)
+typedef float fluid_real_t;
+#else
+typedef double fluid_real_t;
+#endif
+
+
+#if defined(WIN32)
+typedef SOCKET fluid_socket_t;
+#else
+typedef int fluid_socket_t;
+#define INVALID_SOCKET -1
+#endif
+
+#if defined(SUPPORTS_VLA)
+# define FLUID_DECLARE_VLA(_type, _name, _len) \
+ _type _name[_len]
+#else
+# define FLUID_DECLARE_VLA(_type, _name, _len) \
+ _type* _name = g_newa(_type, (_len))
+#endif
+
+
+/** Integer types */
+//typedef gint8 sint8;
+typedef guint8 uint8;
+//typedef gint16 sint16;
+//typedef guint16 uint16;
+typedef gint32 sint32;
+typedef guint32 uint32;
+//typedef gint64 sint64;
+//typedef guint64 uint64;
+
+
+/***************************************************************
+ *
+ * FORWARD DECLARATIONS
+ */
+typedef struct _fluid_env_data_t fluid_env_data_t;
+typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t;
+typedef struct _fluid_channel_t fluid_channel_t;
+typedef struct _fluid_tuning_t fluid_tuning_t;
+typedef struct _fluid_hashtable_t fluid_hashtable_t;
+typedef struct _fluid_client_t fluid_client_t;
+typedef struct _fluid_server_socket_t fluid_server_socket_t;
+typedef struct _fluid_sample_timer_t fluid_sample_timer_t;
+
+/***************************************************************
+ *
+ * CONSTANTS
+ */
+
+#define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */
+#define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */
+#define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */
+#define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */
+#define FLUID_DEFAULT_AUDIO_RT_PRIO 60 /**< Default setting for audio.realtime-prio */
+#define FLUID_DEFAULT_MIDI_RT_PRIO 50 /**< Default setting for midi.realtime-prio */
+
+#ifndef PI
+#define PI 3.141592654
+#endif
+
+/***************************************************************
+ *
+ * SYSTEM INTERFACE
+ */
+typedef FILE* fluid_file;
+
+#define FLUID_MALLOC(_n) malloc(_n)
+#define FLUID_REALLOC(_p,_n) realloc(_p,_n)
+#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t))
+#define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t))
+#define FLUID_FREE(_p) free(_p)
+#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
+#define FLUID_FCLOSE(_f) fclose(_f)
+#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f)
+#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set)
+#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n)
+#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n)
+#define FLUID_STRLEN(_s) strlen(_s)
+#define FLUID_STRCMP(_s,_t) strcmp(_s,_t)
+#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n)
+#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src)
+#define FLUID_STRNCPY(_dst,_src,_n) strncpy(_dst,_src,_n)
+#define FLUID_STRCHR(_s,_c) strchr(_s,_c)
+#define FLUID_STRRCHR(_s,_c) strrchr(_s,_c)
+#ifdef strdup
+#define FLUID_STRDUP(s) strdup(s)
+#else
+#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s)
+#endif
+#define FLUID_SPRINTF sprintf
+#define FLUID_FPRINTF fprintf
+
+#define fluid_clip(_val, _min, _max) \
+{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); }
+
+#if WITH_FTS
+#define FLUID_PRINTF post
+#define FLUID_FLUSH()
+#else
+#define FLUID_PRINTF printf
+#define FLUID_FLUSH() fflush(stdout)
+#endif
+
+#define FLUID_LOG fluid_log
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795
+#endif
+
+
+#define FLUID_ASSERT(a,b)
+#define FLUID_ASSERT_P(a,b)
+
+char* fluid_error(void);
+
+
+/* Internationalization */
+#define _(s) s
+
+
+#endif /* _FLUIDSYNTH_PRIV_H */
diff --git a/libs/fluidsynth/wscript b/libs/fluidsynth/wscript
new file mode 100644
index 0000000000..6451ec829c
--- /dev/null
+++ b/libs/fluidsynth/wscript
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+from waflib import TaskGen
+import os
+import sys
+
+# Version of this package (even if built as a child)
+MAJOR = '1'
+MINOR = '6'
+MICRO = '0'
+LIBFLUIDSYNTH_VERSION = "%s.%s.%s" % (MAJOR, MINOR, MICRO)
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+LIBLTC_LIB_VERSION = '1.1.1'
+
+# Variables for 'waf dist'
+APPNAME = 'libltc'
+VERSION = LIBFLUIDSYNTH_VERSION
+I18N_PACKAGE = 'libfluidsynth'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ if conf.is_defined('USE_EXTERNAL_LIBS'):
+ autowaf.check_pkg(conf, 'fluidsynth', uselib_store='LIBFLUIDSYNTH', atleast_version=LIBFLUIDSYNTH_LIB_VERSION, mandatory=True)
+ else:
+ conf.load('compiler_c')
+ autowaf.configure(conf)
+
+def build(bld):
+ if bld.is_defined('USE_EXTERNAL_LIBS'):
+ return
+ bld (export_includes = ['fluidsynth'],
+ name = 'libfluidsynth_includes'
+ )
+ bld.stlib (source = [
+ 'src/fluid_midi.c',
+ 'src/fluid_adsr_env.c',
+ 'src/fluid_chorus.c',
+ 'src/fluid_iir_filter.c',
+ 'src/fluid_lfo.c',
+ 'src/fluid_rev.c',
+ 'src/fluid_rvoice.c',
+ 'src/fluid_rvoice_dsp.c',
+ 'src/fluid_rvoice_event.c',
+ 'src/fluid_rvoice_mixer.c',
+ 'src/fluid_defsfont.c',
+ 'src/fluid_chan.c',
+ 'src/fluid_event.c',
+ 'src/fluid_gen.c',
+ 'src/fluid_mod.c',
+ 'src/fluid_synth.c',
+ 'src/fluid_tuning.c',
+ 'src/fluid_voice.c',
+ 'src/fluid_conv.c',
+ 'src/fluid_hash.c',
+ 'src/fluid_list.c',
+ 'src/fluid_ringbuffer.c',
+ 'src/fluid_settings.c',
+ 'src/fluid_sys.c'
+ ],
+ cflags = [ '-fPIC', '-fvisibility=hidden' ],
+ includes = ['.', 'src/' ],
+ target = 'libfluidsynth',
+ use = 'libfluidsynth_includes',
+ uselib = 'GLIB',
+ defines = [ 'HAVE_CONFIG_H', 'DEFAULT_SOUNDFONT=""' ]
+ )
+
+def shutdown():
+ autowaf.shutdown()
diff --git a/tools/ardour_fluidsynth.diff b/tools/ardour_fluidsynth.diff
new file mode 100644
index 0000000000..8b9821cf33
--- /dev/null
+++ b/tools/ardour_fluidsynth.diff
@@ -0,0 +1,252 @@
+diff --git a/libs/fluidsynth/src/fluid_defsfont.c b/libs/fluidsynth/src/fluid_defsfont.c
+index 3eea95c..c395218 100644
+--- a/libs/fluidsynth/src/fluid_defsfont.c
++++ b/libs/fluidsynth/src/fluid_defsfont.c
+@@ -109,11 +109,13 @@ char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont)
+ return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data);
+ }
+
++#if 0
+ fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s)
+ {
+ /* This function is here just to avoid an ABI/SONAME bump, see ticket #98. Should never be used. */
+ return NULL;
+ }
++#endif
+
+ fluid_preset_t*
+ fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum)
+diff --git a/libs/fluidsynth/src/fluid_hash.c b/libs/fluidsynth/src/fluid_hash.c
+index a063e29..9d5a920 100644
+--- a/libs/fluidsynth/src/fluid_hash.c
++++ b/libs/fluidsynth/src/fluid_hash.c
+@@ -93,7 +93,7 @@ static const guint primes[] =
+
+ static const unsigned int nprimes = sizeof (primes) / sizeof (primes[0]);
+
+-unsigned int
++static unsigned int
+ spaced_primes_closest (unsigned int num)
+ {
+ unsigned int i;
+@@ -984,6 +984,7 @@ fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable,
+ return deleted;
+ }
+
++#if 0
+ /**
+ * fluid_hashtable_foreach_remove:
+ * @hashtable: a #fluid_hashtable_t.
+@@ -1001,7 +1002,7 @@ fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable,
+ *
+ * Return value: the number of key/value pairs removed.
+ **/
+-unsigned int
++static unsigned int
+ fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data)
+ {
+@@ -1010,6 +1011,7 @@ fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable,
+
+ return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, TRUE);
+ }
++#endif
+
+ /**
+ * fluid_hashtable_foreach_steal:
+diff --git a/libs/fluidsynth/src/fluid_midi.c b/libs/fluidsynth/src/fluid_midi.c
+index 5ceab01..1ee3dd2 100644
+--- a/libs/fluidsynth/src/fluid_midi.c
++++ b/libs/fluidsynth/src/fluid_midi.c
+@@ -1115,10 +1115,11 @@ fluid_track_get_duration(fluid_track_t *track)
+ return time;
+ }
+
++#if 0
+ /*
+ * fluid_track_count_events
+ */
+-int
++static int
+ fluid_track_count_events(fluid_track_t *track, int *on, int *off)
+ {
+ fluid_midi_event_t *evt = track->first;
+@@ -1132,6 +1133,7 @@ fluid_track_count_events(fluid_track_t *track, int *on, int *off)
+ }
+ return FLUID_OK;
+ }
++#endif
+
+ /*
+ * fluid_track_add_event
+@@ -1533,7 +1535,7 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
+ return FLUID_OK;
+ }
+
+-void
++static void
+ fluid_player_advancefile(fluid_player_t *player)
+ {
+ if (player->playlist == NULL) {
+@@ -1553,7 +1555,7 @@ fluid_player_advancefile(fluid_player_t *player)
+ }
+ }
+
+-void
++static void
+ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)
+ {
+ fluid_playlist_item* current_playitem;
+diff --git a/libs/fluidsynth/src/fluid_rev.c b/libs/fluidsynth/src/fluid_rev.c
+index 51b0e79..166007d 100644
+--- a/libs/fluidsynth/src/fluid_rev.c
++++ b/libs/fluidsynth/src/fluid_rev.c
+@@ -75,7 +75,7 @@ void fluid_allpass_init(fluid_allpass* allpass);
+ void fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val);
+ fluid_real_t fluid_allpass_getfeedback(fluid_allpass* allpass);
+
+-void
++static void
+ fluid_allpass_setbuffer(fluid_allpass* allpass, int size)
+ {
+ allpass->bufidx = 0;
+@@ -83,7 +83,7 @@ fluid_allpass_setbuffer(fluid_allpass* allpass, int size)
+ allpass->bufsize = size;
+ }
+
+-void
++static void
+ fluid_allpass_release(fluid_allpass* allpass)
+ {
+ FLUID_FREE(allpass->buffer);
+diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.c b/libs/fluidsynth/src/fluid_rvoice_mixer.c
+index 4672cb8..cc633f5 100644
+--- a/libs/fluidsynth/src/fluid_rvoice_mixer.c
++++ b/libs/fluidsynth/src/fluid_rvoice_mixer.c
+@@ -24,7 +24,7 @@
+ #include "fluid_rev.h"
+ #include "fluid_chorus.h"
+ #include "fluidsynth_priv.h"
+-#include "fluid_ladspa.h"
++//#include "fluid_ladspa.h"
+
+ #define SYNTH_REVERB_CHANNEL 0
+ #define SYNTH_CHORUS_CHANNEL 1
+diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.h b/libs/fluidsynth/src/fluid_rvoice_mixer.h
+index eeb49ec..d4e41ca 100644
+--- a/libs/fluidsynth/src/fluid_rvoice_mixer.h
++++ b/libs/fluidsynth/src/fluid_rvoice_mixer.h
+@@ -24,7 +24,7 @@
+
+ #include "fluidsynth_priv.h"
+ #include "fluid_rvoice.h"
+-#include "fluid_ladspa.h"
++//#include "fluid_ladspa.h"
+
+ typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t;
+
+diff --git a/libs/fluidsynth/src/fluid_settings.c b/libs/fluidsynth/src/fluid_settings.c
+index 78725fb..2061c90 100644
+--- a/libs/fluidsynth/src/fluid_settings.c
++++ b/libs/fluidsynth/src/fluid_settings.c
+@@ -22,9 +22,9 @@
+ #include "fluid_sys.h"
+ #include "fluid_hash.h"
+ #include "fluid_synth.h"
+-#include "fluid_cmd.h"
+-#include "fluid_adriver.h"
+-#include "fluid_mdriver.h"
++//#include "fluid_cmd.h"
++//#include "fluid_adriver.h"
++//#include "fluid_mdriver.h"
+ #include "fluid_settings.h"
+ #include "fluid_midi.h"
+
+@@ -294,11 +294,13 @@ fluid_settings_init(fluid_settings_t* settings)
+ fluid_return_if_fail (settings != NULL);
+
+ fluid_synth_settings(settings);
+- fluid_shell_settings(settings);
++ //fluid_shell_settings(settings);
+ fluid_player_settings(settings);
++#if 0
+ fluid_file_renderer_settings(settings);
+ fluid_audio_driver_settings(settings);
+ fluid_midi_driver_settings(settings);
++#endif
+ }
+
+ static int
+diff --git a/libs/fluidsynth/src/fluid_synth.c b/libs/fluidsynth/src/fluid_synth.c
+index 84ee289..a12260c 100644
+--- a/libs/fluidsynth/src/fluid_synth.c
++++ b/libs/fluidsynth/src/fluid_synth.c
+@@ -471,7 +471,7 @@ struct _fluid_sample_timer_t
+ /*
+ * fluid_sample_timer_process - called when synth->ticks is updated
+ */
+-void fluid_sample_timer_process(fluid_synth_t* synth)
++static void fluid_sample_timer_process(fluid_synth_t* synth)
+ {
+ fluid_sample_timer_t* st;
+ long msec;
+diff --git a/libs/fluidsynth/src/fluid_synth.h b/libs/fluidsynth/src/fluid_synth.h
+index 3af336d..019a8e0 100644
+--- a/libs/fluidsynth/src/fluid_synth.h
++++ b/libs/fluidsynth/src/fluid_synth.h
+@@ -37,8 +37,8 @@
+ #include "fluid_rev.h"
+ #include "fluid_voice.h"
+ #include "fluid_chorus.h"
+-#include "fluid_ladspa.h"
+-#include "fluid_midi_router.h"
++//#include "fluid_ladspa.h"
++//#include "fluid_midi_router.h"
+ #include "fluid_sys.h"
+ #include "fluid_rvoice_event.h"
+
+diff --git a/libs/fluidsynth/src/fluid_sys.c b/libs/fluidsynth/src/fluid_sys.c
+index ee7d8d9..600b04e 100644
+--- a/libs/fluidsynth/src/fluid_sys.c
++++ b/libs/fluidsynth/src/fluid_sys.c
+@@ -686,7 +686,7 @@ fluid_thread_join(fluid_thread_t* thread)
+ }
+
+
+-void
++static void
+ fluid_timer_run (void *data)
+ {
+ fluid_timer_t *timer;
+diff --git a/libs/fluidsynth/src/fluid_tuning.c b/libs/fluidsynth/src/fluid_tuning.c
+index cc440aa..8977ed6 100644
+--- a/libs/fluidsynth/src/fluid_tuning.c
++++ b/libs/fluidsynth/src/fluid_tuning.c
+@@ -143,7 +143,7 @@ char* fluid_tuning_get_name(fluid_tuning_t* tuning)
+ return tuning->name;
+ }
+
+-void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch)
++static void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch)
+ {
+ tuning->pitch[key] = pitch;
+ }
+diff --git a/libs/fluidsynth/src/fluidsynth_priv.h b/libs/fluidsynth/src/fluidsynth_priv.h
+index faf2772..74b9f4b 100644
+--- a/libs/fluidsynth/src/fluidsynth_priv.h
++++ b/libs/fluidsynth/src/fluidsynth_priv.h
+@@ -28,14 +28,6 @@
+ #include "config.h"
+ #endif
+
+-#if defined(__POWERPC__) && !(defined(__APPLE__) && defined(__MACH__))
+-#include "config_maxmsp43.h"
+-#endif
+-
+-#if defined(WIN32) && !defined(MINGW32)
+-#include "config_win32.h"
+-#endif
+-
+ #if HAVE_STRING_H
+ #include <string.h>
+ #endif
diff --git a/tools/update_fluidsynth.sh b/tools/update_fluidsynth.sh
new file mode 100755
index 0000000000..66ad7835fa
--- /dev/null
+++ b/tools/update_fluidsynth.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+if ! test -f wscript || ! test -d gtk2_ardour || ! test -d libs/fluidsynth/;then
+ echo "This script needs to run from ardour's top-level src tree"
+ exit 1
+fi
+
+if test -z "`which rsync`" -o -z "`which git`"; then
+ echo "this script needs rsync and git"
+ exit 1
+fi
+
+ASRC=`pwd`
+set -e
+
+TMP=`mktemp -d`
+test -d "$TMP"
+echo $TMP
+
+trap "rm -rf $TMP" EXIT
+
+cd $TMP
+git clone git://git.code.sf.net/p/fluidsynth/code-git fs-git
+
+FSR=fs-git/fluidsynth/
+
+rsync -auc --info=progress2 \
+ ${FSR}src/midi/fluid_midi.c \
+ ${FSR}src/midi/fluid_midi.h \
+ ${FSR}src/rvoice/fluid_adsr_env.c \
+ ${FSR}src/rvoice/fluid_adsr_env.h \
+ ${FSR}src/rvoice/fluid_chorus.c \
+ ${FSR}src/rvoice/fluid_chorus.h \
+ ${FSR}src/rvoice/fluid_iir_filter.c \
+ ${FSR}src/rvoice/fluid_iir_filter.h \
+ ${FSR}src/rvoice/fluid_lfo.c \
+ ${FSR}src/rvoice/fluid_lfo.h \
+ ${FSR}src/rvoice/fluid_phase.h \
+ ${FSR}src/rvoice/fluid_rev.c \
+ ${FSR}src/rvoice/fluid_rev.h \
+ ${FSR}src/rvoice/fluid_rvoice.c \
+ ${FSR}src/rvoice/fluid_rvoice_dsp.c \
+ ${FSR}src/rvoice/fluid_rvoice_event.c \
+ ${FSR}src/rvoice/fluid_rvoice_event.h \
+ ${FSR}src/rvoice/fluid_rvoice.h \
+ ${FSR}src/rvoice/fluid_rvoice_mixer.c \
+ ${FSR}src/rvoice/fluid_rvoice_mixer.h \
+ ${FSR}src/sfloader/fluid_defsfont.c \
+ ${FSR}src/sfloader/fluid_defsfont.h \
+ ${FSR}src/sfloader/fluid_sfont.h \
+ ${FSR}src/synth/fluid_chan.c \
+ ${FSR}src/synth/fluid_chan.h \
+ ${FSR}src/synth/fluid_event.c \
+ ${FSR}src/synth/fluid_event_priv.h \
+ ${FSR}src/synth/fluid_event_queue.h \
+ ${FSR}src/synth/fluid_gen.c \
+ ${FSR}src/synth/fluid_gen.h \
+ ${FSR}src/synth/fluid_mod.c \
+ ${FSR}src/synth/fluid_mod.h \
+ ${FSR}src/synth/fluid_synth.c \
+ ${FSR}src/synth/fluid_synth.h \
+ ${FSR}src/synth/fluid_tuning.c \
+ ${FSR}src/synth/fluid_tuning.h \
+ ${FSR}src/synth/fluid_voice.c \
+ ${FSR}src/synth/fluid_voice.h \
+ ${FSR}src/utils/fluid_conv.c \
+ ${FSR}src/utils/fluid_conv.h \
+ ${FSR}src/utils/fluid_hash.c \
+ ${FSR}src/utils/fluid_hash.h \
+ ${FSR}src/utils/fluid_list.c \
+ ${FSR}src/utils/fluid_list.h \
+ ${FSR}src/utils/fluid_ringbuffer.c \
+ ${FSR}src/utils/fluid_ringbuffer.h \
+ ${FSR}src/utils/fluid_settings.c \
+ ${FSR}src/utils/fluid_settings.h \
+ ${FSR}src/utils/fluidsynth_priv.h \
+ ${FSR}src/utils/fluid_sys.c \
+ ${FSR}src/utils/fluid_sys.h \
+ \
+ "$ASRC/libs/fluidsynth/src/"
+
+rsync -auc --info=progress2 \
+ --exclude fluidsynth.h \
+ ${FSR}include/fluidsynth/event.h \
+ ${FSR}include/fluidsynth/gen.h \
+ ${FSR}include/fluidsynth/log.h \
+ ${FSR}include/fluidsynth/midi.h \
+ ${FSR}include/fluidsynth/misc.h \
+ ${FSR}include/fluidsynth/mod.h \
+ ${FSR}include/fluidsynth/settings.h \
+ ${FSR}include/fluidsynth/sfont.h \
+ ${FSR}include/fluidsynth/synth.h \
+ ${FSR}include/fluidsynth/types.h \
+ ${FSR}include/fluidsynth/voice.h \
+ \
+ "$ASRC/libs/fluidsynth/fluidsynth/"
+
+cd "$ASRC"
+patch -p1 < tools/ardour_fluidsynth.diff
diff --git a/wscript b/wscript
index fce1359b04..a4ee4ab590 100644
--- a/wscript
+++ b/wscript
@@ -204,6 +204,7 @@ children = [
'libs/qm-dsp',
'libs/vamp-plugins',
'libs/libltc',
+ 'libs/fluidsynth',
'libs/lua',
'libs/ptformat',
# core ardour libraries