Import libfluidsynth into the Ardour codebase
authorRobin Gareus <robin@gareus.org>
Tue, 23 Aug 2016 16:50:54 +0000 (18:50 +0200)
committerRobin Gareus <robin@gareus.org>
Tue, 23 Aug 2016 20:21:03 +0000 (22:21 +0200)
69 files changed:
libs/fluidsynth/README [new file with mode: 0644]
libs/fluidsynth/config.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/event.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/fluidsynth.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/gen.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/log.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/midi.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/misc.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/mod.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/settings.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/sfont.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/synth.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/types.h [new file with mode: 0644]
libs/fluidsynth/fluidsynth/voice.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_adsr_env.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_adsr_env.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_chan.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_chan.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_chorus.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_chorus.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_conv.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_conv.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_defsfont.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_defsfont.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_event.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_event_priv.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_event_queue.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_gen.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_gen.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_hash.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_hash.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_iir_filter.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_iir_filter.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_lfo.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_lfo.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_list.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_list.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_midi.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_midi.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_mod.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_mod.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_phase.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_rev.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_rev.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_ringbuffer.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_ringbuffer.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_rvoice.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_rvoice.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_rvoice_dsp.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_rvoice_event.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_rvoice_event.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_rvoice_mixer.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_rvoice_mixer.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_settings.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_settings.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_sfont.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_synth.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_synth.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_sys.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_sys.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_tuning.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_tuning.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_voice.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_voice.h [new file with mode: 0644]
libs/fluidsynth/src/fluidsynth_priv.h [new file with mode: 0644]
libs/fluidsynth/wscript [new file with mode: 0644]
tools/ardour_fluidsynth.diff [new file with mode: 0644]
tools/update_fluidsynth.sh [new file with mode: 0755]
wscript

diff --git a/libs/fluidsynth/README b/libs/fluidsynth/README
new file mode 100644 (file)
index 0000000..6032b9c
--- /dev/null
@@ -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 (file)
index 0000000..3df4483
--- /dev/null
@@ -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 (file)
index 0000000..b154304
--- /dev/null
@@ -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 (file)
index 0000000..8c599b5
--- /dev/null
@@ -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 (file)
index 0000000..e4bbc8e
--- /dev/null
@@ -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 (file)
index 0000000..85db03e
--- /dev/null
@@ -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 (file)
index 0000000..ab1e6a1
--- /dev/null
@@ -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 (file)
index 0000000..4f97d84
--- /dev/null
@@ -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 (file)
index 0000000..e343095
--- /dev/null
@@ -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 (file)
index 0000000..3a0502a
--- /dev/null
@@ -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 (file)
index 0000000..30aebfd
--- /dev/null
@@ -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 (file)
index 0000000..f62e60c
--- /dev/null
@@ -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 (file)
index 0000000..e956d81
--- /dev/null
@@ -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 (file)
index 0000000..fe7ad8c
--- /dev/null
@@ -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 (file)
index 0000000..1d31fdb
--- /dev/null
@@ -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 (file)
index 0000000..31303a9
--- /dev/null
@@ -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 (file)
index 0000000..c6eb723
--- /dev/null
@@ -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 (file)
index 0000000..85aa1ef
--- /dev/null
@@ -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 (file)
index 0000000..4bead5c
--- /dev/null
@@ -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 (file)
index 0000000..3422fa9
--- /dev/null
@@ -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 (file)
index 0000000..1a790cf
--- /dev/null
@@ -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 (file)
index 0000000..29793c3
--- /dev/null
@@ -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 (file)
index 0000000..c395218
--- /dev/null
@@ -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 (file)
index 0000000..29f3fd9
--- /dev/null
@@ -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 (file)
index 0000000..b3b0608
--- /dev/null
@@ -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 (file)
index 0000000..c0a4c1a
--- /dev/null
@@ -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 (file)
index 0000000..a5c24da
--- /dev/null
@@ -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 (file)
index 0000000..0f1413e
--- /dev/null
@@ -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 (file)
index 0000000..f54d049
--- /dev/null
@@ -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 (file)
index 0000000..9d5a920
--- /dev/null
@@ -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 (file)
index 0000000..3beff06
--- /dev/null
@@ -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 (file)
index 0000000..5b47469
--- /dev/null
@@ -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 (file)
index 0000000..7dc5de1
--- /dev/null
@@ -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 (file)
index 0000000..ff178e0
--- /dev/null
@@ -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 (file)
index 0000000..e9440cf
--- /dev/null
@@ -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 (file)
index 0000000..dd47a39
--- /dev/null
@@ -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 (file)
index 0000000..bdc3291
--- /dev/null
@@ -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 (file)
index 0000000..1ee3dd2
--- /dev/null
@@ -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 (file)
index 0000000..90fcef7
--- /dev/null
@@ -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 (file)
index 0000000..5931aa5
--- /dev/null
@@ -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 (file)
index 0000000..81c9f76
--- /dev/null
@@ -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 (file)
index 0000000..15f2fa7
--- /dev/null
@@ -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 (file)
index 0000000..166007d
--- /dev/null
@@ -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 (file)
index 0000000..f977352
--- /dev/null
@@ -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 (file)
index 0000000..f6c06dd
--- /dev/null
@@ -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 (file)
index 0000000..bd43f8a
--- /dev/null
@@ -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 (file)
index 0000000..ba8da98
--- /dev/null
@@ -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 (file)
index 0000000..4566cb1
--- /dev/null
@@ -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 (file)
index 0000000..df7da50
--- /dev/null
@@ -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 (file)
index 0000000..65edb9d
--- /dev/null
@@ -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 (file)
index 0000000..e8693bc
--- /dev/null
@@ -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 (file)
index 0000000..cc633f5
--- /dev/null
@@ -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 (file)
index 0000000..d4e41ca
--- /dev/null
@@ -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 (file)
index 0000000..2061c90
--- /dev/null
@@ -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 (file)
index 0000000..0eb1f97
--- /dev/null
@@ -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 (file)
index 0000000..51e941e
--- /dev/null
@@ -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 (file)
index 0000000..a12260c
--- /dev/null
@@ -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 (file)
index 0000000..019a8e0
--- /dev/null
@@ -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 (file)
index 0000000..600b04e
--- /dev/null
@@ -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 (file)
index 0000000..4953515
--- /dev/null
@@ -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 (file)
index 0000000..8977ed6
--- /dev/null
@@ -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 (file)
index 0000000..d974139
--- /dev/null
@@ -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 (file)
index 0000000..e6efbac
--- /dev/null
@@ -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 (file)
index 0000000..c43fe59
--- /dev/null
@@ -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 (file)
index 0000000..74b9f4b
--- /dev/null
@@ -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 (file)
index 0000000..6451ec8
--- /dev/null
@@ -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 (file)
index 0000000..8b9821c
--- /dev/null
@@ -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 (executable)
index 0000000..66ad783
--- /dev/null
@@ -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 fce1359b043dbe70e515eea17734c55298b9e2b3..a4ee4ab59062f8c47eef108ef3588db0493341f7 100644 (file)
--- 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