Update Fluidsynth to 2.0.1
authorRobin Gareus <robin@gareus.org>
Wed, 17 Oct 2018 22:41:02 +0000 (00:41 +0200)
committerRobin Gareus <robin@gareus.org>
Wed, 17 Oct 2018 23:43:43 +0000 (01:43 +0200)
72 files changed:
libs/fluidsynth/README
libs/fluidsynth/fluidsynth/event.h
libs/fluidsynth/fluidsynth/fluidsynth.h
libs/fluidsynth/fluidsynth/gen.h
libs/fluidsynth/fluidsynth/log.h
libs/fluidsynth/fluidsynth/midi.h
libs/fluidsynth/fluidsynth/misc.h
libs/fluidsynth/fluidsynth/mod.h
libs/fluidsynth/fluidsynth/settings.h
libs/fluidsynth/fluidsynth/sfont.h
libs/fluidsynth/fluidsynth/synth.h
libs/fluidsynth/fluidsynth/types.h
libs/fluidsynth/fluidsynth/voice.h
libs/fluidsynth/src/fluid_adsr_env.c
libs/fluidsynth/src/fluid_adsr_env.h
libs/fluidsynth/src/fluid_chan.c
libs/fluidsynth/src/fluid_chan.h
libs/fluidsynth/src/fluid_chorus.c
libs/fluidsynth/src/fluid_chorus.h
libs/fluidsynth/src/fluid_conv.c
libs/fluidsynth/src/fluid_conv.h
libs/fluidsynth/src/fluid_defsfont.c
libs/fluidsynth/src/fluid_defsfont.h
libs/fluidsynth/src/fluid_event.c
libs/fluidsynth/src/fluid_event.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_gen.c
libs/fluidsynth/src/fluid_gen.h
libs/fluidsynth/src/fluid_hash.c
libs/fluidsynth/src/fluid_hash.h
libs/fluidsynth/src/fluid_iir_filter.c
libs/fluidsynth/src/fluid_iir_filter.h
libs/fluidsynth/src/fluid_lfo.c
libs/fluidsynth/src/fluid_lfo.h
libs/fluidsynth/src/fluid_list.c
libs/fluidsynth/src/fluid_list.h
libs/fluidsynth/src/fluid_midi.c
libs/fluidsynth/src/fluid_midi.h
libs/fluidsynth/src/fluid_mod.c
libs/fluidsynth/src/fluid_mod.h
libs/fluidsynth/src/fluid_phase.h
libs/fluidsynth/src/fluid_rev.c
libs/fluidsynth/src/fluid_rev.h
libs/fluidsynth/src/fluid_ringbuffer.c
libs/fluidsynth/src/fluid_ringbuffer.h
libs/fluidsynth/src/fluid_rvoice.c
libs/fluidsynth/src/fluid_rvoice.h
libs/fluidsynth/src/fluid_rvoice_dsp.c
libs/fluidsynth/src/fluid_rvoice_event.c
libs/fluidsynth/src/fluid_rvoice_event.h
libs/fluidsynth/src/fluid_rvoice_mixer.c
libs/fluidsynth/src/fluid_rvoice_mixer.h
libs/fluidsynth/src/fluid_samplecache.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_samplecache.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_settings.c
libs/fluidsynth/src/fluid_settings.h
libs/fluidsynth/src/fluid_sffile.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_sffile.h [new file with mode: 0644]
libs/fluidsynth/src/fluid_sfont.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_sfont.h
libs/fluidsynth/src/fluid_synth.c
libs/fluidsynth/src/fluid_synth.h
libs/fluidsynth/src/fluid_synth_monopoly.c [new file with mode: 0644]
libs/fluidsynth/src/fluid_sys.c
libs/fluidsynth/src/fluid_sys.h
libs/fluidsynth/src/fluid_tuning.c
libs/fluidsynth/src/fluid_tuning.h
libs/fluidsynth/src/fluid_voice.c
libs/fluidsynth/src/fluid_voice.h
libs/fluidsynth/src/fluidsynth_priv.h
libs/fluidsynth/wscript
tools/ardour_fluidsynth.diff
tools/update_fluidsynth.sh

index 6032b9c2995a2740494e696385fcebe7f78de1cd..de74bfb54825ad21ef74612142c8151fe92cfb72 100644 (file)
@@ -1,8 +1,7 @@
 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
+from git://github.com/FluidSynth/fluidsynth.git
+rev.  v2.0.1-5-gebc177f  Oct/2018
 
 fluidsynth is licensed in terms of the  LGPL-2+, see individual source
 files for (C) holders.
index b154304515750aa091af3bae851198d41d943205..cbd1fa6a09c1250f3222cb0922f7ca4d5307abcd 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -35,99 +35,102 @@ extern "C" {
 /**
  * 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 */
+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 */
+    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 (useful for giving a callback at a certain time) */
+    FLUID_SEQ_ANYCONTROLCHANGE,        /**< Any control change message (only internally used for remove_events) */
+    FLUID_SEQ_CHANNELPRESSURE,    /**< Channel aftertouch event @since 1.1.0 */
+    FLUID_SEQ_KEYPRESSURE,        /**< Polyphonic aftertouch event @since 2.0.0 */
+    FLUID_SEQ_SYSTEMRESET,        /**< System reset event @since 1.1.0 */
+    FLUID_SEQ_UNREGISTERING,      /**< Called when a sequencer client is being unregistered. @since 1.1.0 */
+#ifndef __DOXYGEN__
+    FLUID_SEQ_LASTEVENT                /**< @internal Defines the count of events enums @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
+#endif
 };
 
-#define FLUID_SEQ_PITCHWHHELSENS        FLUID_SEQ_PITCHWHEELSENS        /**< Old deprecated misspelling of #FLUID_SEQ_PITCHWHEELSENS */
-
 /* Event alloc/free */
-FLUIDSYNTH_API fluid_event_tnew_fluid_event(void);
-FLUIDSYNTH_API void delete_fluid_event(fluid_event_tevt);
+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);
+FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src);
+FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest);
 
 /* Timer events */
-FLUIDSYNTH_API void fluid_event_timer(fluid_event_t* evt, void* data);
+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_note(fluid_event_t *evt, int channel,
+                                     short key, short vel,
+                                     unsigned int duration);
 
-FLUIDSYNTH_API void fluid_event_noteon(fluid_event_tevt, int channel, short key, short vel);
-FLUIDSYNTH_API void fluid_event_noteoff(fluid_event_tevt, int channel, short key);
-FLUIDSYNTH_API void fluid_event_all_sounds_off(fluid_event_tevt, int channel);
-FLUIDSYNTH_API void fluid_event_all_notes_off(fluid_event_tevt, int channel);
+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_tevt, int channel, short bank_num);
-FLUIDSYNTH_API void fluid_event_program_change(fluid_event_tevt, int channel, short preset_num);
-FLUIDSYNTH_API void fluid_event_program_select(fluid_event_tevt, int channel, unsigned int sfont_id, short bank_num, short preset_num);
+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_tevt, int channel, short control, short val);
+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_tevt, int channel, int val);
-FLUIDSYNTH_API void fluid_event_pitch_wheelsens(fluid_event_tevt, int channel, short val);
-FLUIDSYNTH_API void fluid_event_modulation(fluid_event_tevt, int channel, short val);
-FLUIDSYNTH_API void fluid_event_sustain(fluid_event_tevt, int channel, short val);
-FLUIDSYNTH_API void fluid_event_pan(fluid_event_tevt, int channel, short val);
-FLUIDSYNTH_API void fluid_event_volume(fluid_event_tevt, int channel, short val);
-FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_tevt, int channel, short val);
-FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_tevt, int channel, short val);
+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);
+FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, short val);
+FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t *evt, int channel, short val);
+FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t *evt);
 
 
 /* Only for removing events */
-FLUIDSYNTH_API void fluid_event_any_control_change(fluid_event_tevt, int channel);
+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_tevt);
+FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t *evt);
 
 /* Accessing event data */
-FLUIDSYNTH_API int fluid_event_get_type(fluid_event_tevt);
-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_tevt);
-FLUIDSYNTH_API short fluid_event_get_key(fluid_event_tevt);
-FLUIDSYNTH_API short fluid_event_get_velocity(fluid_event_tevt);
-FLUIDSYNTH_API short fluid_event_get_control(fluid_event_tevt);
-FLUIDSYNTH_API short fluid_event_get_value(fluid_event_tevt);
-FLUIDSYNTH_API short fluid_event_get_program(fluid_event_tevt);
-FLUIDSYNTH_API void* fluid_event_get_data(fluid_event_t* evt);
-FLUIDSYNTH_API unsigned int fluid_event_get_duration(fluid_event_tevt);
-FLUIDSYNTH_API short fluid_event_get_bank(fluid_event_tevt);
-FLUIDSYNTH_API int fluid_event_get_pitch(fluid_event_tevt);
-FLUIDSYNTH_API unsigned int fluid_event_get_sfont_id(fluid_event_tevt);
+FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t *evt);
+FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt);
+FLUIDSYNTH_API fluid_seq_id_t 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
 }
index 8c599b5be72389e725fcda50cdbb6e0373c8155e..8852ea2d84ad2b58974a1c1acf9f4587702e9be5 100644 (file)
@@ -15,7 +15,7 @@ extern "C" {
 
 
 FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro);
-FLUIDSYNTH_API char* fluid_version_str(void);
+FLUIDSYNTH_API const char* fluid_version_str(void);
 
 
 #include "types.h"
index e4bbc8ef691ebfb48db3eb91628ca174253c6193..4b625831b0a067f23dcd91f0159fe1ee1322b3f5 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -33,99 +33,83 @@ extern "C" {
 /**
  * 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
+enum fluid_gen_type
 {
-  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;
+    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 */
 
-/**
- * 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 */
-};
+    /* 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 */
 
-FLUIDSYNTH_API int fluid_gen_set_default_values(fluid_gen_t* gen);
+    GEN_CUSTOM_BALANCE,          /**< Balance @note Not a real SoundFont generator */
+    /* non-standard generator for an additional custom high- or low-pass filter */
+    GEN_CUSTOM_FILTERFC,               /**< Custom filter cutoff frequency */
+    GEN_CUSTOM_FILTERQ,                /**< Custom filter Q */
 
+#ifndef __DOXYGEN__
+    GEN_LAST                   /**< @internal Value defines the count of generators (#fluid_gen_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
+#endif
+};
 
 
 #ifdef __cplusplus
index 85db03e1cbfbae7e48c97de47b23d54c8a145310..00d802f50e3c7271ef31b31a85f46195e9dd6545 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -52,13 +52,16 @@ extern "C" {
 /**
  * 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
+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 */
+#ifndef __DOXYGEN__
+    LAST_LOG_LEVEL /**< @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
+#endif
 };
 
 /**
@@ -67,12 +70,12 @@ enum 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);
+typedef void (*fluid_log_function_t)(int level, const char *message, void *data);
 
-FLUIDSYNTH_API 
-fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, voiddata);
+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 void fluid_default_log_function(int level, const char *message, void *data);
 
 FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...);
 
index ab1e6a198bfa087aeb077fa756d063085176f388..cd30e2088b715876f5df4e3b510578aaa54bf640 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -30,27 +30,31 @@ extern "C" {
  * @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);
+FLUIDSYNTH_API fluid_midi_event_t *new_fluid_midi_event(void);
+FLUIDSYNTH_API void 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);
+FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt,
+        void *data, int size, int dynamic);
+FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt,
+        void *data, int size, int dynamic);
 
 /**
  * MIDI router rule type.
@@ -58,13 +62,15 @@ FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t* evt, void *dat
  */
 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_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 */
+#ifndef __DOXYGEN__
+    FLUID_MIDI_ROUTER_RULE_COUNT                  /**< @internal Total count of rule types @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time!*/
+#endif
 } fluid_midi_router_rule_type;
 
 /**
@@ -79,35 +85,35 @@ typedef enum
  * 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_tdriver);
+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 void 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);
 
 
 /**
@@ -116,23 +122,31 @@ FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t* driver);
  */
 enum fluid_player_status
 {
-  FLUID_PLAYER_READY,           /**< Player is ready */
-  FLUID_PLAYER_PLAYING,         /**< Player is currently playing */
-  FLUID_PLAYER_DONE             /**< Player is finished playing */
+    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);
+FLUIDSYNTH_API fluid_player_t *new_fluid_player(fluid_synth_t *synth);
+FLUIDSYNTH_API void 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_set_playback_callback(fluid_player_t *player, handle_midi_event_func_t handler, void *handler_data);
+
+FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t *player);
+FLUIDSYNTH_API int fluid_player_get_current_tick(fluid_player_t *player);
+FLUIDSYNTH_API int fluid_player_get_total_ticks(fluid_player_t *player);
+FLUIDSYNTH_API int fluid_player_get_bpm(fluid_player_t *player);
+FLUIDSYNTH_API int fluid_player_get_midi_tempo(fluid_player_t *player);
+FLUIDSYNTH_API int fluid_player_seek(fluid_player_t *player, int ticks);
+
+///
 
 #ifdef __cplusplus
 }
index 4f97d8437d5afcc80a0b902ed9b0bfa0a1383e94..7a2b457b5aa0e97335cf14be1a4a63b109269ab0 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -36,7 +36,7 @@ extern "C" {
  * 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
+ * @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:
  *
@@ -55,19 +55,13 @@ extern "C" {
  * Value that indicates failure, used by most libfluidsynth functions.
  * @since 1.1.0
  *
- * NOTE: See #FLUID_OK for more details.
+ * @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
+FLUIDSYNTH_API int fluid_is_soundfont(const char *filename);
+FLUIDSYNTH_API int fluid_is_midifile(const char *filename);
 
 
 #ifdef __cplusplus
index e34309546b88c2dd35043c148788cee9fd99dafd..5ea5f89d4aa586846254fb7332d031e23a12f4e4 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -30,26 +30,6 @@ extern "C" {
  * @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.
@@ -60,16 +40,18 @@ struct _fluid_mod_t
  */
 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) */
+    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) */
+
+    FLUID_MOD_SIN = 0x80,            /**< Custom non-standard sinus mapping function */
 };
 
 /**
@@ -78,32 +60,36 @@ enum fluid_mod_flags
  */
 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 */
+    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 fluid_mod_t *new_fluid_mod(void);
+FLUIDSYNTH_API void delete_fluid_mod(fluid_mod_t *mod);
+FLUIDSYNTH_API size_t fluid_mod_sizeof(void);
 
-FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags); 
-FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags); 
-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 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_get_source1(const fluid_mod_t *mod);
+FLUIDSYNTH_API int fluid_mod_get_flags1(const fluid_mod_t *mod);
+FLUIDSYNTH_API int fluid_mod_get_source2(const fluid_mod_t *mod);
+FLUIDSYNTH_API int fluid_mod_get_flags2(const fluid_mod_t *mod);
+FLUIDSYNTH_API int fluid_mod_get_dest(const fluid_mod_t *mod);
+FLUIDSYNTH_API double fluid_mod_get_amount(const fluid_mod_t *mod);
 
-FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2);
+FLUIDSYNTH_API int fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2);
+FLUIDSYNTH_API int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl);
+FLUIDSYNTH_API int fluid_mod_has_dest(const fluid_mod_t *mod, int gen);
 
+FLUIDSYNTH_API void fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src);
 
 #ifdef __cplusplus
 }
index 3a0502a920eea70024230738cf66f208736beeac..aa7f083129294049db4d29e4689bfa511def6844 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -78,35 +78,6 @@ extern "C" {
  */
 #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 */
 
 
@@ -117,70 +88,68 @@ extern "C" {
  * 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 */
+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 fluid_settings_t *new_fluid_settings(void);
+FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t *settings);
 
 FLUIDSYNTH_API
-int fluid_settings_get_hints(fluid_settings_t* settings, const char *name);
+int fluid_settings_get_type(fluid_settings_t *settings, const char *name);
 
 FLUIDSYNTH_API
-int fluid_settings_is_realtime(fluid_settings_t* settings, const char *name);
+int fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *val);
 
 FLUIDSYNTH_API
-int fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str);
+int fluid_settings_is_realtime(fluid_settings_t *settings, const char *name);
 
 FLUIDSYNTH_API
-int fluid_settings_copystr(fluid_settings_t* settings, const char *name, char *str, int len);
+int fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str);
 
 FLUIDSYNTH_API
-int fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str);
+int fluid_settings_copystr(fluid_settings_t *settings, const char *name, char *str, int len);
 
 FLUIDSYNTH_API
-int fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str);
+int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str);
 
 FLUIDSYNTH_API
-char* fluid_settings_getstr_default(fluid_settings_t* settings, const char *name);
+int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def);
 
 FLUIDSYNTH_API
-int fluid_settings_str_equal(fluid_settings_tsettings, const char *name, const char *value);
+int fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *value);
 
 FLUIDSYNTH_API
-int fluid_settings_setnum(fluid_settings_tsettings, const char *name, double val);
+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);
+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);
+int fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val);
 
 FLUIDSYNTH_API
-void fluid_settings_getnum_range(fluid_settings_t* settings, const char *name,
-                               double* min, double* max);
+int fluid_settings_getnum_range(fluid_settings_t *settings, const char *name,
+                                double *min, double *max);
 
 FLUIDSYNTH_API
-int fluid_settings_setint(fluid_settings_tsettings, const char *name, int val);
+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);
+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);
+int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val);
 
 FLUIDSYNTH_API
-void fluid_settings_getint_range(fluid_settings_t* settings, const char *name,
-                               int* min, int* max);
+int fluid_settings_getint_range(fluid_settings_t *settings, const char *name,
+                                int *min, int *max);
 
 /**
  * Callback function type used with fluid_settings_foreach_option()
@@ -188,17 +157,17 @@ void fluid_settings_getint_range(fluid_settings_t* settings, const char *name,
  * @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);
+typedef void (*fluid_settings_foreach_option_t)(void *data, const char *name, const char *option);
 
 FLUIDSYNTH_API
-void fluid_settings_foreach_option(fluid_settings_tsettings,
-                                 const char* name, void* data,
-                                 fluid_settings_foreach_option_t func);
+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);
+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()
@@ -206,11 +175,11 @@ FLUIDSYNTH_API char *fluid_settings_option_concat (fluid_settings_t* settings,
  * @param name Setting name
  * @param type Setting type (#fluid_types_enum)
  */
-typedef void (*fluid_settings_foreach_t)(void *data, char *name, int type);
+typedef void (*fluid_settings_foreach_t)(void *data, const char *name, int type);
 
 FLUIDSYNTH_API
-void fluid_settings_foreach(fluid_settings_t* settings, void* data,
-                          fluid_settings_foreach_t func);
+void fluid_settings_foreach(fluid_settings_t *settings, void *data,
+                            fluid_settings_foreach_t func);
 
 #ifdef __cplusplus
 }
index 30aebfd1261c16d6e3c3131371093f47b4f18954..55413a9e2408065b47c9a644a1225d71d56d81b3 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -31,30 +31,30 @@ extern "C" {
  * @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
+ * synthesizer. 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.
+ * #fluid_sfloader_t instance created by new_fluid_sfloader().
+ * On creation, you must specify a callback function \p load
+ * that will be called for every file attempting to load it and
+ * if successful returns a #fluid_sfont_t instance, 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.
+ * function should return a #fluid_preset_t instance.
  *
- * The #fluid_preset_t structure contains some functions to obtain
+ * The #fluid_preset_t instance contains some functions to obtain
  * information from the preset (name, bank, number). The most
  * important callback is the noteon function. The noteon function
+ * is called by fluidsynth internally and
  * 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_sample_t instance 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
@@ -64,215 +64,250 @@ extern "C" {
 /**
  * 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 */
+enum
+{
+    FLUID_PRESET_SELECTED,                /**< Preset selected notify */
+    FLUID_PRESET_UNSELECTED,              /**< Preset unselected notify */
+    FLUID_SAMPLE_DONE                     /**< Sample no longer needed notify */
 };
 
-
 /**
- * SoundFont loader structure.
+ * Indicates the type of a sample used by the _fluid_sample_t::sampletype field.
  */
-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);
+enum fluid_sample_type
+{
+    FLUID_SAMPLETYPE_MONO = 0x1, /**< Used for mono samples */
+    FLUID_SAMPLETYPE_RIGHT = 0x2, /**< Used for right samples of a stereo pair */
+    FLUID_SAMPLETYPE_LEFT = 0x4, /**< Used for left samples of a stereo pair */
+    FLUID_SAMPLETYPE_LINKED = 0x8, /**< Currently not used */
+    FLUID_SAMPLETYPE_OGG_VORBIS = 0x10, /**< Used for Ogg Vorbis compressed samples @since 1.1.7 */
+    FLUID_SAMPLETYPE_ROM = 0x8000 /**< Indicates ROM samples, causes sample to be ignored */
 };
 
+
 /**
- * Virtual SoundFont instance structure.
+ * Method to load an instrument file (does not actually need to be a real file name,
+ * could be another type of string identifier that the \a loader understands).
+ * @param loader SoundFont loader
+ * @param filename File name or other string identifier
+ * @return The loaded instrument file (SoundFont) or NULL if an error occured.
  */
-struct _fluid_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);
-};
+typedef fluid_sfont_t *(*fluid_sfloader_load_t)(fluid_sfloader_t *loader, const char *filename);
+
+/**
+ * The free method should free the memory allocated for a fluid_sfloader_t instance in
+ * addition to any private data. Any custom user provided cleanup function must ultimately call
+ * delete_fluid_sfloader() to ensure proper cleanup of the #fluid_sfloader_t struct. If no private data
+ * needs to be freed, setting this to delete_fluid_sfloader() is sufficient.
+ * @param loader SoundFont loader
+ */
+typedef void (*fluid_sfloader_free_t)(fluid_sfloader_t *loader);
+
+
+FLUIDSYNTH_API fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free);
+FLUIDSYNTH_API void delete_fluid_sfloader(fluid_sfloader_t *loader);
 
-#define fluid_sfont_get_id(_sf) ((_sf)->id)
+FLUIDSYNTH_API fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings);
 
 /**
- * Virtual SoundFont preset.
+ * Opens the file or memory indicated by \c filename in binary read mode.
+ * \c filename matches the string provided during the fluid_synth_sfload() call.
+ *
+ * @return returns a file handle on success, NULL otherwise
  */
-struct _fluid_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);
-};
+typedef void *(* fluid_sfloader_callback_open_t)(const char *filename);
 
 /**
- * Virtual SoundFont sample.
+ * Reads \c count bytes to the specified buffer \c buf.
+ *
+ * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else returns #FLUID_FAILED and leaves \a buf unmodified.
  */
-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 */
-};
+typedef int (* fluid_sfloader_callback_read_t)(void *buf, int count, void *handle);
+
+/**
+ * Same purpose and behaviour as fseek.
+ *
+ * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END
+ *
+ * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise
+ */
+typedef int (* fluid_sfloader_callback_seek_t)(void *handle, long offset, int origin);
+
+/**
+ * Closes the handle returned by #fluid_sfloader_callback_open_t and frees used ressources.
+ *
+ * @return returns #FLUID_OK on success, #FLUID_FAILED on error
+ */
+typedef int (* fluid_sfloader_callback_close_t)(void *handle);
+
+/** @return returns current file offset or #FLUID_FAILED on error */
+typedef long (* fluid_sfloader_callback_tell_t)(void *handle);
+
+
+FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader,
+        fluid_sfloader_callback_open_t open,
+        fluid_sfloader_callback_read_t read,
+        fluid_sfloader_callback_seek_t seek,
+        fluid_sfloader_callback_tell_t tell,
+        fluid_sfloader_callback_close_t close);
+
+FLUIDSYNTH_API int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data);
+FLUIDSYNTH_API void *fluid_sfloader_get_data(fluid_sfloader_t *loader);
+
+
+
+/**
+ * Method to return the name of a virtual SoundFont.
+ * @param sfont Virtual SoundFont
+ * @return The name of the virtual SoundFont.
+ */
+typedef const char *(*fluid_sfont_get_name_t)(fluid_sfont_t *sfont);
+
+/**
+ * Get a virtual SoundFont preset by bank and program numbers.
+ * @param sfont Virtual SoundFont
+ * @param bank MIDI bank number (0-16383)
+ * @param prenum MIDI preset number (0-127)
+ * @return Should return an allocated virtual preset or NULL if it could not
+ *   be found.
+ */
+typedef fluid_preset_t *(*fluid_sfont_get_preset_t)(fluid_sfont_t *sfont, int bank, int prenum);
+
+/**
+ * Start virtual SoundFont preset iteration method.
+ * @param sfont Virtual SoundFont
+ *
+ * Starts/re-starts virtual preset iteration in a SoundFont.
+ */
+typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont);
+
+/**
+ * Virtual SoundFont preset iteration function.
+ * @param sfont Virtual SoundFont
+ * @param preset Caller supplied uninitialized buffer to fill in with current preset information
+ * @return NULL when no more presets are available, otherwise the a pointer to the current preset
+ *
+ * Should store preset information to the caller supplied \a preset structure
+ * and advance the internal iteration state to the next preset for subsequent
+ * calls.
+ */
+typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont);
+
+/**
+ * Method to free a virtual SoundFont bank. Any custom user provided cleanup function must ultimately call
+ * delete_fluid_sfont() to ensure proper cleanup of the #fluid_sfont_t struct. If no private data
+ * needs to be freed, setting this to delete_fluid_sfont() is sufficient.
+ * @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.
+ */
+typedef int (*fluid_sfont_free_t)(fluid_sfont_t *sfont);
 
 
-#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) */
+FLUIDSYNTH_API fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name,
+        fluid_sfont_get_preset_t get_preset,
+        fluid_sfont_iteration_start_t iter_start,
+        fluid_sfont_iteration_next_t iter_next,
+        fluid_sfont_free_t free);
 
+FLUIDSYNTH_API int delete_fluid_sfont(fluid_sfont_t *sfont);
 
-#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 */
+FLUIDSYNTH_API int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data);
+FLUIDSYNTH_API void *fluid_sfont_get_data(fluid_sfont_t *sfont);
 
+FLUIDSYNTH_API int fluid_sfont_get_id(fluid_sfont_t *sfont);
+FLUIDSYNTH_API const char *fluid_sfont_get_name(fluid_sfont_t *sfont);
+FLUIDSYNTH_API fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum);
+FLUIDSYNTH_API void fluid_sfont_iteration_start(fluid_sfont_t *sfont);
+FLUIDSYNTH_API fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont);
 
+/**
+ * Method to get a virtual SoundFont preset name.
+ * @param preset Virtual SoundFont preset
+ * @return Should return the name of the preset.  The returned string must be
+ *   valid for the duration of the virtual preset (or the duration of the
+ *   SoundFont, in the case of preset iteration).
+ */
+typedef const char *(*fluid_preset_get_name_t)(fluid_preset_t *preset);
+
+/**
+ * Method to get a virtual SoundFont preset MIDI bank number.
+ * @param preset Virtual SoundFont preset
+ * @param return The bank number of the preset
+ */
+typedef int (*fluid_preset_get_banknum_t)(fluid_preset_t *preset);
+
+/**
+ * Method to get a virtual SoundFont preset MIDI program number.
+ * @param preset Virtual SoundFont preset
+ * @param return The program number of the preset
+ */
+typedef int (*fluid_preset_get_num_t)(fluid_preset_t *preset);
+
+/**
+ * Method to handle a noteon event (synthesize the instrument).
+ * @param preset Virtual SoundFont preset
+ * @param synth Synthesizer instance
+ * @param chan MIDI channel number of the note on event
+ * @param key MIDI note number (0-127)
+ * @param vel MIDI velocity (0-127)
+ * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise
+ *
+ * This method may be called from within synthesis context and therefore
+ * should be as efficient as possible and not perform any operations considered
+ * bad for realtime audio output (memory allocations and other OS calls).
+ *
+ * Call fluid_synth_alloc_voice() for every sample that has
+ * to be played. fluid_synth_alloc_voice() expects a pointer to a
+ * #fluid_sample_t structure and returns a pointer to the opaque
+ * #fluid_voice_t structure. To set or increment the values of a
+ * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are
+ * finished initializing the voice call fluid_voice_start() to
+ * start playing the synthesis voice.  Starting with FluidSynth 1.1.0 all voices
+ * created will be started at the same time.
+ */
+typedef int (*fluid_preset_noteon_t)(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel);
+
+/**
+ * Method to free a virtual SoundFont preset. Any custom user provided cleanup function must ultimately call
+ * delete_fluid_preset() to ensure proper cleanup of the #fluid_preset_t struct. If no private data
+ * needs to be freed, setting this to delete_fluid_preset() is sufficient.
+ * @param preset Virtual SoundFont preset
+ * @return Should return 0
+ */
+typedef void (*fluid_preset_free_t)(fluid_preset_t *preset);
+
+FLUIDSYNTH_API fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont,
+        fluid_preset_get_name_t get_name,
+        fluid_preset_get_banknum_t get_bank,
+        fluid_preset_get_num_t get_num,
+        fluid_preset_noteon_t noteon,
+        fluid_preset_free_t free);
+FLUIDSYNTH_API void delete_fluid_preset(fluid_preset_t *preset);
+
+FLUIDSYNTH_API int fluid_preset_set_data(fluid_preset_t *preset, void *data);
+FLUIDSYNTH_API void *fluid_preset_get_data(fluid_preset_t *preset);
+
+FLUIDSYNTH_API const char *fluid_preset_get_name(fluid_preset_t *preset);
+FLUIDSYNTH_API int fluid_preset_get_banknum(fluid_preset_t *preset);
+FLUIDSYNTH_API int fluid_preset_get_num(fluid_preset_t *preset);
+FLUIDSYNTH_API fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset);
+
+FLUIDSYNTH_API fluid_sample_t *new_fluid_sample(void);
+FLUIDSYNTH_API void delete_fluid_sample(fluid_sample_t *sample);
+FLUIDSYNTH_API size_t fluid_sample_sizeof(void);
+
+FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t *sample, const char *name);
+FLUIDSYNTH_API int fluid_sample_set_sound_data(fluid_sample_t *sample,
+        short *data,
+        char *data24,
+        unsigned int nbframes,
+        unsigned int sample_rate,
+        short copy_data);
+
+FLUIDSYNTH_API int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end);
+FLUIDSYNTH_API int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune);
 
 #ifdef __cplusplus
 }
index f62e60cd1bd5a88f69f67a0a4e199ef35dc7969f..a4afb909477c1615e46e67ca81f819c75f220278 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -30,126 +30,112 @@ extern "C" {
 /**
  * @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. 
+ * 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);
 
+FLUIDSYNTH_API fluid_synth_t *new_fluid_synth(fluid_settings_t *settings);
+FLUIDSYNTH_API void delete_fluid_synth(fluid_synth_t *synth);
+FLUIDSYNTH_API fluid_settings_t *fluid_synth_get_settings(fluid_synth_t *synth);
 
 /* MIDI channel messages */
 
-FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_tsynth, int chan, int key, int vel);
-FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_tsynth, int chan, int key);
-FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_tsynth, 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_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_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_key_pressure(fluid_synth_t *synth, int chan, int key, int val);
+FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank);
+FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t *synth, int chan, 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);
+int fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id,
+                               int bank_num, 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);
+fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan,
+        const char *sfont_name, int bank_num,
+        int preset_num);
+FLUIDSYNTH_API
+int fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id,
+                            int *bank_num, int *preset_num);
+FLUIDSYNTH_API int fluid_synth_unset_program(fluid_synth_t *synth, int chan);
+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);
+
+/**
+ * The midi channel type used by fluid_synth_set_channel_type()
+ */
 enum fluid_midi_channel_type
 {
-  CHANNEL_TYPE_MELODIC = 0,
-  CHANNEL_TYPE_DRUM = 1
+    CHANNEL_TYPE_MELODIC = 0, /**< Melodic midi channel */
+    CHANNEL_TYPE_DRUM = 1 /**< Drum midi channel */
 };
 
-int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type);
+FLUIDSYNTH_API int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type);
 
 
 /* Low level access */
-FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan);
-FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t* synth, unsigned int id, 
-                                    fluid_preset_t* preset, int audio_chan, 
-                                    int midi_chan, int key, int vel);
-FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_tsynth, unsigned int id);
+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_tsynth);
-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_tsynth, int sfont_id, int offset);
-FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_tsynth, int sfont_id);
+FLUIDSYNTH_API
+int fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets);
+FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t *synth, int id);
+FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets);
+FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont);
+FLUIDSYNTH_API int 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, 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 */
+FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize,
+        double damping, double width, double level);
+FLUIDSYNTH_API int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize);
+FLUIDSYNTH_API int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping);
+FLUIDSYNTH_API int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width);
+FLUIDSYNTH_API int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level);
+
+FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on);
+FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t *synth);
+FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t *synth);
+FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t *synth);
+FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t *synth);
 
 
 /* Chorus */
@@ -157,107 +143,112 @@ FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t* synth);
 /**
  * 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 */
+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 */
+FLUIDSYNTH_API int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level,
+        double speed, double depth_ms, int type);
+FLUIDSYNTH_API int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr);
+FLUIDSYNTH_API int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level);
+FLUIDSYNTH_API int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed);
+FLUIDSYNTH_API int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms);
+FLUIDSYNTH_API int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type);
 
-#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 */
+FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on);
+FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t *synth);
+FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t *synth);
+FLUIDSYNTH_API double fluid_synth_get_chorus_speed(fluid_synth_t *synth);
+FLUIDSYNTH_API double fluid_synth_get_chorus_depth(fluid_synth_t *synth);
+FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t *synth); /* see fluid_chorus_mod */
 
 
 /* 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);
+FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t *synth);
+FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t *synth);
+FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t *synth);
+FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t *synth);
+FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth);
 
 
 /* Synthesis parameters */
 
-FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_tsynth, float sample_rate);
-FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_tsynth, float gain);
-FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_tsynth);
-FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_tsynth, int polyphony);
-FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_tsynth);
-FLUIDSYNTH_API int fluid_synth_get_active_voice_count(fluid_synth_tsynth);
-FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_tsynth);
+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_tsynth, int chan, int interp_method);
+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. */
+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 */
 
+    FLUID_INTERP_DEFAULT = FLUID_INTERP_4THORDER, /**< Default interpolation method */
+    FLUID_INTERP_HIGHEST = FLUID_INTERP_7THORDER, /**< Highest interpolation method */
+};
 
 /* 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);
+FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t *synth, int chan,
+                                       int param, float value);
+FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param);
 
 
 /* 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_tsynth, 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);
+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_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog,
+                                       const char *name, const double *pitch, int apply);
 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);
+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_activate_tuning(fluid_synth_tsynth, int chan, int bank, int prog,
+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_tsynth, int chan, int apply);
-FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_tsynth);
-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);
+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);
+FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth);
+FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth);
+
+
+/* Default modulators */
+
+/**
+ * Enum used with fluid_synth_add_default_mod() to specify how to handle duplicate modulators.
+ */
+enum fluid_synth_add_mod
+{
+    FLUID_SYNTH_OVERWRITE,        /**< Overwrite any existing matching modulator */
+    FLUID_SYNTH_ADD,              /**< Add (sum) modulator amounts */
+};
+
+FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode);
+FLUIDSYNTH_API int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod);
 
 
 /*
@@ -265,48 +256,141 @@ FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth);
  *
  * To create a synthesizer plugin, create the synthesizer as
  * explained above. Once the synthesizer is created you can call
- * any of the functions below to get the audio. 
+ * 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);
+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 nfx, float *fx[],
+                                       int nout, float *out[]);
+
+
+/* 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);
+
+/**
+ * Specifies the type of filter to use for the custom IIR filter
+ */
+enum fluid_iir_filter_type
+{
+    FLUID_IIR_DISABLED = 0, /**< Custom IIR filter is not operating */
+    FLUID_IIR_LOWPASS, /**< Custom IIR filter is operating as low-pass filter */
+    FLUID_IIR_HIGHPASS, /**< Custom IIR filter is operating as high-pass filter */
+    FLUID_IIR_LAST /**< @internal Value defines the count of filter types (#fluid_iir_filter_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
+};
 
 /**
- * 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'
+ * Specifies optional settings to use for the custom IIR filter
  */
-typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len, 
-                                    void* out1, int loff, int lincr, 
-                                    void* out2, int roff, int rincr);
+enum fluid_iir_filter_flags
+{
+    FLUID_IIR_Q_LINEAR = 1 << 0, /**< The Soundfont spec requires the filter Q to be interpreted in dB. If this flag is set the filter Q is instead assumed to be in a linear range */
+    FLUID_IIR_Q_ZERO_OFF = 1 << 1, /**< If this flag the filter is switched off if Q == 0 (prior to any transformation) */
+    FLUID_IIR_NO_GAIN_AMP = 1 << 2 /**< The Soundfont spec requires to correct the gain of the filter depending on the filter's Q. If this flag is set the filter gain will not be corrected. */
+};
 
-/* Synthesizer's interface to handle SoundFont loaders */
+FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int flags);
+
+
+/* LADSPA */
+
+#ifdef LADSPA
+FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth);
+#endif
+
+
+/* API: Poly mono mode */
+
+/** Interface to poly/mono mode variables
+ *
+ * Channel mode bits OR-ed together so that it matches with the midi spec: poly omnion (0), mono omnion (1), poly omnioff (2), mono omnioff (3)
+ */
+enum fluid_channel_mode_flags
+{
+    FLUID_CHANNEL_POLY_OFF = 0x01, /**< if flag is set, the basic channel is in mono on state, if not set poly is on */
+    FLUID_CHANNEL_OMNI_OFF = 0x02, /**< if flag is set, the basic channel is in omni off state, if not set omni is on */
+};
+
+/** Indicates the breath mode a channel is set to */
+enum fluid_channel_breath_flags
+{
+    FLUID_CHANNEL_BREATH_POLY = 0x10,  /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */
+    FLUID_CHANNEL_BREATH_MONO = 0x20,  /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */
+    FLUID_CHANNEL_BREATH_SYNC = 0x40,  /**< when channel is mono, this flag indicates that the breath controler(MSB)triggers noteon/noteoff on the running note */
+};
+
+/** Indicates the mode a basic channel is set to */
+enum fluid_basic_channel_modes
+{
+    FLUID_CHANNEL_MODE_MASK = (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< Mask Poly and Omni bits of #fluid_channel_mode_flags, usually only used internally */
+    FLUID_CHANNEL_MODE_OMNION_POLY = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 0 */
+    FLUID_CHANNEL_MODE_OMNION_MONO = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 1 */
+    FLUID_CHANNEL_MODE_OMNIOFF_POLY = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 2 */
+    FLUID_CHANNEL_MODE_OMNIOFF_MONO = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 3 */
+    FLUID_CHANNEL_MODE_LAST /**< @internal Value defines the count of basic channel modes (#fluid_basic_channel_modes) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
+};
+
+FLUIDSYNTH_API int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan);
+
+FLUIDSYNTH_API int  fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan,
+        int *basic_chan_out,
+        int *mode_chan_out,
+        int *basic_val_out);
+FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val);
+
+/** Interface to mono legato mode
+ *
+ * Indicates the legato mode a channel is set to
+ * n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played legato with previous note. */
+enum fluid_channel_legato_mode
+{
+    FLUID_CHANNEL_LEGATO_MODE_RETRIGGER, /**< Mode 0 - Release previous note, start a new note */
+    FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER, /**< Mode 1 - On contiguous notes retrigger in attack section using current value, shape attack using current dynamic and make use of previous voices if any */
+    FLUID_CHANNEL_LEGATO_MODE_LAST /**< @internal Value defines the count of legato modes (#fluid_channel_legato_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
+};
+
+FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode);
+FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int  *legatomode);
+
+/** Interface to portamento mode
+ *
+ * Indicates the portamento mode a channel is set to
+ */
+enum fluid_channel_portamento_mode
+{
+    FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE, /**< Mode 0 - Portamento on each note (staccato or legato) */
+    FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY, /**< Mode 1 - Portamento only on legato note */
+    FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY, /**< Mode 2 - Portamento only on staccato note */
+    FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes (#fluid_channel_portamento_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
+};
+
+FLUIDSYNTH_API int fluid_synth_set_portamento_mode(fluid_synth_t *synth,
+        int chan, int portamentomode);
+FLUIDSYNTH_API int fluid_synth_get_portamento_mode(fluid_synth_t *synth,
+        int chan, int   *portamentomode);
+
+/* Interface to breath mode   */
+FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t *synth,
+        int chan, int breathmode);
+FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t *synth,
+        int chan, int  *breathmode);
 
-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
 }
index e956d818db04106897e101d52e28c489984d2e9b..5ad29281adb05d72f454663461315ce657bfdba0 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -35,7 +35,6 @@ extern "C" {
 
 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 */
@@ -49,17 +48,23 @@ 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_hashtable_t fluid_cmd_hash_t;             /**< Command handler hash table */
 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 struct _fluid_cmd_handler_t fluid_cmd_handler_t;        /**< Shell Command Handler */
+#ifdef LADSPA
+typedef struct _fluid_ladspa_fx_t fluid_ladspa_fx_t;            /**< LADSPA effects instance */
+#endif
+typedef struct _fluid_file_callbacks_t fluid_file_callbacks_t;  /**< Callback struct to perform custom file loading of soundfonts */
 
 typedef int fluid_istream_t;    /**< Input stream descriptor */
 typedef int fluid_ostream_t;    /**< Output stream descriptor */
 
+typedef short fluid_seq_id_t; /**< Unique client IDs used by the sequencer and #fluid_event_t, obtained by fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() */
 
 #ifdef __cplusplus
 }
index fe7ad8c1ab181512e9df3a2d3e2bc10ced7d0a38..f0644718b215858d2aad49f52eee1141f45a6ac3 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *  
- * You should have received a copy of the GNU Library General Public
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -36,26 +36,34 @@ extern "C" {
  */
 
 
-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 */
+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 void fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode);
+FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t *voice, int gen);
+FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t *voice, int gen, float val);
+FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t *voice, int gen, float val);
+
+FLUIDSYNTH_API unsigned int fluid_voice_get_id(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_get_channel(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_get_key(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_get_actual_key(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_get_velocity(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_get_actual_velocity(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_is_playing(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_is_on(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_is_sustained(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_is_sostenuto(const fluid_voice_t *voice);
+FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t *s);
+FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t *voice, int gen);
 
-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
 }
index 1d31fdb5e6c76d6ab612abe073d8faa834eda097..00bdd40f220d1b13ae2cf4a19a067ec8dc4c95a4 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "fluid_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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data)
 {
-  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;
+    fluid_adsr_env_t *env = obj;
+    fluid_adsr_env_section_t section = param[0].i;
+    unsigned int count = param[1].i;
+    fluid_real_t coeff = param[2].real;
+    fluid_real_t increment = param[3].real;
+    fluid_real_t min = param[4].real;
+    fluid_real_t max = param[5].real;
+
+    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;
 }
 
index 31303a9ce92c58663baf865838aae4ea8a0c9620..9ed652d0b11ff15aaaee212d0e81081740c39dc6 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 /*
  * 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;
+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
+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 */
+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_tenv, int is_volenv)
+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;
-  }
+    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;
+    }
+    else
+    {
+        env->count++;
+    }
+
+    env->val = x;
 
-  /* 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 
+/* 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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data);
+
+static FLUID_INLINE void
+fluid_adsr_env_reset(fluid_adsr_env_t *env)
 {
-  env->count = 0;
-  env->section = 0;
-  env->val = 0.0f;
+    env->count = 0;
+    env->section = 0;
+    env->val = 0.0f;
 }
 
-static inline fluid_real_t 
-fluid_adsr_env_get_val(fluid_adsr_env_tenv)
+static FLUID_INLINE fluid_real_t
+fluid_adsr_env_get_val(fluid_adsr_env_t *env)
 {
-  return env->val;
+    return env->val;
 }
 
-static inline void
-fluid_adsr_env_set_val(fluid_adsr_env_tenv, fluid_real_t val)
+static FLUID_INLINE void
+fluid_adsr_env_set_val(fluid_adsr_env_t *env, fluid_real_t val)
 {
-  env->val = val;
+    env->val = val;
 }
 
-static inline fluid_adsr_env_section_t
-fluid_adsr_env_get_section(fluid_adsr_env_tenv)
+static FLUID_INLINE fluid_adsr_env_section_t
+fluid_adsr_env_get_section(fluid_adsr_env_t *env)
 {
-  return env->section;
+    return env->section;
 }
 
-static inline void 
-fluid_adsr_env_set_section(fluid_adsr_env_t* env, 
+static FLUID_INLINE void
+fluid_adsr_env_set_section(fluid_adsr_env_t *env,
                            fluid_adsr_env_section_t section)
 {
-  env->section = section;
-  env->count = 0;
+    env->section = section;
+    env->count = 0;
 }
 
-/* Used for determining which voice to kill. 
+/* 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_tenv)
+static FLUID_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;
-  }
+    if(env->section > FLUID_VOICE_ENVATTACK)
+    {
+        return env->val * 1000;
+    }
+    else
+    {
+        return env->data[FLUID_VOICE_ENVATTACK].max;
+    }
 }
 
 #endif
index c6eb723146f5c3218b99b7d08335f4ce8ad6f3d7..49ef99ecbef9e59db56c90eabc8a063cecad1f87 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #define SFONT_MASKVAL   0xFFC00000
 
 
-static void fluid_channel_init(fluid_channel_tchan);
+static void fluid_channel_init(fluid_channel_t *chan);
 
 
-fluid_channel_t*
-new_fluid_channel(fluid_synth_tsynth, int num)
+fluid_channel_t *
+new_fluid_channel(fluid_synth_t *synth, int num)
 {
-  fluid_channel_t* chan;
+    fluid_channel_t *chan;
 
-  chan = FLUID_NEW(fluid_channel_t);
-  if (chan == NULL) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return NULL;
-  }
+    chan = FLUID_NEW(fluid_channel_t);
 
-  chan->synth = synth;
-  chan->channum = num;
-  chan->preset = NULL;
-  chan->tuning = NULL;
+    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);
+    fluid_channel_init(chan);
+    fluid_channel_init_ctrl(chan, 0);
 
-  return chan;
+    return chan;
 }
 
 static void
-fluid_channel_init(fluid_channel_tchan)
+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;
+    fluid_preset_t *newpreset;
+    int i, prognum, banknum;
+
+    chan->sostenuto_orderid = 0;
+    /*--- Init poly/mono modes variables --------------------------------------*/
+    chan->mode = 0;
+    chan->mode_val = 0;
+
+    /* monophonic list initialization */
+    for(i = 0; i < FLUID_CHANNEL_SIZE_MONOLIST; i++)
+    {
+        chan->monolist[i].next = i + 1;
+    }
 
-  if (chan->tuning)
-  {
-    fluid_tuning_unref (chan->tuning, 1);
-    chan->tuning = NULL;
-  }
+    chan->monolist[FLUID_CHANNEL_SIZE_MONOLIST - 1].next = 0; /* ending element chained to the 1st */
+    chan->i_last = chan->n_notes = 0; /* clears the list */
+    chan->i_first = chan->monolist[chan->i_last].next; /* first note index in the list */
+    fluid_channel_clear_prev_note(chan); /* Mark previous note invalid */
+    /*---*/
+    chan->key_mono_sustained = INVALID_NOTE; /* No previous mono note sustained */
+    chan->legatomode = FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER;              /* Default mode */
+    chan->portamentomode = FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY;  /* Default mode */
+    /*--- End of poly/mono initialization --------------------------------------*/
+
+    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 
+  @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_tchan, int is_all_ctrl_off)
+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 */
-  }
+    int i;
+
+    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 ||
+                    i == BALANCE_MSB || i == BALANCE_LSB
+              )
+            {
+                continue;
+            }
+
+            fluid_channel_set_cc(chan, i, 0);
+        }
+    }
+    else
+    {
+        for(i = 0; i < 128; i++)
+        {
+            fluid_channel_set_cc(chan, i, 0);
+        }
+
+        fluid_channel_clear_portamento(chan); /* Clear PTC receive */
+        chan->previous_cc_breath = 0;/* Reset previous breath */
+    }
+
+    /* Reset polyphonic key pressure on all voices */
+    for(i = 0; i < 128; i++)
+    {
+        fluid_channel_set_key_pressure(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);
+
+        /* Balance (MSB & LSB) */
+        fluid_channel_set_cc(chan, BALANCE_MSB, 64);
+        fluid_channel_set_cc(chan, BALANCE_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_tchan)
+void
+delete_fluid_channel(fluid_channel_t *chan)
 {
-  if (chan->preset) delete_fluid_preset (chan->preset);
-  FLUID_FREE(chan);
-  return FLUID_OK;
+    fluid_return_if_fail(chan != NULL);
+
+    FLUID_FREE(chan);
 }
 
 /* FIXME - Calls fluid_channel_init() potentially in synthesis context */
 void
-fluid_channel_reset(fluid_channel_tchan)
+fluid_channel_reset(fluid_channel_t *chan)
 {
-  fluid_channel_init(chan);
-  fluid_channel_init_ctrl(chan, 0);
+    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_channel_set_preset(fluid_channel_t *chan, fluid_preset_t *preset)
 {
+    fluid_sfont_t *sfont;
 
-  fluid_preset_notify (chan->preset, FLUID_PRESET_UNSELECTED, chan->channum);
+    if(chan->preset == preset)
+    {
+        return FLUID_OK;
+    }
 
-  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;
+    if(chan->preset)
+    {
+        sfont = chan->preset->sfont;
+        sfont->refcount--;
+    }
+
+    fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum);
 
-  fluid_preset_notify (preset, FLUID_PRESET_SELECTED, chan->channum);
+    chan->preset = preset;
+
+    if(preset)
+    {
+        sfont = preset->sfont;
+        sfont->refcount++;
+    }
 
-  return FLUID_OK;
+    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_tchan, int sfontnum,
+fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfontnum,
                                   int banknum, int prognum)
 {
-  int oldval, newval, oldmask;
+    int oldval, newval, oldmask;
 
-  newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0)
-    | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0)
-    | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0);
+    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);
+    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;
+    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_tchan, int banklsb)
+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;
+    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_tchan, int bankmsb)
+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;
+    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_tchan, int *sfont,
+fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont,
                                   int *bank, int *prog)
 {
-  int sfont_bank_prog;
+    int sfont_bank_prog;
 
-  sfont_bank_prog = chan->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;
+    }
+}
+
+/**
+ * Updates legato/ staccato playing state
+ * The function is called:
+ * - on noteon before adding a note into the monolist.
+ * - on noteoff after removing a note out of the monolist.
+ * @param chan  fluid_channel_t.
+*/
+static void
+fluid_channel_update_legato_staccato_state(fluid_channel_t *chan)
+{
+    /* Updates legato/ staccato playing state */
+    if(chan->n_notes)
+    {
+        chan->mode |= FLUID_CHANNEL_LEGATO_PLAYING; /* Legato state */
+    }
+    else
+    {
+        chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */
+    }
+}
+
+/**
+ * Adds a note into the monophonic list. The function is part of the legato
+ * detector. fluid_channel_add_monolist() is intended to be called by
+ * fluid_synth_noteon_mono_LOCAL().
+ *
+ * When a note is added at noteOn each element is use in the forward direction
+ * and indexed by i_last variable.
+ *
+ * @param chan  fluid_channel_t.
+ * @param key MIDI note number (0-127).
+ * @param vel MIDI velocity (0-127, 0=noteoff).
+ * @param onenote. When 1 the function adds the note but the monophonic list
+ *                 keeps only one note (used on noteOn poly).
+ * Note: i_last index keeps a trace of the most recent note added.
+ *       prev_note keeps a trace of the note prior i_last note.
+ *       FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state.
+ *
+ * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+*/
+void
+fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key,
+                           unsigned char vel, unsigned char onenote)
+{
+    unsigned char i_last = chan->i_last;
+    /* Updates legato/ staccato playing state */
+    fluid_channel_update_legato_staccato_state(chan);
+
+    if(chan->n_notes)
+    {
+        /* keeps trace of the note prior last note */
+        chan->prev_note = chan->monolist[i_last].note;
+    }
+
+    /* moves i_last forward before writing new note */
+    i_last = chan->monolist[i_last].next;
+    chan->i_last = i_last;                     /* now ilast indexes the last note */
+    chan->monolist[i_last].note = key; /* we save note and velocity */
+    chan->monolist[i_last].vel = vel;
+
+    if(onenote)
+    {
+        /* clears monolist before one note addition */
+        chan->i_first = i_last;
+        chan->n_notes = 0;
+    }
+
+    if(chan->n_notes < FLUID_CHANNEL_SIZE_MONOLIST)
+    {
+        chan->n_notes++; /* updates n_notes */
+    }
+    else
+    {
+        /* The end of buffer is reach. So circular motion for i_first */
+        /* i_first index is moved forward */
+        chan->i_first = chan->monolist[i_last].next;
+    }
+}
+
+/**
+ * Searching a note in the monophonic list. The function is part of the legato
+ * detector. fluid_channel_search_monolist() is intended to be called by
+ * fluid_synth_noteoff_mono_LOCAL().
+ *
+ * The search starts from the first note in the list indexed by i_first
+
+ * @param chan  fluid_channel_t.
+ * @param key MIDI note number (0-127) to search.
+ * @param i_prev pointer on returned index of the note prior the note to search.
+ * @return index of the note if find, FLUID_FAILED otherwise.
+ *
+ */
+int
+fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev)
+{
+    short n = chan->n_notes; /* number of notes in monophonic list */
+    short j, i = chan->i_first; /* searching starts from i_first included */
+
+    for(j = 0 ; j < n ; j++)
+    {
+        if(chan->monolist[i].note == key)
+        {
+            if(i == chan->i_first)
+            {
+                /* tracking index of the previous note (i_prev) */
+                for(j = chan->i_last ; n < FLUID_CHANNEL_SIZE_MONOLIST; n++)
+                {
+                    j = chan->monolist[j].next;
+                }
+
+                * i_prev = j; /* returns index of the previous note */
+            }
+
+            return i; /* returns index of the note to search */
+        }
+
+        * i_prev = i; /* tracking index of the previous note (i_prev) */
+        i = chan->monolist[i].next; /* next element */
+    }
+
+    return FLUID_FAILED; /* not found */
+}
+
+/**
+ * removes a note from the monophonic list. The function is part of
+ * the legato detector.
+ * fluid_channel_remove_monolist() is intended to be called by
+ * fluid_synth_noteoff_mono_LOCAL().
+ *
+ * When a note is removed at noteOff the element concerned is fast unlinked
+ * and relinked after the i_last element.
+ *
+ * @param chan  fluid_channel_t.
+ * @param
+ *   i, index of the note to remove. If i is invalid or the list is
+ *      empty, the function do nothing and returns FLUID_FAILED.
+ * @param
+ *   On input, i_prev is a pointer on index of the note previous i.
+ *   On output i_prev is a pointer on index of the note previous i if i is the last note
+ *   in the list,FLUID_FAILED otherwise. When the returned index is valid it means
+ *   a legato detection on noteoff.
+ *
+ * Note: the following variables in Channel keeps trace of the situation.
+ *       - i_last index keeps a trace of the most recent note played even if
+ *       the list is empty.
+ *       - prev_note keeps a trace of the note removed if it is i_last.
+ *       - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state.
+ *
+ * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+ */
+void
+fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev)
+{
+    unsigned char i_last = chan->i_last;
+
+    /* checks if index is valid */
+    if(i < 0 || i >= FLUID_CHANNEL_SIZE_MONOLIST || !chan->n_notes)
+    {
+        * i_prev =  FLUID_FAILED;
+    }
+
+    /* The element is about to be removed and inserted between i_last and next */
+    /* Note: when i is egal to i_last or egal to i_first, removing/inserting
+       isn't necessary */
+    if(i == i_last)
+    {
+        /* Removing/Inserting isn't necessary */
+        /* keeps trace of the note prior last note */
+        chan->prev_note = chan->monolist[i_last].note;
+        /* moves i_last backward to the previous  */
+        chan->i_last = *i_prev; /* i_last index is moved backward */
+    }
+    else
+    {
+        /* i is before i_last */
+        if(i == chan->i_first)
+        {
+            /* Removing/inserting isn't necessary */
+            /* i_first index is moved forward to the next element*/
+            chan->i_first = chan->monolist[i].next;
+        }
+        else
+        {
+            /* i is between i_first and i_last */
+            /* Unlinks element i and inserts after i_last */
+            chan->monolist[* i_prev].next = chan->monolist[i].next; /* unlinks i */
+            /*inserts i after i_last */
+            chan->monolist[i].next = chan->monolist[i_last].next;
+            chan->monolist[i_last].next = i;
+        }
+
+        * i_prev =  FLUID_FAILED;
+    }
+
+    chan->n_notes--; /* updates the number of note in the list */
+    /* Updates legato/ staccato playing state */
+    fluid_channel_update_legato_staccato_state(chan);
+}
+
+/**
+ * On noteOff on a polyphonic channel,the monophonic list is fully flushed.
+ *
+ * @param chan  fluid_channel_t.
+ * Note: i_last index keeps a trace of the most recent note played even if
+ *       the list is empty.
+ *       prev_note keeps a trace of the note i_last .
+ *       FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing.
+ */
+void fluid_channel_clear_monolist(fluid_channel_t *chan)
+{
+    /* keeps trace off the most recent note played */
+    chan->prev_note = chan->monolist[chan->i_last].note;
+
+    /* flushes the monolist */
+    chan->i_first = chan->monolist[chan->i_last].next;
+    chan->n_notes = 0;
+    /* Update legato/ sataccato playing state */
+    chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */
+}
+
+/**
+ * On noteOn on a polyphonic channel,adds the note into the monophonic list
+ * keeping only this note.
+ * @param
+ *   chan  fluid_channel_t.
+ *   key, vel, note and velocity added in the monolist
+ * Note: i_last index keeps a trace of the most recent note inserted.
+ *       prev_note keeps a trace of the note prior i_last note.
+ *       FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing.
+ */
+void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key,
+                                        unsigned char vel)
+{
+    fluid_channel_add_monolist(chan, key, vel, 1);
+}
+
+/**
+ * The function changes the state (Valid/Invalid) of the previous note played in
+ * a staccato manner (fluid_channel_prev_note()).
+ * When potamento mode 'each note' or 'staccato only' is selected, on next
+ * noteOn a portamento will be started from the most recent note played
+ * staccato.
+ * It will be possible that it isn't appropriate. To give the musician the
+ * possibility to choose a portamento from this note , prev_note will be forced
+ * to invalid state on noteOff if portamento pedal is Off.
+ *
+ * The function is intended to be called when the following event occurs:
+ * - On noteOff (in poly or mono mode), to mark prev_note invalid.
+ * - On Portamento Off(in poly or mono mode), to mark prev_note invalid.
+ * @param chan  fluid_channel_t.
+ */
+void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan)
+{
+    /* checks if the playing is staccato */
+    if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING))
+    {
+
+        /* checks if portamento pedal is off */
+        if(! fluid_channel_portamento(chan))
+        {
+            /* forces prev_note invalid */
+            fluid_channel_clear_prev_note(chan);
+        }
+    }
+
+    /* else prev_note still remains valid for next fromkey portamento */
+}
+
+/**
+ * The function handles poly/mono commutation on legato pedal On/Off.
+ * @param chan  fluid_channel_t.
+ * @param value, value of the CC legato.
+ */
+void fluid_channel_cc_legato(fluid_channel_t *chan, int value)
+{
+    /* Special handling of the monophonic list  */
+    if(!(chan->mode & FLUID_CHANNEL_POLY_OFF) && chan->n_notes)  /* The monophonic list have notes */
+    {
+        if(value < 64)   /* legato is released */
+        {
+            /* returns from monophonic to polyphonic with notes in the monophonic list */
+
+            /* The monophonic list is flushed keeping last note only
+               Note: i_last index keeps a trace of the most recent note played.
+               prev_note keeps a trace of the note i_last.
+               FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing.
+            */
+            chan->i_first = chan->i_last;
+            chan->n_notes = 1;
+        }
+        else /* legato is depressed */
+        {
+            /* Inters in monophonic from polyphonic with note in monophonic list */
+            /* Stops the running note to remain coherent with Breath Sync mode */
+            if((chan->mode &  FLUID_CHANNEL_BREATH_SYNC) && !fluid_channel_breath_msb(chan))
+            {
+                fluid_synth_noteoff_monopoly(chan->synth, chan->channum,
+                                             fluid_channel_last_note(chan), 1);
+            }
+        }
+    }
+}
+
+/**
+ * The function handles CC Breath On/Off detection. When a channel is in
+ * Breath Sync mode and in monophonic playing, the breath controller allows
+ * to trigger noteon/noteoff note when the musician starts to breath (noteon) and
+ * stops to breath (noteoff).
+ * @param chan  fluid_channel_t.
+ * @param value, value of the CC Breath..
+ */
+void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value)
+{
+    if((chan->mode &  FLUID_CHANNEL_BREATH_SYNC)  && fluid_channel_is_playing_mono(chan) &&
+            (chan->n_notes))
+    {
+        /* The monophonic list isn't empty */
+        if((value > 0) && (chan->previous_cc_breath == 0))
+        {
+            /* CC Breath On detection */
+            fluid_synth_noteon_mono_staccato(chan->synth, chan->channum,
+                                             fluid_channel_last_note(chan),
+                                             fluid_channel_last_vel(chan));
+        }
+        else if((value == 0) && (chan->previous_cc_breath > 0))
+        {
+            /* CC Breath Off detection */
+            fluid_synth_noteoff_monopoly(chan->synth, chan->channum,
+                                         fluid_channel_last_note(chan), 1);
+        }
+    }
 
-  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;
+    chan->previous_cc_breath = value;
 }
index 85aa1ef00c0f8860e8ba0af3fe794e115094753b..42d73df7b53fd88502d9f7536f3a164465f5d7ac 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_midi.h"
 #include "fluid_tuning.h"
 
+/* The mononophonic list is part of the legato detector for monophonic mode */
+/* see fluid_synth_monopoly.c about a description of the legato detector device */
+/* Size of the monophonic list
+   - 1 is the minimum. it allows playing legato passage of any number
+     of notes on noteon only.
+   - Size above 1 allows playing legato on noteon but also on noteOff.
+     This allows the  musician to play fast trills.
+     This feature is particularly usful when the MIDI input device is a keyboard.
+     Choosing a size of 10 is sufficient (because most musicians have only 10
+     fingers when playing a monophonic instrument).
+*/
+#define FLUID_CHANNEL_SIZE_MONOLIST  10
+
+/*
+
+            The monophonic list
+   +------------------------------------------------+
+   |    +----+   +----+          +----+   +----+    |
+   |    |note|   |note|          |note|   |note|    |
+   +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+
+        +----+   +----+          +----+   +----+
+         /|\                      /|\
+          |                        |
+       i_first                   i_last
+
+ The monophonic list is a circular buffer of FLUID_CHANNEL_SIZE_MONOLIST elements.
+ Each element is linked forward at initialisation time.
+ - when a note is added at noteOn  (see fluid_channel_add_monolist()) each
+   element is use in the forward direction and indexed by i_last variable.
+ - when a note is removed at noteOff (see fluid_channel_remove_monolist()),
+   the element concerned is fast unlinked and relinked after the i_last element.
+
+ The most recent note added is indexed by i_last.
+ The most ancient note added is the first note indexed by i_first. i_first is
+ moving in the forward direction in a circular manner.
+
+*/
+struct mononote
+{
+    unsigned char next; /* next note */
+    unsigned char note; /* note */
+    unsigned char vel;  /* velocity */
+};
+
 /*
  * fluid_channel_t
  *
  */
 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_synth_t *synth;                 /**< Parent synthesizer instance */
+    int channum;                          /**< MIDI channel number */
+
+    /* Poly Mono variables see macro access description */
+    int mode;                                                          /**< Poly Mono mode */
+    int mode_val;                                                      /**< number of channel in basic channel group */
+
+    /* monophonic list - legato detector */
+    unsigned char i_first;          /**< First note index */
+    unsigned char i_last;           /**< most recent note index since the most recent add */
+    unsigned char prev_note;        /**< previous note of the most recent add/remove */
+    unsigned char n_notes;          /**< actual number of notes in the list */
+    struct mononote monolist[FLUID_CHANNEL_SIZE_MONOLIST];   /**< monophonic list */
+
+    unsigned char key_mono_sustained;         /**< previous sustained monophonic note */
+    unsigned char previous_cc_breath;            /**< Previous Breath */
+    enum fluid_channel_legato_mode legatomode;       /**< legato mode */
+    enum fluid_channel_portamento_mode portamentomode;   /**< portamento mode */
+    /*- End of Poly/mono variables description */
+
+    unsigned char cc[128];                         /**< MIDI controller values from [0;127] */
+    unsigned char key_pressure[128];               /**< MIDI polyphonic key pressure from [0;127] */
+
+    /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */
+    enum fluid_midi_channel_type channel_type;
+    enum fluid_interp interp_method;                    /**< Interpolation method (enum fluid_interp) */
+
+    unsigned char channel_pressure;                 /**< MIDI channel pressure from [0;127] */
+    unsigned char pitch_wheel_sensitivity;          /**< Current pitch wheel sensitivity */
+    short pitch_bend;                      /**< Current pitch bend value */
+    /* 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 tuning_bank;                      /**< Current tuning bank number */
+    int tuning_prog;                      /**< Current tuning program number */
+    fluid_tuning_t *tuning;               /**< Micro tuning */
+
+    fluid_preset_t *preset;               /**< Selected preset */
+    int sfont_bank_prog;                  /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */
+
+    /* NRPN system */
+    enum fluid_gen_type nrpn_select;      /* Generator ID of SoundFont NRPN message */
+    char 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];
 };
 
-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,
+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);
+void 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);
+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_tchan, int banklsb);
-void fluid_channel_set_bank_msb(fluid_channel_tchan, int bankmsb);
-void fluid_channel_get_sfont_bank_prog(fluid_channel_tchan, int *sfont,
+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_key_pressure(chan, key) \
+  ((chan)->key_pressure[key])
+#define fluid_channel_set_key_pressure(chan, key, val) \
+  ((chan)->key_pressure[key] = (val))
 #define fluid_channel_get_channel_pressure(chan) \
   ((chan)->channel_pressure)
 #define fluid_channel_set_channel_pressure(chan, val) \
@@ -138,6 +192,12 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan);
   ((chan)->tuning_prog)
 #define fluid_channel_set_tuning_prog(chan, prog) \
   ((chan)->tuning_prog = (prog))
+#define fluid_channel_portamentotime(_c) \
+    ((_c)->cc[PORTAMENTO_TIME_MSB] * 128 + (_c)->cc[PORTAMENTO_TIME_LSB])
+#define fluid_channel_portamento(_c)                   ((_c)->cc[PORTAMENTO_SWITCH] >= 64)
+#define fluid_channel_breath_msb(_c)                   ((_c)->cc[BREATH_MSB] > 0)
+#define fluid_channel_clear_portamento(_c)             ((_c)->cc[PORTAMENTO_CTRL] = INVALID_NOTE)
+#define fluid_channel_legato(_c)                           ((_c)->cc[LEGATO_SWITCH] >= 64)
 #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; }
@@ -146,4 +206,83 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan);
 #define fluid_channel_get_min_note_length_ticks(chan) \
   ((chan)->synth->min_note_length_ticks)
 
+/* Macros interface to poly/mono mode variables */
+#define MASK_BASICCHANINFOS  (FLUID_CHANNEL_MODE_MASK|FLUID_CHANNEL_BASIC|FLUID_CHANNEL_ENABLED)
+/* Set the basic channel infos for a MIDI basic channel */
+#define fluid_channel_set_basic_channel_info(chan,Infos) \
+    (chan->mode = (chan->mode & ~MASK_BASICCHANINFOS) | (Infos & MASK_BASICCHANINFOS))
+/* Reset the basic channel infos for a MIDI basic channel */
+#define fluid_channel_reset_basic_channel_info(chan) (chan->mode &=  ~MASK_BASICCHANINFOS)
+
+/* Macros interface to breath variables */
+#define FLUID_CHANNEL_BREATH_MASK  (FLUID_CHANNEL_BREATH_POLY|FLUID_CHANNEL_BREATH_MONO|FLUID_CHANNEL_BREATH_SYNC)
+/* Set the breath infos for a MIDI  channel */
+#define fluid_channel_set_breath_info(chan,BreathInfos) \
+(chan->mode = (chan->mode & ~FLUID_CHANNEL_BREATH_MASK) | (BreathInfos & FLUID_CHANNEL_BREATH_MASK))
+/* Get the breath infos for a MIDI  channel */
+#define fluid_channel_get_breath_info(chan) (chan->mode & FLUID_CHANNEL_BREATH_MASK)
+
+/* Returns true when channel is mono or legato is on */
+#define fluid_channel_is_playing_mono(chan) ((chan->mode & FLUID_CHANNEL_POLY_OFF) ||\
+                                             fluid_channel_legato(chan))
+
+/* Macros interface to monophonic list variables */
+#define INVALID_NOTE (255)
+/* Returns true when a note is a valid note */
+#define fluid_channel_is_valid_note(n)    (n != INVALID_NOTE)
+/* Marks prev_note as invalid. */
+#define fluid_channel_clear_prev_note(chan)    (chan->prev_note = INVALID_NOTE)
+
+/* Returns the most recent note from i_last entry of the monophonic list */
+#define fluid_channel_last_note(chan)  (chan->monolist[chan->i_last].note)
+
+/* Returns the most recent velocity from i_last entry of the monophonic list */
+#define fluid_channel_last_vel(chan)   (chan->monolist[chan->i_last].vel)
+
+/*
+  prev_note is used to determine fromkey_portamento as well as
+  fromkey_legato (see fluid_synth_get_fromkey_portamento_legato()).
+
+  prev_note is updated on noteOn/noteOff mono by the legato detector as this:
+  - On noteOn mono, before adding a new note into the monolist,the most
+    recent  note in the list (i.e at i_last position) is kept in prev_note.
+  - Similarly, on  noteOff mono , before removing a note out of the monolist,
+    the most recent note (i.e those at i_last position) is kept in prev_note.
+*/
+#define fluid_channel_prev_note(chan)  (chan->prev_note)
+
+/* Interface to poly/mono mode variables */
+enum fluid_channel_mode_flags_internal
+{
+    FLUID_CHANNEL_BASIC = 0x04,    /**< if flag set the corresponding midi channel is a basic channel */
+    FLUID_CHANNEL_ENABLED = 0x08,  /**< if flag set the corresponding midi channel is enabled, else disabled, i.e. channel ignores any MIDI messages */
+
+    /*
+      FLUID_CHANNEL_LEGATO_PLAYING bit of channel mode keeps trace of the legato /staccato
+      state playing.
+      FLUID_CHANNEL_LEGATO_PLAYING bit is updated on noteOn/noteOff mono by the legato detector:
+      - On noteOn, before inserting a new note into the monolist.
+      - On noteOff, after removing a note out of the monolist.
+
+      - On noteOn, this state is used by fluid_synth_noteon_mono_LOCAL()
+      to play the current  note legato or staccato.
+      - On noteOff, this state is used by fluid_synth_noteoff_mono_LOCAL()
+      to play the current noteOff legato with the most recent note.
+    */
+    /* bit7, 1: means legato playing , 0: means staccato playing */
+    FLUID_CHANNEL_LEGATO_PLAYING = 0x80
+};
+
+/* End of interface to monophonic list variables */
+
+void fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel, unsigned char onenote);
+int fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev);
+void fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev);
+void fluid_channel_clear_monolist(fluid_channel_t *chan);
+void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel);
+void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan);
+void fluid_channel_cc_legato(fluid_channel_t *chan, int value);
+void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value);
+
+
 #endif /* _FLUID_CHAN_H */
index 4bead5ce2d9d407c5861ea4fc02046989ed71337..83a88d3826c85f5a47b126ac5c657b8160291491 100644 (file)
@@ -1,13 +1,25 @@
-/*
- * 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.
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003  Peter Hanappe, Markus Nentwig and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
  */
 
 /*
+  based on a chrous implementation made by Juergen Mueller And Sundry Contributors in 1998
 
   CHANGES
 
@@ -78,8 +90,8 @@
  * Set through MAX_SAMPLES_LN2.
  * For example:
  * MAX_SAMPLES_LN2=12
- * => MAX_SAMPLES=pow(2,12)=4096
- * => MAX_SAMPLES_ANDMASK=4095
+ * => MAX_SAMPLES=pow(2,12-1)=2048
+ * => MAX_SAMPLES_ANDMASK=2047
  */
 #define MAX_SAMPLES_LN2 12
 
 #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];
+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*
+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;
-  }
+    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));
+            };
+        };
+    };
 
-  FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t));
+    /* allocate lookup tables */
+    chorus->lookup_tab = FLUID_ARRAY(int, (int)(chorus->sample_rate / MIN_SPEED_HZ));
 
-  chorus->sample_rate = sample_rate;
+    if(chorus->lookup_tab == NULL)
+    {
+        FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
+        goto error_recovery;
+    }
 
-  /* Lookup table for the SI function (impulse response of an ideal low pass) */
+    /* allocate sample buffer */
 
-  /* i: Offset in terms of whole samples */
-  for (i = 0; i < INTERPOLATION_SAMPLES; i++){
+    chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES);
 
-    /* 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.;
+    if(chorus->chorusbuf == NULL)
+    {
+        FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
+        goto error_recovery;
+    }
 
-      } 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));
-      };
+    if(fluid_chorus_init(chorus) != FLUID_OK)
+    {
+        goto error_recovery;
     };
-  };
-
-  /* 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;
 
-  return chorus;
+error_recovery:
+    delete_fluid_chorus(chorus);
 
- error_recovery:
-  delete_fluid_chorus(chorus);
-  return NULL;
+    return NULL;
 }
 
 void
-delete_fluid_chorus(fluid_chorus_tchorus)
+delete_fluid_chorus(fluid_chorus_t *chorus)
 {
-  if (chorus == NULL) {
-    return;
-  }
+    fluid_return_if_fail(chorus != NULL);
 
-  if (chorus->chorusbuf != NULL) {
     FLUID_FREE(chorus->chorusbuf);
-  }
-
-  if (chorus->lookup_tab != NULL) {
     FLUID_FREE(chorus->lookup_tab);
-  }
-
-  FLUID_FREE(chorus);
+    FLUID_FREE(chorus);
 }
 
 int
-fluid_chorus_init(fluid_chorus_tchorus)
+fluid_chorus_init(fluid_chorus_t *chorus)
 {
-  int i;
+    int i;
 
-  for (i = 0; i < MAX_SAMPLES; i++) {
-    chorus->chorusbuf[i] = 0.0;
-  }
+    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;
+    return FLUID_OK;
 }
 
 void
-fluid_chorus_reset(fluid_chorus_tchorus)
+fluid_chorus_reset(fluid_chorus_t *chorus)
 {
-  fluid_chorus_init(chorus);
+    fluid_chorus_init(chorus);
 }
 
 /**
@@ -243,223 +260,272 @@ fluid_chorus_reset(fluid_chorus_t* chorus)
  * @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)
+fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
+                 fluid_real_t speed, fluid_real_t depth_ms, int type)
 {
-  int 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;
+    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;
+        // set depth to maximum to avoid spamming console with above warning
+        chorus->depth_ms = (modulation_depth_samples * 1000) / chorus->sample_rate;
+    }
+
+    /* initialize LFO table */
+    switch(chorus->type)
+    {
+    default:
+        FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave.");
+        chorus->type = FLUID_CHORUS_MOD_SINE;
+        /* fall-through */
+        
+    case FLUID_CHORUS_MOD_SINE:
+        fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
+                          modulation_depth_samples);
+        break;
+
+    case FLUID_CHORUS_MOD_TRIANGLE:
+        fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples,
+                              modulation_depth_samples);
+        break;
+    }
+
+    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_tchorus, fluid_real_t *in,
-                           fluid_real_t *left_out, fluid_real_t *right_out)
+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;
+    int sample_index;
+    int i;
+    fluid_real_t d_in, d_out;
 
-  for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
+    for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++)
+    {
 
-    d_in = in[sample_index];
-    d_out = 0.0f;
+        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;
+        /* 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;
+        /* 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. */
+        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]]);
+            /* 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;
+            int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
 
-      /* modulo divide by INTERPOLATION_SUBSAMPLES */
-      pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+            /* 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!*/
+            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];
+                /* 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 */
+                pos_samples--;
+            };
 
-    d_out *= chorus->level;
+            /* Cycle the phase of the modulating LFO */
+            chorus->phase[i]++;
 
-    /* Add the chorus sum d_out to output */
-    left_out[sample_index] += d_out;
-    right_out[sample_index] += d_out;
+            chorus->phase[i] %= (chorus->modulation_period_samples);
+        } /* foreach chorus block */
 
-    /* Move forward in circular buffer */
-    chorus->counter++;
-    chorus->counter %= MAX_SAMPLES;
+        d_out *= chorus->level;
 
-  } /* foreach sample */
+        /* 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_tchorus, 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)
 {
-  int sample_index;
-  int i;
-  fluid_real_t d_in, d_out;
+    int sample_index;
+    int i;
+    fluid_real_t d_in, d_out;
 
-  for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
+    for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++)
+    {
 
-    d_in = in[sample_index];
-    d_out = 0.0f;
+        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;
+        /* 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;
+        /* 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]]);
 
-    for (i = 0; i < chorus->number_blocks; i++) {
-      int ii;
-      /* Calculate the delay in subsamples for the delay line of chorus block nr. */
+            int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
 
-      /* 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]]);
+            /* modulo divide by INTERPOLATION_SUBSAMPLES */
+            pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
 
-      int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
+            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!*/
 
-      /* modulo divide by INTERPOLATION_SUBSAMPLES */
-      pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+                /* 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];
 
-      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!*/
+                pos_samples--;
+            };
 
-       /* 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];
+            /* Cycle the phase of the modulating LFO */
+            chorus->phase[i]++;
 
-       pos_samples--;
-      };
-      /* Cycle the phase of the modulating LFO */
-      chorus->phase[i]++;
-      chorus->phase[i] %= (chorus->modulation_period_samples);
-    } /* foreach chorus block */
+            chorus->phase[i] %= (chorus->modulation_period_samples);
+        } /* foreach chorus block */
 
-    d_out *= chorus->level;
+        d_out *= chorus->level;
 
-    /* Store the chorus sum d_out to output */
-    left_out[sample_index] = d_out;
-    right_out[sample_index] = d_out;
+        /* 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;
+        /* Move forward in circular buffer */
+        chorus->counter++;
+        chorus->counter %= MAX_SAMPLES;
 
-  } /* foreach sample */
+    } /* foreach sample */
 }
 
 /* Purpose:
@@ -474,15 +540,25 @@ void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in,
 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]);
-  }
+    int i;
+    double angle, incr, mult;
+
+    /* Pre-calculate increment between angles. */
+    incr = (2. * M_PI) / (double)len;
+
+    /* Pre-calculate 'depth' multiplier. */
+    mult = (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES;
+
+    /* Initialize to zero degrees. */
+    angle = 0.;
+
+    /* Build sine modulation waveform */
+    for(i = 0; i < len; i++)
+    {
+        buf[i] = (int)((1. + sin(angle)) * mult) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
+
+        angle += incr;
+    }
 }
 
 /* Purpose:
@@ -492,15 +568,26 @@ fluid_chorus_sine(int *buf, int len, int depth)
 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;
-  }
+    int *il = buf;
+    int *ir = buf + len - 1;
+    int ival;
+    double val, incr;
+
+    /* Pre-calculate increment for the ramp. */
+    incr = 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES;
+
+    /* Initialize first value */
+    val = 0. - 3. * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
+
+    /* Build triangular modulation waveform */
+    while(il <= ir)
+    {
+        /* Assume 'val' to be always negative for rounding mode */
+        ival = (int)(val - 0.5);
+
+        *il++ = ival;
+        *ir-- = ival;
+
+        val += incr;
+    }
 }
index 3422fa94b27693040c9d1c50dad72c94747881d4..8a6734aa0a0b0c5abd8e0883492f8cdea04c23ba 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -30,31 +30,35 @@ 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;
+    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,
 
-/** Value for fluid_chorus_set() which sets all chorus parameters. */
-#define FLUID_CHORUS_SET_ALL    0x1F
+    /** Value for fluid_chorus_set() which sets all chorus parameters. */
+    FLUID_CHORUS_SET_ALL   =   FLUID_CHORUS_SET_NR
+                               | FLUID_CHORUS_SET_LEVEL
+                               | FLUID_CHORUS_SET_SPEED
+                               | FLUID_CHORUS_SET_DEPTH
+                               | FLUID_CHORUS_SET_TYPE,
+} fluid_chorus_set_t;
 
 /*
  * chorus
  */
-fluid_chorus_tnew_fluid_chorus(fluid_real_t sample_rate);
-void delete_fluid_chorus(fluid_chorus_tchorus);
-int fluid_chorus_init(fluid_chorus_tchorus);
-void fluid_chorus_reset(fluid_chorus_tchorus);
-
-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_tchorus, fluid_real_t *in,
-                           fluid_real_t *left_out, fluid_real_t *right_out);
-void fluid_chorus_processreplace(fluid_chorus_tchorus, fluid_real_t *in,
-                               fluid_real_t *left_out, fluid_real_t *right_out);
+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, fluid_real_t level,
+                      fluid_real_t speed, fluid_real_t 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);
 
 
 
index 1a790cfbfbd3946170efc43d64781333652b54eb..555dd61367761f23ddacd6ce24b4895ee23fb76b 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "fluid_conv.h"
 
+#define FLUID_CENTS_HZ_SIZE     1200
+#define FLUID_VEL_CB_SIZE       128
+#define FLUID_CB_AMP_SIZE       1441
+#define FLUID_PAN_SIZE          1002
 
 /* 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];
+static fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
+static fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
+static fluid_real_t fluid_concave_tab[FLUID_VEL_CB_SIZE];
+static fluid_real_t fluid_convex_tab[FLUID_VEL_CB_SIZE];
+static fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
 
 /*
  * void fluid_synth_init
@@ -38,58 +40,52 @@ fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
 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);
-  }
+    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);
+    }
+
+    /* 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[FLUID_VEL_CB_SIZE - 1] = 1.0;
+
+    /* convex unipolar positive transform curve */
+    fluid_convex_tab[0] = 0;
+    fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.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 < FLUID_VEL_CB_SIZE - 1; i++)
+    {
+        x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((i * i) / (fluid_real_t)((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10;
+        fluid_convex_tab[i] = (fluid_real_t)(1.0 - x);
+        fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = (fluid_real_t) x;
+    }
+
+    /* initialize the pan conversion table */
+    x = M_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);
+    }
 }
 
 /*
@@ -98,35 +94,62 @@ fluid_conversion_config(void)
 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 */
-  }
+    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 */
+    }
 }
 
 /*
@@ -135,55 +158,46 @@ fluid_ct2hz_real(fluid_real_t cents)
 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);
+    /* 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
+ * in: a value between 0 and 1440, 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];
-}
+    /*
+     * 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.
+     */
 
-/*
- * 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];
+    /* 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];
 }
 
 /*
@@ -192,21 +206,27 @@ fluid_atten2amp(fluid_real_t atten)
 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);
+    /* 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);
 }
 
 /*
@@ -215,14 +235,26 @@ fluid_tc2sec_delay(fluid_real_t tc)
 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);
+    /* 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);
 }
 
 /*
@@ -231,8 +263,8 @@ fluid_tc2sec_attack(fluid_real_t tc)
 fluid_real_t
 fluid_tc2sec(fluid_real_t tc)
 {
-  /* No range checking here! */
-  return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+    /* No range checking here! */
+    return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
 }
 
 /*
@@ -241,14 +273,26 @@ fluid_tc2sec(fluid_real_t tc)
 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);
+    /* 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);
 }
 
 /*
@@ -259,7 +303,7 @@ fluid_tc2sec_release(fluid_real_t tc)
 fluid_real_t
 fluid_act2hz(fluid_real_t c)
 {
-  return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0));
+    return (fluid_real_t)(8.176 * pow(2.0, (double) c / 1200.0));
 }
 
 /*
@@ -270,7 +314,7 @@ fluid_act2hz(fluid_real_t c)
 fluid_real_t
 fluid_hz2ct(fluid_real_t f)
 {
-  return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0));
+    return (fluid_real_t)(6900 + 1200 * log(f / 440.0) / M_LN2);
 }
 
 /*
@@ -279,16 +323,53 @@ fluid_hz2ct(fluid_real_t f)
 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)];
-  }
+    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)];
+    }
+}
+
+/*
+ * Return the amount of attenuation based on the balance for the specified
+ * channel. If balance is negative (turned toward left channel, only the right
+ * channel is attenuated. If balance is positive, only the left channel is
+ * attenuated.
+ *
+ * @params balance left/right balance, range [-960;960] in absolute centibels
+ * @return amount of attenuation [0.0;1.0]
+ */
+fluid_real_t fluid_balance(fluid_real_t balance, int left)
+{
+    /* This is the most common case */
+    if(balance == 0)
+    {
+        return 1.0f;
+    }
+
+    if((left && balance < 0) || (!left && balance > 0))
+    {
+        return 1.0f;
+    }
+
+    if(balance < 0)
+    {
+        balance = -balance;
+    }
+
+    return fluid_cb2amp(balance);
 }
 
 /*
@@ -297,12 +378,16 @@ fluid_pan(fluid_real_t c, int left)
 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];
+    if(val < 0)
+    {
+        return 0;
+    }
+    else if(val >= FLUID_VEL_CB_SIZE)
+    {
+        return 1;
+    }
+
+    return fluid_concave_tab[(int) val];
 }
 
 /*
@@ -311,10 +396,14 @@ fluid_concave(fluid_real_t val)
 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];
+    if(val < 0)
+    {
+        return 0;
+    }
+    else if(val >= FLUID_VEL_CB_SIZE)
+    {
+        return 1;
+    }
+
+    return fluid_convex_tab[(int) val];
 }
index 29793c33593b7d308be604e2e59c49a9e83ce4f7..d84a321c6cfc36c1cbeeeff583e43124609919c3 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "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
+/*
+ Attenuation range in centibels.
+ Attenuation range is the dynamic range of the volume envelope generator
+ from 0 to the end of attack segment.
+ fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation.
+ However the spec makes no distinction between 16 or 24 bit synths, so use
+ 96 dB here.
 
-/* 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)*/
+ Note about usefulness of 24 bits:
+ 1)Even fluidsynth is a 24 bit synth, this format is only relevant if
+ the sample format coming from the soundfont is 24 bits and the audio sample format
+ choosen by the application (audio.sample.format) is not 16 bits.
+
+ 2)When the sample soundfont is 16 bits, the internal 24 bits number have
+ 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of
+ this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db)
+ even if this sample is produced by the audio driver using an audio sample format
+ compatible for a 24 bit DAC.
+
+ 3)When the audio sample format settings is 16 bits (audio.sample.format), the
+ audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB
+ even if the initial sample comes from a 24 bits soundfont.
+
+ In both cases (2) or (3), the real dynamic range is only 96 dB.
+
+ Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3):
+ - for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB).
+ - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB).
+ */
+#define FLUID_PEAK_ATTENUATION  960.0f
 
 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);
@@ -48,16 +66,8 @@ 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_balance(fluid_real_t balance, 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 */
index c3952184114e47e76d85fc8119f09255bc8570bb..6e19eb71afaa5ad6aa646fa85c41a9d17327a01d 100644 (file)
@@ -6,16 +6,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 
 #include "fluid_defsfont.h"
-/* Todo: Get rid of that 'include' */
+#include "fluid_sfont.h"
 #include "fluid_sys.h"
+#include "fluid_synth.h"
+#include "fluid_samplecache.h"
+
+/* EMU8k/10k hardware applies this factor to initial attenuation generator values set at preset and
+ * instrument level in a soundfont. We apply this factor when loading the generator values to stay
+ * compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */
+#define EMU_ATTENUATION_FACTOR (0.4f)
+
+/* Dynamic sample loading functions */
+static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset);
+static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset);
+static void unload_sample(fluid_sample_t *sample);
+static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan);
+static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason);
+static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone);
+static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx);
+
 
 /***************************************************************
  *
  *                           SFONT LOADER
  */
 
-fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings)
+/**
+ * Creates a default soundfont2 loader that can be used with fluid_synth_add_sfloader().
+ * By default every synth instance has an initial default soundfont loader instance.
+ * Calling this function is usually only necessary to load a soundfont from memory, by providing custom callback functions via fluid_sfloader_set_callbacks().
+ *
+ * @param settings A settings instance obtained by new_fluid_settings()
+ * @return A default soundfont2 loader struct
+ */
+fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings)
 {
-  fluid_sfloader_t* loader;
+    fluid_sfloader_t *loader;
+    fluid_return_val_if_fail(settings != NULL, NULL);
 
-  loader = FLUID_NEW(fluid_sfloader_t);
-  if (loader == NULL) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return NULL;
-  }
+    loader = new_fluid_sfloader(fluid_defsfloader_load, delete_fluid_sfloader);
 
-  loader->data = settings;
-  loader->free = delete_fluid_defsfloader;
-  loader->load = fluid_defsfloader_load;
+    if(loader == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
 
-  return loader;
-}
+    fluid_sfloader_set_data(loader, settings);
 
-int delete_fluid_defsfloader(fluid_sfloader_t* loader)
-{
-  if (loader) {
-    FLUID_FREE(loader);
-  }
-  return FLUID_OK;
+    return loader;
 }
 
-fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename)
+fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename)
 {
-  fluid_defsfont_t* defsfont;
-  fluid_sfont_t* sfont;
+    fluid_defsfont_t *defsfont;
+    fluid_sfont_t *sfont;
 
-  defsfont = new_fluid_defsfont(loader->data);
+    defsfont = new_fluid_defsfont(fluid_sfloader_get_data(loader));
 
-  if (defsfont == NULL) {
-    return NULL;
-  }
+    if(defsfont == NULL)
+    {
+        return NULL;
+    }
 
-  if (fluid_defsfont_load(defsfont, filename) == FLUID_FAILED) {
-    delete_fluid_defsfont(defsfont);
-    return NULL;
-  }
+    sfont = new_fluid_sfont(fluid_defsfont_sfont_get_name,
+                            fluid_defsfont_sfont_get_preset,
+                            fluid_defsfont_sfont_iteration_start,
+                            fluid_defsfont_sfont_iteration_next,
+                            fluid_defsfont_sfont_delete);
 
-  sfont = FLUID_NEW(fluid_sfont_t);
-  if (sfont == NULL) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return NULL;
-  }
+    if(sfont == NULL)
+    {
+        delete_fluid_defsfont(defsfont);
+        return NULL;
+    }
+
+    fluid_sfont_set_data(sfont, defsfont);
 
-  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;
+    defsfont->sfont = sfont;
+
+    if(fluid_defsfont_load(defsfont, &loader->file_callbacks, filename) == FLUID_FAILED)
+    {
+        fluid_sfont_delete_internal(sfont);
+        return NULL;
+    }
 
-  return sfont;
+    return sfont;
 }
 
 
@@ -95,621 +118,487 @@ fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* file
  *                           PUBLIC INTERFACE
  */
 
-int fluid_defsfont_sfont_delete(fluid_sfont_tsfont)
+int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont)
 {
-  if (delete_fluid_defsfont(sfont->data) != 0) {
-    return -1;
-  }
-  FLUID_FREE(sfont);
-  return 0;
-}
+    if(delete_fluid_defsfont(fluid_sfont_get_data(sfont)) != FLUID_OK)
+    {
+        return -1;
+    }
 
-char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont)
-{
-  return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data);
+    delete_fluid_sfont(sfont);
+    return 0;
 }
 
-#if 0
-fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s)
+const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont)
 {
-  /* This function is here just to avoid an ABI/SONAME bump, see ticket #98. Should never be used. */
-  return NULL;
+    return fluid_defsfont_get_name(fluid_sfont_get_data(sfont));
 }
-#endif
 
-fluid_preset_t*
-fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum)
+fluid_preset_t *
+fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, 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;
+    return fluid_defsfont_get_preset(fluid_sfont_get_data(sfont), bank, prenum);
 }
 
-void fluid_defsfont_sfont_iteration_start(fluid_sfont_tsfont)
+void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont)
 {
-  fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data);
+    fluid_defsfont_iteration_start(fluid_sfont_get_data(sfont));
 }
 
-int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset)
+fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont)
 {
-  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);
+    return fluid_defsfont_iteration_next(fluid_sfont_get_data(sfont));
 }
 
-int fluid_defpreset_preset_delete(fluid_preset_t* preset)
+void fluid_defpreset_preset_delete(fluid_preset_t *preset)
 {
-  fluid_defpreset_t* defpreset = preset ? preset->data : NULL;
-  fluid_defsfont_t* sfont = defpreset ? defpreset->sfont : NULL;
+    fluid_defsfont_t *defsfont;
+    fluid_defpreset_t *defpreset;
 
-  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);
+    defsfont = fluid_sfont_get_data(preset->sfont);
+    defpreset = fluid_preset_get_data(preset);
+
+    if(defsfont)
+    {
+        defsfont->preset = fluid_list_remove(defsfont->preset, defpreset);
+    }
 
-  return 0;
+    delete_fluid_defpreset(defpreset);
+    delete_fluid_preset(preset);
 }
 
-char* fluid_defpreset_preset_get_name(fluid_preset_t* preset)
+const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset)
 {
-  return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data);
+    return fluid_defpreset_get_name(fluid_preset_get_data(preset));
 }
 
-int fluid_defpreset_preset_get_banknum(fluid_preset_tpreset)
+int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset)
 {
-  return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data);
+    return fluid_defpreset_get_banknum(fluid_preset_get_data(preset));
 }
 
-int fluid_defpreset_preset_get_num(fluid_preset_tpreset)
+int fluid_defpreset_preset_get_num(fluid_preset_t *preset)
 {
-  return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data);
+    return fluid_defpreset_get_num(fluid_preset_get_data(preset));
 }
 
-int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth,
-                                int chan, int key, int vel)
+int fluid_defpreset_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth,
+                                  int chan, int key, int vel)
 {
-  return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel);
+    return fluid_defpreset_noteon(fluid_preset_get_data(preset), synth, chan, key, vel);
 }
 
 
-
-
 /***************************************************************
  *
- *                    CACHED SAMPLEDATA LOADER
+ *                           SFONT
  */
 
-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;
+/*
+ * new_fluid_defsfont
+ */
+fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings)
+{
+    fluid_defsfont_t *defsfont;
 
-static fluid_cached_sampledata_t* all_cached_sampledata = NULL;
-static fluid_mutex_t cached_sampledata_mutex = FLUID_MUTEX_INIT;
+    defsfont = FLUID_NEW(fluid_defsfont_t);
 
-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(defsfont == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
 
-  if (stat(filename, &buf) == -1) {
-    return FLUID_FAILED;
-  }
+    FLUID_MEMSET(defsfont, 0, sizeof(*defsfont));
 
-  *modification_time = buf.st_mtime;
-  return FLUID_OK;
-#endif
-}
+    fluid_settings_getint(settings, "synth.lock-memory", &defsfont->mlock);
+    fluid_settings_getint(settings, "synth.dynamic-sample-loading", &defsfont->dynamic_samples);
 
-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;
+    return defsfont;
 }
 
-static int fluid_cached_sampledata_unload(const short *sampledata)
+/*
+ * delete_fluid_defsfont
+ */
+int delete_fluid_defsfont(fluid_defsfont_t *defsfont)
 {
-  fluid_cached_sampledata_t* prev = NULL;
-  fluid_cached_sampledata_t* cached_sampledata;
+    fluid_list_t *list;
+    fluid_preset_t *preset;
+    fluid_sample_t *sample;
 
-  fluid_mutex_lock(cached_sampledata_mutex);
-  cached_sampledata = all_cached_sampledata;
+    fluid_return_val_if_fail(defsfont != NULL, FLUID_OK);
 
-  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);
+    /* Check that no samples are currently used */
+    for(list = defsfont->sample; list; list = fluid_list_next(list))
+    {
+        sample = (fluid_sample_t *) fluid_list_get(list);
 
-        if (prev != NULL) {
-          prev->next = cached_sampledata->next;
-        } else {
-          all_cached_sampledata = cached_sampledata->next;
+        if(sample->refcount != 0)
+        {
+            return FLUID_FAILED;
         }
+    }
 
-        FLUID_FREE(cached_sampledata);
-      }
+    if(defsfont->filename != NULL)
+    {
+        FLUID_FREE(defsfont->filename);
+    }
 
-      goto success_exit;
+    for(list = defsfont->sample; list; list = fluid_list_next(list))
+    {
+        delete_fluid_sample((fluid_sample_t *) fluid_list_get(list));
     }
 
-    prev = cached_sampledata;
-    cached_sampledata = cached_sampledata->next;
-  }
+    if(defsfont->sample)
+    {
+        delete_fluid_list(defsfont->sample);
+    }
 
-  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;
+    if(defsfont->sampledata != NULL)
+    {
+        fluid_samplecache_unload(defsfont->sampledata);
+    }
 
- error_exit:
-  fluid_mutex_unlock(cached_sampledata_mutex);
-  return FLUID_FAILED;
-}
+    for(list = defsfont->preset; list; list = fluid_list_next(list))
+    {
+        preset = (fluid_preset_t *)fluid_list_get(list);
+        fluid_defpreset_preset_delete(preset);
+    }
 
+    delete_fluid_list(defsfont->preset);
 
+    for(list = defsfont->inst; list; list = fluid_list_next(list))
+    {
+        delete_fluid_inst(fluid_list_get(list));
+    }
 
+    delete_fluid_list(defsfont->inst);
 
-/***************************************************************
- *
- *                           SFONT
- */
+    FLUID_FREE(defsfont);
+    return FLUID_OK;
+}
 
 /*
- * new_fluid_defsfont
+ * fluid_defsfont_get_name
  */
-fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings)
+const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont)
 {
-  fluid_defsfont_t* sfont;
-  int i;
+    return defsfont->filename;
+}
 
-  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;
-  }
+/* Load sample data for a single sample from the Soundfont file.
+ * Returns FLUID_OK on error, otherwise FLUID_FAILED
+ */
+int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample)
+{
+    int num_samples;
+    unsigned int source_end = sample->source_end;
 
-  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;
+    /* For uncompressed samples we want to include the 46 zero sample word area following each sample
+     * in the Soundfont. Otherwise samples with loopend > end, which we have decided not to correct, would
+     * be corrected after all in fluid_sample_sanitize_loop */
+    if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
+    {
+        source_end += 46;  /* Length of zero sample word after each sample, according to SF specs */
+
+        /* Safeguard against Soundfonts that are not quite valid and don't include 46 sample words after the
+         * last sample */
+        if(source_end >= (defsfont->samplesize  / sizeof(short)))
+        {
+            source_end = defsfont->samplesize  / sizeof(short);
+        }
     }
-    sfont->preset_stack_size++;
-  }
 
-  return sfont;
-}
+    num_samples = fluid_samplecache_load(
+                      sfdata, sample->source_start, source_end, sample->sampletype,
+                      defsfont->mlock, &sample->data, &sample->data24);
 
-/*
- * 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;
+    if(num_samples < 0)
+    {
+        return FLUID_FAILED;
+    }
+
+    if(num_samples == 0)
+    {
+        sample->start = sample->end = 0;
+        sample->loopstart = sample->loopend = 0;
+        return FLUID_OK;
+    }
+
+    /* Ogg Vorbis samples already have loop pointers relative to the invididual decompressed sample,
+     * but SF2 samples are relative to sample chunk start, so they need to be adjusted */
+    if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
+    {
+        sample->loopstart = sample->source_loopstart - sample->source_start;
+        sample->loopend = sample->source_loopend - sample->source_start;
+    }
+
+    /* As we've just loaded an individual sample into it's own buffer, we need to adjust the start
+     * and end pointers */
+    sample->start = 0;
+    sample->end = num_samples - 1;
+
+    return FLUID_OK;
 }
 
-/*
- * fluid_defsfont_get_name
+/* Loads the sample data for all samples from the Soundfont file. For SF2 files, it loads the data in
+ * one large block. For SF3 files, each compressed sample gets loaded individually.
+ * Returns FLUID_OK on success, otherwise FLUID_FAILED
  */
-char* fluid_defsfont_get_name(fluid_defsfont_t* sfont)
+int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata)
 {
-  return sfont->filename;
-}
+    fluid_list_t *list;
+    fluid_sample_t *sample;
+    int sf3_file = (sfdata->version.major == 3);
+
+    /* For SF2 files, we load the sample data in one large block */
+    if(!sf3_file)
+    {
+        int read_samples;
+        int num_samples = sfdata->samplesize / sizeof(short);
+
+        read_samples = fluid_samplecache_load(sfdata, 0, num_samples - 1, 0, defsfont->mlock,
+                                              &defsfont->sampledata, &defsfont->sample24data);
+
+        if(read_samples != num_samples)
+        {
+            FLUID_LOG(FLUID_ERR, "Attempted to read %d words of sample data, but got %d instead",
+                      num_samples, read_samples);
+            return FLUID_FAILED;
+        }
+    }
+
+    for(list = defsfont->sample; list; list = fluid_list_next(list))
+    {
+        sample = fluid_list_get(list);
+
+        if(sf3_file)
+        {
+            /* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format
+             * anyway */
+            if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED)
+            {
+                FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name);
+                return FLUID_FAILED;
+            }
+
+            fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short));
+        }
+        else
+        {
+            /* Data pointers of SF2 samples point to large sample data block loaded above */
+            sample->data = defsfont->sampledata;
+            sample->data24 = defsfont->sample24data;
+            fluid_sample_sanitize_loop(sample, defsfont->samplesize);
+        }
+
+        fluid_voice_optimize_sample(sample);
+    }
 
+    return FLUID_OK;
+}
 
 /*
  * fluid_defsfont_load
  */
-int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file)
+int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *fcbs, 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);
+    SFData *sfdata;
+    fluid_list_t *p;
+    SFPreset *sfpreset;
+    SFSample *sfsample;
+    fluid_sample_t *sample;
+    fluid_defpreset_t *defpreset = NULL;
 
-  /* 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;
-  }
+    defsfont->filename = FLUID_STRDUP(file);
+
+    if(defsfont->filename == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return FLUID_FAILED;
+    }
+
+    defsfont->fcbs = fcbs;
+
+    /* The actual loading is done in the sfont and sffile files */
+    sfdata = fluid_sffile_open(file, fcbs);
+
+    if(sfdata == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file");
+        return FLUID_FAILED;
+    }
+
+    if(fluid_sffile_parse_presets(sfdata) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Couldn't parse presets from soundfont file");
+        goto err_exit;
+    }
+
+    /* Keep track of the position and size of the sample data because
+       it's loaded separately (and might be unoaded/reloaded in future) */
+    defsfont->samplepos = sfdata->samplepos;
+    defsfont->samplesize = sfdata->samplesize;
+    defsfont->sample24pos = sfdata->sample24pos;
+    defsfont->sample24size = sfdata->sample24size;
 
-  /* 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;
+    /* Create all samples from sample headers */
+    p = sfdata->sample;
 
-  /* load sample data in one block */
-  if (fluid_defsfont_load_sampledata(sfont) != FLUID_OK)
-    goto err_exit;
+    while(p != NULL)
+    {
+        sfsample = (SFSample *)fluid_list_get(p);
+
+        sample = new_fluid_sample();
+
+        if(sample == NULL)
+        {
+            goto err_exit;
+        }
+
+        if(fluid_sample_import_sfont(sample, sfsample, defsfont) == FLUID_OK)
+        {
+            fluid_defsfont_add_sample(defsfont, sample);
+        }
+        else
+        {
+            delete_fluid_sample(sample);
+            sample = NULL;
+        }
+
+        /* Store reference to FluidSynth sample in SFSample for later IZone fixups */
+        sfsample->fluid_sample = sample;
 
-  /* Create all the sample headers */
-  p = sfdata->sample;
-  while (p != NULL) {
-    sfsample = (SFSample *) p->data;
+        p = fluid_list_next(p);
+    }
+
+    /* If dynamic sample loading is disabled, load all samples in the Soundfont */
+    if(!defsfont->dynamic_samples)
+    {
+        if(fluid_defsfont_load_all_sampledata(defsfont, sfdata) == FLUID_FAILED)
+        {
+            FLUID_LOG(FLUID_ERR, "Unable to load all sample data");
+            goto err_exit;
+        }
+    }
 
-    sample = new_fluid_sample();
-    if (sample == NULL) goto err_exit;
+    /* Load all the presets */
+    p = sfdata->preset;
 
-    if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK)
-      goto err_exit;
+    while(p != NULL)
+    {
+        sfpreset = (SFPreset *)fluid_list_get(p);
+        defpreset = new_fluid_defpreset(defsfont);
 
-    /* Store reference to FluidSynth sample in SFSample for later IZone fixups */
-    sfsample->fluid_sample = sample;
+        if(defpreset == NULL)
+        {
+            goto err_exit;
+        }
 
-    fluid_defsfont_add_sample(sfont, sample);
-    fluid_voice_optimize_sample(sample);
-    p = fluid_list_next(p);
-  }
+        if(fluid_defpreset_import_sfont(defpreset, sfpreset, defsfont) != FLUID_OK)
+        {
+            goto err_exit;
+        }
 
-  /* 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_defsfont_add_preset(defsfont, defpreset) == FLUID_FAILED)
+        {
+            goto err_exit;
+        }
 
-    if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK)
-      goto err_exit;
+        p = fluid_list_next(p);
+    }
 
-    fluid_defsfont_add_preset(sfont, preset);
-    p = fluid_list_next(p);
-  }
-  sfont_close (sfdata);
+    fluid_sffile_close(sfdata);
 
-  return FLUID_OK;
+    return FLUID_OK;
 
 err_exit:
-  sfont_close (sfdata);
-  if (preset != NULL)
-    delete_fluid_defpreset(preset);
-  return FLUID_FAILED;
+    fluid_sffile_close(sfdata);
+    delete_fluid_defpreset(defpreset);
+    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)
+int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample)
 {
-  sfont->sample = fluid_list_append(sfont->sample, sample);
-  return FLUID_OK;
+    defsfont->sample = fluid_list_append(defsfont->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)
+int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset)
 {
-  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_preset_t *preset;
 
-/*
- * 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);
+    preset = new_fluid_preset(defsfont->sfont,
+                              fluid_defpreset_preset_get_name,
+                              fluid_defpreset_preset_get_banknum,
+                              fluid_defpreset_preset_get_num,
+                              fluid_defpreset_preset_noteon,
+                              fluid_defpreset_preset_delete);
+
+    if(defsfont->dynamic_samples)
+    {
+        preset->notify = dynamic_samples_preset_notify;
+    }
+
+    if(preset == NULL)
+    {
+        return FLUID_FAILED;
+    }
+
+    fluid_preset_set_data(preset, defpreset);
+
+    defsfont->preset = fluid_list_append(defsfont->preset, preset);
+
+    return FLUID_OK;
 }
 
 /*
  * fluid_defsfont_get_preset
  */
-fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num)
+fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, 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_preset_t *preset;
+    fluid_list_t *list;
+
+    for(list = defsfont->preset; list != NULL; list = fluid_list_next(list))
+    {
+        preset = (fluid_preset_t *)fluid_list_get(list);
+
+        if((fluid_preset_get_banknum(preset) == bank) && (fluid_preset_get_num(preset) == num))
+        {
+            return preset;
+        }
+    }
+
+    return NULL;
 }
 
 /*
  * fluid_defsfont_iteration_start
  */
-void fluid_defsfont_iteration_start(fluid_defsfont_tsfont)
+void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont)
 {
-  sfont->iter_cur = sfont->preset;
+    defsfont->preset_iter_cur = defsfont->preset;
 }
 
 /*
  * fluid_defsfont_iteration_next
  */
-int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset)
+fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont)
 {
-  if (sfont->iter_cur == NULL) {
-    return 0;
-  }
+    fluid_preset_t *preset = (fluid_preset_t *)fluid_list_get(defsfont->preset_iter_cur);
+
+    defsfont->preset_iter_cur = fluid_list_next(defsfont->preset_iter_cur);
 
-  preset->data = (void*) sfont->iter_cur;
-  sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur);
-  return 1;
+    return preset;
 }
 
 /***************************************************************
@@ -720,75 +609,77 @@ int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* prese
 /*
  * new_fluid_defpreset
  */
-fluid_defpreset_t*
-new_fluid_defpreset(fluid_defsfont_tsfont)
+fluid_defpreset_t *
+new_fluid_defpreset(fluid_defsfont_t *defsfont)
 {
-  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;
+    fluid_defpreset_t *defpreset = FLUID_NEW(fluid_defpreset_t);
+
+    if(defpreset == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    defpreset->next = NULL;
+    defpreset->defsfont = defsfont;
+    defpreset->name[0] = 0;
+    defpreset->bank = 0;
+    defpreset->num = 0;
+    defpreset->global_zone = NULL;
+    defpreset->zone = NULL;
+    return defpreset;
 }
 
 /*
  * delete_fluid_defpreset
  */
-int
-delete_fluid_defpreset(fluid_defpreset_tpreset)
+void
+delete_fluid_defpreset(fluid_defpreset_t *defpreset)
 {
-  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;
+    fluid_preset_zone_t *zone;
+
+    fluid_return_if_fail(defpreset != NULL);
+
+    delete_fluid_preset_zone(defpreset->global_zone);
+    defpreset->global_zone = NULL;
+
+    zone = defpreset->zone;
+
+    while(zone != NULL)
+    {
+        defpreset->zone = zone->next;
+        delete_fluid_preset_zone(zone);
+        zone = defpreset->zone;
+    }
+
+    FLUID_FREE(defpreset);
 }
 
 int
-fluid_defpreset_get_banknum(fluid_defpreset_tpreset)
+fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset)
 {
-  return preset->bank;
+    return defpreset->bank;
 }
 
 int
-fluid_defpreset_get_num(fluid_defpreset_tpreset)
+fluid_defpreset_get_num(fluid_defpreset_t *defpreset)
 {
-  return preset->num;
+    return defpreset->num;
 }
 
-char*
-fluid_defpreset_get_name(fluid_defpreset_tpreset)
+const char *
+fluid_defpreset_get_name(fluid_defpreset_t *defpreset)
 {
-  return preset->name;
+    return defpreset->name;
 }
 
 /*
  * fluid_defpreset_next
  */
-fluid_defpreset_t*
-fluid_defpreset_next(fluid_defpreset_tpreset)
+fluid_defpreset_t *
+fluid_defpreset_next(fluid_defpreset_t *defpreset)
 {
-  return preset->next;
+    return defpreset->next;
 }
 
 
@@ -796,2637 +687,1505 @@ fluid_defpreset_next(fluid_defpreset_t* preset)
  * fluid_defpreset_noteon
  */
 int
-fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel)
+fluid_defpreset_noteon(fluid_defpreset_t *defpreset, 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;
+    fluid_preset_zone_t *preset_zone, *global_preset_zone;
+    fluid_inst_t *inst;
+    fluid_inst_zone_t *inst_zone, *global_inst_zone;
+    fluid_voice_zone_t *voice_zone;
+    fluid_list_t *list;
+    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);
+    global_preset_zone = fluid_defpreset_get_global_zone(defpreset);
 
-  /* run thru all the zones of this preset */
-  preset_zone = fluid_defpreset_get_zone(preset);
-  while (preset_zone != NULL) {
+    /* run thru all the zones of this preset */
+    preset_zone = fluid_defpreset_get_zone(defpreset);
 
-    /* check if the note falls into the key and velocity range of this
-       preset */
-    if (fluid_preset_zone_inside_range(preset_zone, key, vel)) {
+    while(preset_zone != NULL)
+    {
+
+        /* check if the note falls into the key and velocity range of this
+           preset */
+        if(fluid_zone_inside_range(&preset_zone->range, 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 that could start a voice */
+            for(list = preset_zone->voice_zone; list != NULL; list = fluid_list_next(list))
+            {
+                voice_zone = fluid_list_get(list);
 
-      inst = fluid_preset_zone_get_inst(preset_zone);
-      global_inst_zone = fluid_inst_get_global_zone(inst);
+                /* check if the instrument zone is ignored and the note falls into
+                   the key and velocity range of this  instrument zone.
+                   An instrument zone must be ignored when its voice is already running
+                   played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */
+                if(fluid_zone_inside_range(&voice_zone->range, key, vel))
+                {
+
+                    inst_zone = voice_zone->inst_zone;
 
-      /* run thru all the zones of this instrument */
-      inst_zone = fluid_inst_get_zone(inst);
-         while (inst_zone != NULL) {
+                    /* this is a good zone. allocate a new synthesis process and initialize it */
+                    voice = fluid_synth_alloc_voice_LOCAL(synth, inst_zone->sample, chan, key, vel, &voice_zone->range);
+
+                    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.
+                         However this check is not necessary when the soundfont
+                         loader has ignored invalid preset generators.
+                         Actually load_pgen()has ignored these invalid preset
+                         generators:
+                           GEN_STARTADDROFS,      GEN_ENDADDROFS,
+                           GEN_STARTLOOPADDROFS,  GEN_ENDLOOPADDROFS,
+                           GEN_STARTADDRCOARSEOFS,GEN_ENDADDRCOARSEOFS,
+                           GEN_STARTLOOPADDRCOARSEOFS,
+                           GEN_KEYNUM, GEN_VELOCITY,
+                           GEN_ENDLOOPADDRCOARSEOFS,
+                           GEN_SAMPLEMODE, GEN_EXCLUSIVECLASS,GEN_OVERRIDEROOTKEY
+                        */
+
+                        /* 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.
+                             */
+                        }
+                    } /* 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)
+                     */
+                }
+
+            }
+        }
 
-       /* 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;
-       }
+        preset_zone = fluid_preset_zone_next(preset_zone);
+    }
 
-       /* 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;
+    return FLUID_OK;
 }
 
 /*
  * fluid_defpreset_set_global_zone
  */
 int
-fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone)
+fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone)
 {
-  preset->global_zone = zone;
-  return FLUID_OK;
+    defpreset->global_zone = zone;
+    return FLUID_OK;
 }
 
 /*
  * fluid_defpreset_import_sfont
  */
 int
-fluid_defpreset_import_sfont(fluid_defpreset_tpreset,
-                            SFPreset* sfpreset,
-                            fluid_defsfont_t* sfont)
+fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset,
+                             SFPreset *sfpreset,
+                             fluid_defsfont_t *defsfont)
 {
-  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_list_t *p;
+    SFZone *sfzone;
+    fluid_preset_zone_t *zone;
+    int count;
+    char zone_name[256];
+
+    if(FLUID_STRLEN(sfpreset->name) > 0)
+    {
+        FLUID_STRCPY(defpreset->name, sfpreset->name);
+    }
+    else
+    {
+        FLUID_SNPRINTF(defpreset->name, sizeof(defpreset->name), "Bank%d,Pre%d", sfpreset->bank, sfpreset->prenum);
+    }
+
+    defpreset->bank = sfpreset->bank;
+    defpreset->num = sfpreset->prenum;
+    p = sfpreset->zone;
+    count = 0;
+
+    while(p != NULL)
+    {
+        sfzone = (SFZone *)fluid_list_get(p);
+        FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", defpreset->name, count);
+        zone = new_fluid_preset_zone(zone_name);
+
+        if(zone == NULL)
+        {
+            return FLUID_FAILED;
+        }
+
+        if(fluid_preset_zone_import_sfont(zone, sfzone, defsfont) != 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(defpreset, zone);
+        }
+        else if(fluid_defpreset_add_zone(defpreset, 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)
+fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, 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;
+    if(defpreset->zone == NULL)
+    {
+        zone->next = NULL;
+        defpreset->zone = zone;
+    }
+    else
+    {
+        zone->next = defpreset->zone;
+        defpreset->zone = zone;
+    }
+
+    return FLUID_OK;
 }
 
 /*
  * fluid_defpreset_get_zone
  */
-fluid_preset_zone_t*
-fluid_defpreset_get_zone(fluid_defpreset_tpreset)
+fluid_preset_zone_t *
+fluid_defpreset_get_zone(fluid_defpreset_t *defpreset)
 {
-  return preset->zone;
+    return defpreset->zone;
 }
 
 /*
  * fluid_defpreset_get_global_zone
  */
-fluid_preset_zone_t*
-fluid_defpreset_get_global_zone(fluid_defpreset_tpreset)
+fluid_preset_zone_t *
+fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset)
 {
-  return preset->global_zone;
+    return defpreset->global_zone;
 }
 
+/***************************************************************
+ *
+ *                           PRESET_ZONE
+ */
+
 /*
  * fluid_preset_zone_next
  */
-fluid_preset_zone_t*
-fluid_preset_zone_next(fluid_preset_zone_t* preset)
+fluid_preset_zone_t *
+fluid_preset_zone_next(fluid_preset_zone_t *zone)
 {
-  return preset->next;
+    return zone->next;
 }
 
 /*
  * new_fluid_preset_zone
  */
-fluid_preset_zone_t*
+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;
-}
+    fluid_preset_zone_t *zone = NULL;
+    zone = FLUID_NEW(fluid_preset_zone_t);
 
-/***************************************************************
- *
- *                           PRESET_ZONE
- */
+    if(zone == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
 
-/*
- * delete_fluid_preset_zone
- */
-int
-delete_fluid_preset_zone(fluid_preset_zone_t* zone)
-{
-  fluid_mod_t *mod, *tmp;
+    zone->next = NULL;
+    zone->voice_zone = NULL;
+    zone->name = FLUID_STRDUP(name);
 
-  mod = zone->mod;
-  while (mod)  /* delete the modulators */
+    if(zone->name == NULL)
     {
-      tmp = mod;
-      mod = mod->next;
-      fluid_mod_delete (tmp);
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        FLUID_FREE(zone);
+        return NULL;
     }
 
-  if (zone->name) FLUID_FREE (zone->name);
-  if (zone->inst) delete_fluid_inst (zone->inst);
-  FLUID_FREE(zone);
-  return FLUID_OK;
-}
+    zone->inst = NULL;
+    zone->range.keylo = 0;
+    zone->range.keyhi = 128;
+    zone->range.vello = 0;
+    zone->range.velhi = 128;
+    zone->range.ignore = FALSE;
 
-/*
- * 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;
+    /* 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;
 }
 
 /*
- * fluid_preset_zone_get_inst
+ * delete_fluid_preset_zone
  */
-fluid_inst_t*
-fluid_preset_zone_get_inst(fluid_preset_zone_t* zone)
+void
+delete_fluid_preset_zone(fluid_preset_zone_t *zone)
 {
-  return zone->inst;
-}
+    fluid_mod_t *mod, *tmp;
+    fluid_list_t *list;
 
-/*
- * 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));
-}
+    fluid_return_if_fail(zone != NULL);
 
-/***************************************************************
- *
- *                           INST
- */
+    mod = zone->mod;
 
-/*
- * 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;
+    while(mod) /* delete the modulators */
+    {
+        tmp = mod;
+        mod = mod->next;
+        delete_fluid_mod(tmp);
     }
-    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;
+
+    for(list = zone->voice_zone; list != NULL; list = fluid_list_next(list))
+    {
+        FLUID_FREE(fluid_list_get(list));
     }
-    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;
+    delete_fluid_list(zone->voice_zone);
+
+    FLUID_FREE(zone->name);
+    FLUID_FREE(zone);
 }
 
-/*
- * fluid_inst_import_sfont
- */
-int
-fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont)
+static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone)
 {
-  fluid_list_t *p;
-  SFZone* sfzone;
-  fluid_inst_zone_t* zone;
-  char zone_name[256];
-  int count;
+    fluid_inst_zone_t *inst_zone;
+    fluid_sample_t *sample;
+    fluid_voice_zone_t *voice_zone;
+    fluid_zone_range_t *irange;
+    fluid_zone_range_t *prange = &preset_zone->range;
 
-  p = sfinst->zone;
-  if ((sfinst->name != NULL) && (FLUID_STRLEN(sfinst->name) > 0)) {
-    FLUID_STRCPY(inst->name, sfinst->name);
-  } else {
-    FLUID_STRCPY(inst->name, "<untitled>");
-  }
+    fluid_return_val_if_fail(preset_zone->inst != NULL, FLUID_FAILED);
 
-  count = 0;
-  while (p != NULL) {
+    inst_zone = fluid_inst_get_zone(preset_zone->inst);
 
-    sfzone = (SFZone *) p->data;
-    FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count);
+    while(inst_zone != NULL)
+    {
 
-    zone = new_fluid_inst_zone(zone_name);
-    if (zone == NULL) {
-      return FLUID_FAILED;
-    }
+        /* We only create voice ranges for zones that could actually start a voice,
+         * i.e. that have a sample and don't point to ROM */
+        sample = fluid_inst_zone_get_sample(inst_zone);
 
-    if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) {
-      delete_fluid_inst_zone(zone);
-      return FLUID_FAILED;
-    }
+        if((sample == NULL) || fluid_sample_in_rom(sample))
+        {
+            inst_zone = fluid_inst_zone_next(inst_zone);
+            continue;
+        }
 
-    if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) {
-      fluid_inst_set_global_zone(inst, zone);
+        voice_zone = FLUID_NEW(fluid_voice_zone_t);
 
-    } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) {
-      return FLUID_FAILED;
+        if(voice_zone == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            return FLUID_FAILED;
+        }
+
+        voice_zone->inst_zone = inst_zone;
+
+        irange = &inst_zone->range;
+
+        voice_zone->range.keylo = (prange->keylo > irange->keylo) ? prange->keylo : irange->keylo;
+        voice_zone->range.keyhi = (prange->keyhi < irange->keyhi) ? prange->keyhi : irange->keyhi;
+        voice_zone->range.vello = (prange->vello > irange->vello) ? prange->vello : irange->vello;
+        voice_zone->range.velhi = (prange->velhi < irange->velhi) ? prange->velhi : irange->velhi;
+        voice_zone->range.ignore = FALSE;
+
+        preset_zone->voice_zone = fluid_list_append(preset_zone->voice_zone, voice_zone);
+
+        inst_zone = fluid_inst_zone_next(inst_zone);
     }
 
-    p = fluid_list_next(p);
-    count++;
-  }
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 /*
- * fluid_inst_add_zone
+ * fluid_preset_zone_import_sfont
  */
 int
-fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone)
+fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont)
 {
-  if (inst->zone == NULL) {
-    zone->next = NULL;
-    inst->zone = zone;
-  } else {
-    zone->next = inst->zone;
-    inst->zone = zone;
-  }
-  return FLUID_OK;
-}
+    fluid_list_t *r;
+    SFGen *sfgen;
+    SFInst *sfinst;
+    int count;
 
-/*
- * fluid_inst_get_zone
- */
-fluid_inst_zone_t*
-fluid_inst_get_zone(fluid_inst_t* inst)
-{
-  return inst->zone;
+    for(count = 0, r = sfzone->gen; r != NULL; count++)
+    {
+        sfgen = (SFGen *)fluid_list_get(r);
+
+        switch(sfgen->id)
+        {
+        case GEN_KEYRANGE:
+            zone->range.keylo = sfgen->amount.range.lo;
+            zone->range.keyhi = sfgen->amount.range.hi;
+            break;
+
+        case GEN_VELRANGE:
+            zone->range.vello = sfgen->amount.range.lo;
+            zone->range.velhi = sfgen->amount.range.hi;
+            break;
+
+        case GEN_ATTENUATION:
+            /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at
+             * preset and instrument level */
+            zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR;
+            zone->gen[sfgen->id].flags = GEN_SET;
+            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))
+    {
+        sfinst = sfzone->instsamp->data;
+
+        zone->inst = find_inst_by_idx(defsfont, sfinst->idx);
+
+        if(zone->inst == NULL)
+        {
+            zone->inst = fluid_inst_import_sfont(zone, sfinst, defsfont);
+        }
+
+        if(zone->inst == NULL)
+        {
+            return FLUID_FAILED;
+        }
+
+        if(fluid_preset_zone_create_voice_zones(zone) == FLUID_FAILED)
+        {
+            return FLUID_FAILED;
+        }
+    }
+
+    /* Import the modulators (only SF2.1 and higher) */
+    for(count = 0, r = sfzone->mod; r != NULL; count++)
+    {
+
+        SFMod *mod_src = (SFMod *)fluid_list_get(r);
+        fluid_mod_t *mod_dest = new_fluid_mod();
+        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_inst_get_global_zone
+ * fluid_preset_zone_get_inst
  */
-fluid_inst_zone_t*
-fluid_inst_get_global_zone(fluid_inst_t* inst)
+fluid_inst_*
+fluid_preset_zone_get_inst(fluid_preset_zone_t *zone)
 {
-  return inst->global_zone;
+    return zone->inst;
 }
 
+
 /***************************************************************
  *
- *                           INST_ZONE
+ *                           INST
  */
 
 /*
- * new_fluid_inst_zone
+ * new_fluid_inst
  */
-fluid_inst_zone_t*
-new_fluid_inst_zone(char* name)
+fluid_inst_*
+new_fluid_inst()
 {
-  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;
+    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_zone
+ * delete_fluid_inst
  */
-int
-delete_fluid_inst_zone(fluid_inst_zone_t* zone)
+void
+delete_fluid_inst(fluid_inst_t *inst)
 {
-  fluid_mod_t *mod, *tmp;
+    fluid_inst_zone_t *zone;
 
-  mod = zone->mod;
-  while (mod)  /* delete the modulators */
+    fluid_return_if_fail(inst != NULL);
+
+    delete_fluid_inst_zone(inst->global_zone);
+    inst->global_zone = NULL;
+
+    zone = inst->zone;
+
+    while(zone != NULL)
     {
-      tmp = mod;
-      mod = mod->next;
-      fluid_mod_delete (tmp);
+        inst->zone = zone->next;
+        delete_fluid_inst_zone(zone);
+        zone = inst->zone;
     }
 
-  if (zone->name) FLUID_FREE (zone->name);
-  FLUID_FREE(zone);
-  return FLUID_OK;
+    FLUID_FREE(inst);
 }
 
 /*
- * fluid_inst_zone_next
+ * fluid_inst_set_global_zone
  */
-fluid_inst_zone_t*
-fluid_inst_zone_next(fluid_inst_zone_t* zone)
+int
+fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone)
 {
-  return zone->next;
+    inst->global_zone = zone;
+    return FLUID_OK;
 }
 
 /*
- * fluid_inst_zone_import_sfont
+ * fluid_inst_import_sfont
+ */
+fluid_inst_t *
+fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_defsfont_t *defsfont)
+{
+    fluid_list_t *p;
+    fluid_inst_t *inst;
+    SFZone *sfzone;
+    fluid_inst_zone_t *inst_zone;
+    char zone_name[256];
+    int count;
+
+    inst = (fluid_inst_t *) new_fluid_inst();
+
+    if(inst == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    inst->source_idx = sfinst->idx;
+
+    p = sfinst->zone;
+
+    if(FLUID_STRLEN(sfinst->name) > 0)
+    {
+        FLUID_STRCPY(inst->name, sfinst->name);
+    }
+    else
+    {
+        FLUID_STRCPY(inst->name, "<untitled>");
+    }
+
+    count = 0;
+
+    while(p != NULL)
+    {
+
+        sfzone = (SFZone *)fluid_list_get(p);
+        FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", inst->name, count);
+
+        inst_zone = new_fluid_inst_zone(zone_name);
+
+        if(inst_zone == NULL)
+        {
+            return NULL;
+        }
+
+        if(fluid_inst_zone_import_sfont(inst_zone, sfzone, defsfont) != FLUID_OK)
+        {
+            delete_fluid_inst_zone(inst_zone);
+            return NULL;
+        }
+
+        if((count == 0) && (fluid_inst_zone_get_sample(inst_zone) == NULL))
+        {
+            fluid_inst_set_global_zone(inst, inst_zone);
+
+        }
+        else if(fluid_inst_add_zone(inst, inst_zone) != FLUID_OK)
+        {
+            return NULL;
+        }
+
+        p = fluid_list_next(p);
+        count++;
+    }
+
+    defsfont->inst = fluid_list_append(defsfont->inst, inst);
+    return inst;
+}
+
+/*
+ * fluid_inst_add_zone
  */
 int
-fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont)
+fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone)
 {
-  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;
+    if(inst->zone == NULL)
+    {
+        zone->next = NULL;
+        inst->zone = zone;
+    }
+    else
+    {
+        zone->next = inst->zone;
+        inst->zone = zone;
+    }
+
+    return FLUID_OK;
 }
 
 /*
- * fluid_inst_zone_get_sample
+ * fluid_inst_get_zone
  */
-fluid_sample_t*
-fluid_inst_zone_get_sample(fluid_inst_zone_t* zone)
+fluid_inst_zone_t *
+fluid_inst_get_zone(fluid_inst_t *inst)
 {
-  return zone->sample;
+    return inst->zone;
 }
 
 /*
- * fluid_inst_zone_inside_range
+ * fluid_inst_get_global_zone
  */
-int
-fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel)
+fluid_inst_zone_t *
+fluid_inst_get_global_zone(fluid_inst_t *inst)
 {
-  return ((zone->keylo <= key) &&
-         (zone->keyhi >= key) &&
-         (zone->vello <= vel) &&
-         (zone->velhi >= vel));
+    return inst->global_zone;
 }
 
 /***************************************************************
  *
- *                           SAMPLE
+ *                           INST_ZONE
  */
 
 /*
- * new_fluid_sample
+ * new_fluid_inst_zone
  */
-fluid_sample_t*
-new_fluid_sample()
+fluid_inst_zone_t *
+new_fluid_inst_zone(char *name)
 {
-  fluid_sample_t* sample = NULL;
+    fluid_inst_zone_t *zone = NULL;
+    zone = FLUID_NEW(fluid_inst_zone_t);
 
-  sample = FLUID_NEW(fluid_sample_t);
-  if (sample == NULL) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return NULL;
-  }
+    if(zone == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
 
-  memset(sample, 0, sizeof(fluid_sample_t));
-  sample->valid = 1;
+    zone->next = NULL;
+    zone->name = FLUID_STRDUP(name);
 
-  return sample;
+    if(zone->name == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        FLUID_FREE(zone);
+        return NULL;
+    }
+
+    zone->sample = NULL;
+    zone->range.keylo = 0;
+    zone->range.keyhi = 128;
+    zone->range.vello = 0;
+    zone->range.velhi = 128;
+    zone->range.ignore = FALSE;
+    /* 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_sample
+ * delete_fluid_inst_zone
  */
-int
-delete_fluid_sample(fluid_sample_t* sample)
+void
+delete_fluid_inst_zone(fluid_inst_zone_t *zone)
 {
-  FLUID_FREE(sample);
-  return FLUID_OK;
+    fluid_mod_t *mod, *tmp;
+
+    fluid_return_if_fail(zone != NULL);
+
+    mod = zone->mod;
+
+    while(mod) /* delete the modulators */
+    {
+        tmp = mod;
+        mod = mod->next;
+        delete_fluid_mod(tmp);
+    }
+
+    FLUID_FREE(zone->name);
+    FLUID_FREE(zone);
 }
 
 /*
- * fluid_sample_in_rom
+ * fluid_inst_zone_next
  */
-int
-fluid_sample_in_rom(fluid_sample_t* sample)
+fluid_inst_zone_t *
+fluid_inst_zone_next(fluid_inst_zone_t *zone)
 {
-  return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
+    return zone->next;
 }
 
 /*
- * fluid_sample_import_sfont
+ * fluid_inst_zone_import_sfont
  */
 int
-fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont)
+fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont)
 {
-  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;
-}
+    fluid_list_t *r;
+    SFGen *sfgen;
+    int count;
 
+    for(count = 0, r = sfzone->gen; r != NULL; count++)
+    {
+        sfgen = (SFGen *)fluid_list_get(r);
+
+        switch(sfgen->id)
+        {
+        case GEN_KEYRANGE:
+            inst_zone->range.keylo = sfgen->amount.range.lo;
+            inst_zone->range.keyhi = sfgen->amount.range.hi;
+            break;
+
+        case GEN_VELRANGE:
+            inst_zone->range.vello = sfgen->amount.range.lo;
+            inst_zone->range.velhi = sfgen->amount.range.hi;
+            break;
+
+        case GEN_ATTENUATION:
+            /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at
+             * preset and instrument level */
+            inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR;
+            inst_zone->gen[sfgen->id].flags = GEN_SET;
+            break;
+
+        default:
+            /* FIXME: some generators have an unsigned word amount value but
+            i don't know which ones */
+            inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+            inst_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))
+    {
+        inst_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 *)fluid_list_get(r);
+        int type;
+        fluid_mod_t *mod_dest;
 
-/*=================================sfload.c========================
-  Borrowed from Smurf SoundFont Editor by Josh Green
-  =================================================================*/
+        mod_dest = new_fluid_mod();
 
-/*
-   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;
+        if(mod_dest == NULL)
+        {
+            return FLUID_FAILED;
+        }
 
-  p = (unsigned int *) & idlist;
-  for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1)
-    if (*p == id)
-      return (i + 1);
+        mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
 
-  return (UNKN_ID);
-}
+        /* *** Amount *** */
+        mod_dest->amount = mod_src->amount;
 
-SFData *
-sfload_file (const char * fname)
-{
-  SFData *sf = NULL;
-  FILE *fd;
-  int fsize = 0;
-  int err = FALSE;
+        /* *** 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;
 
-  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;
-    }
+        /* 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;
+        }
 
-  if (!err)
-    {
-      memset (sf, 0, sizeof (SFData)); /* zero sfdata */
-      sf->fname = FLUID_STRDUP (fname);        /* copy file name */
-      sf->sffd = fd;
-    }
+        /* 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;
+        }
 
-  /* 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);
+        /* 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;
+        }
 
-  if (!err && !load_body (fsize, sf, fd))
-    err = TRUE;                        /* load the sfont */
+        /* 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 (err)
-    {
-      if (sf)
-       sfont_close (sf);
-      return (NULL);
-    }
+        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;
+        }
 
-  return (sf);
-}
+        /* *** Dest *** */
+        mod_dest->dest = mod_src->dest; /* index of controlled generator */
 
-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);
-}
+        /* *** 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;
 
-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);
-}
+        /* 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;
+        }
 
-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);
-}
+        /* 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;
+        }
 
-static int
-process_sdta (unsigned int size, SFData * sf, FILE * fd)
-{
-  SFChunk chunk;
+        /* 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;
+        }
 
-  if (size == 0)
-    return (OK);               /* no sample data? */
+        /* modulator source types: SF2.01 section 8.2.1 page 52 */
+        type = (mod_src->amtsrc) >> 10;
+        type &= 63; /* type is a 6-bit value */
 
-  /* read sub chunk */
-  READCHUNK (&chunk, fd);
-  size -= 8;
+        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;
+        }
 
-  if (chunkid (chunk.id) != SMPL_ID)
-    return (gerr (ErrCorr,
-       _("Expected SMPL chunk found invalid id instead")));
+        /* *** 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;
+        }
 
-  /* 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")));
+        /* 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)
+        {
+            inst_zone->mod = mod_dest;
+        }
+        else
+        {
+            fluid_mod_t *last_mod = inst_zone->mod;
 
-  /* sample data follows */
-  sf->samplepos = ftell (fd);
+            /* Find the end of the list */
+            while(last_mod->next != NULL)
+            {
+                last_mod = last_mod->next;
+            }
 
-  /* used in fixup_sample() to check validity of sample headers */
-  sdtachunk_size = chunk.size;
-  sf->samplesize = chunk.size;
+            last_mod->next = mod_dest;
+        }
 
-  FSKIP (size, fd);
+        r = fluid_list_next(r);
+    } /* foreach modulator */
 
-  return (OK);
+    return FLUID_OK;
 }
 
-static int
-pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk,
-  int * size, FILE * fd)
+/*
+ * fluid_inst_zone_get_sample
+ */
+fluid_sample_t *
+fluid_inst_zone_get_sample(fluid_inst_zone_t *zone)
 {
-  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);
+    return zone->sample;
 }
 
-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
+fluid_zone_inside_range(fluid_zone_range_t *range, int key, int vel)
 {
-  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);
-}
+    /* ignoreInstrumentZone is set in mono legato playing */
+    int ignore_zone = range->ignore;
 
-/* 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);
-}
+    /* Reset the 'ignore' request */
+    range->ignore = FALSE;
 
-/* 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);
+    return !ignore_zone && ((range->keylo <= key) &&
+                            (range->keyhi >= key) &&
+                            (range->vello <= vel) &&
+                            (range->velhi >= vel));
 }
 
-/* -------------------------------------------------------------------
- * 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);
-}
+ *                           SAMPLE
+ */
 
-/* instrument bag loader */
-static int
-load_ibag (int size, SFData * sf, FILE * fd)
+/*
+ * fluid_sample_in_rom
+ */
+int
+fluid_sample_in_rom(fluid_sample_t *sample)
 {
-  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);
+    return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
 }
 
-/* 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_sample_import_sfont
+ */
+int
+fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont)
 {
-  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);
-}
+    FLUID_STRCPY(sample->name, sfsample->name);
 
-/* sample header loader */
-static int
-load_shdr (unsigned int size, SFData * sf, FILE * fd)
-{
-  unsigned int i;
-  SFSample *p;
+    sample->source_start = sfsample->start;
+    sample->source_end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */
+    sample->source_loopstart = sfsample->loopstart;
+    sample->source_loopend = sfsample->loopend;
 
-  if (size % SFSHDRSIZE || size == 0)  /* size is multiple of SHDR size? */
-    return (gerr (ErrCorr, _("Sample header has invalid size")));
+    sample->start = sample->source_start;
+    sample->end = sample->source_end;
+    sample->loopstart = sample->source_loopstart;
+    sample->loopend = sample->source_loopend;
+    sample->samplerate = sfsample->samplerate;
+    sample->origpitch = sfsample->origpitch;
+    sample->pitchadj = sfsample->pitchadj;
+    sample->sampletype = sfsample->sampletype;
 
-  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);
+    if(defsfont->dynamic_samples)
+    {
+        sample->notify = dynamic_samples_sample_notify;
     }
 
-  /* load all sample headers */
-  for (i = 0; i < size; i++)
+    if(fluid_sample_validate(sample, defsfont->samplesize) == FLUID_FAILED)
     {
-      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;
+        return FLUID_FAILED;
     }
 
-  FSKIP (SFSHDRSIZE, fd);      /* skip terminal shdr */
-
-  return (OK);
+    return FLUID_OK;
 }
 
-/* "fixup" (inst # -> inst ptr) instrument references in preset list */
-static int
-fixup_pgen (SFData * sf)
+/* Called if a sample is no longer used by a voice. Used by dynamic sample loading
+ * to unload a sample that is not used by any loaded presets anymore but couldn't
+ * be unloaded straight away because it was still in use by a voice. */
+static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason)
 {
-  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);
-}
+    if(reason == FLUID_SAMPLE_DONE && sample->preset_count == 0)
+    {
+        unload_sample(sample);
+    }
 
-/* "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);
+    return FLUID_OK;
 }
 
-/* convert sample end, loopstart and loopend to offsets and check if valid */
-static int
-fixup_sample (SFData * sf)
+/* Called if a preset has been selected for or unselected from a channel. Used by
+ * dynamic sample loading to load and unload samples on demand. */
+static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan)
 {
-  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
-  ================================================================*/
-
+    fluid_defsfont_t *defsfont;
 
-/* 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
+    if(reason == FLUID_PRESET_SELECTED)
+    {
+        FLUID_LOG(FLUID_DBG, "Selected preset '%s' on channel %d", fluid_preset_get_name(preset), chan);
+        defsfont = fluid_sfont_get_data(preset->sfont);
+        load_preset_samples(defsfont, preset);
+    }
+    else if(reason == FLUID_PRESET_UNSELECTED)
+    {
+        FLUID_LOG(FLUID_DBG, "Deselected preset '%s' from channel %d", fluid_preset_get_name(preset), chan);
+        defsfont = fluid_sfont_get_data(preset->sfont);
+        unload_preset_samples(defsfont, preset);
+    }
 
-unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4,
-  Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0
-};
+    return FLUID_OK;
+}
 
-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)
+/* Walk through all samples used by the passed in preset and make sure that the
+ * sample data is loaded for each sample. Used by dynamic sample loading. */
+static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset)
 {
-  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);
-}
+    fluid_defpreset_t *defpreset;
+    fluid_preset_zone_t *preset_zone;
+    fluid_inst_t *inst;
+    fluid_inst_zone_t *inst_zone;
+    fluid_sample_t *sample;
+    SFData *sffile = NULL;
 
-/* free all elements of a zone (Preset or Instrument) */
-void
-sfont_free_zone (SFZone * zone)
-{
-  fluid_list_t *p;
+    defpreset = fluid_preset_get_data(preset);
+    preset_zone = fluid_defpreset_get_zone(defpreset);
 
-  if (!zone)
-    return;
+    while(preset_zone != NULL)
+    {
+        inst = fluid_preset_zone_get_inst(preset_zone);
+        inst_zone = fluid_inst_get_zone(inst);
+
+        while(inst_zone != NULL)
+        {
+            sample = fluid_inst_zone_get_sample(inst_zone);
+
+            if((sample != NULL) && (sample->start != sample->end))
+            {
+                sample->preset_count++;
+
+                /* If this is the first time this sample has been selected,
+                 * load the sampledata */
+                if(sample->preset_count == 1)
+                {
+                    /* Make sure we have an open Soundfont file. Do this here
+                     * to avoid having to open the file if no loading is necessary
+                     * for a preset */
+                    if(sffile == NULL)
+                    {
+                        sffile = fluid_sffile_open(defsfont->filename, defsfont->fcbs);
+
+                        if(sffile == NULL)
+                        {
+                            FLUID_LOG(FLUID_ERR, "Unable to open Soundfont file");
+                            return FLUID_FAILED;
+                        }
+                    }
+
+                    if(fluid_defsfont_load_sampledata(defsfont, sffile, sample) == FLUID_OK)
+                    {
+                        fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short));
+                        fluid_voice_optimize_sample(sample);
+                    }
+                    else
+                    {
+                        FLUID_LOG(FLUID_ERR, "Unable to load sample '%s', disabling", sample->name);
+                        sample->start = sample->end = 0;
+                    }
+                }
+            }
+
+            inst_zone = fluid_inst_zone_next(inst_zone);
+        }
 
-  p = zone->gen;
-  while (p)
-    {                          /* Free gen chunks for this zone */
-      if (p->data)
-       FLUID_FREE (p->data);
-      p = fluid_list_next (p);
+        preset_zone = fluid_preset_zone_next(preset_zone);
     }
-  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);
+    if(sffile != NULL)
+    {
+        fluid_sffile_close(sffile);
     }
-  delete_fluid_list (zone->mod);       /* free modlist */
 
-  FLUID_FREE (zone);   /* free zone chunk */
+    return FLUID_OK;
 }
 
-/* preset sort function, first by bank, then by preset # */
-int
-sfont_preset_compare_func (void* a, void* b)
+/* Walk through all samples used by the passed in preset and unload the sample data
+ * of each sample that is not used by any selected preset anymore. Used by dynamic
+ * sample loading. */
+static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset)
 {
-  int aval, bval;
+    fluid_defpreset_t *defpreset;
+    fluid_preset_zone_t *preset_zone;
+    fluid_inst_t *inst;
+    fluid_inst_zone_t *inst_zone;
+    fluid_sample_t *sample;
+
+    defpreset = fluid_preset_get_data(preset);
+    preset_zone = fluid_defpreset_get_zone(defpreset);
 
-  aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum;
-  bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum;
+    while(preset_zone != NULL)
+    {
+        inst = fluid_preset_zone_get_inst(preset_zone);
+        inst_zone = fluid_inst_get_zone(inst);
+
+        while(inst_zone != NULL)
+        {
+            sample = fluid_inst_zone_get_sample(inst_zone);
+
+            if((sample != NULL) && (sample->preset_count > 0))
+            {
+                sample->preset_count--;
+
+                /* If the sample is not used by any preset or used by a
+                 * sounding voice, unload it from the sample cache. If it's
+                 * still in use by a voice, dynamic_samples_sample_notify will
+                 * take care of unloading the sample as soon as the voice is
+                 * finished with it (but only on the next API call). */
+                if(sample->preset_count == 0 && sample->refcount == 0)
+                {
+                    unload_sample(sample);
+                }
+            }
+
+            inst_zone = fluid_inst_zone_next(inst_zone);
+        }
 
-  return (aval - bval);
+        preset_zone = fluid_preset_zone_next(preset_zone);
+    }
+
+    return FLUID_OK;
 }
 
-/* delete zone from zone list */
-void
-sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone)
+/* Unload an unused sample from the samplecache */
+static void unload_sample(fluid_sample_t *sample)
 {
-  *zlist = fluid_list_remove (*zlist, (void*) zone);
-  sfont_free_zone (zone);
-}
+    fluid_return_if_fail(sample != NULL);
+    fluid_return_if_fail(sample->data != NULL);
+    fluid_return_if_fail(sample->preset_count == 0);
+    fluid_return_if_fail(sample->refcount == 0);
 
-/* 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;
+    FLUID_LOG(FLUID_DBG, "Unloading sample '%s'", sample->name);
 
-  p = genlist;
-  while (p)
+    if(fluid_samplecache_unload(sample->data) == FLUID_FAILED)
     {
-      if (p->data == NULL)
-       return (NULL);
-      if (gen == ((SFGen *) p->data)->id)
-       break;
-      p = fluid_list_next (p);
+        FLUID_LOG(FLUID_ERR, "Unable to unload sample '%s'", sample->name);
+    }
+    else
+    {
+        sample->data = NULL;
+        sample->data24 = NULL;
     }
-  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, ...)
+static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx)
 {
-  va_list args;
-
-  va_start (args, fmt);
-  vprintf(fmt, args);
-  va_end (args);
-
-  printf("\n");
+    fluid_list_t *list;
+    fluid_inst_t *inst;
 
-  return (FAIL);
-}
+    for(list = defsfont->inst; list != NULL; list = fluid_list_next(list))
+    {
+        inst = fluid_list_get(list);
 
-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);
-}
+        if(inst->source_idx == idx)
+        {
+            return inst;
+        }
+    }
 
-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);
+    return NULL;
 }
index 29f3fd9e86b90df50aed52cb03d0af8d6642bdbb..8b24934d816f5e36209bee4b012723fea335a972 100644 (file)
@@ -5,16 +5,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "fluidsynth.h"
 #include "fluidsynth_priv.h"
+#include "fluid_sffile.h"
 #include "fluid_list.h"
+#include "fluid_mod.h"
+#include "fluid_gen.h"
 
 
 
-/********************************************************************************/
-/********************************************************************************/
-/********************************************************************************/
-/********************************************************************************/
-/********************************************************************************/
-
 /*-----------------------------------sfont.h----------------------------*/
 
 #define SF_SAMPMODES_LOOP      1
 
 #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
@@ -367,7 +52,26 @@ 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;
+typedef struct _fluid_inst_zone_t fluid_inst_zone_t;            /**< Soundfont Instrument Zone */
+typedef struct _fluid_voice_zone_t fluid_voice_zone_t;
+
+/* defines the velocity and key range for a zone */
+struct _fluid_zone_range_t
+{
+    int keylo;
+    int keyhi;
+    int vello;
+    int velhi;
+    unsigned char ignore;      /* set to TRUE for legato playing to ignore this range zone */
+};
+
+/* Stored on a preset zone to keep track of the inst zones that could start a voice
+ * and their combined preset zone/instument zone ranges */
+struct _fluid_voice_zone_t
+{
+    fluid_inst_zone_t *inst_zone;
+    fluid_zone_range_t range;
+};
 
 /*
 
@@ -375,57 +79,62 @@ typedef struct _fluid_inst_zone_t fluid_inst_zone_t;
 
  */
 
-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);
+fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename);
 
 
-int fluid_defsfont_sfont_delete(fluid_sfont_tsfont);
-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_tsfont);
-int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset);
+int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont);
+const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont);
+fluid_preset_t *fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum);
+void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont);
+fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont);
 
 
-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_tpreset);
-int fluid_defpreset_preset_get_num(fluid_preset_tpreset);
-int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+void fluid_defpreset_preset_delete(fluid_preset_t *preset);
+const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset);
+int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset);
+int fluid_defpreset_preset_get_num(fluid_preset_t *preset);
+int fluid_defpreset_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel);
 
+int fluid_zone_inside_range(fluid_zone_range_t *zone_range, 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 */
+    const fluid_file_callbacks_t *fcbs; /* the file callbacks used to load this Soundfont */
+    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 in bytes */
+    short *sampledata;        /* the sample data, loaded in ram */
+
+    unsigned int sample24pos;          /* position within sffd of the sm24 chunk, set to zero if no 24 bit sample support */
+    unsigned int sample24size;         /* length within sffd of the sm24 chunk */
+    char *sample24data;        /* if not NULL, the least significant byte of the 24bit sample data, loaded in ram */
+
+    fluid_sfont_t *sfont;      /* pointer to parent sfont */
+    fluid_list_t *sample;      /* the samples in this soundfont */
+    fluid_list_t *preset;      /* the presets of this soundfont */
+    fluid_list_t *inst;        /* the instruments of this soundfont */
+    int mlock;                 /* Should we try memlock (avoid swapping)? */
+    int dynamic_samples;       /* Enables dynamic sample loading if set */
+
+    fluid_list_t *preset_iter_cur;       /* the current preset in the iteration */
 };
 
 
-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_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings);
+int delete_fluid_defsfont(fluid_defsfont_t *defsfont);
+int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *file_callbacks, const char *file);
+const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont);
+fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int prenum);
+void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont);
+fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont);
+int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample);
+int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata);
+
+int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample);
+int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset);
 
 
 /*
@@ -433,98 +142,90 @@ int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset
  */
 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 *next;
+    fluid_defsfont_t *defsfont;           /* 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_tpreset);
-int fluid_defpreset_get_num(fluid_defpreset_tpreset);
-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_defpreset_t *new_fluid_defpreset(fluid_defsfont_t *defsfont);
+void delete_fluid_defpreset(fluid_defpreset_t *defpreset);
+fluid_defpreset_t *fluid_defpreset_next(fluid_defpreset_t *defpreset);
+int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont);
+int fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone);
+int fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone);
+fluid_preset_zone_t *fluid_defpreset_get_zone(fluid_defpreset_t *defpreset);
+fluid_preset_zone_t *fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset);
+int fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset);
+int fluid_defpreset_get_num(fluid_defpreset_t *defpreset);
+const char *fluid_defpreset_get_name(fluid_defpreset_t *defpreset);
+int fluid_defpreset_noteon(fluid_defpreset_t *defpreset, 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 *next;
+    char *name;
+    fluid_inst_t *inst;
+    fluid_list_t *voice_zone;
+    fluid_zone_range_t range;
+    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_preset_zone_t *new_fluid_preset_zone(char *name);
+void delete_fluid_preset_zone(fluid_preset_zone_t *zone);
+fluid_preset_zone_t *fluid_preset_zone_next(fluid_preset_zone_t *zone);
+int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defssfont);
+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;
+    char name[21];
+    int source_idx; /* Index of instrument in source Soundfont */
+    fluid_inst_zone_t *global_zone;
+    fluid_inst_zone_t *zone;
 };
 
-fluid_inst_tnew_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_t *new_fluid_inst(void);
+fluid_inst_t *fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_defsfont_t *defsfont);
+void delete_fluid_inst(fluid_inst_t *inst);
+int fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone);
+int fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone);
+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 *next;
+    char *name;
+    fluid_sample_t *sample;
+    fluid_zone_range_t range;
+    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_inst_zone_t *new_fluid_inst_zone(char *name);
+void delete_fluid_inst_zone(fluid_inst_zone_t *zone);
+fluid_inst_zone_t *fluid_inst_zone_next(fluid_inst_zone_t *zone);
+int fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont);
+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);
+int fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont);
+int fluid_sample_in_rom(fluid_sample_t *sample);
 
 
 #endif  /* _FLUID_SFONT_H */
index b3b060834530719a4bd3d081f46c78aefcb95dee..d7962eac98352a633b7fccdd6957e3f0af23f8e7 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -28,7 +28,7 @@
 */
 
 
-#include "fluid_event_priv.h"
+#include "fluid_event.h"
 #include "fluidsynth_priv.h"
 
 /***************************************************************
 /* Event alloc/free */
 
 void
-fluid_event_clear(fluid_event_tevt)
+fluid_event_clear(fluid_event_t *evt)
 {
-  FLUID_MEMSET(evt, 0, sizeof(fluid_event_t));
+    FLUID_MEMSET(evt, 0, sizeof(fluid_event_t));
 
-  // by default, no type
-  evt->dest = -1;
-  evt->src = -1;
-  evt->type = -1;
+    // 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*
+fluid_event_t *
 new_fluid_event()
 {
-  fluid_event_t* evt;
+    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);
+    evt = FLUID_NEW(fluid_event_t);
 
-  return(evt);
+    if(evt == NULL)
+    {
+        FLUID_LOG(FLUID_PANIC, "event: Out of memory\n");
+        return NULL;
+    }
+
+    fluid_event_clear(evt);
+
+    return(evt);
 }
 
 /**
@@ -73,14 +76,11 @@ new_fluid_event()
  * @param evt Sequencer event structure created by new_fluid_event().
  */
 void
-delete_fluid_event(fluid_event_tevt)
+delete_fluid_event(fluid_event_t *evt)
 {
+    fluid_return_if_fail(evt != NULL);
 
-  if (evt == NULL) {
-    return;
-  }
-
-  FLUID_FREE(evt);
+    FLUID_FREE(evt);
 }
 
 /**
@@ -90,43 +90,43 @@ delete_fluid_event(fluid_event_t* evt)
  * @param time Time value to assign
  */
 void
-fluid_event_set_time(fluid_event_tevt, unsigned int time)
+fluid_event_set_time(fluid_event_t *evt, unsigned int time)
 {
-       evt->time = time;
+    evt->time = time;
 }
 
 /**
- * Set source of a sequencer event (DOCME).
+ * Set source of a sequencer event. \c src must be a unique sequencer ID or -1 if not set.
  * @param evt Sequencer event structure
- * @param src DOCME
+ * @param src Unique sequencer ID
  */
 void
-fluid_event_set_source(fluid_event_t* evt, short src)
+fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src)
 {
-       evt->src = src;
+    evt->src = src;
 }
 
 /**
- * Set destination of a sequencer event (DOCME).
+ * Set destination of this sequencer event, i.e. the sequencer client this event will be sent to. \c dest must be a unique sequencer ID.
  * @param evt Sequencer event structure
- * @param dest DOCME
+ * @param dest The destination unique sequencer ID
  */
 void
-fluid_event_set_dest(fluid_event_t* evt, short dest)
+fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest)
 {
-       evt->dest = dest;
+    evt->dest = dest;
 }
 
 /**
  * Set a sequencer event to be a timer event.
  * @param evt Sequencer event structure
- * @param data DOCME
+ * @param data User supplied data pointer
  */
 void
-fluid_event_timer(fluid_event_t* evt, void* data)
+fluid_event_timer(fluid_event_t *evt, void *data)
 {
-       evt->type = FLUID_SEQ_TIMER;
-       evt->data = data;
+    evt->type = FLUID_SEQ_TIMER;
+    evt->data = data;
 }
 
 /**
@@ -137,12 +137,12 @@ fluid_event_timer(fluid_event_t* evt, void* data)
  * @param vel MIDI velocity value (0-127)
  */
 void
-fluid_event_noteon(fluid_event_tevt, int channel, short key, short vel)
+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;
+    evt->type = FLUID_SEQ_NOTEON;
+    evt->channel = channel;
+    evt->key = key;
+    evt->vel = vel;
 }
 
 /**
@@ -152,11 +152,11 @@ fluid_event_noteon(fluid_event_t* evt, int channel, short key, short vel)
  * @param key MIDI note number (0-127)
  */
 void
-fluid_event_noteoff(fluid_event_tevt, int channel, short key)
+fluid_event_noteoff(fluid_event_t *evt, int channel, short key)
 {
-       evt->type = FLUID_SEQ_NOTEOFF;
-       evt->channel = channel;
-       evt->key = key;
+    evt->type = FLUID_SEQ_NOTEOFF;
+    evt->channel = channel;
+    evt->key = key;
 }
 
 /**
@@ -165,16 +165,16 @@ fluid_event_noteoff(fluid_event_t* evt, int channel, short key)
  * @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?)
+ * @param duration Duration of note in the time scale used by the sequencer (by default milliseconds)
  */
 void
-fluid_event_note(fluid_event_tevt, int channel, short key, short vel, unsigned int duration)
+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;
+    evt->type = FLUID_SEQ_NOTE;
+    evt->channel = channel;
+    evt->key = key;
+    evt->vel = vel;
+    evt->duration = duration;
 }
 
 /**
@@ -183,10 +183,10 @@ fluid_event_note(fluid_event_t* evt, int channel, short key, short vel, unsigned
  * @param channel MIDI channel number
  */
 void
-fluid_event_all_sounds_off(fluid_event_tevt, int channel)
+fluid_event_all_sounds_off(fluid_event_t *evt, int channel)
 {
-       evt->type = FLUID_SEQ_ALLSOUNDSOFF;
-       evt->channel = channel;
+    evt->type = FLUID_SEQ_ALLSOUNDSOFF;
+    evt->channel = channel;
 }
 
 /**
@@ -195,10 +195,10 @@ fluid_event_all_sounds_off(fluid_event_t* evt, int channel)
  * @param channel MIDI channel number
  */
 void
-fluid_event_all_notes_off(fluid_event_tevt, int channel)
+fluid_event_all_notes_off(fluid_event_t *evt, int channel)
 {
-       evt->type = FLUID_SEQ_ALLNOTESOFF;
-       evt->channel = channel;
+    evt->type = FLUID_SEQ_ALLNOTESOFF;
+    evt->channel = channel;
 }
 
 /**
@@ -208,11 +208,11 @@ fluid_event_all_notes_off(fluid_event_t* evt, int channel)
  * @param bank_num MIDI bank number (0-16383)
  */
 void
-fluid_event_bank_select(fluid_event_tevt, int channel, short bank_num)
+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;
+    evt->type = FLUID_SEQ_BANKSELECT;
+    evt->channel = channel;
+    evt->control = bank_num;
 }
 
 /**
@@ -222,11 +222,11 @@ fluid_event_bank_select(fluid_event_t* evt, int channel, short bank_num)
  * @param val MIDI program number (0-127)
  */
 void
-fluid_event_program_change(fluid_event_tevt, int channel, short val)
+fluid_event_program_change(fluid_event_t *evt, int channel, short val)
 {
-       evt->type = FLUID_SEQ_PROGRAMCHANGE;
-       evt->channel = channel;
-       evt->value = val;
+    evt->type = FLUID_SEQ_PROGRAMCHANGE;
+    evt->channel = channel;
+    evt->value = val;
 }
 
 /**
@@ -238,27 +238,26 @@ fluid_event_program_change(fluid_event_t* evt, int channel, short val)
  * @param preset_num MIDI preset number (0-127)
  */
 void
-fluid_event_program_select(fluid_event_tevt, int channel,
-                                                 unsigned int sfont_id, short bank_num, short preset_num)
+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;
+    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.
+ * Set a sequencer event to be an any control change event (for internal use).
  * @param evt Sequencer event structure
  * @param channel MIDI channel number
- * DOCME
  */
 void
-fluid_event_any_control_change(fluid_event_tevt, int channel)
+fluid_event_any_control_change(fluid_event_t *evt, int channel)
 {
-       evt->type = FLUID_SEQ_ANYCONTROLCHANGE;
-       evt->channel = channel;
+    evt->type = FLUID_SEQ_ANYCONTROLCHANGE;
+    evt->channel = channel;
 }
 
 /**
@@ -268,27 +267,36 @@ fluid_event_any_control_change(fluid_event_t* evt, int channel)
  * @param pitch MIDI pitch bend value (0-16383, 8192 = no bend)
  */
 void
-fluid_event_pitch_bend(fluid_event_tevt, int channel, int pitch)
+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;
+    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?)
+ * @param value MIDI pitch wheel sensitivity value in semitones
  */
 void
-fluid_event_pitch_wheelsens(fluid_event_tevt, int channel, short value)
+fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, short value)
 {
-       evt->type = FLUID_SEQ_PITCHWHHELSENS;
-       evt->channel = channel;
-       evt->value = value;
+    evt->type = FLUID_SEQ_PITCHWHEELSENS;
+    evt->channel = channel;
+    evt->value = value;
 }
 
 /**
@@ -298,13 +306,22 @@ fluid_event_pitch_wheelsens(fluid_event_t* evt, int channel, short value)
  * @param val MIDI modulation value (0-127)
  */
 void
-fluid_event_modulation(fluid_event_tevt, int channel, short val)
+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;
+    evt->type = FLUID_SEQ_MODULATION;
+    evt->channel = channel;
+
+    if(val < 0)
+    {
+        val = 0;
+    }
+
+    if(val > 127)
+    {
+        val = 127;
+    }
+
+    evt->value = val;
 }
 
 /**
@@ -314,13 +331,22 @@ fluid_event_modulation(fluid_event_t* evt, int channel, short val)
  * @param val MIDI sustain value (0-127)
  */
 void
-fluid_event_sustain(fluid_event_tevt, int channel, short val)
+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;
+    evt->type = FLUID_SEQ_SUSTAIN;
+    evt->channel = channel;
+
+    if(val < 0)
+    {
+        val = 0;
+    }
+
+    if(val > 127)
+    {
+        val = 127;
+    }
+
+    evt->value = val;
 }
 
 /**
@@ -328,15 +354,15 @@ fluid_event_sustain(fluid_event_t* evt, int channel, short val)
  * @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?)
+ * @param val MIDI control value (0-127)
  */
 void
-fluid_event_control_change(fluid_event_tevt, int channel, short control, short val)
+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;
+    evt->type = FLUID_SEQ_CONTROLCHANGE;
+    evt->channel = channel;
+    evt->control = control;
+    evt->value = val;
 }
 
 /**
@@ -346,13 +372,22 @@ fluid_event_control_change(fluid_event_t* evt, int channel, short control, short
  * @param val MIDI panning value (0-127, 0=left, 64 = middle, 127 = right)
  */
 void
-fluid_event_pan(fluid_event_tevt, int channel, short val)
+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;
+    evt->type = FLUID_SEQ_PAN;
+    evt->channel = channel;
+
+    if(val < 0)
+    {
+        val = 0;
+    }
+
+    if(val > 127)
+    {
+        val = 127;
+    }
+
+    evt->value = val;
 }
 
 /**
@@ -362,13 +397,22 @@ fluid_event_pan(fluid_event_t* evt, int channel, short val)
  * @param val Volume value (0-127)
  */
 void
-fluid_event_volume(fluid_event_tevt, int channel, short val)
+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;
+    evt->type = FLUID_SEQ_VOLUME;
+    evt->channel = channel;
+
+    if(val < 0)
+    {
+        val = 0;
+    }
+
+    if(val > 127)
+    {
+        val = 127;
+    }
+
+    evt->value = val;
 }
 
 /**
@@ -378,13 +422,22 @@ fluid_event_volume(fluid_event_t* evt, int channel, short val)
  * @param val Reverb amount (0-127)
  */
 void
-fluid_event_reverb_send(fluid_event_tevt, int channel, short val)
+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;
+    evt->type = FLUID_SEQ_REVERBSEND;
+    evt->channel = channel;
+
+    if(val < 0)
+    {
+        val = 0;
+    }
+
+    if(val > 127)
+    {
+        val = 127;
+    }
+
+    evt->value = val;
 }
 
 /**
@@ -394,13 +447,22 @@ fluid_event_reverb_send(fluid_event_t* evt, int channel, short val)
  * @param val Chorus amount (0-127)
  */
 void
-fluid_event_chorus_send(fluid_event_tevt, int channel, short val)
+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;
+    evt->type = FLUID_SEQ_CHORUSSEND;
+    evt->channel = channel;
+
+    if(val < 0)
+    {
+        val = 0;
+    }
+
+    if(val > 127)
+    {
+        val = 127;
+    }
+
+    evt->value = val;
 }
 
 
@@ -410,9 +472,9 @@ fluid_event_chorus_send(fluid_event_t* evt, int channel, short val)
  * @since 1.1.0
  */
 void
-fluid_event_unregistering(fluid_event_tevt)
+fluid_event_unregistering(fluid_event_t *evt)
 {
-       evt->type = FLUID_SEQ_UNREGISTERING;
+    evt->type = FLUID_SEQ_UNREGISTERING;
 }
 
 /**
@@ -422,14 +484,61 @@ fluid_event_unregistering(fluid_event_t* evt)
  * @param val Aftertouch amount (0-127)
  * @since 1.1.0
  */
-void 
-fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val)
+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 polyphonic aftertouch event.
+ * @param evt Sequencer event structure
+ * @param channel MIDI channel number
+ * @param key MIDI note number (0-127)
+ * @param val Aftertouch amount (0-127)
+ * @since 2.0.0
+ */
+void
+fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, short val)
 {
-       evt->type = FLUID_SEQ_CHANNELPRESSURE;
-       evt->channel = channel;
-       if (val < 0) val = 0;
-       if (val > 127) val = 127;
-       evt->value = val;
+    evt->type = FLUID_SEQ_KEYPRESSURE;
+    evt->channel = channel;
+
+    if(key < 0)
+    {
+        key = 0;
+    }
+
+    if(key > 127)
+    {
+        key = 127;
+    }
+
+    if(val < 0)
+    {
+        val = 0;
+    }
+
+    if(val > 127)
+    {
+        val = 127;
+    }
+
+    evt->key = key;
+    evt->value = val;
 }
 
 /**
@@ -437,10 +546,10 @@ fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val)
  * @param evt Sequencer event structure
  * @since 1.1.0
  */
-void 
-fluid_event_system_reset(fluid_event_tevt)
+void
+fluid_event_system_reset(fluid_event_t *evt)
 {
-       evt->type = FLUID_SEQ_SYSTEMRESET;
+    evt->type = FLUID_SEQ_SYSTEMRESET;
 }
 
 
@@ -454,49 +563,50 @@ fluid_event_system_reset(fluid_event_t* evt)
  * @param evt Sequencer event structure
  * @return Event type (#fluid_seq_event_type).
  */
-int fluid_event_get_type(fluid_event_tevt)
+int fluid_event_get_type(fluid_event_t *evt)
 {
-       return evt->type;
+    return evt->type;
 }
 
 /**
+ * @internal
  * Get the time field from a sequencer event structure.
  * @param evt Sequencer event structure
- * @return Time value (DOCME units?)
+ * @return Time value
  */
-unsigned int fluid_event_get_time(fluid_event_tevt)
+unsigned int fluid_event_get_time(fluid_event_t *evt)
 {
-       return evt->time;
+    return evt->time;
 }
 
 /**
- * Get the source field from a sequencer event structure.
+ * Get the source sequencer client from a sequencer event structure.
  * @param evt Sequencer event structure
- * @return DOCME
+ * @return source field of the sequencer event
  */
-short fluid_event_get_source(fluid_event_t* evt)
+fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt)
 {
-       return evt->src;
+    return evt->src;
 }
 
 /**
- * Get the dest field from a sequencer event structure.
+ * Get the dest sequencer client from a sequencer event structure.
  * @param evt Sequencer event structure
- * @return DOCME
+ * @return dest field of the sequencer event
  */
-short fluid_event_get_dest(fluid_event_t* evt)
+fluid_seq_id_t fluid_event_get_dest(fluid_event_t *evt)
 {
-       return evt->dest;
+    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?)
+ * @return MIDI zero-based channel number
  */
-int fluid_event_get_channel(fluid_event_tevt)
+int fluid_event_get_channel(fluid_event_t *evt)
 {
-       return evt->channel;
+    return evt->channel;
 }
 
 /**
@@ -504,9 +614,9 @@ int fluid_event_get_channel(fluid_event_t* evt)
  * @param evt Sequencer event structure
  * @return MIDI note number (0-127)
  */
-short fluid_event_get_key(fluid_event_tevt)
+short fluid_event_get_key(fluid_event_t *evt)
 {
-       return evt->key;
+    return evt->key;
 }
 
 /**
@@ -514,10 +624,10 @@ short fluid_event_get_key(fluid_event_t* evt)
  * @param evt Sequencer event structure
  * @return MIDI velocity value (0-127)
  */
-short fluid_event_get_velocity(fluid_event_tevt)
+short fluid_event_get_velocity(fluid_event_t *evt)
 
 {
-       return evt->vel;
+    return evt->vel;
 }
 
 /**
@@ -525,9 +635,9 @@ short fluid_event_get_velocity(fluid_event_t* evt)
  * @param evt Sequencer event structure
  * @return MIDI control number (0-127)
  */
-short fluid_event_get_control(fluid_event_tevt)
+short fluid_event_get_control(fluid_event_t *evt)
 {
-       return evt->control;
+    return evt->control;
 }
 
 /**
@@ -537,13 +647,13 @@ short fluid_event_get_control(fluid_event_t* evt)
  *
  * 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_PITCHWHEELSENS, #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_tevt)
+short fluid_event_get_value(fluid_event_t *evt)
 {
-       return evt->value;
+    return evt->value;
 }
 
 /**
@@ -553,21 +663,21 @@ short fluid_event_get_value(fluid_event_t* evt)
  *
  * Used by the #FLUID_SEQ_TIMER event type.
  */
-void* fluid_event_get_data(fluid_event_t* evt)
+void *fluid_event_get_data(fluid_event_t *evt)
 {
-       return evt->data;
+    return evt->data;
 }
 
 /**
  * Get the duration field from a sequencer event structure.
  * @param evt Sequencer event structure
- * @return Note duration value (DOCME units?)
+ * @return Note duration value in the time scale used by the sequencer (by default milliseconds)
  *
  * Used by the #FLUID_SEQ_NOTE event type.
  */
-unsigned int fluid_event_get_duration(fluid_event_tevt)
+unsigned int fluid_event_get_duration(fluid_event_t *evt)
 {
-       return evt->duration;
+    return evt->duration;
 }
 
 /**
@@ -578,9 +688,9 @@ unsigned int fluid_event_get_duration(fluid_event_t* evt)
  * Used by the #FLUID_SEQ_BANKSELECT and #FLUID_SEQ_PROGRAMSELECT
  * event types.
  */
-short fluid_event_get_bank(fluid_event_tevt)
+short fluid_event_get_bank(fluid_event_t *evt)
 {
-       return evt->control;
+    return evt->control;
 }
 
 /**
@@ -590,9 +700,9 @@ short fluid_event_get_bank(fluid_event_t* evt)
  *
  * Used by the #FLUID_SEQ_PITCHBEND event type.
  */
-int fluid_event_get_pitch(fluid_event_tevt)
+int fluid_event_get_pitch(fluid_event_t *evt)
 {
-       return evt->pitch;
+    return evt->pitch;
 }
 
 /**
@@ -604,9 +714,9 @@ int fluid_event_get_pitch(fluid_event_t* evt)
  * event types.
  */
 short
-fluid_event_get_program(fluid_event_tevt)
+fluid_event_get_program(fluid_event_t *evt)
 {
-       return evt->value;
+    return evt->value;
 }
 
 /**
@@ -617,9 +727,9 @@ fluid_event_get_program(fluid_event_t* evt)
  * Used by the #FLUID_SEQ_PROGRAMSELECT event type.
  */
 unsigned int
-fluid_event_get_sfont_id(fluid_event_tevt)
+fluid_event_get_sfont_id(fluid_event_t *evt)
 {
-       return evt->duration;
+    return evt->duration;
 }
 
 
@@ -628,154 +738,176 @@ fluid_event_get_sfont_id(fluid_event_t* evt)
 /* heap management  */
 /********************/
 
-fluid_evt_heap_t*
+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;
+    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 = FLUID_NEW(fluid_evt_heap_t);
 
-  heap->freelist = NULL;
-  fluid_mutex_init(heap->mutex);
+    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);
+    /* 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;
-  }
+    /* 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);
+    /* 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);
-  }
+    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);
+    return (heap);
 }
 
 void
-_fluid_evt_heap_free(fluid_evt_heap_theap)
+_fluid_evt_heap_free(fluid_evt_heap_t *heap)
 {
 #ifdef HEAP_WITH_DYNALLOC
-  fluid_evt_entry *tmp, *next;
+    fluid_evt_entry *tmp, *next;
+
+    /* LOCK */
+    fluid_mutex_lock(heap->mutex);
 
-  /* LOCK */
-  fluid_mutex_lock(heap->mutex);
+    tmp = heap->freelist;
 
-  tmp = heap->freelist;
-  while (tmp) {
-    next = tmp->next;
-    FLUID_FREE(tmp);
-    tmp = next;
-  }
+    while(tmp)
+    {
+        next = tmp->next;
+        FLUID_FREE(tmp);
+        tmp = next;
+    }
 
-  /* UNLOCK */
-  fluid_mutex_unlock(heap->mutex);
-  fluid_mutex_destroy(heap->mutex);
+    /* UNLOCK */
+    fluid_mutex_unlock(heap->mutex);
+    fluid_mutex_destroy(heap->mutex);
 
-  FLUID_FREE(heap);
+    FLUID_FREE(heap);
 
 #else
-       FLUID_FREE(heap);
+    FLUID_FREE(heap);
 #endif
 }
 
-fluid_evt_entry*
-_fluid_seq_heap_get_free(fluid_evt_heap_theap)
+fluid_evt_entry *
+_fluid_seq_heap_get_free(fluid_evt_heap_t *heap)
 {
 #ifdef HEAP_WITH_DYNALLOC
-  fluid_evt_entry* evt = NULL;
+    fluid_evt_entry *evt = NULL;
 
-  /* LOCK */
-  fluid_mutex_lock(heap->mutex);
+    /* 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;
+
+    if(heap->freelist == NULL)
+    {
+        heap->freelist = FLUID_NEW(fluid_evt_entry);
+
+        if(heap->freelist != NULL)
+        {
+            heap->freelist->next = NULL;
+        }
     }
-  }
+
 #endif
 
-  evt = heap->freelist;
+    evt = heap->freelist;
 
-  if (evt != NULL) {
-    heap->freelist = heap->freelist->next;
-    evt->next = NULL;
-  }
+    if(evt != NULL)
+    {
+        heap->freelist = heap->freelist->next;
+        evt->next = NULL;
+    }
 
-  /* UNLOCK */
-  fluid_mutex_unlock(heap->mutex);
+    /* UNLOCK */
+    fluid_mutex_unlock(heap->mutex);
 
-  return evt;
+    return evt;
 
 #else
-       fluid_evt_entry* evt;
-       if (heap->head == NULL) return NULL;
+    fluid_evt_entry *evt;
 
-       /* take from head of the heap */
-       /* critical - should threadlock ? */
-       evt = heap->head;
-       heap->head = heap->head->next;
+    if(heap->head == NULL)
+    {
+        return NULL;
+    }
 
-       return evt;
+    /* 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)
+_fluid_seq_heap_set_free(fluid_evt_heap_t *heap, fluid_evt_entry *evt)
 {
 #ifdef HEAP_WITH_DYNALLOC
 
-  /* LOCK */
-  fluid_mutex_lock(heap->mutex);
+    /* LOCK */
+    fluid_mutex_lock(heap->mutex);
 
-  evt->next = heap->freelist;
-  heap->freelist = evt;
+    evt->next = heap->freelist;
+    heap->freelist = evt;
 
-  /* UNLOCK */
-  fluid_mutex_unlock(heap->mutex);
+    /* 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;
+    /* 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.h b/libs/fluidsynth/src/fluid_event.h
new file mode 100644 (file)
index 0000000..4beb014
--- /dev/null
@@ -0,0 +1,87 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003  Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#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;
+    fluid_seq_id_t src;
+    fluid_seq_id_t 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 */
index 0f1413eab2e80707f5ca5cc1338a64f84e481a03..f01f94194278b0d7128ff5175ca6e73d5b3546a9 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 
 /* 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 }
+static const 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 },
+    { GEN_CUSTOM_BALANCE,         1,     0,    -960.0f,    960.0f,       0.0f },
+    { GEN_CUSTOM_FILTERFC,        1,     2,       0.0f,  22050.0f,       0.0f },
+    { GEN_CUSTOM_FILTERQ,         1,     1,       0.0f,    960.0f,       0.0f }
 };
 
 
 /**
  * Set an array of generators to their default values.
  * @param gen Array of generators (should be #GEN_LAST in size).
- * @return Always returns 0
+ * @return Always returns #FLUID_OK
  */
 int
-fluid_gen_set_default_values(fluid_gen_tgen)
+fluid_gen_set_default_values(fluid_gen_t *gen)
 {
-       int i;
+    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;
-       }
+    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;
+    return FLUID_OK;
 }
 
 
@@ -115,35 +120,37 @@ fluid_gen_set_default_values(fluid_gen_t* gen)
  * Set an array of generators to their initial value
  */
 int
-fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel)
+fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel)
 {
-       int i;
+    int i;
 
-       fluid_gen_set_default_values(gen);
+    fluid_gen_set_default_values(gen);
 
-       for (i = 0; i < GEN_LAST; i++) {
-               gen[i].nrpn = fluid_channel_get_gen(channel, i);
+    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;
-               }
-       }
+        /* 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;
+    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));
+    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;
+    fluid_real_t value = (float) data - 8192.0f;
+    fluid_clip(value, -8192, 8192);
+    return value * (float) fluid_gen_info[gen].nrpn_scale;
 }
index f54d0490fc8329f876c4a4f7a2828967796310bb..d156b807b9ee7d3dd557c87adb915f67b4dd9350 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "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()) */
+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;
 
+/*
+ * 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 */
+};
+
 #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);
+int fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel);
+int fluid_gen_set_default_values(fluid_gen_t *gen);
 
 
 #endif /* _FLUID_GEN_H */
index 9d5a92009e651c107b1697c171a5e1cfc6b3339a..b6586895b50e4a28a70ea565af788264f0bf9ae2 100644 (file)
 
 typedef struct
 {
-  fluid_hashtable_t *hashtable;
-  fluid_hashnode_t *prev_node;
-  fluid_hashnode_t *node;
-  int position;
-  int pre_advanced;    // Boolean
-  int version;
+    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[] =
+static const unsigned int 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,
+    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 const unsigned int nprimes = FLUID_N_ELEMENTS(primes);
 
 static unsigned int
-spaced_primes_closest (unsigned int num)
+spaced_primes_closest(unsigned int num)
 {
-  unsigned int i;
+    unsigned int i;
 
-  for (i = 0; i < nprimes; i++)
-    if (primes[i] > num)
-      return primes[i];
+    for(i = 0; i < nprimes; i++)
+    {
+        if(primes[i] > num)
+        {
+            return primes[i];
+        }
+    }
 
-  return primes[nprimes - 1];
+    return primes[nprimes - 1];
 }
 
 /* End excerpt from glib gprimes.c */
@@ -136,51 +140,57 @@ spaced_primes_closest (unsigned int num)
  * 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)
+static FLUID_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)
+    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)
     {
-      while ((node = *node_ptr))
+        *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;
+            if(node->key_hash == hash_value &&
+                    hashtable->key_equal_func(node->key, key))
+            {
+                break;
+            }
 
-          node_ptr = &(*node_ptr)->next;
+            node_ptr = &(*node_ptr)->next;
         }
     }
-  else
+    else
     {
-      while ((node = *node_ptr))
+        while((node = *node_ptr))
         {
-          if (node->key == key)
-            break;
+            if(node->key == key)
+            {
+                break;
+            }
 
-          node_ptr = &(*node_ptr)->next;
+            node_ptr = &(*node_ptr)->next;
         }
     }
 
-  return node_ptr;
+    return node_ptr;
 }
 
 /*
@@ -213,25 +223,29 @@ fluid_hashtable_lookup_node (fluid_hashtable_t *hashtable, const void *key,
  * modified at all.  Stay tuned. :)
  */
 static void
-fluid_hashtable_remove_node (fluid_hashtable_t *hashtable,
-                             fluid_hashnode_t  ***node_ptr_ptr, int notify)
+fluid_hashtable_remove_node(fluid_hashtable_t *hashtable,
+                            fluid_hashnode_t  ***node_ptr_ptr, int notify)
 {
-  fluid_hashnode_t **node_ptr, *node;
+    fluid_hashnode_t **node_ptr, *node;
 
-  node_ptr = *node_ptr_ptr;
-  node = *node_ptr;
+    node_ptr = *node_ptr_ptr;
+    node = *node_ptr;
 
-  *node_ptr = node->next;
+    *node_ptr = node->next;
 
-  if (notify && hashtable->key_destroy_func)
-    hashtable->key_destroy_func (node->key);
+    if(notify && hashtable->key_destroy_func)
+    {
+        hashtable->key_destroy_func(node->key);
+    }
 
-  if (notify && hashtable->value_destroy_func)
-    hashtable->value_destroy_func (node->value);
+    if(notify && hashtable->value_destroy_func)
+    {
+        hashtable->value_destroy_func(node->value);
+    }
 
-  FLUID_FREE (node);
+    FLUID_FREE(node);
 
-  hashtable->nnodes--;
+    hashtable->nnodes--;
 }
 
 /*
@@ -246,16 +260,20 @@ fluid_hashtable_remove_node (fluid_hashtable_t *hashtable,
  * for the key and value of the hash node.
  */
 static void
-fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify)
+fluid_hashtable_remove_all_nodes(fluid_hashtable_t *hashtable, int notify)
 {
-  fluid_hashnode_t **node_ptr;
-  int i;
+    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);
+    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;
+    hashtable->nnodes = 0;
 }
 
 /*
@@ -268,43 +286,45 @@ fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify)
  * fluid_hashtable_maybe_resize() instead.
  */
 static void
-fluid_hashtable_resize (fluid_hashtable_t *hashtable)
+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;
+    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_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);
+    new_nodes = FLUID_ARRAY(fluid_hashnode_t *, new_size);
 
-  if (!new_nodes)
-  {
-    FLUID_LOG (FLUID_ERR, "Out of memory");
-    return;
-  }
+    if(!new_nodes)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return;
+    }
 
-  FLUID_MEMSET (new_nodes, 0, new_size * sizeof (fluid_hashnode_t *));
+    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;
+    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;
+            hash_val = node->key_hash % new_size;
 
-       node->next = new_nodes[hash_val];
-       new_nodes[hash_val] = node;
-      }
+            node->next = new_nodes[hash_val];
+            new_nodes[hash_val] = node;
+        }
+    }
 
-  FLUID_FREE (hashtable->nodes);
-  hashtable->nodes = new_nodes;
-  hashtable->size = new_size;
+    FLUID_FREE(hashtable->nodes);
+    hashtable->nodes = new_nodes;
+    hashtable->size = new_size;
 }
 
 /*
@@ -316,15 +336,17 @@ fluid_hashtable_resize (fluid_hashtable_t *hashtable)
  * 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)
+static FLUID_INLINE void
+fluid_hashtable_maybe_resize(fluid_hashtable_t *hashtable)
 {
-  int nnodes = hashtable->nnodes;
-  int size = hashtable->size;
+    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);
+    if((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) ||
+            (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE))
+    {
+        fluid_hashtable_resize(hashtable);
+    }
 }
 
 /**
@@ -345,10 +367,10 @@ fluid_hashtable_maybe_resize (fluid_hashtable_t *hashtable)
  *
  * 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)
+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);
+    return new_fluid_hashtable_full(hash_func, key_equal_func, NULL, NULL);
 }
 
 
@@ -369,33 +391,39 @@ new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_f
  *
  * 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 *
+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;
+    fluid_hashtable_t *hashtable;
 
-  hashtable = FLUID_NEW (fluid_hashtable_t);
+    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;
+    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;
+    fluid_atomic_int_set(&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);
+    if(hashtable->nodes == NULL)
+    {
+        delete_fluid_hashtable(hashtable);
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+    FLUID_MEMSET(hashtable->nodes, 0, hashtable->size * sizeof(*hashtable->nodes));
+
+    return hashtable;
 }
 
 /**
@@ -411,7 +439,7 @@ new_fluid_hashtable_full (fluid_hash_func_t hash_func,
  * gpointer key, value;
  *
  * fluid_hashtable_iter_init (&iter, hashtable);
- * while (fluid_hashtable_iter_next (&iter, &key, &value)) 
+ * while (fluid_hashtable_iter_next (&iter, &key, &value))
  *   {
  *     /&ast; do something with key and value &ast;/
  *   }
@@ -420,19 +448,19 @@ new_fluid_hashtable_full (fluid_hash_func_t hash_func,
  * Since: 2.16
  **/
 void
-fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter,
-                           fluid_hashtable_t *hashtable)
+fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter,
+                          fluid_hashtable_t *hashtable)
 {
-  RealIter *ri = (RealIter *) iter;
+    RealIter *ri = (RealIter *) iter;
 
-  fluid_return_if_fail (iter != NULL);
-  fluid_return_if_fail (hashtable != NULL);
+    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;
+    ri->hashtable = hashtable;
+    ri->prev_node = NULL;
+    ri->node = NULL;
+    ri->position = -1;
+    ri->pre_advanced = FALSE;
 }
 
 /**
@@ -450,45 +478,55 @@ fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter,
  * Since: 2.16
  **/
 int
-fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key,
-                           void **value)
+fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key,
+                          void **value)
 {
-  RealIter *ri = (RealIter *) iter;
+    RealIter *ri = (RealIter *) iter;
 
-  fluid_return_val_if_fail (iter != NULL, FALSE);
+    fluid_return_val_if_fail(iter != NULL, FALSE);
 
-  if (ri->pre_advanced)
+    if(ri->pre_advanced)
     {
-      ri->pre_advanced = FALSE;
+        ri->pre_advanced = FALSE;
 
-      if (ri->node == NULL)
-       return FALSE;
+        if(ri->node == NULL)
+        {
+            return FALSE;
+        }
     }
-  else
+    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(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;
+    if(key != NULL)
+    {
+        *key = ri->node->key;
+    }
+
+    if(value != NULL)
+    {
+        *value = ri->node->value;
+    }
 
-  return TRUE;
+    return TRUE;
 }
 
 /**
@@ -502,62 +540,74 @@ fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key,
  * Since: 2.16
  **/
 fluid_hashtable_t *
-fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter)
+fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter)
 {
-  fluid_return_val_if_fail (iter != NULL, NULL);
+    fluid_return_val_if_fail(iter != NULL, NULL);
 
-  return ((RealIter *) iter)->hashtable;
+    return ((RealIter *) iter)->hashtable;
 }
 
 static void
-iter_remove_or_steal (RealIter *ri, int notify)
+iter_remove_or_steal(RealIter *ri, int notify)
 {
-  fluid_hashnode_t *prev;
-  fluid_hashnode_t *node;
-  int position;
+    fluid_hashnode_t *prev;
+    fluid_hashnode_t *node;
+    int position;
 
-  fluid_return_if_fail (ri != NULL);
-  fluid_return_if_fail (ri->node != NULL);
+    fluid_return_if_fail(ri != NULL);
+    fluid_return_if_fail(ri->node != NULL);
 
-  prev = ri->prev_node;
-  node = ri->node;
-  position = ri->position;
+    prev = ri->prev_node;
+    node = ri->node;
+    position = ri->position;
 
-  /* pre-advance the iterator since we will remove the node */
+    /* pre-advance the iterator since we will remove the node */
 
-  ri->node = ri->node->next;
-  /* ri->prev_node is still the correct previous node */
+    ri->node = ri->node->next;
+    /* ri->prev_node is still the correct previous node */
 
-  while (ri->node == NULL)
+    while(ri->node == NULL)
     {
-      ri->position++;
-      if (ri->position >= ri->hashtable->size)
-       break;
+        ri->position++;
 
-      ri->prev_node = NULL;
-      ri->node = ri->hashtable->nodes[ri->position];
+        if(ri->position >= ri->hashtable->size)
+        {
+            break;
+        }
+
+        ri->prev_node = NULL;
+        ri->node = ri->hashtable->nodes[ri->position];
     }
 
-  ri->pre_advanced = TRUE;
+    ri->pre_advanced = TRUE;
 
-  /* remove the node */
+    /* remove the node */
 
-  if (prev != NULL)
-    prev->next = node->next;
-  else
-    ri->hashtable->nodes[position] = node->next;
+    if(prev != NULL)
+    {
+        prev->next = node->next;
+    }
+    else
+    {
+        ri->hashtable->nodes[position] = node->next;
+    }
 
-  if (notify)
+    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);
+        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);
+    FLUID_FREE(node);
 
-  ri->hashtable->nnodes--;
+    ri->hashtable->nnodes--;
 }
 
 /**
@@ -571,15 +621,15 @@ iter_remove_or_steal (RealIter *ri, int notify)
  *
  * 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 
+ * 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)
+fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter)
 {
-  iter_remove_or_steal ((RealIter *) iter, TRUE);
+    iter_remove_or_steal((RealIter *) iter, TRUE);
 }
 
 /**
@@ -595,9 +645,9 @@ fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter)
  * Since: 2.16
  **/
 void
-fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter)
+fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter)
 {
-  iter_remove_or_steal ((RealIter *) iter, FALSE);
+    iter_remove_or_steal((RealIter *) iter, FALSE);
 }
 
 
@@ -612,14 +662,14 @@ fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter)
  *
  * Since: 2.10
  **/
-fluid_hashtable_t*
-fluid_hashtable_ref (fluid_hashtable_t *hashtable)
+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_return_val_if_fail(hashtable != NULL, NULL);
+    fluid_return_val_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0, hashtable);
 
-  fluid_atomic_int_add (&hashtable->ref_count, 1);
-  return hashtable;
+    fluid_atomic_int_add(&hashtable->ref_count, 1);
+    return hashtable;
 }
 
 /**
@@ -634,16 +684,16 @@ fluid_hashtable_ref (fluid_hashtable_t *hashtable)
  * Since: 2.10
  **/
 void
-fluid_hashtable_unref (fluid_hashtable_t *hashtable)
+fluid_hashtable_unref(fluid_hashtable_t *hashtable)
 {
-  fluid_return_if_fail (hashtable != NULL);
-  fluid_return_if_fail (hashtable->ref_count > 0);
+    fluid_return_if_fail(hashtable != NULL);
+    fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0);
 
-  if (fluid_atomic_int_exchange_and_add (&hashtable->ref_count, -1) - 1 == 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);
+        fluid_hashtable_remove_all_nodes(hashtable, TRUE);
+        FLUID_FREE(hashtable->nodes);
+        FLUID_FREE(hashtable);
     }
 }
 
@@ -659,13 +709,13 @@ fluid_hashtable_unref (fluid_hashtable_t *hashtable)
  * destruction phase.
  **/
 void
-delete_fluid_hashtable (fluid_hashtable_t *hashtable)
+delete_fluid_hashtable(fluid_hashtable_t *hashtable)
 {
-  fluid_return_if_fail (hashtable != NULL);
-  fluid_return_if_fail (hashtable->ref_count > 0);
+    fluid_return_if_fail(hashtable != NULL);
+    fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0);
 
-  fluid_hashtable_remove_all (hashtable);
-  fluid_hashtable_unref (hashtable);
+    fluid_hashtable_remove_all(hashtable);
+    fluid_hashtable_unref(hashtable);
 }
 
 /**
@@ -681,15 +731,15 @@ delete_fluid_hashtable (fluid_hashtable_t *hashtable)
  * 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_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key)
 {
-  fluid_hashnode_t *node;
+    fluid_hashnode_t *node;
 
-  fluid_return_val_if_fail (hashtable != NULL, NULL);
+    fluid_return_val_if_fail(hashtable != NULL, NULL);
 
-  node = *fluid_hashtable_lookup_node (hashtable, key, NULL);
+    node = *fluid_hashtable_lookup_node(hashtable, key, NULL);
 
-  return node ? node->value : NULL;
+    return node ? node->value : NULL;
 }
 
 /**
@@ -707,26 +757,32 @@ fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key)
  * 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_hashtable_lookup_extended(fluid_hashtable_t *hashtable,
+                                const void *lookup_key,
+                                void **orig_key, void **value)
 {
-  fluid_hashnode_t *node;
+    fluid_hashnode_t *node;
 
-  fluid_return_val_if_fail (hashtable != NULL, FALSE);
+    fluid_return_val_if_fail(hashtable != NULL, FALSE);
 
-  node = *fluid_hashtable_lookup_node (hashtable, lookup_key, NULL);
+    node = *fluid_hashtable_lookup_node(hashtable, lookup_key, NULL);
 
-  if (node == NULL)
-    return FALSE;
+    if(node == NULL)
+    {
+        return FALSE;
+    }
 
-  if (orig_key)
-    *orig_key = node->key;
+    if(orig_key)
+    {
+        *orig_key = node->key;
+    }
 
-  if (value)
-    *value = node->value;
+    if(value)
+    {
+        *value = node->value;
+    }
 
-  return TRUE;
+    return TRUE;
 }
 
 /*
@@ -746,54 +802,61 @@ fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable,
  * new node.
  */
 static void
-fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key,
-                                 void *value, int keep_new_key)
+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_hashnode_t **node_ptr, *node;
+    unsigned int key_hash;
 
-  fluid_return_if_fail (hashtable != NULL);
-  fluid_return_if_fail (hashtable->ref_count > 0);
+    fluid_return_if_fail(hashtable != NULL);
+    fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0);
 
-  node_ptr = fluid_hashtable_lookup_node (hashtable, key, &key_hash);
+    node_ptr = fluid_hashtable_lookup_node(hashtable, key, &key_hash);
 
-  if ((node = *node_ptr))
+    if((node = *node_ptr))
     {
-      if (keep_new_key)
+        if(keep_new_key)
         {
-          if (hashtable->key_destroy_func)
-            hashtable->key_destroy_func (node->key);
-          node->key = key;
+            if(hashtable->key_destroy_func)
+            {
+                hashtable->key_destroy_func(node->key);
+            }
+
+            node->key = key;
         }
-      else
+        else
         {
-          if (hashtable->key_destroy_func)
-            hashtable->key_destroy_func (key);
+            if(hashtable->key_destroy_func)
+            {
+                hashtable->key_destroy_func(key);
+            }
         }
 
-      if (hashtable->value_destroy_func)
-        hashtable->value_destroy_func (node->value);
+        if(hashtable->value_destroy_func)
+        {
+            hashtable->value_destroy_func(node->value);
+        }
 
-      node->value = value;
+        node->value = value;
     }
-  else
+    else
     {
-      node = FLUID_NEW (fluid_hashnode_t);
+        node = FLUID_NEW(fluid_hashnode_t);
 
-      if (!node)
-      {
-        FLUID_LOG (FLUID_ERR, "Out of memory");
-        return;
-      }
+        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->key = key;
+        node->value = value;
+        node->key_hash = key_hash;
+        node->next = NULL;
 
-      *node_ptr = node;
-      hashtable->nnodes++;
-      fluid_hashtable_maybe_resize (hashtable);
+        *node_ptr = node;
+        hashtable->nnodes++;
+        fluid_hashtable_maybe_resize(hashtable);
     }
 }
 
@@ -812,9 +875,9 @@ fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key,
  * using that function.
  **/
 void
-fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value)
+fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value)
 {
-  fluid_hashtable_insert_internal (hashtable, key, value, FALSE);
+    fluid_hashtable_insert_internal(hashtable, key, value, FALSE);
 }
 
 /**
@@ -831,9 +894,9 @@ fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value)
  * #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_replace(fluid_hashtable_t *hashtable, void *key, void *value)
 {
-  fluid_hashtable_insert_internal (hashtable, key, value, TRUE);
+    fluid_hashtable_insert_internal(hashtable, key, value, TRUE);
 }
 
 /*
@@ -850,21 +913,24 @@ fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value)
  * destroy notify handlers only if @notify is %TRUE.
  */
 static int
-fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key,
-                                 int notify)
+fluid_hashtable_remove_internal(fluid_hashtable_t *hashtable, const void *key,
+                                int notify)
 {
-  fluid_hashnode_t **node_ptr;
+    fluid_hashnode_t **node_ptr;
+
+    fluid_return_val_if_fail(hashtable != NULL, FALSE);
 
-  fluid_return_val_if_fail (hashtable != NULL, FALSE);
+    node_ptr = fluid_hashtable_lookup_node(hashtable, key, NULL);
 
-  node_ptr = fluid_hashtable_lookup_node (hashtable, key, NULL);
-  if (*node_ptr == NULL)
-    return FALSE;
+    if(*node_ptr == NULL)
+    {
+        return FALSE;
+    }
 
-  fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
-  fluid_hashtable_maybe_resize (hashtable);
+    fluid_hashtable_remove_node(hashtable, &node_ptr, notify);
+    fluid_hashtable_maybe_resize(hashtable);
 
-  return TRUE;
+    return TRUE;
 }
 
 /**
@@ -882,9 +948,9 @@ fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key,
  * 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)
+fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key)
 {
-  return fluid_hashtable_remove_internal (hashtable, key, TRUE);
+    return fluid_hashtable_remove_internal(hashtable, key, TRUE);
 }
 
 /**
@@ -898,9 +964,9 @@ fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key)
  * 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)
+fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key)
 {
-  return fluid_hashtable_remove_internal (hashtable, key, FALSE);
+    return fluid_hashtable_remove_internal(hashtable, key, FALSE);
 }
 
 /**
@@ -917,14 +983,15 @@ fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key)
  * Since: 2.12
  **/
 void
-fluid_hashtable_remove_all (fluid_hashtable_t *hashtable)
+fluid_hashtable_remove_all(fluid_hashtable_t *hashtable)
 {
-  fluid_return_if_fail (hashtable != NULL);
+    fluid_return_if_fail(hashtable != NULL);
 
-  fluid_hashtable_remove_all_nodes (hashtable, TRUE);
-  fluid_hashtable_maybe_resize (hashtable);
+    fluid_hashtable_remove_all_nodes(hashtable, TRUE);
+    fluid_hashtable_maybe_resize(hashtable);
 }
 
+#if 0
 /**
  * fluid_hashtable_steal_all:
  * @hashtable: a #fluid_hashtable_t.
@@ -935,13 +1002,14 @@ fluid_hashtable_remove_all (fluid_hashtable_t *hashtable)
  * Since: 2.12
  **/
 void
-fluid_hashtable_steal_all (fluid_hashtable_t *hashtable)
+fluid_hashtable_steal_all(fluid_hashtable_t *hashtable)
 {
-  fluid_return_if_fail (hashtable != NULL);
+    fluid_return_if_fail(hashtable != NULL);
 
-  fluid_hashtable_remove_all_nodes (hashtable, FALSE);
-  fluid_hashtable_maybe_resize (hashtable);
+    fluid_hashtable_remove_all_nodes(hashtable, FALSE);
+    fluid_hashtable_maybe_resize(hashtable);
 }
+#endif
 
 /*
  * fluid_hashtable_foreach_remove_or_steal:
@@ -961,27 +1029,33 @@ fluid_hashtable_steal_all (fluid_hashtable_t *hashtable)
  * 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_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;
+    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))
+    for(i = 0; i < hashtable->size; i++)
+    {
+        for(node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;)
         {
-          fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
-          deleted++;
+            if((* func)(node->key, node->value, user_data))
+            {
+                fluid_hashtable_remove_node(hashtable, &node_ptr, notify);
+                deleted++;
+            }
+            else
+            {
+                node_ptr = &node->next;
+            }
         }
-      else
-        node_ptr = &node->next;
+    }
 
-  fluid_hashtable_maybe_resize (hashtable);
+    fluid_hashtable_maybe_resize(hashtable);
 
-  return deleted;
+    return deleted;
 }
 
 #if 0
@@ -997,19 +1071,19 @@ fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable,
  * 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 
+ * 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_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);
+    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);
+    return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, TRUE);
 }
 #endif
 
@@ -1023,19 +1097,19 @@ fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable,
  * 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 
+ * 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_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);
+    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);
+    return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, FALSE);
 }
 
 /**
@@ -1055,18 +1129,22 @@ fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable,
  * 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_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func,
+                        void *user_data)
 {
-  fluid_hashnode_t *node;
-  int i;
+    fluid_hashnode_t *node;
+    int i;
 
-  fluid_return_if_fail (hashtable != NULL);
-  fluid_return_if_fail (func != NULL);
+    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);
+    for(i = 0; i < hashtable->size; i++)
+    {
+        for(node = hashtable->nodes[i]; node; node = node->next)
+        {
+            (* func)(node->key, node->value, user_data);
+        }
+    }
 }
 
 /**
@@ -1096,20 +1174,27 @@ fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func,
  * Since: 2.4
  **/
 void *
-fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,
-                      void *user_data)
+fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,
+                     void *user_data)
 {
-  fluid_hashnode_t *node;
-  int i;
+    fluid_hashnode_t *node;
+    int i;
 
-  fluid_return_val_if_fail (hashtable != NULL, NULL);
-  fluid_return_val_if_fail (predicate != NULL, NULL);
+    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;
+            }
+        }
+    }
 
-  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;
+    return NULL;
 }
 
 /**
@@ -1121,11 +1206,11 @@ fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,
  * Return value: the number of key/value pairs in the #fluid_hashtable_t.
  **/
 unsigned int
-fluid_hashtable_size (fluid_hashtable_t *hashtable)
+fluid_hashtable_size(fluid_hashtable_t *hashtable)
 {
-  fluid_return_val_if_fail (hashtable != NULL, 0);
+    fluid_return_val_if_fail(hashtable != NULL, 0);
 
-  return hashtable->nnodes;
+    return hashtable->nnodes;
 }
 
 /**
@@ -1143,20 +1228,25 @@ fluid_hashtable_size (fluid_hashtable_t *hashtable)
  * Since: 2.14
  */
 fluid_list_t *
-fluid_hashtable_get_keys (fluid_hashtable_t *hashtable)
+fluid_hashtable_get_keys(fluid_hashtable_t *hashtable)
 {
-  fluid_hashnode_t *node;
-  int i;
-  fluid_list_t *retval;
+    fluid_hashnode_t *node;
+    int i;
+    fluid_list_t *retval;
+
+    fluid_return_val_if_fail(hashtable != NULL, NULL);
 
-  fluid_return_val_if_fail (hashtable != NULL, NULL);
+    retval = 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);
+    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;
+    return retval;
 }
 
 /**
@@ -1174,20 +1264,25 @@ fluid_hashtable_get_keys (fluid_hashtable_t *hashtable)
  * Since: 2.14
  */
 fluid_list_t *
-fluid_hashtable_get_values (fluid_hashtable_t *hashtable)
+fluid_hashtable_get_values(fluid_hashtable_t *hashtable)
 {
-  fluid_hashnode_t *node;
-  int i;
-  fluid_list_t *retval;
+    fluid_hashnode_t *node;
+    int i;
+    fluid_list_t *retval;
 
-  fluid_return_val_if_fail (hashtable != NULL, NULL);
+    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);
+    retval = NULL;
 
-  return retval;
+    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;
 }
 
 
@@ -1198,20 +1293,20 @@ fluid_hashtable_get_values (fluid_hashtable_t *hashtable)
  * 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 
+ *
+ * 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)
+fluid_str_equal(const void *v1, const void *v2)
 {
-  const char *string1 = v1;
-  const char *string2 = v2;
-  
-  return strcmp (string1, string2) == 0;
+    const char *string1 = v1;
+    const char *string2 = v2;
+
+    return FLUID_STRCMP(string1, string2) == 0;
 }
 
 /**
@@ -1219,23 +1314,27 @@ fluid_str_equal (const void *v1, const void *v2)
  * @v: a string key
  *
  * Converts a string to a hash value.
- * It can be passed to new_fluid_hashtable() as the @hash_func 
+ * 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)
+fluid_str_hash(const void *v)
 {
-  /* 31 bit hash function */
-  const signed char *p = v;
-  uint32 h = *p;
+    /* 31 bit hash function */
+    const signed char *p = v;
+    uint32_t h = *p;
 
-  if (h)
-    for (p += 1; *p != '\0'; p++)
-      h = (h << 5) - h + *p;
+    if(h)
+    {
+        for(p += 1; *p != '\0'; p++)
+        {
+            h = (h << 5) - h + *p;
+        }
+    }
 
-  return h;
+    return h;
 }
 
 
@@ -1250,13 +1349,13 @@ fluid_str_hash (const void *v)
  * 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)
+fluid_direct_equal(const void *v1, const void *v2)
 {
-  return v1 == v2;
+    return v1 == v2;
 }
 
 /**
@@ -1264,15 +1363,15 @@ fluid_direct_equal (const void *v1, const void *v2)
  * @v: a void * key
  *
  * Converts a gpointer to a hash value.
- * It can be passed to g_hashtable_new() as the @hash_func parameter, 
+ * 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)
+fluid_direct_hash(const void *v)
 {
-  return FLUID_POINTER_TO_UINT (v);
+    return FLUID_POINTER_TO_UINT(v);
 }
 
 /**
@@ -1280,17 +1379,17 @@ fluid_direct_hash (const void *v)
  * @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 
+ * 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)
+fluid_int_equal(const void *v1, const void *v2)
 {
-  return *((const int*) v1) == *((const int*) v2);
+    return *((const int *) v1) == *((const int *) v2);
 }
 
 /**
@@ -1298,13 +1397,13 @@ fluid_int_equal (const void *v1, const void *v2)
  * @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, 
+ * 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)
+fluid_int_hash(const void *v)
 {
-  return *(const int*) v;
+    return *(const int *) v;
 }
index 3beff0607eadee53c281f66b4d7755e97af335f6..96b2471be51349ffa5bcdb54967b40f37c436706 100644 (file)
@@ -27,7 +27,7 @@
 /*
  * 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_...
  */
@@ -52,80 +52,80 @@ typedef struct _fluid_hashnode_t      fluid_hashnode_t;
 
 struct _fluid_hashnode_t
 {
-  void *key;
-  void *value;
-  fluid_hashnode_t *next;
-  unsigned int key_hash;
+    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)
+    int size;
+    int nnodes;
+    fluid_hashnode_t **nodes;
+    fluid_hash_func_t hash_func;
+    fluid_equal_func_t key_equal_func;
+    fluid_atomic_int_t 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;
+    /*< 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);
+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 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);
-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);
+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 */
 
index 5b474692aed58e81e8b932e941e275ba313eb955..0770ef62b6dd185617dfd1aa9aff12f8f85058af 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_conv.h"
 
 /**
- * Applies a lowpass filter with variable cutoff frequency and quality factor.
+ * Applies a low- or high-pass filter with variable cutoff frequency and quality factor
+ * for a given biquad transfer function:
+ *          b0 + b1*z^-1 + b2*z^-2
+ *  H(z) = ------------------------
+ *          a0 + a1*z^-1 + a2*z^-2
+ *
  * Also modifies filter state accordingly.
  * @param iir_filter Filter parameter
  * @param dsp_buf Pointer to the synthesized audio data
  */
 /*
  * Variable description:
- * - dsp_a1, dsp_a2, dsp_b0, dsp_b1, dsp_b2: Filter coefficients
+ * - dsp_a1, dsp_a2: Filter coefficients for the the previously filtered output signal
+ * - dsp_b0, dsp_b1, dsp_b2: Filter coefficients for input signal
+ * - coefficients normalized to a0
  *
  * 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_tiir_filter,
+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;
+    if(iir_filter->type == FLUID_IIR_DISABLED || iir_filter->q_lin == 0)
+    {
+        return;
+    }
+    else
+    {
+        /* 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? */
+        }
 
-  fluid_real_t dsp_centernode;
-  int dsp_i;
+        /* 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_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;
+            }
+        }
 
-  /* filter (implement the voice filter according to SoundFont standard) */
+        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;
 
-  /* Check for denormal number (too close to zero). */
-  if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f;  /* FIXME JMG - Is this even needed? */
+        fluid_check_fpe("voice_filter");
+    }
+}
 
-  /* 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;
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init)
+{
+    fluid_iir_filter_t *iir_filter = obj;
+    enum fluid_iir_filter_type type = param[0].i;
+    enum fluid_iir_filter_flags flags = param[1].i;
 
+    iir_filter->type = type;
+    iir_filter->flags = flags;
 
-    /* Increment is added to each filter coefficient filter_coeff_incr_count times. */
-    for (dsp_i = 0; dsp_i < count; dsp_i++)
+    if(type != FLUID_IIR_DISABLED)
     {
-      /* 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;
+        fluid_iir_filter_reset(iir_filter);
     }
-  }
-
-  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)
+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;
+    iir_filter->hist1 = 0;
+    iir_filter->hist2 = 0;
+    iir_filter->last_fres = -1.;
+    iir_filter->q_lin = 0;
+    iir_filter->filter_startup = 1;
 }
 
-void 
-fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter, 
-                          fluid_real_t fres)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres)
 {
-  iir_filter->fres = fres;
-  iir_filter->last_fres = -1.;
-}
+    fluid_iir_filter_t *iir_filter = obj;
+    fluid_real_t fres = param[0].real;
 
+    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)
+static fluid_real_t fluid_iir_filter_q_from_dB(fluid_real_t q_dB)
 {
+    /* The generator contains 'centibels' (1/10 dB) => divide by 10 to
+     * obtain dB */
+    q_dB /= 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;
+
     /* 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));
+    return 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));
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q)
+{
+    fluid_iir_filter_t *iir_filter = obj;
+    fluid_real_t q = param[0].real;
+    int flags = iir_filter->flags;
+
+    if(flags & FLUID_IIR_Q_ZERO_OFF && q <= 0.0)
+    {
+        q = 0;
+    }
+    else if(flags & FLUID_IIR_Q_LINEAR)
+    {
+        /* q is linear (only for user-defined filter)
+         * increase to avoid Q being somewhere between zero and one,
+         * which results in some strange amplified lowpass signal
+         */
+        q++;
+    }
+    else
+    {
+        q = fluid_iir_filter_q_from_dB(q);
+    }
+
+    iir_filter->q_lin = q;
+    iir_filter->filter_gain = 1.0;
+
+    if(!(flags & FLUID_IIR_NO_GAIN_AMP))
+    {
+        /* 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 /= sqrt(q);
+    }
 
     /* 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, 
+static FLUID_INLINE void
+fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter,
+                                        int transition_samples,
                                         fluid_real_t output_rate)
 {
+    /* FLUID_IIR_Q_LINEAR may switch the filter off by setting Q==0 */
+    if(iir_filter->q_lin == 0)
+    {
+        return;
+    }
+    else
+    {
+        /*
+         * 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; */
+
+        /* "a" coeffs are same for all 3 available filter types */
+        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 b02_temp, b1_temp;
+
+        switch(iir_filter->type)
+        {
+        case FLUID_IIR_HIGHPASS:
+            b1_temp = (1.0f + cos_coeff) * a0_inv * iir_filter->filter_gain;
+
+            /* both b0 -and- b2 */
+            b02_temp = b1_temp * 0.5f;
+
+            b1_temp *= -1.0f;
+            break;
+
+        case FLUID_IIR_LOWPASS:
+            b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain;
+
+            /* both b0 -and- b2 */
+            b02_temp = b1_temp * 0.5f;
+            break;
+
+        default:
+            /* filter disabled, should never get here */
+            return;
+        }
 
-  /*
-    * 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;
+        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;
+        }
+        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");
     }
-    /* 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, 
+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");
+    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(iir_filter->type != FLUID_IIR_DISABLED && fabs(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");
 
 }
 
index 7dc5de14a57a1d4b068926eaea31339ddbcf699f..355d197f1cb42b56591c89d7af0a7f894ddd4163 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 typedef struct _fluid_iir_filter_t fluid_iir_filter_t;
 
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q);
 
-void fluid_iir_filter_apply(fluid_iir_filter_tiir_filter,
-                            fluid_real_t *dsp_buf, int dsp_buf_count); 
+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_tiir_filter);
+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); 
+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 */
+    enum fluid_iir_filter_type type; /* specifies the type of this filter */
+    enum fluid_iir_filter_flags flags; /* additional flags to customize this filter */
+
+    /* 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.
+    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 */
+    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
index ff178e0012c7b2d12b7c158c292d7a43221ac677..ae21cdd0f725d078d56ffbe68c5c1229ca1c9f5d 100644 (file)
@@ -1,13 +1,17 @@
 #include "fluid_lfo.h"
 
-void
-fluid_lfo_set_incr(fluid_lfo_t* lfo, fluid_real_t increment)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr)
 {
-  lfo->increment = increment;
+    fluid_lfo_t *lfo = obj;
+    fluid_real_t increment = param[0].real;
+
+    lfo->increment = increment;
 }
 
-void
-fluid_lfo_set_delay(fluid_lfo_t* lfo, unsigned int delay)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay)
 {
-  lfo->delay = delay;
+    fluid_lfo_t *lfo = obj;
+    unsigned int delay = param[0].i;
+
+    lfo->delay = delay;
 }
index e9440cf526bd7ba431c0c89554672a29775a934d..b9a9ca6eaaf0c7ec607f5566ca3f48faa47ef937 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #ifndef _FLUID_LFO_H
 #define _FLUID_LFO_H
 
-#include "fluidsynth_priv.h"
+#include "fluid_sys.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 */
+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_tlfo)
+static FLUID_INLINE void
+fluid_lfo_reset(fluid_lfo_t *lfo)
 {
-  lfo->val = 0.0f;
+    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);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay);
 
-static inline fluid_real_t
-fluid_lfo_get_val(fluid_lfo_tlfo)
+static FLUID_INLINE fluid_real_t
+fluid_lfo_get_val(fluid_lfo_t *lfo)
 {
-  return lfo->val;
+    return lfo->val;
 }
 
-static inline void 
-fluid_lfo_calc(fluid_lfo_tlfo, unsigned int cur_delay)
+static FLUID_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;
-  }
+    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;
+    }
 
 }
 
index dd47a39e0e215e60b5fbbf861c1f5bdc8287242d..c92d731f8932fb9ff79df8faf6a6d835c57f94a4 100644 (file)
@@ -2,16 +2,16 @@
  * 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
+ * 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
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * 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.
 
 
 
+#include "fluid_sys.h"
 #include "fluid_list.h"
 
 
-fluid_list_t*
+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;
+    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;
-  }
+    fluid_list_t *next;
+    fluid_return_if_fail(list != NULL);
+
+    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 *
+fluid_list_append(fluid_list_t *list, void  *data)
 {
-  fluid_list_t *new_list;
-  fluid_list_t *last;
+    fluid_list_t *new_list;
+    fluid_list_t *last;
 
-  new_list = new_fluid_list();
-  new_list->data = data;
+    new_list = new_fluid_list();
+    new_list->data = data;
 
-  if (list)
+    if(list)
     {
-      last = fluid_list_last(list);
-      /* g_assert (last != NULL); */
-      last->next = new_list;
+        last = fluid_list_last(list);
+        /* g_assert (last != NULL); */
+        last->next = new_list;
 
-      return list;
+        return list;
+    }
+    else
+    {
+        return new_list;
     }
-  else
-      return new_list;
 }
 
-fluid_list_t*
-fluid_list_prepend(fluid_list_t *list, voiddata)
+fluid_list_t *
+fluid_list_prepend(fluid_list_t *list, void *data)
 {
-  fluid_list_t *new_list;
+    fluid_list_t *new_list;
 
-  new_list = new_fluid_list();
-  new_list->data = data;
-  new_list->next = list;
+    new_list = new_fluid_list();
+    new_list->data = data;
+    new_list->next = list;
 
-  return new_list;
+    return new_list;
 }
 
-fluid_list_t*
+fluid_list_t *
 fluid_list_nth(fluid_list_t *list, int n)
 {
-  while ((n-- > 0) && list) {
-    list = list->next;
-  }
+    while((n-- > 0) && list)
+    {
+        list = list->next;
+    }
 
-  return list;
+    return list;
 }
 
-fluid_list_t*
-fluid_list_remove(fluid_list_t *list, voiddata)
+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;
-    }
+    fluid_list_t *tmp;
+    fluid_list_t *prev;
 
-    prev = tmp;
-    tmp = tmp->next;
-  }
+    prev = NULL;
+    tmp = list;
 
-  return 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_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;
-    }
+    fluid_list_t *tmp;
+    fluid_list_t *prev;
+
+    prev = NULL;
+    tmp = list;
 
-    prev = tmp;
-    tmp = tmp->next;
-  }
+    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;
+    return list;
 }
 
-static fluid_list_t*
+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;
+    fluid_list_t list, *l;
 
-  l = &list;
+    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;
+    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;
+    l->next = l1 ? l1 : l2;
+
+    return list.next;
 }
 
-fluid_list_t*
+fluid_list_t *
 fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func)
 {
-  fluid_list_t *l1, *l2;
+    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);
+    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_t *
 fluid_list_last(fluid_list_t *list)
 {
-  if (list) {
-    while (list->next)
-      list = list->next;
-  }
+    if(list)
+    {
+        while(list->next)
+        {
+            list = list->next;
+        }
+    }
 
-  return list;
+    return list;
 }
 
 int
 fluid_list_size(fluid_list_t *list)
 {
-  int n = 0;
-  while (list) {
-    n++;
-    list = list->next;
-  }
-  return n;
+    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 *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;
+    fluid_list_t *new_list;
+    fluid_list_t *cur;
+    fluid_list_t *prev = NULL;
 
-  new_list = new_fluid_list();
-  new_list->data = data;
+    new_list = new_fluid_list();
+    new_list->data = data;
 
-  cur = list;
-  while ((n-- > 0) && cur) {
-    prev = cur;
-    cur = cur->next;
-  }
+    cur = list;
 
-  new_list->next = cur;
+    while((n-- > 0) && cur)
+    {
+        prev = cur;
+        cur = cur->next;
+    }
 
-  if (prev) {
-    prev->next = new_list;
-    return list;
-  } else {
-    return new_list;
-  }
+    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)
+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;
+    if(a && b)
+    {
+        return FLUID_STRCMP((char *)a, (char *)b);
+    }
+
+    if(!a && !b)
+    {
+        return 0;
+    }
+
+    if(a)
+    {
+        return -1;
+    }
+
+    return 1;
 }
index bdc32918b86162717b2ff5b62db970d17f9739e8..5be1cc3923ad0b9db6eb0a9dac034ae681174a05 100644 (file)
@@ -2,16 +2,16 @@
  * 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
+ * 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
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * 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.
 
 typedef struct _fluid_list_t fluid_list_t;
 
-typedef int (*fluid_compare_func_t)(void* a, void* b);
+typedef int (*fluid_compare_func_t)(void *a, void *b);
 
 struct _fluid_list_t
 {
-  void* data;
-  fluid_list_t *next;
+    void *data;
+    fluid_list_t *next;
 };
 
-fluid_list_tnew_fluid_list(void);
+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_tfluid_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_tfluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink);
-fluid_list_tfluid_list_nth(fluid_list_t *list, int n);
-fluid_list_tfluid_list_last(fluid_list_t *list);
-fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data);
+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);
+int fluid_list_str_compare_func(void *a, void *b);
 
 #endif  /* _FLUID_LIST_H */
index fc58d839efb099d4364983bbf73bc028de20bdac..bdf72dd68187250156477ec3017ea18657026652 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 
 static int fluid_midi_event_length(unsigned char event);
+static int fluid_isasciistring(char *s);
+static long fluid_getlength(unsigned char *s);
+
 
 /* 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);
+static char *fluid_file_read_full(fluid_file fp, size_t *length);
+static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic);
 #define READ_FULL_INITIAL_BUFLEN 1024
 
+static fluid_track_t *new_fluid_track(int num);
+static void delete_fluid_track(fluid_track_t *track);
+static int fluid_track_set_name(fluid_track_t *track, char *name);
+static int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt);
+static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track);
+static int fluid_track_get_duration(fluid_track_t *track);
+static int fluid_track_reset(fluid_track_t *track);
+
+static int fluid_track_send_events(fluid_track_t *track,
+                                   fluid_synth_t *synth,
+                                   fluid_player_t *player,
+                                   unsigned int ticks);
+
+
+static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track);
+static int fluid_player_callback(void *data, unsigned int msec);
+static int fluid_player_reset(fluid_player_t *player);
+static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item);
+static void fluid_player_advancefile(fluid_player_t *player);
+static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec);
+
+static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length);
+static void delete_fluid_midi_file(fluid_midi_file *mf);
+static int fluid_midi_file_read_mthd(fluid_midi_file *midifile);
+static int fluid_midi_file_load_tracks(fluid_midi_file *midifile, fluid_player_t *player);
+static int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num);
+static int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track);
+static int fluid_midi_file_read_varlen(fluid_midi_file *mf);
+static int fluid_midi_file_getc(fluid_midi_file *mf);
+static int fluid_midi_file_push(fluid_midi_file *mf, int c);
+static int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len);
+static int fluid_midi_file_skip(fluid_midi_file *mf, int len);
+static int fluid_midi_file_eof(fluid_midi_file *mf);
+static int fluid_midi_file_read_tracklen(fluid_midi_file *mf);
+static int fluid_midi_file_eot(fluid_midi_file *mf);
+static int fluid_midi_file_get_division(fluid_midi_file *midifile);
+
 #if 0 // disable file I/O with Ardour
 /***************************************************************
  *
@@ -49,15 +90,18 @@ static char* fluid_file_read_full(fluid_file fp, size_t* length);
  * @return New MIDI file handle or NULL on error.
  */
 fluid_midi_file *
-new_fluid_midi_file(const charbuffer, size_t length)
+new_fluid_midi_file(const char *buffer, size_t length)
 {
     fluid_midi_file *mf;
 
     mf = FLUID_NEW(fluid_midi_file);
-    if (mf == NULL) {
+
+    if(mf == NULL)
+    {
         FLUID_LOG(FLUID_ERR, "Out of memory");
         return NULL;
     }
+
     FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file));
 
     mf->c = -1;
@@ -68,45 +112,58 @@ new_fluid_midi_file(const char* buffer, size_t length)
     mf->buf_pos = 0;
     mf->eof = FALSE;
 
-    if (fluid_midi_file_read_mthd(mf) != FLUID_OK) {
+    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_tlength)
+static char *
+fluid_file_read_full(fluid_file fp, size_t *length)
 {
     size_t buflen;
-    charbuffer;
+    char *buffer;
     size_t n;
+
     /* Work out the length of the file in advance */
-    if (FLUID_FSEEK(fp, 0, SEEK_END) != 0)
+    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)
+
+    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) {
+
+    if(buffer == NULL)
+    {
         FLUID_LOG(FLUID_PANIC, "Out of memory");
         return NULL;
     }
+
     n = FLUID_FREAD(buffer, 1, buflen, fp);
-    if (n != buflen) {
+
+    if(n != buflen)
+    {
         FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n,
                   buflen);
         FLUID_FREE(buffer);
         return NULL;
     };
+
     *length = n;
+
     return buffer;
 }
 
@@ -116,35 +173,40 @@ fluid_file_read_full(fluid_file fp, size_t* length)
  * @param mf MIDI file handle to close and free.
  */
 void
-delete_fluid_midi_file (fluid_midi_file *mf)
+delete_fluid_midi_file(fluid_midi_file *mf)
 {
-    if (mf == NULL) {
-        return;
-    }
+    fluid_return_if_fail(mf != NULL);
+
     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
+ * returns -1 if EOF or read error
  */
 int
-fluid_midi_file_getc (fluid_midi_file *mf)
+fluid_midi_file_getc(fluid_midi_file *mf)
 {
     unsigned char c;
-    if (mf->c >= 0) {
+
+    if(mf->c >= 0)
+    {
         c = mf->c;
         mf->c = -1;
-    } else {
-        if (mf->buf_pos >= mf->buf_len) {
+    }
+    else
+    {
+        if(mf->buf_pos >= mf->buf_len)
+        {
             mf->eof = TRUE;
-            return FLUID_FAILED;
+            return -1;
         }
+
         c = mf->buffer[mf->buf_pos++];
         mf->trackpos++;
     }
+
     return (int) c;
 }
 
@@ -166,23 +228,35 @@ 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) {
+              ? len : mf->buf_len - mf->buf_pos;
+
+    if(num != len)
+    {
         mf->eof = TRUE;
     }
-    if (num < 0) {
+
+    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);
+    FLUID_MEMCPY(buf, mf->buffer + mf->buf_pos, num);
     mf->buf_pos += num;
-    if (num == len)
+
+    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;
 }
@@ -191,15 +265,18 @@ fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len)
  * fluid_midi_file_skip
  */
 int
-fluid_midi_file_skip (fluid_midi_file *mf, int skip)
+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) {
+    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;
@@ -210,15 +287,15 @@ fluid_midi_file_skip (fluid_midi_file *mf, int skip)
 /*
  * fluid_midi_file_eof
  */
-int fluid_midi_file_eof(fluid_midi_filemf)
+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;
+    /* 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;
 }
 
 /*
@@ -227,30 +304,40 @@ int fluid_midi_file_eof(fluid_midi_file* mf)
 int
 fluid_midi_file_read_mthd(fluid_midi_file *mf)
 {
-    char mthd[15];
-    if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) {
+    char mthd[14];
+
+    if(fluid_midi_file_read(mf, mthd, sizeof(mthd)) != FLUID_OK)
+    {
         return FLUID_FAILED;
     }
-    if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6)
-            || (mthd[9] > 2)) {
+
+    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");
+                  "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->ntracks += (unsigned int)(mthd[10]) << 16;
+
+    if((signed char)mthd[12] < 0)
+    {
         mf->uses_smpte = 1;
-        mf->smpte_fps = -mthd[12];
+        mf->smpte_fps = -(signed char)mthd[12];
         mf->smpte_res = (unsigned) mthd[13];
         FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet");
         return FLUID_FAILED;
-    } else {
+    }
+    else
+    {
         mf->uses_smpte = 0;
-        mf->division = (mthd[12] << 8) | (mthd[13] & 0xff);
+        mf->division = ((unsigned)mthd[12] << 8) | ((unsigned)mthd[13] & 0xff);
         FLUID_LOG(FLUID_DBG, "Division=%d", mf->division);
     }
+
     return FLUID_OK;
 }
 
@@ -261,11 +348,15 @@ 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) {
+
+    for(i = 0; i < mf->ntracks; i++)
+    {
+        if(fluid_midi_file_read_track(mf, player, i) != FLUID_OK)
+        {
             return FLUID_FAILED;
         }
     }
+
     return FLUID_OK;
 }
 
@@ -275,14 +366,23 @@ fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player)
 int
 fluid_isasciistring(char *s)
 {
+    /* From ctype.h */
+#define fluid_isascii(c)    (((c) & ~0x7f) == 0)
+
     int i;
     int len = (int) FLUID_STRLEN(s);
-    for (i = 0; i < len; i++) {
-        if (!fluid_isascii(s[i])) {
+
+    for(i = 0; i < len; i++)
+    {
+        if(!fluid_isascii(s[i]))
+        {
             return 0;
         }
     }
+
     return 1;
+
+#undef fluid_isascii
 }
 
 /*
@@ -303,9 +403,12 @@ int
 fluid_midi_file_read_tracklen(fluid_midi_file *mf)
 {
     unsigned char length[5];
-    if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+
+    if(fluid_midi_file_read(mf, length, 4) != FLUID_OK)
+    {
         return FLUID_FAILED;
     }
+
     mf->tracklen = fluid_getlength(length);
     mf->trackpos = 0;
     mf->eot = 0;
@@ -319,9 +422,12 @@ int
 fluid_midi_file_eot(fluid_midi_file *mf)
 {
 #if DEBUG
-    if (mf->trackpos > mf->tracklen) {
+
+    if(mf->trackpos > mf->tracklen)
+    {
         printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen);
     }
+
 #endif
     return mf->eot || (mf->trackpos >= mf->tracklen);
 }
@@ -337,69 +443,93 @@ fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num)
     int found_track = 0;
     int skip;
 
-    if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) {
+    if(fluid_midi_file_read(mf, id, 4) != FLUID_OK)
+    {
         return FLUID_FAILED;
     }
+
     id[4] = '\0';
     mf->dtime = 0;
 
-    while (!found_track) {
+    while(!found_track)
+    {
 
-        if (fluid_isasciistring((char *) id) == 0) {
+        if(fluid_isasciistring((char *) id) == 0)
+        {
             FLUID_LOG(FLUID_ERR,
-                    "An non-ascii track header found, corrupt file");
+                      "An non-ascii track header found, corrupt file");
             return FLUID_FAILED;
 
-        } else if (strcmp((char *) id, "MTrk") == 0) {
+        }
+        else if(FLUID_STRCMP((char *) id, "MTrk") == 0)
+        {
 
             found_track = 1;
 
-            if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) {
+            if(fluid_midi_file_read_tracklen(mf) != FLUID_OK)
+            {
                 return FLUID_FAILED;
             }
 
             track = new_fluid_track(num);
-            if (track == NULL) {
+
+            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) {
+            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) {
-                if (fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) {
+            if(mf->trackpos < mf->tracklen)
+            {
+                if(fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK)
+                {
                     delete_fluid_track(track);
                     return FLUID_FAILED;
                 }
             }
 
-            if (fluid_player_add_track(player, track) != FLUID_OK) {
+            if(fluid_player_add_track(player, track) != FLUID_OK)
+            {
                 delete_fluid_track(track);
                 return FLUID_FAILED;
             }
 
-        } else {
+        }
+        else
+        {
             found_track = 0;
-            if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+
+            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) {
+            if(fluid_midi_file_skip(mf, skip) != FLUID_OK)
+            {
                 return FLUID_FAILED;
             }
         }
     }
-    if (fluid_midi_file_eof(mf)) {
+
+    if(fluid_midi_file_eof(mf))
+    {
         FLUID_LOG(FLUID_ERR, "Unexpected end of file");
         return FLUID_FAILED;
     }
+
     return FLUID_OK;
 }
 
@@ -412,24 +542,35 @@ fluid_midi_file_read_varlen(fluid_midi_file *mf)
     int i;
     int c;
     mf->varlen = 0;
-    for (i = 0;; i++) {
-        if (i == 4) {
+
+    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) {
+
+        if(c < 0)
+        {
             FLUID_LOG(FLUID_ERR, "Unexpected end of file");
             return FLUID_FAILED;
         }
-        if (c & 0x80) {
-            mf->varlen |= (int) (c & 0x7F);
+
+        if(c & 0x80)
+        {
+            mf->varlen |= (int)(c & 0x7F);
             mf->varlen <<= 7;
-        } else {
+        }
+        else
+        {
             mf->varlen += c;
             break;
         }
     }
+
     return FLUID_OK;
 }
 
@@ -453,24 +594,31 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)
     int size;
 
     /* read the delta-time of the event */
-    if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+    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) {
+
+    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) {
+    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;
     }
@@ -479,40 +627,49 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)
 
     mf->running_status = status;
 
-    if ((status == MIDI_SYSEX)) { /* system exclusif */
+    if(status == MIDI_SYSEX)    /* system exclusif */
+    {
         /* read the length of the message */
-        if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+        if(fluid_midi_file_read_varlen(mf) != FLUID_OK)
+        {
             return FLUID_FAILED;
         }
 
-        if (mf->varlen) {
+        if(mf->varlen)
+        {
             FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
-                    __LINE__, mf->varlen);
+                      __LINE__, mf->varlen);
             metadata = FLUID_MALLOC(mf->varlen + 1);
 
-            if (metadata == NULL) {
+            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);
+            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) {
+
+            if(evt == NULL)
+            {
                 FLUID_LOG(FLUID_ERR, "Out of memory");
-                FLUID_FREE (metadata);
+                FLUID_FREE(metadata);
                 return FLUID_FAILED;
             }
 
             evt->dtime = mf->dtime;
             size = mf->varlen;
 
-            if (metadata[mf->varlen - 1] == MIDI_EOX)
+            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);
@@ -522,232 +679,318 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)
 
         return FLUID_OK;
 
-    } else if (status == MIDI_META_EVENT) { /* meta events */
+    }
+    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) {
+
+        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) {
+        if(fluid_midi_file_read_varlen(mf) != FLUID_OK)
+        {
             return FLUID_FAILED;
         }
 
-        if (mf->varlen < 255) {
+        if(mf->varlen < 255)
+        {
             metadata = &static_buf[0];
-        } else {
+        }
+        else
+        {
             FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
-                    __LINE__, mf->varlen);
+                      __LINE__, mf->varlen);
             dyn_buf = FLUID_MALLOC(mf->varlen + 1);
-            if (dyn_buf == NULL) {
+
+            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) {
+        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) {
+        switch(type)
+        {
 
-            case MIDI_COPYRIGHT:
-                metadata[mf->varlen] = 0;
-                break;
+        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_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_INST_NAME:
+            metadata[mf->varlen] = 0;
+            break;
+
+        case MIDI_LYRIC:
+        case MIDI_TEXT:
+        {
+            void *tmp;
+            int size = mf->varlen + 1;
+
+            /* NULL terminate strings for safety */
+            metadata[size - 1] = '\0';
 
-            case MIDI_LYRIC:
+            evt = new_fluid_midi_event();
+
+            if(evt == NULL)
+            {
+                FLUID_LOG(FLUID_ERR, "Out of memory");
+                result = FLUID_FAILED;
                 break;
+            }
 
-            case MIDI_MARKER:
+            evt->dtime = mf->dtime;
+
+            tmp = FLUID_MALLOC(size);
+
+            if(tmp == NULL)
+            {
+                FLUID_LOG(FLUID_PANIC, "Out of memory");
+                delete_fluid_midi_event(evt);
+                evt = NULL;
+                result = FLUID_FAILED;
                 break;
+            }
 
-            case MIDI_CUE_POINT:
-                break; /* don't care much for text events */
+            FLUID_MEMCPY(tmp, metadata, size);
 
-            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;
+            fluid_midi_event_set_sysex_LOCAL(evt, type, tmp, size, TRUE);
+            fluid_track_add_event(track, evt);
+            mf->dtime = 0;
+        }
+        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;
+            }
 
-            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;
+            mf->eot = 1;
+            evt = new_fluid_midi_event();
+
+            if(evt == NULL)
+            {
+                FLUID_LOG(FLUID_ERR, "Out of memory");
+                result = FLUID_FAILED;
                 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];
+            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;
+            }
 
-                FLUID_LOG(FLUID_DBG,
-                        "signature=%d/%d, metronome=%d, 32nd-notes=%d",
-                        nominator, denominator, clocks, notes);
+            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;
+            }
 
-            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]; */
+            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;
+            }
 
-            case MIDI_SEQUENCER_EVENT:
+            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);
 
-            default:
+            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) {
+        if(dyn_buf)
+        {
             FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__);
             FLUID_FREE(dyn_buf);
         }
 
         return result;
 
-    } else { /* channel messages */
+    }
+    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) {
+        if((param1 = fluid_midi_file_getc(mf)) < 0)
+        {
             FLUID_LOG(FLUID_ERR, "Unexpected end of file");
             return FLUID_FAILED;
         }
 
-        switch (type) {
+        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_ON:
+            if((param2 = fluid_midi_file_getc(mf)) < 0)
+            {
+                FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+                return FLUID_FAILED;
+            }
 
-            case NOTE_OFF:
-                if ((param2 = fluid_midi_file_getc(mf)) < 0) {
-                    FLUID_LOG(FLUID_ERR, "Unexpected end of file");
-                    return FLUID_FAILED;
-                }
-                break;
+            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 NOTE_OFF:
+            if((param2 = fluid_midi_file_getc(mf)) < 0)
+            {
+                FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+                return FLUID_FAILED;
+            }
 
-            case CONTROL_CHANGE:
-                if ((param2 = fluid_midi_file_getc(mf)) < 0) {
-                    FLUID_LOG(FLUID_ERR, "Unexpected end of file");
-                    return FLUID_FAILED;
-                }
-                break;
+            break;
 
-            case PROGRAM_CHANGE:
-                break;
+        case KEY_PRESSURE:
+            if((param2 = fluid_midi_file_getc(mf)) < 0)
+            {
+                FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+                return FLUID_FAILED;
+            }
 
-            case CHANNEL_PRESSURE:
-                break;
+            break;
 
-            case PITCH_BEND:
-                if ((param2 = fluid_midi_file_getc(mf)) < 0) {
-                    FLUID_LOG(FLUID_ERR, "Unexpected end of file");
-                    return FLUID_FAILED;
-                }
+        case CONTROL_CHANGE:
+            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;
+            break;
+
+        case PROGRAM_CHANGE:
+            break;
+
+        case CHANNEL_PRESSURE:
+            break;
 
-            default:
-                /* Can't possibly happen !? */
-                FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event");
+        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) {
+
+        if(evt == NULL)
+        {
             FLUID_LOG(FLUID_ERR, "Out of memory");
             return FLUID_FAILED;
         }
+
         evt->dtime = mf->dtime;
         evt->type = type;
         evt->channel = channel;
@@ -756,6 +999,7 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)
         fluid_track_add_event(track, evt);
         mf->dtime = 0;
     }
+
     return FLUID_OK;
 }
 
@@ -779,14 +1023,17 @@ fluid_midi_file_get_division(fluid_midi_file *midifile)
  * @return New MIDI event structure or NULL when out of memory.
  */
 fluid_midi_event_t *
-new_fluid_midi_event ()
+new_fluid_midi_event()
 {
-    fluid_midi_event_tevt;
+    fluid_midi_event_t *evt;
     evt = FLUID_NEW(fluid_midi_event_t);
-    if (evt == NULL) {
+
+    if(evt == NULL)
+    {
         FLUID_LOG(FLUID_ERR, "Out of memory");
         return NULL;
     }
+
     evt->dtime = 0;
     evt->type = 0;
     evt->channel = 0;
@@ -800,24 +1047,27 @@ new_fluid_midi_event ()
 /**
  * Delete MIDI event structure.
  * @param evt MIDI event structure
- * @return Always returns #FLUID_OK
  */
-int
+void
 delete_fluid_midi_event(fluid_midi_event_t *evt)
 {
     fluid_midi_event_t *temp;
+    fluid_return_if_fail(evt != NULL);
 
-    while (evt) {
+    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);
+        if((evt->type == MIDI_SYSEX || (evt-> type == MIDI_TEXT) || (evt->type == MIDI_LYRIC)) &&
+                evt->paramptr && evt->param2)
+        {
+            FLUID_FREE(evt->paramptr);
+        }
 
         FLUID_FREE(evt);
         evt = temp;
     }
-    return FLUID_OK;
 }
 
 /**
@@ -1016,22 +1266,65 @@ fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)
  * 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 size Size of SYSEX data in bytes
  * @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.
+ * @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;
+    fluid_midi_event_set_sysex_LOCAL(evt, MIDI_SYSEX, data, size, dynamic);
+    return FLUID_OK;
+}
+
+/**
+ * Assign text data to a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param data Pointer to text data
+ * @param size Size of text data in bytes
+ * @param dynamic TRUE if the data has been dynamically allocated and
+ *   should be freed when the event is freed via delete_fluid_midi_event()
+ * @return Always returns #FLUID_OK
+ *
+ * @since 2.0.0
+ * @note Unlike the other event assignment functions, this one sets evt->type.
+ */
+int
+fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic)
+{
+    fluid_midi_event_set_sysex_LOCAL(evt, MIDI_TEXT, data, size, dynamic);
+    return FLUID_OK;
+}
+
+/**
+ * Assign lyric data to a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param data Pointer to lyric data
+ * @param size Size of lyric data in bytes
+ * @param dynamic TRUE if the data has been dynamically allocated and
+ *   should be freed when the event is freed via delete_fluid_midi_event()
+ * @return Always returns #FLUID_OK
+ *
+ * @since 2.0.0
+ * @note Unlike the other event assignment functions, this one sets evt->type.
+ */
+int
+fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic)
+{
+    fluid_midi_event_set_sysex_LOCAL(evt, MIDI_LYRIC, data, size, dynamic);
+    return FLUID_OK;
+}
+
+static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic)
+{
+    evt->type = type;
+    evt->paramptr = data;
     evt->param1 = size;
     evt->param2 = dynamic;
-    return FLUID_OK;
 }
 
 /******************************************************
@@ -1047,9 +1340,12 @@ new_fluid_track(int num)
 {
     fluid_track_t *track;
     track = FLUID_NEW(fluid_track_t);
-    if (track == NULL) {
+
+    if(track == NULL)
+    {
         return NULL;
     }
+
     track->name = NULL;
     track->num = num;
     track->first = NULL;
@@ -1062,17 +1358,14 @@ new_fluid_track(int num)
 /*
  * delete_fluid_track
  */
-int
+void
 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_return_if_fail(track != NULL);
+
+    FLUID_FREE(track->name);
+    delete_fluid_midi_event(track->first);
     FLUID_FREE(track);
-    return FLUID_OK;
 }
 
 /*
@@ -1082,32 +1375,31 @@ int
 fluid_track_set_name(fluid_track_t *track, char *name)
 {
     int len;
-    if (track->name != NULL) {
+
+    if(track->name != NULL)
+    {
         FLUID_FREE(track->name);
     }
-    if (name == NULL) {
+
+    if(name == NULL)
+    {
         track->name = NULL;
         return FLUID_OK;
     }
+
     len = FLUID_STRLEN(name);
     track->name = FLUID_MALLOC(len + 1);
-    if (track->name == NULL) {
+
+    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
  */
@@ -1116,29 +1408,14 @@ fluid_track_get_duration(fluid_track_t *track)
 {
     int time = 0;
     fluid_midi_event_t *evt = track->first;
-    while (evt != NULL) {
+
+    while(evt != NULL)
+    {
         time += evt->dtime;
         evt = evt->next;
     }
-    return time;
-}
 
-/*
- * 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;
+    return time;
 }
 
 /*
@@ -1148,25 +1425,20 @@ int
 fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt)
 {
     evt->next = NULL;
-    if (track->first == NULL) {
+
+    if(track->first == NULL)
+    {
         track->first = evt;
         track->cur = evt;
         track->last = evt;
-    } else {
+    }
+    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;
+    return FLUID_OK;
 }
 
 /*
@@ -1175,9 +1447,11 @@ fluid_track_first_event(fluid_track_t *track)
 fluid_midi_event_t *
 fluid_track_next_event(fluid_track_t *track)
 {
-    if (track->cur != NULL) {
+    if(track->cur != NULL)
+    {
         track->cur = track->cur->next;
     }
+
     return track->cur;
 }
 
@@ -1197,17 +1471,31 @@ 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)
+                        fluid_synth_t *synth,
+                        fluid_player_t *player,
+                        unsigned int ticks)
 {
     int status = FLUID_OK;
     fluid_midi_event_t *event;
+    int seeking = player->seek_ticks >= 0;
+
+    if(seeking)
+    {
+        ticks = player->seek_ticks; /* update target ticks */
+
+        if(track->ticks > ticks)
+        {
+            fluid_track_reset(track);    /* reset track if seeking backwards */
+        }
+    }
 
-    while (1) {
+    while(1)
+    {
 
         event = track->cur;
-        if (event == NULL) {
+
+        if(event == NULL)
+        {
             return status;
         }
 
@@ -1218,25 +1506,37 @@ fluid_track_send_events(fluid_track_t *track,
         /*                    event->dtime, */
         /*                    track->ticks + event->dtime); */
 
-        if (track->ticks + event->dtime > ticks) {
+        if(track->ticks + event->dtime > ticks)
+        {
             return status;
         }
 
         track->ticks += event->dtime;
 
-        if (!player || event->type == MIDI_EOT) {
+        if(!player || event->type == MIDI_EOT)
+        {
         }
-        else if (event->type == MIDI_SET_TEMPO) {
-            fluid_player_set_midi_tempo(player, event->param1);
+        else if(seeking && (event->type == NOTE_ON || event->type == NOTE_OFF))
+        {
+            /* skip on/off messages */
         }
-        else {
-            if (player->playback_callback)
+        else
+        {
+            if(player->playback_callback)
+            {
                 player->playback_callback(player->playback_userdata, event);
+            }
+        }
+
+        if(event->type == MIDI_SET_TEMPO)
+        {
+            fluid_player_set_midi_tempo(player, event->param1);
         }
 
         fluid_track_next_event(track);
 
     }
+
     return status;
 }
 
@@ -1244,6 +1544,14 @@ fluid_track_send_events(fluid_track_t *track,
  *
  *     fluid_player
  */
+static void
+fluid_player_handle_reset_synth(void *data, const char *name, int value)
+{
+    fluid_player_t *player = data;
+    fluid_return_if_fail(player != NULL);
+
+    player->reset_synth_between_songs = value;
+}
 
 /**
  * Create a new MIDI player.
@@ -1256,16 +1564,22 @@ new_fluid_player(fluid_synth_t *synth)
     int i;
     fluid_player_t *player;
     player = FLUID_NEW(fluid_player_t);
-    if (player == NULL) {
+
+    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++) {
+
+    for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++)
+    {
         player->track[i] = NULL;
     }
+
     player->synth = synth;
     player->system_timer = NULL;
     player->sample_timer = NULL;
@@ -1277,13 +1591,16 @@ new_fluid_player(fluid_synth_t *synth)
     player->deltatime = 4.0;
     player->cur_msec = 0;
     player->cur_ticks = 0;
+    player->seek_ticks = -1;
     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");
+                               "player.timing-source", "system");
 
     fluid_settings_getint(synth->settings, "player.reset-synth", &i);
-    player->reset_synth_between_songs = i;
+    fluid_player_handle_reset_synth(player, NULL, i);
+    
+    fluid_settings_callback_int(synth->settings, "player.reset-synth",
+                                fluid_player_handle_reset_synth, player);
 
     return player;
 }
@@ -1291,23 +1608,22 @@ new_fluid_player(fluid_synth_t *synth)
 /**
  * Delete a MIDI player instance.
  * @param player MIDI player instance
- * @return Always returns #FLUID_OK
  */
-int
+void
 delete_fluid_player(fluid_player_t *player)
 {
     fluid_list_t *q;
-    fluid_playlist_item* pi;
+    fluid_playlist_item *pi;
+
+    fluid_return_if_fail(player != NULL);
 
-    if (player == NULL) {
-        return FLUID_OK;
-    }
     fluid_player_stop(player);
     fluid_player_reset(player);
 
-    while (player->playlist != NULL) {
+    while(player->playlist != NULL)
+    {
         q = player->playlist->next;
-        pi = (fluid_playlist_item*) player->playlist->data;
+        pi = (fluid_playlist_item *) player->playlist->data;
         FLUID_FREE(pi->filename);
         FLUID_FREE(pi->buffer);
         FLUID_FREE(pi);
@@ -1316,7 +1632,6 @@ delete_fluid_player(fluid_player_t *player)
     }
 
     FLUID_FREE(player);
-    return FLUID_OK;
 }
 
 /**
@@ -1327,14 +1642,12 @@ 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_register_str(settings, "player.timing-source", "sample", 0);
     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);
+    fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, FLUID_HINT_TOGGLED);
 }
 
 
@@ -1343,12 +1656,15 @@ fluid_player_reset(fluid_player_t *player)
 {
     int i;
 
-    for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
-        if (player->track[i] != NULL) {
+    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; */
@@ -1366,52 +1682,33 @@ fluid_player_reset(fluid_player_t *player)
 int
 fluid_player_add_track(fluid_player_t *player, fluid_track_t *track)
 {
-    if (player->ntracks < MAX_NUMBER_OF_TRACKS) {
+    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;
+    else
+    {
+        return FLUID_FAILED;
     }
 }
 
 /**
- * Change the MIDI callback function. This is usually set to 
+ * 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 
+ * 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. 
+ * 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)
+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;
@@ -1428,8 +1725,10 @@ 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) {
+    char *f = FLUID_STRDUP(midifile);
+
+    if(!pi || !f)
+    {
         FLUID_FREE(pi);
         FLUID_FREE(f);
         FLUID_LOG(FLUID_PANIC, "Out of memory");
@@ -1453,12 +1752,14 @@ fluid_player_add(fluid_player_t *player, const char *midifile)
  * @return #FLUID_OK or #FLUID_FAILED
  */
 int
-fluid_player_add_mem(fluid_player_tplayer, const void *buffer, size_t len)
+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) {
+
+    if(!pi || !buf_copy)
+    {
         FLUID_FREE(pi);
         FLUID_FREE(buf_copy);
         FLUID_LOG(FLUID_PANIC, "Out of memory");
@@ -1480,28 +1781,33 @@ int
 fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
 {
     fluid_midi_file *midifile;
-    charbuffer;
+    char *buffer;
     size_t buffer_length;
     int buffer_owned;
 
-    if (item->filename != NULL)
+    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);
+                  item->filename);
         /* Read the entire contents of the file into the buffer */
         fp = FLUID_FOPEN(item->filename, "rb");
-        if (fp == NULL) {
+
+        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)
+
+        if(buffer == NULL)
         {
             FLUID_FCLOSE(fp);
             return FLUID_FAILED;
         }
+
         buffer_owned = 1;
         FLUID_FCLOSE(fp);
     }
@@ -1509,7 +1815,7 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
     {
         /* 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);
+                  __FILE__, __LINE__, item->buffer);
         buffer = (char *) item->buffer;
         buffer_length = item->buffer_len;
         /* Do not free the buffer (it is owned by the playlist) */
@@ -1517,59 +1823,83 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
     }
 
     midifile = new_fluid_midi_file(buffer, buffer_length);
-    if (midifile == NULL) {
-        if (buffer_owned) {
+
+    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) {
+    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) {
+
+    if(buffer_owned)
+    {
         FLUID_FREE(buffer);
     }
+
     return FLUID_OK;
 }
 
-static void
+void
 fluid_player_advancefile(fluid_player_t *player)
 {
-    if (player->playlist == NULL) {
+    if(player->playlist == NULL)
+    {
         return; /* No files to play */
     }
-    if (player->currentfile != NULL) {
+
+    if(player->currentfile != NULL)
+    {
         player->currentfile = fluid_list_next(player->currentfile);
     }
-    if (player->currentfile == NULL) {
-        if (player->loop == 0) {
+
+    if(player->currentfile == NULL)
+    {
+        if(player->loop == 0)
+        {
             return; /* We're done playing */
         }
-        if (player->loop > 0) {
+
+        if(player->loop > 0)
+        {
             player->loop--;
         }
+
         player->currentfile = player->playlist;
     }
 }
 
-static void
+void
 fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)
 {
-    fluid_playlist_itemcurrent_playitem;
+    fluid_playlist_item *current_playitem;
     int i;
 
-    do {
+    do
+    {
         fluid_player_advancefile(player);
-        if (player->currentfile == NULL) {
+
+        if(player->currentfile == NULL)
+        {
             /* Failed to find next song, probably since we're finished */
             player->status = FLUID_PLAYER_DONE;
             return;
@@ -1577,7 +1907,8 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)
 
         fluid_player_reset(player);
         current_playitem = (fluid_playlist_item *) player->currentfile->data;
-    } while (fluid_player_load(player, current_playitem) != FLUID_OK);
+    }
+    while(fluid_player_load(player, current_playitem) != FLUID_OK);
 
     /* Successfully loaded midi file */
 
@@ -1586,18 +1917,20 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)
     player->start_ticks = 0;
     player->cur_ticks = 0;
 
-    if (player->reset_synth_between_songs) {
+    if(player->reset_synth_between_songs)
+    {
         fluid_synth_system_reset(player->synth);
     }
 
-    for (i = 0; i < player->ntracks; i++) {
-        if (player->track[i] != NULL) {
+    for(i = 0; i < player->ntracks; i++)
+    {
+        if(player->track[i] != NULL)
+        {
             fluid_track_reset(player->track[i]);
         }
     }
 }
 
-
 /*
  * fluid_player_callback
  */
@@ -1613,36 +1946,61 @@ fluid_player_callback(void *data, unsigned int msec)
     synth = player->synth;
 
     loadnextfile = player->currentfile == NULL ? 1 : 0;
-    do {
-        if (loadnextfile) {
+
+    do
+    {
+        if(loadnextfile)
+        {
             loadnextfile = 0;
             fluid_player_playlist_load(player, msec);
-            if (player->currentfile == NULL) {
+
+            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));
+                             + (int)((double)(player->cur_msec - player->start_msec)
+                                     / player->deltatime + 0.5)); /* 0.5 to average overall error when casting */
 
-        for (i = 0; i < player->ntracks; i++) {
-            if (!fluid_track_eot(player->track[i])) {
+        if(player->seek_ticks >= 0)
+        {
+            fluid_synth_all_sounds_off(synth, -1); /* avoid hanging notes */
+        }
+
+        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(fluid_track_send_events(player->track[i], synth, player,
+                                           player->cur_ticks) != FLUID_OK)
+                {
                     /* */
                 }
             }
         }
 
-        if (status == FLUID_PLAYER_DONE) {
+        if(player->seek_ticks >= 0)
+        {
+            player->start_ticks = player->seek_ticks;   /* tick position of last tempo value (which is now) */
+            player->cur_ticks = player->seek_ticks;
+            player->begin_msec = msec;      /* only used to calculate the duration of playing */
+            player->start_msec = msec;      /* should be the (synth)-time of the last tempo change */
+            player->seek_ticks = -1;        /* clear seek_ticks */
+        }
+
+        if(status == FLUID_PLAYER_DONE)
+        {
             FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__,
-                    __LINE__, (msec - player->begin_msec) / 1000.0);
+                      __LINE__, (msec - player->begin_msec) / 1000.0);
             loadnextfile = 1;
         }
-    } while (loadnextfile);
+    }
+    while(loadnextfile);
 
     player->status = status;
 
@@ -1657,30 +2015,39 @@ fluid_player_callback(void *data, unsigned int msec)
 int
 fluid_player_play(fluid_player_t *player)
 {
-    if (player->status == FLUID_PLAYER_PLAYING) {
+    if(player->status == FLUID_PLAYER_PLAYING)
+    {
         return FLUID_OK;
     }
 
-    if (player->playlist == NULL) {
+    if(player->playlist == NULL)
+    {
         return FLUID_OK;
     }
 
     player->status = FLUID_PLAYER_PLAYING;
 
-    if (player->use_system_timer) {
+    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) {
+                                               fluid_player_callback, (void *) player, TRUE, FALSE, TRUE);
+
+        if(player->system_timer == NULL)
+        {
             return FLUID_FAILED;
         }
-    } else {
+    }
+    else
+    {
         player->sample_timer = new_fluid_sample_timer(player->synth,
-                fluid_player_callback, (void *) player);
+                               fluid_player_callback, (void *) player);
 
-        if (player->sample_timer == NULL) {
+        if(player->sample_timer == NULL)
+        {
             return FLUID_FAILED;
         }
     }
+
     return FLUID_OK;
 }
 
@@ -1692,12 +2059,16 @@ fluid_player_play(fluid_player_t *player)
 int
 fluid_player_stop(fluid_player_t *player)
 {
-    if (player->system_timer != NULL) {
+    if(player->system_timer != NULL)
+    {
         delete_fluid_timer(player->system_timer);
     }
-    if (player->sample_timer != NULL) {
+
+    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;
@@ -1717,13 +2088,35 @@ fluid_player_get_status(fluid_player_t *player)
 }
 
 /**
- * Enable looping of a MIDI player 
+ * Seek in the currently playing file.
+ * @param player MIDI player instance
+ * @param ticks the position to seek to in the current file
+ * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file,
+ *   #FLUID_OK otherwise
+ * @since 2.0.0
+ *
+ * The actual seek is performed during the player_callback.
+ */
+int fluid_player_seek(fluid_player_t *player, int ticks)
+{
+    if(ticks < 0 || ticks > fluid_player_get_total_ticks(player))
+    {
+        return FLUID_FAILED;
+    }
+
+    player->seek_ticks = ticks;
+    return FLUID_OK;
+}
+
+
+/**
+ * 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 
+ * 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)
@@ -1746,8 +2139,8 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)
     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);
+              "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;
 }
@@ -1758,10 +2151,9 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)
  * @param bpm Tempo in beats per minute
  * @return Always returns #FLUID_OK
  */
-int
-fluid_player_set_bpm(fluid_player_t *player, int bpm)
+int fluid_player_set_bpm(fluid_player_t *player, int bpm)
 {
-    return fluid_player_set_midi_tempo(player, (int) ((double) 60 * 1e6 / bpm));
+    return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm));
 }
 
 /**
@@ -1772,21 +2164,82 @@ fluid_player_set_bpm(fluid_player_t *player, int bpm)
 int
 fluid_player_join(fluid_player_t *player)
 {
-    if (player->system_timer) {
+    if(player->system_timer)
+    {
         return fluid_timer_join(player->system_timer);
-    } else if (player->sample_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
+        while(player->status != FLUID_PLAYER_DONE)
+        {
+            fluid_msleep(10);
         }
     }
+
     return FLUID_OK;
 }
 
+/**
+ * Get the number of tempo ticks passed.
+ * @param player MIDI player instance
+ * @return The number of tempo ticks passed
+ * @since 1.1.7
+ */
+int fluid_player_get_current_tick(fluid_player_t *player)
+{
+    return player->cur_ticks;
+}
+
+/**
+ * Looks through all available MIDI tracks and gets the absolute tick of the very last event to play.
+ * @param player MIDI player instance
+ * @return Total tick count of the sequence
+ * @since 1.1.7
+ */
+int fluid_player_get_total_ticks(fluid_player_t *player)
+{
+    int i;
+    int maxTicks = 0;
+
+    for(i = 0; i < player->ntracks; i++)
+    {
+        if(player->track[i] != NULL)
+        {
+            int ticks = fluid_track_get_duration(player->track[i]);
+
+            if(ticks > maxTicks)
+            {
+                maxTicks = ticks;
+            }
+        }
+    }
+
+    return maxTicks;
+}
+
+/**
+ * Get the tempo of a MIDI player in beats per minute.
+ * @param player MIDI player instance
+ * @return MIDI player tempo in BPM
+ * @since 1.1.7
+ */
+int fluid_player_get_bpm(fluid_player_t *player)
+{
+    return (int)(60e6 / player->miditempo);
+}
+
+/**
+ * Get the tempo of a MIDI player.
+ * @param player MIDI player instance
+ * @return Tempo of the MIDI player (in microseconds per quarter note, as per MIDI file spec)
+ * @since 1.1.7
+ */
+int fluid_player_get_midi_tempo(fluid_player_t *player)
+{
+    return player->miditempo;
+}
+
 /************************************************************************
  *       MIDI PARSER
  *
@@ -1796,14 +2249,17 @@ fluid_player_join(fluid_player_t *player)
  * new_fluid_midi_parser
  */
 fluid_midi_parser_t *
-new_fluid_midi_parser ()
+new_fluid_midi_parser()
 {
     fluid_midi_parser_t *parser;
     parser = FLUID_NEW(fluid_midi_parser_t);
-    if (parser == NULL) {
+
+    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;
 }
@@ -1811,11 +2267,12 @@ new_fluid_midi_parser ()
 /*
  * delete_fluid_midi_parser
  */
-int
+void
 delete_fluid_midi_parser(fluid_midi_parser_t *parser)
 {
+    fluid_return_if_fail(parser != NULL);
+
     FLUID_FREE(parser);
-    return FLUID_OK;
 }
 
 /**
@@ -1824,6 +2281,11 @@ delete_fluid_midi_parser(fluid_midi_parser_t *parser)
  * @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.
+ * @internal Do not expose this function to the public API. It would allow downstream
+ * apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out
+ * the needed midi information using the getter functions of fluid_midi_event_t.
+ * This parser however is incomplete as it e.g. only provides a limited buffer to
+ * store and process SYSEX data (i.e. doesnt allow arbitrary lengths)
  */
 fluid_midi_event_t *
 fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
@@ -1832,8 +2294,10 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
 
     /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle
      * of another message. */
-    if (c >= 0xF8) {
-        if (c == MIDI_SYSTEM_RESET) {
+    if(c >= 0xF8)
+    {
+        if(c == MIDI_SYSTEM_RESET)
+        {
             parser->event.type = c;
             parser->status = 0; /* clear the status */
             return &parser->event;
@@ -1843,30 +2307,40 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
     }
 
     /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */
-    if (c & 0x80) {
+    if(c & 0x80)
+    {
         /* Any status byte terminates SYSEX messages (not just 0xF7) */
-        if (parser->status == MIDI_SYSEX && parser->nr_bytes > 0) {
+        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
+                                       FALSE);
+        }
+        else
+        {
             event = NULL;
+        }
 
-        if (c < 0xF0) /* Voice category message? */
+        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;
+                                     - 1;
 
             parser->nr_bytes = 0; /* 0  bytes read so far */
-        } else if (c == MIDI_SYSEX) {
+        }
+        else if(c == MIDI_SYSEX)
+        {
             parser->status = MIDI_SYSEX;
             parser->nr_bytes = 0;
-        } else
-            parser->status = 0; /* Discard other system messages (0xF1-0xF7) */
+        }
+        else
+        {
+            parser->status = 0;    /* Discard other system messages (0xF1-0xF7) */
+        }
 
         return event; /* Return SYSEX event or NULL */
     }
@@ -1874,11 +2348,14 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
     /* Data/parameter byte */
 
     /* Discard data bytes for events we don't care about */
-    if (parser->status == 0)
+    if(parser->status == 0)
+    {
         return NULL;
+    }
 
     /* Max data size exceeded? (SYSEX messages only really) */
-    if (parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) {
+    if(parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE)
+    {
         parser->status = 0; /* Discard the rest of the message */
         return NULL;
     }
@@ -1887,8 +2364,10 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
     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)
+    if(parser->status == MIDI_SYSEX || parser->nr_bytes < parser->nr_bytes_total)
+    {
         return NULL;
+    }
 
     /* Event is complete, return it.
      * Running status byte MIDI feature is also handled here. */
@@ -1896,22 +2375,25 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
     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;
+    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;
@@ -1922,28 +2404,35 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
 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;
+    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;
 }
 #endif
index 90fcef7c916290e1b54135e54357f73344f75ccc..9e34a0ffeef7f60743669690a929aedbf1be9f6f 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -27,9 +27,9 @@
 
 typedef struct _fluid_midi_parser_t fluid_midi_parser_t;
 
-fluid_midi_parser_tnew_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);
+fluid_midi_parser_t *new_fluid_midi_parser(void);
+void 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);
 
 
 /***************************************************************
@@ -40,140 +40,146 @@ fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigne
 
 #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_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
+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 = 0x44,
+    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_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
+enum midi_meta_event
+{
+    MIDI_TEXT = 0x01,
+    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 */
+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 */
@@ -185,17 +191,18 @@ enum midi_sysex_manuf {
 /**
  * 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) */
+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 */
@@ -204,9 +211,9 @@ enum midi_sysex_tuning_msg_id {
 
 enum fluid_driver_status
 {
-  FLUID_MIDI_READY,
-  FLUID_MIDI_LISTENING,
-  FLUID_MIDI_DONE
+    FLUID_MIDI_READY,
+    FLUID_MIDI_LISTENING,
+    FLUID_MIDI_DONE
 };
 
 /***************************************************************
@@ -214,58 +221,40 @@ enum fluid_driver_status
  *         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 */
+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;
+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.
@@ -273,92 +262,73 @@ int fluid_track_send_events(fluid_track_t* track,
  */
 typedef struct
 {
-    charfilename;     /** Name of file (owned); NULL if data pre-loaded */
-    voidbuffer;       /** The MIDI file data (owned); NULL if filename */
+    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 */
+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 seek_ticks;           /* new position in tempo ticks (midi ticks) for seeking */
+    int start_ticks;          /* the number of tempo ticks passed at the last tempo change */
+    int cur_ticks;            /* the number of tempo ticks passed */
+    int 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);
+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
+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;
+    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) */
@@ -366,17 +336,15 @@ int fluid_midi_file_get_division(fluid_midi_file* midifile);
 /*
  * 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. */
+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 */
index 5931aa52a6f31832822cefe98e8202bae41e165f..9a48ed45d8840f4a973f498fdd03e04d73496549 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_chan.h"
 #include "fluid_voice.h"
 
-/*
- * fluid_mod_clone
+/**
+ * Clone the modulators destination, sources, flags and amount.
+ * @param mod the modulator to store the copy to
+ * @param src the source modulator to retrieve the information from
+ * @note The \c next member of \c mod will be left unchanged.
  */
 void
-fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src)
+fluid_mod_clone(fluid_mod_t *mod, const 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;
+    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 mod The modulator instance
  * @param src Modulator source (#fluid_mod_src or a MIDI controller number)
  * @param flags Flags determining mapping function and whether the source
  *   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_tmod, int src, int flags)
+fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags)
 {
-  mod->src1 = src;
-  mod->flags1 = flags;
+    mod->src1 = src;
+    mod->flags1 = flags;
 }
 
 /**
  * Set a modulator's secondary source controller and flags.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @param src Modulator source (#fluid_mod_src or a MIDI controller number)
  * @param flags Flags determining mapping function and whether the source
  *   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_tmod, int src, int flags)
+fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags)
 {
-  mod->src2 = src;
-  mod->flags2 = flags;
+    mod->src2 = src;
+    mod->flags2 = flags;
 }
 
 /**
  * Set the destination effect of a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @param dest Destination generator (#fluid_gen_type)
  */
 void
-fluid_mod_set_dest(fluid_mod_tmod, int dest)
+fluid_mod_set_dest(fluid_mod_t *mod, int dest)
 {
-  mod->dest = dest;
+    mod->dest = dest;
 }
 
 /**
  * Set the scale amount of a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @param amount Scale amount to assign
  */
 void
-fluid_mod_set_amount(fluid_mod_tmod, double amount)
+fluid_mod_set_amount(fluid_mod_t *mod, double amount)
 {
-  mod->amount = (double) amount;
+    mod->amount = (double) amount;
 }
 
 /**
  * Get the primary source value from a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @return The primary source value (#fluid_mod_src or a MIDI CC controller value).
  */
 int
-fluid_mod_get_source1(fluid_mod_t* mod)
+fluid_mod_get_source1(const fluid_mod_t *mod)
 {
-  return mod->src1;
+    return mod->src1;
 }
 
 /**
  * Get primary source flags from a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @return The primary source flags (#fluid_mod_flags).
  */
 int
-fluid_mod_get_flags1(fluid_mod_t* mod)
+fluid_mod_get_flags1(const fluid_mod_t *mod)
 {
-  return mod->flags1;
+    return mod->flags1;
 }
 
 /**
  * Get the secondary source value from a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value).
  */
 int
-fluid_mod_get_source2(fluid_mod_t* mod)
+fluid_mod_get_source2(const fluid_mod_t *mod)
 {
-  return mod->src2;
+    return mod->src2;
 }
 
 /**
  * Get secondary source flags from a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @return The secondary source flags (#fluid_mod_flags).
  */
 int
-fluid_mod_get_flags2(fluid_mod_t* mod)
+fluid_mod_get_flags2(const fluid_mod_t *mod)
 {
-  return mod->flags2;
+    return mod->flags2;
 }
 
 /**
  * Get destination effect from a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @return Destination generator (#fluid_gen_type)
  */
 int
-fluid_mod_get_dest(fluid_mod_t* mod)
+fluid_mod_get_dest(const fluid_mod_t *mod)
 {
-  return mod->dest;
+    return mod->dest;
 }
 
 /**
  * Get the scale amount from a modulator.
- * @param mod Modulator
+ * @param mod The modulator instance
  * @return Scale amount
  */
 double
-fluid_mod_get_amount(fluid_mod_t* mod)
+fluid_mod_get_amount(const fluid_mod_t *mod)
+{
+    return (double) mod->amount;
+}
+
+/*
+ * retrieves the initial value from the given source of the modulator
+ */
+static fluid_real_t
+fluid_mod_get_source_value(const unsigned char mod_src,
+                           const unsigned char mod_flags,
+                           fluid_real_t *range,
+                           const fluid_voice_t *voice
+                          )
 {
-  return (fluid_real_t) mod->amount;
+    const fluid_channel_t *chan = voice->channel;
+    fluid_real_t val;
+
+    if(mod_flags & FLUID_MOD_CC)
+    {
+        /* From MIDI Recommended Practice (RP-036) Default Pan Formula:
+         * "Since MIDI controller values range from 0 to 127, the exact center
+         * of the range, 63.5, cannot be represented. Therefore, the effective
+         * range for CC#10 is modified to be 1 to 127, and values 0 and 1 both
+         * pan hard left. The recommended method is to subtract 1 from the
+         * value of CC#10, and saturate the result to be non-negative."
+         *
+         * We treat the balance control in exactly the same way, as the same
+         * problem applies here as well.
+         */
+        if(mod_src == PAN_MSB || mod_src == BALANCE_MSB)
+        {
+            *range = 126;
+            val = fluid_channel_get_cc(chan, mod_src) - 1;
+
+            if(val < 0)
+            {
+                val = 0;
+            }
+        }
+        else
+        {
+            val = fluid_channel_get_cc(chan, mod_src);
+        }
+    }
+    else
+    {
+        switch(mod_src)
+        {
+        case FLUID_MOD_NONE:         /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */
+            val = *range;
+            break;
+
+        case FLUID_MOD_VELOCITY:
+            val = fluid_voice_get_actual_velocity(voice);
+            break;
+
+        case FLUID_MOD_KEY:
+            val = fluid_voice_get_actual_key(voice);
+            break;
+
+        case FLUID_MOD_KEYPRESSURE:
+            val = fluid_channel_get_key_pressure(chan, voice->key);
+            break;
+
+        case FLUID_MOD_CHANNELPRESSURE:
+            val = fluid_channel_get_channel_pressure(chan);
+            break;
+
+        case FLUID_MOD_PITCHWHEEL:
+            val = fluid_channel_get_pitch_bend(chan);
+            *range = 0x4000;
+            break;
+
+        case FLUID_MOD_PITCHWHEELSENS:
+            val = fluid_channel_get_pitch_wheel_sensitivity(chan);
+            break;
+
+        default:
+            FLUID_LOG(FLUID_ERR, "Unknown modulator source '%d', disabling modulator.", mod_src);
+            val = 0.0;
+        }
+    }
+
+    return val;
 }
 
+/**
+ * transforms the initial value retrieved by \c fluid_mod_get_source_value into [0.0;1.0]
+ */
+static fluid_real_t
+fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range)
+{
+    /* normalized value, i.e. usually in the range [0;1]
+     *
+     * if val was retrieved from pitch_bend then [-0.5;0.5]
+     */
+    const fluid_real_t val_norm = val / range;
+
+    /* we could also only switch case the lower nibble of mod_flags, however
+     * this would keep us from adding further mod types in the future
+     *
+     * instead just remove the flag(s) we already took care of
+     */
+    mod_flags &= ~FLUID_MOD_CC;
+
+    switch(mod_flags/* & 0x0f*/)
+    {
+    case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =0 */
+        val = val_norm;
+        break;
+
+    case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =1 */
+        val = 1.0f - val_norm;
+        break;
+
+    case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =2 */
+        val = -1.0f + 2.0f * val_norm;
+        break;
+
+    case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =3 */
+        val = 1.0f - 2.0f * val_norm;
+        break;
+
+    case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =4 */
+        val = fluid_concave(127 * (val_norm));
+        break;
+
+    case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =5 */
+        val = fluid_concave(127 * (1.0f - val_norm));
+        break;
+
+    case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =6 */
+        val = (val_norm > 0.5f) ?  fluid_concave(127 * 2 * (val_norm - 0.5f))
+              : -fluid_concave(127 * 2 * (0.5f - val_norm));
+        break;
+
+    case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =7 */
+        val = (val_norm > 0.5f) ? -fluid_concave(127 * 2 * (val_norm - 0.5f))
+              :  fluid_concave(127 * 2 * (0.5f - val_norm));
+        break;
+
+    case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =8 */
+        val = fluid_convex(127 * (val_norm));
+        break;
+
+    case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =9 */
+        val = fluid_convex(127 * (1.0f - val_norm));
+        break;
+
+    case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =10 */
+        val = (val_norm > 0.5f) ?  fluid_convex(127 * 2 * (val_norm - 0.5f))
+              : -fluid_convex(127 * 2 * (0.5f - val_norm));
+        break;
+
+    case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =11 */
+        val = (val_norm > 0.5f) ? -fluid_convex(127 * 2 * (val_norm - 0.5f))
+              :  fluid_convex(127 * 2 * (0.5f - val_norm));
+        break;
+
+    case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =12 */
+        val = (val_norm >= 0.5f) ? 1.0f : 0.0f;
+        break;
+
+    case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =13 */
+        val = (val_norm >= 0.5f) ? 0.0f : 1.0f;
+        break;
+
+    case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =14 */
+        val = (val_norm >= 0.5f) ? 1.0f : -1.0f;
+        break;
+
+    case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =15 */
+        val = (val_norm >= 0.5f) ? -1.0f : 1.0f;
+        break;
+
+    /*
+     * MIDI CCs only have a resolution of 7 bits. The closer val_norm gets to 1,
+     * the less will be the resulting change of the sinus. When using this sin()
+     * for scaling the cutoff frequency, there will be no audible difference between
+     * MIDI CCs 118 to 127. To avoid this waste of CCs multiply with 0.87
+     * (at least for unipolar) which makes sin() never get to 1.0 but to 0.98 which
+     * is close enough.
+     */
+    case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* custom sin(x) */
+        val = sin(M_PI / 2 * val_norm * 0.87);
+        break;
+
+    case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* custom */
+        val = sin(M_PI / 2 * (1.0f - val_norm) * 0.87);
+        break;
+
+    case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* custom */
+        val = (val_norm > 0.5f) ?  sin(M_PI / 2 * 2 * (val_norm - 0.5f))
+              : -sin(M_PI / 2 * 2 * (0.5f - val_norm));
+        break;
+
+    case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* custom */
+        val = (val_norm > 0.5f) ? -sin(M_PI / 2 * 2 * (val_norm - 0.5f))
+              :  sin(M_PI / 2 * 2 * (0.5f - val_norm));
+        break;
+
+    default:
+        FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags);
+        val = 0.0f;
+        break;
+    }
+
+    return val;
+}
 
 /*
  * fluid_mod_get_value
  */
 fluid_real_t
-fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice)
+fluid_mod_get_value(fluid_mod_t *mod, 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)) {
+    extern fluid_mod_t default_vel2filter_mod;
+
+    fluid_real_t v1 = 0.0, v2 = 1.0;
+    fluid_real_t range1 = 127.0, range2 = 127.0;
+
+    /* '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(fluid_mod_test_identity(mod, &default_vel2filter_mod))
+    {
 // 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;
+        /*
+            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;
     }
-*/
-     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;
-      }
-    }
+    /* get the initial value of the first source */
+    if(mod->src1 > 0)
+    {
+        v1 = fluid_mod_get_source_value(mod->src1, mod->flags1, &range1, voice);
 
-    /* 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;
+        /* transform the input value */
+        v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1);
     }
-  } 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;
-      }
+    else
+    {
+        return 0.0;
     }
 
-    /* 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;
+    /* no need to go further */
+    if(v1 == 0.0f)
+    {
+        return 0.0f;
+    }
+
+    /* get the second input source */
+    if(mod->src2 > 0)
+    {
+        v2 = fluid_mod_get_source_value(mod->src2, mod->flags2, &range2, voice);
+
+        /* transform the second input value */
+        v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2);
+    }
+    else
+    {
+        v2 = 1.0f;
     }
-  } else {
-    v2 = 1.0f;
-  }
 
-  /* it's as simple as that: */
-  return (fluid_real_t) mod->amount * v1 * v2;
+    /* 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 *
+new_fluid_mod()
 {
-  fluid_mod_t* mod = FLUID_NEW (fluid_mod_t);
-  if (mod == NULL) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return NULL;
-  }
-  return mod;
+    fluid_mod_t *mod = FLUID_NEW(fluid_mod_t);
+
+    if(mod == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    return mod;
 }
 
 /**
@@ -412,9 +469,21 @@ fluid_mod_new()
  * @param mod Modulator to free
  */
 void
-fluid_mod_delete (fluid_mod_t *mod)
+delete_fluid_mod(fluid_mod_t *mod)
+{
+    FLUID_FREE(mod);
+}
+
+/**
+ * Returns the size of the fluid_mod_t structure.
+ *
+ * Useful in low latency scenarios e.g. to allocate a modulator on the stack.
+ *
+ * @return Size of fluid_mod_t in bytes
+ */
+size_t fluid_mod_sizeof()
 {
-  FLUID_FREE(mod);
+    return sizeof(fluid_mod_t);
 }
 
 /**
@@ -426,63 +495,181 @@ fluid_mod_delete (fluid_mod_t *mod)
  * 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)
+fluid_mod_test_identity(const fluid_mod_t *mod1, const 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;
+}
+
+/**
+ * Check if the modulator has the given source.
+ *
+ * @param mod The modulator instance
+ * @param cc Boolean value indicating if ctrl is a CC controller or not
+ * @param ctrl The source to check for (if \c cc == FALSE : a value of type #fluid_mod_src, else the value of the MIDI CC to check for)
+ *
+ * @return TRUE if the modulator has the given source, FALSE otherwise.
+ */
+int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl)
+{
+    return
+        (
+            (
+                ((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))
+            )
+        );
+}
+
+/**
+ * Check if the modulator has the given destination.
+ * @param mod The modulator instance
+ * @param gen The destination generator of type #fluid_gen_type to check for
+ * @return TRUE if the modulator has the given destination, FALSE otherwise.
+ */
+int fluid_mod_has_dest(const fluid_mod_t *mod, int gen)
 {
-  return mod1->dest == mod2->dest
-    && mod1->src1 == mod2->src1
-    && mod1->src2 == mod2->src2
-    && mod1->flags1 == mod2->flags1
-    && mod1->flags2 == mod2->flags2;
+    return mod->dest == gen;
 }
 
+
 /* 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);
-};
+#ifdef DEBUG
+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_CUSTOM_FILTERQ:
+        printf("custom-Q");
+        break;
+
+    case GEN_CUSTOM_FILTERFC:
+        printf("custom-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_CUSTOM_BALANCE:
+        printf("balance");
+        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);
+};
+#endif
 
index 81c9f76c70c69d4f1c15e5d115f51a1bda8b7d42..e834baa5136e6e6690a45223ce2964881c367d92 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "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);
+/*
+ * 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;
+};
 
-#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)))))
+fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice);
 
-#define fluid_mod_has_dest(mod,gen)  ((mod)->dest == gen)
+#ifdef DEBUG
+void fluid_dump_modulator(fluid_mod_t *mod);
+#endif
 
 
 #endif /* _FLUID_MOD_H */
index 15f2fa7550a93fd3ccaec05055a8bb6230161d3f..08975cbb1784c6bfdf73b748283b7cd5abff7fc1 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -22,9 +22,7 @@
 #ifndef _FLUID_PHASE_H
 #define _FLUID_PHASE_H
 
-#if HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 /*
  *  phase
@@ -47,7 +45,7 @@
 * 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;
+typedef uint64_t fluid_phase_t;
 
 /* Purpose:
  * Set a to b.
@@ -56,26 +54,26 @@ typedef unsigned long long fluid_phase_t;
  */
 #define fluid_phase_set(a,b) a=b;
 
-#define fluid_phase_set_int(a, b)    ((a) = ((unsigned long long)(b)) << 32)
+#define fluid_phase_set_int(a, b)    ((a) = ((uint64_t)(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)
+  (a) = (((uint64_t)(b)) << 32) \
+  | (uint32_t) (((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))
+  ((((uint64_t)(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))
+  ((uint32_t)((_x) & 0xFFFFFFFF))
 
 /* Get the phase index with fractional rounding */
 #define fluid_phase_index_round(_x) \
@@ -108,7 +106,7 @@ typedef unsigned long long fluid_phase_t;
 /* Purpose:
  * Subtract b samples from a.
  */
-#define fluid_phase_sub_int(a, b)  ((a) -= (unsigned long long)(b) << 32)
+#define fluid_phase_sub_int(a, b)  ((a) -= (uint64_t)(b) << 32)
 
 /* Purpose:
  * Creates the expression a.index++. */
index 166007da3f812ffd66878d903189f8c418b3e566..51b4faa252d44653550fc3c70668f09a6dbb4811 100644 (file)
@@ -9,6 +9,7 @@
   Translated to C by Peter Hanappe, Mai 2001
 */
 
+#include "fluid_sys.h"
 #include "fluid_rev.h"
 
 /***************************************************************
 
 /* 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!!.
+ * 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. On some platforms this will lead to drastic spikes in the
+ * CPU load. This is especially noticable on some older Pentium (especially
+ * Pentium 3) processors, but even more modern Intel Core processors still show
+ * reduced performance with denormals. While there are compile-time switches to
+ * treat denormals as zero for a lot of processors, those are not available or
+ * effective on all platforms.
  *
- * 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.
+ * The fix used here: 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.
  */
-
-# 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;
+struct _fluid_allpass
+{
+    fluid_real_t feedback;
+    fluid_real_t *buffer;
+    int bufsize;
+    int bufidx;
 };
 
-void fluid_allpass_init(fluid_allpassallpass);
-void fluid_allpass_setfeedback(fluid_allpassallpass, fluid_real_t val);
-fluid_real_t fluid_allpass_getfeedback(fluid_allpassallpass);
+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_allpassallpass, int size)
+fluid_allpass_setbuffer(fluid_allpass *allpass, int size)
 {
-  allpass->bufidx = 0;
-  allpass->buffer = FLUID_ARRAY(fluid_real_t,size);
-  allpass->bufsize = size;
+    allpass->bufidx = 0;
+    allpass->buffer = FLUID_ARRAY(fluid_real_t, size);
+    allpass->bufsize = size;
 }
 
 static void
-fluid_allpass_release(fluid_allpassallpass)
+fluid_allpass_release(fluid_allpass *allpass)
 {
-  FLUID_FREE(allpass->buffer);
+    FLUID_FREE(allpass->buffer);
 }
 
 void
-fluid_allpass_init(fluid_allpassallpass)
+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. */
-  }
+    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_allpassallpass, fluid_real_t val)
+fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val)
 {
-  allpass->feedback = val;
+    allpass->feedback = val;
 }
 
 fluid_real_t
-fluid_allpass_getfeedback(fluid_allpassallpass)
+fluid_allpass_getfeedback(fluid_allpass *allpass)
 {
-  return allpass->feedback;
+    return allpass->feedback;
 }
 
 #define fluid_allpass_process(_allpass, _input) \
@@ -125,87 +103,76 @@ fluid_allpass_getfeedback(fluid_allpass* allpass)
   _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;
+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_combcomb, int size);
-void fluid_comb_release(fluid_combcomb);
-void fluid_comb_init(fluid_combcomb);
-void fluid_comb_setdamp(fluid_combcomb, fluid_real_t val);
-fluid_real_t fluid_comb_getdamp(fluid_combcomb);
-void fluid_comb_setfeedback(fluid_combcomb, fluid_real_t val);
-fluid_real_t fluid_comb_getfeedback(fluid_combcomb);
+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_combcomb, int size)
+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;
+    comb->filterstore = 0;
+    comb->bufidx = 0;
+    comb->buffer = FLUID_ARRAY(fluid_real_t, size);
+    comb->bufsize = size;
 }
 
 void
-fluid_comb_release(fluid_combcomb)
+fluid_comb_release(fluid_comb *comb)
 {
-  FLUID_FREE(comb->buffer);
+    FLUID_FREE(comb->buffer);
 }
 
 void
-fluid_comb_init(fluid_combcomb)
+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. */
-  }
+    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_combcomb, fluid_real_t val)
+fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val)
 {
-  comb->damp1 = val;
-  comb->damp2 = 1 - val;
+    comb->damp1 = val;
+    comb->damp2 = 1 - val;
 }
 
 fluid_real_t
-fluid_comb_getdamp(fluid_combcomb)
+fluid_comb_getdamp(fluid_comb *comb)
 {
-  return comb->damp1;
+    return comb->damp1;
 }
 
 void
-fluid_comb_setfeedback(fluid_combcomb, fluid_real_t val)
+fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val)
 {
-  comb->feedback = val;
+    comb->feedback = val;
 }
 
 fluid_real_t
-fluid_comb_getfeedback(fluid_combcomb)
+fluid_comb_getfeedback(fluid_comb *comb)
 {
-  return comb->feedback;
+    return comb->feedback;
 }
 
 #define fluid_comb_process(_comb, _input, _output) \
@@ -219,34 +186,21 @@ fluid_comb_getfeedback(fluid_comb* comb)
   _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
+/* scale_wet_width is a compensation weight factor to get an output
+   amplitude (wet) rather independent of the width setting.
+    0: the output amplitude is fully dependant on the width setting.
+   >0: the output amplitude is less dependant on the width setting.
+   With a scale_wet_width of 0.2 the output amplitude is rather
+   independent of width setting (see fluid_revmodel_update()).
+ */
+#define scale_wet_width 0.2f
 #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
 
 /*
@@ -280,221 +234,255 @@ fluid_comb_getfeedback(fluid_comb* comb)
 #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];
+struct _fluid_revmodel_t
+{
+    fluid_real_t roomsize;
+    fluid_real_t damp;
+    fluid_real_t level, 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_trev);
-static void fluid_revmodel_init(fluid_revmodel_trev);
-void fluid_set_revmodel_buffers(fluid_revmodel_trev, fluid_real_t sample_rate);
+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*
+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;
+    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;
+
+    return rev;
 }
 
 void
-delete_fluid_revmodel(fluid_revmodel_trev)
+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);
+    int i;
+    fluid_return_if_fail(rev != NULL);
+
+    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);
+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_trev)
+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]);
-  }
+    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_trev)
+fluid_revmodel_reset(fluid_revmodel_t *rev)
 {
-  fluid_revmodel_init(rev);
+    fluid_revmodel_init(rev);
 }
 
 void
-fluid_revmodel_processreplace(fluid_revmodel_trev, fluid_real_t *in,
-                            fluid_real_t *left_out, fluid_real_t *right_out)
+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);
+    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;
     }
-    /* 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_trev, fluid_real_t *in,
-                        fluid_real_t *left_out, fluid_real_t *right_out)
+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);
+    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;
     }
-
-    /* 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_trev)
+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);
-  }
+    /* Recalculate internal values after parameter change */
+    int i;
+
+    /* The stereo amplitude equation (wet1 and wet2 below) have a
+    tendency to produce high amplitude with high width values ( 1 < width < 100).
+    This results in an unwanted noisy output clipped by the audio card.
+    To avoid this dependency, we divide by (1 + rev->width * scale_wet_width)
+    Actually, with a scale_wet_width of 0.2, (regardless of level setting),
+    the output amplitude (wet) seems rather independent of width setting */
+    fluid_real_t wet = (rev->level * scalewet) /
+                       (1.0f + rev->width * scale_wet_width);
+
+    /* wet1 and wet2 are used by the stereo effect controled by the width setting
+    for producing a stereo ouptput from a monophonic reverb signal.
+    Please see the note above about a side effect tendency */
+    rev->wet1 = wet * (rev->width / 2.0f + 0.5f);
+    rev->wet2 = 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);
-  }
+    for(i = 0; i < numcombs; i++)
+    {
+        fluid_comb_setdamp(&rev->combL[i], rev->damp);
+        fluid_comb_setdamp(&rev->combR[i], rev->damp);
+    }
 }
 
 /**
@@ -508,37 +496,53 @@ fluid_revmodel_update(fluid_revmodel_t* rev)
  * @param level Reverb level
  */
 void
-fluid_revmodel_set(fluid_revmodel_t* rev, int set, float roomsize,
-                   float damping, float width, float level)
+fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
+                   fluid_real_t damping, fluid_real_t width, fluid_real_t level)
 {
-  if (set & FLUID_REVMODEL_SET_ROOMSIZE)
-    rev->roomsize = (roomsize * scaleroom) + offsetroom;
+    if(set & FLUID_REVMODEL_SET_ROOMSIZE)
+    {
+        /* With upper limit above 1.07, the output amplitude will grow
+        exponentially. So, keeping this upper limit to 1.0 seems sufficient
+        as it produces yet a long reverb time */
+        fluid_clip(roomsize, 0.0f, 1.0f);
+        rev->roomsize = (roomsize * scaleroom) + offsetroom;
+    }
 
-  if (set & FLUID_REVMODEL_SET_DAMPING)
-    rev->damp = damping * scaledamp;
+    if(set & FLUID_REVMODEL_SET_DAMPING)
+    {
+        rev->damp = damping * scaledamp;
+    }
 
-  if (set & FLUID_REVMODEL_SET_WIDTH)
-    rev->width = width;
+    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;
-  }
+    if(set & FLUID_REVMODEL_SET_LEVEL)
+    {
+        fluid_clip(level, 0.0f, 1.0f);
+        rev->level = level;
+    }
 
-  fluid_revmodel_update (rev);
+    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);
+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);
 }
index f977352cf503511a3f13fe2df02152d883d9d767..69c00ea71c6340f9bb68003f9e0369bfb76f248f 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -30,44 +30,48 @@ 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_ROOMSIZE       = 1 << 0,
+    FLUID_REVMODEL_SET_DAMPING        = 1 << 1,
+    FLUID_REVMODEL_SET_WIDTH          = 1 << 2,
+    FLUID_REVMODEL_SET_LEVEL          = 1 << 3,
+
+    /** Value for fluid_revmodel_set() which sets all reverb parameters. */
+    FLUID_REVMODEL_SET_ALL            =   FLUID_REVMODEL_SET_LEVEL
+                                          | FLUID_REVMODEL_SET_WIDTH
+                                          | FLUID_REVMODEL_SET_DAMPING
+                                          | FLUID_REVMODEL_SET_ROOMSIZE,
 } 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;
+typedef struct _fluid_revmodel_presets_t
+{
+    const 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_tnew_fluid_revmodel(fluid_real_t sample_rate);
-void delete_fluid_revmodel(fluid_revmodel_trev);
+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_trev, fluid_real_t *in,
-                             fluid_real_t *left_out, fluid_real_t *right_out);
+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_trev, 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_trev);
+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_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
+                        fluid_real_t damping, fluid_real_t width, fluid_real_t level);
 
-void fluid_revmodel_samplerate_change(fluid_revmodel_trev, fluid_real_t sample_rate);
+void fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate);
 
 #endif /* _FLUID_REV_H */
index f6c06dd76d16819fa2cdbb4a48cfce5070faadc0..71fd1e48a3b6aa170811d3c92d6c62e7b198ac99 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
  * only be one producer thread and one consumer thread.
  */
 fluid_ringbuffer_t *
-new_fluid_ringbuffer (int count, int elementsize)
+new_fluid_ringbuffer(int count, int elementsize)
 {
-  fluid_ringbuffer_t *queue;
+    fluid_ringbuffer_t *queue;
 
-  fluid_return_val_if_fail (count > 0, NULL);
+    fluid_return_val_if_fail(count > 0, NULL);
 
-  queue = FLUID_NEW (fluid_ringbuffer_t);
+    queue = FLUID_NEW(fluid_ringbuffer_t);
 
-  if (!queue)
-  {
-    FLUID_LOG (FLUID_ERR, "Out of memory");
-    return NULL;
-  }
+    if(!queue)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
 
-  queue->array = FLUID_MALLOC (elementsize * count);
+    queue->array = FLUID_MALLOC(elementsize * count);
 
-  if (!queue->array)
-  {
-    FLUID_FREE (queue);
-    FLUID_LOG (FLUID_ERR, "Out of memory");
-    return NULL;
-  }
+    if(!queue->array)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        delete_fluid_ringbuffer(queue);
+        return NULL;
+    }
 
-  /* Clear array, in case dynamic pointer reclaiming is being done */
-  FLUID_MEMSET (queue->array, 0, elementsize * count);
+    /* 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;
+    queue->totalcount = count;
+    queue->elementsize = elementsize;
+    fluid_atomic_int_set(&queue->count, 0);
+    queue->in = 0;
+    queue->out = 0;
 
-  return (queue);
+    return (queue);
 }
 
 /**
@@ -82,8 +82,9 @@ new_fluid_ringbuffer (int count, int elementsize)
  * producer threads will no longer access it.
  */
 void
-delete_fluid_ringbuffer (fluid_ringbuffer_t *queue)
+delete_fluid_ringbuffer(fluid_ringbuffer_t *queue)
 {
-  FLUID_FREE (queue->array);
-  FLUID_FREE (queue);
+    fluid_return_if_fail(queue != NULL);
+    FLUID_FREE(queue->array);
+    FLUID_FREE(queue);
 }
index bd43f8a2503a5da3e953fe02203c27af68557f06..e78d52291b059558112894bf82909990db551262 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "fluid_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;     
+    char *array;  /**< Queue array of arbitrary size elements */
+    int totalcount;       /**< Total count of elements in array */
+    fluid_atomic_int_t 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);
+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
+ * @param offset 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"
@@ -55,11 +55,11 @@ void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue);
  * 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)
+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);
+    return fluid_atomic_int_get(&queue->count) + offset >= queue->totalcount ? NULL
+           : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount);
 }
 
 /**
@@ -71,13 +71,16 @@ fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset)
  * 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_ringbuffer_next_inptr(fluid_ringbuffer_t *queue, int count)
 {
-  fluid_atomic_int_add (&queue->count, count);
+    fluid_atomic_int_add(&queue->count, count);
+
+    queue->in += count;
 
-  queue->in += count;
-  if (queue->in >= queue->totalcount)
-    queue->in -= queue->totalcount;
+    if(queue->in >= queue->totalcount)
+    {
+        queue->in -= queue->totalcount;
+    }
 }
 
 /**
@@ -86,9 +89,9 @@ fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count)
  * @return amount of items currently in queue
  */
 static FLUID_INLINE int
-fluid_ringbuffer_get_count (fluid_ringbuffer_t *queue)
+fluid_ringbuffer_get_count(fluid_ringbuffer_t *queue)
 {
-  return fluid_atomic_int_get (&queue->count);
+    return fluid_atomic_int_get(&queue->count);
 }
 
 
@@ -101,11 +104,11 @@ fluid_ringbuffer_get_count (fluid_ringbuffer_t *queue)
  * 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)
+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;
+    return fluid_ringbuffer_get_count(queue) == 0 ? NULL
+           : queue->array + queue->elementsize * queue->out;
 }
 
 
@@ -117,12 +120,14 @@ fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue)
  * 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_ringbuffer_next_outptr(fluid_ringbuffer_t *queue)
 {
-  fluid_atomic_int_add (&queue->count, -1);
+    fluid_atomic_int_add(&queue->count, -1);
 
-  if (++queue->out == queue->totalcount)
-    queue->out = 0;
+    if(++queue->out == queue->totalcount)
+    {
+        queue->out = 0;
+    }
 }
 
 #endif /* _FLUID_ringbuffer_H */
index ba8da9833398f05dd989e2eedec3fea0605a788d..e76871163785ecaebf401034aa7875709d9cf498 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_conv.h"
 #include "fluid_sys.h"
 
+
+static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks);
+
 /**
  * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise
  */
-static inline int
-fluid_rvoice_calc_amp(fluid_rvoice_tvoice)
+static FLUID_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.
-     */
+    fluid_real_t target_amp;   /* target amplitude */
 
-    /* If the loop amplitude is known, we can use it if the voice loop is within
-     * the sample loop
-     */
+    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. */
+    }
 
-    /* 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;
+    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_cb2amp(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
-      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;
+        fluid_real_t amplitude_that_reaches_noise_floor;
+        fluid_real_t amp_max;
+
+        target_amp = fluid_cb2amp(voice->dsp.attenuation)
+                     * fluid_cb2amp(FLUID_PEAK_ATTENUATION * (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_cb2amp(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;
+    /* 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");
+    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;
+    /* 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;
+    return 1;
 }
 
 
@@ -113,140 +124,164 @@ fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
  * TODO: Investigate whether this can be moved from rvoice to voice.
  */
 static void
-fluid_rvoice_check_sample_sanity(fluid_rvoice_tvoice)
+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;
+    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 */
+    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("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);
+    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;
+    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;
+    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!"); */
+    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(voice->dsp.start == voice->dsp.end)
+    {
+        fluid_rvoice_voiceoff(voice, NULL);
+        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(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); */
+    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;
+    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
@@ -259,406 +294,603 @@ fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice)
  *
  * @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 
+ * @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).
+ * routine is in (fluid_rvoice_dsp.c).
  */
 int
-fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
+fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
 {
-  int ticks = voice->envlfo.ticks;
-  int count;
+    int ticks = voice->envlfo.ticks;
+    int count, is_looping;
 
-  /******************* sample sanity check **********/
+    /******************* sample sanity check **********/
 
-  if (!voice->dsp.sample)
-    return 0;
-  if (voice->dsp.check_sample_sanity_flag)
-    fluid_rvoice_check_sample_sanity(voice);
+    if(!voice->dsp.sample)
+    {
+        return 0;
+    }
 
-  /******************* noteoff check ****************/
+    if(voice->dsp.check_sample_sanity_flag)
+    {
+        fluid_rvoice_check_sample_sanity(voice);
+    }
 
-  if (voice->envlfo.noteoff_ticks != 0 && 
-      voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) {
-    fluid_rvoice_noteoff(voice, 0);
-  }
+    /******************* noteoff check ****************/
 
-  voice->envlfo.ticks += FLUID_BUFSIZE;
+    if(voice->envlfo.noteoff_ticks != 0 &&
+            voice->envlfo.ticks >= voice->envlfo.noteoff_ticks)
+    {
+        fluid_rvoice_noteoff_LOCAL(voice, 0);
+    }
 
-  /******************* vol env **********************/
+    voice->envlfo.ticks += FLUID_BUFSIZE;
 
-  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;
+    /******************* vol env **********************/
 
-  /******************* mod env **********************/
+    fluid_adsr_env_calc(&voice->envlfo.volenv, 1);
+    fluid_check_fpe("voice_write vol env");
 
-  fluid_adsr_env_calc(&voice->envlfo.modenv, 0);
-  fluid_check_fpe ("voice_write mod env");
+    if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED)
+    {
+        return 0;
+    }
 
-  /******************* lfo **********************/
+    /******************* mod env **********************/
 
-  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");
+    fluid_adsr_env_calc(&voice->envlfo.modenv, 0);
+    fluid_check_fpe("voice_write mod env");
 
-  /******************* amplitude **********************/
+    /******************* lfo **********************/
 
-  count = fluid_rvoice_calc_amp(voice);
-  if (count <= 0) 
-    return count;
+    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");
 
-  /******************* phase **********************/
+    /******************* amplitude **********************/
 
-  /* 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;
+    count = fluid_rvoice_calc_amp(voice);
 
-  fluid_check_fpe ("voice_write phase calculation");
+    if(count <= 0)
+    {
+        return count;
+    }
 
-  /* 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;
+    /******************* 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 +
+                            voice->dsp.pitchoffset +
+                            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;
+
+    /******************* portamento ****************/
+    /* pitchoffset is updated if enabled.
+       Pitchoffset will be added to dsp pitch at next phase calculation time */
+
+    /* In most cases portamento will be disabled. Thus first verify that portamento is
+     * enabled before updating pitchoffset and before disabling portamento when necessary,
+     * in order to keep the performance loss at minimum.
+     * If the algorithm would first update pitchoffset and then verify if portamento
+     * needs to be disabled, there would be a significant performance drop on a x87 FPU
+     */
+    if(voice->dsp.pitchinc > 0.0f)
+    {
+        /* portamento is enabled, so update pitchoffset */
+        voice->dsp.pitchoffset += voice->dsp.pitchinc;
+
+        /* when pitchoffset reaches 0.0f, portamento is disabled */
+        if(voice->dsp.pitchoffset > 0.0f)
+        {
+            voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f;
+        }
+    }
+    else if(voice->dsp.pitchinc < 0.0f)
+    {
+        /* portamento is enabled, so update pitchoffset */
+        voice->dsp.pitchoffset += voice->dsp.pitchinc;
+
+        /* when pitchoffset reaches 0.0f, portamento is disabled */
+        if(voice->dsp.pitchoffset < 0.0f)
+        {
+            voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f;
+        }
+    }
+
+    fluid_check_fpe("voice_write phase calculation");
 
-  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);
+    /* 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;
+    }
 
-  /*********************** 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; 
+    /* voice is currently looping? */
+    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);
 
-  switch (voice->dsp.interp_method)
-  {
+    /*********************** 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. */
+
+    switch(voice->dsp.interp_method)
+    {
     case FLUID_INTERP_NONE:
-      count = fluid_rvoice_dsp_interpolate_none (&voice->dsp);
-      break;
+        count = fluid_rvoice_dsp_interpolate_none(&voice->dsp, dsp_buf, is_looping);
+        break;
+
     case FLUID_INTERP_LINEAR:
-      count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp);
-      break;
+        count = fluid_rvoice_dsp_interpolate_linear(&voice->dsp, dsp_buf, is_looping);
+        break;
+
     case FLUID_INTERP_4THORDER:
     default:
-      count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp);
-      break;
+        count = fluid_rvoice_dsp_interpolate_4th_order(&voice->dsp, dsp_buf, is_looping);
+        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;
+        count = fluid_rvoice_dsp_interpolate_7th_order(&voice->dsp, dsp_buf, is_looping);
+        break;
+    }
 
-  /*************** 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_check_fpe("voice_write interpolation");
 
-  fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
+    if(count == 0)
+    {
+        return count;
+    }
 
-  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);
 
-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];
-}
+    fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
 
-/**
- * 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];
-    }
-  }
+    /* additional custom filter - only uses the fixed modulator, no lfos... */
+    fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0);
+    fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count);
+
+    return count;
 }
 
 /**
  * Initialize buffers up to (and including) bufnum
  */
 static int
-fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_tbuffers, unsigned int bufnum)
+fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t *buffers, unsigned int bufnum)
 {
-  unsigned int i; 
+    unsigned int i;
 
-  if (bufnum < buffers->count) return FLUID_OK;
-  if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED;
+    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;
+    for(i = buffers->count; i <= bufnum; i++)
+    {
+        buffers->bufs[i].amp = 0.0f;
+    }
+
+    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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp)
 {
-  if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
-    return;
-  buffers->bufs[bufnum].amp = value;
+    fluid_rvoice_buffers_t *buffers = obj;
+    unsigned int bufnum = param[0].i;
+    fluid_real_t value = param[1].real;
+
+    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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping)
 {
-  if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
-    return;
-  buffers->bufs[bufnum].mapping = mapping;
+    fluid_rvoice_buffers_t *buffers = obj;
+    unsigned int bufnum = param[0].i;
+    int mapping = param[1].i;
+
+    if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
+    {
+        return;
+    }
+
+    buffers->bufs[bufnum].mapping = mapping;
 }
 
 
-void
-fluid_rvoice_reset(fluid_rvoice_t* voice)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset)
 {
-  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
+    fluid_rvoice_t *voice = obj;
+
+    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);
+    /* legato initialization */
+    voice->dsp.pitchoffset = 0.0;   /* portamento initialization */
+    voice->dsp.pitchinc = 0.0;
+
+    /* mod env initialization*/
+    fluid_adsr_env_reset(&voice->envlfo.modenv);
 
-  /* vol env initialization */
-  fluid_adsr_env_reset(&voice->envlfo.volenv);
+    /* 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);
+    /* 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);
+    /* Clear sample history in filter */
+    fluid_iir_filter_reset(&voice->resonant_filter);
+    fluid_iir_filter_reset(&voice->resonant_custom_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;
+    /* 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;
 }
 
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff)
+{
+    fluid_rvoice_t *rvoice = obj;
+    unsigned int min_ticks = param[0].i;
 
-void 
-fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks)
+    fluid_rvoice_noteoff_LOCAL(rvoice, min_ticks);
+}
+
+static void
+fluid_rvoice_noteoff_LOCAL(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(min_ticks > voice->envlfo.ticks)
+    {
+        /* Delay noteoff */
+        voice->envlfo.noteoff_ticks = min_ticks;
+        return;
+    }
 
-  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);
+    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) * fluid_cb2amp(lfo);
+            fluid_real_t env_value = - ((-200 * log(amp) / log(10.0) - lfo) / FLUID_PEAK_ATTENUATION - 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);
+}
+
+/**
+ * skips to Attack section
+ *
+ * Updates vol and attack data
+ * Correction on volume val to achieve equivalent amplitude at noteOn legato
+ *
+ * @param voice the synthesis voice to be updated
+*/
+static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voice)
+{
+    /* skips to Attack section */
+    /* Once in Attack section, current count must be reset, to be sure
+    that the section will be not be prematurely finished. */
+    fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK);
+    {
+        /* Correction on volume val to achieve equivalent amplitude at noteOn legato */
+        fluid_env_data_t *env_data;
+        fluid_real_t peak = fluid_cb2amp(voice->dsp.attenuation);
+        fluid_real_t prev_peak = fluid_cb2amp(voice->dsp.prev_attenuation);
+        voice->envlfo.volenv.val = (voice->envlfo.volenv.val  * prev_peak) / peak;
+        /* Correction on slope direction for Attack section */
+        env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK];
+
+        if(voice->envlfo.volenv.val <= 1.0f)
+        {
+            /* slope attack for legato note needs to be positive from val  up to 1 */
+            env_data->increment = 1.0f / env_data->count;
+            env_data->min = -1.0f;
+            env_data->max =  1.0f;
+        }
+        else
+        {
+            /* slope attack for legato note needs to be negative: from val  down to 1 */
+            env_data->increment = -voice->envlfo.volenv.val / env_data->count;
+            env_data->min = 1.0f;
+            env_data->max = voice->envlfo.volenv.val;
+        }
+    }
+}
+
+/**
+ * Used by legato Mode : multi_retrigger
+ *  see fluid_synth_noteon_mono_legato_multi_retrigger()
+ * @param voice the synthesis voice to be updated
+*/
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
+{
+    fluid_rvoice_t *voice = obj;
+    int section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
+
+    /*-------------------------------------------------------------------------
+     Section skip for volume envelope
+    --------------------------------------------------------------------------*/
+    if(section >= FLUID_VOICE_ENVHOLD)
+    {
+        /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new
+        volenv_val to achieve equivalent amplitude during the attack phase
+        for seamless volume transition. */
+        fluid_real_t amp_cb, env_value;
+        amp_cb = FLUID_PEAK_ATTENUATION *
+                 (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv));
+        env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */
+        fluid_clip(env_value, 0.0, 1.0);
+        fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);
+        /* next, skips to Attack section */
     }
-  }
-  fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
-  fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
+
+    /* skips to Attack section from any section */
+    /* Update vol and  attack data */
+    fluid_rvoice_local_retrigger_attack(voice);
+    /*-------------------------------------------------------------------------
+     Section skip for modulation envelope
+    --------------------------------------------------------------------------*/
+    /* Skips from any section to ATTACK section */
+    fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK);
+    /* Actually (v 1.1.6) all sections are linear, so there is no need to
+     correct val value. However soundfont 2.01/2.4 spec. says that Attack should
+     be convex (see issue #153  from Christian Collins). In the case Attack
+     section would be changed to a non linear shape it will be necessary to do
+     a correction for seamless val transition. Here is the place to do this */
 }
 
+/**
+ * sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc
+ * @param voice rvoice to set portamento.
+ * @param countinc increment count number.
+ * @param pitchoffset pitch offset to apply to voice dsp.pitch.
+ *
+ * Notes:
+ * 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...),
+ *   pitchoffset is accumulated in current dsp pitchoffset.
+ * 2) And to get constant portamento duration, dsp pitch increment is updated.
+*/
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento)
+{
+    fluid_rvoice_t *voice = obj;
+    unsigned int countinc = param[0].i;
+    fluid_real_t pitchoffset = param[1].real;
+
+    if(countinc)
+    {
+        voice->dsp.pitchoffset += pitchoffset;
+        voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc;
+    }
 
-void 
-fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value)
+    /* Then during the voice processing (in fluid_rvoice_write()),
+    dsp.pitchoffset will be incremented by dsp pitchinc. */
+}
+
+
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate)
 {
-  voice->dsp.output_rate = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->dsp.output_rate = value;
 }
 
-void 
-fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method)
 {
-  voice->dsp.interp_method = value;
+    fluid_rvoice_t *voice = obj;
+    int value = param[0].i;
+
+    voice->dsp.interp_method = value;
 }
 
-void 
-fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz)
 {
-  voice->dsp.root_pitch_hz = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->dsp.root_pitch_hz = value;
 }
 
-void 
-fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch)
 {
-  voice->dsp.pitch = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->dsp.pitch = value;
 }
 
 
-void 
-fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation)
 {
-  voice->dsp.attenuation = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->dsp.prev_attenuation = voice->dsp.attenuation;
+    voice->dsp.attenuation = value;
 }
 
-void 
-fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB)
 {
-  voice->dsp.min_attenuation_cB = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->dsp.min_attenuation_cB = value;
 }
 
-void 
-fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch)
 {
-  voice->envlfo.viblfo_to_pitch = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->envlfo.viblfo_to_pitch = value;
 }
 
-void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch)
 {
-  voice->envlfo.modlfo_to_pitch = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->envlfo.modlfo_to_pitch = value;
 }
 
-void 
-fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol)
 {
-  voice->envlfo.modlfo_to_vol = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->envlfo.modlfo_to_vol = value;
 }
 
-void 
-fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc)
 {
-  voice->envlfo.modlfo_to_fc = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->envlfo.modlfo_to_fc = value;
 }
 
-void 
-fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc)
 {
-  voice->envlfo.modenv_to_fc = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->envlfo.modenv_to_fc = value;
 }
 
-void 
-fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch)
 {
-  voice->envlfo.modenv_to_pitch = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
+
+    voice->envlfo.modenv_to_pitch = value;
 }
 
-void 
-fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain)
 {
-  voice->dsp.synth_gain = value;
+    fluid_rvoice_t *voice = obj;
+    fluid_real_t value = param[0].real;
 
-  /* 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;
+    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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start)
 {
-  voice->dsp.start = value;
-  voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+    fluid_rvoice_t *voice = obj;
+    int value = param[0].i;
+
+    voice->dsp.start = value;
+    voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
 }
 
-void 
-fluid_rvoice_set_end(fluid_rvoice_t* voice, int value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end)
 {
-  voice->dsp.end = value;
-  voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+    fluid_rvoice_t *voice = obj;
+    int value = param[0].i;
+
+    voice->dsp.end = value;
+    voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
 }
 
-void 
-fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart)
 {
-  voice->dsp.loopstart = value;
-  voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+    fluid_rvoice_t *voice = obj;
+    int value = param[0].i;
+
+    voice->dsp.loopstart = value;
+    voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
 }
 
-void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend)
 {
-  voice->dsp.loopend = value;
-  voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+    fluid_rvoice_t *voice = obj;
+    int value = param[0].i;
+
+    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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode)
 {
-  voice->dsp.samplemode = value;
-  voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
+    fluid_rvoice_t *voice = obj;
+    enum fluid_loop value = param[0].i;
+
+    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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample)
 {
-  voice->dsp.sample = value;
-  if (value) {
-    voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
-  }
+    fluid_rvoice_t *voice = obj;
+    fluid_sample_t *value = param[0].ptr;
+
+    voice->dsp.sample = value;
+
+    if(value)
+    {
+        voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
+    }
 }
 
-void 
-fluid_rvoice_voiceoff(fluid_rvoice_t* voice)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff)
 {
-  fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);
-  fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);
+    fluid_rvoice_t *voice = obj;
+
+    fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);
+    fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);
 }
 
 
index 4566cb1de334e9f4a5a6a7100a5c19c0e85ab8c7..bae3ac9390c797dd926296e68ab8edd23313fb1b 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -36,165 +36,190 @@ 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 :)
+ * 24 bits => 144-4 = 140 dB dynamic range => 1.e-7
+ * 1.e-7 * 2 == 2.e-7 :)
  */
-#define FLUID_NOISE_FLOOR 0.00003
+#define FLUID_NOISE_FLOOR 2.e-7
 
-
-enum fluid_loop {
-  FLUID_UNLOOPED = 0,
-  FLUID_LOOP_DURING_RELEASE = 1,
-  FLUID_NOTUSED = 2,
-  FLUID_LOOP_UNTIL_RELEASE = 3
+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;
+    /* 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 */
+    /* interpolation method, as in fluid_interp in fluidsynth.h */
+    enum fluid_interp interp_method;
+    enum fluid_loop samplemode;
+
+    /* Flag that is set as soon as the first loop is completed. */
+    char has_looped;
 
+    /* Flag that initiates, that sample-related parameters have to be checked. */
+    char check_sample_sanity_flag;
 
-       /* Dynamic input to the interpolator below */
+    fluid_sample_t *sample;
 
-       fluid_real_t *dsp_buf;          /* buffer to store interpolated sample data to */
+    /* 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) */
 
-       fluid_real_t amp;                /* current linear amplitude */
-       fluid_real_t amp_incr;          /* amplitude increment value for the next FLUID_BUFSIZE samples */
+    /* Stuff needed for portamento calculations */
+    fluid_real_t pitchoffset;        /* the portamento range in midicents */
+    fluid_real_t pitchinc;           /* the portamento increment in midicents */
 
-       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;
+    /* 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 */
+
+    fluid_real_t attenuation;        /* the attenuation in centibels */
+    fluid_real_t prev_attenuation;   /* the previous attenuation in centibels
+                                       used by fluid_rvoice_multi_retrigger_attack() */
+    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 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 */
 };
 
 /* 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];
+    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
+/*
+ * Hard real-time 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;
+    fluid_rvoice_envlfo_t envlfo;
+    fluid_rvoice_dsp_t dsp;
+    fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */
+    fluid_iir_filter_t resonant_custom_filter; /* optional custom/general-purpose IIR resonant 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);
+int fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf);
+
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping);
+
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample);
 
 /* defined in fluid_rvoice_dsp.c */
+void fluid_rvoice_dsp_config(void);
+int fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping);
+int fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping);
+int fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping);
+int fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping);
 
-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);
+
+/*
+ * Combines the most significant 16 bit part of a sample with a potentially present
+ * least sig. 8 bit part in order to create a 24 bit sample.
+ */
+static FLUID_INLINE int32_t
+fluid_rvoice_get_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx)
+{
+    /* cast sample to unsigned type, so we can safely shift and bitwise or
+     * without relying on undefined behaviour (should never happen anyway ofc...) */
+    uint32_t msb = (uint32_t)dsp_msb[idx];
+    uint8_t lsb = 0U;
+
+    /* most soundfonts have 16 bit samples, assume that it's unlikely we
+     * experience 24 bit samples here */
+    if(FLUID_UNLIKELY(dsp_lsb != NULL))
+    {
+        lsb = (uint8_t)dsp_lsb[idx];
+    }
+
+    return (int32_t)((msb << 8) | lsb);
+}
 
 #endif
index df7da5022decf971561d094785c6ad12f8b1ea58..cc162829f6d5085d44d6aa14739b333207685ce8 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -28,7 +28,7 @@
  * Interpolates audio data (obtains values between the samples of the original
  * waveform data).
  *
- * Variables loaded from the voice structure (assigned in fluid_voice_write()):
+ * Variables loaded from the voice structure (assigned in fluid_rvoice_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).
@@ -61,122 +61,138 @@ static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7];
 
 
 /* Initializes interpolation tables */
-void fluid_rvoice_dsp_config (void)
+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++)
+    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++)
     {
-      /* 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;
+        /* 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)
+            {
+                double arg = M_PI * i_shifted;
+                v = (fluid_real_t)sin(arg) / (arg);
+                /* Hanning window */
+                v *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * arg / (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]);
-  }
+
+    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");
+    fluid_check_fpe("interpolation table calculation");
+}
+
+static FLUID_INLINE fluid_real_t
+fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx)
+{
+    int32_t sample = fluid_rvoice_get_sample(dsp_msb, dsp_lsb, idx);
+    return (fluid_real_t)sample;
 }
 
 /* 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_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping)
 {
-  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++)
+    fluid_phase_t dsp_phase = voice->phase;
+    fluid_phase_t dsp_phase_incr;
+    short int *dsp_data = voice->sample->data;
+    char *dsp_data24 = voice->sample->data24;
+    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;
+
+    /* Convert playback "speed" floating point value to phase index/fract */
+    fluid_phase_set_float(dsp_phase_incr, voice->phase_incr);
+
+    end_index = looping ? voice->loopend - 1 : voice->end;
+
+    while(1)
     {
-      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;
+        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 * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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;
+        }
     }
 
-    /* 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;
-    }
+    voice->phase = dsp_phase;
+    voice->amp = dsp_amp;
 
-    /* break out if filled buffer */
-    if (dsp_i >= FLUID_BUFSIZE) break;
-  }
-
-  voice->phase = dsp_phase;
-  voice->amp = dsp_amp;
-
-  return (dsp_i);
+    return (dsp_i);
 }
 
 /* Straight line interpolation.
@@ -184,88 +200,99 @@ fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice)
  * smaller if end of sample occurs).
  */
 int
-fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice)
+fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping)
 {
-  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++)
+    fluid_phase_t dsp_phase = voice->phase;
+    fluid_phase_t dsp_phase_incr;
+    short int *dsp_data = voice->sample->data;
+    char *dsp_data24 = voice->sample->data24;
+    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;
+    fluid_real_t point;
+    const fluid_real_t *FLUID_RESTRICT coeffs;
+
+    /* Convert playback "speed" floating point value to phase index/fract */
+    fluid_phase_set_float(dsp_phase_incr, voice->phase_incr);
+
+    /* 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)
     {
-      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;
+        point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart);    /* loop start */
     }
-
-    /* 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++)
+    else
     {
-      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 */
+        point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end);    /* duplicate end for samples no longer looping */
     }
 
-    if (!looping) break;       /* break out if not looping (end of sample) */
-
-    /* go back to loop start (if past */
-    if (dsp_phase_index > end_index)
+    while(1)
     {
-      fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
-      voice->has_looped = 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                        + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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 */
     }
 
-    /* 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;
 
-  voice->phase = dsp_phase;
-  voice->amp = dsp_amp;
-
-  return (dsp_i);
+    return (dsp_i);
 }
 
 /* 4th order (cubic) interpolation.
@@ -273,149 +300,158 @@ fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice)
  * smaller if end of sample occurs).
  */
 int
-fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
+fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping)
 {
-  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++)
+    fluid_phase_t dsp_phase = voice->phase;
+    fluid_phase_t dsp_phase_incr;
+    short int *dsp_data = voice->sample->data;
+    char *dsp_data24 = voice->sample->data24;
+    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;
+    fluid_real_t start_point, end_point1, end_point2;
+    const fluid_real_t *FLUID_RESTRICT coeffs;
+
+    /* Convert playback "speed" floating point value to phase index/fract */
+    fluid_phase_set_float(dsp_phase_incr, voice->phase_incr);
+
+    /* 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 */
     {
-      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;
+        start_index = voice->loopstart;
+        start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); /* last point in loop (wrap around) */
     }
-
-    /* interpolate the sequence of sample points */
-    for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+    else
     {
-      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;
+        start_index = voice->start;
+        start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start);       /* just duplicate the point */
     }
 
-    /* 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++)
+    /* get points off the end (loop start if looping, duplicate point if end) */
+    if(looping)
     {
-      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_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart);
+        end_point2 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1);
     }
-
-    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++)
+    else
     {
-      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;
+        end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end);
+        end_point2 = end_point1;
     }
 
-    if (!looping) break;       /* break out if not looping (end of sample) */
-
-    /* go back to loop start */
-    if (dsp_phase_index > end_index)
+    while(1)
     {
-      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];
-      }
+        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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                              + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                              + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                              + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                              + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                              + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                              + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                              + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                              + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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 */
     }
 
-    /* break out if filled buffer */
-    if (dsp_i >= FLUID_BUFSIZE) break;
+    voice->phase = dsp_phase;
+    voice->amp = dsp_amp;
 
-    end_index -= 2;    /* set end back to third to last sample point */
-  }
-
-  voice->phase = dsp_phase;
-  voice->amp = dsp_amp;
-
-  return (dsp_i);
+    return (dsp_i);
 }
 
 /* 7th order interpolation.
@@ -423,253 +459,257 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
  * smaller if end of sample occurs).
  */
 int
-fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
+fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping)
 {
-  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++)
+    fluid_phase_t dsp_phase = voice->phase;
+    fluid_phase_t dsp_phase_incr;
+    short int *dsp_data = voice->sample->data;
+    char *dsp_data24 = voice->sample->data24;
+    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;
+    fluid_real_t start_points[3], end_points[3];
+    const fluid_real_t *FLUID_RESTRICT coeffs;
+
+    /* 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);
+
+    /* 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 */
     {
-      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 = voice->loopstart;
+        start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1);
+        start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2);
+        start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3);
     }
-
-    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++)
+    else
     {
-      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;
+        start_index = voice->start;
+        start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start);   /* just duplicate the start point */
+        start_points[1] = start_points[0];
+        start_points[2] = start_points[0];
     }
 
-    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++)
+    /* get the 3 points off the end (loop start if looping, duplicate point if end) */
+    if(looping)
     {
-      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_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart);
+        end_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1);
+        end_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 2);
     }
-
-    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++)
+    else
     {
-      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;
+        end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end);
+        end_points[1] = end_points[0];
+        end_points[2] = end_points[0];
     }
 
-    if (!looping) break;       /* break out if not looping (end of sample) */
-
-    /* go back to loop start */
-    if (dsp_phase_index > end_index)
+    while(1)
     {
-      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];
-      }
+        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] * start_points[2]
+                                + coeffs[1] * start_points[1]
+                                + coeffs[2] * start_points[0]
+                                + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                                + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)
+                                + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * start_points[1]
+                                + coeffs[1] * start_points[0]
+                                + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                                + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                                + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)
+                                + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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] * start_points[0]
+                                + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2)
+                                + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                                + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                                + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)
+                                + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3)
+                                + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2)
+                                + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                                + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                                + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)
+                                + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3)
+                                + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2)
+                                + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                                + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                                + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)
+                                + coeffs[6] * 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3)
+                                + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2)
+                                + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                                + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)
+                                + coeffs[5] * end_points[0]
+                                + coeffs[6] * 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_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3)
+                                + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2)
+                                + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1)
+                                + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+                                + coeffs[4] * end_points[0]
+                                + coeffs[5] * end_points[1]
+                                + coeffs[6] * 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] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1);
+                start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2);
+                start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, 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 */
     }
 
-    /* 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);
+    /* 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;
+    voice->phase = dsp_phase;
+    voice->amp = dsp_amp;
 
-  return (dsp_i);
+    return (dsp_i);
 }
index 65edb9dacb73490c8bc90e7a903923142bfa7bce..4a513778bdf87a23aa35dca1dcca90f9476a13d2 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_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; }
+static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event);
 
-void
-fluid_rvoice_event_dispatch(fluid_rvoice_event_tevent)
+static FLUID_INLINE 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);
+    event->method(event->object, event->param);
 }
 
 
 /**
  * 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 
+ * 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_eventhandler_push_int_real(fluid_rvoice_eventhandler_t *handler,
+                                        fluid_rvoice_function_t 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;
-}
+    fluid_rvoice_event_t local_event;
 
+    local_event.method = method;
+    local_event.object = object;
+    local_event.param[0].i = intparam;
+    local_event.param[1].real = realparam;
 
-int 
-fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler, 
-                                   void* method, void* object, void* ptr)
+    return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event);
+}
+
+int
+fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, fluid_rvoice_param_t param[MAX_EVENT_PARAMS])
 {
-  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;
+    fluid_rvoice_event_t local_event;
+
+    local_event.method = method;
+    local_event.object = object;
+    FLUID_MEMCPY(&local_event.param, param, sizeof(*param) * MAX_EVENT_PARAMS);
+
+    return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event);
 }
 
+int
+fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler,
+                                   fluid_rvoice_function_t method, void *object, void *ptr)
+{
+    fluid_rvoice_event_t local_event;
+
+    local_event.method = method;
+    local_event.object = object;
+    local_event.param[0].ptr = 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)
+    return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event);
+}
+
+static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event)
 {
-  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;
+    fluid_rvoice_event_t *event;
+    int old_queue_stored = fluid_atomic_int_add(&handler->queue_stored, 1);
+
+    event = fluid_ringbuffer_get_inptr(handler->queue, old_queue_stored);
+
+    if(event == NULL)
+    {
+        fluid_atomic_int_add(&handler->queue_stored, -1);
+        FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
+        return FLUID_FAILED; // Buffer full...
+    }
+
+    FLUID_MEMCPY(event, src_event, sizeof(*event));
+
+    return FLUID_OK;
 }
 
 
-static void 
-finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice)
+void
+fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, 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_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 *
+new_fluid_rvoice_eventhandler(int queuesize,
+                              int finished_voices_size, int bufs, int fx_bufs, int fx_units, fluid_real_t sample_rate, int extra_threads, int prio)
 {
-  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;
-  
+    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;
+
+    fluid_atomic_int_set(&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, fx_units, sample_rate, eventhandler, extra_threads, prio);
+
+    if(eventhandler->mixer == NULL)
+    {
+        goto error_recovery;
+    }
+
+    return eventhandler;
+
 error_recovery:
-  delete_fluid_rvoice_eventhandler(eventhandler);
-  return NULL;
+    delete_fluid_rvoice_eventhandler(eventhandler);
+    return NULL;
 }
 
-int 
-fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_thandler)
+int
+fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *handler)
 {
-  return fluid_ringbuffer_get_count(handler->queue);
+    return fluid_ringbuffer_get_count(handler->queue);
 }
 
 
@@ -268,26 +170,30 @@ fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t* handler)
  * Call fluid_rvoice_event_dispatch for all events in queue
  * @return number of events dispatched
  */
-int 
-fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_thandler)
+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;
+    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_thandler)
+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);
+    fluid_return_if_fail(handler != NULL);
+
+    delete_fluid_rvoice_mixer(handler->mixer);
+    delete_fluid_ringbuffer(handler->queue);
+    delete_fluid_ringbuffer(handler->finished_voices);
+    FLUID_FREE(handler);
 }
index e8693bcd210263036d0a288d5b28ee37d1bab26b..d1fd8d62cb3b042cdefbefad0bac9dcc3ab623db 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_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);
 
+struct _fluid_rvoice_event_t
+{
+    fluid_rvoice_function_t method;
+    void *object;
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
+};
 
-/**
- * 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
+/*
+ * Bridge between the renderer thread and the midi state thread.
+ * 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;
+struct _fluid_rvoice_eventhandler_t
+{
+    fluid_ringbuffer_t *queue; /**< List of fluid_rvoice_event_t */
+    fluid_atomic_int_t 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_tnew_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 *new_fluid_rvoice_eventhandler(
+    int queuesize, int finished_voices_size, int bufs,
+    int fx_bufs, int fx_units, fluid_real_t sample_rate, int, int);
 
-void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*);
+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*);
+int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t *);
+int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *);
+void fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler,
+        fluid_rvoice_t *rvoice);
 
-static FLUID_INLINE void 
-fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_thandler)
+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;
-  }
+    int queue_stored = fluid_atomic_int_get(&handler->queue_stored);
+
+    if(queue_stored > 0)
+    {
+        fluid_atomic_int_set(&handler->queue_stored, 0);
+        fluid_ringbuffer_next_inptr(handler->queue, queue_stored);
+    }
 }
 
 /**
  * @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_thandler)
+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;
+    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_int_real(fluid_rvoice_eventhandler_t *handler,
+        fluid_rvoice_function_t 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_push_ptr(fluid_rvoice_eventhandler_t *handler,
+                                       fluid_rvoice_function_t 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);
+int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler,
+                                   fluid_rvoice_function_t method, void *object,
+                                   fluid_rvoice_param_t param[MAX_EVENT_PARAMS]);
 
 static FLUID_INLINE void
-fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t* handler, 
-                                     fluid_rvoice_trvoice)
+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);
 }
 
 
index d5369aacce9ea4f5ddca65bb9ba7e5686304f26e..8c5254f269e9917e41f761040a3df5861595a764 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_rev.h"
 #include "fluid_chorus.h"
 #include "fluidsynth_priv.h"
-//#include "fluid_ladspa.h"
-
-#define SYNTH_REVERB_CHANNEL 0
-#define SYNTH_CHORUS_CHANNEL 1
+#include "fluid_synth.h"
 
 #undef ENABLE_MIXER_THREADS // Ardour does the multithreading -- synth.cpu-cores defaults to 1
 
 
 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 */
+struct _fluid_mixer_buffers_t
+{
+    fluid_rvoice_mixer_t *mixer; /**< Owner of object */
+#if 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;
+    fluid_rvoice_t **finished_voices; /* List of voices who have finished */
+    int finished_voice_count;
+
+    fluid_atomic_int_t ready;             /**< Atomic: buffers are ready for mixing */
+
+    fluid_real_t *local_buf;
+
+    int buf_count;
+    int fx_buf_count;
+
+    /** buffer to store the left part of a stereo channel to.
+     * Specifically a two dimensional array, containing \c buf_count sample buffers
+     * (i.e. for each synth.audio-channels), of which each contains
+     * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples)
+     * @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT
+     * boundary provided that this pointer points to an aligned buffer.
+     * So make sure to access the sample buffer by first aligning this
+     * pointer using fluid_align_ptr()
+     */
+    fluid_real_t *left_buf;
+
+    /** dito, but for right part of a stereo channel */
+    fluid_real_t *right_buf;
+
+    /** buffer to store the left part of a stereo effects channel to.
+     * Specifically a two dimensional array, containing \c fx_buf_count buffers
+     * (i.e. for each synth.effects-channels), of which each buffer contains
+     * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples)
+     */
+    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_mixer_fx_t
+{
+    fluid_revmodel_t *reverb; /**< Reverb unit */
+    fluid_chorus_t *chorus; /**< Chorus unit */
 };
 
-struct _fluid_rvoice_mixer_t {
-  fluid_mixer_fx_t fx;
+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_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */
+    fluid_rvoice_eventhandler_t *eventhandler;
 
-  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 */
+    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 */
+    int fx_units;
+    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? */
 
 #ifdef LADSPA
-  fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */
+    fluid_ladspa_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */
 #endif
 
-#ifdef ENABLE_MIXER_THREADS
+#if 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) */
+    fluid_atomic_int_t threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */
+    fluid_atomic_int_t 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");
-  }
+#if ENABLE_MIXER_THREADS
+static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer);
+static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level);
 #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)
+static FLUID_INLINE void
+fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount)
 {
-  mixer->remove_voice_callback_userdata = userdata;
-  mixer->remove_voice_callback = func;
-}
+    const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units;
+    int i, f;
 
+    void (*reverb_process_func)(fluid_revmodel_t *rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
+    void (*chorus_process_func)(fluid_chorus_t *chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);
 
+    fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r;
 
-/**
- * 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;
+    // all dry unprocessed mono input is stored in the left channel
+    fluid_real_t *in_rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+    fluid_real_t *in_ch = in_rev;
+
+    fluid_profile_ref_var(prof_ref);
 
-  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;
+    if(mixer->mix_fx_to_out)
+    {
+        // mix effects to first stereo channel
+        out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);
+        out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+        reverb_process_func = fluid_revmodel_processmix;
+        chorus_process_func = fluid_chorus_processmix;
+
+    }
+    else
+    {
+        // replace effects into respective stereo effects channel
+        out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+        out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+        reverb_process_func = fluid_revmodel_processreplace;
+        chorus_process_func = fluid_chorus_processreplace;
+    }
+
+
+    if(mixer->with_reverb)
+    {
+        for(f = 0; f < mixer->fx_units; f++)
+        {
+            int buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL;
+            
+            for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+            {
+                int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i;
+                
+                reverb_process_func(mixer->fx[f].reverb,
+                                    &in_rev[samp_idx],
+                                    mixer->mix_fx_to_out ? &out_rev_l[i] : &out_rev_l[samp_idx],
+                                    mixer->mix_fx_to_out ? &out_rev_r[i] : &out_rev_r[samp_idx]);
+            }
+        }
+
+        fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref, 0,
+                      current_blockcount * FLUID_BUFSIZE);
+    }
+
+    if(mixer->with_chorus)
+    {
+        for(f = 0; f < mixer->fx_units; f++)
+        {
+            int buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL;
+            
+            for(i = 0; i < current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE)
+            {
+                int samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + i;
+                
+                chorus_process_func(mixer->fx[f].chorus,
+                                    &in_ch [samp_idx],
+                                    mixer->mix_fx_to_out ? &out_ch_l[i] : &out_ch_l[samp_idx],
+                                    mixer->mix_fx_to_out ? &out_ch_r[i] : &out_ch_r[samp_idx]);
+            }
+        }
+
+        fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref, 0,
+                      current_blockcount * FLUID_BUFSIZE);
+    }
+
+#ifdef LADSPA
+
+    /* Run the signal through the LADSPA Fx unit. The buffers have already been
+     * set up in fluid_rvoice_mixer_set_ladspa. */
+    if(mixer->ladspa_fx)
+    {
+        fluid_ladspa_run(mixer->ladspa_fx, current_blockcount, FLUID_BUFSIZE);
+        fluid_check_fpe("LADSPA");
     }
-  }
-  fluid_rvoice_buffers_mix(&rvoice->buffers, local_buf, result, bufs, bufcount);
 
-  return result;
+#endif
 }
 
 /**
  * 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 int
+fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbufs)
+{
+    fluid_real_t *base_ptr;
+    int i;
+    const int fx_channels_per_unit = buffers->fx_buf_count / buffers->mixer->fx_units;
+    const int offset = buffers->buf_count * 2;
+    int with_reverb = buffers->mixer->with_reverb;
+    int with_chorus = buffers->mixer->with_chorus;
+
+    /* Set up the reverb and chorus buffers only when the effect is enabled or
+     * when LADSPA is active. Nonexisting buffers are detected in the DSP loop.
+     * Not sending the effect signals saves some time in that case. */
+#ifdef LADSPA
+    int with_ladspa = (buffers->mixer->ladspa_fx != NULL);
+    with_reverb = (with_reverb | with_ladspa);
+    with_chorus = (with_chorus | with_ladspa);
+#endif
+
+    // all the dry, non-processed mono audio for effects is to be stored in the left buffers
+    base_ptr = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < buffers->mixer->fx_units; i++)
+    {
+        int fx_idx = i * fx_channels_per_unit;
+        
+        outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] =
+            (with_reverb)
+            ? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]
+            : NULL;
+            
+        outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] =
+            (with_chorus)
+            ? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]
+            : NULL;
+    }
+    
+    /* The output associated with a MIDI channel is wrapped around
+     * using the number of audio groups as modulo divider.  This is
+     * typically the number of output channels on the 'sound card',
+     * 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.
+     */
+    base_ptr = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < buffers->buf_count; i++)
+    {
+        outbufs[i * 2] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+    }
+
+    base_ptr = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < buffers->buf_count; i++)
+    {
+        outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+    }
+
+    return offset + buffers->fx_buf_count;
 }
 
 
 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]);
+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];
+                }
+            }
+        }
+
+        buffers->mixer->active_voices = av;
+
+        fluid_rvoice_eventhandler_finished_voice_callback(buffers->mixer->eventhandler, v);
+    }
+
+    buffers->finished_voice_count = 0;
+}
+
+static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t *mixer)
+{
+#if 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);
+    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)
+
+static FLUID_INLINE fluid_real_t *
+get_dest_buf(fluid_rvoice_buffers_t *buffers, int index,
+             fluid_real_t **dest_bufs, int dest_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);
-  }
+    int j = buffers->bufs[index].mapping;
+
+    if(j >= dest_bufcount || j < 0)
+    {
+        return NULL;
+    }
+
+    return dest_bufs[j];
 }
-/*
-static int fluid_mixer_buffers_replace_voice(fluid_mixer_buffers_t* buffers, 
-                                             fluid_rvoice_t* voice)
+
+/**
+ * Mix data down to buffers
+ *
+ * @param buffers Destination buffer(s)
+ * @param dsp_buf Mono sample source
+ * @param start_block Block to start mixing at
+ * @param sample_count number of samples to mix following \c start_block
+ * @param dest_bufs Array of buffers to mixdown to
+ * @param dest_bufcount Length of dest_bufs
+ */
+static void
+fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,
+                         fluid_real_t *FLUID_RESTRICT dsp_buf,
+                         int start_block, int sample_count,
+                         fluid_real_t **dest_bufs, int dest_bufcount)
 {
-  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++;
+    int bufcount = buffers->count;
+    int i, dsp_i;
+
+    if(sample_count <= 0 || dest_bufcount <= 0)
+    {
+        return;
+    }
+
+    FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0);
+    FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0);
+
+    for(i = 0; i < bufcount; i++)
+    {
+        fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);
+        fluid_real_t amp = buffers->bufs[i].amp;
+
+        if(buf == NULL || amp == 0.0f)
+        {
+            continue;
+        }
+
+        FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0);
+
+        #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT)
+
+        for(dsp_i = (start_block * FLUID_BUFSIZE); dsp_i < sample_count; dsp_i++)
+        {
+            buf[dsp_i] += amp * dsp_buf[dsp_i];
+        }
     }
-  fvc = buffers->finished_voice_count;
-  return retval;  
 }
-*/
 
-int 
-fluid_rvoice_mixer_add_voice(fluid_rvoice_mixer_t* mixer, fluid_rvoice_t* voice)
+/**
+ * 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.
+ */
+static FLUID_INLINE void
+fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers,
+                               fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs,
+                               unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount)
 {
-  int i;
+    int i, total_samples = 0, start_block = 0;
 
-  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;
+    for(i = 0; i < blockcount; i++)
+    {
+        int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]);
+
+        if(s == -1)
+        {
+            start_block += s;
+            s = FLUID_BUFSIZE;
+        }
+
+        total_samples += s;
+
+        if(s < FLUID_BUFSIZE)
+        {
+            break;
+        }
     }
-    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;
+
+    fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, -start_block, total_samples - ((-start_block)*FLUID_BUFSIZE), dest_bufs, dest_bufcount);
+
+    if(total_samples < blockcount * FLUID_BUFSIZE)
+    {
+        fluid_finish_rvoice(buffers, rvoice);
     }
-  }
+}
 
-  /* This should never happen */
-  FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");
-  return FLUID_FAILED;
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice)
+{
+    int i;
+    fluid_rvoice_mixer_t *mixer = obj;
+    fluid_rvoice_t *voice = param[0].ptr;
+
+    if(mixer->active_voices < mixer->polyphony)
+    {
+        mixer->rvoices[mixer->active_voices++] = voice;
+        return; // success
+    }
+
+    /* 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;
+        }
+
+        if(mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED)
+        {
+            fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]);
+            mixer->rvoices[i] = voice;
+            return; // success
+        }
+    }
+
+    /* This should never happen */
+    FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");
+    return;
 }
 
-static int 
-fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_tbuffers, int value)
+static int
+fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t *buffers, int value)
 {
-  void* newptr;
+    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;
+    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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony)
 {
-  void* newptr;
-  if (handler->active_voices > value) 
-    return FLUID_FAILED;
+    void *newptr;
+    fluid_rvoice_mixer_t *handler = obj;
+    int value = param[0].i;
 
-  newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t*));
-  if (newptr == NULL) 
-    return FLUID_FAILED;
-  handler->rvoices = newptr;
+    if(handler->active_voices > value)
+    {
+        return /*FLUID_FAILED*/;
+    }
 
-  if (fluid_mixer_buffers_update_polyphony(&handler->buffers, value) 
-      == FLUID_FAILED)
-    return FLUID_FAILED;
+    newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t *));
 
-#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;
-  }
+    if(newptr == NULL)
+    {
+        return /*FLUID_FAILED*/;
+    }
+
+    handler->rvoices = newptr;
+
+    if(fluid_mixer_buffers_update_polyphony(&handler->buffers, value)
+            == FLUID_FAILED)
+    {
+        return /*FLUID_FAILED*/;
+    }
+
+#if 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;
+    handler->polyphony = value;
+    return /*FLUID_OK*/;
 }
 
 
-static void 
-fluid_render_loop_singlethread(fluid_rvoice_mixer_t* mixer)
+static void
+fluid_render_loop_singlethread(fluid_rvoice_mixer_t *mixer, int blockcount)
 {
-  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);
-  }
-}
+    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_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT);
 
+    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, local_buf, blockcount);
+        fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1,
+                      blockcount * FLUID_BUFSIZE);
+    }
+}
 
 static FLUID_INLINE void
-fluid_mixer_buffers_zero(fluid_mixer_buffers_t* buffers)
+fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount)
 {
-  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);
-  }
-}
+    int i, size = current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t);
 
+    /* TODO: Optimize by only zero out the buffers we actually use later on. */
+    int buf_count = buffers->buf_count, fx_buf_count = buffers->fx_buf_count;
 
+    fluid_real_t *FLUID_RESTRICT buf_l = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT);
+    fluid_real_t *FLUID_RESTRICT buf_r = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT);
 
-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 */
+    for(i = 0; i < buf_count; i++)
+    {
+        FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+        FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+    }
+
+    buf_l = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+    buf_r = fluid_align_ptr(buffers->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < fx_buf_count; i++)
+    {
+        FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+        FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);
+    }
+}
 
-  buffers->left_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
-  buffers->right_buf = FLUID_ARRAY(fluid_real_t*, buffers->buf_count);
+static int
+fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer)
+{
+    const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT;
 
-  if ((buffers->left_buf == NULL) || (buffers->right_buf == NULL)) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return 0;
-  }
+    buffers->mixer = mixer;
+    buffers->buf_count = mixer->buffers.buf_count;
+    buffers->fx_buf_count = mixer->buffers.fx_buf_count;
 
-  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*));
+    /* Local mono voice buf */
+    buffers->local_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, samplecount, FLUID_DEFAULT_ALIGNMENT);
 
-  for (i = 0; i < buffers->buf_count; i++) {
+    /* Left and right audio buffers */
 
-    buffers->left_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
-    buffers->right_buf[i] = FLUID_ARRAY(fluid_real_t, samplecount);
+    buffers->left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
+    buffers->right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
 
-    if ((buffers->left_buf[i] == NULL) || (buffers->right_buf[i] == NULL)) {
-      FLUID_LOG(FLUID_ERR, "Out of memory");
-      return 0;
+    if((buffers->local_buf == NULL) || (buffers->left_buf == NULL) || (buffers->right_buf == 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);
+    /* Effects audio buffers */
 
-  if ((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return 0;
-  }
+    buffers->fx_left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
+    buffers->fx_right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);
 
-  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*));
+    if((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL))
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return 0;
+    }
 
-  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);
+    buffers->finished_voices = NULL;
 
-    if ((buffers->fx_left_buf[i] == NULL) || (buffers->fx_right_buf[i] == NULL)) {
-      FLUID_LOG(FLUID_ERR, "Out of memory");
-      return 0;
+    if(fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony)
+            == FLUID_FAILED)
+    {
+        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;
+
+    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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_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);
+    fluid_rvoice_mixer_t *mixer = obj;
+    fluid_real_t samplerate = param[1].real; // becausee fluid_synth_update_mixer() puts real into arg2
+
+    int i;
+    for(i = 0; i < mixer->fx_units; i++)
+    {
+        if(mixer->fx[i].chorus)
+        {
+            delete_fluid_chorus(mixer->fx[i].chorus);
+        }
+
+        mixer->fx[i].chorus = new_fluid_chorus(samplerate);
+
+        if(mixer->fx[i].reverb)
+        {
+            fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate);
+        }
+    }
+
+#if LADSPA
+
+    if(mixer->ladspa_fx != NULL)
+    {
+        fluid_ladspa_set_sample_rate(mixer->ladspa_fx, samplerate);
+    }
+
+#endif
 }
 
 
@@ -530,188 +686,299 @@ fluid_rvoice_mixer_set_samplerate(fluid_rvoice_mixer_t* mixer, fluid_real_t samp
  * @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 *
+new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *evthandler, int extra_threads, int prio)
 {
-  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) {
+    int i;
+    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->eventhandler = evthandler;
+    mixer->fx_units = fx_units;
+    mixer->buffers.buf_count = buf_count;
+    mixer->buffers.fx_buf_count = fx_buf_count * fx_units;
+
+    /* allocate the reverb module */
+    mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units);
+    if(mixer->fx == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        goto error_recovery;
+    }
+    
+    FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx));
+    
+    for(i = 0; i < fx_units; i++)
+    {
+        mixer->fx[i].reverb = new_fluid_revmodel(sample_rate);
+        mixer->fx[i].chorus = new_fluid_chorus(sample_rate);
+
+        if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            goto error_recovery;
+        }
+    }
+
+    if(!fluid_mixer_buffers_init(&mixer->buffers, mixer))
+    {
+        goto error_recovery;
+    }
+
+#if 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)
+    {
+        goto error_recovery;
+    }
+
+    if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK)
+    {
+        goto error_recovery;
+    }
+
+#endif
+
+    return mixer;
+    
+error_recovery:
     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_mixer_buffers_free(fluid_mixer_buffers_t *buffers)
+{
+    FLUID_FREE(buffers->finished_voices);
+
+    /* free all the sample buffers */
+    FLUID_FREE(buffers->local_buf);
     FLUID_FREE(buffers->left_buf);
-  }
+    FLUID_FREE(buffers->right_buf);
+    FLUID_FREE(buffers->fx_left_buf);
+    FLUID_FREE(buffers->fx_right_buf);
+}
+
+void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer)
+{
+    int i;
+    
+    fluid_return_if_fail(mixer != NULL);
 
-  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]);
-      }
+#if ENABLE_MIXER_THREADS
+    delete_rvoice_mixer_threads(mixer);
+
+    if(mixer->thread_ready)
+    {
+        delete_fluid_cond(mixer->thread_ready);
     }
-    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]);
-      }
+    if(mixer->wakeup_threads)
+    {
+        delete_fluid_cond(mixer->wakeup_threads);
     }
-    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]);
-      }
+    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);
     }
-    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);
+    fluid_mixer_buffers_free(&mixer->buffers);
+
+    
+    for(i = 0; i < mixer->fx_units; i++)
+    {
+        if(mixer->fx[i].reverb)
+        {
+            delete_fluid_revmodel(mixer->fx[i].reverb);
+        }
+
+        if(mixer->fx[i].chorus)
+        {
+            delete_fluid_chorus(mixer->fx[i].chorus);
+        }
+    }
+
+    FLUID_FREE(mixer->fx);
+    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)
+#ifdef LADSPA
+/**
+ * Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers
+ * as LADSPA host buffers with sensible names */
+void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer,
+                                   fluid_ladspa_fx_t *ladspa_fx, int audio_groups)
 {
-  mixer->LADSPA_FxUnit = ladspa;
+    mixer->ladspa_fx = ladspa_fx;
+
+    if(ladspa_fx == NULL)
+    {
+        return;
+    }
+    else
+    {
+        fluid_real_t *main_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);
+        fluid_real_t *main_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+        fluid_real_t *rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+        fluid_real_t *chor = rev;
+
+        rev = &rev[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+        chor = &chor[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];
+
+        fluid_ladspa_add_host_ports(ladspa_fx, "Main:L", audio_groups,
+                                    main_l,
+                                    FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+
+        fluid_ladspa_add_host_ports(ladspa_fx, "Main:R", audio_groups,
+                                    main_r,
+                                    FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+
+        fluid_ladspa_add_host_ports(ladspa_fx, "Reverb:Send", 1,
+                                    rev,
+                                    FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+
+        fluid_ladspa_add_host_ports(ladspa_fx, "Chorus:Send", 1,
+                                    chor,
+                                    FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);
+    }
 }
 #endif
 
-void fluid_rvoice_mixer_set_reverb_enabled(fluid_rvoice_mixer_t* mixer, int on)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled)
 {
-  mixer->fx.with_reverb = on;
+    fluid_rvoice_mixer_t *mixer = obj;
+    int on = param[0].i;
+
+    mixer->with_reverb = on;
 }
 
-void fluid_rvoice_mixer_set_chorus_enabled(fluid_rvoice_mixer_t* mixer, int on)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled)
 {
-  mixer->fx.with_chorus = on;
+    fluid_rvoice_mixer_t *mixer = obj;
+    int on = param[0].i;
+    mixer->with_chorus = on;
 }
 
-void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_tmixer, int on)
+void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on)
 {
-  mixer->fx.mix_fx_to_out = on;
+    mixer->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)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params)
 {
-  fluid_chorus_set(mixer->fx.chorus, set, nr, level, speed, depth_ms, type);
+    fluid_rvoice_mixer_t *mixer = obj;
+    int set = param[0].i;
+    int nr = param[1].i;
+    fluid_real_t level = param[2].real;
+    fluid_real_t speed = param[3].real;
+    fluid_real_t depth_ms = param[4].real;
+    int type = param[5].i;
+
+    int i;
+    for(i = 0; i < mixer->fx_units; i++)
+    {
+        fluid_chorus_set(mixer->fx[i].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)
+
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params)
 {
-  fluid_revmodel_set(mixer->fx.reverb, set, roomsize, damping, width, level); 
+    fluid_rvoice_mixer_t *mixer = obj;
+    int set = param[0].i;
+    fluid_real_t roomsize = param[1].real;
+    fluid_real_t damping = param[2].real;
+    fluid_real_t width = param[3].real;
+    fluid_real_t level = param[4].real;
+
+    int i;
+    for(i = 0; i < mixer->fx_units; i++)
+    {
+        fluid_revmodel_set(mixer->fx[i].reverb, set, roomsize, damping, width, level);
+    }
 }
 
-void fluid_rvoice_mixer_reset_fx(fluid_rvoice_mixer_t* mixer)
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb)
 {
-  fluid_revmodel_reset(mixer->fx.reverb);
-  fluid_chorus_reset(mixer->fx.chorus);
+    fluid_rvoice_mixer_t *mixer = obj;
+    int i;
+    for(i = 0; i < mixer->fx_units; i++)
+    {
+        fluid_revmodel_reset(mixer->fx[i].reverb);
+    }
+}
+
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus)
+{
+    fluid_rvoice_mixer_t *mixer = obj;
+    int i;
+    for(i = 0; i < mixer->fx_units; i++)
+    {
+        fluid_chorus_reset(mixer->fx[i].chorus);
+    }
 }
 
-void fluid_rvoice_mixer_reset_reverb(fluid_rvoice_mixer_t* mixer)
+int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer,
+                                fluid_real_t **left, fluid_real_t **right)
 {
-  fluid_revmodel_reset(mixer->fx.reverb);
+    *left = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);
+    *right = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);
+    return mixer->buffers.buf_count;
 }
 
-void fluid_rvoice_mixer_reset_chorus(fluid_rvoice_mixer_t* mixer)
+int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer,
+                                   fluid_real_t **fx_left, fluid_real_t **fx_right)
 {
-  fluid_chorus_reset(mixer->fx.chorus);
+    *fx_left = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+    *fx_right = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+    return mixer->buffers.fx_buf_count;
 }
 
-int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t* mixer, 
-                                 fluid_real_t*** left, fluid_real_t*** right)
+int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer)
 {
-  *left = mixer->buffers.left_buf;
-  *right = mixer->buffers.right_buf;
-  return mixer->buffers.buf_count;
+    return FLUID_MIXER_MAX_BUFFERS_DEFAULT;
 }
 
+#if WITH_PROFILING
+int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer)
+{
+    return mixer->active_voices;
+}
+#endif
 
-#ifdef ENABLE_MIXER_THREADS
+#if ENABLE_MIXER_THREADS
 
-static FLUID_INLINE fluid_rvoice_t
-fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_tmixer)
+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];
+    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
@@ -720,255 +987,391 @@ fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t* mixer)
 #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 fluid_thread_return_t
+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;
+    int current_blockcount = 0;
+    fluid_real_t *local_buf = fluid_align_ptr(buffers->local_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    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)
+            {
+                // blockcount may have changed, since thread was put to sleep
+                current_blockcount = mixer->current_blockcount;
+                fluid_mixer_buffers_zero(buffers, current_blockcount);
+                bufcount = fluid_mixer_buffers_prepare(buffers, bufs);
+                hasValidData = 1;
+            }
+
+            // then render voice to buffers
+            fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount, local_buf, current_blockcount);
+        }
+    }
 
+    return FLUID_THREAD_RETURN_VALUE;
 }
 
 static void
-fluid_mixer_buffers_mix(fluid_mixer_buffers_t* dest, fluid_mixer_buffers_t* src)
+fluid_mixer_buffers_mix(fluid_mixer_buffers_t *dst, fluid_mixer_buffers_t *src, int current_blockcount)
 {
-  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];
+    int i, j;
+    int scount = current_blockcount * FLUID_BUFSIZE;
+    int minbuf;
+    fluid_real_t *FLUID_RESTRICT base_src;
+    fluid_real_t *FLUID_RESTRICT base_dst;
+
+    minbuf = dst->buf_count;
+
+    if(minbuf > src->buf_count)
+    {
+        minbuf = src->buf_count;
+    }
+
+    base_src = fluid_align_ptr(src->left_buf, FLUID_DEFAULT_ALIGNMENT);
+    base_dst = fluid_align_ptr(dst->left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < minbuf; i++)
+    {
+        #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+        for(j = 0; j < scount; j++)
+        {
+            int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+            base_dst[dsp_i] += base_src[dsp_i];
+        }
+    }
+
+    base_src = fluid_align_ptr(src->right_buf, FLUID_DEFAULT_ALIGNMENT);
+    base_dst = fluid_align_ptr(dst->right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < minbuf; i++)
+    {
+        #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+        for(j = 0; j < scount; j++)
+        {
+            int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+            base_dst[dsp_i] += base_src[dsp_i];
+        }
     }
-  }
 
-  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];
+    minbuf = dst->fx_buf_count;
+
+    if(minbuf > src->fx_buf_count)
+    {
+        minbuf = src->fx_buf_count;
+    }
+
+    base_src = fluid_align_ptr(src->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+    base_dst = fluid_align_ptr(dst->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < minbuf; i++)
+    {
+        #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+        for(j = 0; j < scount; j++)
+        {
+            int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+            base_dst[dsp_i] += base_src[dsp_i];
+        }
+    }
+
+    base_src = fluid_align_ptr(src->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+    base_dst = fluid_align_ptr(dst->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);
+
+    for(i = 0; i < minbuf; i++)
+    {
+        #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)
+
+        for(j = 0; j < scount; j++)
+        {
+            int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;
+            base_dst[dsp_i] += base_src[dsp_i];
+        }
     }
-  }
 }
 
 
 /**
- * Go through all threads and see if someone is finished for mixing 
+ * 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);
+static int
+fluid_mixer_mix_in(fluid_rvoice_mixer_t *mixer, int extra_threads, int current_blockcount)
+{
+    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], current_blockcount);
+                hasmixed = 1;
+                break;
+            }
+        }
+    }
+    while(hasmixed);
+
+    return result;
 }
 
-#endif
+static void
+fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcount)
+{
+    int i, bufcount;
+    fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT);
 
-/**
- * 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_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, current_blockcount);
+        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, current_blockcount))
+    {
+        // 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, local_buf, current_blockcount);
+            fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1,
+                          current_blockcount * FLUID_BUFSIZE);
+            //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",
+    //     current_blockcount, test, mixer->active_voices, waits);
+}
+
+static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer)
+{
+    int i;
     fluid_atomic_int_set(&mixer->threads_should_terminate, 1);
     // Signal threads to wake up
     fluid_cond_mutex_lock(mixer->wakeup_threads_m);
-    for (i=0; i < mixer->thread_count; i++)
-      fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE);
+
+    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]);
+
+    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
+/**
+ * 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
+ */
+static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level)
+{
+    char name[16];
+    int i;
+
+    // Kill all existing threads first
+    if(mixer->thread_count)
+    {
+        delete_rvoice_mixer_threads(mixer);
+    }
+
+    if(thread_count == 0)
+    {
+        return FLUID_OK;
+    }
+
+    // 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_FAILED;
+    }
+
+    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_FAILED;
+        }
+
+        fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA);
+        FLUID_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 FLUID_FAILED;
+        }
+    }
+
+    return FLUID_OK;
 }
+#endif
 
 /**
  * Synthesize audio into buffers
- * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples 
+ * @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
+int
+fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount)
+{
+    fluid_profile_ref_var(prof_ref);
+
+    mixer->current_blockcount = blockcount;
+
+    // Zero buffers
+    fluid_mixer_buffers_zero(&mixer->buffers, blockcount);
+    fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices,
+                  blockcount * FLUID_BUFSIZE);
+
+#if ENABLE_MIXER_THREADS
+
+    if(mixer->thread_count > 0)
+    {
+        fluid_render_loop_multithread(mixer, blockcount);
+    }
+    else
 #endif
-    fluid_render_loop_singlethread(mixer);
-  fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref);
-    
+    {
+        fluid_render_loop_singlethread(mixer, blockcount);
+    }
+
+    fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices,
+                  blockcount * FLUID_BUFSIZE);
+
 
-  // Process reverb & chorus
-  fluid_rvoice_mixer_process_fx(mixer);
+    // Process reverb & chorus
+    fluid_rvoice_mixer_process_fx(mixer, blockcount);
 
-  // Call the callback and pack active voice array
-  fluid_rvoice_mixer_process_finished_voices(mixer);
+    // Call the callback and pack active voice array
+    fluid_rvoice_mixer_process_finished_voices(mixer);
 
-  return mixer->current_blockcount;
+    return blockcount;
 }
index d4e41ca0a8deaa60c78c8003a392e3520ce74822..1b3fceb342b35743d51a376ab68707102e60fde4 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "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) 
-
+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);
+int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer,
+                                   fluid_real_t **fx_left, fluid_real_t **fx_right);
+int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer);
+#if WITH_PROFILING
+int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer);
+#endif
+fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units,
+        fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *, int, int);
 
-void fluid_rvoice_mixer_set_finished_voices_callback(
-  fluid_rvoice_mixer_t* mixer,
-  void (*func)(void*, fluid_rvoice_t*),
-  void* userdata);
+void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *);
 
 
-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);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params);
 
-fluid_rvoice_mixer_t* new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, 
-                                            fluid_real_t sample_rate);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb);
+DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus);
 
-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);
+void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on);
+#ifdef LADSPA
+void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer,
+                                   fluid_ladspa_fx_t *ladspa_fx, int audio_groups);
 #endif
 
 #endif
diff --git a/libs/fluidsynth/src/fluid_samplecache.c b/libs/fluidsynth/src/fluid_samplecache.c
new file mode 100644 (file)
index 0000000..773e19f
--- /dev/null
@@ -0,0 +1,295 @@
+/* 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 Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+/* CACHED SAMPLE DATA LOADER
+ *
+ * This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read
+ * data across all FluidSynth instances in a global (process-wide) list.
+ */
+
+#include "fluid_samplecache.h"
+#include "fluid_sys.h"
+#include "fluidsynth.h"
+#include "fluid_list.h"
+
+
+typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t;
+
+struct _fluid_samplecache_entry_t
+{
+    /* The follwing members all form the cache key */
+    char *filename;
+    time_t modification_time;
+    unsigned int sf_samplepos;
+    unsigned int sf_samplesize;
+    unsigned int sf_sample24pos;
+    unsigned int sf_sample24size;
+    unsigned int sample_start;
+    unsigned int sample_end;
+    int sample_type;
+    /*  End of cache key members */
+
+    short *sample_data;
+    char *sample_data24;
+    int sample_count;
+
+    int num_references;
+    int mlocked;
+};
+
+static fluid_list_t *samplecache_list = NULL;
+static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
+
+static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
+static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
+static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
+
+static int fluid_get_file_modification_time(char *filename, time_t *modification_time);
+
+
+/* PUBLIC INTERFACE */
+
+int fluid_samplecache_load(SFData *sf,
+                           unsigned int sample_start, unsigned int sample_end, int sample_type,
+                           int try_mlock, short **sample_data, char **sample_data24)
+{
+    fluid_samplecache_entry_t *entry;
+    int ret;
+
+    fluid_mutex_lock(samplecache_mutex);
+
+    entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type);
+
+    if(entry == NULL)
+    {
+        entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type);
+
+        if(entry == NULL)
+        {
+            ret = -1;
+            goto unlock_exit;
+        }
+
+        samplecache_list = fluid_list_prepend(samplecache_list, entry);
+    }
+
+    if(try_mlock && !entry->mlocked)
+    {
+        /* Lock the memory to disable paging. It's okay if this fails. It
+         * probably means that the user doesn't have the required permission. */
+        if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0)
+        {
+            if(entry->sample_data24 != NULL)
+            {
+                entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0);
+            }
+            else
+            {
+                entry->mlocked = TRUE;
+            }
+
+            if(!entry->mlocked)
+            {
+                fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));
+                FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
+            }
+        }
+    }
+
+    entry->num_references++;
+    *sample_data = entry->sample_data;
+    *sample_data24 = entry->sample_data24;
+    ret = entry->sample_count;
+
+unlock_exit:
+    fluid_mutex_unlock(samplecache_mutex);
+    return ret;
+}
+
+int fluid_samplecache_unload(const short *sample_data)
+{
+    fluid_list_t *entry_list;
+    fluid_samplecache_entry_t *entry;
+    int ret;
+
+    fluid_mutex_lock(samplecache_mutex);
+
+    entry_list = samplecache_list;
+
+    while(entry_list)
+    {
+        entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);
+
+        if(sample_data == entry->sample_data)
+        {
+            entry->num_references--;
+
+            if(entry->num_references == 0)
+            {
+                if(entry->mlocked)
+                {
+                    fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));
+
+                    if(entry->sample_data24 != NULL)
+                    {
+                        fluid_munlock(entry->sample_data24, entry->sample_count);
+                    }
+                }
+
+                samplecache_list = fluid_list_remove(samplecache_list, entry);
+                delete_samplecache_entry(entry);
+            }
+
+            ret = FLUID_OK;
+            goto unlock_exit;
+        }
+
+        entry_list = fluid_list_next(entry_list);
+    }
+
+    FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache.");
+    ret = FLUID_FAILED;
+
+unlock_exit:
+    fluid_mutex_unlock(samplecache_mutex);
+    return ret;
+}
+
+
+/* Private functions */
+static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
+        unsigned int sample_start,
+        unsigned int sample_end,
+        int sample_type)
+{
+    fluid_samplecache_entry_t *entry;
+
+    entry = FLUID_NEW(fluid_samplecache_entry_t);
+
+    if(entry == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    FLUID_MEMSET(entry, 0, sizeof(*entry));
+
+    entry->filename = FLUID_STRDUP(sf->fname);
+
+    if(entry->filename == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        goto error_exit;
+    }
+
+    if(fluid_get_file_modification_time(entry->filename, &entry->modification_time) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
+        entry->modification_time = 0;
+    }
+
+    entry->sf_samplepos = sf->samplepos;
+    entry->sf_samplesize = sf->samplesize;
+    entry->sf_sample24pos = sf->sample24pos;
+    entry->sf_sample24size = sf->sample24size;
+    entry->sample_start = sample_start;
+    entry->sample_end = sample_end;
+    entry->sample_type = sample_type;
+
+    entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,
+                          &entry->sample_data, &entry->sample_data24);
+
+    if(entry->sample_count < 0)
+    {
+        goto error_exit;
+    }
+
+    return entry;
+
+error_exit:
+    delete_samplecache_entry(entry);
+    return NULL;
+}
+
+static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)
+{
+    fluid_return_if_fail(entry != NULL);
+
+    FLUID_FREE(entry->filename);
+    FLUID_FREE(entry->sample_data);
+    FLUID_FREE(entry->sample_data24);
+    FLUID_FREE(entry);
+}
+
+static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
+        unsigned int sample_start,
+        unsigned int sample_end,
+        int sample_type)
+{
+    time_t mtime;
+    fluid_list_t *entry_list;
+    fluid_samplecache_entry_t *entry;
+
+    if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
+        mtime = 0;
+    }
+
+    entry_list = samplecache_list;
+
+    while(entry_list)
+    {
+        entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);
+
+        if((FLUID_STRCMP(sf->fname, entry->filename) == 0) &&
+                (mtime == entry->modification_time) &&
+                (sf->samplepos == entry->sf_samplepos) &&
+                (sf->samplesize == entry->sf_samplesize) &&
+                (sf->sample24pos == entry->sf_sample24pos) &&
+                (sf->sample24size == entry->sf_sample24size) &&
+                (sample_start == entry->sample_start) &&
+                (sample_end == entry->sample_end) &&
+                (sample_type == entry->sample_type))
+        {
+            return entry;
+        }
+
+        entry_list = fluid_list_next(entry_list);
+    }
+
+    return NULL;
+}
+
+static int fluid_get_file_modification_time(char *filename, time_t *modification_time)
+{
+    fluid_stat_buf_t buf;
+
+    if(fluid_stat(filename, &buf))
+    {
+        return FLUID_FAILED;
+    }
+
+    *modification_time = buf.st_mtime;
+    return FLUID_OK;
+}
diff --git a/libs/fluidsynth/src/fluid_samplecache.h b/libs/fluidsynth/src/fluid_samplecache.h
new file mode 100644 (file)
index 0000000..49b802b
--- /dev/null
@@ -0,0 +1,34 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003  Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_SAMPLECACHE_H
+#define _FLUID_SAMPLECACHE_H
+
+#include "fluid_sfont.h"
+#include "fluid_sffile.h"
+
+int fluid_samplecache_load(SFData *sf,
+                           unsigned int sample_start, unsigned int sample_end, int sample_type,
+                           int try_mlock, short **data, char **data24);
+
+int fluid_samplecache_unload(const short *sample_data);
+
+#endif /* _FLUID_SAMPLECACHE_H */
index 181484ecc3e252c17c4bb11a7b48a8dece637a8d..05423384ee89651e4fecf68ce23336b910639b3f 100644 (file)
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
  */
 
-#include "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_tsettings);
-static void fluid_settings_key_destroy_func(voidvalue);
-static void fluid_settings_value_destroy_func(voidvalue);
+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;
+typedef struct
+{
+    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;
+typedef struct
+{
+    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;
+typedef struct
+{
+    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;
+typedef struct
+{
+    fluid_hashtable_t *hashtable;
 } fluid_set_setting_t;
 
+typedef struct
+{
+    int type;             /**< fluid_types_enum */
 
-static fluid_str_setting_t*
-new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data)
+    union
+    {
+        fluid_str_setting_t str;
+        fluid_num_setting_t num;
+        fluid_int_setting_t i;
+        fluid_set_setting_t set;
+    };
+} fluid_setting_node_t;
+
+static fluid_setting_node_t *
+new_fluid_str_setting(const char *value, const char *def, int hints)
 {
-  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;
+    fluid_setting_node_t *node;
+    fluid_str_setting_t *str;
+
+    node = FLUID_NEW(fluid_setting_node_t);
+
+    if(!node)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    node->type = FLUID_STR_TYPE;
+
+    str = &node->str;
+    str->value = value ? FLUID_STRDUP(value) : NULL;
+    str->def = def ? FLUID_STRDUP(def) : NULL;
+    str->hints = hints;
+    str->options = NULL;
+    str->update = NULL;
+    str->data = NULL;
+    return node;
 }
 
 static void
-delete_fluid_str_setting(fluid_str_setting_t* str)
+delete_fluid_str_setting(fluid_setting_node_t *node)
 {
-  if (!str) return;
+    fluid_return_if_fail(node != NULL);
 
-  if (str->value) FLUID_FREE(str->value);
-  if (str->def) FLUID_FREE(str->def);
+    FLUID_ASSERT(node->type == FLUID_STR_TYPE);
 
-  if (str->options) {
-    fluid_list_t* list = str->options;
+    FLUID_FREE(node->str.value);
+    FLUID_FREE(node->str.def);
 
-    while (list) {
-      FLUID_FREE (list->data);
-      list = fluid_list_next(list);
-    }
+    if(node->str.options)
+    {
+        fluid_list_t *list = node->str.options;
+
+        while(list)
+        {
+            FLUID_FREE(list->data);
+            list = fluid_list_next(list);
+        }
 
-    delete_fluid_list(str->options);
-  }
+        delete_fluid_list(node->str.options);
+    }
 
-  FLUID_FREE(str);
+    FLUID_FREE(node);
 }
 
 
-static fluid_num_setting_t*
-new_fluid_num_setting(double min, double max, double def,
-                    int hints, fluid_num_update_t fun, void* data)
+static fluid_setting_node_t *
+new_fluid_num_setting(double min, double max, double def, int hints)
 {
-  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;
+    fluid_setting_node_t *node;
+    fluid_num_setting_t *num;
+
+    node = FLUID_NEW(fluid_setting_node_t);
+
+    if(!node)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    node->type = FLUID_NUM_TYPE;
+
+    num = &node->num;
+    num->value = def;
+    num->def = def;
+    num->min = min;
+    num->max = max;
+    num->hints = hints;
+    num->update = NULL;
+    num->data = NULL;
+
+    return node;
 }
 
 static void
-delete_fluid_num_setting(fluid_num_setting_t* setting)
+delete_fluid_num_setting(fluid_setting_node_t *node)
 {
-  if (setting) FLUID_FREE(setting);
+    fluid_return_if_fail(node != NULL);
+
+    FLUID_ASSERT(node->type == FLUID_NUM_TYPE);
+    FLUID_FREE(node);
 }
 
-static fluid_int_setting_t*
-new_fluid_int_setting(int min, int max, int def,
-                    int hints, fluid_int_update_t fun, void* data)
+static fluid_setting_node_t *
+new_fluid_int_setting(int min, int max, int def, int hints)
 {
-  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;
+    fluid_setting_node_t *node;
+    fluid_int_setting_t *i;
+
+    node = FLUID_NEW(fluid_setting_node_t);
+
+    if(!node)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    node->type = FLUID_INT_TYPE;
+
+    i = &node->i;
+    i->value = def;
+    i->def = def;
+    i->min = min;
+    i->max = max;
+    i->hints = hints;
+    i->update = NULL;
+    i->data = NULL;
+    return node;
 }
 
 static void
-delete_fluid_int_setting(fluid_int_setting_t* setting)
+delete_fluid_int_setting(fluid_setting_node_t *node)
 {
-  if (setting) FLUID_FREE(setting);
+    fluid_return_if_fail(node != NULL);
+
+    FLUID_ASSERT(node->type == FLUID_INT_TYPE);
+    FLUID_FREE(node);
 }
 
-static fluid_set_setting_t*
+static fluid_setting_node_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;
+    fluid_setting_node_t *node;
+    fluid_set_setting_t *set;
+
+    node = FLUID_NEW(fluid_setting_node_t);
+
+    if(!node)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    node->type = FLUID_SET_TYPE;
+    set = &node->set;
+
+    set->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
+                     fluid_settings_key_destroy_func,
+                     fluid_settings_value_destroy_func);
+
+    if(!set->hashtable)
+    {
+        FLUID_FREE(node);
+        return NULL;
+    }
+
+    return node;
 }
 
 static void
-delete_fluid_set_setting(fluid_set_setting_t* setting)
+delete_fluid_set_setting(fluid_setting_node_t *node)
 {
-  if (setting)
-  {
-    delete_fluid_hashtable(setting->hashtable);
-    FLUID_FREE(setting);
-  }
+    fluid_return_if_fail(node != NULL);
+
+    FLUID_ASSERT(node->type == FLUID_SET_TYPE);
+    delete_fluid_hashtable(node->set.hashtable);
+    FLUID_FREE(node);
 }
 
 /**
@@ -234,16 +257,20 @@ delete_fluid_set_setting(fluid_set_setting_t* setting)
 fluid_settings_t *
 new_fluid_settings(void)
 {
-  fluid_settings_t* settings;
+    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);
 
-  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;
+    if(settings == NULL)
+    {
+        return NULL;
+    }
 
-  fluid_rec_mutex_init (settings->mutex);
-  fluid_settings_init(settings);
-  return settings;
+    fluid_rec_mutex_init(settings->mutex);
+    fluid_settings_init(settings);
+    return settings;
 }
 
 /**
@@ -251,86 +278,93 @@ new_fluid_settings(void)
  * @param settings a settings object
  */
 void
-delete_fluid_settings(fluid_settings_tsettings)
+delete_fluid_settings(fluid_settings_t *settings)
 {
-  fluid_return_if_fail (settings != NULL);
+    fluid_return_if_fail(settings != NULL);
 
-  fluid_rec_mutex_destroy (settings->mutex);
-  delete_fluid_hashtable(settings);
+    fluid_rec_mutex_destroy(settings->mutex);
+    delete_fluid_hashtable(settings);
 }
 
 /* Settings hash key destroy function */
 static void
-fluid_settings_key_destroy_func(voidvalue)
+fluid_settings_key_destroy_func(void *value)
 {
-  FLUID_FREE (value);   /* Free the string key value */
+    FLUID_FREE(value);    /* Free the string key value */
 }
 
 /* Settings hash value destroy function */
 static void
-fluid_settings_value_destroy_func(voidvalue)
+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;
-  }
+    fluid_setting_node_t *node = value;
+
+    switch(node->type)
+    {
+    case FLUID_NUM_TYPE:
+        delete_fluid_num_setting(node);
+        break;
+
+    case FLUID_INT_TYPE:
+        delete_fluid_int_setting(node);
+        break;
+
+    case FLUID_STR_TYPE:
+        delete_fluid_str_setting(node);
+        break;
+
+    case FLUID_SET_TYPE:
+        delete_fluid_set_setting(node);
+        break;
+    }
 }
 
 void
-fluid_settings_init(fluid_settings_tsettings)
+fluid_settings_init(fluid_settings_t *settings)
 {
-  fluid_return_if_fail (settings != NULL);
+    fluid_return_if_fail(settings != NULL);
 
-  fluid_synth_settings(settings);
+    fluid_synth_settings(settings);
 #if 0
-  fluid_shell_settings(settings);
-  fluid_player_settings(settings);
-  fluid_file_renderer_settings(settings);
-  fluid_audio_driver_settings(settings);
-  fluid_midi_driver_settings(settings);
+    fluid_shell_settings(settings);
+    fluid_player_settings(settings);
+    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;
+    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;
-  }
+    if(FLUID_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;
+    FLUID_STRCPY(buf, s);      /* copy string to buffer, since it gets modified */
+    tokstr = buf;
 
-  while ((tok = fluid_strtok (&tokstr, ".")))
-  {
-    if (n >= MAX_SETTINGS_TOKENS)
+    while((tok = fluid_strtok(&tokstr, ".")))
     {
-      FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",
-               MAX_SETTINGS_TOKENS);
-      return 0;
-    } else
-        ptr[n++] = tok;
-  }
+        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;
+    return n;
 }
 
 /**
@@ -339,34 +373,45 @@ fluid_settings_tokenize(const char *s, char *buf, char **ptr)
  * @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
+ * @return #FLUID_OK if the node exists, #FLUID_FAILED otherwise
  */
 static int
-fluid_settings_get(fluid_settings_tsettings, const char *name,
+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;
+    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);
 
-  ntokens = fluid_settings_tokenize (name, buf, tokens);
+    if(table == NULL || ntokens <= 0)
+    {
+        return FLUID_FAILED;
+    }
 
-  if (table == NULL || ntokens <= 0) return 0;
+    for(n = 0; n < ntokens; n++)
+    {
 
-  for (n = 0; n < ntokens; n++) {
+        node = fluid_hashtable_lookup(table, tokens[n]);
 
-    node = fluid_hashtable_lookup(table, tokens[n]);
-    if (!node) return 0;
+        if(!node)
+        {
+            return FLUID_FAILED;
+        }
 
-    table = (node->type == FLUID_SET_TYPE) ? ((fluid_set_setting_t *)node)->hashtable : NULL;
-  }
+        table = (node->type == FLUID_SET_TYPE) ? node->set.hashtable : NULL;
+    }
 
-  if (value) *value = node;
+    if(value)
+    {
+        *value = node;
+    }
 
-  return 1;
+    return FLUID_OK;
 }
 
 /**
@@ -375,583 +420,741 @@ fluid_settings_get(fluid_settings_t* settings, const char *name,
  * @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
+ * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
  */
 static int
-fluid_settings_set(fluid_settings_t* settings, const char *name, void* value)
+fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_node_t *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;
+    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;
 
-  for (n = 0; n < num; n++) {
+    num = fluid_settings_tokenize(name, buf, tokens);
 
-    node = fluid_hashtable_lookup(table, tokens[n]);
+    if(num == 0)
+    {
+        return FLUID_FAILED;
+    }
 
-    if (node) {
+    num--;
 
-      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;
-      }
+    for(n = 0; n < num; n++)
+    {
 
-    } else {
-      /* create a new node */
-      fluid_set_setting_t* setnode;
+        node = fluid_hashtable_lookup(table, tokens[n]);
 
-      dupname = FLUID_STRDUP (tokens[n]);
-      setnode = new_fluid_set_setting ();
+        if(node)
+        {
 
-      if (!dupname || !setnode)
-      {
-        if (dupname) FLUID_FREE (dupname);
-        else FLUID_LOG(FLUID_ERR, "Out of memory");
+            if(node->type == FLUID_SET_TYPE)
+            {
+                table = node->set.hashtable;
+            }
+            else
+            {
+                /* path ends prematurely */
+                FLUID_LOG(FLUID_WARN, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name);
+                return FLUID_FAILED;
+            }
 
-        if (setnode) delete_fluid_set_setting (setnode);
+        }
+        else
+        {
+            /* create a new node */
+            fluid_setting_node_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 FLUID_FAILED;
+            }
+
+            fluid_hashtable_insert(table, dupname, setnode);
+            table = setnode->set.hashtable;
+        }
+    }
 
-        return 0;
-      }
+    dupname = FLUID_STRDUP(tokens[num]);
 
-      fluid_hashtable_insert(table, dupname, setnode);
-      table = setnode->hashtable;
+    if(!dupname)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return FLUID_FAILED;
     }
-  }
 
-  dupname = FLUID_STRDUP (tokens[num]);
+    fluid_hashtable_insert(table, dupname, value);
 
-  if (!dupname)
-  {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return 0;
-  }
+    return FLUID_OK;
+}
+
+/**
+ * Registers a new string value for the specified setting.
+ *
+ * @param settings a settings object
+ * @param name the setting's name
+ * @param def the default value for the setting
+ * @param hints the hints for the setting
+ * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise
+ */
+int
+fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints)
+{
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  fluid_hashtable_insert(table, dupname, value);
+    fluid_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
 
-  return 1;
+    fluid_rec_mutex_lock(settings->mutex);
+
+    if(fluid_settings_get(settings, name, &node) != FLUID_OK)
+    {
+        node = new_fluid_str_setting(def, def, hints);
+        retval = fluid_settings_set(settings, name, node);
+
+        if(retval != FLUID_OK)
+        {
+            delete_fluid_str_setting(node);
+        }
+    }
+    else
+    {
+        /* if variable already exists, don't change its value. */
+        if(node->type == FLUID_STR_TYPE)
+        {
+            fluid_str_setting_t *setting = &node->str;
+            setting->def = def ? FLUID_STRDUP(def) : NULL;
+            setting->hints = hints;
+            retval = FLUID_OK;
+        }
+        else
+        {
+            FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+        }
+    }
+
+    fluid_rec_mutex_unlock(settings->mutex);
+
+    return retval;
 }
 
-/** returns 1 if the value has been registered correctly, 0
-    otherwise */
+/**
+ * Registers a new float value for the specified setting.
+ *
+ * @param settings a settings object
+ * @param name the setting's name
+ * @param def the default value for the setting
+ * @param min the smallest allowed value for the setting
+ * @param max the largest allowed value for the setting
+ * @param hints the hints for the setting
+ * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise
+ */
 int
-fluid_settings_register_str(fluid_settings_t* settings, const char* name, const char* def, int hints,
-                           fluid_str_update_t fun, void* data)
+fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def,
+                            double min, double max, int hints)
 {
-  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;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
+
+    fluid_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+
+    /* 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) != FLUID_OK)
+    {
+        /* insert a new setting */
+        node = new_fluid_num_setting(min, max, def, hints);
+        retval = fluid_settings_set(settings, name, node);
+
+        if(retval != FLUID_OK)
+        {
+            delete_fluid_num_setting(node);
+        }
+    }
+    else
+    {
+        if(node->type == FLUID_NUM_TYPE)
+        {
+            /* update the existing setting but don't change its value */
+            fluid_num_setting_t *setting = &node->num;
+            setting->min = min;
+            setting->max = max;
+            setting->def = def;
+            setting->hints = hints;
+            retval = FLUID_OK;
+        }
+        else
+        {
+            /* type mismatch */
+            FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+        }
+    }
+
+    fluid_rec_mutex_unlock(settings->mutex);
+
+    return retval;
 }
 
-/** returns 1 if the value has been register correctly, zero
-    otherwise */
+/**
+ * Registers a new integer value for the specified setting.
+ *
+ * @param settings a settings object
+ * @param name the setting's name
+ * @param def the default value for the setting
+ * @param min the smallest allowed value for the setting
+ * @param max the largest allowed value for the setting
+ * @param hints the hints for the setting
+ * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED 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_settings_register_int(fluid_settings_t *settings, const char *name, int def,
+                            int min, int max, int hints)
 {
-  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;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
+
+    fluid_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+
+    /* 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) != FLUID_OK)
+    {
+        /* insert a new setting */
+        node = new_fluid_int_setting(min, max, def, hints);
+        retval = fluid_settings_set(settings, name, node);
+
+        if(retval != FLUID_OK)
+        {
+            delete_fluid_int_setting(node);
+        }
+    }
+    else
+    {
+        if(node->type == FLUID_INT_TYPE)
+        {
+            /* update the existing setting but don't change its value */
+            fluid_int_setting_t *setting = &node->i;
+            setting->min = min;
+            setting->max = max;
+            setting->def = def;
+            setting->hints = hints;
+            retval = FLUID_OK;
+        }
+        else
+        {
+            /* type mismatch */
+            FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+        }
+    }
+
+    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)
+/**
+ * Registers a callback for the specified string setting.
+ *
+ * @param settings a settings object
+ * @param name the setting's name
+ * @param callback an update function for the setting
+ * @param data user supplied data passed to the update function
+ * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise
+ */
+int fluid_settings_callback_str(fluid_settings_t *settings, const char *name,
+                                fluid_str_update_t callback, 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;
+    fluid_setting_node_t *node;
+    fluid_str_setting_t *setting;
+
+    fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
+
+    fluid_rec_mutex_lock(settings->mutex);
+
+    if((fluid_settings_get(settings, name, &node) != FLUID_OK)
+            || node->type != FLUID_STR_TYPE)
+    {
+        fluid_rec_mutex_unlock(settings->mutex);
+        return FLUID_FAILED;
+    }
+
+    setting = &node->str;
+    setting->update = callback;
+    setting->data = data;
+
+    fluid_rec_mutex_unlock(settings->mutex);
+    return FLUID_OK;
 }
 
 /**
- * Get the type of the setting with the given name
+ * Registers a callback for the specified numeric setting.
  *
  * @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
+ * @param name the setting's name
+ * @param callback an update function for the setting
+ * @param data user supplied data passed to the update function
+ * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise
  */
-int
-fluid_settings_get_type(fluid_settings_t* settings, const char *name)
+int fluid_settings_callback_num(fluid_settings_t *settings, const char *name,
+                                fluid_num_update_t callback, void *data)
 {
-  fluid_setting_node_t *node;
-  int type;
+    fluid_setting_node_t *node;
+    fluid_num_setting_t *setting;
+
+    fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
 
-  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);
+
+    if((fluid_settings_get(settings, name, &node) != FLUID_OK)
+            || node->type != FLUID_NUM_TYPE)
+    {
+        fluid_rec_mutex_unlock(settings->mutex);
+        return FLUID_FAILED;
+    }
 
-  fluid_rec_mutex_lock (settings->mutex);
-  type = fluid_settings_get (settings, name, &node) ? node->type : FLUID_NO_TYPE;
-  fluid_rec_mutex_unlock (settings->mutex);
+    setting = &node->num;
+    setting->update = callback;
+    setting->data = data;
 
-  return (type);
+    fluid_rec_mutex_unlock(settings->mutex);
+    return FLUID_OK;
 }
 
 /**
- * Get the hints for the named setting as an integer bitmap
+ * Registers a callback for the specified int setting.
  *
  * @param settings a settings object
- * @param name a setting's name
- * @return the hints associated to the named setting if it exists, zero otherwise
+ * @param name the setting's name
+ * @param callback an update function for the setting
+ * @param data user supplied data passed to the update function
+ * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise
  */
-int
-fluid_settings_get_hints(fluid_settings_t* settings, const char *name)
+int fluid_settings_callback_int(fluid_settings_t *settings, const char *name,
+                                fluid_int_update_t callback, void *data)
 {
-  fluid_setting_node_t *node;
-  int hints = 0;
+    fluid_setting_node_t *node;
+    fluid_int_setting_t *setting;
 
-  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(settings != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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;
+    if((fluid_settings_get(settings, name, &node) != FLUID_OK)
+            || node->type != FLUID_INT_TYPE)
+    {
+        fluid_rec_mutex_unlock(settings->mutex);
+        return FLUID_FAILED;
     }
-  }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    setting = &node->i;
+    setting->update = callback;
+    setting->data = data;
 
-  return hints;
+    fluid_rec_mutex_unlock(settings->mutex);
+    return FLUID_OK;
 }
 
 /**
- * Ask whether the setting is changeable in real-time.
+ * Get the type of the setting with the given name
  *
  * @param settings a settings object
  * @param name a setting's name
- * @return non zero if the setting is changeable in real-time
+ * @return the type for the named setting (see #fluid_types_enum), or #FLUID_NO_TYPE when it does not exist
  */
 int
-fluid_settings_is_realtime(fluid_settings_t* settings, const char *name)
+fluid_settings_get_type(fluid_settings_t *settings, const char *name)
 {
-  fluid_setting_node_t *node;
-  int isrealtime = FALSE;
+    fluid_setting_node_t *node;
+    int type = FLUID_NO_TYPE;
 
-  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(settings != NULL, type);
+    fluid_return_val_if_fail(name != NULL, type);
+    fluid_return_val_if_fail(name[0] != '\0', type);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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;
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK)
+    {
+        type = node->type;
     }
-  }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return isrealtime;
+    return type;
 }
 
 /**
- * Set a string value for a named setting
+ * Get the hints for the named setting as an integer bitmap
  *
  * @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
+ * @param hints set to the hints associated to the setting if it exists
+ * @return #FLUID_OK if hints associated to the named setting exist, #FLUID_FAILED otherwise
  */
 int
-fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str)
+fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hints)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
+
+    fluid_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', 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);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK)
+    {
+        if(node->type == FLUID_NUM_TYPE)
+        {
+            fluid_num_setting_t *setting = &node->num;
+            *hints = setting->hints;
+            retval = FLUID_OK;
+        }
+        else if(node->type == FLUID_STR_TYPE)
+        {
+            fluid_str_setting_t *setting = &node->str;
+            *hints = setting->hints;
+            retval = FLUID_OK;
+        }
+        else if(node->type == FLUID_INT_TYPE)
+        {
+            fluid_int_setting_t *setting = &node->i;
+            *hints = setting->hints;
+            retval = FLUID_OK;
+        }
+    }
 
-  if (fluid_settings_get (settings, name, &node)) {
-    if (node->type == FLUID_STR_TYPE) {
-      fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+    fluid_rec_mutex_unlock(settings->mutex);
 
-      if (setting->value) FLUID_FREE (setting->value);
-      setting->value = str ? FLUID_STRDUP (str) : NULL;
+    return retval;
+}
 
-      /* 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;
+/**
+ * Ask whether the setting is changeable in real-time.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return TRUE if the setting is changeable in real-time, FALSE otherwise
+ */
+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);
 
-      if (setting->hints & FLUID_HINT_TOGGLED)
-      {
-        if (FLUID_STRCMP (str, "yes") == 0)
+    fluid_rec_mutex_lock(settings->mutex);
+
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK)
+    {
+        if(node->type == FLUID_NUM_TYPE)
+        {
+            fluid_num_setting_t *setting = &node->num;
+            isrealtime = setting->update != NULL;
+        }
+        else if(node->type == FLUID_STR_TYPE)
         {
-          setting->value = TRUE;
-          if (setting->update) (*setting->update)(setting->data, name, TRUE);
+            fluid_str_setting_t *setting = &node->str;
+            isrealtime = setting->update != NULL;
         }
-        else if (FLUID_STRCMP (str, "no") == 0)
+        else if(node->type == FLUID_INT_TYPE)
         {
-          setting->value = FALSE;
-          if (setting->update) (*setting->update)(setting->data, name, FALSE);
+            fluid_int_setting_t *setting = &node->i;
+            isrealtime = setting->update != NULL;
         }
-      }
     }
-  } 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);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return isrealtime;
 }
 
 /**
- * Copy the value of a string setting
+ * Set a string value for a named 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.
+ * @param str new string value
+ * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
  */
 int
-fluid_settings_copystr(fluid_settings_t* settings, const char *name,
-                       char *str, int len)
+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_return_val_if_fail (str != NULL, 0);
-  fluid_return_val_if_fail (len > 0, 0);
+    fluid_setting_node_t *node;
+    fluid_str_setting_t *setting;
+    char *new_value = NULL;
+    fluid_str_update_t callback = NULL;
+    void *data = NULL;
 
-  str[0] = 0;
+    fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_rec_mutex_lock(settings->mutex);
 
-  if (fluid_settings_get (settings, name, &node))
-  {
-    if (node->type == FLUID_STR_TYPE)
+    if((fluid_settings_get(settings, name, &node) != FLUID_OK)
+            || (node->type != FLUID_STR_TYPE))
     {
-      fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+        goto error_recovery;
+    }
 
-      if (setting->value)
-      {
-        FLUID_STRNCPY (str, setting->value, len);
-        str[len - 1] = 0;   /* Force terminate, in case of truncation */
-      }
+    setting = &node->str;
 
-      retval = 1;
+    if(setting->value)
+    {
+        FLUID_FREE(setting->value);
     }
-    else if (node->type == FLUID_INT_TYPE)      /* Handle boolean integers for backwards compatibility */
+
+    if(str)
     {
-      fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+        new_value = FLUID_STRDUP(str);
+
+        if(new_value == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            goto error_recovery;
+        }
+    }
+
+    setting->value = new_value;
 
-      if (setting->hints & FLUID_HINT_TOGGLED)
-      {
-        FLUID_STRNCPY (str, setting->value ? "yes" : "no", len);
-        str[len - 1] = 0;   /* Force terminate, in case of truncation */
+    callback = setting->update;
+    data = setting->data;
 
-        retval = 1;
-      }
+    /* Release the mutex before calling the update callback, to avoid
+     * possible deadlocks with FluidSynths API lock */
+    fluid_rec_mutex_unlock(settings->mutex);
+
+    if(callback)
+    {
+        (*callback)(data, name, new_value);
     }
-  }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    return FLUID_OK;
 
-  return retval;
+error_recovery:
+    fluid_rec_mutex_unlock(settings->mutex);
+    return FLUID_FAILED;
 }
 
 /**
- * Duplicate the value of a string setting
+ * Copy the value of a string setting into the provided buffer (thread safe)
  * @param settings a settings object
  * @param name a setting's name
- * @param str Location to store pointer to allocated duplicate string
- * @return 1 if the value exists and was successfully duplicated, 0 otherwise
+ * @param str Caller supplied buffer to copy string value to
+ * @param len Size of 'str' buffer (no more than len bytes will be written, which
+ *   will always include a zero terminator)
+ * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
  * @since 1.1.0
  *
- * 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.
+ * @note A size of 256 should be more than sufficient for the string buffer.
  */
 int
-fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str)
+fluid_settings_copystr(fluid_settings_t *settings, const char *name,
+                       char *str, int len)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
+
+    fluid_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(str != NULL, retval);
+    fluid_return_val_if_fail(len > 0, 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_return_val_if_fail (str != NULL, 0);
+    str[0] = 0;
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_rec_mutex_lock(settings->mutex);
 
-  if (fluid_settings_get(settings, name, &node))
-  {
-    if (node->type == FLUID_STR_TYPE)
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK)
     {
-      fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+        if(node->type == FLUID_STR_TYPE)
+        {
+            fluid_str_setting_t *setting = &node->str;
 
-      if (setting->value)
-      {
-        *str = FLUID_STRDUP (setting->value);
-        if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
-      }
+            if(setting->value)
+            {
+                FLUID_STRNCPY(str, setting->value, len);
+            }
 
-      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;
+            retval = FLUID_OK;
+        }
+        else if(node->type == FLUID_INT_TYPE)       /* Handle boolean integers for backwards compatibility */
+        {
+            fluid_int_setting_t *setting = &node->i;
 
-      if (setting->hints & FLUID_HINT_TOGGLED)
-      {
-        *str = FLUID_STRDUP (setting->value ? "yes" : "no");
-        if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
+            if(setting->hints & FLUID_HINT_TOGGLED)
+            {
+                FLUID_STRNCPY(str, setting->value ? "yes" : "no", len);
 
-        if (!setting->value || *str) retval = 1;    /* Don't set to 1 if out of memory */
-      }
+                retval = FLUID_OK;
+            }
+        }
     }
-  }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
 }
 
 /**
- * Get the value of a string setting
+ * 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 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.
+ * @param str Location to store pointer to allocated duplicate string
+ * @return #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED otherwise
+ * @since 1.1.0
  *
- * 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.
+ * 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_getstr(fluid_settings_t* settings, const char *name, char** str)
+fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(str != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_rec_mutex_lock(settings->mutex);
 
-  if (fluid_settings_get(settings, name, &node))
-  {
-    if (node->type == FLUID_STR_TYPE)
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK)
     {
-      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;
-      }
+        if(node->type == FLUID_STR_TYPE)
+        {
+            fluid_str_setting_t *setting = &node->str;
+
+            if(setting->value)
+            {
+                *str = FLUID_STRDUP(setting->value);
+
+                if(!*str)
+                {
+                    FLUID_LOG(FLUID_ERR, "Out of memory");
+                }
+            }
+
+            if(!setting->value || *str)
+            {
+                retval = FLUID_OK;    /* Don't set to FLUID_OK if out of memory */
+            }
+        }
+        else if(node->type == FLUID_INT_TYPE)       /* Handle boolean integers for backwards compatibility */
+        {
+            fluid_int_setting_t *setting = &node->i;
+
+            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 = FLUID_OK;    /* Don't set to FLUID_OK if out of memory */
+                }
+            }
+        }
     }
-  }
-  else *str = NULL;
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    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
+ * @return TRUE if the value exists and is equal to 's', FALSE otherwise
  */
 int
-fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const char *s)
+fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *s)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = 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_return_val_if_fail (s != NULL, 0);
+    fluid_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(s != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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 */
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK)
     {
-      fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+        if(node->type == FLUID_STR_TYPE)
+        {
+            fluid_str_setting_t *setting = &node->str;
+
+            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 = &node->i;
 
-      if (setting->hints & FLUID_HINT_TOGGLED)
-        retval = FLUID_STRCMP (setting->value ? "yes" : "no", s) == 0;
+            if(setting->hints & FLUID_HINT_TOGGLED)
+            {
+                retval = FLUID_STRCMP(setting->value ? "yes" : "no", s) == 0;
+            }
+        }
     }
-  }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
 }
 
 /**
@@ -960,39 +1163,43 @@ fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const ch
  *
  * @param settings a settings object
  * @param name a setting's name
- * @return the default string value of the setting if it exists, NULL otherwise
+ * @param def the default string value of the setting if it exists
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
  */
-char*
-fluid_settings_getstr_default(fluid_settings_t* settings, const char *name)
+int
+fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def)
 {
-  fluid_setting_node_t *node;
-  char *retval = NULL;
+    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_return_val_if_fail(settings != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_rec_mutex_lock(settings->mutex);
 
-  if (fluid_settings_get (settings, name, &node))
-  {
-    if (node->type == FLUID_STR_TYPE)
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK)
     {
-      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(node->type == FLUID_STR_TYPE)
+        {
+            fluid_str_setting_t *setting = &node->str;
+            retval = setting->def;
+        }
+        else if(node->type == FLUID_INT_TYPE)       /* Handle boolean integers for backwards compatibility */
+        {
+            fluid_int_setting_t *setting = &node->i;
 
-      if (setting->hints & FLUID_HINT_TOGGLED)
-        retval = setting->def ? "yes" : "no";
+            if(setting->hints & FLUID_HINT_TOGGLED)
+            {
+                retval = setting->def ? "yes" : "no";
+            }
+        }
     }
-  }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    *def = retval;
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval != NULL ? FLUID_OK : FLUID_FAILED;
 }
 
 /**
@@ -1000,35 +1207,36 @@ fluid_settings_getstr_default(fluid_settings_t* settings, const char *name)
  * @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
+ * @return #FLUID_OK if the setting exists and option was added, #FLUID_FAILED otherwise
  *
  * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set.
  */
 int
-fluid_settings_add_option(fluid_settings_tsettings, const char *name, const char *s)
+fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(s != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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;
-  }
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_STR_TYPE))
+    {
+        fluid_str_setting_t *setting = &node->str;
+        char *copy = FLUID_STRDUP(s);
+        setting->options = fluid_list_append(setting->options, copy);
+        setting->hints |= FLUID_HINT_OPTIONLIST;
+        retval = FLUID_OK;
+    }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
 }
 
 /**
@@ -1036,42 +1244,47 @@ fluid_settings_add_option(fluid_settings_t* settings, const char *name, const ch
  * @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
+ * @return #FLUID_OK if the setting exists and option was removed, #FLUID_FAILED otherwise
  */
 int
-fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const char* s)
+fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(s != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_rec_mutex_lock(settings->mutex);
 
-  if (fluid_settings_get(settings, name, &node)
-      && (node->type == FLUID_STR_TYPE)) {
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_STR_TYPE))
+    {
 
-    fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
-    fluid_list_t* list = setting->options;
+        fluid_str_setting_t *setting = &node->str;
+        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);
+        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 = FLUID_OK;
+                break;
+            }
+
+            list = fluid_list_next(list);
+        }
     }
-  }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
 }
 
 /**
@@ -1080,46 +1293,55 @@ fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const
  * @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
+ * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
  */
 int
-fluid_settings_setnum(fluid_settings_tsettings, const char *name, double val)
+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_setting_node_t *node;
+    fluid_num_setting_t *setting;
+    fluid_num_update_t callback = NULL;
+    void *data = NULL;
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
 
-  if (fluid_settings_get(settings, name, &node)) {
-    if (node->type == FLUID_NUM_TYPE) {
-      setting = (fluid_num_setting_t*) node;
+    fluid_rec_mutex_lock(settings->mutex);
 
-      if (val < setting->min) val = setting->min;
-      else if (val > setting->max) val = setting->max;
+    if((fluid_settings_get(settings, name, &node) != FLUID_OK)
+            || (node->type != FLUID_NUM_TYPE))
+    {
+        goto error_recovery;
+    }
 
-      setting->value = val;
+    setting = &node->num;
 
-      /* Call under lock to keep update() synchronized with the current value */
-      if (setting->update) (*setting->update)(setting->data, name, val);
-      retval = 1;
+    if(val < setting->min || val > setting->max)
+    {
+        FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name);
+        goto error_recovery;
     }
-  } 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);
+    callback = setting->update;
+    data = setting->data;
 
-  return retval;
+    /* Release the mutex before calling the update callback, to avoid
+     * possible deadlocks with FluidSynths API lock */
+    fluid_rec_mutex_unlock(settings->mutex);
+
+    if(callback)
+    {
+        (*callback)(data, name, val);
+    }
+
+    return FLUID_OK;
+
+error_recovery:
+    fluid_rec_mutex_unlock(settings->mutex);
+    return FLUID_FAILED;
 }
 
 /**
@@ -1128,31 +1350,53 @@ fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val)
  * @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
+ * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
  */
 int
-fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val)
+fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(val != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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;
-  }
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_NUM_TYPE))
+    {
+        fluid_num_setting_t *setting = &node->num;
+        *val = setting->value;
+        retval = FLUID_OK;
+    }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
+}
+
+/**
+ * float-typed wrapper for fluid_settings_getnum
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val variable pointer to receive the setting's float value
+ * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
+ */
+int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val)
+{
+    double tmp;
+
+    if(fluid_settings_getnum(settings, name, &tmp) == FLUID_OK)
+    {
+        *val = tmp;
+        return FLUID_OK;
+    }
+
+    return FLUID_FAILED;
 }
 
 /**
@@ -1162,29 +1406,35 @@ fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val)
  * @param name a setting's name
  * @param min setting's range lower limit
  * @param max setting's range upper limit
+ * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise
  */
-void
-fluid_settings_getnum_range(fluid_settings_tsettings, const char *name,
-                            double* min, double* max)
+int
+fluid_settings_getnum_range(fluid_settings_t *settings, const char *name,
+                            double *min, double *max)
 {
-  fluid_setting_node_t *node;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
+
+    fluid_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(min != NULL, retval);
+    fluid_return_val_if_fail(max != NULL, retval);
 
-  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);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_NUM_TYPE))
+    {
+        fluid_num_setting_t *setting = &node->num;
+        *min = setting->min;
+        *max = setting->max;
+        retval = FLUID_OK;
+    }
 
-  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);
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    return retval;
 }
 
 /**
@@ -1192,29 +1442,33 @@ fluid_settings_getnum_range(fluid_settings_t* settings, const char *name,
  *
  * @param settings a settings object
  * @param name a setting's name
- * @return the default value if the named setting exists, 0.0f otherwise
+ * @param val set to the default value if the named setting exists
+ * @return #FLUID_OK if the default value of the named setting exists, #FLUID_FAILED otherwise
  */
-double
-fluid_settings_getnum_default(fluid_settings_t* settings, const char *name)
+int
+fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val)
 {
-  fluid_setting_node_t *node;
-  double retval = 0.0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(val != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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;
-  }
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_NUM_TYPE))
+    {
+        fluid_num_setting_t *setting = &node->num;
+        *val = setting->def;
+        retval = FLUID_OK;
+    }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
 }
 
 /**
@@ -1223,46 +1477,55 @@ fluid_settings_getnum_default(fluid_settings_t* settings, const char *name)
  * @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
+ * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
  */
 int
-fluid_settings_setint(fluid_settings_tsettings, const char *name, int val)
+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_setting_node_t *node;
+    fluid_int_setting_t *setting;
+    fluid_int_update_t callback = NULL;
+    void *data = NULL;
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
 
-  if (fluid_settings_get(settings, name, &node)) {
-    if (node->type == FLUID_INT_TYPE) {
-      setting = (fluid_int_setting_t*) node;
+    fluid_rec_mutex_lock(settings->mutex);
 
-      if (val < setting->min) val = setting->min;
-      else if (val > setting->max) val = setting->max;
+    if((fluid_settings_get(settings, name, &node) != FLUID_OK)
+            || (node->type != FLUID_INT_TYPE))
+    {
+        goto error_recovery;
+    }
 
-      setting->value = val;
+    setting = &node->i;
 
-      /* Call under lock to keep update() synchronized with the current value */
-      if (setting->update) (*setting->update)(setting->data, name, val);
-      retval = 1;
+    if(val < setting->min || val > setting->max)
+    {
+        FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name);
+        goto error_recovery;
     }
-  } 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);
+    callback = setting->update;
+    data = setting->data;
+
+    /* Release the mutex before calling the update callback, to avoid
+     * possible deadlocks with FluidSynths API lock */
+    fluid_rec_mutex_unlock(settings->mutex);
+
+    if(callback)
+    {
+        (*callback)(data, name, val);
+    }
+
+    return FLUID_OK;
 
-  return retval;
+error_recovery:
+    fluid_rec_mutex_unlock(settings->mutex);
+    return FLUID_FAILED;
 }
 
 /**
@@ -1271,31 +1534,32 @@ fluid_settings_setint(fluid_settings_t* settings, const char *name, int val)
  * @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
+ * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
  */
 int
-fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val)
+fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(val != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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;
-  }
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_INT_TYPE))
+    {
+        fluid_int_setting_t *setting = &node->i;
+        *val = setting->value;
+        retval = FLUID_OK;
+    }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
 }
 
 /**
@@ -1304,29 +1568,35 @@ fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val)
  * @param name a setting's name
  * @param min setting's range lower limit
  * @param max setting's range upper limit
+ * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise
  */
-void
-fluid_settings_getint_range(fluid_settings_tsettings, const char *name,
-                            int* min, int* max)
+int
+fluid_settings_getint_range(fluid_settings_t *settings, const char *name,
+                            int *min, int *max)
 {
-  fluid_setting_node_t *node;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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_return_val_if_fail(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(min != NULL, retval);
+    fluid_return_val_if_fail(max != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    fluid_rec_mutex_lock(settings->mutex);
+
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_INT_TYPE))
+    {
+        fluid_int_setting_t *setting = &node->i;
+        *min = setting->min;
+        *max = setting->max;
+        retval = FLUID_OK;
+    }
 
-  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);
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    return retval;
 }
 
 /**
@@ -1334,29 +1604,32 @@ fluid_settings_getint_range(fluid_settings_t* settings, const char *name,
  *
  * @param settings a settings object
  * @param name a setting's name
- * @return the setting's default integer value it it exists, zero otherwise
+ * @param val set to the setting's default integer value if it exists
+ * @return #FLUID_OK if the setting's default integer value exists, #FLUID_FAILED otherwise
  */
-int
-fluid_settings_getint_default(fluid_settings_t* settings, const char *name)
+int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val)
 {
-  fluid_setting_node_t *node;
-  int retval = 0;
+    fluid_setting_node_t *node;
+    int retval = FLUID_FAILED;
 
-  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(settings != NULL, retval);
+    fluid_return_val_if_fail(name != NULL, retval);
+    fluid_return_val_if_fail(name[0] != '\0', retval);
+    fluid_return_val_if_fail(val != NULL, retval);
 
-  fluid_rec_mutex_lock (settings->mutex);
+    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;
-  }
+    if(fluid_settings_get(settings, name, &node) == FLUID_OK
+            && (node->type == FLUID_INT_TYPE))
+    {
+        fluid_int_setting_t *setting = &node->i;
+        *val = setting->def;
+        retval = FLUID_OK;
+    }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return retval;
+    return retval;
 }
 
 /**
@@ -1368,45 +1641,50 @@ fluid_settings_getint_default(fluid_settings_t* settings, const char *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
+ * @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_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_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_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 */
+    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;
-  }
+    if(fluid_settings_get(settings, name, &node) != FLUID_OK
+            || node->type != FLUID_STR_TYPE)
+    {
+        fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
+        return;
+    }
 
-  setting = (fluid_str_setting_t*)node;
+    setting = &node->str;
 
-  /* Duplicate option list */
-  for (p = setting->options; p; p = p->next)
-    newlist = fluid_list_append (newlist, fluid_list_get (p));
+    /* 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);
+    /* 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));
+    for(p = newlist; p; p = p->next)
+    {
+        (*func)(data, name, (const char *)fluid_list_get(p));
+    }
 
-  fluid_rec_mutex_unlock (settings->mutex);   /* -- unlock */
+    fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
 
-  delete_fluid_list (newlist);
+    delete_fluid_list(newlist);
 }
 
 /**
@@ -1418,21 +1696,26 @@ fluid_settings_foreach_option (fluid_settings_t* settings, const char *name,
  * @since 1.1.0
  */
 int
-fluid_settings_option_count (fluid_settings_t *settings, const char *name)
+fluid_settings_option_count(fluid_settings_t *settings, const char *name)
 {
-  fluid_setting_node_t *node;
-  int count = -1;
+    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_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) == FLUID_OK
+            && node->type == FLUID_STR_TYPE)
+    {
+        count = fluid_list_size(node->str.options);
+    }
 
-  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);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  return (count);
+    return (count);
 }
 
 /**
@@ -1445,113 +1728,136 @@ fluid_settings_option_count (fluid_settings_t *settings, const char *name)
  * @since 1.1.0
  */
 char *
-fluid_settings_option_concat (fluid_settings_t *settings, const char *name,
-                              const char *separator)
+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_setting_node_t *node;
+    fluid_str_setting_t *setting;
+    fluid_list_t *p, *newlist = NULL;
+    size_t 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);
+    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 = ", ";
+    if(!separator)
+    {
+        separator = ", ";
+    }
 
-  fluid_rec_mutex_lock (settings->mutex);       /* ++ lock */
+    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);
-  }
+    if(fluid_settings_get(settings, name, &node) != FLUID_OK
+            || node->type != FLUID_STR_TYPE)
+    {
+        fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
+        return (NULL);
+    }
 
-  setting = (fluid_str_setting_t*)node;
+    setting = &node->str;
 
-  /* 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);
+    /* 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)
+        if(option)
+        {
+            newlist = fluid_list_append(newlist, option);
+            len += FLUID_STRLEN(option);
+        }
+    }
+
+    if(count > 1)
     {
-      newlist = fluid_list_append (newlist, option);
-      len += strlen (option);
+        len += (count - 1) * FLUID_STRLEN(separator);
     }
-  }
 
-  if (count > 1) len += (count - 1) * strlen (separator);
-  len++;        /* For terminator */
+    len++;        /* For terminator */
 
-  /* Sort by name */
-  newlist = fluid_list_sort (newlist, fluid_list_str_compare_func);
+    /* Sort by name */
+    newlist = fluid_list_sort(newlist, fluid_list_str_compare_func);
 
-  str = FLUID_MALLOC (len);
+    str = FLUID_MALLOC(len);
 
-  if (str)
-  {
-    str[0] = 0;
-    for (p = newlist; p; p = p->next)
+    if(str)
     {
-      option = fluid_list_get (p);
-      strcat (str, option);
-      if (p->next) strcat (str, separator);
+        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 */
+    fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
 
-  delete_fluid_list (newlist);
+    delete_fluid_list(newlist);
 
-  if (!str) FLUID_LOG (FLUID_ERR, "Out of memory");
+    if(!str)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+    }
 
-  return (str);
+    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() */
+    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_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;
+    fluid_settings_foreach_bag_t *bag = data;
+    char *keystr = key;
+    fluid_setting_node_t *node = value;
+    size_t pathlen;
+    char *s;
+
+    pathlen = FLUID_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(node->set.hashtable,
+                                fluid_settings_foreach_iter, bag);
+        break;
+    }
+
+    bag->path[pathlen] = 0;
+
+    return 0;
 }
 
 /**
@@ -1562,41 +1868,78 @@ fluid_settings_foreach_iter (void* key, void* value, void* data)
  * @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
+ * @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(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_settings_foreach_bag_t bag;
+    fluid_setting_node_t *node;
+    fluid_list_t *p;
+
+    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)
+    {
+        if(fluid_settings_get(settings, (const char *)(p->data), &node) == FLUID_OK
+                && node)
+        {
+            (*func)(data, (const char *)(p->data), node->type);
+        }
+
+        FLUID_FREE(p->data);        /* -- Free name */
+    }
 
-  fluid_return_if_fail (settings != NULL);
-  fluid_return_if_fail (func != NULL);
+    fluid_rec_mutex_unlock(settings->mutex);
 
-  bag.path[0] = 0;
-  bag.names = NULL;
+    delete_fluid_list(bag.names);         /* -- Free names list */
+}
 
-  fluid_rec_mutex_lock (settings->mutex);
+/**
+ * Split a comma-separated list of integers and fill the passed
+ * in buffer with the parsed values.
+ *
+ * @param str the comma-separated string to split
+ * @param buf user-supplied buffer to hold the parsed numbers
+ * @param buf_len length of user-supplied buffer
+ * @return number of parsed values or -1 on failure
+ */
+int fluid_settings_split_csv(const char *str, int *buf, int buf_len)
+{
+    char *s;
+    char *tok;
+    char *tokstr;
+    int n = 0;
 
-  /* Add all node names to the bag.names list */
-  fluid_hashtable_foreach (settings, fluid_settings_foreach_iter, &bag);
+    s = tokstr = FLUID_STRDUP(str);
 
-  /* Sort names */
-  bag.names = fluid_list_sort (bag.names, fluid_list_str_compare_func);
+    if(s == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return -1;
+    }
 
-  /* 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 */
-  }
+    while((tok = fluid_strtok(&tokstr, ",")) && n < buf_len)
+    {
+        buf[n++] = atoi(tok);
+    }
 
-  fluid_rec_mutex_unlock (settings->mutex);
+    FLUID_FREE(s);
 
-  delete_fluid_list (bag.names);        /* -- Free names list */
+    return n;
 }
index 244f0b457b3198712335574f3daeab2739cbd796..4a952f1bad7b7a752ac6da507fc164e265a88172 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #ifndef _FLUID_SETTINGS_H
 #define _FLUID_SETTINGS_H
 
+int fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s);
+int fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s);
 
 
-/** returns 1 if the option was added, 0 otherwise */
-int fluid_settings_add_option(fluid_settings_t* settings, const char* name, const char* s);
+typedef void (*fluid_str_update_t)(void *data, const char *name, const char *value);
 
-/** returns 1 if the option was added, 0 otherwise */
-int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, const char* s);
+int fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints);
+int fluid_settings_callback_str(fluid_settings_t *settings, const char *name,
+                                fluid_str_update_t fun, void *data);
 
 
-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);
+typedef void (*fluid_num_update_t)(void *data, const char *name, double value);
 
-/** returns 0 if the value has been registered correctly, non-zero
-    otherwise */
-int fluid_settings_register_str(fluid_settings_t* settings, const char* name, const char* def, int hints,
-                              fluid_str_update_t fun, void* data);
+int fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def,
+                                double min, double max, int hints);
+int fluid_settings_callback_num(fluid_settings_t *settings, const char *name,
+                                fluid_num_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);
+/* Type specific wrapper for fluid_settings_getnum */
+int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val);
 
-/** 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);
 
+typedef void (*fluid_int_update_t)(void *data, const char *name, int value);
+int fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def,
+                                int min, int max, int hints);
+int fluid_settings_callback_int(fluid_settings_t *settings, const char *name,
+                                fluid_int_update_t fun, void *data);
+
+int fluid_settings_split_csv(const char *str, int *buf, int buf_len);
 
 #endif /* _FLUID_SETTINGS_H */
diff --git a/libs/fluidsynth/src/fluid_sffile.c b/libs/fluidsynth/src/fluid_sffile.c
new file mode 100644 (file)
index 0000000..b3e64cc
--- /dev/null
@@ -0,0 +1,2566 @@
+/* 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 Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#include "fluid_sffile.h"
+#include "fluid_sfont.h"
+#include "fluid_sys.h"
+
+#if LIBSNDFILE_SUPPORT
+#include <sndfile.h>
+#endif
+
+/*=================================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
+*/
+
+/* 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 */
+    SM24_ID
+};
+
+static const char idlist[] = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
+                              "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24"
+                             };
+
+
+/* generator types */
+typedef enum
+{
+    Gen_StartAddrOfs,
+    Gen_EndAddrOfs,
+    Gen_StartLoopAddrOfs,
+    Gen_EndLoopAddrOfs,
+    Gen_StartAddrCoarseOfs,
+    Gen_ModLFO2Pitch,
+    Gen_VibLFO2Pitch,
+    Gen_ModEnv2Pitch,
+    Gen_FilterFc,
+    Gen_FilterQ,
+    Gen_ModLFO2FilterFc,
+    Gen_ModEnv2FilterFc,
+    Gen_EndAddrCoarseOfs,
+    Gen_ModLFO2Vol,
+    Gen_Unused1,
+    Gen_ChorusSend,
+    Gen_ReverbSend,
+    Gen_Pan,
+    Gen_Unused2,
+    Gen_Unused3,
+    Gen_Unused4,
+    Gen_ModLFODelay,
+    Gen_ModLFOFreq,
+    Gen_VibLFODelay,
+    Gen_VibLFOFreq,
+    Gen_ModEnvDelay,
+    Gen_ModEnvAttack,
+    Gen_ModEnvHold,
+    Gen_ModEnvDecay,
+    Gen_ModEnvSustain,
+    Gen_ModEnvRelease,
+    Gen_Key2ModEnvHold,
+    Gen_Key2ModEnvDecay,
+    Gen_VolEnvDelay,
+    Gen_VolEnvAttack,
+    Gen_VolEnvHold,
+    Gen_VolEnvDecay,
+    Gen_VolEnvSustain,
+    Gen_VolEnvRelease,
+    Gen_Key2VolEnvHold,
+    Gen_Key2VolEnvDecay,
+    Gen_Instrument,
+    Gen_Reserved1,
+    Gen_KeyRange,
+    Gen_VelRange,
+    Gen_StartLoopAddrCoarseOfs,
+    Gen_Keynum,
+    Gen_Velocity,
+    Gen_Attenuation,
+    Gen_Reserved2,
+    Gen_EndLoopAddrCoarseOfs,
+    Gen_CoarseTune,
+    Gen_FineTune,
+    Gen_SampleId,
+    Gen_SampleModes,
+    Gen_Reserved3,
+    Gen_ScaleTune,
+    Gen_ExclusiveClass,
+    Gen_OverrideRootKey,
+    Gen_Dummy
+} Gen_Type;
+
+#define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */
+#define Gen_Count Gen_Dummy /* count of generators */
+#define GenArrSize sizeof(SFGenAmount) * Gen_Count /* gen array size */
+
+
+static const unsigned short invalid_inst_gen[] =
+{
+    Gen_Unused1,
+    Gen_Unused2,
+    Gen_Unused3,
+    Gen_Unused4,
+    Gen_Reserved1,
+    Gen_Reserved2,
+    Gen_Reserved3,
+    0
+};
+
+static const unsigned short invalid_preset_gen[] =
+{
+    Gen_StartAddrOfs,
+    Gen_EndAddrOfs,
+    Gen_StartLoopAddrOfs,
+    Gen_EndLoopAddrOfs,
+    Gen_StartAddrCoarseOfs,
+    Gen_EndAddrCoarseOfs,
+    Gen_StartLoopAddrCoarseOfs,
+    Gen_Keynum,
+    Gen_Velocity,
+    Gen_EndLoopAddrCoarseOfs,
+    Gen_SampleModes,
+    Gen_ExclusiveClass,
+    Gen_OverrideRootKey,
+    0
+};
+
+
+#define CHNKIDSTR(id) &idlist[(id - 1) * 4]
+
+/* sfont file chunk sizes */
+#define SF_PHDR_SIZE (38)
+#define SF_BAG_SIZE  (4)
+#define SF_MOD_SIZE  (10)
+#define SF_GEN_SIZE  (4)
+#define SF_IHDR_SIZE (22)
+#define SF_SHDR_SIZE (46)
+
+
+#define READCHUNK(sf, var)                                                  \
+    do                                                                      \
+    {                                                                       \
+        if (sf->fcbs->fread(var, 8, sf->sffd) == FLUID_FAILED)              \
+            return FALSE;                                                   \
+        ((SFChunk *)(var))->size = FLUID_LE32TOH(((SFChunk *)(var))->size); \
+    } while (0)
+
+#define READD(sf, var)                                            \
+    do                                                            \
+    {                                                             \
+        uint32_t _temp;                                           \
+        if (sf->fcbs->fread(&_temp, 4, sf->sffd) == FLUID_FAILED) \
+            return FALSE;                                         \
+        var = FLUID_LE32TOH(_temp);                               \
+    } while (0)
+
+#define READW(sf, var)                                            \
+    do                                                            \
+    {                                                             \
+        uint16_t _temp;                                           \
+        if (sf->fcbs->fread(&_temp, 2, sf->sffd) == FLUID_FAILED) \
+            return FALSE;                                         \
+        var = FLUID_LE16TOH(_temp);                               \
+    } while (0)
+
+#define READID(sf, var)                                        \
+    do                                                         \
+    {                                                          \
+        if (sf->fcbs->fread(var, 4, sf->sffd) == FLUID_FAILED) \
+            return FALSE;                                      \
+    } while (0)
+
+#define READSTR(sf, var)                                        \
+    do                                                          \
+    {                                                           \
+        if (sf->fcbs->fread(var, 20, sf->sffd) == FLUID_FAILED) \
+            return FALSE;                                       \
+        (*var)[20] = '\0';                                      \
+    } while (0)
+
+#define READB(sf, var)                                          \
+    do                                                          \
+    {                                                           \
+        if (sf->fcbs->fread(&var, 1, sf->sffd) == FLUID_FAILED) \
+            return FALSE;                                       \
+    } while (0)
+
+#define FSKIP(sf, size)                                                \
+    do                                                                 \
+    {                                                                  \
+        if (sf->fcbs->fseek(sf->sffd, size, SEEK_CUR) == FLUID_FAILED) \
+            return FALSE;                                              \
+    } while (0)
+
+#define FSKIPW(sf)                                                  \
+    do                                                              \
+    {                                                               \
+        if (sf->fcbs->fseek(sf->sffd, 2, SEEK_CUR) == FLUID_FAILED) \
+            return FALSE;                                           \
+    } while (0)
+
+/* removes and advances a fluid_list_t pointer */
+#define SLADVREM(list, item)                        \
+    do                                              \
+    {                                               \
+        fluid_list_t *_temp = item;                 \
+        item = fluid_list_next(item);               \
+        list = fluid_list_remove_link(list, _temp); \
+        delete1_fluid_list(_temp);                  \
+    } while (0)
+
+
+static int load_header(SFData *sf);
+static int load_body(SFData *sf);
+static int process_info(SFData *sf, int size);
+static int process_sdta(SFData *sf, unsigned int size);
+static int process_pdta(SFData *sf, int size);
+static int load_phdr(SFData *sf, int size);
+static int load_pbag(SFData *sf, int size);
+static int load_pmod(SFData *sf, int size);
+static int load_pgen(SFData *sf, int size);
+static int load_ihdr(SFData *sf, int size);
+static int load_ibag(SFData *sf, int size);
+static int load_imod(SFData *sf, int size);
+static int load_igen(SFData *sf, int size);
+static int load_shdr(SFData *sf, unsigned int size);
+static int fixup_pgen(SFData *sf);
+static int fixup_igen(SFData *sf);
+
+static int chunkid(unsigned int id);
+static int read_listchunk(SFData *sf, SFChunk *chunk);
+static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size);
+static int preset_compare_func(void *a, void *b);
+static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist);
+static int valid_inst_genid(unsigned short genid);
+static int valid_preset_genid(unsigned short genid);
+
+
+static void delete_preset(SFPreset *preset);
+static void delete_inst(SFInst *inst);
+static void delete_zone(SFZone *zone);
+
+static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data);
+static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24);
+
+/*
+ * Open a SoundFont file and parse it's contents into a SFData structure.
+ *
+ * @param fname filename
+ * @param fcbs file callback structure
+ * @return the partially parsed SoundFont as SFData structure or NULL on error
+ */
+SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs)
+{
+    SFData *sf;
+    int fsize = 0;
+
+    if(!(sf = FLUID_NEW(SFData)))
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    FLUID_MEMSET(sf, 0, sizeof(SFData));
+
+    sf->fcbs = fcbs;
+
+    if((sf->sffd = fcbs->fopen(fname)) == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Unable to open file '%s'", fname);
+        goto error_exit;
+    }
+
+    sf->fname = FLUID_STRDUP(fname);
+
+    if(sf->fname == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        goto error_exit;
+    }
+
+    /* get size of file by seeking to end */
+    if(fcbs->fseek(sf->sffd, 0L, SEEK_END) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Seek to end of file failed");
+        goto error_exit;
+    }
+
+    if((fsize = fcbs->ftell(sf->sffd)) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Get end of file position failed");
+        goto error_exit;
+    }
+
+    sf->filesize = fsize;
+
+    if(fcbs->fseek(sf->sffd, 0, SEEK_SET) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Rewind to start of file failed");
+        goto error_exit;
+    }
+
+    if(!load_header(sf))
+    {
+        goto error_exit;
+    }
+
+    return sf;
+
+error_exit:
+    fluid_sffile_close(sf);
+    return NULL;
+}
+
+/*
+ * Parse all preset information from the soundfont
+ *
+ * @return FLUID_OK on success, otherwise FLUID_FAILED
+ */
+int fluid_sffile_parse_presets(SFData *sf)
+{
+    if(!load_body(sf))
+    {
+        return FLUID_FAILED;
+    }
+
+    return FLUID_OK;
+}
+
+/* Load sample data from the soundfont file
+ *
+ * This function will always return the sample data in WAV format. If the sample_type specifies an
+ * Ogg Vorbis compressed sample, it will be decompressed automatically before returning.
+ *
+ * @param sf SFData instance
+ * @param sample_start index of first sample point in Soundfont sample chunk
+ * @param sample_end index of last sample point in Soundfont sample chunk
+ * @param sample_type type of the sample in Soundfont
+ * @param data pointer to sample data pointer, will point to loaded sample data on success
+ * @param data24 pointer to 24-bit sample data pointer if 24-bit data present, will point to loaded
+ *               24-bit sample data on success or NULL if no 24-bit data is present in file
+ *
+ * @return The number of sample words in returned buffers or -1 on failure
+ */
+int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end,
+                                  int sample_type, short **data, char **data24)
+{
+    int num_samples;
+
+    if(sample_type & FLUID_SAMPLETYPE_OGG_VORBIS)
+    {
+        num_samples = fluid_sffile_read_vorbis(sf, sample_start, sample_end, data);
+    }
+    else
+    {
+        num_samples = fluid_sffile_read_wav(sf, sample_start, sample_end, data, data24);
+    }
+
+    return num_samples;
+}
+
+/*
+ * Close a SoundFont file and free the SFData structure.
+ *
+ * @param sf pointer to SFData structure
+ * @param fcbs file callback structure
+ */
+void fluid_sffile_close(SFData *sf)
+{
+    fluid_list_t *entry;
+    SFPreset *preset;
+    SFInst *inst;
+
+    if(sf->sffd)
+    {
+        sf->fcbs->fclose(sf->sffd);
+    }
+
+    FLUID_FREE(sf->fname);
+
+    entry = sf->info;
+
+    while(entry)
+    {
+        FLUID_FREE(fluid_list_get(entry));
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(sf->info);
+
+    entry = sf->preset;
+
+    while(entry)
+    {
+        preset = (SFPreset *)fluid_list_get(entry);
+        delete_preset(preset);
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(sf->preset);
+
+    entry = sf->inst;
+
+    while(entry)
+    {
+        inst = (SFInst *)fluid_list_get(entry);
+        delete_inst(inst);
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(sf->inst);
+
+    entry = sf->sample;
+
+    while(entry)
+    {
+        FLUID_FREE(fluid_list_get(entry));
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(sf->sample);
+
+    FLUID_FREE(sf);
+}
+
+
+/*
+ * Private functions
+ */
+
+/* sound font file load functions */
+static int chunkid(unsigned int id)
+{
+    unsigned int i;
+    const unsigned int *p;
+
+    p = (const unsigned int *)&idlist;
+
+    for(i = 0; i < sizeof(idlist) / sizeof(int); i++, p += 1)
+    {
+        if(*p == id)
+        {
+            return (i + 1);
+        }
+    }
+
+    return UNKN_ID;
+}
+
+static int load_header(SFData *sf)
+{
+    SFChunk chunk;
+
+    READCHUNK(sf, &chunk); /* load RIFF chunk */
+
+    if(chunkid(chunk.id) != RIFF_ID)
+    {
+        /* error if not RIFF */
+        FLUID_LOG(FLUID_ERR, "Not a RIFF file");
+        return FALSE;
+    }
+
+    READID(sf, &chunk.id); /* load file ID */
+
+    if(chunkid(chunk.id) != SFBK_ID)
+    {
+        /* error if not SFBK_ID */
+        FLUID_LOG(FLUID_ERR, "Not a SoundFont file");
+        return FALSE;
+    }
+
+    if(chunk.size != sf->filesize - 8)
+    {
+        FLUID_LOG(FLUID_ERR, "SoundFont file size mismatch");
+        return FALSE;
+    }
+
+    /* Process INFO block */
+    if(!read_listchunk(sf, &chunk))
+    {
+        return FALSE;
+    }
+
+    if(chunkid(chunk.id) != INFO_ID)
+    {
+        FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk");
+        return FALSE;
+    }
+
+    if(!process_info(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    /* Process sample chunk */
+    if(!read_listchunk(sf, &chunk))
+    {
+        return FALSE;
+    }
+
+    if(chunkid(chunk.id) != SDTA_ID)
+    {
+        FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk");
+        return FALSE;
+    }
+
+    if(!process_sdta(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    /* process HYDRA chunk */
+    if(!read_listchunk(sf, &chunk))
+    {
+        return FALSE;
+    }
+
+    if(chunkid(chunk.id) != PDTA_ID)
+    {
+        FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk");
+        return FALSE;
+    }
+
+    sf->hydrapos = sf->fcbs->ftell(sf->sffd);
+    sf->hydrasize = chunk.size;
+
+    return TRUE;
+}
+
+static int load_body(SFData *sf)
+{
+    if(sf->fcbs->fseek(sf->sffd, sf->hydrapos, SEEK_SET) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to seek to HYDRA position");
+        return FALSE;
+    }
+
+    if(!process_pdta(sf, sf->hydrasize))
+    {
+        return FALSE;
+    }
+
+    if(!fixup_pgen(sf))
+    {
+        return FALSE;
+    }
+
+    if(!fixup_igen(sf))
+    {
+        return FALSE;
+    }
+
+    /* sort preset list by bank, preset # */
+    sf->preset = fluid_list_sort(sf->preset, (fluid_compare_func_t)preset_compare_func);
+
+    return TRUE;
+}
+
+static int read_listchunk(SFData *sf, SFChunk *chunk)
+{
+    READCHUNK(sf, chunk); /* read list chunk */
+
+    if(chunkid(chunk->id) != LIST_ID)  /* error if ! list chunk */
+    {
+        FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse");
+        return FALSE;
+    }
+
+    READID(sf, &chunk->id); /* read id string */
+    chunk->size -= 4;
+    return TRUE;
+}
+
+static int process_info(SFData *sf, int size)
+{
+    SFChunk chunk;
+    unsigned char id;
+    char *item;
+    unsigned short ver;
+
+    while(size > 0)
+    {
+        READCHUNK(sf, &chunk);
+        size -= 8;
+
+        id = chunkid(chunk.id);
+
+        if(id == IFIL_ID)
+        {
+            /* sound font version chunk? */
+            if(chunk.size != 4)
+            {
+                FLUID_LOG(FLUID_ERR, "Sound font version info chunk has invalid size");
+                return FALSE;
+            }
+
+            READW(sf, ver);
+            sf->version.major = ver;
+            READW(sf, ver);
+            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 FALSE;
+            }
+
+            if(sf->version.major == 3)
+            {
+#if !LIBSNDFILE_SUPPORT
+                FLUID_LOG(FLUID_WARN,
+                          "Sound font version is %d.%d but fluidsynth was compiled without"
+                          " support for (v3.x)",
+                          sf->version.major, sf->version.minor);
+                return FALSE;
+#endif
+            }
+            else if(sf->version.major > 2)
+            {
+                FLUID_LOG(FLUID_WARN,
+                          "Sound font version is %d.%d which is newer than"
+                          " what this version of fluidsynth was designed for (v2.0x)",
+                          sf->version.major, sf->version.minor);
+                return FALSE;
+            }
+        }
+        else if(id == IVER_ID)
+        {
+            /* ROM version chunk? */
+            if(chunk.size != 4)
+            {
+                FLUID_LOG(FLUID_ERR, "ROM version info chunk has invalid size");
+                return FALSE;
+            }
+
+            READW(sf, ver);
+            sf->romver.major = ver;
+            READW(sf, ver);
+            sf->romver.minor = ver;
+        }
+        else if(id != UNKN_ID)
+        {
+            if((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2))
+            {
+                FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes",
+                          &chunk.id, chunk.size);
+                return FALSE;
+            }
+
+            /* alloc for chunk id and da chunk */
+            if(!(item = FLUID_MALLOC(chunk.size + 1)))
+            {
+                FLUID_LOG(FLUID_ERR, "Out of memory");
+                return FALSE;
+            }
+
+            /* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */
+            sf->info = fluid_list_append(sf->info, item);
+
+            *(unsigned char *)item = id;
+
+            if(sf->fcbs->fread(&item[1], chunk.size, sf->sffd) == FLUID_FAILED)
+            {
+                return FALSE;
+            }
+
+            /* force terminate info item (don't forget uint8 info ID) */
+            *(item + chunk.size) = '\0';
+        }
+        else
+        {
+            FLUID_LOG(FLUID_ERR, "Invalid chunk id in INFO chunk");
+            return FALSE;
+        }
+
+        size -= chunk.size;
+    }
+
+    if(size < 0)
+    {
+        FLUID_LOG(FLUID_ERR, "INFO chunk size mismatch");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static int process_sdta(SFData *sf, unsigned int size)
+{
+    SFChunk chunk;
+
+    if(size == 0)
+    {
+        return TRUE;    /* no sample data? */
+    }
+
+    /* read sub chunk */
+    READCHUNK(sf, &chunk);
+    size -= 8;
+
+    if(chunkid(chunk.id) != SMPL_ID)
+    {
+        FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead");
+        return FALSE;
+    }
+
+    /* 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)
+    {
+        FLUID_LOG(FLUID_ERR, "SDTA chunk size mismatch");
+        return FALSE;
+    }
+
+    /* sample data follows */
+    sf->samplepos = sf->fcbs->ftell(sf->sffd);
+
+    /* used to check validity of sample headers */
+    sf->samplesize = chunk.size;
+
+    FSKIP(sf, chunk.size);
+    size -= chunk.size;
+
+    if(sf->version.major >= 2 && sf->version.minor >= 4)
+    {
+        /* any chance to find another chunk here? */
+        if(size > 8)
+        {
+            /* read sub chunk */
+            READCHUNK(sf, &chunk);
+            size -= 8;
+
+            if(chunkid(chunk.id) == SM24_ID)
+            {
+                int sm24size, sdtahalfsize;
+
+                FLUID_LOG(FLUID_DBG, "Found SM24 chunk");
+
+                if(chunk.size > size)
+                {
+                    FLUID_LOG(FLUID_WARN, "SM24 exeeds SDTA chunk, ignoring SM24");
+                    goto ret; // no error
+                }
+
+                sdtahalfsize = sf->samplesize / 2;
+                /* + 1 byte in the case that half the size of smpl chunk is an odd value */
+                sdtahalfsize += sdtahalfsize % 2;
+                sm24size = chunk.size;
+
+                if(sdtahalfsize != sm24size)
+                {
+                    FLUID_LOG(FLUID_WARN, "SM24 not equal to half the size of SMPL chunk (0x%X != "
+                              "0x%X), ignoring SM24",
+                              sm24size, sdtahalfsize);
+                    goto ret; // no error
+                }
+
+                /* sample data24 follows */
+                sf->sample24pos = sf->fcbs->ftell(sf->sffd);
+                sf->sample24size = sm24size;
+            }
+        }
+    }
+
+ret:
+    FSKIP(sf, size);
+
+    return TRUE;
+}
+
+static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size)
+{
+    unsigned int id;
+    const char *expstr;
+
+    expstr = CHNKIDSTR(expid); /* in case we need it */
+
+    READCHUNK(sf, chunk);
+    *size -= 8;
+
+    if((id = chunkid(chunk->id)) != expid)
+    {
+        FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", expstr);
+        return FALSE;
+    }
+
+    if(chunk->size % reclen)  /* valid chunk size? */
+    {
+        FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", expstr, reclen);
+        return FALSE;
+    }
+
+    if((*size -= chunk->size) < 0)
+    {
+        FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", expstr);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static int process_pdta(SFData *sf, int size)
+{
+    SFChunk chunk;
+
+    if(!pdtahelper(sf, PHDR_ID, SF_PHDR_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_phdr(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, PBAG_ID, SF_BAG_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_pbag(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, PMOD_ID, SF_MOD_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_pmod(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, PGEN_ID, SF_GEN_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_pgen(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, IHDR_ID, SF_IHDR_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_ihdr(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, IBAG_ID, SF_BAG_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_ibag(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, IMOD_ID, SF_MOD_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_imod(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, IGEN_ID, SF_GEN_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_igen(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    if(!pdtahelper(sf, SHDR_ID, SF_SHDR_SIZE, &chunk, &size))
+    {
+        return FALSE;
+    }
+
+    if(!load_shdr(sf, chunk.size))
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/* preset header loader */
+static int load_phdr(SFData *sf, int size)
+{
+    int i, i2;
+    SFPreset *preset, *prev_preset = NULL;
+    unsigned short pbag_idx, prev_pbag_idx = 0;
+
+    if(size % SF_PHDR_SIZE || size == 0)
+    {
+        FLUID_LOG(FLUID_ERR, "Preset header chunk size is invalid");
+        return FALSE;
+    }
+
+    i = size / SF_PHDR_SIZE - 1;
+
+    if(i == 0)
+    {
+        /* at least one preset + term record */
+        FLUID_LOG(FLUID_WARN, "File contains no presets");
+        FSKIP(sf, SF_PHDR_SIZE);
+        return TRUE;
+    }
+
+    for(; i > 0; i--)
+    {
+        /* load all preset headers */
+        if((preset = FLUID_NEW(SFPreset)) == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            return FALSE;
+        }
+        sf->preset = fluid_list_append(sf->preset, preset);
+        preset->zone = NULL; /* In case of failure, fluid_sffile_close can cleanup */
+        READSTR(sf, &preset->name); /* possible read failure ^ */
+        READW(sf, preset->prenum);
+        READW(sf, preset->bank);
+        READW(sf, pbag_idx);
+        READD(sf, preset->libr);
+        READD(sf, preset->genre);
+        READD(sf, preset->morph);
+
+        if(prev_preset)
+        {
+            /* not first preset? */
+            if(pbag_idx < prev_pbag_idx)
+            {
+                FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic");
+                return FALSE;
+            }
+
+            i2 = pbag_idx - prev_pbag_idx;
+
+            while(i2--)
+            {
+                prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL);
+            }
+        }
+        else if(pbag_idx > 0)  /* 1st preset, warn if ofs >0 */
+        {
+            FLUID_LOG(FLUID_WARN, "%d preset zones not referenced, discarding", pbag_idx);
+        }
+
+        prev_preset = preset; /* update preset ptr */
+        prev_pbag_idx = pbag_idx;
+    }
+
+    FSKIP(sf, 24);
+    READW(sf, pbag_idx); /* Read terminal generator index */
+    FSKIP(sf, 12);
+
+    if(pbag_idx < prev_pbag_idx)
+    {
+        FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic");
+        return FALSE;
+    }
+
+    i2 = pbag_idx - prev_pbag_idx;
+
+    while(i2--)
+    {
+        prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL);
+    }
+
+    return TRUE;
+}
+
+/* preset bag loader */
+static int load_pbag(SFData *sf, int size)
+{
+    fluid_list_t *p, *p2;
+    SFZone *z, *pz = NULL;
+    unsigned short genndx, modndx;
+    unsigned short pgenndx = 0, pmodndx = 0;
+    unsigned short i;
+
+    if(size % SF_BAG_SIZE || size == 0)  /* size is multiple of SF_BAG_SIZE? */
+    {
+        FLUID_LOG(FLUID_ERR, "Preset bag chunk size is invalid");
+        return FALSE;
+    }
+
+    p = sf->preset;
+
+    while(p)
+    {
+        /* traverse through presets */
+        p2 = ((SFPreset *)(p->data))->zone;
+
+        while(p2)
+        {
+            /* traverse preset's zones */
+            if((size -= SF_BAG_SIZE) < 0)
+            {
+                FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch");
+                return FALSE;
+            }
+
+            if((z = FLUID_NEW(SFZone)) == NULL)
+            {
+                FLUID_LOG(FLUID_ERR, "Out of memory");
+                return FALSE;
+            }
+            p2->data = z;
+            z->gen = NULL; /* Init gen and mod before possible failure, */
+            z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */
+            READW(sf, genndx); /* possible read failure ^ */
+            READW(sf, modndx);
+            z->instsamp = NULL;
+
+            if(pz)
+            {
+                /* if not first zone */
+                if(genndx < pgenndx)
+                {
+                    FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic");
+                    return FALSE;
+                }
+
+                if(modndx < pmodndx)
+                {
+                    FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic");
+                    return FALSE;
+                }
+
+                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 -= SF_BAG_SIZE;
+
+    if(size != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch");
+        return FALSE;
+    }
+
+    READW(sf, genndx);
+    READW(sf, modndx);
+
+    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 TRUE;
+    }
+
+    if(genndx < pgenndx)
+    {
+        FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic");
+        return FALSE;
+    }
+
+    if(modndx < pmodndx)
+    {
+        FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic");
+        return FALSE;
+    }
+
+    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 TRUE;
+}
+
+/* preset modulator loader */
+static int load_pmod(SFData *sf, int size)
+{
+    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 -= SF_MOD_SIZE) < 0)
+                {
+                    FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch");
+                    return FALSE;
+                }
+
+                if((m = FLUID_NEW(SFMod)) == NULL)
+                {
+                    FLUID_LOG(FLUID_ERR, "Out of memory");
+                    return FALSE;
+                }
+                p3->data = m;
+                READW(sf, m->src);
+                READW(sf, m->dest);
+                READW(sf, m->amount);
+                READW(sf, m->amtsrc);
+                READW(sf, m->trans);
+                p3 = fluid_list_next(p3);
+            }
+
+            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 TRUE;
+    }
+
+    size -= SF_MOD_SIZE;
+
+    if(size != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch");
+        return FALSE;
+    }
+
+    FSKIP(sf, SF_MOD_SIZE); /* terminal mod */
+
+    return TRUE;
+}
+
+/* -------------------------------------------------------------------
+ * 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(SFData *sf, int size)
+{
+    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 -= SF_GEN_SIZE) < 0)
+                {
+                    FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch");
+                    return FALSE;
+                }
+
+                READW(sf, genid);
+
+                if(genid == Gen_KeyRange)
+                {
+                    /* nothing precedes */
+                    if(level == 0)
+                    {
+                        level = 1;
+                        READB(sf, genval.range.lo);
+                        READB(sf, genval.range.hi);
+                    }
+                    else
+                    {
+                        skip = TRUE;
+                    }
+                }
+                else if(genid == Gen_VelRange)
+                {
+                    /* only KeyRange precedes */
+                    if(level <= 1)
+                    {
+                        level = 2;
+                        READB(sf, genval.range.lo);
+                        READB(sf, genval.range.hi);
+                    }
+                    else
+                    {
+                        skip = TRUE;
+                    }
+                }
+                else if(genid == Gen_Instrument)
+                {
+                    /* inst is last gen */
+                    level = 3;
+                    READW(sf, genval.uword);
+                    ((SFZone *)(p2->data))->instsamp = FLUID_INT_TO_POINTER(genval.uword + 1);
+                    break; /* break out of generator loop */
+                }
+                else
+                {
+                    level = 2;
+
+                    if(valid_preset_genid(genid))
+                    {
+                        /* generator valid? */
+                        READW(sf, genval.sword);
+                        dup = find_gen_by_id(genid, z->gen);
+                    }
+                    else
+                    {
+                        skip = TRUE;
+                    }
+                }
+
+                if(!skip)
+                {
+                    if(!dup)
+                    {
+                        /* if gen ! dup alloc new */
+                        if((g = FLUID_NEW(SFGen)) == NULL)
+                        {
+                            FLUID_LOG(FLUID_ERR, "Out of memory");
+                            return FALSE;
+                        }
+                        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(sf);
+                }
+
+                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);
+                    *hz = fluid_list_remove(*hz, p2->data);
+                    delete_zone((SFZone *)fluid_list_get(p2));
+                }
+            }
+
+            while(p3)
+            {
+                /* Kill any zones following an instrument */
+                discarded = TRUE;
+
+                if((size -= SF_GEN_SIZE) < 0)
+                {
+                    FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch");
+                    return FALSE;
+                }
+
+                FSKIP(sf, SF_GEN_SIZE);
+                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 TRUE;
+    }
+
+    size -= SF_GEN_SIZE;
+
+    if(size != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch");
+        return FALSE;
+    }
+
+    FSKIP(sf, SF_GEN_SIZE); /* terminal gen */
+
+    return TRUE;
+}
+
+/* instrument header loader */
+static int load_ihdr(SFData *sf, int size)
+{
+    int i, i2;
+    SFInst *p, *pr = NULL; /* ptr to current & previous instrument */
+    unsigned short zndx, pzndx = 0;
+
+    if(size % SF_IHDR_SIZE || size == 0)  /* chunk size is valid? */
+    {
+        FLUID_LOG(FLUID_ERR, "Instrument header has invalid size");
+        return FALSE;
+    }
+
+    size = size / SF_IHDR_SIZE - 1;
+
+    if(size == 0)
+    {
+        /* at least one preset + term record */
+        FLUID_LOG(FLUID_WARN, "File contains no instruments");
+        FSKIP(sf, SF_IHDR_SIZE);
+        return TRUE;
+    }
+
+    for(i = 0; i < size; i++)
+    {
+        /* load all instrument headers */
+        if((p = FLUID_NEW(SFInst)) == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            return FALSE;
+        }
+        sf->inst = fluid_list_append(sf->inst, p);
+        p->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */
+        p->idx = i;
+        READSTR(sf, &p->name); /* Possible read failure ^ */
+        READW(sf, zndx);
+
+        if(pr)
+        {
+            /* not first instrument? */
+            if(zndx < pzndx)
+            {
+                FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic");
+                return FALSE;
+            }
+
+            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(sf, 20);
+    READW(sf, zndx);
+
+    if(zndx < pzndx)
+    {
+        FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic");
+        return FALSE;
+    }
+
+    i2 = zndx - pzndx;
+
+    while(i2--)
+    {
+        pr->zone = fluid_list_prepend(pr->zone, NULL);
+    }
+
+    return TRUE;
+}
+
+/* instrument bag loader */
+static int load_ibag(SFData *sf, int size)
+{
+    fluid_list_t *p, *p2;
+    SFZone *z, *pz = NULL;
+    unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0;
+    int i;
+
+    if(size % SF_BAG_SIZE || size == 0)  /* size is multiple of SF_BAG_SIZE? */
+    {
+        FLUID_LOG(FLUID_ERR, "Instrument bag chunk size is invalid");
+        return FALSE;
+    }
+
+    p = sf->inst;
+
+    while(p)
+    {
+        /* traverse through inst */
+        p2 = ((SFInst *)(p->data))->zone;
+
+        while(p2)
+        {
+            /* load this inst's zones */
+            if((size -= SF_BAG_SIZE) < 0)
+            {
+                FLUID_LOG(FLUID_ERR, "Instrument bag chunk size mismatch");
+                return FALSE;
+            }
+
+            if((z = FLUID_NEW(SFZone)) == NULL)
+            {
+                FLUID_LOG(FLUID_ERR, "Out of memory");
+                return FALSE;
+            }
+            p2->data = z;
+            z->gen = NULL; /* In case of failure, */
+            z->mod = NULL; /* fluid_sffile_close can clean up */
+            READW(sf, genndx); /* READW = possible read failure */
+            READW(sf, modndx);
+            z->instsamp = NULL;
+
+            if(pz)
+            {
+                /* if not first zone */
+                if(genndx < pgenndx)
+                {
+                    FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic");
+                    return FALSE;
+                }
+
+                if(modndx < pmodndx)
+                {
+                    FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic");
+                    return FALSE;
+                }
+
+                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 -= SF_BAG_SIZE;
+
+    if(size != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "Instrument chunk size mismatch");
+        return FALSE;
+    }
+
+    READW(sf, genndx);
+    READW(sf, modndx);
+
+    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 TRUE;
+    }
+
+    if(genndx < pgenndx)
+    {
+        FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic");
+        return FALSE;
+    }
+
+    if(modndx < pmodndx)
+    {
+        FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic");
+        return FALSE;
+    }
+
+    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 TRUE;
+}
+
+/* instrument modulator loader */
+static int load_imod(SFData *sf, int size)
+{
+    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 -= SF_MOD_SIZE) < 0)
+                {
+                    FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch");
+                    return FALSE;
+                }
+
+                if((m = FLUID_NEW(SFMod)) == NULL)
+                {
+                    FLUID_LOG(FLUID_ERR, "Out of memory");
+                    return FALSE;
+                }
+                p3->data = m;
+                READW(sf, m->src);
+                READW(sf, m->dest);
+                READW(sf, m->amount);
+                READW(sf, m->amtsrc);
+                READW(sf, m->trans);
+                p3 = fluid_list_next(p3);
+            }
+
+            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 TRUE;
+    }
+
+    size -= SF_MOD_SIZE;
+
+    if(size != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch");
+        return FALSE;
+    }
+
+    FSKIP(sf, SF_MOD_SIZE); /* terminal mod */
+
+    return TRUE;
+}
+
+/* load instrument generators (see load_pgen for loading rules) */
+static int load_igen(SFData *sf, int size)
+{
+    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 -= SF_GEN_SIZE) < 0)
+                {
+                    FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch");
+                    return FALSE;
+                }
+
+                READW(sf, genid);
+
+                if(genid == Gen_KeyRange)
+                {
+                    /* nothing precedes */
+                    if(level == 0)
+                    {
+                        level = 1;
+                        READB(sf, genval.range.lo);
+                        READB(sf, genval.range.hi);
+                    }
+                    else
+                    {
+                        skip = TRUE;
+                    }
+                }
+                else if(genid == Gen_VelRange)
+                {
+                    /* only KeyRange precedes */
+                    if(level <= 1)
+                    {
+                        level = 2;
+                        READB(sf, genval.range.lo);
+                        READB(sf, genval.range.hi);
+                    }
+                    else
+                    {
+                        skip = TRUE;
+                    }
+                }
+                else if(genid == Gen_SampleId)
+                {
+                    /* sample is last gen */
+                    level = 3;
+                    READW(sf, genval.uword);
+                    ((SFZone *)(p2->data))->instsamp = FLUID_INT_TO_POINTER(genval.uword + 1);
+                    break; /* break out of generator loop */
+                }
+                else
+                {
+                    level = 2;
+
+                    if(valid_inst_genid(genid))
+                    {
+                        /* gen valid? */
+                        READW(sf, genval.sword);
+                        dup = find_gen_by_id(genid, z->gen);
+                    }
+                    else
+                    {
+                        skip = TRUE;
+                    }
+                }
+
+                if(!skip)
+                {
+                    if(!dup)
+                    {
+                        /* if gen ! dup alloc new */
+                        if((g = FLUID_NEW(SFGen)) == NULL)
+                        {
+                            FLUID_LOG(FLUID_ERR, "Out of memory");
+                            return FALSE;
+                        }
+                        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(sf);
+                }
+
+                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);
+                    *hz = fluid_list_remove(*hz, p2->data);
+                    delete_zone((SFZone *)fluid_list_get(p2));
+                }
+            }
+
+            while(p3)
+            {
+                /* Kill any zones following a sample */
+                discarded = TRUE;
+
+                if((size -= SF_GEN_SIZE) < 0)
+                {
+                    FLUID_LOG(FLUID_ERR, "Instrument generator chunk size mismatch");
+                    return FALSE;
+                }
+
+                FSKIP(sf, SF_GEN_SIZE);
+                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 TRUE;
+    }
+
+    size -= SF_GEN_SIZE;
+
+    if(size != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch");
+        return FALSE;
+    }
+
+    FSKIP(sf, SF_GEN_SIZE); /* terminal gen */
+
+    return TRUE;
+}
+
+/* sample header loader */
+static int load_shdr(SFData *sf, unsigned int size)
+{
+    unsigned int i;
+    SFSample *p;
+
+    if(size % SF_SHDR_SIZE || size == 0)  /* size is multiple of SHDR size? */
+    {
+        FLUID_LOG(FLUID_ERR, "Sample header has invalid size");
+        return FALSE;
+    }
+
+    size = size / SF_SHDR_SIZE - 1;
+
+    if(size == 0)
+    {
+        /* at least one sample + term record? */
+        FLUID_LOG(FLUID_WARN, "File contains no samples");
+        FSKIP(sf, SF_SHDR_SIZE);
+        return TRUE;
+    }
+
+    /* load all sample headers */
+    for(i = 0; i < size; i++)
+    {
+        if((p = FLUID_NEW(SFSample)) == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            return FALSE;
+        }
+        sf->sample = fluid_list_append(sf->sample, p);
+        READSTR(sf, &p->name);
+        READD(sf, p->start);
+        READD(sf, p->end);
+        READD(sf, p->loopstart);
+        READD(sf, p->loopend);
+        READD(sf, p->samplerate);
+        READB(sf, p->origpitch);
+        READB(sf, p->pitchadj);
+        FSKIPW(sf); /* skip sample link */
+        READW(sf, p->sampletype);
+        p->samfile = 0;
+    }
+
+    FSKIP(sf, SF_SHDR_SIZE); /* skip terminal shdr */
+
+    return TRUE;
+}
+
+/* "fixup" (inst # -> inst ptr) instrument references in preset list */
+static int fixup_pgen(SFData *sf)
+{
+    fluid_list_t *p, *p2, *p3;
+    SFZone *z;
+    int i;
+
+    p = sf->preset;
+
+    while(p)
+    {
+        p2 = ((SFPreset *)(p->data))->zone;
+
+        while(p2)
+        {
+            /* traverse this preset's zones */
+            z = (SFZone *)(p2->data);
+
+            if((i = FLUID_POINTER_TO_INT(z->instsamp)))
+            {
+                /* load instrument # */
+                p3 = fluid_list_nth(sf->inst, i - 1);
+
+                if(!p3)
+                {
+                    FLUID_LOG(FLUID_ERR, "Preset %03d %03d: Invalid instrument reference",
+                              ((SFPreset *)(p->data))->bank, ((SFPreset *)(p->data))->prenum);
+                    return FALSE;
+                }
+
+                z->instsamp = p3;
+            }
+            else
+            {
+                z->instsamp = NULL;
+            }
+
+            p2 = fluid_list_next(p2);
+        }
+
+        p = fluid_list_next(p);
+    }
+
+    return TRUE;
+}
+
+/* "fixup" (sample # -> sample ptr) sample references in instrument list */
+static int fixup_igen(SFData *sf)
+{
+    fluid_list_t *p, *p2, *p3;
+    SFZone *z;
+    int i;
+
+    p = sf->inst;
+
+    while(p)
+    {
+        p2 = ((SFInst *)(p->data))->zone;
+
+        while(p2)
+        {
+            /* traverse instrument's zones */
+            z = (SFZone *)(p2->data);
+
+            if((i = FLUID_POINTER_TO_INT(z->instsamp)))
+            {
+                /* load sample # */
+                p3 = fluid_list_nth(sf->sample, i - 1);
+
+                if(!p3)
+                {
+                    FLUID_LOG(FLUID_ERR, "Instrument '%s': Invalid sample reference",
+                              ((SFInst *)(p->data))->name);
+                    return FALSE;
+                }
+
+                z->instsamp = p3;
+            }
+
+            p2 = fluid_list_next(p2);
+        }
+
+        p = fluid_list_next(p);
+    }
+
+    return TRUE;
+}
+
+static void delete_preset(SFPreset *preset)
+{
+    fluid_list_t *entry;
+    SFZone *zone;
+
+    if(!preset)
+    {
+        return;
+    }
+
+    entry = preset->zone;
+
+    while(entry)
+    {
+        zone = (SFZone *)fluid_list_get(entry);
+        delete_zone(zone);
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(preset->zone);
+    
+    FLUID_FREE(preset);
+}
+
+static void delete_inst(SFInst *inst)
+{
+    fluid_list_t *entry;
+    SFZone *zone;
+
+    if(!inst)
+    {
+        return;
+    }
+
+    entry = inst->zone;
+
+    while(entry)
+    {
+        zone = (SFZone *)fluid_list_get(entry);
+        delete_zone(zone);
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(inst->zone);
+    
+    FLUID_FREE(inst);
+}
+
+
+/* Free all elements of a zone (Preset or Instrument) */
+static void delete_zone(SFZone *zone)
+{
+    fluid_list_t *entry;
+
+    if(!zone)
+    {
+        return;
+    }
+
+    entry = zone->gen;
+
+    while(entry)
+    {
+        FLUID_FREE(fluid_list_get(entry));
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(zone->gen);
+
+    entry = zone->mod;
+
+    while(entry)
+    {
+        FLUID_FREE(fluid_list_get(entry));
+        entry = fluid_list_next(entry);
+    }
+
+    delete_fluid_list(zone->mod);
+
+    FLUID_FREE(zone);
+}
+
+/* preset sort function, first by bank, then by preset # */
+static int 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);
+}
+
+/* Find a generator by its id in the passed in list.
+ *
+ * @return pointer to SFGen if found, otherwise NULL
+ */
+static fluid_list_t *find_gen_by_id(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 */
+static int valid_inst_genid(unsigned short genid)
+{
+    int i = 0;
+
+    if(genid > Gen_MaxValid)
+    {
+        return FALSE;
+    }
+
+    while(invalid_inst_gen[i] && invalid_inst_gen[i] != genid)
+    {
+        i++;
+    }
+
+    return (invalid_inst_gen[i] == 0);
+}
+
+/* check validity of preset generator */
+static int valid_preset_genid(unsigned short genid)
+{
+    int i = 0;
+
+    if(!valid_inst_genid(genid))
+    {
+        return FALSE;
+    }
+
+    while(invalid_preset_gen[i] && invalid_preset_gen[i] != genid)
+    {
+        i++;
+    }
+
+    return (invalid_preset_gen[i] == 0);
+}
+
+
+static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24)
+{
+    short *loaded_data = NULL;
+    char *loaded_data24 = NULL;
+
+    int num_samples = (end + 1) - start;
+    fluid_return_val_if_fail(num_samples > 0, -1);
+
+    if((start * sizeof(short) > sf->samplesize) || (end * sizeof(short) > sf->samplesize))
+    {
+        FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk");
+        goto error_exit;
+    }
+
+    /* Load 16-bit sample data */
+    if(sf->fcbs->fseek(sf->sffd, sf->samplepos + (start * sizeof(short)), SEEK_SET) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to seek to sample position");
+        goto error_exit;
+    }
+
+    loaded_data = FLUID_ARRAY(short, num_samples);
+
+    if(loaded_data == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        goto error_exit;
+    }
+
+    if(sf->fcbs->fread(loaded_data, num_samples * sizeof(short), sf->sffd) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to read sample data");
+        goto error_exit;
+    }
+
+    /* If this machine is big endian, byte swap the 16 bit samples */
+    if(FLUID_IS_BIG_ENDIAN)
+    {
+        int i;
+
+        for(i = 0; i < num_samples; i++)
+        {
+            loaded_data[i] = FLUID_LE16TOH(loaded_data[i]);
+        }
+    }
+
+    *data = loaded_data;
+
+    /* Optionally load additional 8 bit sample data for 24-bit support. Any failures while loading
+     * the 24-bit sample data will be logged as errors but won't prevent the sample reading to
+     * fail, as sound output is still possible with the 16-bit sample data. */
+    if(sf->sample24pos)
+    {
+        if((start > sf->sample24size) || (end > sf->sample24size))
+        {
+            FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit sample data chunk");
+            goto error24_exit;
+        }
+
+        if(sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED)
+        {
+            FLUID_LOG(FLUID_ERR, "Failed to seek position for 24-bit sample data in data file");
+            goto error24_exit;
+        }
+
+        loaded_data24 = FLUID_ARRAY(char, num_samples);
+
+        if(loaded_data24 == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory reading 24-bit sample data");
+            goto error24_exit;
+        }
+
+        if(sf->fcbs->fread(loaded_data24, num_samples, sf->sffd) == FLUID_FAILED)
+        {
+            FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data");
+            goto error24_exit;
+        }
+    }
+
+    *data24 = loaded_data24;
+
+    return num_samples;
+
+error24_exit:
+    FLUID_LOG(FLUID_WARN, "Ignoring 24-bit sample data, sound quality might suffer");
+    FLUID_FREE(loaded_data24);
+    *data24 = NULL;
+    return num_samples;
+
+error_exit:
+    FLUID_FREE(loaded_data);
+    FLUID_FREE(loaded_data24);
+    return -1;
+}
+
+
+/* Ogg Vorbis loading and decompression */
+#if LIBSNDFILE_SUPPORT
+
+/* Virtual file access rountines to allow loading individually compressed
+ * samples from the Soundfont sample data chunk using the file callbacks
+ * passing in during opening of the file */
+typedef struct _sfvio_data_t
+{
+    SFData *sffile;
+    sf_count_t start;  /* start byte offset of compressed data */
+    sf_count_t end;    /* end byte offset of compressed data */
+    sf_count_t offset; /* current virtual file offset from start byte offset */
+
+} sfvio_data_t;
+
+static sf_count_t sfvio_get_filelen(void *user_data)
+{
+    sfvio_data_t *data = user_data;
+
+    return (data->end + 1) - data->start;
+}
+
+static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data)
+{
+    sfvio_data_t *data = user_data;
+    SFData *sf = data->sffile;
+    sf_count_t new_offset;
+
+    switch(whence)
+    {
+    case SEEK_SET:
+        new_offset = offset;
+        break;
+
+    case SEEK_CUR:
+        new_offset = data->offset + offset;
+        break;
+
+    case SEEK_END:
+        new_offset = sfvio_get_filelen(user_data) + offset;
+        break;
+
+    default:
+        goto fail; /* proper error handling not possible?? */
+    }
+
+    if(sf->fcbs->fseek(sf->sffd, sf->samplepos + data->start + new_offset, SEEK_SET) != FLUID_FAILED)
+    {
+        data->offset = new_offset;
+    }
+
+fail:
+    return data->offset;
+}
+
+static sf_count_t sfvio_read(void *ptr, sf_count_t count, void *user_data)
+{
+    sfvio_data_t *data = user_data;
+    SFData *sf = data->sffile;
+    sf_count_t remain;
+
+    remain = sfvio_get_filelen(user_data) - data->offset;
+
+    if(count > remain)
+    {
+        count = remain;
+    }
+
+    if(count == 0)
+    {
+        return count;
+    }
+
+    if(sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data");
+        return 0;
+    }
+
+    data->offset += count;
+
+    return count;
+}
+
+static sf_count_t sfvio_tell(void *user_data)
+{
+    sfvio_data_t *data = user_data;
+
+    return data->offset;
+}
+
+/**
+ * Read Ogg Vorbis compressed data from the Soundfont and decompress it, returning the number of samples
+ * in the decompressed WAV. Only 16-bit mono samples are supported.
+ *
+ * Note that this function takes byte indices for start and end source data. The sample headers in SF3
+ * files use byte indices, so those pointers can be passed directly to this function.
+ *
+ * This function uses a virtual file structure in order to read the Ogg Vorbis
+ * data from arbitrary locations in the source file.
+ */
+static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data)
+{
+    SNDFILE *sndfile;
+    SF_INFO sfinfo;
+    SF_VIRTUAL_IO sfvio =
+    {
+        sfvio_get_filelen,
+        sfvio_seek,
+        sfvio_read,
+        NULL,
+        sfvio_tell
+    };
+    sfvio_data_t sfdata;
+    short *wav_data = NULL;
+
+    if((start_byte > sf->samplesize) || (end_byte > sf->samplesize))
+    {
+        FLUID_LOG(FLUID_ERR, "Ogg Vorbis data offsets exceed sample data chunk");
+        return -1;
+    }
+
+    // Initialize file position indicator and SF_INFO structure
+    sfdata.sffile = sf;
+    sfdata.start = start_byte;
+    sfdata.end = end_byte;
+    sfdata.offset = 0;
+
+    memset(&sfinfo, 0, sizeof(sfinfo));
+
+    /* Seek to beginning of Ogg Vorbis data in Soundfont */
+    if(sf->fcbs->fseek(sf->sffd, sf->samplepos + start_byte, SEEK_SET) == FLUID_FAILED)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to seek to compressd sample position");
+        return -1;
+    }
+
+    // Open sample as a virtual file
+    sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata);
+
+    if(!sndfile)
+    {
+        FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
+        return -1;
+    }
+
+    // Empty sample
+    if(!sfinfo.frames || !sfinfo.channels)
+    {
+        FLUID_LOG(FLUID_DBG, "Empty decompressed sample");
+        *data = NULL;
+        sf_close(sndfile);
+        return 0;
+    }
+
+    /* FIXME: ensure that the decompressed WAV data is 16-bit mono? */
+
+    wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels);
+
+    if(!wav_data)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        goto error_exit;
+    }
+
+    /* Automatically decompresses the Ogg Vorbis data to 16-bit WAV */
+    if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames)
+    {
+        FLUID_LOG(FLUID_DBG, "Decompression failed!");
+        FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
+        goto error_exit;
+    }
+
+    sf_close(sndfile);
+
+    *data = wav_data;
+
+    return sfinfo.frames;
+
+error_exit:
+    FLUID_FREE(wav_data);
+    sf_close(sndfile);
+    return -1;
+}
+#else
+static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data)
+{
+    return -1;
+}
+#endif
diff --git a/libs/fluidsynth/src/fluid_sffile.h b/libs/fluidsynth/src/fluid_sffile.h
new file mode 100644 (file)
index 0000000..0c315c7
--- /dev/null
@@ -0,0 +1,230 @@
+/* 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 Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_SFFILE_H
+#define _FLUID_SFFILE_H
+
+
+#include "fluid_gen.h"
+#include "fluid_list.h"
+#include "fluid_mod.h"
+#include "fluidsynth.h"
+#include "fluidsynth_priv.h"
+
+
+/* Sound Font structure defines */
+
+/* Forward declarations */
+typedef union _SFGenAmount SFGenAmount;
+typedef struct _SFVersion SFVersion;
+typedef struct _SFMod SFMod;
+typedef struct _SFGen SFGen;
+typedef struct _SFZone SFZone;
+typedef struct _SFSample SFSample;
+typedef struct _SFInst SFInst;
+typedef struct _SFPreset SFPreset;
+typedef struct _SFData SFData;
+typedef struct _SFChunk SFChunk;
+typedef struct _SFPhdr SFPhdr;
+typedef struct _SFBag SFBag;
+typedef struct _SFIhdr SFIhdr;
+typedef struct _SFShdr SFShdr;
+
+
+struct _SFVersion
+{
+    /* version structure */
+    unsigned short major;
+    unsigned short minor;
+};
+
+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 */
+};
+
+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;
+};
+
+struct _SFGen
+{
+    /* Generator structure */
+    unsigned short id; /* generator ID */
+    SFGenAmount amount; /* generator value */
+};
+
+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 */
+};
+
+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) */
+};
+
+struct _SFInst
+{
+    /* Instrument structure */
+    char name[21]; /* Name of instrument */
+    int idx; /* Index of this instrument in the Soundfont */
+    fluid_list_t *zone; /* list of instrument zones */
+};
+
+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 */
+};
+
+/* NOTE: sffd is also used to determine if sound font is new (NULL) */
+struct _SFData
+{
+    /* Sound font data structure */
+    SFVersion version; /* sound font version */
+    SFVersion romver; /* ROM version */
+
+    unsigned int filesize;
+
+    unsigned int samplepos; /* position within sffd of the sample chunk */
+    unsigned int samplesize; /* length within sffd of the sample chunk */
+
+    unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit
+                                 sample support */
+    unsigned int sample24size; /* length within sffd of the sm24 chunk */
+
+    unsigned int hydrapos;
+    unsigned int hydrasize;
+
+    char *fname; /* file name */
+    FILE *sffd; /* loaded sfont file descriptor */
+    const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */
+
+    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 */
+};
+
+/* functions */
+
+
+/*-----------------------------------sffile.h----------------------------*/
+/*
+   File structures and routines (used to be in sffile.h)
+*/
+
+/* sfont file data structures */
+struct _SFChunk
+{
+    /* RIFF file chunk structure */
+    unsigned int id; /* chunk id */
+    unsigned int size; /* size of the following chunk */
+};
+
+struct _SFPhdr
+{
+    unsigned char name[20]; /* preset name */
+    unsigned short preset; /* preset number */
+    unsigned short bank; /* bank number */
+    unsigned short pbagndx; /* index into preset bag */
+    unsigned int library; /* just for preserving them */
+    unsigned int genre; /* Not used */
+    unsigned int morphology; /* Not used */
+};
+
+struct _SFBag
+{
+    unsigned short genndx; /* index into generator list */
+    unsigned short modndx; /* index into modulator list */
+};
+
+struct _SFIhdr
+{
+    char name[20]; /* Name of instrument */
+    unsigned short ibagndx; /* Instrument bag index */
+};
+
+struct _SFShdr
+{
+    /* Sample header loading struct */
+    char name[20]; /* Sample name */
+    unsigned int start; /* Offset to start of sample */
+    unsigned int end; /* Offset to end of sample */
+    unsigned int loopstart; /* Offset to start of loop */
+    unsigned int loopend; /* Offset to end of loop */
+    unsigned int samplerate; /* Sample rate recorded at */
+    unsigned char origpitch; /* root midi key number */
+    signed char pitchadj; /* pitch correction in cents */
+    unsigned short samplelink; /* Not used */
+    unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */
+};
+
+/* Public functions  */
+SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs);
+void fluid_sffile_close(SFData *sf);
+int fluid_sffile_parse_presets(SFData *sf);
+int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end,
+                                  int sample_type, short **data, char **data24);
+
+#endif /* _FLUID_SFFILE_H */
diff --git a/libs/fluidsynth/src/fluid_sfont.c b/libs/fluidsynth/src/fluid_sfont.c
new file mode 100644 (file)
index 0000000..405bec6
--- /dev/null
@@ -0,0 +1,784 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003  Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_sfont.h"
+#include "fluid_sys.h"
+
+
+void *default_fopen(const char *path)
+{
+    return FLUID_FOPEN(path, "rb");
+}
+
+int default_fclose(void *handle)
+{
+    return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED;
+}
+
+long default_ftell(void *handle)
+{
+    return FLUID_FTELL((FILE *)handle);
+}
+
+int safe_fread(void *buf, int count, void *fd)
+{
+    if(FLUID_FREAD(buf, count, 1, (FILE *)fd) != 1)
+    {
+        if(feof((FILE *)fd))
+        {
+            FLUID_LOG(FLUID_ERR, "EOF while attemping to read %d bytes", count);
+        }
+        else
+        {
+            FLUID_LOG(FLUID_ERR, "File read failed");
+        }
+
+        return FLUID_FAILED;
+    }
+
+    return FLUID_OK;
+}
+
+int safe_fseek(void *fd, long ofs, int whence)
+{
+    if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "File seek failed with offset = %ld and whence = %d", ofs, whence);
+        return FLUID_FAILED;
+    }
+
+    return FLUID_OK;
+}
+
+/**
+ * Creates a new SoundFont loader.
+ *
+ * @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t).
+ * @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t).
+ * Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader().
+ *
+ * @return the SoundFont loader instance on success, NULL otherwise.
+ */
+fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free)
+{
+    fluid_sfloader_t *loader;
+
+    fluid_return_val_if_fail(load != NULL, NULL);
+    fluid_return_val_if_fail(free != NULL, NULL);
+
+    loader = FLUID_NEW(fluid_sfloader_t);
+
+    if(loader == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    FLUID_MEMSET(loader, 0, sizeof(*loader));
+
+    loader->load = load;
+    loader->free = free;
+    fluid_sfloader_set_callbacks(loader,
+                                 default_fopen,
+                                 safe_fread,
+                                 safe_fseek,
+                                 default_ftell,
+                                 default_fclose);
+
+    return loader;
+}
+
+/**
+ * Frees a SoundFont loader created with new_fluid_sfloader().
+ *
+ * @param loader The SoundFont loader instance to free.
+ */
+void delete_fluid_sfloader(fluid_sfloader_t *loader)
+{
+    fluid_return_if_fail(loader != NULL);
+
+    FLUID_FREE(loader);
+}
+
+/**
+ * Specify private data to be used by #fluid_sfloader_load_t.
+ *
+ * @param loader The SoundFont loader instance.
+ * @param data The private data to store.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise.
+ */
+int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data)
+{
+    fluid_return_val_if_fail(loader != NULL, FLUID_FAILED);
+
+    loader->data = data;
+    return FLUID_OK;
+}
+
+/**
+ * Obtain private data previously set with fluid_sfloader_set_data().
+ *
+ * @param loader The SoundFont loader instance.
+ * @return The private data or NULL if none explicitly set before.
+ */
+void *fluid_sfloader_get_data(fluid_sfloader_t *loader)
+{
+    fluid_return_val_if_fail(loader != NULL, NULL);
+
+    return loader->data;
+}
+
+/**
+ * Set custom callbacks to be used upon soundfont loading.
+ *
+ * Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example.
+ *
+ * @param loader The SoundFont loader instance.
+ * @param open A function implementing #fluid_sfloader_callback_open_t.
+ * @param read A function implementing #fluid_sfloader_callback_read_t.
+ * @param seek A function implementing #fluid_sfloader_callback_seek_t.
+ * @param tell A function implementing #fluid_sfloader_callback_tell_t.
+ * @param close A function implementing #fluid_sfloader_callback_close_t.
+ * @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise.
+ */
+int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader,
+                                 fluid_sfloader_callback_open_t open,
+                                 fluid_sfloader_callback_read_t read,
+                                 fluid_sfloader_callback_seek_t seek,
+                                 fluid_sfloader_callback_tell_t tell,
+                                 fluid_sfloader_callback_close_t close)
+{
+    fluid_file_callbacks_t *cb;
+
+    fluid_return_val_if_fail(loader != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(open != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(read != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(seek != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(tell != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(close != NULL, FLUID_FAILED);
+
+    cb = &loader->file_callbacks;
+
+    cb->fopen = open;
+    cb->fread = read;
+    cb->fseek = seek;
+    cb->ftell = tell;
+    cb->fclose = close;
+
+    return FLUID_OK;
+}
+
+/**
+ * Creates a new virtual SoundFont instance structure.
+ * @param get_name A function implementing #fluid_sfont_get_name_t.
+ * @param get_preset A function implementing #fluid_sfont_get_preset_t.
+ * @param iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed.
+ * @param iter_next A function implementing #fluid_sfont_iteration_next_t, or NULL if preset iteration not needed.
+ * @param free A function implementing #fluid_sfont_free_t.
+ * @return The soundfont instance on success or NULL otherwise.
+ */
+fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name,
+                               fluid_sfont_get_preset_t get_preset,
+                               fluid_sfont_iteration_start_t iter_start,
+                               fluid_sfont_iteration_next_t iter_next,
+                               fluid_sfont_free_t free)
+{
+    fluid_sfont_t *sfont;
+
+    fluid_return_val_if_fail(get_name != NULL, NULL);
+    fluid_return_val_if_fail(get_preset != NULL, NULL);
+    fluid_return_val_if_fail(free != NULL, NULL);
+
+    sfont = FLUID_NEW(fluid_sfont_t);
+
+    if(sfont == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    FLUID_MEMSET(sfont, 0, sizeof(*sfont));
+
+    sfont->get_name = get_name;
+    sfont->get_preset = get_preset;
+    sfont->iteration_start = iter_start;
+    sfont->iteration_next = iter_next;
+    sfont->free = free;
+
+    return sfont;
+}
+
+/**
+ * Set private data to use with a SoundFont instance.
+ *
+ * @param sfont The SoundFont instance.
+ * @param data The private data to store.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise.
+ */
+int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data)
+{
+    fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);
+
+    sfont->data = data;
+    return FLUID_OK;
+}
+
+/**
+ * Retrieve the private data of a SoundFont instance.
+ *
+ * @param sfont The SoundFont instance.
+ * @return The private data or NULL if none explicitly set before.
+ */
+void *fluid_sfont_get_data(fluid_sfont_t *sfont)
+{
+    fluid_return_val_if_fail(sfont != NULL, NULL);
+
+    return sfont->data;
+}
+
+/**
+ * Retrieve the unique ID of a SoundFont instance.
+ *
+ * @param sfont The SoundFont instance.
+ * @return The SoundFont ID.
+ */
+int fluid_sfont_get_id(fluid_sfont_t *sfont)
+{
+    return sfont->id;
+}
+
+/**
+ * Retrieve the name of a SoundFont instance.
+ *
+ * @param sfont The SoundFont instance.
+ * @return The name of the SoundFont.
+ */
+const char *fluid_sfont_get_name(fluid_sfont_t *sfont)
+{
+    return sfont->get_name(sfont);
+}
+
+/**
+ * Retrieve the preset assigned the a SoundFont instance
+ * for the given bank and preset number.
+ * @param sfont The SoundFont instance.
+ * @param bank bank number of the preset
+ * @param prenum program number of the preset
+ * @return The preset instance or NULL if none found.
+ */
+fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum)
+{
+    return sfont->get_preset(sfont, bank, prenum);
+}
+
+
+/**
+ * Starts / re-starts virtual preset iteration in a SoundFont.
+ * @param sfont Virtual SoundFont instance
+ */
+void fluid_sfont_iteration_start(fluid_sfont_t *sfont)
+{
+    fluid_return_if_fail(sfont != NULL);
+    fluid_return_if_fail(sfont->iteration_start != NULL);
+
+    sfont->iteration_start(sfont);
+}
+
+/**
+ * Virtual SoundFont preset iteration function.
+ *
+ * Returns preset information to the caller and advances the
+ * internal iteration state to the next preset for subsequent calls.
+ * @param sfont The SoundFont instance.
+ * @return NULL when no more presets are available, otherwise the a pointer to the current preset
+ */
+fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont)
+{
+    fluid_return_val_if_fail(sfont != NULL, NULL);
+    fluid_return_val_if_fail(sfont->iteration_next != NULL, NULL);
+
+    return sfont->iteration_next(sfont);
+}
+
+/**
+ * Destroys a SoundFont instance created with new_fluid_sfont().
+ *
+ * Implements #fluid_sfont_free_t.
+ *
+ * @param sfont The SoundFont instance to destroy.
+ * @return Always returns 0.
+ */
+int delete_fluid_sfont(fluid_sfont_t *sfont)
+{
+    fluid_return_val_if_fail(sfont != NULL, 0);
+
+    FLUID_FREE(sfont);
+    return 0;
+}
+
+/**
+ * Create a virtual SoundFont preset instance.
+ *
+ * @param parent_sfont The SoundFont instance this preset shall belong to
+ * @param get_name A function implementing #fluid_preset_get_name_t
+ * @param get_bank A function implementing #fluid_preset_get_banknum_t
+ * @param get_num A function implementing #fluid_preset_get_num_t
+ * @param noteon A function implementing #fluid_preset_noteon_t
+ * @param free A function implementing #fluid_preset_free_t
+ * @return The preset instance on success, NULL otherwise.
+ */
+fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont,
+                                 fluid_preset_get_name_t get_name,
+                                 fluid_preset_get_banknum_t get_bank,
+                                 fluid_preset_get_num_t get_num,
+                                 fluid_preset_noteon_t noteon,
+                                 fluid_preset_free_t free)
+{
+    fluid_preset_t *preset;
+
+    fluid_return_val_if_fail(parent_sfont != NULL, NULL);
+    fluid_return_val_if_fail(get_name != NULL, NULL);
+    fluid_return_val_if_fail(get_bank != NULL, NULL);
+    fluid_return_val_if_fail(get_num != NULL, NULL);
+    fluid_return_val_if_fail(noteon != NULL, NULL);
+    fluid_return_val_if_fail(free != NULL, NULL);
+
+    preset = FLUID_NEW(fluid_preset_t);
+
+    if(preset == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    FLUID_MEMSET(preset, 0, sizeof(*preset));
+
+    preset->sfont = parent_sfont;
+    preset->get_name = get_name;
+    preset->get_banknum = get_bank;
+    preset->get_num = get_num;
+    preset->noteon = noteon;
+    preset->free = free;
+
+    return preset;
+}
+
+/**
+ * Set private data to use with a SoundFont preset instance.
+ *
+ * @param preset The SoundFont preset instance.
+ * @param data The private data to store.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise.
+ */
+int fluid_preset_set_data(fluid_preset_t *preset, void *data)
+{
+    fluid_return_val_if_fail(preset != NULL, FLUID_FAILED);
+
+    preset->data = data;
+    return FLUID_OK;
+}
+
+/**
+ * Retrieve the private data of a SoundFont preset instance.
+ *
+ * @param preset The SoundFont preset instance.
+ * @return The private data or NULL if none explicitly set before.
+ */
+void *fluid_preset_get_data(fluid_preset_t *preset)
+{
+    fluid_return_val_if_fail(preset != NULL, NULL);
+
+    return preset->data;
+}
+
+/**
+ * Retrieves the presets name by executing the \p get_name function
+ * provided on its creation.
+ *
+ * @param preset The SoundFont preset instance.
+ * @return Pointer to a NULL-terminated string containing the presets name.
+ */
+const char *fluid_preset_get_name(fluid_preset_t *preset)
+{
+    return preset->get_name(preset);
+}
+
+/**
+ * Retrieves the presets bank number by executing the \p get_bank function
+ * provided on its creation.
+ *
+ * @param preset The SoundFont preset instance.
+ * @return The bank number of \p preset.
+ */
+int fluid_preset_get_banknum(fluid_preset_t *preset)
+{
+    return preset->get_banknum(preset);
+}
+
+/**
+ * Retrieves the presets (instrument) number by executing the \p get_num function
+ * provided on its creation.
+ *
+ * @param preset The SoundFont preset instance.
+ * @return The number of \p preset.
+ */
+int fluid_preset_get_num(fluid_preset_t *preset)
+{
+    return preset->get_num(preset);
+}
+
+/**
+ * Retrieves the presets parent SoundFont instance.
+ *
+ * @param preset The SoundFont preset instance.
+ * @return The parent SoundFont of \p preset.
+ */
+fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset)
+{
+    return preset->sfont;
+}
+
+/**
+ * Destroys a SoundFont preset instance created with new_fluid_preset().
+ *
+ * Implements #fluid_preset_free_t.
+ *
+ * @param preset The SoundFont preset instance to destroy.
+ */
+void delete_fluid_preset(fluid_preset_t *preset)
+{
+    fluid_return_if_fail(preset != NULL);
+
+    FLUID_FREE(preset);
+}
+
+/**
+ * Create a new sample instance.
+ * @return  The sample on success, NULL otherwise.
+ */
+fluid_sample_t *
+new_fluid_sample()
+{
+    fluid_sample_t *sample = NULL;
+
+    sample = FLUID_NEW(fluid_sample_t);
+
+    if(sample == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    FLUID_MEMSET(sample, 0, sizeof(*sample));
+
+    return sample;
+}
+
+/**
+ * Destroy a sample instance previously created with new_fluid_sample().
+ * @param sample The sample to destroy.
+ */
+void
+delete_fluid_sample(fluid_sample_t *sample)
+{
+    fluid_return_if_fail(sample != NULL);
+
+    if(sample->auto_free)
+    {
+        FLUID_FREE(sample->data);
+        FLUID_FREE(sample->data24);
+    }
+
+    FLUID_FREE(sample);
+}
+
+/**
+ * Returns the size of the fluid_sample_t structure.
+ *
+ * Useful in low latency scenarios e.g. to allocate a sample on the stack.
+ *
+ * @return Size of fluid_sample_t in bytes
+ */
+size_t fluid_sample_sizeof()
+{
+    return sizeof(fluid_sample_t);
+}
+
+/**
+ * Set the name of a SoundFont sample.
+ * @param sample SoundFont sample
+ * @param name Name to assign to sample (20 chars in length + zero terminator)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_sample_set_name(fluid_sample_t *sample, const char *name)
+{
+    fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
+
+    FLUID_STRNCPY(sample->name, name, sizeof(sample->name));
+    return FLUID_OK;
+}
+
+/**
+ * Assign sample data to a SoundFont sample.
+ * @param sample SoundFont sample
+ * @param data Buffer containing 16 bit (mono-)audio sample data
+ * @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples
+ * @param nbframes Number of samples in \a data
+ * @param sample_rate Sampling rate of the sample data
+ * @param copy_data TRUE to copy the sample data (and automatically free it upon delete_fluid_sample()), FALSE to use it directly (and not free it)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ *
+ * @note If \a copy_data is FALSE, data should have 8 unused frames at start
+ * and 8 unused frames at the end and \a nbframes should be >=48
+ */
+int
+fluid_sample_set_sound_data(fluid_sample_t *sample,
+                            short *data,
+                            char *data24,
+                            unsigned int nbframes,
+                            unsigned int sample_rate,
+                            short copy_data
+                           )
+{
+    /* the number of samples before the start and after the end */
+#define SAMPLE_LOOP_MARGIN 8U
+
+    fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(data != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(nbframes == 0, FLUID_FAILED);
+
+    /* in case we already have some data */
+    if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free)
+    {
+        FLUID_FREE(sample->data);
+        FLUID_FREE(sample->data24);
+        sample->data = NULL;
+        sample->data24 = NULL;
+    }
+
+    if(copy_data)
+    {
+        unsigned int storedNbFrames;
+
+        /* nbframes should be >= 48 (SoundFont specs) */
+        storedNbFrames = nbframes;
+
+        if(storedNbFrames < 48)
+        {
+            storedNbFrames = 48;
+        }
+
+        storedNbFrames += 2 * SAMPLE_LOOP_MARGIN;
+
+        sample->data = FLUID_ARRAY(short, storedNbFrames);
+
+        if(sample->data == NULL)
+        {
+            goto error_rec;
+        }
+
+        FLUID_MEMSET(sample->data, 0, storedNbFrames);
+        FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short));
+
+        if(data24 != NULL)
+        {
+            sample->data24 = FLUID_ARRAY(char, storedNbFrames);
+
+            if(sample->data24 == NULL)
+            {
+                goto error_rec;
+            }
+
+            FLUID_MEMSET(sample->data24, 0, storedNbFrames);
+            FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes * sizeof(char));
+        }
+
+        /* pointers */
+        /* all from the start of data */
+        sample->start = SAMPLE_LOOP_MARGIN;
+        sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames - 1;
+    }
+    else
+    {
+        /* we cannot assure the SAMPLE_LOOP_MARGIN */
+        sample->data = data;
+        sample->data24 = data24;
+        sample->start = 0;
+        sample->end = nbframes - 1;
+    }
+
+    sample->samplerate = sample_rate;
+    sample->sampletype = FLUID_SAMPLETYPE_MONO;
+    sample->auto_free = copy_data;
+
+    return FLUID_OK;
+
+error_rec:
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    FLUID_FREE(sample->data);
+    FLUID_FREE(sample->data24);
+    return FLUID_FAILED;
+
+#undef SAMPLE_LOOP_MARGIN
+}
+
+/**
+ * Set the loop of a sample.
+ *
+ * @param sample SoundFont sample
+ * @param loop_start Start sample index of the loop.
+ * @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played).
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise.
+ */
+int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end)
+{
+    fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
+
+    sample->loopstart = loop_start;
+    sample->loopend = loop_end;
+
+    return FLUID_OK;
+}
+
+/**
+ * Set the pitch of a sample.
+ *
+ * @param sample SoundFont sample
+ * @param root_key Root MIDI note of sample (0-127)
+ * @param fine_tune Fine tune in cents
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise.
+ */
+int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune)
+{
+    fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(0 <= root_key && root_key <= 127, FLUID_FAILED);
+
+    sample->origpitch = root_key;
+    sample->pitchadj = fine_tune;
+
+    return FLUID_OK;
+}
+
+
+/**
+ * Validate parameters of a sample
+ *
+ */
+int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size)
+{
+    /* ROM samples are unusable for us by definition */
+    if(sample->sampletype & FLUID_SAMPLETYPE_ROM)
+    {
+        FLUID_LOG(FLUID_WARN, "Sample '%s': ROM sample ignored", sample->name);
+        return FLUID_FAILED;
+    }
+
+    /* Ogg vorbis compressed samples in the SF3 format use byte indices for
+     * sample start and end pointers before decompression. Standard SF2 samples
+     * use sample word indices for all pointers, so use half the buffer_size
+     * for validation. */
+    if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
+    {
+        if(buffer_size % 2)
+        {
+            FLUID_LOG(FLUID_WARN, "Sample '%s': invalid buffer size", sample->name);
+            return FLUID_FAILED;
+        }
+
+        buffer_size /= 2;
+    }
+
+    if((sample->end > buffer_size) || (sample->start >= sample->end))
+    {
+        FLUID_LOG(FLUID_WARN, "Sample '%s': invalid start/end file positions", sample->name);
+        return FLUID_FAILED;
+    }
+
+    return FLUID_OK;
+}
+
+/* Check the sample loop pointers and optionally convert them to something
+ * usable in case they are broken. Return a boolean indicating if the pointers
+ * have been modified, so the user can be notified of possible audio glitches.
+ */
+int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)
+{
+    int modified = FALSE;
+    unsigned int max_end = buffer_size / 2;
+    /* In fluid_sample_t the sample end pointer points to the last sample, not
+     * to the data word after the last sample. FIXME: why? */
+    unsigned int sample_end = sample->end + 1;
+
+    if(sample->loopstart == sample->loopend)
+    {
+        /* Some SoundFonts disable loops by setting loopstart = loopend. While
+         * technically invalid, we decided to accept those samples anyway. Just
+         * ensure that those two pointers are within the sampledata by setting
+         * them to 0. Don't report the modification, as this change has no audible
+         * effect. */
+        sample->loopstart = sample->loopend = 0;
+        return FALSE;
+    }
+    else if(sample->loopstart > sample->loopend)
+    {
+        unsigned int tmp;
+
+        /* If loop start and end are reversed, try to swap them around and
+         * continue validation */
+        FLUID_LOG(FLUID_DBG, "Sample '%s': reversed loop pointers '%d' - '%d', trying to fix",
+                  sample->name, sample->loopstart, sample->loopend);
+        tmp = sample->loopstart;
+        sample->loopstart = sample->loopend;
+        sample->loopend = tmp;
+        modified = TRUE;
+    }
+
+    /* The SoundFont 2.4 spec defines the loopstart index as the first sample
+     * point of the loop while loopend is the first point AFTER the last sample
+     * of the loop. However we cannot be sure whether any of loopend or end is
+     * correct. Hours of thinking through this have concluded that it would be
+     * best practice to mangle with loops as little as necessary by only making
+     * sure the pointers are within sample->start to max_end. Incorrect
+     * soundfont shall preferably fail loudly. */
+    if((sample->loopstart < sample->start) || (sample->loopstart > max_end))
+    {
+        FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop start '%d', setting to sample start '%d'",
+                  sample->name, sample->loopstart, sample->start);
+        sample->loopstart = sample->start;
+        modified = TRUE;
+    }
+
+    if((sample->loopend < sample->start) || (sample->loopend > max_end))
+    {
+        FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop end '%d', setting to sample end '%d'",
+                  sample->name, sample->loopend, sample_end);
+        sample->loopend = sample_end;
+        modified = TRUE;
+    }
+
+    if((sample->loopstart > sample_end) || (sample->loopend > sample_end))
+    {
+        FLUID_LOG(FLUID_DBG, "Sample '%s': loop range '%d - %d' after sample end '%d', using it anyway",
+                  sample->name, sample->loopstart, sample->loopend, sample_end);
+    }
+
+    return modified;
+}
index 51e941e4401498aa029c34c0fc3dc2a0175e9595..24773353df61ef8ca2131a4975ac67dbec11ee3a 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #ifndef _PRIV_FLUID_SFONT_H
 #define _PRIV_FLUID_SFONT_H
 
+#include "fluidsynth.h"
+
+int fluid_sample_validate(fluid_sample_t *sample, unsigned int max_end);
+int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end);
 
 /*
  * Utility macros to access soundfonts, presets, and samples
 #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 fluid_sfont_delete_internal(_sf)   ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0)
 
 
-#define delete_fluid_preset(_preset) \
+#define fluid_preset_delete_internal(_preset) \
   { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }}
 
-#define fluid_preset_get_data(_preset) (_preset)->data
-#define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); }
-#define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset)
-#define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset)
-#define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset)
-
 #define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \
   (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel)
 
 
 
 
+/**
+ * File callback structure to enable custom soundfont loading (e.g. from memory).
+ */
+struct _fluid_file_callbacks_t
+{
+    fluid_sfloader_callback_open_t  fopen;
+    fluid_sfloader_callback_read_t  fread;
+    fluid_sfloader_callback_seek_t  fseek;
+    fluid_sfloader_callback_close_t fclose;
+    fluid_sfloader_callback_tell_t  ftell;
+};
+
+/**
+ * SoundFont loader structure.
+ */
+struct _fluid_sfloader_t
+{
+    void *data;           /**< User defined data pointer used by _fluid_sfloader_t::load() */
+
+    /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */
+    fluid_file_callbacks_t file_callbacks;
+
+    fluid_sfloader_free_t free;
+
+    fluid_sfloader_load_t load;
+};
+
+/**
+ * Virtual SoundFont instance structure.
+ */
+struct _fluid_sfont_t
+{
+    void *data;           /**< User defined data */
+    int id;               /**< SoundFont ID */
+    int refcount;         /**< SoundFont reference count (1 if no presets referencing it) */
+    int bankofs;          /**< Bank offset */
+
+    fluid_sfont_free_t free;
+
+    fluid_sfont_get_name_t get_name;
+
+    fluid_sfont_get_preset_t get_preset;
+
+    fluid_sfont_iteration_start_t iteration_start;
+
+    fluid_sfont_iteration_next_t iteration_next;
+};
+
+/**
+ * Virtual SoundFont preset.
+ */
+struct _fluid_preset_t
+{
+    void *data;                                   /**< User supplied data */
+    fluid_sfont_t *sfont;                         /**< Parent virtual SoundFont */
+
+    fluid_preset_free_t free;
+
+    fluid_preset_get_name_t get_name;
+
+    fluid_preset_get_banknum_t get_banknum;
+
+    fluid_preset_get_num_t get_num;
+
+    fluid_preset_noteon_t noteon;
+
+    /**
+     * Virtual SoundFont preset notify method.
+     * @param preset Virtual SoundFont preset
+     * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED
+     * @param chan MIDI channel number
+     * @return Should return #FLUID_OK
+     *
+     * Implement this optional method if the preset needs to be notified about
+     * preset select and unselect events.
+     *
+     * This method may be called from within synthesis context and therefore
+     * should be as efficient as possible and not perform any operations considered
+     * bad for realtime audio output (memory allocations and other OS calls).
+     */
+    int (*notify)(fluid_preset_t *preset, int reason, int chan);
+};
+
+/**
+ * Virtual SoundFont sample.
+ */
+struct _fluid_sample_t
+{
+    char name[21];                /**< Sample name */
+
+    /* The following for sample pointers store the original pointers from the Soundfont
+     * file. They are never changed after loading and are used to re-create the
+     * actual sample pointers after a sample has been unloaded and loaded again. The
+     * actual sample pointers get modified during loading for SF3 (compressed) samples
+     * and individually loaded SF2 samples. */
+    unsigned int source_start;
+    unsigned int source_end;
+    unsigned int source_loopstart;
+    unsigned int source_loopend;
+
+    unsigned int start;           /**< Start index */
+    unsigned int end;          /**< End index, index of last valid sample point (contrary to SF spec) */
+    unsigned int loopstart;       /**< Loop start index */
+    unsigned int loopend;         /**< Loop end index, first point following the loop (superimposed on loopstart) */
+
+    unsigned int samplerate;      /**< Sample rate */
+    int origpitch;                /**< Original pitch (MIDI note number, 0-127) */
+    int pitchadj;                 /**< Fine pitch adjustment (+/- 99 cents) */
+    int sampletype;               /**< Specifies the type of this sample as indicated by the #fluid_sample_type enum */
+    int auto_free;                /**< TRUE if _fluid_sample_t::data and _fluid_sample_t::data24 should be freed upon sample destruction */
+    short *data;                  /**< Pointer to the sample's 16 bit PCM data */
+    char *data24;                 /**< If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples */
+
+    int amplitude_that_reaches_noise_floor_is_valid;      /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */
+    double amplitude_that_reaches_noise_floor;            /**< The amplitude at which the sample's loop will be below the noise floor.  For voice off optimization, calculated automatically. */
+
+    unsigned int refcount;        /**< Count of voices using this sample */
+    int preset_count;             /**< Count of selected presets using this sample (used for dynamic sample loading) */
+
+    /**
+     * Implement this function to receive notification when sample is no longer used.
+     * @param sample Virtual SoundFont sample
+     * @param reason #FLUID_SAMPLE_DONE only currently
+     * @return Should return #FLUID_OK
+     */
+    int (*notify)(fluid_sample_t *sample, int reason);
+
+    void *userdata;       /**< User defined data */
+};
+
+
 #endif /* _PRIV_FLUID_SFONT_H */
index 8a30e250ba3953d2d6ac6fdc84884af0f8d17c11..e8845632f1bb1fb0a0a912c66b865537e7d6ddd2 100644 (file)
@@ -3,30 +3,27 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
  */
 
-#include <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
 #include <fenv.h>
 
 /* seems to not be declared in fenv.h */
-extern int feenableexcept (int excepts);
+extern int feenableexcept(int excepts);
 #endif
 
-static void fluid_synth_init(void);
+#define FLUID_API_RETURN(return_value) \
+  do { fluid_synth_api_exit(synth); \
+  return return_value; } while (0)
 
-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);
+#define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \
+  do { if (FLUID_LIKELY(synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)) \
+       {} \
+       else \
+       { FLUID_API_RETURN(return_value); } \
+  } while (0)
 
+#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_API_RETURN(fail_value); \
+  } \
 
+static void fluid_synth_init(void);
+static void fluid_synth_api_enter(fluid_synth_t *synth);
+static void fluid_synth_api_exit(fluid_synth_t *synth);
+
+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_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 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_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key);
+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, int sfontnum,
+                       int banknum, int prognum);
+static fluid_preset_t *
+fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname,
+                                     int banknum, int prognum);
+
+static void fluid_synth_update_presets(fluid_synth_t *synth);
+static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth);
+static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony);
+static void init_dither(void);
+static FLUID_INLINE int roundi(float x);
+static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount);
+
+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 int fluid_synth_sfunload_callback(void *data, unsigned int msec);
+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);
+
+
+static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels);
+
+
+/* Callback handlers for real-time settings */
+static void fluid_synth_handle_sample_rate(void *data, const char *name, double value);
+static void fluid_synth_handle_gain(void *data, const char *name, double value);
+static void fluid_synth_handle_polyphony(void *data, const char *name, int value);
+static void fluid_synth_handle_device_id(void *data, const char *name, int value);
+static void fluid_synth_handle_overflow(void *data, const char *name, double value);
+static void fluid_synth_handle_important_channels(void *data, const char *name,
+        const char *value);
+static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value);
+static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value);
+
+
+static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan);
+static int fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val);
+static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val);
+static int fluid_synth_set_reverb_full_LOCAL(fluid_synth_t *synth, int set, double roomsize,
+        double damping, double width, double level);
+
+static int fluid_synth_set_chorus_full_LOCAL(fluid_synth_t *synth, int set, int nr, double level,
+        double speed, double depth_ms, int type);
 
 /***************************************************************
  *
@@ -117,7 +145,8 @@ static void fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id);
  */
 
 /* has the synth module been initialized? */
-static int fluid_synth_initialized = 0;
+/* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */
+static fluid_atomic_int_t fluid_synth_initialized = {0};
 static void fluid_synth_init(void);
 static void init_dither(void);
 
@@ -128,26 +157,34 @@ static void init_dither(void);
  * 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 */
+static fluid_mod_t default_vel2att_mod;        /* SF2.01 section 8.4.1  */
+/*not static */ fluid_mod_t default_vel2filter_mod;     /* SF2.01 section 8.4.2  */
+static fluid_mod_t default_at2viblfo_mod;      /* SF2.01 section 8.4.3  */
+static fluid_mod_t default_mod2viblfo_mod;     /* SF2.01 section 8.4.4  */
+static fluid_mod_t default_att_mod;            /* SF2.01 section 8.4.5  */
+static fluid_mod_t default_pan_mod;            /* SF2.01 section 8.4.6  */
+static fluid_mod_t default_expr_mod;           /* SF2.01 section 8.4.7  */
+static fluid_mod_t default_reverb_mod;         /* SF2.01 section 8.4.8  */
+static fluid_mod_t default_chorus_mod;         /* SF2.01 section 8.4.9  */
+static fluid_mod_t default_pitch_bend_mod;     /* SF2.01 section 8.4.10 */
+static fluid_mod_t custom_balance_mod;         /* Non-standard modulator */
+
+
+/* custom_breath2att_modulator is not a default modulator specified in SF
+it is intended to replace default_vel2att_mod on demand using
+API fluid_set_breath_mode() or command shell setbreathmode.
+*/
+static fluid_mod_t custom_breath2att_mod;
 
 /* 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 }
+static const 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 },
 };
 
 
@@ -156,76 +193,60 @@ static fluid_revmodel_presets_t revmodel_preset[] = {
  *               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");
-  
+void fluid_synth_settings(fluid_settings_t *settings)
+{
+    fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, FLUID_HINT_TOGGLED);
+
+    fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, FLUID_HINT_TOGGLED);
+    fluid_settings_register_num(settings, "synth.reverb.room-size", FLUID_REVERB_DEFAULT_ROOMSIZE, 0.0f, 1.0f, 0);
+    fluid_settings_register_num(settings, "synth.reverb.damp", FLUID_REVERB_DEFAULT_DAMP, 0.0f, 1.0f, 0);
+    fluid_settings_register_num(settings, "synth.reverb.width", FLUID_REVERB_DEFAULT_WIDTH, 0.0f, 100.0f, 0);
+    fluid_settings_register_num(settings, "synth.reverb.level", FLUID_REVERB_DEFAULT_LEVEL, 0.0f, 1.0f, 0);
+
+    fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED);
+    fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0);
+    fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0);
+    fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.29f, 5.0f, 0);
+    fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0f, 256.0f, 0);
+
+    fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED);
+    fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, FLUID_HINT_TOGGLED);
+    fluid_settings_register_str(settings, "midi.portname", "", 0);
+
+#ifdef DEFAULT_SOUNDFONT
+    fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0);
+#endif
+
+    fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0);
+    fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0);
+    fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0);
+    fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0);
+    fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0);
+    fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0);
+    fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0);
+    fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0);
+    fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0);
+    fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0);
+
+    fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0);
+
+    fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED);
+
+    fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0);
+    fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0);
+    fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0);
+    fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0);
+    fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0);
+    fluid_settings_register_num(settings, "synth.overflow.important", 5000, -50000, 50000, 0);
+    fluid_settings_register_str(settings, "synth.overflow.important-channels", "", 0);
+
+    fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0);
+    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");
+
+    fluid_settings_register_int(settings, "synth.dynamic-sample-loading", 0, 0, 1, FLUID_HINT_TOGGLED);
 }
 
 /**
@@ -236,9 +257,9 @@ void fluid_synth_settings(fluid_settings_t* settings)
  */
 void fluid_version(int *major, int *minor, int *micro)
 {
-  *major = FLUIDSYNTH_VERSION_MAJOR;
-  *minor = FLUIDSYNTH_VERSION_MINOR;
-  *micro = FLUIDSYNTH_VERSION_MICRO;
+    *major = FLUIDSYNTH_VERSION_MAJOR;
+    *minor = FLUIDSYNTH_VERSION_MINOR;
+    *micro = FLUIDSYNTH_VERSION_MICRO;
 }
 
 /**
@@ -246,25 +267,12 @@ void fluid_version(int *major, int *minor, int *micro)
  * @return FluidSynth version string, which is internal and should not be
  *   modified or freed.
  */
-char *
-fluid_version_str (void)
+const char *
+fluid_version_str(void)
 {
-  return FLUIDSYNTH_VERSION;
+    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
  *
@@ -273,253 +281,286 @@ fluid_version_str (void)
 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);
+    /* 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)
+    fluid_conversion_config();
+
+    fluid_rvoice_dsp_config();
+
+    init_dither();
+
+    /* custom_breath2att_mod is not a default modulator specified in SF2.01.
+     it is intended to replace default_vel2att_mod on demand using
+     API fluid_set_breath_mode() or command shell setbreathmode.
+     */
+    fluid_mod_set_source1(&custom_breath2att_mod, /* The modulator we are programming here */
+                          BREATH_MSB,    /* Source. breath MSB corresponds to 2. */
+                          FLUID_MOD_CC           /* 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(&custom_breath2att_mod, 0, 0); /* No 2nd source */
+    fluid_mod_set_dest(&custom_breath2att_mod, GEN_ATTENUATION);  /* Target: Initial attenuation */
+    fluid_mod_set_amount(&custom_breath2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */
+
+    /* 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, FLUID_PEAK_ATTENUATION); /* 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, MODULATION_MSB, /* 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, VOLUME_MSB,    /* 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, FLUID_PEAK_ATTENUATION);  /* Amount: 960 */
+
+
+
+    /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */
+    fluid_mod_set_source1(&default_pan_mod, PAN_MSB,       /* 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, EXPRESSION_MSB, /* 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, FLUID_PEAK_ATTENUATION);  /* Amount: 960 */
+
+
+
+    /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */
+    fluid_mod_set_source1(&default_reverb_mod, EFFECTS_DEPTH1, /* 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, EFFECTS_DEPTH3, /* 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 */
+
+
+    /* Non-standard MIDI continuous controller 8 to channel stereo balance */
+    fluid_mod_set_source1(&custom_balance_mod, BALANCE_MSB, /* Index=8 */
+                          FLUID_MOD_CC                              /* CC=1 */
+                          | FLUID_MOD_CONCAVE                       /* type=1 */
+                          | FLUID_MOD_BIPOLAR                       /* P=1 */
+                          | FLUID_MOD_POSITIVE                      /* D=0 */
+                         );
+    fluid_mod_set_source2(&custom_balance_mod, 0, 0);
+    fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE);     /* Destination: stereo balance */
+    /* Amount: 96 dB of attenuation (on the opposite channel) */
+    fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */
+}
+
+static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t *synth)
+{
     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_tsynth, int val)
+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_atomic_int_add(&synth->ticks_since_start, val);
 }
 
 
 /***************************************************************
- *                    FLUID SAMPLE TIMERS 
- *    Timers that use written audio data as timing reference       
+ *                    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_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;
+static void fluid_sample_timer_process(fluid_synth_t *synth)
+{
+    fluid_sample_timer_t *st, *stnext;
+    long msec;
+    int cont;
+    unsigned int ticks = fluid_synth_get_ticks(synth);
+
+    for(st = synth->sample_timers; st; st = stnext)
+    {
+        /* st may be freed in the callback below. cache it's successor now to avoid use after free */
+        stnext = 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;
+}
+
+void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer)
+{
+    fluid_sample_timer_t **ptr;
+    fluid_return_if_fail(synth != NULL);
+    fluid_return_if_fail(timer != NULL);
+
+    ptr = &synth->sample_timers;
+
+    while(*ptr)
+    {
+        if(*ptr == timer)
+        {
+            *ptr = timer->next;
+            FLUID_FREE(timer);
+            return;
+        }
+
+        ptr = &((*ptr)->next);
+    }
 }
 
 
@@ -529,362 +570,536 @@ int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer)
  */
 
 static FLUID_INLINE void
-fluid_synth_update_mixer(fluid_synth_t* synth, void* method, int intparam,
-                        fluid_real_t realparam)
+fluid_synth_update_mixer(fluid_synth_t *synth, fluid_rvoice_function_t 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);
+    fluid_return_if_fail(synth != NULL && synth->eventhandler != NULL);
+    fluid_return_if_fail(synth->eventhandler->mixer != NULL);
+    fluid_rvoice_eventhandler_push_int_real(synth->eventhandler, method,
+                                            synth->eventhandler->mixer,
+                                            intparam, realparam);
 }
 
+static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_synth_t *synth)
+{
+    int i;
+    fluid_settings_getint(synth->settings, "synth.min-note-length", &i);
+    return (unsigned int)(i * synth->sample_rate / 1000.0f);
+}
 
 /**
  * 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
+ * @note The settings parameter is used directly and should not be modified
  * or freed independently.
  */
-fluid_synth_t*
+fluid_synth_t *
 new_fluid_synth(fluid_settings_t *settings)
 {
-  fluid_synth_t* synth;
-  fluid_sfloader_t* loader;
-  double gain;
-  int i, nbuf;
+    fluid_synth_t *synth;
+    fluid_sfloader_t *loader;
+    char *important_channels;
+    int i, nbuf, prio_level = 0;
+    int with_ladspa = 0;
+
+    /* initialize all the conversion tables and other stuff */
+    if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1))
+    {
+        fluid_synth_init();
+    }
 
-  /* 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);
 
-  /* 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; 
+    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.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_getint(settings, "synth.effects-groups", &synth->effects_groups);
+    fluid_settings_getnum_float(settings, "synth.gain", &synth->gain);
+    fluid_settings_getint(settings, "synth.device-id", &synth->device_id);
+    fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores);
+
+    fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion);
+    fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released);
+    fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained);
+    fluid_settings_getnum_float(settings, "synth.overflow.volume", &synth->overflow.volume);
+    fluid_settings_getnum_float(settings, "synth.overflow.age", &synth->overflow.age);
+    fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important);
+
+    /* register the callbacks */
+    fluid_settings_callback_num(settings, "synth.sample-rate",
+                                fluid_synth_handle_sample_rate, synth);
+    fluid_settings_callback_num(settings, "synth.gain",
+                                fluid_synth_handle_gain, synth);
+    fluid_settings_callback_int(settings, "synth.polyphony",
+                                fluid_synth_handle_polyphony, synth);
+    fluid_settings_callback_int(settings, "synth.device-id",
+                                fluid_synth_handle_device_id, synth);
+    fluid_settings_callback_num(settings, "synth.overflow.percussion",
+                                fluid_synth_handle_overflow, synth);
+    fluid_settings_callback_num(settings, "synth.overflow.sustained",
+                                fluid_synth_handle_overflow, synth);
+    fluid_settings_callback_num(settings, "synth.overflow.released",
+                                fluid_synth_handle_overflow, synth);
+    fluid_settings_callback_num(settings, "synth.overflow.age",
+                                fluid_synth_handle_overflow, synth);
+    fluid_settings_callback_num(settings, "synth.overflow.volume",
+                                fluid_synth_handle_overflow, synth);
+    fluid_settings_callback_num(settings, "synth.overflow.important",
+                                fluid_synth_handle_overflow, synth);
+    fluid_settings_callback_str(settings, "synth.overflow.important-channels",
+                                fluid_synth_handle_important_channels, synth);
+    fluid_settings_callback_num(settings, "synth.reverb.room-size",
+                                fluid_synth_handle_reverb_chorus_num, synth);
+    fluid_settings_callback_num(settings, "synth.reverb.damp",
+                                fluid_synth_handle_reverb_chorus_num, synth);
+    fluid_settings_callback_num(settings, "synth.reverb.width",
+                                fluid_synth_handle_reverb_chorus_num, synth);
+    fluid_settings_callback_num(settings, "synth.reverb.level",
+                                fluid_synth_handle_reverb_chorus_num, synth);
+    fluid_settings_callback_int(settings, "synth.reverb.active",
+                                fluid_synth_handle_reverb_chorus_int, synth);
+    fluid_settings_callback_int(settings, "synth.chorus.active",
+                                fluid_synth_handle_reverb_chorus_int, synth);
+    fluid_settings_callback_int(settings, "synth.chorus.nr",
+                                fluid_synth_handle_reverb_chorus_int, synth);
+    fluid_settings_callback_num(settings, "synth.chorus.level",
+                                fluid_synth_handle_reverb_chorus_num, synth);
+    fluid_settings_callback_num(settings, "synth.chorus.depth",
+                                fluid_synth_handle_reverb_chorus_num, synth);
+    fluid_settings_callback_num(settings, "synth.chorus.speed",
+                                fluid_synth_handle_reverb_chorus_num, 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;
+    }
+
+    if(fluid_settings_dupstr(settings, "synth.overflow.important-channels",
+                             &important_channels) == FLUID_OK)
+    {
+        if(fluid_synth_set_important_channels(synth, important_channels) != FLUID_OK)
+        {
+            FLUID_LOG(FLUID_WARN, "Failed to set overflow important channels");
+        }
+
+        FLUID_FREE(important_channels);
+    }
+
+    /* as soon as the synth is created it starts playing. */
+    synth->state = FLUID_SYNTH_PLAYING;
+
+    synth->fromkey_portamento = INVALID_NOTE;          /* disable portamento */
+
+    fluid_atomic_int_set(&synth->ticks_since_start, 0);
+    synth->tuning = NULL;
+    fluid_private_init(synth->tuning_iter);
+
+    /* Initialize multi-core variables if multiple cores enabled */
+    if(synth->cores > 1)
+    {
+        fluid_settings_getint(synth->settings, "audio.realtime-prio", &prio_level);
+    }
+
+    /* Allocate event queue for rvoice mixer */
+    /* In an overflow situation, a new voice takes about 50 spaces in the queue! */
+    synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64,
+                          synth->polyphony, nbuf, synth->effects_channels, synth->effects_groups, synth->sample_rate, synth->cores - 1, prio_level);
+
+    if(synth->eventhandler == NULL)
+    {
+        goto error_recovery;
+    }
 
+    /* Setup the list of default modulators.
+     * Needs to happen after eventhandler has been set up, as fluid_synth_enter_api is called in the process */
+    synth->default_mod = NULL;
+    fluid_synth_add_default_mod(synth, &default_vel2att_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_vel2filter_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_at2viblfo_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_mod2viblfo_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_att_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_pan_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_expr_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_reverb_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_chorus_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &default_pitch_bend_mod, FLUID_SYNTH_ADD);
+    fluid_synth_add_default_mod(synth, &custom_balance_mod, FLUID_SYNTH_ADD);
+
+    /* Create and initialize the Fx unit.*/
+    fluid_settings_getint(settings, "synth.ladspa.active", &with_ladspa);
+
+    if(with_ladspa)
+    {
 #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->ladspa_fx = new_fluid_ladspa_fx(synth->sample_rate,
+                                               FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE);
+
+        if(synth->ladspa_fx == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            goto error_recovery;
+        }
+
+        fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->ladspa_fx,
+                                      synth->audio_groups);
+#else /* LADSPA */
+        FLUID_LOG(FLUID_WARN, "FluidSynth has not been compiled with LADSPA support");
+#endif /* LADSPA */
+    }
+
+    /* 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->eventhandler, synth->sample_rate);
+
+        if(synth->voice[i] == NULL)
+        {
+            goto error_recovery;
+        }
+    }
+
+    /* sets a default basic channel */
+    /* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */
+    /* (i.e all channels are polyphonic) */
+    /* Must be called after channel objects allocation */
+    fluid_synth_set_basic_channel_LOCAL(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY,
+                                        synth->midi_channels);
+
+    synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth);
+
+
+    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;
+
+    {
+        double room, damp, width, level;
+
+        fluid_settings_getnum(settings, "synth.reverb.room-size", &room);
+        fluid_settings_getnum(settings, "synth.reverb.damp", &damp);
+        fluid_settings_getnum(settings, "synth.reverb.width", &width);
+        fluid_settings_getnum(settings, "synth.reverb.level", &level);
+
+        fluid_synth_set_reverb_full_LOCAL(synth,
+                                          FLUID_REVMODEL_SET_ALL,
+                                          room,
+                                          damp,
+                                          width,
+                                          level);
+    }
+
+    {
+        double level, speed, depth;
+
+        fluid_settings_getint(settings, "synth.chorus.nr", &i);
+        fluid_settings_getnum(settings, "synth.chorus.level", &level);
+        fluid_settings_getnum(settings, "synth.chorus.speed", &speed);
+        fluid_settings_getnum(settings, "synth.chorus.depth", &depth);
+
+        fluid_synth_set_chorus_full_LOCAL(synth,
+                                          FLUID_CHORUS_SET_ALL,
+                                          i,
+                                          level,
+                                          speed,
+                                          depth,
+                                          FLUID_CHORUS_DEFAULT_TYPE);
+    }
+
+
     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);
+    if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gm"))
+    {
+        synth->bank_select = FLUID_BANK_STYLE_GM;
+    }
+    else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gs"))
+    {
+        synth->bank_select = FLUID_BANK_STYLE_GS;
+    }
+    else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "xg"))
+    {
+        synth->bank_select = FLUID_BANK_STYLE_XG;
+    }
+    else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "mma"))
+    {
+        synth->bank_select = FLUID_BANK_STYLE_MMA;
+    }
+
+    fluid_synth_process_event_queue(synth);
 
-  /* FIXME */
-  synth->start = fluid_curtime();
+    /* FIXME */
+    synth->start = fluid_curtime();
 
-  return synth;
+    return synth;
 
- error_recovery:
-  delete_fluid_synth(synth);
-  return NULL;
+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,
+ * @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_tsynth)
+void
+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;
+    int i, k;
+    fluid_list_t *list;
+    fluid_sfont_t *sfont;
+    fluid_sfloader_t *loader;
+    fluid_mod_t *default_mod;
+    fluid_mod_t *mod;
 
-  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);
+    fluid_return_if_fail(synth != NULL);
+
+    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);
+                /* If we only use fluid_voice_off(voice) it will trigger a delayed
+                 * fluid_voice_stop(voice) via fluid_synth_check_finished_voices().
+                 * But here, we are deleting the fluid_synth_t instance so
+                 * fluid_voice_stop() will be never triggered resulting in
+                 * SoundFont data never unloaded (i.e a serious memory leak).
+                 * So, fluid_voice_stop() must be explicitly called to insure
+                 * unloading SoundFont data
+                 */
+                fluid_voice_stop(voice);
+            }
+        }
+    }
 
-  /* 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);
-  }
+    /* also unset all presets for clean SoundFont unload */
+    if(synth->channel != NULL)
+    {
+        for(i = 0; i < synth->midi_channels; i++)
+        {
+            fluid_channel_set_preset(synth->channel[i], NULL);
+        }
+    }
 
-  delete_fluid_list(synth->sfont_info);
+    delete_fluid_rvoice_eventhandler(synth->eventhandler);
 
+    /* delete all the SoundFonts */
+    for(list = synth->sfont; list; list = fluid_list_next(list))
+    {
+        sfont = fluid_list_get(list);
+        fluid_sfont_delete_internal(sfont);
+    }
 
-  /* Delete the SoundFont info hash */
-  if (synth->sfont_hash) delete_fluid_hashtable (synth->sfont_hash);
+    delete_fluid_list(synth->sfont);
 
+    /* delete all the SoundFont loaders */
 
-  /* 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);
+    }
 
-  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);
 
-  delete_fluid_list(synth->loaders);
 
+    if(synth->channel != NULL)
+    {
+        for(i = 0; i < synth->midi_channels; i++)
+        {
+            delete_fluid_channel(synth->channel[i]);
+        }
 
-  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);
     }
-    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]);
-      }
+    if(synth->voice != NULL)
+    {
+        for(i = 0; i < synth->nvoice; i++)
+        {
+            delete_fluid_voice(synth->voice[i]);
+        }
+
+        FLUID_FREE(synth->voice);
     }
-    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]);
-      }
+    /* 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++)
+                {
+                    delete_fluid_tuning(synth->tuning[i][k]);
+                }
+
+                FLUID_FREE(synth->tuning[i]);
+            }
+        }
+
+        FLUID_FREE(synth->tuning);
     }
-    FLUID_FREE(synth->tuning);
-  }
 
-  fluid_private_free (synth->tuning_iter);
+    fluid_private_free(synth->tuning_iter);
 
 #ifdef LADSPA
-  /* Release the LADSPA Fx unit */
-  fluid_LADSPA_shutdown(synth->LADSPA_FxUnit);
-  FLUID_FREE(synth->LADSPA_FxUnit);
+    /* Release the LADSPA effects unit */
+    delete_fluid_ladspa_fx(synth->ladspa_fx);
 #endif
 
-  fluid_rec_mutex_destroy(synth->mutex);
+    /* delete all default modulators */
+    default_mod = synth->default_mod;
+
+    while(default_mod != NULL)
+    {
+        mod = default_mod;
+        default_mod = mod->next;
+        delete_fluid_mod(mod);
+    }
 
-  FLUID_FREE(synth);
+    FLUID_FREE(synth->overflow.important_channels);
 
-  return FLUID_OK;
+    fluid_rec_mutex_destroy(synth->mutex);
+
+    FLUID_FREE(synth);
 }
 
 /**
@@ -896,10 +1111,10 @@ delete_fluid_synth(fluid_synth_t* synth)
  */
 /* 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_tsynth)
+const char *
+fluid_synth_error(fluid_synth_t *synth)
 {
-  return fluid_error();
+    return fluid_error();
 }
 
 /**
@@ -908,288 +1123,630 @@ fluid_synth_error(fluid_synth_t* synth)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_noteon(fluid_synth_tsynth, int chan, int key, int vel)
+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);
+    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);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
 
-  result = fluid_synth_noteon_LOCAL (synth, chan, key, vel);
-  FLUID_API_RETURN(result);
+    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_tsynth, int chan, int key, int vel)
+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);
+    fluid_channel_t *channel ;
 
-  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");
+    /* notes with velocity zero go to noteoff  */
+    if(vel == 0)
+    {
+        return fluid_synth_noteoff_LOCAL(synth, chan, key);
     }
-    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);
+    channel = synth->channel[chan];
+
+    /* makes 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;
+    }
 
-  return fluid_preset_noteon(channel->preset, synth, chan, key, vel);
+    if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */
+    {
+        /* play the noteOn in monophonic */
+        return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel);
+    }
+    else
+    {
+        /* channel is poly and legato CC is Off) */
+
+        /* plays the noteOn in polyphonic */
+        /* Sets the note at first position in monophonic list */
+        /* In the case where the musician intends to inter the channel in monophonic
+        (by depressing the CC legato on), the next noteOn mono could be played legato
+         with the previous note poly (if the musician choose this).
+            */
+        fluid_channel_set_onenote_monolist(channel, (unsigned char) key,
+                                           (unsigned char) vel);
+
+        /* 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);
+
+        /* a noteon poly is passed to fluid_synth_noteon_monopoly_legato().
+          This allows an opportunity to get this note played legato with a previous
+          note if a CC PTC have been received before this noteon. This behavior is
+          a MIDI specification (see FluidPolymono-0004.pdf chapter 4.3-a ,3.4.11
+          for details).
+        */
+        return fluid_synth_noteon_monopoly_legato(synth, chan, INVALID_NOTE, key, vel);
+    }
 }
 
 /**
- * Send a note-off event to a FluidSynth object.
+ * Sends 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
+ * @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_tsynth, int chan, int key)
+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);
+    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);
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(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
+fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key)
+{
+    int status;
+    fluid_channel_t *channel = synth->channel[chan];
+
+    if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */
+    {
+        /* play the noteOff in monophonic */
+        status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key);
+    }
+    else
+    {
+        /* channel is poly and legato CC is Off) */
+        /* removes the note from the monophonic list */
+        if(key == fluid_channel_last_note(channel))
+        {
+            fluid_channel_clear_monolist(channel);
+        }
+
+        status = fluid_synth_noteoff_monopoly(synth, chan, key, 0);
+    }
+
+    /* Changes the state (Valid/Invalid) of the most recent note played in a
+       staccato manner */
+    fluid_channel_invalid_prev_note_staccato(channel);
+    return status;
+}
+
+/* Damps 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_tsynth, int chan)
+fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t *synth, int chan)
 {
-  fluid_voice_t* voice;
-  int i;
+    fluid_channel_t *channel = synth->channel[chan];
+    fluid_voice_t *voice;
+    int i;
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-    if ((voice->chan == chan) && _SUSTAINED(voice))
-     fluid_voice_release(voice);
-  }
+        if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice))
+        {
+            if(voice->key == channel->key_mono_sustained)
+            {
+                /* key_mono_sustained is a possible mono note sustainted
+                (by sustain or sostenuto pedal). It must be marked released
+                (INVALID_NOTE) here because it is released only by sustain pedal */
+                channel->key_mono_sustained = INVALID_NOTE;
+            }
+
+            fluid_voice_release(voice);
+        }
+    }
 
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
-/* Damp voices on a channel (turn notes off), if they're sustained by
+/* Damps 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_tsynth, int chan)
+fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t *synth, int chan)
 {
-  fluid_voice_t* voice;
-  int i;
+    fluid_channel_t *channel = synth->channel[chan];
+    fluid_voice_t *voice;
+    int i;
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-    if ((voice->chan == chan) && _HELD_BY_SOSTENUTO(voice))
-     fluid_voice_release(voice);
-  }
+        if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice))
+        {
+            if(voice->key == channel->key_mono_sustained)
+            {
+                /* key_mono_sustained is a possible mono note sustainted
+                (by sustain or sostenuto pedal). It must be marked released
+                (INVALID_NOTE) here because it is released only by sostenuto pedal */
+                channel->key_mono_sustained = INVALID_NOTE;
+            }
+
+            fluid_voice_release(voice);
+        }
+    }
 
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
-
 /**
- * Send a MIDI controller event on a MIDI channel.
+ * Adds the specified modulator \c mod as default modulator to the synth. \c mod will
+ * take effect for any subsequently created voice.
  * @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
+ * @param mod Modulator info (values copied, passed in object can be freed immediately afterwards)
+ * @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ * 
+ * @note Not realtime safe (due to internal memory allocation) and therefore should not be called
+ * from synthesis context at the risk of stalling audio output.
  */
 int
-fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val)
+fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode)
 {
-  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);
+    fluid_mod_t *default_mod;
+    fluid_mod_t *last_mod = NULL;
+    fluid_mod_t *new_mod;
 
-  if (synth->verbose)
-    FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val);
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);
+    fluid_synth_api_enter(synth);
 
-  fluid_channel_set_cc (synth->channel[chan], num, val);
-  result = fluid_synth_cc_LOCAL (synth, chan, num);
-  FLUID_API_RETURN(result);
-}
+    default_mod = synth->default_mod;
+
+    while(default_mod != NULL)
+    {
+        if(fluid_mod_test_identity(default_mod, mod))
+        {
+            if(mode == FLUID_SYNTH_ADD)
+            {
+                default_mod->amount += mod->amount;
+            }
+            else if(mode == FLUID_SYNTH_OVERWRITE)
+            {
+                default_mod->amount = mod->amount;
+            }
+            else
+            {
+                FLUID_API_RETURN(FLUID_FAILED);
+            }
+
+            FLUID_API_RETURN(FLUID_OK);
+        }
+
+        last_mod = default_mod;
+        default_mod = default_mod->next;
+    }
+
+    /* Add a new modulator (no existing modulator to add / overwrite). */
+    new_mod = new_fluid_mod();
+
+    if(new_mod == NULL)
+    {
+        FLUID_API_RETURN(FLUID_FAILED);
+    }
+
+    fluid_mod_clone(new_mod, mod);
+    new_mod->next = NULL;
+
+    if(last_mod == NULL)
+    {
+        synth->default_mod = new_mod;
+    }
+    else
+    {
+        last_mod->next = new_mod;
+    }
+
+    FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Removes the specified modulator \c mod from the synth's default modulator list.
+ * fluid_mod_test_identity() will be used to test modulator matching.
+ * @param synth synth instance
+ * @param mod The modulator to remove
+ * @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise
+ * 
+ * @note Not realtime safe (due to internal memory allocation) and therefore should not be called
+ * from synthesis context at the risk of stalling audio output.
+ */
+int
+fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod)
+{
+    fluid_mod_t *default_mod;
+    fluid_mod_t *last_mod;
+
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);
+    fluid_synth_api_enter(synth);
+
+    last_mod = default_mod = synth->default_mod;
+
+    while(default_mod != NULL)
+    {
+        if(fluid_mod_test_identity(default_mod, mod))
+        {
+            if(synth->default_mod == default_mod)
+            {
+                synth->default_mod = synth->default_mod->next;
+            }
+            else
+            {
+                last_mod->next = default_mod->next;
+            }
+
+            delete_fluid_mod(default_mod);
+            FLUID_API_RETURN(FLUID_OK);
+        }
+
+        last_mod = default_mod;
+        default_mod = default_mod->next;
+    }
+
+    FLUID_API_RETURN(FLUID_FAILED);
+}
+
+
+/**
+ * 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
+ * @note This function supports MIDI Global Controllers which will be sent to
+ * all channels of the basic channel if this basic channel is in mode OmniOff/Mono.
+ * This is accomplished by sending the CC one MIDI channel below the basic
+ * channel of the receiver.
+ * Examples: let a synthesizer with 16 MIDI channels:
+ * - Let a basic channel 7 in mode 3 (Omni Off, Mono). If MIDI channel 6 is disabled it
+ *    could be used as CC global for all channels belonging to basic channel 7.
+ * - Let a basic channel 0 in mode 3. If MIDI channel 15  is disabled it could be used
+ *   as CC global for all channels belonging to basic channel 0.
+ */
+int
+fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val)
+{
+    int result = FLUID_FAILED;
+    fluid_channel_t *channel;
+    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);
+
+    channel = synth->channel[chan];
+
+    if(channel->mode &  FLUID_CHANNEL_ENABLED)
+    {
+        /* chan is enabled */
+        if(synth->verbose)
+        {
+            FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val);
+        }
+
+        fluid_channel_set_cc(channel, num, val);
+        result = fluid_synth_cc_LOCAL(synth, chan, num);
+    }
+    else /* chan is disabled so it is a candidate for global channel */
+    {
+        /* looks for next basic channel */
+        int n_chan = synth->midi_channels; /* MIDI Channels number */
+        int basicchan ;
+
+        if(chan < n_chan - 1)
+        {
+            basicchan = chan + 1;    /* next channel */
+        }
+        else
+        {
+            basicchan = 0;    /* wrap to 0 */
+        }
+
+        channel = synth->channel[basicchan];
+
+        /* Channel must be a basicchan in mode OMNIOFF_MONO */
+        if((channel->mode &  FLUID_CHANNEL_BASIC) &&
+                ((channel->mode & FLUID_CHANNEL_MODE_MASK) == FLUID_CHANNEL_MODE_OMNIOFF_MONO))
+        {
+            /* sends cc to all channels in this basic channel */
+            int i, nbr = channel->mode_val;
+
+            for(i = basicchan; i < basicchan + nbr; i++)
+            {
+                if(synth->verbose)
+                {
+                    FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val);
+                }
+
+                fluid_channel_set_cc(synth->channel[i], num, val);
+                result = fluid_synth_cc_LOCAL(synth, i, num);
+            }
+        }
+        /* The channel chan is not a valid 'global channel' */
+        else
+        {
+            result = FLUID_FAILED;
+        }
+    }
+
+    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;
+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)
+    {
+
+    /* CC omnioff, omnion, mono, poly */
+    case POLY_OFF:
+    case POLY_ON:
+    case OMNI_OFF:
+    case OMNI_ON:
+
+        /* allowed only if channum is a basic channel */
+        if(chan->mode &  FLUID_CHANNEL_BASIC)
+        {
+            /* Construction of new_mode from current channel mode and this CC mode */
+            int new_mode = chan->mode & FLUID_CHANNEL_MODE_MASK;
+
+            switch(num)
+            {
+            case POLY_OFF:
+                new_mode |= FLUID_CHANNEL_POLY_OFF;
+                break;
+
+            case POLY_ON:
+                new_mode &= ~FLUID_CHANNEL_POLY_OFF;
+                break;
+
+            case OMNI_OFF:
+                new_mode |= FLUID_CHANNEL_OMNI_OFF;
+                break;
+
+            case OMNI_ON:
+                new_mode &= ~FLUID_CHANNEL_OMNI_OFF;
+                break;
+
+            default: /* should never happen */
+                return FLUID_FAILED;
+            }
+
+            /* MIDI specs: if value is 0 it means all channels from channum to next
+                basic channel minus 1 (if any) or to MIDI channel count minus 1.
+                However, if value is > 0 (e.g. 4), the group of channels will be be
+                limited to 4.
+               value is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode
+                implies a group of only one channel.
+            */
+            /* Checks value range and changes this existing basic channel group */
+            value = fluid_synth_check_next_basic_channel(synth, channum, new_mode, value);
+
+            if(value != FLUID_FAILED)
+            {
+                /* reset the current basic channel before changing it */
+                fluid_synth_reset_basic_channel_LOCAL(synth, channum, chan->mode_val);
+                fluid_synth_set_basic_channel_LOCAL(synth, channum, new_mode, value);
+                break; /* FLUID_OK */
+            }
+        }
+
+        return FLUID_FAILED;
+
+    case LEGATO_SWITCH:
+        /* handles Poly/mono commutation on Legato pedal On/Off.*/
+        fluid_channel_cc_legato(chan, value);
+        break;
+
+    case PORTAMENTO_SWITCH:
+        /* Special handling of the monophonic list  */
+        /* Invalids the most recent note played in a staccato manner */
+        fluid_channel_invalid_prev_note_staccato(chan);
+        break;
+
+    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 (+/- 100 cents, 8192 = center) */
+                fluid_synth_set_gen_LOCAL(synth, channum, GEN_FINETUNE,
+                                          (data - 8192) / 8192.0 * 100.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;
+            }
         }
-      }
-      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;
+
+        chan->nrpn_active = 1;
+        break;
+
+    case RPN_MSB:
+    case RPN_LSB:
+        chan->nrpn_active = 0;
+        break;
+
+    case BREATH_MSB:
+        /* handles CC Breath On/Off noteOn/noteOff mode */
+        fluid_channel_cc_breath_note_on_off(chan, value);
+
+    /* fall-through */
+    default:
+        return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num);
+    }
+
+    return FLUID_OK;
 }
 
 /**
@@ -1198,30 +1755,35 @@ fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval)
+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_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);
 
-  FLUID_API_ENTRY_CHAN(FLUID_FAILED);
-  
-  *pval = fluid_channel_get_cc (synth->channel[chan], num);
-  FLUID_API_RETURN(FLUID_OK);
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(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)
+static void
+fluid_synth_handle_device_id(void *data, const char *name, int value)
 {
-  fluid_synth_api_enter(synth);
-  synth->device_id = value;
-  fluid_synth_api_exit(synth);
-  return 0;
+    fluid_synth_t *synth = (fluid_synth_t *)data;
+    fluid_return_if_fail(synth != NULL);
+
+    fluid_synth_api_enter(synth);
+    synth->device_id = value;
+    fluid_synth_api_exit(synth);
 }
 
 /**
@@ -1237,7 +1799,7 @@ fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value)
  *   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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.1.0
  */
 /* SYSEX format (0xF0 and 0xF7 not passed to this function):
@@ -1249,397 +1811,502 @@ 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;
+    int avail_response = 0;
 
-  if (handled) *handled = FALSE;
+    if(handled)
+    {
+        *handled = FALSE;
+    }
 
-  if (response_len)
-  {
-    avail_response = *response_len;
-    *response_len = 0;
-  }
+    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);
+    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;
+    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);
+    /* 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;
+        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)
-  {
+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_t 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(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;
+            }
 
-      if (avail_response < *response_len) return FLUID_FAILED;
+            return FLUID_OK;
+        }
 
-      /* 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;
-      }
+        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 = response;
+        *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME;
+        *resptr++ = synth->device_id;
+        *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID;
+        *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP;
 
-      *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;
+        }
 
-      if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK)
-        *resptr++ = bank;
+        *resptr++ = prog;
+        FLUID_STRNCPY(resptr, name, 16);
+        resptr += 16;
 
-      *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);
 
-      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);
 
-        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;
+        }
 
-        *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];
+            }
+        }
 
-      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;
+        *resptr++ = chksum & 0x7F;
 
-        for (i = 21; i < 128 * 3 + 21; i++)
-          chksum ^= response[i];
-      }
-      else
-      {
-        for (i = 1, chksum = 0; i < 406; i++)
-          chksum ^= response[i];
-      }
+        if(handled)
+        {
+            *handled = TRUE;
+        }
 
-      *resptr++ = chksum & 0x7F;
+        break;
 
-      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;
-      }
+        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[6] * 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;
+            }
 
-      prog = *dataptr++;
-      count = *dataptr++;
+            keys[index] = note;
 
-      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++;
 
-        note = *dataptr++;
-        frac = *dataptr++;
-        frac2 = *dataptr++;
+            if(note & 0x80 || frac & 0x80 || frac2 & 0x80)
+            {
+                return FLUID_OK;
+            }
 
-        if (note & 0x80 || frac & 0x80 || frac2 & 0x80)
-          return FLUID_OK;
+            frac = frac << 7 | frac2;
 
-        frac = frac << 7 | frac2;
+            /* No change pitch value?  Doesn't really make sense to send that, but.. */
+            if(note == 0x7F && frac == 16383)
+            {
+                continue;
+            }
 
-        /* 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;
+            }
+        }
 
-        tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0);
-        index++;
-      }
+        if(handled)
+        {
+            *handled = TRUE;
+        }
 
-      if (index > 0)
-      {
-        if (fluid_synth_tune_notes (synth, bank, prog, index, keys, tunedata,
-                                    realtime) == FLUID_FAILED)
-          return FLUID_FAILED;
-      }
+        break;
 
-      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((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(data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80)
+        {
+            return FLUID_OK;
+        }
 
-      if (dryrun)
-      {
-        if (handled) *handled = TRUE;
-        return FLUID_OK;
-      }
+        if(dryrun)
+        {
+            if(handled)
+            {
+                *handled = TRUE;
+            }
+
+            return FLUID_OK;
+        }
 
-      channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6];
+        channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6];
 
-      if (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE)
-      {
-        for (i = 0; i < 12; i++)
+        if(msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE)
         {
-          frac = data[i + 7];
-          if (frac & 0x80) return FLUID_OK;
-          tunedata[i] = (int)frac - 64;
+            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++)
+        else
         {
-          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);
+            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",
+        if(fluid_synth_activate_octave_tuning(synth, 0, 0, "SYSEX",
                                               tunedata, realtime) == FLUID_FAILED)
-        return 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 (channels)
-      {
-        for (i = 0; i < 16; i++)
+        if(handled)
         {
-          if (channels & (1 << i))
-            fluid_synth_activate_tuning (synth, i, 0, 0, realtime);
+            *handled = TRUE;
         }
-      }
 
-      if (handled) *handled = TRUE;
-      break;
-  }
+        break;
+    }
 
-  return FLUID_OK;
+    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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.1.4
  */
 int
-fluid_synth_all_notes_off(fluid_synth_tsynth, int chan)
+fluid_synth_all_notes_off(fluid_synth_t *synth, int chan)
 {
-  int result;
+    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);
 
-  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);
+    if(chan >= synth->midi_channels)
+    {
+        result = FLUID_FAILED;
+    }
+    else
+    {
+        /* Allowed (even for channel disabled) as chan = -1 selects all channels */
+        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)
+//static int
+int
+fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan)
 {
-  fluid_voice_t* voice;
-  int i;
+    fluid_voice_t *voice;
+    int i;
+
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+        if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice))))
+        {
+            fluid_voice_noteoff(voice);
+        }
+    }
 
-    if (_PLAYING(voice) && ((-1 == chan) || (chan == voice->chan)))
-      fluid_voice_noteoff(voice);
-  }
-  return FLUID_OK;
+    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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.1.4
  */
 int
-fluid_synth_all_sounds_off(fluid_synth_tsynth, int chan)
+fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan)
 {
-  int result;
+    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
+    {
+        /* Allowed (even for channel disabled) as chan = -1 selects all channels */
+        result = fluid_synth_all_sounds_off_LOCAL(synth, chan);
+    }
 
-  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);
+    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_tsynth, int chan)
+fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan)
 {
-  fluid_voice_t* voice;
-  int i;
+    fluid_voice_t *voice;
+    int i;
+
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+        if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice))))
+        {
+            fluid_voice_off(voice);
+        }
+    }
 
-    if (_PLAYING(voice) && ((-1 == chan) || (chan == voice->chan)))
-      fluid_voice_off(voice);
-  }
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 /**
  * Reset reverb engine
  * @param synth FluidSynth instance
- * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_reset_reverb(fluid_synth_tsynth)
+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);
+    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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_reset_chorus(fluid_synth_tsynth)
+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);
+    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.
+ * Send MIDI system reset command (big red 'panic' button), turns off notes, resets
+ * controllers and restores initial basic channel configuration.
  * @param synth FluidSynth instance
- * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_system_reset(fluid_synth_tsynth)
+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);
+    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_tsynth)
+fluid_synth_system_reset_LOCAL(fluid_synth_t *synth)
 {
-  fluid_voice_t* voice;
-  int i;
+    int i;
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+    fluid_synth_all_sounds_off_LOCAL(synth, -1);
 
-    if (_PLAYING(voice))
-      fluid_voice_off(voice);
-  }
+    for(i = 0; i < synth->midi_channels; i++)
+    {
+        fluid_channel_reset(synth->channel[i]);
+    }
 
-  for (i = 0; i < synth->midi_channels; i++)
-    fluid_channel_reset(synth->channel[i]);
+    /* Basic channel 0, Mode Omni On Poly */
+    fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY,
+                                  synth->midi_channels);
 
-  fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_fx, 0, 0.0f); 
+    fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f);
+    fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f);
 
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 /**
@@ -1648,42 +2315,50 @@ fluid_synth_system_reset_LOCAL(fluid_synth_t* synth)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 static int
-fluid_synth_modulate_voices_LOCAL(fluid_synth_tsynth, int chan, int is_cc, int ctrl)
+fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, int is_cc, int ctrl)
 {
-  fluid_voice_t* voice;
-  int i;
+    fluid_voice_t *voice;
+    int i;
+
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+        if(fluid_voice_get_channel(voice) == chan)
+        {
+            fluid_voice_modulate(voice, is_cc, ctrl);
+        }
+    }
 
-    if (voice->chan == chan)
-      fluid_voice_modulate(voice, is_cc, ctrl);
-  }
-  return FLUID_OK;
+    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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 static int
-fluid_synth_modulate_voices_all_LOCAL(fluid_synth_tsynth, int chan)
+fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan)
 {
-  fluid_voice_t* voice;
-  int i;
+    fluid_voice_t *voice;
+    int i;
+
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+        if(fluid_voice_get_channel(voice) == chan)
+        {
+            fluid_voice_modulate_all(voice);
+        }
+    }
 
-    if (voice->chan == chan)
-      fluid_voice_modulate_all(voice);
-  }
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 /**
@@ -1691,30 +2366,93 @@ fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_channel_pressure(fluid_synth_tsynth, int chan, int val)
+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);
+    int result;
+    fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED);
+
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
 
-  FLUID_API_ENTRY_CHAN(FLUID_FAILED);
-  
-  if (synth->verbose)
-    FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val);
+    if(synth->verbose)
+    {
+        FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val);
+    }
 
-  fluid_channel_set_channel_pressure (synth->channel[chan], val);
+    fluid_channel_set_channel_pressure(synth->channel[chan], val);
+    result = fluid_synth_update_channel_pressure_LOCAL(synth, chan);
 
-  result = fluid_synth_update_channel_pressure_LOCAL (synth, chan);
-  FLUID_API_RETURN(result);
+    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)
+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 polyphonic key pressure controller value.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param key MIDI key number (0-127)
+ * @param val MIDI key pressure value (0-127)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ * @since 2.0.0
+ */
+int
+fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val)
+{
+    int result;
+    fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);
+    fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED);
+
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
+
+    if(synth->verbose)
+    {
+        FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val);
+    }
+
+    fluid_channel_set_key_pressure(synth->channel[chan], key, val);
+    result = fluid_synth_update_key_pressure_LOCAL(synth, chan, key);
+
+    FLUID_API_RETURN(result);
+}
+
+/* Updates key pressure from within synthesis thread */
+static int
+fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key)
 {
-  return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_CHANNELPRESSURE);
+    fluid_voice_t *voice;
+    int i;
+    int result = FLUID_OK;
+
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
+
+        if(voice->chan == chan && voice->key == key)
+        {
+            result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE);
+
+            if(result != FLUID_OK)
+            {
+                return result;
+            }
+        }
+    }
+
+    return result;
 }
 
 /**
@@ -1722,29 +2460,34 @@ fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int chan)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_pitch_bend(fluid_synth_tsynth, int chan, int val)
+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);
+    int result;
+    fluid_return_val_if_fail(val >= 0 && val <= 16383, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(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);
+    fluid_channel_set_pitch_bend(synth->channel[chan], val);
+    result = fluid_synth_update_pitch_bend_LOCAL(synth, chan);
 
-  result = fluid_synth_update_pitch_bend_LOCAL (synth, chan);
-  FLUID_API_RETURN(result);
+    FLUID_API_RETURN(result);
 }
 
 /* Local synthesis thread variant of pitch bend */
 static int
-fluid_synth_update_pitch_bend_LOCAL(fluid_synth_tsynth, int chan)
+fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan)
 {
-  return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_PITCHWHEEL);
+    return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEEL);
 }
 
 /**
@@ -1753,16 +2496,22 @@ fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend)
+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);
+    int result;
+    fluid_return_val_if_fail(ppitch_bend != NULL, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
+
+    *ppitch_bend = fluid_channel_get_pitch_bend(synth->channel[chan]);
+    result = FLUID_OK;
+
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -1770,29 +2519,34 @@ fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_pitch_wheel_sens(fluid_synth_tsynth, int chan, int val)
+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);
+    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);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(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);
+    fluid_channel_set_pitch_wheel_sensitivity(synth->channel[chan], val);
+    result = fluid_synth_update_pitch_wheel_sens_LOCAL(synth, chan);
 
-  result = fluid_synth_update_pitch_wheel_sens_LOCAL (synth, chan);
-  FLUID_API_RETURN(result);
+    FLUID_API_RETURN(result);
 }
 
 /* Local synthesis thread variant of set pitch wheel sensitivity */
 static int
-fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_tsynth, int chan)
+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);
+    return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEELSENS);
 }
 
 /**
@@ -1800,17 +2554,23 @@ fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan)
  * @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
+ * @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_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);
+    int result;
+    fluid_return_val_if_fail(pval != NULL, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
+
+    *pval = fluid_channel_get_pitch_wheel_sensitivity(synth->channel[chan]);
+    result = FLUID_OK;
+
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -1818,108 +2578,97 @@ fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval)
  * @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
+ * @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_synth_set_preset(fluid_synth_t *synth, int chan, fluid_preset_t *preset)
 {
-  fluid_channel_t *channel;
+    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);
+    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];
+    channel = synth->channel[chan];
 
-  return fluid_channel_set_preset (channel, preset);
+    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)
+static fluid_preset_t *
+fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum,
+                       int banknum, 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;
+    fluid_sfont_t *sfont;
+    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);
+    /* 128 indicates an "unset" operation" */
+    if(prognum == FLUID_UNSET_PROGRAM)
+    {
+        return NULL;
+    }
 
-    if (fluid_sfont_get_id (sfont_info->sfont) == sfontnum)
+    for(list = synth->sfont; list; list = fluid_list_next(list))
     {
-      preset = fluid_sfont_get_preset (sfont_info->sfont,
-                                       banknum - sfont_info->bankofs, prognum);
-      if (preset) sfont_info->refcount++;       /* Add reference to SoundFont */
-      break;
+        sfont = fluid_list_get(list);
+
+        if(fluid_sfont_get_id(sfont) == sfontnum)
+        {
+            return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);
+        }
     }
-  }
 
-  return preset;
+    return NULL;
 }
 
 /* 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_tsynth, const char *sfontname,
-                                     unsigned int banknum, unsigned int prognum)
+static fluid_preset_t *
+fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname,
+                                     int banknum, 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);
+    fluid_sfont_t *sfont;
+    fluid_list_t *list;
 
-    if (FLUID_STRCMP (fluid_sfont_get_name (sfont_info->sfont), sfontname) == 0)
+    for(list = synth->sfont; list; list = fluid_list_next(list))
     {
-      preset = fluid_sfont_get_preset (sfont_info->sfont,
-                                       banknum - sfont_info->bankofs, prognum);
-      if (preset) sfont_info->refcount++;       /* Add reference to SoundFont */
-      break;
+        sfont = fluid_list_get(list);
+
+        if(FLUID_STRCMP(fluid_sfont_get_name(sfont), sfontname) == 0)
+        {
+            return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);
+        }
     }
-  }
 
-  return preset;
+    return NULL;
 }
 
 /* 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 *
+fluid_synth_find_preset(fluid_synth_t *synth, int banknum,
+                        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);
+    fluid_preset_t *preset;
+    fluid_sfont_t *sfont;
+    fluid_list_t *list;
 
-    preset = fluid_sfont_get_preset (sfont_info->sfont,
-                                     banknum - sfont_info->bankofs, prognum);
-    if (preset)
+    for(list = synth->sfont; list; list = fluid_list_next(list))
     {
-      sfont_info->refcount++;       /* Add reference to SoundFont */
-      break;
+        sfont = fluid_list_get(list);
+
+        preset = fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum);
+
+        if(preset)
+        {
+            return preset;
+        }
     }
-  }
 
-  return preset;
+    return NULL;
 }
 
 /**
@@ -1927,7 +2676,7 @@ fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum,
  * @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
+ * @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. */
@@ -1935,72 +2684,90 @@ fluid_synth_find_preset(fluid_synth_t* synth, unsigned int banknum,
 /* 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;
+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_FAILED;
+
+    fluid_return_val_if_fail(prognum >= 0 && prognum <= 128, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(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 first preset in bank 0 (usually piano...) */
-        if (!preset)
+        /* Fallback to another preset if not found */
+        if(!preset)
         {
-         subst_prog = 0;
-          preset = fluid_synth_find_preset(synth, subst_bank, subst_prog);
+            /* Percussion: Fallback to preset 0 in percussion bank */
+            if(channel->channel_type == CHANNEL_TYPE_DRUM)
+            {
+                subst_prog = 0;
+                subst_bank = DRUM_INST_BANK;
+                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);
+            }
         }
-      }
-
-      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);
+    /* 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);
 }
 
 /**
@@ -2008,16 +2775,28 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ * @note This function does not change the instrument currently assigned to \c chan,
+ * as it is usually called prior to fluid_synth_program_change(). If you still want
+ * instrument changes to take effect immediately, call fluid_synth_program_reset()
+ * after having set up the bank configuration.
+ *
  */
 int
-fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank)
+fluid_synth_bank_select(fluid_synth_t *synth, int chan, 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);
+    int result;
+    fluid_return_val_if_fail(bank <= 16383, FLUID_FAILED);
+    fluid_return_val_if_fail(bank >= 0, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
+
+    fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1);
+    result = FLUID_OK;
+
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -2025,16 +2804,25 @@ fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ * @note This function does not change the instrument currently assigned to \c chan,
+ * as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change().
+ * If you still want instrument changes to take effect immediately, call fluid_synth_program_reset()
+ * after having selected the soundfont.
  */
 int
-fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id)
+fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id)
 {
-  FLUID_API_ENTRY_CHAN(FLUID_FAILED);
-  
-  fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);
+    int result;
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
 
-  FLUID_API_RETURN(FLUID_OK);
+    fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1);
+    result = FLUID_OK;
+
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -2044,18 +2832,15 @@ fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id)
  * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.1.1
  *
- * Note: Channel retains its SoundFont ID and bank numbers, while the program
+ * @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)
+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);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    FLUID_API_RETURN(fluid_synth_program_change(synth, chan, FLUID_UNSET_PROGRAM));
 }
 
 /**
@@ -2065,27 +2850,35 @@ fluid_synth_unset_program (fluid_synth_t *synth, int chan)
  * @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
+ * @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_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id,
+                        int *bank_num, int *preset_num)
 {
-  fluid_channel_t* channel;
+    int result;
+    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);
 
-  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);
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
 
-  channel = synth->channel[chan];
-  fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num,
-                                    (int *)preset_num);
+    channel = synth->channel[chan];
+    fluid_channel_get_sfont_bank_prog(channel, sfont_id, bank_num, 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;
+    }
 
-  /* 128 indicates that the preset is unset.  Set to 0 to be backwards compatible. */
-  if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0;
+    result = FLUID_OK;
 
-  FLUID_API_RETURN(FLUID_OK);
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -2095,34 +2888,40 @@ fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id,
  * @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
+ * @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_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id,
+                           int bank_num, int preset_num)
 {
-  fluid_preset_t* preset = NULL;
-  fluid_channel_t* channel;
-  int result;
-  FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    fluid_preset_t *preset = NULL;
+    fluid_channel_t *channel;
+    int result;
+    fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED);
+    fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED);
 
-  channel = synth->channel[chan];
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
 
-  /* ++ Allocate preset */
-  preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num);
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
 
-  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);
-  }
+    channel = synth->channel[chan];
+
+    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);
+    /* 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);
 }
 
 /**
@@ -2132,37 +2931,42 @@ fluid_synth_program_select(fluid_synth_t* synth, int chan, unsigned int sfont_id
  * @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
+ * @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);
-  }
+fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan,
+        const char *sfont_name, int bank_num,
+        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);
 
-  /* 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);  
+    /* Allowed only on MIDI channel enabled */
+    FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED);
+
+    channel = synth->channel[chan];
+
+    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);
 }
 
 /*
@@ -2171,63 +2975,66 @@ fluid_synth_program_select_by_sfont_name (fluid_synth_t* synth, int chan,
  * unloaded or reloaded.
  */
 static void
-fluid_synth_update_presets(fluid_synth_tsynth)
+fluid_synth_update_presets(fluid_synth_t *synth)
 {
-  fluid_channel_t *channel;
-  fluid_preset_t *preset;
-  int sfont, bank, prog;
-  int chan;
+    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);
-  }
+    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)
+/* Handler for synth.sample-rate setting. */
+static void
+fluid_synth_handle_sample_rate(void *data, const char *name, double value)
 {
-  fluid_synth_set_sample_rate(synth, (float) value);
-  return 0;
+    fluid_synth_t *synth = (fluid_synth_t *)data;
+    fluid_synth_set_sample_rate(synth, (float) value);
 }
 
+
 /**
- * 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.
+ * Set sample rate of the synth.
+ * @note This function should only be used when no voices or notes are active.
  * @param synth FluidSynth instance
  * @param sample_rate New sample rate (Hz)
  * @since 1.1.2
  */
-void 
-fluid_synth_set_sample_rate(fluid_synth_tsynth, float sample_rate)
+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);
+    int i;
+    fluid_return_if_fail(synth != NULL);
+    fluid_synth_api_enter(synth);
+    fluid_clip(sample_rate, 8000.0f, 96000.0f);
+    synth->sample_rate = sample_rate;
+
+    synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth);
+
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        fluid_voice_set_output_rate(synth->voice[i], sample_rate);
+    }
+
+    fluid_synth_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)
+static void
+fluid_synth_handle_gain(void *data, const char *name, double value)
 {
-  fluid_synth_set_gain(synth, (float) value);
-  return 0;
+    fluid_synth_t *synth = (fluid_synth_t *)data;
+    fluid_synth_set_gain(synth, (float) value);
 }
 
 /**
@@ -2236,33 +3043,37 @@ fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value)
  * @param gain Gain value (function clamps value to the range 0.0 to 10.0)
  */
 void
-fluid_synth_set_gain(fluid_synth_tsynth, float gain)
+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);
+    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);
+    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_tsynth)
+fluid_synth_update_gain_LOCAL(fluid_synth_t *synth)
 {
-  fluid_voice_t *voice;
-  float gain;
-  int i;
+    fluid_voice_t *voice;
+    float gain;
+    int i;
 
-  gain = synth->gain;
+    gain = synth->gain;
 
-  for (i = 0; i < synth->polyphony; i++)
-  {
-    voice = synth->voice[i];
-    if (_PLAYING (voice)) fluid_voice_set_gain (voice, gain);
-  }
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
+
+        if(fluid_voice_is_playing(voice))
+        {
+            fluid_voice_set_gain(voice, gain);
+        }
+    }
 }
 
 /**
@@ -2271,119 +3082,144 @@ fluid_synth_update_gain_LOCAL(fluid_synth_t* synth)
  * @return Synth gain value (0.0 to 10.0)
  */
 float
-fluid_synth_get_gain(fluid_synth_tsynth)
+fluid_synth_get_gain(fluid_synth_t *synth)
 {
-  float result;
-  fluid_return_val_if_fail (synth != NULL, 0.0);
-  fluid_synth_api_enter(synth);
+    float result;
+    fluid_return_val_if_fail(synth != NULL, 0.0);
+    fluid_synth_api_enter(synth);
 
-  result = synth->gain;
-  FLUID_API_RETURN(result);
+    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)
+static void
+fluid_synth_handle_polyphony(void *data, const char *name, int value)
 {
-  fluid_synth_set_polyphony(synth, value);
-  return 0;
+    fluid_synth_t *synth = (fluid_synth_t *)data;
+    fluid_synth_set_polyphony(synth, value);
 }
 
 /**
  * Set synthesizer polyphony (max number of voices).
  * @param synth FluidSynth instance
  * @param polyphony Polyphony to assign
- * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.0.6
  */
 int
-fluid_synth_set_polyphony(fluid_synth_tsynth, int polyphony)
+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);
+    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);
+    result = fluid_synth_update_polyphony_LOCAL(synth, polyphony);
 
-  FLUID_API_RETURN(result);
+    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)
+fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony)
 {
-  int result;
-  fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
-  fluid_synth_api_enter(synth);
-  
-  result = synth->polyphony;
-  FLUID_API_RETURN(result);
-}
+    fluid_voice_t *voice;
+    int i;
 
-/**
- * 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)
+    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->eventhandler, synth->sample_rate);
+
+            if(synth->voice[i] == NULL)
+            {
+                return FLUID_FAILED;
+            }
+
+            fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags);
+        }
+
+        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(fluid_voice_is_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);
+}
+
+/**
+ * @brief Get current number of active voices.
+ *
+ * I.e. the no. of voices that have been
+ * started and have not yet finished. Unless called from synthesis context,
+ * this number does not necessarily have to be equal to the number of voices
+ * currently processed by the DSP loop, see below.
+ * @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. Otherwise every call to this function may return different
+ * voice counts as it may change after any (concurrent) call to fluid_synth_write_*() made by
+ * e.g. an audio driver or the applications audio rendering thread.
+ */
+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);
+    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);
+    result = synth->active_voice_count;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -2394,171 +3230,485 @@ fluid_synth_get_active_voice_count(fluid_synth_t* synth)
  * Audio is synthesized this number of frames at a time.  Defaults to 64 frames.
  */
 int
-fluid_synth_get_internal_bufsize(fluid_synth_tsynth)
+fluid_synth_get_internal_bufsize(fluid_synth_t *synth)
 {
-  return FLUID_BUFSIZE;
+    return FLUID_BUFSIZE;
 }
 
 /**
- * Resend a bank select and a program change for every channel.
+ * Resend a bank select and a program change for every channel and assign corresponding instruments.
  * @param synth FluidSynth instance
- * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @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_tsynth)
+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);
+    int i, prog;
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    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.
+ * Synthesize a block of floating point audio to separate audio buffers (multichannel rendering). First effect channel used by reverb, second for chorus.
  * @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
+ * @param left Array of float buffers to store left channel of planar audio (as many as \c synth.audio-channels buffers, each of \c len in size)
+ * @param right Array of float buffers to store right channel of planar audio (size: dito)
+ * @param fx_left Since 1.1.7: If not \c NULL, array of float buffers to store left effect channels (as many as \c synth.effects-channels buffers, each of \c len in size)
+ * @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ *
+ * @note Should only be called from synthesis thread.
  *
- * NOTE: Should only be called from synthesis thread.
+ * @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release. It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more powerful and flexible fluid_synth_process().
+ *
+ * Usage example:
+ * @code{.cpp}
+    const int FramesToRender = 64;
+    int channels;
+    // retrieve number of stereo audio channels
+    fluid_settings_getint(settings, "synth.audio-channels", &channels);
+
+    // we need twice as many (mono-)buffers
+    channels *= 2;
+
+    // fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: each midi channel gets rendered to its own stereo buffer, rather than having one buffer and interleaved PCM
+    float** mix_buf = new float*[channels];
+    for(int i = 0; i < channels; i++)
+    {
+        mix_buf[i] = new float[FramesToRender];
+    }
+
+    // retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) and chrous (second chan))
+    fluid_settings_getint(settings, "synth.effects-channels", &channels);
+    channels *= 2;
+
+    float** fx_buf = new float*[channels];
+    for(int i = 0; i < channels; i++)
+    {
+        fx_buf[i] = new float[FramesToRender];
+    }
+
+    float** mix_buf_l = mix_buf;
+    float** mix_buf_r = &mix_buf[channels/2];
+
+    float** fx_buf_l = fx_buf;
+    float** fx_buf_r = &fx_buf[channels/2];
+
+    fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r)
+ * @endcode
  */
 int
-fluid_synth_nwrite_float(fluid_synth_tsynth, 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;
+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, *fx_left_in;
+    fluid_real_t *right_in, *fx_right_in;
+    double time = fluid_utime();
+    int i, num, available, count;
 #ifdef WITH_FLOAT
-  int bytes;
+    int bytes;
 #endif
-  float cpu_load;
+    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);
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(left != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(right != NULL, FLUID_FAILED);
+    
+    /* 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);
+        fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);
 
-    num = (available > len)? len : available;
+        num = (available > len) ? len : available;
 #ifdef WITH_FLOAT
-    bytes = num * sizeof(float);
+        bytes = num * sizeof(float);
 #endif
 
-    for (i = 0; i < synth->audio_channels; i++) {
+        for(i = 0; i < synth->audio_channels; i++)
+        {
+#ifdef WITH_FLOAT
+            FLUID_MEMCPY(left[i], &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);
+            FLUID_MEMCPY(right[i], &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);
+#else //WITH_FLOAT
+            int j;
+
+            for(j = 0; j < num; j++)
+            {
+                left[i][j] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
+                right[i][j] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
+            }
+
+#endif //WITH_FLOAT
+        }
+
+        for(i = 0; i < synth->effects_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);
+
+            if(fx_left != NULL)
+            {
+                FLUID_MEMCPY(fx_left[i], &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes);
+            }
+
+            if(fx_right != NULL)
+            {
+                FLUID_MEMCPY(fx_right[i], &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + 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];
-      }
+            int j;
+
+            if(fx_left != NULL)
+            {
+                for(j = 0; j < num; j++)
+                {
+                    fx_left[i][j] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
+                }
+            }
+
+            if(fx_right != NULL)
+            {
+                for(j = 0; j < num; j++)
+                {
+                    fx_right[i][j] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur];
+                }
+            }
+
 #endif //WITH_FLOAT
+        }
+
+        count += num;
+        num += synth->cur; /* if we're now done, num becomes the new synth->cur below */
     }
-    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);
+    /* 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);
+        fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);
 
-    num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE;
+        num = (FLUID_BUFSIZE > len - count) ? len - count : FLUID_BUFSIZE;
 #ifdef WITH_FLOAT
-    bytes = num * sizeof(float);
+        bytes = num * sizeof(float);
 #endif
 
-    for (i = 0; i < synth->audio_channels; i++) {
+        for(i = 0; i < synth->audio_channels; i++)
+        {
+#ifdef WITH_FLOAT
+            FLUID_MEMCPY(left[i] + count, &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);
+            FLUID_MEMCPY(right[i] + count, &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);
+#else //WITH_FLOAT
+            int j;
+
+            for(j = 0; j < num; j++)
+            {
+                left[i][j + count] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
+                right[i][j + count] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
+            }
+
+#endif //WITH_FLOAT
+        }
+
+        for(i = 0; i < synth->effects_channels; i++)
+        {
 #ifdef WITH_FLOAT
-      FLUID_MEMCPY(left[i] + count, left_in[i], bytes);
-      FLUID_MEMCPY(right[i] + count, right_in[i], bytes);
+
+            if(fx_left != NULL)
+            {
+                FLUID_MEMCPY(fx_left[i] + count, &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes);
+            }
+
+            if(fx_right != NULL)
+            {
+                FLUID_MEMCPY(fx_right[i] + count, &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], 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];
-      }
+            int j;
+
+            if(fx_left != NULL)
+            {
+                for(j = 0; j < num; j++)
+                {
+                    fx_left[i][j + count] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
+                }
+            }
+
+            if(fx_right != NULL)
+            {
+                for(j = 0; j < num; j++)
+                {
+                    fx_right[i][j + count] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j];
+                }
+            }
+
 #endif //WITH_FLOAT
+        }
+
+        count += num;
     }
 
-    count += num;
-  }
+    synth->cur = num;
 
-  synth->cur = num;
+    time = fluid_utime() - time;
+    cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
+    fluid_atomic_float_set(&synth->cpu_load, cpu_load);
 
-  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);
+    return FLUID_OK;
+}
 
-  if (!synth->eventhandler->is_threadsafe)
-    fluid_synth_api_exit(synth);
-  
-  return FLUID_OK;
+/**
+ * mixes the samples of \p in to \p out
+ * 
+ * @param out the output sample buffer to mix to
+ * @param ooff sample offset in \p out
+ * @param in the rvoice_mixer input sample buffer to mix from
+ * @param ioff sample offset in \p in
+ * @param buf_idx the sample buffer index of \p in to mix from
+ * @param num number of samples to mix
+ */
+static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out,
+                                                       int ooff,
+                                                       const fluid_real_t *FLUID_RESTRICT in,
+                                                       int ioff,
+                                                       int buf_idx,
+                                                       int num)
+{
+    if(out != NULL)
+    {
+        int j;
+        for(j = 0; j < num; j++)
+        {
+            out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff];
+        }
+    }
 }
 
 /**
- * Synthesize floating point audio to audio buffers.
+ * @brief Synthesize floating point audio to stereo audio channels (implements the default interface #fluid_audio_func_t).
+ *
+ * Synthesize and <strong>mix</strong> audio to a given number of planar audio buffers.
+ * Therefore pass <code>nout = N*2</code> float buffers to \p out in order to render
+ * the synthesized audio to \p N stereo channels. Each float buffer must be
+ * able to hold \p len elements.
+ *
+ * \p out contains an array of planar buffers for normal, dry, stereo
+ * audio (alternating left and right). Like:
+@code{.cpp}
+out[0]  = left_buffer_audio_channel_0
+out[1]  = right_buffer_audio_channel_0
+out[2]  = left_buffer_audio_channel_1
+out[3]  = right_buffer_audio_channel_1
+...
+out[ (i * 2 + 0) % nout ]  = left_buffer_audio_channel_i
+out[ (i * 2 + 1) % nout ]  = right_buffer_audio_channel_i
+@endcode
+ *
+ * for zero-based channel index \p i.
+ * The buffer layout of \p fx used for storing effects
+ * like reverb and chorus looks similar:
+@code{.cpp}
+fx[0]  = left_buffer_channel_of_reverb_unit_0
+fx[1]  = right_buffer_channel_of_reverb_unit_0
+fx[2]  = left_buffer_channel_of_chorus_unit_0
+fx[3]  = right_buffer_channel_of_chorus_unit_0
+fx[4]  = left_buffer_channel_of_reverb_unit_1
+fx[5]  = right_buffer_channel_of_reverb_unit_1
+fx[6]  = left_buffer_channel_of_chorus_unit_1
+fx[7]  = right_buffer_channel_of_chorus_unit_1
+fx[8]  = left_buffer_channel_of_reverb_unit_2
+...
+fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ]  = left_buffer_for_effect_channel_j_of_unit_k
+fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ]  = right_buffer_for_effect_channel_j_of_unit_k
+@endcode
+ * where <code>0 <= k < fluid_synth_count_effects_groups()</code> is a zero-based index denoting the effects unit and
+ * <code>0 <= j < fluid_synth_count_effects_channels()</code> is a zero-based index denoting the effect channel within
+ * unit \p k.
+ *
+ * Any voice playing is assigned to audio channels based on the MIDI channel its playing on. Let \p chan be the
+ * zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is
+ * going to be rendered to use:
+ *
+ * <code>i = chan % fluid_synth_count_audio_groups()</code>
+ *
+ * <code>k = chan % fluid_synth_count_effects_groups()</code>
+ *
  * @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
+ * @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx.
+ * @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo)
+ * and in the range <code>0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.
+ * @param fx Array of buffers to store effects audio to. Buffers may
+alias with buffers of \c out. NULL buffers are permitted and will cause to skip mixing any audio into that buffer.
+ * @param nout Count of arrays in \c out. Must be a multiple of 2
+(because of stereo) and in the range <code>0 <= nout/2 <= fluid_synth_count_audio_channels()</code>.
+ * @param out Array of buffers to store (dry) audio to. Buffers may
+alias with buffers of \c fx. NULL buffers are permitted and will cause to skip mixing any audio into that buffer.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise.
  *
- * 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!
+ * @parblock
+ * @note The owner of the sample buffers must zero them out before calling this
+ * function, because any synthesized audio is mixed (i.e. added) to the buffers.
+ * E.g. if fluid_synth_process() is called from a custom audio driver process function
+ * (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers.
+ * @endparblock
+ *
+ * @parblock
+ * @note No matter how many buffers you pass in, fluid_synth_process()
+ * will always render all audio channels to the
+ * buffers in \c out and all effects channels to the
+ * buffers in \c fx, provided that <code>nout > 0</code> and <code>nfx > 0</code> respectively. If
+ * <code>nout/2 < fluid_synth_count_audio_channels()</code> it will wrap around. Same
+ * is true for effects audio if <code>nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups())</code>.
+ * See usage examples below.
+ * @endparblock
+ *
+ * @parblock
+ * @note Should only be called from synthesis thread.
+ * @endparblock
  */
 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);
-    if ((left == NULL) || (right == NULL)) {
-      FLUID_LOG(FLUID_ERR, "Out of memory.");
-      FLUID_FREE(left);
-      FLUID_FREE(right);
-      return FLUID_FAILED;
-    }
-    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);
+fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
+                    int nout, float *out[])
+{
+    fluid_real_t *left_in, *fx_left_in;
+    fluid_real_t *right_in, *fx_right_in;
+    int nfxchan, nfxunits, naudchan;
+
+    double time = fluid_utime();
+    int i, f, num, count;
+
+    float cpu_load;
+
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED);
+    fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED);
+
+    nfxchan = synth->effects_channels;
+    nfxunits = synth->effects_groups;
+    naudchan = synth->audio_channels;
+
+    fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED);
+    fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED);
+
+    fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+    fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in);
+    fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE);
+
+
+    /* First, take what's still available in the buffer */
+    count = 0;
+    num = synth->cur;
+
+    if(synth->cur < FLUID_BUFSIZE)
+    {
+        int available = FLUID_BUFSIZE - synth->cur;
+        num = (available > len) ? len : available;
+
+        if(nout != 0)
+        {
+            for(i = 0; i < naudchan; i++)
+            {
+                float *out_buf = out[(i * 2) % nout];
+                fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num);
+
+                out_buf = out[(i * 2 + 1) % nout];
+                fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num);
+            }
+        }
+
+        if(nfx != 0)
+        {
+            // loop over all effects units
+            for(f = 0; f < nfxunits; f++)
+            {
+                // write out all effects (i.e. reverb and chorus)
+                for(i = 0; i < nfxchan; i++)
+                {
+                    int buf_idx = f * nfxchan + i;
+                    
+                    float *out_buf = fx[(buf_idx * 2) % nfx];
+                    fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num);
+
+                    out_buf = fx[(buf_idx * 2 + 1) % nfx];
+                    fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num);
+                }
+            }
+        }
+
+        count += num;
+        num += synth->cur; /* if we're now done, num becomes the new synth->cur below */
+    }
+
+    /* Then, render blocks and copy till we have 'len' samples  */
+    while(count < len)
+    {
+        int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
+        int blockcount = fluid_synth_render_blocks(synth, blocksleft);
+
+        num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE;
+
+        if(nout != 0)
+        {
+            for(i = 0; i < naudchan; i++)
+            {
+                float *out_buf = out[(i * 2) % nout];
+                fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num);
+
+                out_buf = out[(i * 2 + 1) % nout];
+                fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num);
+            }
+        }
+
+        if(nfx != 0)
+        {
+            // loop over all effects units
+            for(f = 0; f < nfxunits; f++)
+            {
+                // write out all effects (i.e. reverb and chorus)
+                for(i = 0; i < nfxchan; i++)
+                {
+                    int buf_idx = f * nfxchan + i;
+                    
+                    float *out_buf = fx[(buf_idx * 2) % nfx];
+                    fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num);
+                    
+                    out_buf = fx[(buf_idx * 2 + 1) % nfx];
+                    fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num);
+                }
+            }
+        }
+
+        count += num;
+    }
+
+    synth->cur = num;
+
+    time = fluid_utime() - time;
+    cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
+    fluid_atomic_float_set(&synth->cpu_load, cpu_load);
+
     return FLUID_OK;
-  }
 }
 
 /**
@@ -2571,59 +3721,63 @@ fluid_synth_process(fluid_synth_t* synth, int len, int nin, float** in,
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED 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 Should only be called from synthesis thread.
+ * @note Reverb and Chorus are mixed to \c lout resp. \c rout.
  */
 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);
+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);
+    
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
+
+    fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
+    l = synth->cur;
+    fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
 
-       l = 0;
-      }
+    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];
-  }
+        left_out[j] = (float) left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l];
+        right_out[k] = (float) right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l];
+    }
 
-  synth->cur = 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);
+    time = fluid_utime() - time;
+    cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
+    fluid_atomic_float_set(&synth->cpu_load, cpu_load);
 
-  if (!synth->eventhandler->is_threadsafe)
-    fluid_synth_api_exit(synth);
-  fluid_profile(FLUID_PROF_WRITE, prof_ref);
-  
-  return FLUID_OK;
+    fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
+                        fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
+                        len);
+    return FLUID_OK;
 }
 
 #define DITHER_SIZE 48000
@@ -2632,31 +3786,39 @@ fluid_synth_write_float(fluid_synth_t* synth, int len,
 static float rand_table[DITHER_CHANNELS][DITHER_SIZE];
 
 /* Init dither table */
-static void 
+static void
 init_dither(void)
 {
-  float d, dp;
-  int c, i;
+    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;
+    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;
     }
-    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)
+static FLUID_INLINE int
+roundi(float x)
 {
-  if (x >= 0.0f)
-    return (int)(x+0.5f);
-  else
-    return (int)(x-0.5f);
+    if(x >= 0.0f)
+    {
+        return (int)(x + 0.5f);
+    }
+    else
+    {
+        return (int)(x - 0.5f);
+    }
 }
 
 /**
@@ -2669,84 +3831,98 @@ roundi (float x)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED 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
+ * @note Should only be called from synthesis thread.
+ * @note Reverb and Chorus are mixed to \c lout resp. \c rout.
+ * @note Dithering is performed when converting from internal floating point to
  * 16 bit audio.
  */
 int
-fluid_synth_write_s16(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);
+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;
+    float cpu_load;
+
+    fluid_profile_ref_var(prof_ref);
+
+    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;
+    cur = synth->cur;
+    di = synth->dither_index;
 
-  for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) {
+    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;
+        /* fill up the buffers as needed */
+        if(cur >= 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);
+            cur = 0;
+        }
 
-      //fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref_on_block);
-    }
+        left_sample = roundi(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]);
+        right_sample = roundi(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]);
 
-    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++;
 
-    di++;
-    if (di >= DITHER_SIZE) di = 0;
+        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;
+        }
 
-    /* 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;
+        if(right_sample > 32767.0f)
+        {
+            right_sample = 32767.0f;
+        }
 
-    left_out[j] = (signed short) left_sample;
-    right_out[k] = (signed short) right_sample;
-  }
+        if(right_sample < -32768.0f)
+        {
+            right_sample = -32768.0f;
+        }
 
-  synth->cur = cur;
-  synth->dither_index = di;    /* keep dither buffer continous */
+        left_out[j] = (signed short) left_sample;
+        right_out[k] = (signed short) right_sample;
+    }
 
-  fluid_profile(FLUID_PROF_WRITE, prof_ref);
+    synth->cur = cur;
+    synth->dither_index = di;  /* keep dither buffer continous */
 
-  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);
+    time = fluid_utime() - time;
+    cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
+    fluid_atomic_float_set(&synth->cpu_load, cpu_load);
 
-  if (!synth->eventhandler->is_threadsafe)
-    fluid_synth_api_exit(synth);
-  
-  return 0;
+    fluid_profile_write(FLUID_PROF_WRITE, prof_ref,
+                        fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
+                        len);
+    return 0;
 }
 
 /**
@@ -2764,63 +3940,87 @@ fluid_synth_write_s16(fluid_synth_t* synth, int len,
  * @param roff Offset index in 'rout' for first sample
  * @param rincr Increment between samples stored to 'rout'
  *
- * NOTE: Currently private to libfluidsynth.
+ * @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);
+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)
+    {
 
-  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]);
 
-    left_sample = roundi (lin[i] * 32766.0f + rand_table[0][di]);
-    right_sample = roundi (rin[i] * 32766.0f + rand_table[1][di]);
+        di++;
 
-    di++;
-    if (di >= DITHER_SIZE) di = 0;
+        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;
+        }
 
-    /* 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;
+        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;
-  }
+        left_out[j] = (signed short) left_sample;
+        right_out[k] = (signed short) right_sample;
+    }
 
-  *dither_index = di;  /* keep dither buffer continous */
+    *dither_index = di;        /* keep dither buffer continous */
 
-  fluid_profile(FLUID_PROF_WRITE, prof_ref);
+    fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len);
 }
 
 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;
-      }
+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_stop(synth->voice[j]);
+                break;
+            }
+            else if(synth->voice[j]->overflow_rvoice == fv)
+            {
+                fluid_voice_overflow_rvoice_finished(synth->voice[j]);
+                break;
+            }
+        }
     }
-  }
 }
 
 /**
@@ -2828,9 +4028,9 @@ fluid_synth_check_finished_voices(fluid_synth_t* synth)
  * Make sure no (other) rendering is running in parallel when
  * you call this function!
  */
-void fluid_synth_process_event_queue(fluid_synth_tsynth)
+void fluid_synth_process_event_queue(fluid_synth_t *synth)
 {
-  fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
+    fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
 }
 
 
@@ -2840,102 +4040,206 @@ void fluid_synth_process_event_queue(fluid_synth_t* synth)
  * @return number of blocks rendered. Might (often) return less than requested
  */
 static int
-fluid_synth_render_blocks(fluid_synth_tsynth, int blockcount)
+fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount)
 {
-  int i;
-  fluid_profile_ref_var (prof_ref);
+    int i, maxblocks;
+    fluid_profile_ref_var(prof_ref);
 
-  /* Assign ID of synthesis thread */
+    /* 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;
-}
+    fluid_check_fpe("??? Just starting up ???");
 
+    fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
 
-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);
-}
+    /* do not render more blocks than we can store internally */
+    maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer);
 
+    if(blockcount > maxblocks)
+    {
+        blockcount = maxblocks;
+    }
 
-/* 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];
+    for(i = 0; i < blockcount; i++)
+    {
+        fluid_sample_timer_process(synth);
+        fluid_synth_add_ticks(synth, FLUID_BUFSIZE);
 
-    /* safeguard against an available voice. */
-    if (_AVAILABLE(voice)) {
-      return voice;
+        /* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all()
+         * (should only happen with parallel render) stop processing and go for rendering
+         */
+        if(fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler))
+        {
+            // Something has happened, we can't process more
+            blockcount = i + 1;
+            break;
+        }
     }
-    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;
-    }
-  }
+    fluid_check_fpe("fluid_sample_timer_process");
 
-  if (best_voice_index < 0) {
-    return NULL;
-  }
+    blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount);
 
-  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);
+    /* Testcase, that provokes a denormal floating point error */
+#if 0
+    {
+        float num = 1;
 
-  return voice;
+        while(num != 0)
+        {
+            num *= 0.5;
+        };
+    };
+#endif
+    fluid_check_fpe("??? Remainder of synth_one_block ???");
+    fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref,
+                  fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer),
+                  blockcount * FLUID_BUFSIZE);
+    return blockcount;
+}
+
+/*
+ * Handler for synth.reverb.* and synth.chorus.* double settings.
+ */
+static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value)
+{
+    fluid_synth_t *synth = (fluid_synth_t *)data;
+    fluid_return_if_fail(synth != NULL);
+
+    if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0)
+    {
+        fluid_synth_set_reverb_roomsize(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0)
+    {
+        fluid_synth_set_reverb_damp(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.reverb.width") == 0)
+    {
+        fluid_synth_set_reverb_width(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.reverb.level") == 0)
+    {
+        fluid_synth_set_reverb_level(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0)
+    {
+        fluid_synth_set_chorus_depth(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0)
+    {
+        fluid_synth_set_chorus_speed(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.chorus.level") == 0)
+    {
+        fluid_synth_set_chorus_level(synth, value);
+    }
+}
+
+/*
+ * Handler for synth.reverb.* and synth.chorus.* integer settings.
+ */
+static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value)
+{
+    fluid_synth_t *synth = (fluid_synth_t *)data;
+    fluid_return_if_fail(synth != NULL);
+
+    if(FLUID_STRCMP(name, "synth.reverb.active") == 0)
+    {
+        fluid_synth_set_reverb_on(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.chorus.active") == 0)
+    {
+        fluid_synth_set_chorus_on(synth, value);
+    }
+    else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0)
+    {
+        fluid_synth_set_chorus_nr(synth, value);
+    }
+}
+
+/*
+ * Handler for synth.overflow.* settings.
+ */
+static void fluid_synth_handle_overflow(void *data, const char *name, double value)
+{
+    fluid_synth_t *synth = (fluid_synth_t *)data;
+    fluid_return_if_fail(synth != NULL);
+
+    fluid_synth_api_enter(synth);
+
+    if(FLUID_STRCMP(name, "synth.overflow.percussion") == 0)
+    {
+        synth->overflow.percussion = value;
+    }
+    else if(FLUID_STRCMP(name, "synth.overflow.released") == 0)
+    {
+        synth->overflow.released = value;
+    }
+    else if(FLUID_STRCMP(name, "synth.overflow.sustained") == 0)
+    {
+        synth->overflow.sustained = value;
+    }
+    else if(FLUID_STRCMP(name, "synth.overflow.volume") == 0)
+    {
+        synth->overflow.volume = value;
+    }
+    else if(FLUID_STRCMP(name, "synth.overflow.age") == 0)
+    {
+        synth->overflow.age = value;
+    }
+    else if(FLUID_STRCMP(name, "synth.overflow.important") == 0)
+    {
+        synth->overflow.important = value;
+    }
+
+    fluid_synth_api_exit(synth);
+}
+
+/* Selects a voice for killing. */
+static fluid_voice_t *
+fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth)
+{
+    int i;
+    float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1;
+    float 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 ",
+              fluid_voice_get_id(voice), best_voice_index, fluid_voice_get_channel(voice), fluid_voice_get_key(voice));
+    fluid_voice_off(voice);
+
+    return voice;
 }
 
 
@@ -2952,108 +4256,150 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
  * 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
+ * @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);
+fluid_voice_t *
+fluid_synth_alloc_voice(fluid_synth_t *synth, fluid_sample_t *sample,
+                        int chan, int key, int vel)
+{
+    fluid_return_val_if_fail(sample != NULL, NULL);
+    FLUID_API_ENTRY_CHAN(NULL);
+    FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL));
+
+}
+
+fluid_voice_t *
+fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range)
+{
+    int i, k;
+    fluid_voice_t *voice = NULL;
+    fluid_channel_t *channel = NULL;
+    unsigned int ticks;
+
+    /* 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);
+        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);
+    }
+
+    channel = synth->channel[chan];
+
+    if(fluid_voice_init(voice, sample, zone_range, channel, key, vel,
+                        synth->storeid, ticks, synth->gain) != FLUID_OK)
+    {
+        FLUID_LOG(FLUID_WARN, "Failed to initialize voice");
+        return NULL;
+    }
+
+    /* add the default modulators to the synthesis process. */
+    /* custom_breath2att_modulator is not a default modulator specified in SF
+      it is intended to replace default_vel2att_mod for this channel on demand using
+      API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel.
+    */
+    {
+        int mono = fluid_channel_is_playing_mono(channel);
+        fluid_mod_t *default_mod = synth->default_mod;
+
+        while(default_mod != NULL)
+        {
+            if(
+                /* See if default_mod is the velocity_to_attenuation modulator */
+                fluid_mod_test_identity(default_mod, &default_vel2att_mod) &&
+                // See if a replacement by custom_breath2att_modulator has been demanded
+                // for this channel
+                ((!mono && (channel->mode &  FLUID_CHANNEL_BREATH_POLY)) ||
+                 (mono && (channel->mode &  FLUID_CHANNEL_BREATH_MONO)))
+            )
+            {
+                // Replacement of default_vel2att modulator by custom_breath2att_modulator
+                fluid_voice_add_mod(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT);
+            }
+            else
+            {
+                fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT);
+            }
+
+            // Next default modulator to add to the voice
+            default_mod = default_mod->next;
+        }
+    }
+
+    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_tsynth,
-                                          fluid_voice_t* new_voice)
+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;
+    int excl_class = fluid_voice_gen_value(new_voice, GEN_EXCLUSIVECLASS);
+    int i;
 
-  /* Excl. class 0: No exclusive class */
-  if (excl_class == 0) return;
+    /* 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];
+    /* Kill all notes on the same channel with the same exclusive class */
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        fluid_voice_t *existing_voice = synth->voice[i];
+        int existing_excl_class = fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS);
 
-    /* If voice is playing, on the same channel, has same exclusive
-     * class and is not part of the same noteon event (voice group), then kill it */
+        /* If 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);
-  }
+        if(fluid_voice_is_playing(existing_voice)
+                && fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice)
+                && existing_excl_class == excl_class
+                && fluid_voice_get_id(existing_voice) != fluid_voice_get_id(new_voice))
+        {
+            fluid_voice_kill_excl(existing_voice);
+        }
+    }
 }
 
 /**
@@ -3064,34 +4410,33 @@ fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t* synth,
  * 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
+ * @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_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(synth != NULL);
+    fluid_return_if_fail(voice != NULL);
 //  fluid_return_if_fail (fluid_synth_is_synth_thread (synth));
-  fluid_synth_api_enter(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);
+    /* 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_start(voice);     /* Start the new voice */
     fluid_voice_lock_rvoice(voice);
-  fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);
-  fluid_synth_api_exit(synth);
+    fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);
+    fluid_synth_api_exit(synth);
 }
 
 /**
- * Add a SoundFont loader interface.
+ * Add a SoundFont loader to the synth. This function takes ownership of \c loader
+ * and frees it automatically upon \c synth destruction.
  * @param synth FluidSynth instance
- * @param loader Loader API structure, used directly and should remain allocated
- *   as long as the synth instance is used.
+ * @param loader Loader API structure
  *
  * SoundFont loaders are used to add custom instrument loading to FluidSynth.
  * The caller supplied functions for loading files, allocating presets,
@@ -3099,20 +4444,22 @@ fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice)
  * 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.
+ * @note Should only be called before any SoundFont files are loaded.
  */
 void
-fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader)
+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);
+
+    /* Test if sfont is already loaded */
+    if(synth->sfont == NULL)
+    {
+        synth->loaders = fluid_list_prepend(synth->loaders, loader);
+    }
 
-  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);
+    fluid_synth_api_exit(synth);
 }
 
 /**
@@ -3121,301 +4468,287 @@ fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader)
  * 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 synth FluidSynth 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
+ * @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset())
+ * @return SoundFont ID on success, #FLUID_FAILED on error
  */
 int
-fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets)
+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);
+    fluid_sfont_t *sfont;
+    fluid_list_t *list;
+    fluid_sfloader_t *loader;
+    int sfont_id;
 
-    if (sfont != NULL) {
-      sfont_info = new_fluid_sfont_info (synth, sfont);
-
-      if (!sfont_info)
-      {
-        delete_fluid_sfont (sfont);
-        FLUID_API_RETURN(FLUID_FAILED);
-      }
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(filename != NULL, FLUID_FAILED);
+    fluid_synth_api_enter(synth);
 
-      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 */
+    sfont_id = synth->sfont_id;
 
-      /* reset the presets for all channels if requested */
-      if (reset_presets) fluid_synth_program_reset(synth);
+    if(++sfont_id != FLUID_FAILED)
+    {
+        /* MT NOTE: Loaders list should not change. */
 
-      FLUID_API_RETURN((int)sfont_id);
-    }
-  }
+        for(list = synth->loaders; list; list = fluid_list_next(list))
+        {
+            loader = (fluid_sfloader_t *) fluid_list_get(list);
 
-  FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
-  FLUID_API_RETURN(FLUID_FAILED);
-}
+            sfont = fluid_sfloader_load(loader, filename);
 
-/* 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;
+            if(sfont != NULL)
+            {
+                sfont->refcount++;
+                synth->sfont_id = sfont->id = sfont_id;
 
-  sfont_info = FLUID_NEW (fluid_sfont_info_t);
+                synth->sfont = fluid_list_prepend(synth->sfont, sfont);   /* prepend to list */
 
-  if (!sfont_info)
-  {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return NULL;
-  }
+                /* reset the presets for all channels if requested */
+                if(reset_presets)
+                {
+                    fluid_synth_program_reset(synth);
+                }
 
-  sfont_info->sfont = sfont;
-  sfont_info->synth = synth;
-  sfont_info->refcount = 1;     /* Start with refcount of 1 for owning synth */
-  sfont_info->bankofs = 0;
+                FLUID_API_RETURN(sfont_id);
+            }
+        }
+    }
 
-  return (sfont_info);
+    FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
+    FLUID_API_RETURN(FLUID_FAILED);
 }
 
 /**
  * Unload a SoundFont.
- * @param synth SoundFont instance
+ * @param synth FluidSynth instance
  * @param id ID of SoundFont to unload
  * @param reset_presets TRUE to re-assign presets for all MIDI channels
- * @return FLUID_OK on success, FLUID_FAILED on error
+ * @return #FLUID_OK on success, #FLUID_FAILED on error
  */
 int
-fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets)
+fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets)
 {
-  fluid_sfont_info_t *sfont_info = NULL;
-  fluid_list_t *list;
+    fluid_sfont_t *sfont = 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);
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_synth_api_enter(synth);
 
-    if (fluid_sfont_get_id (sfont_info->sfont) == id)
+    /* remove the SoundFont from the list */
+    for(list = synth->sfont; list; list = fluid_list_next(list))
     {
-      synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info);
-      break;
+        sfont = fluid_list_get(list);
+
+        if(fluid_sfont_get_id(sfont) == id)
+        {
+            synth->sfont = fluid_list_remove(synth->sfont, sfont);
+            break;
+        }
     }
-  }
 
-  if (!list) {
-    FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
-    FLUID_API_RETURN(FLUID_FAILED);
-  }
+    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);
+    /* 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);
+    /* -- Remove synth->sfont list's reference to SoundFont */
+    fluid_synth_sfont_unref(synth, sfont);
 
-  FLUID_API_RETURN(FLUID_OK);
+    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_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 != NULL);     /* Shouldn't happen, programming error if so */
 
-  fluid_return_if_fail (sfont_info != NULL);    /* Shouldn't happen, programming error if so */
+    sfont->refcount--;             /* -- Remove the sfont list's reference */
 
-  if (refcount == 0)                    /* No more references? - Attempt delete */
-  {
-    if (delete_fluid_sfont (sfont_info->sfont) == 0)    /* SoundFont loader can block SoundFont unload */
+    if(sfont->refcount == 0)  /* No more references? - Attempt delete */
     {
-      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);    
-  }
+        if(fluid_sfont_delete_internal(sfont) == 0)      /* SoundFont loader can block SoundFont unload */
+        {
+            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, 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(voiddata, unsigned int msec)
+fluid_synth_sfunload_callback(void *data, unsigned int msec)
 {
-  fluid_sfont_info_t *sfont_info = (fluid_sfont_info_t *)data;
+    fluid_sfont_t *sfont = data;
 
-  if (delete_fluid_sfont (sfont_info->sfont) == 0)
-  {
-    FLUID_FREE (sfont_info);
-    FLUID_LOG (FLUID_DBG, "Unloaded SoundFont");
-    return FALSE;
-  }
-  else return TRUE;
+    if(fluid_sfont_delete_internal(sfont) == 0)
+    {
+        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 synth FluidSynth instance
  * @param id ID of SoundFont to reload
- * @return SoundFont ID on success, FLUID_FAILED on error
+ * @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);
-  }
+fluid_synth_sfreload(fluid_synth_t *synth, int id)
+{
+    char *filename = NULL;
+    fluid_sfont_t *sfont;
+    fluid_sfloader_t *loader;
+    fluid_list_t *list;
+    int index, ret = FLUID_FAILED;
+
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_synth_api_enter(synth);
 
-  /* keep a copy of the SoundFont's filename */
-  FLUID_STRCPY (filename, fluid_sfont_get_name (old_sfont_info->sfont));
+    /* Search for SoundFont and get its index */
+    for(list = synth->sfont, index = 0; list; list = fluid_list_next(list), index++)
+    {
+        sfont = fluid_list_get(list);
 
-  if (fluid_synth_sfunload (synth, id, FALSE) != FLUID_OK)
-    FLUID_API_RETURN(FLUID_FAILED);
+        if(fluid_sfont_get_id(sfont) == id)
+        {
+            break;
+        }
+    }
+
+    if(!list)
+    {
+        FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
+        goto exit;
+    }
 
-  /* MT Note: SoundFont loader list will not change */
+    /* keep a copy of the SoundFont's filename */
+    filename = FLUID_STRDUP(fluid_sfont_get_name(sfont));
 
-  for (list = synth->loaders; list; list = fluid_list_next(list)) {
-    loader = (fluid_sfloader_t*) fluid_list_get(list);
+    if(filename == NULL || fluid_synth_sfunload(synth, id, FALSE) != FLUID_OK)
+    {
+        goto exit;
+    }
 
-    sfont = fluid_sfloader_load(loader, filename);
+    /* MT Note: SoundFont loader list will not change */
 
-    if (sfont != NULL) {
-      sfont->id = id;
+    for(list = synth->loaders; list; list = fluid_list_next(list))
+    {
+        loader = (fluid_sfloader_t *) fluid_list_get(list);
 
-      sfont_info = new_fluid_sfont_info (synth, sfont);
+        sfont = fluid_sfloader_load(loader, filename);
 
-      if (!sfont_info)
-      {
-        delete_fluid_sfont (sfont);
-        FLUID_API_RETURN(FLUID_FAILED);
-      }
+        if(sfont != NULL)
+        {
+            sfont->id = id;
+            sfont->refcount++;
 
-      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 */
+            synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont);  /* insert the sfont at the same index */
 
-      /* reset the presets for all channels */
-      fluid_synth_update_presets(synth);
-      FLUID_API_RETURN(sfont->id);
+            /* reset the presets for all channels */
+            fluid_synth_update_presets(synth);
+            ret = id;
+            goto exit;
+        }
     }
-  }
 
-  FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
-  FLUID_API_RETURN(FLUID_FAILED);  
+    FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
+
+exit:
+    FLUID_FREE(filename);
+    FLUID_API_RETURN(ret);
 }
 
 /**
  * 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
+ * @return New assigned SoundFont ID or #FLUID_FAILED on error
  */
 int
-fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
+fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont)
 {
-  fluid_sfont_info_t *sfont_info;
-  unsigned int sfont_id;
+    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);
 
-  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 = synth->sfont_id;
 
-  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 */
+    if(++sfont_id != FLUID_FAILED)
+    {
+        synth->sfont_id = sfont->id = sfont_id;
+        synth->sfont = fluid_list_prepend(synth->sfont, sfont);        /* prepend to list */
 
-  /* reset the presets for all channels */
-  fluid_synth_program_reset (synth);
+        /* reset the presets for all channels */
+        fluid_synth_program_reset(synth);
+    }
 
-  FLUID_API_RETURN(sfont_id);
+    FLUID_API_RETURN(sfont_id);
 }
 
 /**
  * Remove a SoundFont from the SoundFont stack without deleting it.
  * @param synth FluidSynth instance
  * @param sfont SoundFont to remove
+ * @return #FLUID_OK if \c sfont successfully removed, #FLUID_FAILED otherwise
  *
  * 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
+ * @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)
+int
+fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont)
 {
-  fluid_sfont_info_t *sfont_info;
-  fluid_list_t *list;
+    fluid_sfont_t *sfont_tmp;
+    fluid_list_t *list;
+    int ret = FLUID_FAILED;
 
-  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);
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);
+    fluid_synth_api_enter(synth);
 
-    if (sfont_info->sfont == sfont)
+    /* remove the SoundFont from the list */
+    for(list = synth->sfont; list; list = fluid_list_next(list))
     {
-      synth->sfont_info = fluid_list_remove (synth->sfont_info, sfont_info);
+        sfont_tmp = fluid_list_get(list);
 
-      /* Remove from SoundFont hash regardless of refcount (SoundFont delete is up to caller) */
-      fluid_hashtable_remove (synth->sfont_hash, sfont_info->sfont);
-      break;
+        if(sfont_tmp == sfont)
+        {
+            synth->sfont = fluid_list_remove(synth->sfont, sfont_tmp);
+            ret = FLUID_OK;
+            break;
+        }
     }
-  }
 
-  /* reset the presets for all channels */
-  fluid_synth_program_reset (synth);
-  fluid_synth_api_exit(synth);
+    /* reset the presets for all channels */
+    fluid_synth_program_reset(synth);
+
+    FLUID_API_RETURN(ret);
 }
 
 /**
@@ -3424,14 +4757,14 @@ fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
  * @return Count of loaded SoundFont files.
  */
 int
-fluid_synth_sfcount(fluid_synth_tsynth)
+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);
+    int count;
+
+    fluid_return_val_if_fail(synth != NULL, 0);
+    fluid_synth_api_enter(synth);
+    count = fluid_list_size(synth->sfont);
+    FLUID_API_RETURN(count);
 }
 
 /**
@@ -3440,20 +4773,25 @@ fluid_synth_sfcount(fluid_synth_t* synth)
  * @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
+ * @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_tsynth, unsigned int num)
+fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num)
 {
-  fluid_sfont_t *sfont = NULL;
-  fluid_list_t *list;
+    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, num);
 
-  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);
+    if(list)
+    {
+        sfont = fluid_list_get(list);
+    }
+
+    FLUID_API_RETURN(sfont);
 }
 
 /**
@@ -3462,25 +4800,29 @@ fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num)
  * @param id SoundFont ID
  * @return SoundFont instance or NULL if invalid ID
  *
- * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
+ * @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_synth_get_sfont_by_id(fluid_synth_t *synth, int id)
 {
-  fluid_sfont_t* sfont = NULL;
-  fluid_list_t* list;
+    fluid_sfont_t *sfont = NULL;
+    fluid_list_t *list;
+
+    fluid_return_val_if_fail(synth != NULL, NULL);
+    fluid_synth_api_enter(synth);
 
-  fluid_return_val_if_fail (synth != NULL, NULL);
-  fluid_synth_api_enter(synth);
+    for(list = synth->sfont; list; list = fluid_list_next(list))
+    {
+        sfont = fluid_list_get(list);
 
-  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;
-  }
+        if(fluid_sfont_get_id(sfont) == id)
+        {
+            break;
+        }
+    }
 
-  FLUID_API_RETURN(list ? sfont : NULL);
+    FLUID_API_RETURN(list ? sfont : NULL);
 }
 
 /**
@@ -3490,137 +4832,92 @@ fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id)
  * @return SoundFont instance or NULL if invalid name
  * @since 1.1.0
  *
- * NOTE: Caller should be certain that SoundFont is not deleted (unloaded) for
+ * @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_tsynth, const char *name)
+fluid_synth_get_sfont_by_name(fluid_synth_t *synth, const char *name)
 {
-  fluid_sfont_t* sfont = NULL;
-  fluid_list_t* list;
+    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);
 
-  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; list; list = fluid_list_next(list))
+    {
+        sfont = fluid_list_get(list);
 
-  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;
-  }
+        if(FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0)
+        {
+            break;
+        }
+    }
 
-  FLUID_API_RETURN(list ? sfont : NULL);
+    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.
+ * @return Preset or NULL if no preset active on \c chan
  *
- * NOTE: Should only be called from within synthesis thread, which includes
- * SoundFont loader preset noteon methods.  Not thread safe otherwise.
+ * @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_synth_get_channel_preset(fluid_synth_t *synth, int chan)
 {
-  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';
+    fluid_preset_t *result;
+    fluid_channel_t *channel;
+    FLUID_API_ENTRY_CHAN(NULL);
 
-    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;
+    channel = synth->channel[chan];
+    result = channel->preset;
+    fluid_synth_api_exit(synth);
+    return result;
 }
 
 /**
- * Get list of voices.
+ * Get list of currently playing 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
+ * @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,
+fluid_synth_get_voicelist(fluid_synth_t *synth, fluid_voice_t *buf[], int bufsize,
                           int id)
 {
-  int count = 0;
-  int i;
+    int count = 0;
+    int i;
+
+    fluid_return_if_fail(synth != NULL);
+    fluid_return_if_fail(buf != NULL);
+    fluid_synth_api_enter(synth);
 
-  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];
 
-  for (i = 0; i < synth->polyphony && count < bufsize; i++) {
-    fluid_voice_t* voice = synth->voice[i];
+        if(fluid_voice_is_playing(voice) && (id < 0 || (int)voice->id == id))
+        {
+            buf[count++] = voice;
+        }
+    }
 
-    if (_PLAYING(voice) && (id < 0 || (int)voice->id == id))
-      buf[count++] = voice;
-  }
+    if(count < bufsize)
+    {
+        buf[count] = NULL;
+    }
 
-  if (count < bufsize) buf[count] = NULL;
-  fluid_synth_api_exit(synth);
+    fluid_synth_api_exit(synth);
 }
 
 /**
@@ -3629,56 +4926,94 @@ fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsiz
  * @param on TRUE to enable reverb, FALSE to disable
  */
 void
-fluid_synth_set_reverb_on(fluid_synth_tsynth, int on)
+fluid_synth_set_reverb_on(fluid_synth_t *synth, int on)
 {
-  fluid_return_if_fail (synth != NULL);
+    fluid_return_if_fail(synth != NULL);
+
+    fluid_synth_api_enter(synth);
 
-  fluid_atomic_int_set (&synth->with_reverb, on != 0);
-  fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled,
-                          on != 0, 0.0f);
+    synth->with_reverb = (on != 0);
+    fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled,
+                             on != 0, 0.0f);
+    fluid_synth_api_exit(synth);
 }
 
 /**
  * Activate a reverb preset.
  * @param synth FluidSynth instance
  * @param num Reverb preset number
- * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  *
- * NOTE: Currently private to libfluidsynth.
+ * @note Currently private to libfluidsynth.
  */
 int
-fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num)
+fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned 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;
+    fluid_return_val_if_fail(
+        num < FLUID_N_ELEMENTS(revmodel_preset),
+        FLUID_FAILED
+    );
+
+    fluid_synth_set_reverb(synth, revmodel_preset[num].roomsize,
+                           revmodel_preset[num].damp, revmodel_preset[num].width,
+                           revmodel_preset[num].level);
+    return FLUID_OK;
 }
 
 /**
  * Set reverb parameters.
  * @param synth FluidSynth instance
- * @param roomsize Reverb room size value (0.0-1.2)
+ * @param roomsize Reverb room size value (0.0-1.0)
  * @param damping Reverb damping value (0.0-1.0)
  * @param width Reverb width value (0.0-100.0)
  * @param level Reverb level value (0.0-1.0)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  *
- * NOTE: Not realtime safe and therefore should not be called from synthesis
+ * @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_tsynth, double roomsize, double damping,
+int
+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);
+    return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_ALL,
+                                       roomsize, damping, width, level);
+}
+
+/**
+ * Set reverb roomsize. See fluid_synth_set_reverb() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize)
+{
+    return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_ROOMSIZE, roomsize, 0, 0, 0);
+}
+
+/**
+ * Set reverb damping. See fluid_synth_set_reverb() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping)
+{
+    return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_DAMPING, 0, damping, 0, 0);
+}
+
+/**
+ * Set reverb width. See fluid_synth_set_reverb() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width)
+{
+    return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_WIDTH, 0, 0, width, 0);
+}
+
+/**
+ * Set reverb level. See fluid_synth_set_reverb() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level)
+{
+    return fluid_synth_set_reverb_full(synth, FLUID_REVMODEL_SET_LEVEL, 0, 0, 0, level);
 }
 
 /**
@@ -3689,42 +5024,66 @@ fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping,
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  *
- * NOTE: Not realtime safe and therefore should not be called from synthesis
+ * @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_tsynth, int set, double roomsize,
+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);
+    int ret;
+
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    /* if non of the flags is set, fail */
+    fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED);
 
-  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 */
 
-  /* Synth shadow values are set here so that they will be returned if querried */
+    fluid_synth_api_enter(synth);
+    ret = fluid_synth_set_reverb_full_LOCAL(synth, set, roomsize, damping, width, level);
+    FLUID_API_RETURN(ret);
+}
 
-  fluid_synth_api_enter(synth);
+static int
+fluid_synth_set_reverb_full_LOCAL(fluid_synth_t *synth, int set, double roomsize,
+                                  double damping, double width, double level)
+{
+    int ret;
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
 
-  if (set & FLUID_REVMODEL_SET_ROOMSIZE)
-    fluid_atomic_float_set (&synth->reverb_roomsize, roomsize);
+    if(set & FLUID_REVMODEL_SET_ROOMSIZE)
+    {
+        synth->reverb_roomsize = roomsize;
+    }
 
-  if (set & FLUID_REVMODEL_SET_DAMPING)
-    fluid_atomic_float_set (&synth->reverb_damping, damping);
+    if(set & FLUID_REVMODEL_SET_DAMPING)
+    {
+        synth->reverb_damping = damping;
+    }
 
-  if (set & FLUID_REVMODEL_SET_WIDTH)
-    fluid_atomic_float_set (&synth->reverb_width, width);
+    if(set & FLUID_REVMODEL_SET_WIDTH)
+    {
+        synth->reverb_width = width;
+    }
 
-  if (set & FLUID_REVMODEL_SET_LEVEL)
-    fluid_atomic_float_set (&synth->reverb_level, level);
+    if(set & FLUID_REVMODEL_SET_LEVEL)
+    {
+        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);
+    param[0].i = set;
+    param[1].real = roomsize;
+    param[2].real = damping;
+    param[3].real = width;
+    param[4].real = level;
+    /* finally enqueue an rvoice event to the mixer to actual update reverb */
+    ret = fluid_rvoice_eventhandler_push(synth->eventhandler,
+                                         fluid_rvoice_mixer_set_reverb_params,
+                                         synth->eventhandler->mixer,
+                                         param);
+    return ret;
 }
 
 /**
@@ -3733,13 +5092,13 @@ fluid_synth_set_reverb_full(fluid_synth_t* synth, int set, double roomsize,
  * @return Reverb room size (0.0-1.2)
  */
 double
-fluid_synth_get_reverb_roomsize(fluid_synth_tsynth)
+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);
+    double result;
+    fluid_return_val_if_fail(synth != NULL, 0.0);
+    fluid_synth_api_enter(synth);
+    result = synth->reverb_roomsize;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3748,14 +5107,14 @@ fluid_synth_get_reverb_roomsize(fluid_synth_t* synth)
  * @return Reverb damping value (0.0-1.0)
  */
 double
-fluid_synth_get_reverb_damp(fluid_synth_tsynth)
+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);
+    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);
+    result = synth->reverb_damping;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3764,14 +5123,14 @@ fluid_synth_get_reverb_damp(fluid_synth_t* synth)
  * @return Reverb level value (0.0-1.0)
  */
 double
-fluid_synth_get_reverb_level(fluid_synth_tsynth)
+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);
+    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);
+    result = synth->reverb_level;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3780,14 +5139,14 @@ fluid_synth_get_reverb_level(fluid_synth_t* synth)
  * @return Reverb width value (0.0-100.0)
  */
 double
-fluid_synth_get_reverb_width(fluid_synth_tsynth)
+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);
+    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);
+    result = synth->reverb_width;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3795,20 +5154,21 @@ fluid_synth_get_reverb_width(fluid_synth_t* synth)
  * @param synth FluidSynth instance
  * @param on TRUE to enable chorus, FALSE to disable
  */
-void 
-fluid_synth_set_chorus_on(fluid_synth_tsynth, int on)
+void
+fluid_synth_set_chorus_on(fluid_synth_t *synth, int on)
 {
-  fluid_return_if_fail (synth != NULL);
-  fluid_synth_api_enter(synth);
+    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);
+    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.
+ * Set chorus parameters. It should be turned on with fluid_synth_set_chorus_on().
+ * Keep in mind, that the needed CPU time is proportional to 'nr'.
  * @param synth FluidSynth instance
  * @param nr Chorus voice count (0-99, CPU time consumption proportional to
  *   this value)
@@ -3817,13 +5177,58 @@ fluid_synth_set_chorus_on(fluid_synth_t* synth, int on)
  * @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)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
-void
-fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
-                       double speed, double depth_ms, int type)
+int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level,
+                           double speed, double depth_ms, int type)
+{
+    return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_ALL, nr, level, speed,
+                                       depth_ms, type);
+}
+
+/**
+ * Set the chorus voice count. See fluid_synth_set_chorus() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr)
+{
+    return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_NR, nr, 0, 0, 0, 0);
+}
+
+/**
+ * Set the chorus level. See fluid_synth_set_chorus() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level)
+{
+    return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_LEVEL, 0, level, 0, 0, 0);
+}
+
+/**
+ * Set the chorus speed. See fluid_synth_set_chorus() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed)
+{
+    return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_SPEED, 0, 0, speed, 0, 0);
+}
+
+/**
+ * Set the chorus depth. See fluid_synth_set_chorus() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms)
+{
+    return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_DEPTH, 0, 0, 0, depth_ms, 0);
+}
+
+/**
+ * Set the chorus type. See fluid_synth_set_chorus() for further info.
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type)
 {
-  fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_ALL, nr, level, speed,
-                               depth_ms, type);
+    return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_TYPE, 0, 0, 0, 0, type);
 }
 
 /**
@@ -3837,40 +5242,70 @@ fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
  * @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)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_set_chorus_full(fluid_synth_tsynth, int set, int nr, double level,
+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);
+    int ret;
+
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    /* if non of the flags is set, fail */
+    fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED);
+
+    /* Synth shadow values are set here so that they will be returned if queried */
+    fluid_synth_api_enter(synth);
+
+    ret = fluid_synth_set_chorus_full_LOCAL(synth, set, nr, level, speed, depth_ms, type);
 
-  if (!(set & FLUID_CHORUS_SET_ALL))
-    set = FLUID_CHORUS_SET_ALL;
+    FLUID_API_RETURN(ret);
+}
+
+static int
+fluid_synth_set_chorus_full_LOCAL(fluid_synth_t *synth, int set, int nr, double level,
+                                  double speed, double depth_ms, int type)
+{
+    int ret;
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
 
-  /* Synth shadow values are set here so that they will be returned if queried */
-  fluid_synth_api_enter(synth);
+    if(set & FLUID_CHORUS_SET_NR)
+    {
+        synth->chorus_nr = nr;
+    }
 
-  if (set & FLUID_CHORUS_SET_NR)
-    fluid_atomic_int_set (&synth->chorus_nr, nr);
+    if(set & FLUID_CHORUS_SET_LEVEL)
+    {
+        synth->chorus_level = level;
+    }
 
-  if (set & FLUID_CHORUS_SET_LEVEL)
-    fluid_atomic_float_set (&synth->chorus_level, level);
+    if(set & FLUID_CHORUS_SET_SPEED)
+    {
+        synth->chorus_speed = speed;
+    }
 
-  if (set & FLUID_CHORUS_SET_SPEED)
-    fluid_atomic_float_set (&synth->chorus_speed, speed);
+    if(set & FLUID_CHORUS_SET_DEPTH)
+    {
+        synth->chorus_depth = depth_ms;
+    }
 
-  if (set & FLUID_CHORUS_SET_DEPTH)
-    fluid_atomic_float_set (&synth->chorus_depth, depth_ms);
+    if(set & FLUID_CHORUS_SET_TYPE)
+    {
+        synth->chorus_type = type;
+    }
 
-  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);
+    param[0].i = set;
+    param[1].i = nr;
+    param[2].real = level;
+    param[3].real = speed;
+    param[4].real = depth_ms;
+    param[5].i = type;
+    ret = fluid_rvoice_eventhandler_push(synth->eventhandler,
+                                         fluid_rvoice_mixer_set_chorus_params,
+                                         synth->eventhandler->mixer,
+                                         param);
 
-  FLUID_API_RETURN(FLUID_OK);
+    return (ret);
 }
 
 /**
@@ -3879,14 +5314,14 @@ fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level,
  * @return Chorus voice count (0-99)
  */
 int
-fluid_synth_get_chorus_nr(fluid_synth_tsynth)
+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);
+    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);
+    result = synth->chorus_nr;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3895,14 +5330,14 @@ fluid_synth_get_chorus_nr(fluid_synth_t* synth)
  * @return Chorus level value (0.0-10.0)
  */
 double
-fluid_synth_get_chorus_level(fluid_synth_tsynth)
+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);
+    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);
+    result = synth->chorus_level;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3911,14 +5346,14 @@ fluid_synth_get_chorus_level(fluid_synth_t* synth)
  * @return Chorus speed in Hz (0.29-5.0)
  */
 double
-fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth)
+fluid_synth_get_chorus_speed(fluid_synth_t *synth)
 {
-  double result;
-  fluid_return_val_if_fail (synth != NULL, 0.0);
-  fluid_synth_api_enter(synth);
+    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);
+    result = synth->chorus_speed;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3927,14 +5362,14 @@ fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth)
  * @return Chorus depth
  */
 double
-fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth)
+fluid_synth_get_chorus_depth(fluid_synth_t *synth)
 {
-  double result;
-  fluid_return_val_if_fail (synth != NULL, 0.0);
-  fluid_synth_api_enter(synth);
+    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);
+    result = synth->chorus_depth;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -3943,14 +5378,14 @@ fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth)
  * @return Chorus waveform type (#fluid_chorus_mod)
  */
 int
-fluid_synth_get_chorus_type(fluid_synth_tsynth)
+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);
+    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);
+    result = synth->chorus_type;
+    FLUID_API_RETURN(result);
 }
 
 /*
@@ -3962,28 +5397,42 @@ fluid_synth_get_chorus_type(fluid_synth_t* synth)
  * several voice processes, for example a stereo sample.  Don't
  * release those...
  */
-static void
-fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_tsynth, int chan,
-                                             int key)
+void
+fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan,
+        int key)
 {
-  int i;
-  fluid_voice_t* voice;
+    int i;
+    fluid_voice_t *voice;
+
+    /* storeid is a parameter for fluid_voice_init() */
+    synth->storeid = synth->noteid++;
 
-  synth->storeid = synth->noteid++;
+    /* for "monophonic playing" key is the previous sustained note
+      if it exists (0 to 127) or INVALID_NOTE otherwise */
+    if(key == INVALID_NOTE)
+    {
+        return;
+    }
+
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-  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);
+        if(fluid_voice_is_playing(voice)
+                && (fluid_voice_get_channel(voice) == chan)
+                && (fluid_voice_get_key(voice) == key)
+                && (fluid_voice_get_id(voice) != synth->noteid))
+        {
+            /* Id of voices that was sustained by sostenuto */
+            if(fluid_voice_is_sostenuto(voice))
+            {
+                synth->storeid = fluid_voice_get_id(voice);
+            }
+
+            /* Force the voice into release stage (pedaling is ignored) */
+            fluid_voice_release(voice);
+        }
     }
-  }
 }
 
 /**
@@ -3991,29 +5440,36 @@ fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan,
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  */
 int
-fluid_synth_set_interp_method(fluid_synth_tsynth, int chan, int interp_method)
+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);
+    int i;
 
-  if (synth->channel[0] == NULL) {
-    FLUID_LOG (FLUID_ERR, "Channels don't exist (yet)!");
-    FLUID_API_RETURN(FLUID_FAILED);
-  }
+    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);
-  }
+    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);
+    FLUID_API_RETURN(FLUID_OK);
 };
 
 /**
@@ -4022,14 +5478,14 @@ fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method)
  * @return Count of MIDI channels
  */
 int
-fluid_synth_count_midi_channels(fluid_synth_tsynth)
+fluid_synth_count_midi_channels(fluid_synth_t *synth)
 {
-  int result;
-  fluid_return_val_if_fail (synth != NULL, 0);
-  fluid_synth_api_enter(synth);
+    int result;
+    fluid_return_val_if_fail(synth != NULL, 0);
+    fluid_synth_api_enter(synth);
 
-  result = synth->midi_channels;
-  FLUID_API_RETURN(result);
+    result = synth->midi_channels;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -4038,14 +5494,14 @@ fluid_synth_count_midi_channels(fluid_synth_t* synth)
  * @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc)
  */
 int
-fluid_synth_count_audio_channels(fluid_synth_tsynth)
+fluid_synth_count_audio_channels(fluid_synth_t *synth)
 {
-  int result;
-  fluid_return_val_if_fail (synth != NULL, 0);
-  fluid_synth_api_enter(synth);
+    int result;
+    fluid_return_val_if_fail(synth != NULL, 0);
+    fluid_synth_api_enter(synth);
 
-  result = synth->audio_channels;
-  FLUID_API_RETURN(result);
+    result = synth->audio_channels;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -4056,14 +5512,14 @@ fluid_synth_count_audio_channels(fluid_synth_t* synth)
  * @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc)
  */
 int
-fluid_synth_count_audio_groups(fluid_synth_tsynth)
+fluid_synth_count_audio_groups(fluid_synth_t *synth)
 {
-  int result;
-  fluid_return_val_if_fail (synth != NULL, 0);
-  fluid_synth_api_enter(synth);
+    int result;
+    fluid_return_val_if_fail(synth != NULL, 0);
+    fluid_synth_api_enter(synth);
 
-  result = synth->audio_groups;
-  FLUID_API_RETURN(result);
+    result = synth->audio_groups;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -4072,14 +5528,30 @@ fluid_synth_count_audio_groups(fluid_synth_t* synth)
  * @return Count of allocated effects channels
  */
 int
-fluid_synth_count_effects_channels(fluid_synth_t* synth)
+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 total number of allocated effects units.
+ * @param synth FluidSynth instance
+ * @return Count of allocated effects units
+ */
+int
+fluid_synth_count_effects_groups(fluid_synth_t *synth)
 {
-  int result;
-  fluid_return_val_if_fail (synth != NULL, 0);
-  fluid_synth_api_enter(synth);
+    int result;
+    fluid_return_val_if_fail(synth != NULL, 0);
+    fluid_synth_api_enter(synth);
 
-  result = synth->effects_channels;
-  FLUID_API_RETURN(result);
+    result = synth->effects_groups;
+    FLUID_API_RETURN(result);
 }
 
 /**
@@ -4088,138 +5560,139 @@ fluid_synth_count_effects_channels(fluid_synth_t* synth)
  * @return Estimated CPU load value in percent (0-100)
  */
 double
-fluid_synth_get_cpu_load(fluid_synth_tsynth)
+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);
+    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_tsynth, int bank, int prog)
+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;
+    if((synth->tuning == NULL) ||
+            (synth->tuning[bank] == NULL) ||
+            (synth->tuning[bank][prog] == NULL))
+    {
+        return NULL;
+    }
 
-  return synth->tuning[bank][prog];
+    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_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;
+    fluid_tuning_t *old_tuning;
+
+    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;
+        }
 
-  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 **));
     }
-    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;
+    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 *));
     }
-    FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*));
-  }
 
-  old_tuning = synth->tuning[bank][prog];
-  synth->tuning[bank][prog] = tuning;
+    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);
+    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;
+    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_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];
+    fluid_channel_t *channel;
+    int old_tuning_unref = 0;
+    int i;
 
-    if (fluid_channel_get_tuning (channel) == old_tuning)
+    for(i = 0; i < synth->midi_channels; i++)
     {
-      old_tuning_unref++;
-      if (new_tuning) fluid_tuning_ref (new_tuning);    /* ++ ref new tuning for channel */
-      fluid_channel_set_tuning (channel, new_tuning);
+        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 */
+            }
 
-      if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, 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);
     }
-  }
 
-  /* 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;
+    if(!unref_new || !new_tuning)
+    {
+        return;
+    }
 
-  fluid_tuning_unref (new_tuning, 1);
+    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_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];
+    fluid_voice_t *voice;
+    int i;
 
-    if (_ON (voice) && (voice->channel == channel))
+    for(i = 0; i < synth->polyphony; i++)
     {
-      fluid_voice_calculate_gen_pitch (voice);
-      fluid_voice_update_param (voice, GEN_PITCH);
-    }
-  }
-}
+        voice = synth->voice[i];
 
-/**
- * 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);
+        if(fluid_voice_is_on(voice) && (voice->channel == channel))
+        {
+            fluid_voice_calculate_gen_pitch(voice);
+            fluid_voice_update_param(voice, GEN_PITCH);
+        }
+    }
 }
 
 /**
@@ -4230,58 +5703,48 @@ fluid_synth_create_key_tuning(fluid_synth_t* synth, int bank, int prog,
  * @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.
+ *   Pass NULL to create a equal 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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.1.0
  */
 int
-fluid_synth_activate_key_tuning(fluid_synth_tsynth, int bank, int prog,
-                                const char* name, const double* pitch, int apply)
+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_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(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);
+    fluid_synth_api_enter(synth);
 
-  tuning = new_fluid_tuning (name, bank, prog);
+    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);
-}
+    if(tuning)
+    {
+        if(pitch)
+        {
+            fluid_tuning_set_all(tuning, pitch);
+        }
 
-/**
- * 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);
+        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);
 }
 
 /**
@@ -4295,34 +5758,41 @@ fluid_synth_create_octave_tuning(fluid_synth_t* synth, int bank, int prog,
  *   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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.1.0
  */
 int
-fluid_synth_activate_octave_tuning(fluid_synth_tsynth, int bank, int prog,
-                                   const char* name, const double* pitch, int apply)
+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_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_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);
+    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 (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;
+        if(retval == FLUID_FAILED)
+        {
+            fluid_tuning_unref(tuning, 1);
+        }
+    }
+    else
+    {
+        retval = FLUID_FAILED;
+    }
 
-  FLUID_API_RETURN(retval);
+    FLUID_API_RETURN(retval);
 }
 
 /**
@@ -4336,68 +5806,60 @@ fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog,
  *   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
+ * @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
+ * @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_tsynth, int bank, int prog,
-                       int len, const int *key, const doublepitch, int apply)
+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_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);
+    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);
 
-  old_tuning = fluid_synth_get_tuning (synth, bank, prog);
+    fluid_synth_api_enter(synth);
 
-  if (old_tuning)
-    new_tuning = fluid_tuning_duplicate (old_tuning);
-  else new_tuning = new_fluid_tuning ("Unnamed", bank, prog);
+    old_tuning = fluid_synth_get_tuning(synth, bank, prog);
 
-  if (new_tuning)
-  {
-    for (i = 0; i < len; i++)
-      fluid_tuning_set_pitch (new_tuning, key[i], pitch[i]);
+    if(old_tuning)
+    {
+        new_tuning = fluid_tuning_duplicate(old_tuning);
+    }
+    else
+    {
+        new_tuning = new_fluid_tuning("Unnamed", bank, prog);
+    }
 
-    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;
+    if(new_tuning)
+    {
+        for(i = 0; i < len; i++)
+        {
+            fluid_tuning_set_pitch(new_tuning, key[i], pitch[i]);
+        }
 
-  FLUID_API_RETURN(retval);
-}
+        retval = fluid_synth_replace_tuning_LOCK(synth, new_tuning, bank, prog, apply);
 
-/**
- * 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);
+        if(retval == FLUID_FAILED)
+        {
+            fluid_tuning_unref(new_tuning, 1);
+        }
+    }
+    else
+    {
+        retval = FLUID_FAILED;
+    }
+
+    FLUID_API_RETURN(retval);
 }
 
 /**
@@ -4407,90 +5869,84 @@ fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int bank, int prog)
  * @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
+ * @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
+ * @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_tsynth, int chan, int bank, int prog,
+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_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_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);
 
-  FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    tuning = fluid_synth_get_tuning(synth, bank, prog);
 
-  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 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_synth_replace_tuning_LOCK(synth, tuning, bank, prog, FALSE);
+        }
+    }
 
-  if (tuning) fluid_tuning_ref (tuning);  /* ++ ref for outside of lock */
+    if(tuning)
+    {
+        fluid_tuning_ref(tuning);    /* ++ ref for outside of lock */
+    }
 
-  if (!tuning) 
-    FLUID_API_RETURN(FLUID_FAILED);
+    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_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_tuning_unref(tuning, 1);    /* -- unref for outside of lock */
 
-  FLUID_API_RETURN(retval);
+    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_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];
+    fluid_tuning_t *old_tuning;
+    fluid_channel_t *channel;
 
-  old_tuning = fluid_channel_get_tuning (channel);
-  fluid_channel_set_tuning (channel, tuning);   /* !! Takes over callers reference */
+    channel = synth->channel[chan];
 
-  if (apply) fluid_synth_update_voice_tuning_LOCAL (synth, channel);
+    old_tuning = fluid_channel_get_tuning(channel);
+    fluid_channel_set_tuning(channel, tuning);    /* !! Takes over callers reference */
 
-  /* Send unref old tuning event */
-  if (old_tuning)
-  {
-    fluid_tuning_unref (old_tuning, 1);
-  }
+    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);
+    return FLUID_OK;
 }
 
 /**
@@ -4498,19 +5954,19 @@ fluid_synth_reset_tuning(fluid_synth_t* synth, int chan)
  * @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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
  * @since 1.1.0
  */
 int
-fluid_synth_deactivate_tuning(fluid_synth_tsynth, int chan, int apply)
+fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply)
 {
-  int retval = FLUID_OK;
+    int retval = FLUID_OK;
 
-  FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
 
-  retval = fluid_synth_set_tuning_LOCAL (synth, chan, NULL, apply);
+    retval = fluid_synth_set_tuning_LOCAL(synth, chan, NULL, apply);
 
-  FLUID_API_RETURN(retval);
+    FLUID_API_RETURN(retval);
 }
 
 /**
@@ -4518,12 +5974,12 @@ fluid_synth_deactivate_tuning(fluid_synth_t* synth, int chan, int apply)
  * @param synth FluidSynth instance
  */
 void
-fluid_synth_tuning_iteration_start(fluid_synth_tsynth)
+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);
+    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);
 }
 
 /**
@@ -4534,526 +5990,1039 @@ fluid_synth_tuning_iteration_start(fluid_synth_t* synth)
  * @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)
+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)
+        {
+            FLUID_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;
+}
+
+/**
+ * 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
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ *
+ * 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_gen(fluid_synth_t *synth, int chan, int param, float value)
+{
+    return fluid_synth_set_gen2(synth, chan, param, value, FALSE, FALSE);
+}
+
+/**
+ * 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 FALSE to assign a relative value, TRUE to assign an absolute value
+ * @param normalized FALSE if value is specified in the native units of the generator,
+ *   TRUE 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
+ *
+ * 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);
+}
+
+/* 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(fluid_voice_get_channel(voice) == chan)
+        {
+            fluid_voice_set_param(voice, param, value, absolute);
+        }
+    }
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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 KEY_PRESSURE:
+        return fluid_synth_key_pressure(synth, chan,
+                                        fluid_midi_event_get_key(event),
+                                        fluid_midi_event_get_value(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);
+
+    case MIDI_TEXT:
+    case MIDI_LYRIC:
+    case MIDI_SET_TEMPO:
+        return FLUID_OK;
+    }
+
+    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(fluid_voice_is_on(voice) && (fluid_voice_get_id(voice) == id))
+        {
+            fluid_voice_noteoff(voice);
+        }
+    }
+}
+
+/**
+ * Offset the bank numbers of a loaded SoundFont, i.e.\ subtract
+ * \c offset from any bank number when assigning instruments.
+ *
+ * @param synth FluidSynth instance
+ * @param sfont_id ID of a loaded SoundFont
+ * @param offset Bank offset value to apply to all instruments
+ * @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise
+ */
+int
+fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset)
+{
+    fluid_sfont_t *sfont;
+    fluid_list_t *list;
+
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_synth_api_enter(synth);
+
+    for(list = synth->sfont; list; list = fluid_list_next(list))
+    {
+        sfont = fluid_list_get(list);
+
+        if(fluid_sfont_get_id(sfont) == sfont_id)
+        {
+            sfont->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)
 {
-  void *pval;
-  int b = 0, p = 0;
+    fluid_sfont_t *sfont;
+    fluid_list_t *list;
+    int offset = 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;
+    fluid_return_val_if_fail(synth != NULL, 0);
+    fluid_synth_api_enter(synth);
 
-  if (!synth->tuning)
-  {
-    FLUID_API_RETURN(0);
-  }
+    for(list = synth->sfont; list; list = fluid_list_next(list))
+    {
+        sfont = fluid_list_get(list);
 
-  for (; b < 128; b++, p = 0)
-  {
-    if (synth->tuning[b] == NULL) continue;
+        if(fluid_sfont_get_id(sfont) == sfont_id)
+        {
+            offset = sfont->bankofs;
+            break;
+        }
+    }
 
-    for (; p < 128; p++)
+    if(!list)
     {
-      if (synth->tuning[b][p] == NULL) continue;
+        FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id);
+        FLUID_API_RETURN(0);
+    }
 
-      *bank = b;
-      *prog = p;
+    FLUID_API_RETURN(offset);
+}
 
-      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));
+void
+fluid_synth_api_enter(fluid_synth_t *synth)
+{
+    if(synth->use_mutex)
+    {
+        fluid_rec_mutex_lock(synth->mutex);
+    }
 
-      FLUID_API_RETURN(1);
+    if(!synth->public_api_count)
+    {
+        fluid_synth_check_finished_voices(synth);
     }
-  }
 
-  FLUID_API_RETURN(0);
+    synth->public_api_count++;
 }
 
-/**
- * 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)
+void fluid_synth_api_exit(fluid_synth_t *synth)
 {
-  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);
+    synth->public_api_count--;
 
-  if (tuning)
-  {
-    if (name)
+    if(!synth->public_api_count)
     {
-      snprintf (name, len - 1, "%s", fluid_tuning_get_name (tuning));
-      name[len - 1] = 0;  /* make sure the string is null terminated */
+        fluid_rvoice_eventhandler_flush(synth->eventhandler);
     }
 
-    if (pitch)
-      FLUID_MEMCPY (pitch, fluid_tuning_get_all (tuning), 128 * sizeof (double));
-  }
+    if(synth->use_mutex)
+    {
+        fluid_rec_mutex_unlock(synth->mutex);
+    }
 
-  FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED);
 }
 
 /**
- * Get settings assigned to a synth.
+ * Set midi channel type
  * @param synth FluidSynth instance
- * @return FluidSynth settings which are assigned to the synth
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param type MIDI channel type (#fluid_midi_channel_type)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ * @since 1.1.4
  */
-fluid_settings_t *
-fluid_synth_get_settings(fluid_synth_t* synth)
+int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type)
 {
-  fluid_return_val_if_fail (synth != NULL, NULL);
+    fluid_return_val_if_fail((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
 
-  return synth->settings;
+    synth->channel[chan]->channel_type = type;
+
+    FLUID_API_RETURN(FLUID_OK);
 }
 
+#ifdef LADSPA
 /**
- * Convenience function to set a string setting of a synth.
+ * Return the LADSPA effects instance used by FluidSynth
+ *
  * @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
+ * @return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active
  */
-int
-fluid_synth_setstr(fluid_synth_t* synth, const char* name, const char* str)
+fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)
 {
-  fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
-  fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(synth != NULL, NULL);
 
-  return fluid_settings_setstr(synth->settings, name, str);
+    return synth->ladspa_fx;
 }
+#endif
 
 /**
- * Convenience function to duplicate a string setting of a synth.
+ * Configure a general-purpose IIR biquad filter.
+ *
+ * This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard.
+ * By default this filter is off (#FLUID_IIR_DISABLED).
+ *
  * @param synth FluidSynth instance
- * @param 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
+ * @param type Type of the IIR filter to use (see #fluid_iir_filter_type)
+ * @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags)
  *
- * The returned string is owned by the caller and should be freed with free()
- * when finished with it.
+ * @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED
  */
-int
-fluid_synth_dupstr(fluid_synth_t* synth, const char* name, char** str)
+int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags)
 {
-  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);
-}
+    int i;
+    fluid_voice_t *voice;
 
-/**
- * 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);
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+    fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED);
 
-  return fluid_settings_setnum(synth->settings, name, val);
-}
+    fluid_synth_api_enter(synth);
 
-/**
- * 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);
+    synth->custom_filter_type = type;
+    synth->custom_filter_flags = flags;
 
-  return fluid_settings_getnum(synth->settings, name, val);
-}
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
 
-/**
- * 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);
+        fluid_voice_set_custom_filter(voice, type, flags);
+    }
 
-  return fluid_settings_setint(synth->settings, name, val);
+    FLUID_API_RETURN(FLUID_OK);
 }
 
 /**
- * Convenience function to get an integer setting of a synth.
+ * Set the important channels for voice overflow priority calculation.
+ *
  * @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
+ * @param channels comma-separated list of channel numbers
+ * @return #FLUID_OK on success, otherwise #FLUID_FAILED
  */
-int
-fluid_synth_getint(fluid_synth_t* synth, const char* name, int* val)
+static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels)
 {
-  fluid_return_val_if_fail (synth != NULL, FLUID_FAILED);
-  fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
+    int i;
+    int retval = FLUID_FAILED;
+    int *values = NULL;
+    int num_values;
+    fluid_overflow_prio_t *scores;
 
-  return fluid_settings_getint(synth->settings, name, val);
-}
+    fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
 
-/**
- * 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);
+    scores = &synth->overflow;
 
-  fluid_synth_set_gen_LOCAL (synth, chan, param, value, FALSE);
+    if(scores->num_important_channels < synth->midi_channels)
+    {
+        scores->important_channels = FLUID_REALLOC(scores->important_channels,
+                                     sizeof(*scores->important_channels) * synth->midi_channels);
 
-  FLUID_API_RETURN(FLUID_OK);
-}
+        if(scores->important_channels == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            goto exit;
+        }
 
-/* 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;
+        scores->num_important_channels = synth->midi_channels;
+    }
 
-  fluid_channel_set_gen (synth->channel[chan], param, value, absolute);
+    FLUID_MEMSET(scores->important_channels, FALSE,
+                 sizeof(*scores->important_channels) * scores->num_important_channels);
 
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+    if(channels != NULL)
+    {
+        values = FLUID_ARRAY(int, synth->midi_channels);
 
-    if (voice->chan == chan)
-      fluid_voice_set_param (voice, param, value, absolute);
-  }
-}
+        if(values == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            goto exit;
+        }
 
-/**
- * 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);
+        /* Every channel given in the comma-separated list of channel numbers
+         * is set to TRUE, i.e. flagging it as "important". Channel numbers are
+         * 1-based. */
+        num_values = fluid_settings_split_csv(channels, values, synth->midi_channels);
 
-  v = normalized ? fluid_gen_scale(param, value) : value;
+        for(i = 0; i < num_values; i++)
+        {
+            if(values[i] > 0 && values[i] <= synth->midi_channels)
+            {
+                scores->important_channels[values[i] - 1] = TRUE;
+            }
+        }
+    }
 
-  fluid_synth_set_gen_LOCAL (synth, chan, param, v, absolute);
+    retval = FLUID_OK;
 
-  FLUID_API_RETURN(FLUID_OK);
+exit:
+    FLUID_FREE(values);
+    return retval;
 }
 
-/**
- * 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
+/*
+ * Handler for synth.overflow.important-channels setting.
  */
-float
-fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param)
+static void fluid_synth_handle_important_channels(void *data, const char *name,
+        const char *value)
 {
-  float result;
-  fluid_return_val_if_fail (param >= 0 && param < GEN_LAST, FLUID_FAILED);
-  FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    fluid_synth_t *synth = (fluid_synth_t *)data;
 
-  result = fluid_channel_get_gen(synth->channel[chan], param);
-  FLUID_API_RETURN(result);
+    fluid_synth_api_enter(synth);
+    fluid_synth_set_important_channels(synth, value);
+    fluid_synth_api_exit(synth);
 }
 
+
+/**  API legato mode *********************************************************/
+
 /**
- * Assign a MIDI router to a synth.
- * @param synth FluidSynth instance
- * @param router MIDI router to assign to the synth
+ * Sets the legato mode of a channel.
  *
- * NOTE: This should only be done once and prior to using the synth.
+ * @param synth the synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode.
+ *
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
+ *   - \a legatomode is invalid.
  */
-void
-fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router)
+int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode)
 {
-  fluid_return_if_fail (synth != NULL);
-  fluid_synth_api_enter(synth);
-
-  synth->midi_router = router;
-  fluid_synth_api_exit(synth);
-};
+    /* checks parameters first */
+    fluid_return_val_if_fail(legatomode >= 0, FLUID_FAILED);
+    fluid_return_val_if_fail(legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    /**/
+    synth->channel[chan]->legatomode = legatomode;
+    /**/
+    FLUID_API_RETURN(FLUID_OK);
+}
 
 /**
- * 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
+ * Gets the legato mode of a channel.
+ *
+ * @param synth the synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode.
+ *
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
+ *   - \a legatomode is NULL.
  */
-int
-fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event)
+int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode)
 {
-  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));
+    /* checks parameters first */
+    fluid_return_val_if_fail(legatomode != NULL, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    /**/
+    * legatomode = synth->channel[chan]->legatomode;
+    /**/
+    FLUID_API_RETURN(FLUID_OK);
+}
 
-      case PITCH_BEND:
-       return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event));
+/**  API portamento mode *********************************************************/
 
-      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;
+/**
+ * Sets the portamento mode of a channel.
+ *
+ * @param synth the synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode.
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
+ *   - \a portamentomode is invalid.
+ */
+int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan,
+                                    int portamentomode)
+{
+    /* checks parameters first */
+    fluid_return_val_if_fail(portamentomode >= 0, FLUID_FAILED);
+    fluid_return_val_if_fail(portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    /**/
+    synth->channel[chan]->portamentomode = portamentomode;
+    /**/
+    FLUID_API_RETURN(FLUID_OK);
 }
 
 /**
- * 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
+ * Gets the portamento mode of a channel.
  *
- * NOTE: Should only be called from within synthesis thread, which includes
- * SoundFont loader preset noteon method.
+ * @param synth the synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode.
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
+ *   - \a portamentomode is NULL.
+ */
+int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan,
+                                    int *portamentomode)
+{
+    /* checks parameters first */
+    fluid_return_val_if_fail(portamentomode != NULL, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    /**/
+    * portamentomode = synth->channel[chan]->portamentomode;
+    /**/
+    FLUID_API_RETURN(FLUID_OK);
+}
+
+/**  API breath mode *********************************************************/
+
+/**
+ * Sets the breath mode of a channel.
+ *
+ * @param synth the synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param breathmode The breath mode as indicated by #fluid_channel_breath_flags.
+ *
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
  */
-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 fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode)
 {
-  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);
+    /* checks parameters first */
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    /**/
+    fluid_channel_set_breath_info(synth->channel[chan], breathmode);
+    /**/
+    FLUID_API_RETURN(FLUID_OK);
 }
 
 /**
- * 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
+ * Gets the breath mode of a channel.
  *
- * 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.
+ * @param synth the synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags.
+ *
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
+ *   - \a breathmode is NULL.
  */
-int
-fluid_synth_stop(fluid_synth_t* synth, unsigned int id)
+int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode)
 {
-  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);
+    /* checks parameters first */
+    fluid_return_val_if_fail(breathmode != NULL, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+    /**/
+    * breathmode = fluid_channel_get_breath_info(synth->channel[chan]);
+    /**/
+    FLUID_API_RETURN(FLUID_OK);
 }
 
-/* Local synthesis thread variant of fluid_synth_stop */
+/**  API Poly/mono mode ******************************************************/
+
+/*
+ * Resets a basic channel group of MIDI channels.
+ * @param synth the synth instance.
+ * @param chan the beginning channel of the group.
+ * @param nbr_chan the number of channel in the group.
+*/
 static void
-fluid_synth_stop_LOCAL (fluid_synth_t *synth, unsigned int id)
+fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan)
 {
-  fluid_voice_t* voice;
-  int i;
-
-  for (i = 0; i < synth->polyphony; i++) {
-    voice = synth->voice[i];
+    int i;
 
-    if (_ON(voice) && (fluid_voice_get_id (voice) == id))
-      fluid_voice_noteoff(voice);
-  }
+    for(i = chan; i < chan + nbr_chan; i++)
+    {
+        fluid_channel_reset_basic_channel_info(synth->channel[i]);
+        synth->channel[i]->mode_val = 0;
+    }
 }
 
 /**
- * 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
+ * Disables and unassigns all channels from a basic channel group.
+ *
+ * @param synth The synth instance.
+ * @param chan The basic channel of the group to reset or -1 to reset all channels.
+ * @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset())
+ * a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY.
+ * All other channels belong to this basic channel group. Make sure to call this function before
+ * setting any custom basic channel setup.
+ *
+ * @return
+ *  - #FLUID_OK on success.
+ *  - #FLUID_FAILED
+ *    - \a synth is NULL.
+ *    - \a chan is outside MIDI channel count.
+ *    - \a chan isn't a basic channel.
  */
-int
-fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset)
+int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan)
 {
-  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);
+    int nbr_chan;
 
-    if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id)
+    /* checks parameters first */
+    if(chan < 0)
     {
-      sfont_info->bankofs = offset;
-      break;
+        fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+        fluid_synth_api_enter(synth);
+        /* The range is all MIDI channels from 0 to MIDI channel count -1 */
+        chan = 0; /* beginning chan */
+        nbr_chan =  synth->midi_channels; /* MIDI Channels number */
     }
-  }
+    else
+    {
+        FLUID_API_ENTRY_CHAN(FLUID_FAILED);
 
-  if (!list)
-  {
-    FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id);
-    FLUID_API_RETURN(FLUID_FAILED);
-  }
+        /* checks if chan is a basic channel */
+        if(!(synth->channel[chan]->mode &  FLUID_CHANNEL_BASIC))
+        {
+            FLUID_API_RETURN(FLUID_FAILED);
+        }
 
-  FLUID_API_RETURN(FLUID_OK);
+        /* The range is all MIDI channels in the group from chan */
+        nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */
+    }
+
+    /* resets the range of MIDI channels */
+    fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan);
+    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
+ * Checks if a new basic channel group overlaps the next basic channel group.
+ *
+ * On success the function returns the possible number of channel for this
+ * new basic channel group.
+ * The function fails if the new group overlaps the next basic channel group.
+ *
+ * @param see fluid_synth_set_basic_channel.
+ * @return
+ * - On success, the effective number of channels for this new basic channel group,
+ *   #FLUID_FAILED otherwise.
+ * - #FLUID_FAILED
+ *   - \a val has a number of channels overlapping next basic channel group or been
+ *     above MIDI channel count.
  */
-int
-fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id)
+static int
+fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val)
 {
-  fluid_sfont_info_t *sfont_info;
-  fluid_list_t *list;
-  int offset = 0;
+    int i, n_chan = synth->midi_channels; /* MIDI Channels count */
+    int real_val = val; /* real number of channels in the group */
+
+    /* adjusts val range */
+    if(mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY)
+    {
+        real_val = 1; /* mode poly omnioff implies a group of only one channel.*/
+    }
+    else if(val == 0)
+    {
+        /* mode poly omnion (0), mono omnion (1), mono omni off (3) */
+        /* value 0 means all possible channels from basicchan to MIDI channel count -1.*/
+        real_val = n_chan - basicchan;
+    }
+    /* checks if val range is above MIDI channel count */
+    else if(basicchan + val > n_chan)
+    {
+        return FLUID_FAILED;
+    }
 
-  fluid_return_val_if_fail (synth != NULL, 0);
-  fluid_synth_api_enter(synth);
+    /* checks if this basic channel group overlaps next basic channel group */
+    for(i = basicchan + 1; i < basicchan + real_val; i++)
+    {
+        if(synth->channel[i]->mode &  FLUID_CHANNEL_BASIC)
+        {
+            /* A value of 0 for val means all possible channels from basicchan to
+            to the next basic channel -1 (if any).
+            When i reachs the next basic channel group, real_val will be
+            limited if it is possible */
+            if(val == 0)
+            {
+                /* limitation of real_val */
+                real_val = i - basicchan;
+                break;
+            }
+
+            /* overlap with the next basic channel group */
+            return FLUID_FAILED;
+        }
+    }
 
-  for (list = synth->sfont_info; list; list = fluid_list_next(list)) {
-    sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
+    return real_val;
+}
 
-    if (fluid_sfont_get_id (sfont_info->sfont) == (unsigned int)sfont_id)
+/**
+ * Sets a new basic channel group only. The function doesn't allow to change an
+ * existing basic channel.
+ *
+ * The function fails if any channel overlaps any existing basic channel group.
+ * To make room if necessary, basic channel groups can be cleared using
+ * fluid_synth_reset_basic_channel().
+ *
+ * @param synth the synth instance.
+ * @param chan the basic Channel number (0 to MIDI channel count-1).
+ * @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes).
+ * @param val number of channels in the group.
+ * @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY,
+ * #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value
+ * of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any)
+ * or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY
+ * as this mode implies a group of only one channel.
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
+ *   - \a mode is invalid.
+ *   - \a val has a number of channels overlapping another basic channel group or been
+ *     above MIDI channel count.
+ *   - When the function fails, any existing basic channels aren't modified.
+ */
+int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val)
+{
+    /* check parameters */
+    fluid_return_val_if_fail(mode >= 0, FLUID_FAILED);
+    fluid_return_val_if_fail(mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED);
+    fluid_return_val_if_fail(val >= 0, FLUID_FAILED);
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    /**/
+    if(val > 0 && chan + val > synth->midi_channels)
     {
-      offset = sfont_info->bankofs;
-      break;
+        FLUID_API_RETURN(FLUID_FAILED);
     }
-  }
 
-  if (!list)
-  {
-    FLUID_LOG (FLUID_ERR, "No SoundFont with id = %d", sfont_id);
-    FLUID_API_RETURN(0);
-  }
+    /* Checks if there is an overlap with the next basic channel */
+    val = fluid_synth_check_next_basic_channel(synth, chan, mode, val);
 
-  FLUID_API_RETURN(offset);
-}
+    if(val == FLUID_FAILED || synth->channel[chan]->mode &  FLUID_CHANNEL_ENABLED)
+    {
+        /* overlap with the next or previous channel group */
+        FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan);
+        FLUID_API_RETURN(FLUID_FAILED);
+    }
 
-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++;
+    /* sets a new basic channel group */
+    fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val);
+    /**/
+    FLUID_API_RETURN(FLUID_OK);
 }
 
-void fluid_synth_api_exit(fluid_synth_t* synth)
+/*
+ * Local version of fluid_synth_set_basic_channel(), called internally:
+ * - by fluid_synth_set_basic_channel() to set a new basic channel group.
+ * - during creation new_fluid_synth() or on CC reset to set a default basic channel group.
+ * - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group.
+ *
+ * @param see fluid_synth_set_basic_channel()
+*/
+static void
+fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val)
 {
-  synth->public_api_count--;
-  if (!synth->public_api_count) {
-    fluid_rvoice_eventhandler_flush(synth->eventhandler);
-  }
+    int i;
 
-  if (synth->use_mutex) {
-    fluid_rec_mutex_unlock(synth->mutex);
-  }
-  
-}
+    /* sets the basic channel group */
+    for(i = basicchan; i < basicchan + val; i++)
+    {
+        int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */
+        int new_val;
+        /* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */
+        fluid_synth_all_notes_off_LOCAL(synth, i);
+
+        if(i == basicchan)
+        {
+            new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */
+            new_val = val;     /* number of channels in the group */
+        }
+        else
+        {
+            new_val = 0; /* val is 0 for other channel than basic channel */
+        }
 
+        /* Channel is enabled */
+        new_mode |= FLUID_CHANNEL_ENABLED;
+        /* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */
+        fluid_channel_set_basic_channel_info(synth->channel[i], new_mode);
+        synth->channel[i]->mode_val = new_val;
+    }
+}
 
 /**
- * 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
+ * Searchs a previous basic channel starting from chan.
+ *
+ * @param synth the synth instance.
+ * @param chan starting index of the search (including chan).
+ * @return index of the basic channel if found , FLUID_FAILED otherwise.
  */
-int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type)
+static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan)
 {
-  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;
+    for(; chan >= 0; chan--)
+    {
+        /* searchs previous basic channel */
+        if(synth->channel[chan]->mode &  FLUID_CHANNEL_BASIC)
+        {
+            /* chan is the previous basic channel */
+            return chan;
+        }
+    }
 
-  FLUID_API_RETURN(FLUID_OK);
+    return FLUID_FAILED;
 }
 
+/**
+ * Returns poly mono mode information of any MIDI channel.
+ *
+ * @param synth the synth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled.
+ * @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled.
+ * @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled.
+ * @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL
+ *  the corresponding information isn't returned.
+ *
+ * @return
+ * - #FLUID_OK on success.
+ * - #FLUID_FAILED
+ *   - \a synth is NULL.
+ *   - \a chan is outside MIDI channel count.
+ */
+int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan,
+                                  int *basic_chan_out,
+                                  int *mode_out,
+                                  int *val_out)
+{
+    int basic_chan = FLUID_FAILED;
+    int mode = FLUID_FAILED;
+    int val = FLUID_FAILED;
+
+    /* checks parameters first */
+    FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+    if((synth->channel[chan]->mode &  FLUID_CHANNEL_ENABLED) &&
+            /* chan is enabled , we search the basic channel chan belongs to */
+            (basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED)
+    {
+        mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK;
+        val = synth->channel[basic_chan]->mode_val;
+    }
+
+    /* returns the informations if they are requested */
+    if(basic_chan_out)
+    {
+        * basic_chan_out = basic_chan;
+    }
+
+    if(mode_out)
+    {
+        * mode_out = mode;
+    }
+
+    if(val_out)
+    {
+        * val_out = val;
+    }
+
+    FLUID_API_RETURN(FLUID_OK);
+}
index 019a8e0d55c345ba843af9161cd1e37cebb81ada..96dc545746a82eae6ecf34fc20bf1a46af903ae3 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
  *                         INCLUDES
  */
 
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include "fluidsynth_priv.h"
-#include "fluid_event_queue.h"
+#include "fluid_sys.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"
 
 /***************************************************************
 
 #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
+#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 */
 
+#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 */
 
 /***************************************************************
  *
  *                         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.
@@ -82,26 +73,15 @@ enum fluid_midi_bank_select
 
 enum fluid_synth_status
 {
-  FLUID_SYNTH_CLEAN,
-  FLUID_SYNTH_PLAYING,
-  FLUID_SYNTH_QUIET,
-  FLUID_SYNTH_STOPPED
+    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
  *
@@ -113,125 +93,141 @@ typedef struct _fluid_sfont_info_t {
  * 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...?).
+ * ladspa_fx - same instance copied in rendering thread. Synchronising handled internally.
  *
  */
 
 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.
+    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? */
+    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) */
+    int effects_channels;              /**< the number of effects channels (>= 2) */
+    int effects_groups;                /**< the number of effects units (>= 1) */
+    int state;                         /**< the synthesizer state */
+    fluid_atomic_uint_t 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;          /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */
+    int sfont_id;             /**< Incrementing ID assigned to each loaded SoundFont */
+
+    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;
+    int fromkey_portamento;                     /**< fromkey portamento */
+    fluid_rvoice_eventhandler_t *eventhandler;
+
+    double reverb_roomsize;             /**< Shadow of reverb roomsize */
+    double reverb_damping;              /**< Shadow of reverb damping */
+    double reverb_width;                /**< Shadow of reverb width */
+    double reverb_level;                /**< Shadow of reverb level */
+
+    int chorus_nr;                     /**< Shadow of chorus number */
+    double chorus_level;                /**< Shadow of chorus level */
+    double chorus_speed;                /**< Shadow of chorus speed */
+    double chorus_depth;                /**< Shadow of chorus depth */
+    int chorus_type;                   /**< Shadow of chorus type */
+
+    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) */
+
+    fluid_atomic_float_t 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_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) */
+
+    fluid_mod_t *default_mod;          /**< the (dynamic) list of default modulators */
 
 #ifdef LADSPA
-  fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Effects unit for LADSPA support */
+    fluid_ladspa_fx_t *ladspa_fx;      /**< Effects unit for LADSPA support */
 #endif
+    enum fluid_iir_filter_type custom_filter_type; /**< filter type of the user-defined filter currently used for all voices */
+    enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */
 };
 
-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,
+/**
+ * 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);
+
+fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth,
+                                        int banknum,
+                                        int prognum);
+void fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont);
+
+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, unsigned int num);
+int fluid_synth_set_reverb_full(fluid_synth_t *synth, int set, double roomsize,
                                 double damping, double width, double level);
 
-int fluid_synth_reset_chorus(fluid_synth_tsynth);
-int fluid_synth_set_chorus_full(fluid_synth_tsynth, int set, int nr, 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);
+fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data);
+void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer);
 
-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_tsynth);
+void fluid_synth_process_event_queue(fluid_synth_t *synth);
 
+int fluid_synth_set_gen2(fluid_synth_t *synth, int chan,
+                         int param, float value,
+                         int absolute, int normalized);
 /*
  * misc
  */
+void fluid_synth_settings(fluid_settings_t *settings);
+
+
+/* extern declared in fluid_synth_monopoly.c */
+
+int fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel);
+int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, int key, int vel);
+int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key);
+int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, int fromkey, int tokey, int vel);
+int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, char Mono);
 
-void fluid_synth_settings(fluid_settings_t* settings);
+fluid_voice_t *
+fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range);
 
+void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, int key);
 #endif  /* _FLUID_SYNTH_H */
diff --git a/libs/fluidsynth/src/fluid_synth_monopoly.c b/libs/fluidsynth/src/fluid_synth_monopoly.c
new file mode 100644 (file)
index 0000000..b7828af
--- /dev/null
@@ -0,0 +1,727 @@
+/* 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_synth.h"
+#include "fluid_chan.h"
+#include "fluid_defsfont.h"
+
+
+/******************************************************************************
+  The legato detector is composed as this,
+  variables:
+  - monolist: monophonic list variable.
+  - prev_note: to store the most recent note before adding on noteon or before
+               removing on noteoff.
+  - FLUID_CHANNEL_LEGATO_PLAYING: legato/staccato state bit that informs on
+               legato or staccato playing.
+  functions:
+  - fluid_channel_add_monolist(), for inserting a new note.
+  - fluid_channel_search_monolist(), for searching the position of a note
+    into the list.
+  - fluid_channel_remove_monolist(), for removing a note from the list.
+
+            The monophonic list
+   +------------------------------------------------+
+   |    +----+   +----+          +----+   +----+    |
+   |    |note|   |note|          |note|   |note|    |
+   +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+
+        +----+   +----+          +----+   +----+
+         /|\                      /|\
+          |                        |
+       i_first                   i_last
+
+  The list allows an easy automatic detection of a legato passage when it is
+  played on a MIDI keyboard input device.
+  It is useful also when the input device is an ewi (electronic wind instrument)
+  or evi (electronic valve instrument) and these instruments are unable to send
+  MIDI CC legato on/off.
+
+  The list memorizes the notes in playing order.
+  - (a) On noteOn n2, if a previous note n1 exists, there is a legato
+     detection with n1 (with or without portamento from n1 to n2 See note below).
+  - (b) On noteOff of the running note n2, if a previous note n1 exists,
+     there is a legato detection from n2 to n1, allowing fast trills playing
+     (with or without portamento from n2 to n1. See note below).
+
+  Notes in the list are inserted to the end of the list that works like a
+  circular buffer.The features are:
+
+  1) It is always possible to play an infinite legato passage in
+     direct order (n1_On,n2_On,n3_On,....).
+
+  2) Playing legato in the reverse order (n10_Off, n9_Off,,...) helps in
+     fast trills playing as the list memorizes 10 most recent notes.
+
+  3) Playing an infinite lagato passage in ascendant or descendant order,
+     without playing trills is always possible using the usual way like this:
+      First we begin with an ascendant passage,
+      n1On, (n2On,n1Off), (n3On,n2Off) , (n4On,n3Off), then
+         we continue with a descendant passage
+      (n3On,n4off), (n2On,n3off), (n1On,n2off), n1Off...and so on
+
+ Each MIDI channel have a legato detector.
+
+ Note:
+  Portamento is a feature independant of the legato detector. So
+  portamento isn't part of the lagato detector. However portamento
+  (when enabled) is triggered at noteOn (like legato). Like in legato
+  situation it is usual to have a portamento from a note 'fromkey' to another
+  note 'tokey'. Portamento fromkey note choice is determined at noteOn by
+  fluid_synth_get_fromkey_portamento_legato() (see below).
+
+  More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+******************************************************************************/
+
+
+/*****************************************************************************
+ Portamento related functions in Poly or Mono mode
+******************************************************************************/
+
+/**
+ * fluid_synth_get_fromkey_portamento_legato returns two informations:
+ *    - fromkey note for portamento.
+ *    - fromkey note for legato.
+ *                                                 +-----> fromkey_portamento
+ *                                           ______|________
+ *                portamento modes >------->|               |
+ *                                          | get_fromkey   |
+ *  Porta.on/off >------------------------->|_______________|
+ *  (PTC)                                          |
+ *                                                 +-----> fromkey_legato
+ *
+ * The functions is intended to be call on noteOn mono
+ * see fluid_synth_noteon_mono_staccato(), fluid_synth_noteon_monopoly_legato()
+ * -------
+ * 1)The function determines if a portamento must occur on next noteOn.
+ * The value returned is 'fromkey portamento' which is the pitchstart key
+ * of a portamento, as function of PTC or (default_fromkey, prev_note) both
+ * if Portamento On. By order of precedence the result is:
+ *  1.1) PTC have precedence over Portamento On.
+ *       If CC PTC has been received, its value supersedes and any
+ *       portamento pedal On, default_fromkey,prev_note or portamento mode.
+ *  1.2) Otherwise ,when Portamento On the function takes the following value:
+ *       - default_fromkey if valid
+ *       - otherwise prev_note(prev_note is the note prior the most recent
+ *         note played).
+ *       Then portamento mode is applied to validate the value choosen.
+ *       Where portamento mode is:
+ *       - each note, a portamento occurs on each note.
+ *       - legato only, portamento only on notes played legato.
+ *       - staccato only, portamento only on notes played staccato.
+ *  1.3) Otherwise, portamento is off,INVALID_NOTE is returned (portamento is disabled).
+ * ------
+ * 2)The function determines if a legato playing must occur on next noteOn.
+ *  'fromkey legato note' is returned as a function of default_fromkey, PTC,
+ *   current mono/poly mode,actual 'staccato/legato' playing state and prev_note.
+ *   By order of precedence the result is:
+ *   2.1) If valid, default_fromkey have precedence over any others values.
+ *   2.2) Otherwise if CC PTC has been received its value is returned.
+ *   2.3) Otherwise fromkey legato is determined from the mono/poly mode,
+ *        the actual 'staccato/legato' playing state (FLUID_CHANNEL_LEGATO_PLAYING) and prev_note
+ *        as this:
+ *        - in (poly/Mono) staccato , INVALID_NOTE is returned.
+ *        - in poly  legato , actually we don't want playing legato. So
+ *          INVALID_NOTE is returned.
+ *        - in mono legato , prev_note is returned.
+ *
+ * On input
+ * @param chan  fluid_channel_t.
+ * @param defaultFromkey, the defaut 'fromkey portamento' note or 'fromkey legato'
+ *       note (see description above).
+ *
+ * @return
+ *  1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento.
+ *  If valid,it means that portamento is enabled .
+ *
+ *  2)The 'fromkey legato' note is returned.
+ *
+ * Notes about usage:
+ * The function is intended to be called when the following event occurs:
+ * - On noteOn (Poly or Mono) after insertion in the monophonic list.
+ * - On noteOff(mono legato playing). In this case, default_fromkey must be valid.
+ *
+ * Typical calling usage:
+ * - In poly, default_fromkey must be INVALID_NOTE.
+ * - In mono staccato playing,default_fromkey must be INVALID_NOTE.
+ * - In mono legato playing,default_fromkey must be valid.
+ */
+static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan,
+        int default_fromkey)
+{
+    unsigned char ptc = fluid_channel_get_cc(chan, PORTAMENTO_CTRL);
+
+    if(fluid_channel_is_valid_note(ptc))
+    {
+        /* CC PTC has been received */
+        fluid_channel_clear_portamento(chan);  /* clears the CC PTC receive */
+        chan->synth->fromkey_portamento =  ptc;/* returns fromkey portamento */
+
+        /* returns fromkey legato */
+        if(!fluid_channel_is_valid_note(default_fromkey))
+        {
+            default_fromkey = ptc;
+        }
+    }
+    else
+    {
+        /* determines and returns fromkey portamento */
+        unsigned char fromkey_portamento = INVALID_NOTE;
+
+        if(fluid_channel_portamento(chan))
+        {
+            /* Portamento when Portamento pedal is On */
+            /* 'fromkey portamento'is determined from the portamento mode
+             and the most recent note played (prev_note)*/
+            enum fluid_channel_portamento_mode portamentomode = chan->portamentomode;
+
+            if(fluid_channel_is_valid_note(default_fromkey))
+            {
+                fromkey_portamento = default_fromkey; /* on each note */
+            }
+            else
+            {
+                fromkey_portamento = fluid_channel_prev_note(chan); /* on each note */
+            }
+
+            if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY)
+            {
+                /* Mode portamento:legato only */
+                if(!(chan->mode  & FLUID_CHANNEL_LEGATO_PLAYING))
+                {
+                    fromkey_portamento = INVALID_NOTE;
+                }
+            }
+            else if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY)
+            {
+                /* Mode portamento:staccato only */
+                if(chan->mode  & FLUID_CHANNEL_LEGATO_PLAYING)
+                {
+                    fromkey_portamento = INVALID_NOTE;
+                }
+            }
+
+            /* else Mode portamento: on each note (staccato/legato) */
+        }
+
+        /* Returns fromkey portamento */
+        chan->synth->fromkey_portamento = fromkey_portamento;
+
+        /* Determines and returns fromkey legato */
+        if(!fluid_channel_is_valid_note(default_fromkey))
+        {
+            /* in staccato (poly/Mono) returns INVALID_NOTE */
+            /* In mono mode legato playing returns the note prior most
+               recent note played */
+            if(fluid_channel_is_playing_mono(chan) && (chan->mode  & FLUID_CHANNEL_LEGATO_PLAYING))
+            {
+                default_fromkey = fluid_channel_prev_note(chan); /* note prior last note */
+            }
+
+            /* In poly mode legato playing, actually we don't want playing legato.
+            So returns INVALID_NOTE */
+        }
+    }
+
+    return default_fromkey; /* Returns legato fromkey */
+}
+
+/*****************************************************************************
+ noteon - noteoff functions in Mono mode
+******************************************************************************/
+/*
+ *  noteon - noteoff on a channel in "monophonic playing".
+ *
+ *  A channel needs to be played monophonic if this channel has been set in
+ *  monophonic mode by basic channel API.(see fluid_synth_polymono.c).
+ *  A channel needs also to be played monophonic if it has been set in
+ *  polyphonic mode and legato pedal is On during the playing.
+ *  When a channel is in "monophonic playing" state, only one note at a time can be
+ *  played in a staccato or legato manner (with or without portamento).
+ *  More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+ *                                           _______________
+ *                 ________________         |    noteon     |
+ *                | legato detector|    O-->| mono_staccato |--*-> preset_noteon
+ *  noteon_mono ->| (add_monolist) |--O--   |_______________|  |   (with or without)
+ *  LOCAL         |________________|    O         /|\          |   (portamento)
+ *                  /|\ set_onenote     |          | fromkey   |
+ *                   |                  |          | portamento|
+ *  noteOn poly  >---*------------------*          |           |
+ *                                      |          |           |
+ *                                      |    _____ |________   |
+ *                portamento modes >--- | ->|               |  |
+ *                                      |   |  get_fromkey  |  |
+ *  Porta.on/off >--------------------- | ->|_______________|  |
+ *  (PTC)                               |          |           |
+ *                                      |  fromkey | fromkey   |
+ *                                      |  legato  | portamento|
+ *                                      |    _____\|/_______   |
+ *                                      *-->|    noteon     |--/
+ *                                      |   |    monopoly   |
+ *                                      |   |    legato     |----> voices
+ *                legato modes >------- | ->|_______________|      triggering
+ *                                      |                          (with or without)
+ *                                      |                          (portamento)
+ *                                      |
+ *                                      |
+ *  noteOff poly >---*----------------- | ---------+
+ *                   |  clear           |          |
+ *                 _\|/_____________    |          |
+ *                | legato detector |   O          |
+ *  noteoff_mono->|(search_monolist)|-O--    _____\|/_______
+ *  LOCAL         |(remove_monolist)|   O-->|   noteoff     |
+ *                |_________________|       |   monopoly    |----> noteoff
+ *  Sust.on/off  >------------------------->|_______________|
+ *  Sost.on/off
+------------------------------------------------------------------------------*/
+int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key,
+                                 char Mono);
+
+int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan,
+                                       int fromkey, int tokey, int vel);
+
+/**
+ * Plays a noteon event for a Synth instance in "monophonic playing" state.
+ * Please see the description above about "monophonic playing".
+ *                                          _______________
+ *                ________________         |    noteon     |
+ *               | legato detector|    O-->| mono_staccato |--->preset_noteon
+ * noteon_mono ->| (add_monolist) |--O--   |_______________|
+ * LOCAL         |________________|    O
+ *                                     |
+ *                                     |
+ *                                     |
+ *                                     |
+ *                                     |
+ *                                     |
+ *                                     |
+ *                                     |
+ *                                     |
+ *                                     |    _______________
+ *                                     |   |   noteon      |
+ *                                     +-->|  monopoly     |
+ *                                         |   legato      |---> voices
+ *                                         |_______________|     triggering
+ *
+ * The function uses the legato detector (see above) to determine if the note must
+ * be played staccato or legato.
+ *
+ * @param synth 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).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ */
+int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan,
+                                  int key,  int vel)
+{
+    fluid_channel_t *channel = synth->channel[chan];
+
+    /* Adds the note into the monophonic list */
+    fluid_channel_add_monolist(channel, key, vel, 0);
+
+    /* in Breath Sync mode, the noteon triggering is postponed
+       until the musician starts blowing in the breath controller */
+    if(!(channel->mode &  FLUID_CHANNEL_BREATH_SYNC) ||
+            fluid_channel_breath_msb(channel))
+    {
+        /* legato/staccato playing detection */
+        if(channel->mode  & FLUID_CHANNEL_LEGATO_PLAYING)
+        {
+            /* legato playing */
+            /* legato from prev_note to key */
+            /* the voices from prev_note key number are to be used to play key number */
+            /* fromkey must be valid */
+            return     fluid_synth_noteon_monopoly_legato(synth, chan,
+                    fluid_channel_prev_note(channel), key, vel);
+        }
+        else
+        {
+            /* staccato playing */
+            return fluid_synth_noteon_mono_staccato(synth, chan, key, vel);
+        }
+    }
+    else
+    {
+        return FLUID_OK;
+    }
+}
+
+/**
+ * Plays a noteoff event for a Synth instance in "monophonic playing" state.
+ * Please see the description above about "monophonic playing"
+ *
+ *                                           _______________
+ *                                          |   noteon      |
+ *                                      +-->|  monopoly     |
+ *                                      |   |   legato      |----> voices
+ *                                      |   |_______________|      triggering
+ *                                      |                          (with or without)
+ *                                      |                          (portamento)
+ *                                      |
+ *                                      |
+ *                                      |
+ *                                      |
+ *                                      |
+ *                                      |
+ *                 _________________    |
+ *                | legato detector |   O
+ *  noteoff_mono->|(search_monolist)|-O--    _______________
+ *  LOCAL         |(remove_monolist)|   O-->|   noteoff     |
+ *                |_________________|       |   monopoly    |----> noteoff
+ *                                          |_______________|
+ *
+ * The function uses the legato detector (see above) to determine if the noteoff must
+ * be played staccato or legato.
+ *
+ * @param synth 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.
+ */
+int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key)
+{
+    int status;
+    int i, i_prev;
+    fluid_channel_t *channel = synth->channel[chan];
+    /* searching the note in the monophonic list */
+    i = fluid_channel_search_monolist(channel, key, &i_prev);
+
+    if(i >= 0)
+    {
+        /* the note is in the monophonic list */
+        /* Removes the note from the monophonic list */
+        fluid_channel_remove_monolist(channel, i, &i_prev);
+
+        /* in Breath Sync mode, the noteoff triggering is done
+           if the musician is blowing in the breath controller */
+        if(!(channel->mode &  FLUID_CHANNEL_BREATH_SYNC) ||
+                fluid_channel_breath_msb(channel))
+        {
+            /* legato playing detection */
+            if(channel->mode  & FLUID_CHANNEL_LEGATO_PLAYING)
+            {
+                /* the list contains others notes */
+                if(i_prev >= 0)
+                {
+                    /* legato playing detection on noteoff */
+                    /* legato from key to i_prev key */
+                    /* the voices from key number are to be used to
+                    play i_prev key number. */
+                    status = fluid_synth_noteon_monopoly_legato(synth, chan,
+                             key, channel->monolist[i_prev].note,
+                             channel->monolist[i_prev].vel);
+                }
+                /* else the note doesn't need to be played off */
+                else
+                {
+                    status = FLUID_OK;
+                }
+            }
+            else
+            {
+                /* the monophonic list is empty */
+                /* plays the monophonic note noteoff and eventually held
+                by sustain/sostenuto */
+                status = fluid_synth_noteoff_monopoly(synth, chan, key, 1);
+            }
+        }
+        else
+        {
+            status = FLUID_OK;
+        }
+    }
+    else
+    {
+        /* the note is not found in the list so the note was
+        played On when the channel was in polyphonic playing */
+        /* plays the noteoff as for polyphonic  */
+        status = fluid_synth_noteoff_monopoly(synth, chan, key, 0);
+    }
+
+    return status;
+}
+
+/*----------------------------------------------------------------------------
+ staccato playing
+-----------------------------------------------------------------------------*/
+/**
+ * Plays noteon for a monophonic note in staccato manner.
+ * Please see the description above about "monophonic playing".
+ *                                         _______________
+ *                                        |    noteon     |
+ *  noteon_mono >------------------------>| mono_staccato |----> preset_noteon
+ *                                        |_______________|      (with or without)
+ *  LOCAL                                       /|\              (portamento)
+ *                                               | fromkey
+ *                                               | portamento
+ *                                               |
+ *                                               |
+ *                                         ______|________
+ *                portamento modes >----->|               |
+ *                                        |  get_fromkey  |
+ *  Porta.on/off >----------------------->|_______________|
+ *  Portamento
+ *  (PTC)
+ *
+ * We are in staccato situation (where no previous note have been depressed).
+ * Before the note been passed to fluid_preset_noteon(), the function must determine
+ * the from_key_portamento parameter used by fluid_preset_noteon().
+ *
+ * from_key_portamento is returned by fluid_synth_get_fromkey_portamento_legato() function.
+ * fromkey_portamento is set to valid/invalid  key value depending of the portamento
+ * modes (see portamento mode API) , CC portamento On/Off , and CC portamento control
+ * (PTC).
+ *
+ * @param synth 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).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ */
+int
+fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel)
+{
+    fluid_channel_t *channel = synth->channel[chan];
+
+    /* Before playing a new note, if a previous monophonic note is currently
+       sustained it needs to be released */
+    fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, channel->key_mono_sustained);
+    /* Get possible 'fromkey portamento'   */
+    fluid_synth_get_fromkey_portamento_legato(channel, INVALID_NOTE);
+    /* The note needs to be played by voices allocation  */
+    return fluid_preset_noteon(channel->preset, synth, chan, key, vel);
+}
+
+/**
+ * Plays noteoff for a polyphonic or monophonic note
+ * Please see the description above about "monophonic playing".
+ *
+ *
+ *  noteOff poly >---------------------------------+
+ *                                                 |
+ *                                                 |
+ *                                                 |
+ *  noteoff_mono                             _____\|/_______
+ *  LOCAL        >------------------------->|   noteoff     |
+ *                                          |   monopoly    |----> noteoff
+ *  Sust.on/off  >------------------------->|_______________|
+ *  Sost.on/off
+ *
+ * The function has the same behaviour when the noteoff is poly of mono, except
+ * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the
+ * key is memorized. This is neccessary when the next mono note will be played
+ * staccato, as any current mono note currently sustained will need to be released
+ * (see fluid_synth_noteon_mono_staccato()).
+ * Note also that for a monophonic legato passage, the function is called only when
+ * the last noteoff of the passage occurs. That means that if sustain or sostenuto
+ * is depressed, only the last note of a legato passage will be sustained.
+ *
+ * @param synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param key MIDI note number (0-127).
+ * @param Mono, 1 noteoff on monophonic note.
+ *              0 noteoff on polyphonic note.
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ *
+ * Note: On return, on monophonic, possible sustained note is memorized in
+ * key_mono_sustained. Memorization is done here on noteOff.
+ */
+int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key,
+                                 char Mono)
+{
+    int status = FLUID_FAILED;
+    fluid_voice_t *voice;
+    int i;
+    fluid_channel_t *channel = synth->channel[chan];
+
+    /* Key_sustained is prepared to return no note sustained (INVALID_NOTE) */
+    if(Mono)
+    {
+        channel->key_mono_sustained = INVALID_NOTE; /* no mono note sustained */
+    }
+
+    /* noteoff for all voices with same chan and same key */
+    for(i = 0; i < synth->polyphony; i++)
+    {
+        voice = synth->voice[i];
+
+        if(fluid_voice_is_on(voice) &&
+                fluid_voice_get_channel(voice) == chan &&
+                fluid_voice_get_key(voice) == 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",
+                          fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0,
+                          fluid_voice_get_id(voice),
+                          (fluid_curtime() - synth->start) / 1000.0f,
+                          used_voices);
+            } /* if verbose */
+
+            fluid_voice_noteoff(voice);
+
+            /* noteoff on monophonic note */
+            /* Key memorization if the note is sustained  */
+            if(Mono &&
+                    (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice)))
+            {
+                channel->key_mono_sustained = key;
+            }
+
+            status = FLUID_OK;
+        } /* if voice on */
+    } /* for all voices */
+
+    return status;
+}
+
+/*----------------------------------------------------------------------------
+ legato playing
+-----------------------------------------------------------------------------*/
+/**
+ * Plays noteon for a monophonic note played legato.
+ * Please see the description above about "monophonic playing".
+ *
+ *
+ *                                         _______________
+ *                portamento modes >----->|               |
+ *                                        | get_fromkey   |
+ *  Porta.on/off >----------------------->|_______________|
+ *  Portamento                                   |
+ *  (PTC)                                        |           +-->preset_noteon
+ *                                       fromkey | fromkey   |  (with or without)
+ *                                       legato  | portamento|  (portamento)
+ *                                         _____\|/_______   |
+ *                                        |   noteon      |--+
+ *  noteon_mono >------------------------>|  monopoly     |
+ *  LOCAL                                 |   legato      |----->voices
+ *                                        |_______________|      triggering
+ *                                              /|\              (with or without)
+ *                                               |               (portamento)
+ *                legato modes >-----------------+
+ *
+ * We are in legato situation (where a previous note has been depressed).
+ * The function must determine the from_key_portamento and from_key_legato parameters
+ * used respectively by fluid_preset_noteon() function and voices triggering functions.
+ *
+ * from_key_portamento and from_key_legato are returned by
+ * fluid_synth_get_fromkey_portamento_legato() function.
+ * fromkey_portamento is set to valid/invalid  key value depending of the portamento
+ * modes (see portamento mode API), CC portamento On/Off, and CC portamento control
+ * (PTC).
+ * Then, depending of the legato modes (see legato mode API), the function will call
+ * the appropriate triggering functions.
+ * @param synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param fromkey MIDI note number (0-127).
+ * @param tokey MIDI note number (0-127).
+ * @param vel MIDI velocity (0-127).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ *
+ * Note: The voices with key 'fromkey' are to be used to play key 'tokey'.
+ * The function is able to play legato through Preset Zone(s) (PZ) and
+ * Instrument Zone(s) (IZ) as far as possible.
+ * When key tokey is outside the current Instrument Zone, Preset Zone,
+ * current 'fromkey' voices are released. If necessary new voices
+ * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s).
+ * More informations in FluidPolyMono-0004.pdf chapter 4.7 (Appendices).
+ */
+int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan,
+                                       int fromkey, int tokey, int vel)
+{
+    fluid_channel_t *channel = synth->channel[chan];
+    enum fluid_channel_legato_mode legatomode = channel->legatomode;
+    fluid_voice_t *voice;
+    int i ;
+    /* Gets possible 'fromkey portamento' and possible 'fromkey legato' note  */
+    fromkey = fluid_synth_get_fromkey_portamento_legato(channel, fromkey);
+
+    if(fluid_channel_is_valid_note(fromkey))
+    {
+        for(i = 0; i < synth->polyphony; i++)
+        {
+            /* searching fromkey voices: only those who don't have 'note off' */
+            voice = synth->voice[i];
+
+            if(fluid_voice_is_on(voice) &&
+                    fluid_voice_get_channel(voice) == chan &&
+                    fluid_voice_get_key(voice) == fromkey)
+            {
+                fluid_zone_range_t *zone_range = voice->zone_range;
+
+                /* Ignores voice when there is no instrument zone (i.e no zone_range). Otherwise
+                   checks if tokey is inside the range of the running voice */
+                if(zone_range && fluid_zone_inside_range(zone_range, tokey, vel))
+                {
+                    switch(legatomode)
+                    {
+                    case FLUID_CHANNEL_LEGATO_MODE_RETRIGGER: /* mode 0 */
+                        fluid_voice_release(voice); /* normal release */
+                        break;
+
+                    case FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER: /* mode 1 */
+                        /* Skip in attack section */
+                        fluid_voice_update_multi_retrigger_attack(voice, tokey, vel);
+
+                        /* Starts portamento if enabled */
+                        if(fluid_channel_is_valid_note(synth->fromkey_portamento))
+                        {
+                            /* Sends portamento parameters to the voice dsp */
+                            fluid_voice_update_portamento(voice,
+                                                          synth->fromkey_portamento,
+                                                          tokey);
+                        }
+
+                        /* The voice is now used to play tokey in legato manner */
+                        /* Marks this Instrument Zone to be ignored during next
+                        fluid_preset_noteon() */
+                        zone_range->ignore = TRUE;
+                        break;
+
+                    default: /* Invalid mode: this should never happen */
+                        FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d",
+                                  legatomode);
+                        return FLUID_FAILED;
+                    }
+                }
+                else
+                {
+                    /* tokey note is outside the voice range, so the voice is released */
+                    fluid_voice_release(voice);
+                }
+            }
+        }
+    }
+
+    /* May be,tokey will enter in new others Insrument Zone(s),Preset Zone(s), in
+       this case it needs to be played by voices allocation  */
+    return fluid_preset_noteon(channel->preset, synth, chan, tokey, vel);
+}
index 328f2556d6238329f68e3b793269d7d498aa1396..c9662f7787d869ee682017b11a8a7a238eeec770 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 /* 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
+#ifdef _WIN32
+#define FLUID_SOCKET_FLAG      0x40000000
+#else
+#define FLUID_SOCKET_FLAG      0x00000000
+#define SOCKET_ERROR           -1
+#define INVALID_SOCKET         -1
+#endif
 
 /* 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_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;
+    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;
+    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, charbuf, int len);
+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, ...)
+static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] =
 {
-  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);
+    fluid_default_log_function,
+    fluid_default_log_function,
+    fluid_default_log_function,
+    fluid_default_log_function,
+    fluid_default_log_function
+};
+static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL };
 
-    fun = fluid_log_function[FLUID_DBG];
-    if (fun != NULL) {
-      (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]);
-    }
-  }
-  return 0;
-}
-#endif
+static const char fluid_libname[] = "fluidsynth";
 
 /**
  * Installs a new log function for a specified log level.
@@ -116,16 +96,18 @@ int fluid_debug(int level, char * fmt, ...)
  * @return The previously installed function.
  */
 fluid_log_function_t
-fluid_set_log_function(int level, fluid_log_function_t fun, voiddata)
+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;
+    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;
 }
 
 /**
@@ -135,75 +117,46 @@ fluid_set_log_function(int level, fluid_log_function_t fun, void* data)
  * @param data User supplied data (not used)
  */
 void
-fluid_default_log_function(int level, char* message, void* data)
+fluid_default_log_function(int level, const char *message, void *data)
 {
-  FILE* out;
+    FILE *out;
 
 #if defined(WIN32)
-  out = stdout;
+    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);
+    out = stderr;
 #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) {
+    switch(level)
+    {
+    case FLUID_PANIC:
+        FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
+        break;
 
-    fluid_log_initialized = 1;
+    case FLUID_ERR:
+        FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
+        break;
 
-    if (fluid_log_function[FLUID_PANIC] == NULL) {
-      fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL);
-    }
+    case FLUID_WARN:
+        FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
+        break;
 
-    if (fluid_log_function[FLUID_ERR] == NULL) {
-      fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL);
-    }
+    case FLUID_INFO:
+        FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+        break;
 
-    if (fluid_log_function[FLUID_WARN] == NULL) {
-      fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL);
-    }
+    case FLUID_DBG:
+#if DEBUG
+        FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
+#endif
+        break;
 
-    if (fluid_log_function[FLUID_INFO] == NULL) {
-      fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL);
+    default:
+        FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+        break;
     }
 
-    if (fluid_log_function[FLUID_DBG] == NULL) {
-      fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL);
-    }
-  }
+    fflush(out);
 }
 
 /**
@@ -214,22 +167,26 @@ fluid_log_config(void)
  * @return Always returns #FLUID_FAILED
  */
 int
-fluid_log(int level, const charfmt, ...)
+fluid_log(int level, const char *fmt, ...)
 {
-  fluid_log_function_t fun = NULL;
+    fluid_log_function_t fun = NULL;
 
-  va_list args;
-  va_start (args, fmt);
-  vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
-  va_end (args);
+    va_list args;
+    va_start(args, fmt);
+    FLUID_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]);
+    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;
+
+    return FLUID_FAILED;
 }
 
 /**
@@ -245,70 +202,77 @@ fluid_log(int level, const char* fmt, ...)
  * @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 *fluid_strtok(char **str, const char *delim)
 {
-  char *s, *d, *token;
-  char c;
+    char *s,  *token;
+               const char *d;
+    char c;
 
-  if (str == NULL || delim == NULL || !*delim)
-  {
-    FLUID_LOG(FLUID_ERR, "Null pointer");
-    return NULL;
-  }
+    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) */
+    s = *str;
 
-  /* skip delimiter chars at beginning of token */
-  do
-  {
-    c = *s;
-    if (!c)    /* end of source string? */
+    if(!s)
     {
-      *str = NULL;
-      return NULL;
+        return NULL;    /* str points to a NULL pointer? (tokenize already ended) */
     }
 
-    for (d = delim; *d; d++)   /* is source char a token char? */
+    /* skip delimiter chars at beginning of token */
+    do
     {
-      if (c == *d)     /* token char match? */
-      {
-       s++;            /* advance to next source char */
-       break;
-      }
+        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 */
+    while(*d);         /* while token char match */
 
-  token = s;           /* start of token found */
+    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? */
+    /* search for next token char or end of source string */
+    for(s = s + 1; *s; s++)
     {
-      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;
-      }
+        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;
+    /* we get here only if source string ended */
+    *str = NULL;
+    return token;
 }
 
 /*
  * fluid_error
  */
-char*
+char *
 fluid_error()
 {
-  return fluid_errbuf;
+    return fluid_errbuf;
 }
 
 /**
@@ -322,19 +286,23 @@ fluid_error()
 int
 fluid_is_midifile(const char *filename)
 {
-  FILE* fp = fopen(filename, "rb");
-  char id[4];
+    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;
+    }
 
-  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;
+    return FLUID_STRNCMP(id, "MThd", 4) == 0;
 }
 
 /**
@@ -342,25 +310,43 @@ fluid_is_midifile(const char *filename)
  * @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.
+ * @note The current implementation only checks for the "RIFF" and "sfbk" headers in
+ * the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
  */
 int
 fluid_is_soundfont(const char *filename)
 {
-  FILE* fp = fopen(filename, "rb");
-  char id[4];
+    FILE *fp = fopen(filename, "rb");
+    char riff_id[4], sfbk_id[4];
+
+    if(fp == NULL)
+    {
+        return 0;
+    }
+
+    if((fread((void *) riff_id, 1, sizeof(riff_id), fp) != sizeof(riff_id)) ||
+            (fseek(fp, 4, SEEK_CUR) != 0) ||
+            (fread((void *) sfbk_id, 1, sizeof(sfbk_id), fp) != sizeof(sfbk_id)))
+    {
+        goto error_rec;
+    }
 
-  if (fp == NULL) {
-    return 0;
-  }
-  if (fread((void*) id, 1, 4, fp) != 4) {
+    fclose(fp);
+    return (FLUID_STRNCMP(riff_id, "RIFF", sizeof(riff_id)) == 0) &&
+           (FLUID_STRNCMP(sfbk_id, "sfbk", sizeof(sfbk_id)) == 0);
+
+error_rec:
     fclose(fp);
     return 0;
-  }
-  fclose(fp);
+}
 
-  return strncmp(id, "RIFF", 4) == 0;
+/**
+ * Suspend the execution of the current thread for the specified amount of time.
+ * @param milliseconds to wait.
+ */
+void fluid_msleep(unsigned int msecs)
+{
+    g_usleep(msecs * 1000);
 }
 
 /**
@@ -369,78 +355,121 @@ fluid_is_soundfont(const char *filename)
  */
 unsigned int fluid_curtime(void)
 {
-  static glong initial_seconds = 0;
-  GTimeVal timeval;
+    static glong initial_seconds = 0;
+    GTimeVal timeval;
 
-  if (initial_seconds == 0) {
-    g_get_current_time (&timeval);
-    initial_seconds = timeval.tv_sec;
-  }
+    if(initial_seconds == 0)
+    {
+        g_get_current_time(&timeval);
+        initial_seconds = timeval.tv_sec;
+    }
 
-  g_get_current_time (&timeval);
+    g_get_current_time(&timeval);
 
-  return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0);
+    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.
+ * @return time in microseconds.
+ * Note: When used for profiling we need high precision clock given
+ * by g_get_monotonic_time()if available (glib version >= 2.53.3).
+ * If glib version is too old and in the case of Windows the function
+ * uses high precision performance counter instead of g_getmonotic_time().
  */
 double
-fluid_utime (void)
+fluid_utime(void)
 {
-  GTimeVal timeval;
+    double utime;
+
+#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28
+    /* use high precision monotonic clock if available (g_monotonic_time().
+     * For Winfdows, if this clock is actually implemented as low prec. clock
+     * (i.e. in case glib is too old), high precision performance counter are
+     * used instead.
+     * see: https://bugzilla.gnome.org/show_bug.cgi?id=783340
+     */
+#if defined(WITH_PROFILING) &&  defined(WIN32) &&\
+       /* glib < 2.53.3 */\
+       (GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3))
+    /* use high precision performance counter. */
+    static LARGE_INTEGER freq_cache = {0, 0};  /* Performance Frequency */
+    LARGE_INTEGER perf_cpt;
+
+    if(! freq_cache.QuadPart)
+    {
+        QueryPerformanceFrequency(&freq_cache);  /* Frequency value */
+    }
 
-  g_get_current_time (&timeval);
+    QueryPerformanceCounter(&perf_cpt); /* Counter value */
+    utime = perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */
+#else
+    utime = g_get_monotonic_time();
+#endif
+#else
+    /* fallback to less precise clock */
+    GTimeVal timeval;
+    g_get_current_time(&timeval);
+    utime = (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
+#endif
 
-  return (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
+    return utime;
 }
 
 
+
 #if defined(WIN32)      /* Windoze specific stuff */
 
 void
-fluid_thread_self_set_prio (int prio_level)
+fluid_thread_self_set_prio(int prio_level)
 {
-  if (prio_level > 0)
-    SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+    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)
+fluid_thread_self_set_prio(int prio_level)
 {
-  if (prio_level > 0)
-    DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
+    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)
+fluid_thread_self_set_prio(int prio_level)
 {
-  struct sched_param priority;
+    struct sched_param priority;
 
-  if (prio_level > 0)
-  {
+    if(prio_level > 0)
+    {
 
-    memset(&priority, 0, sizeof(priority));
-    priority.sched_priority = prio_level;
+        memset(&priority, 0, sizeof(priority));
+        priority.sched_priority = prio_level;
+
+        if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &priority) == 0)
+        {
+            return;
+        }
 
-    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;
-    }
+        /* 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");
-  }
+        FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority");
+    }
 }
 
 #ifdef FPE_CHECK
@@ -480,34 +509,34 @@ fluid_thread_self_set_prio (int prio_level)
  * 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(charexplanation)
+unsigned int fluid_check_fpe_i386(char *explanation)
 {
-  unsigned int s;
+    unsigned int s;
 
-  _FPU_GET_SW(s);
-  _FPU_CLR_SW();
+    _FPU_GET_SW(s);
+    _FPU_CLR_SW();
 
-  s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE;
+    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 " : "");
-  }
+    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;
+    return s;
 }
 
 /* Purpose:
  * Clear floating point exception.
  */
-void fluid_clear_fpe_i386 (void)
+void fluid_clear_fpe_i386(void)
 {
-  _FPU_CLR_SW();
+    _FPU_CLR_SW();
 }
 
 #endif // ifdef FPE_CHECK
@@ -523,47 +552,409 @@ void fluid_clear_fpe_i386 (void)
  */
 
 #if WITH_PROFILING
+/* Profiling interface beetween profiling command shell and audio rendering API
+  (FluidProfile_0004.pdf- 3.2.2).
+  Macros are in defined in fluid_sys.h.
+*/
 
-fluid_profile_data_t fluid_profile_data[] =
+/*
+  -----------------------------------------------------------------------------
+  Shell task side |    Profiling interface              |  Audio task side
+  -----------------------------------------------------------------------------
+  profiling       |    Internal    |      |             |      Audio
+  command   <---> |<-- profling -->| Data |<--macros -->| <--> rendering
+  shell           |    API         |      |             |      API
+
+*/
+/* default parameters for shell command "prof_start" in fluid_sys.c */
+unsigned short fluid_profile_notes = 0; /* number of generated notes */
+/* preset bank:0 prog:16 (organ) */
+unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK;
+unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG;
+
+/* print mode */
+unsigned char fluid_profile_print = FLUID_PROFILE_DEFAULT_PRINT;
+/* number of measures */
+unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF;
+/* measure duration in ms */
+unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION;
+/* lock between multiple-shell */
+fluid_atomic_int_t fluid_profile_lock = 0;
+/**/
+
+/*----------------------------------------------
+  Profiling Data
+-----------------------------------------------*/
+unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */
+unsigned int fluid_profile_end_ticks = 0;          /* ending position (in ticks) */
+fluid_profile_data_t fluid_profile_data[] =        /* Data duration */
 {
-  { 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}
+    {"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0},
+    {"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0}
 };
 
 
+/*----------------------------------------------
+  Internal profiling API
+-----------------------------------------------*/
+/* logging profiling data (used on synthesizer instance deletion) */
 void fluid_profiling_print(void)
 {
-  int i;
+    int i;
 
-  printf("fluid_profiling_print\n");
+    printf("fluid_profiling_print\n");
 
-  FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");
+    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);
+    for(i = 0; i < FLUID_PROFILE_NBR; 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);
+        }
     }
-  }
 }
 
+/* Macro that returns cpu load in percent (%)
+ * @dur: duration (micro second).
+ * @sample_rate: sample_rate used in audio driver (Hz).
+ * @n_amples: number of samples collected during 'dur' duration.
+*/
+#define fluid_profile_load(dur,sample_rate,n_samples) \
+        (dur * sample_rate / n_samples / 10000.0)
+
+
+/* prints cpu loads only
+*
+* @param sample_rate the sample rate of audio output.
+* @param out output stream device.
+*
+* ------------------------------------------------------------------------------
+* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices
+* ------------------------------------------------------------------------------
+* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices
+* -------|---------|---------|----------|---------|---------|-------------------
+*     250|   41.544|   41.544|     0.000|    0.000|    0.163|              612
+*/
+static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out)
+{
+    unsigned int n_voices; /* voices number */
+    static const char max_voices_not_available[] = "      not available";
+    const char *pmax_voices;
+    char max_voices_available[20];
+
+    /* First computes data to be printed */
+    double  total, voices, reverb, chorus, all_voices, voice;
+    /* voices number */
+    n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?
+               fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices /
+               fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count : 0;
+
+    /* total load (%) */
+    total =  fluid_profile_data[FLUID_PROF_WRITE].count ?
+             fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total, sample_rate,
+                                fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0;
+
+    /* reverb load (%) */
+    reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ?
+             fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total,
+                                sample_rate,
+                                fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0;
+
+    /* chorus load (%) */
+    chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ?
+             fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total,
+                                sample_rate,
+                                fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0;
+
+    /* total voices load: total - reverb - chorus (%) */
+    voices = total - reverb - chorus;
+
+    /* One voice load (%): all_voices / n_voices. */
+    all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?
+                 fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total,
+                                    sample_rate,
+                                    fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0;
+
+    voice = n_voices ?  all_voices / n_voices : 0;
+
+    /* estimated maximum voices number */
+    if(voice > 0)
+    {
+        FLUID_SNPRINTF(max_voices_available, sizeof(max_voices_available),
+                       "%17d", (unsigned int)((100.0 - reverb - chorus) / voice));
+        pmax_voices = max_voices_available;
+    }
+    else
+    {
+        pmax_voices = max_voices_not_available;
+    }
 
-#endif /* WITH_PROFILING */
+    /* Now prints data */
+    fluid_ostream_printf(out,
+                         " ------------------------------------------------------------------------------\n");
+    fluid_ostream_printf(out,
+                         " Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n",
+                         sample_rate, 1000000.0 / sample_rate);
+    fluid_ostream_printf(out,
+                         " ------------------------------------------------------------------------------\n");
+    fluid_ostream_printf(out,
+                         " nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n");
+    fluid_ostream_printf(out,
+                         " -------|---------|---------|----------|---------|---------|-------------------\n");
+    fluid_ostream_printf(out,
+                         "%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices,
+                         reverb, chorus, voice, pmax_voices);
+}
+
+/*
+* prints profiling data (used by profile shell command: prof_start).
+* The function is an internal profiling API between the "profile" command
+* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
+*
+* @param sample_rate the sample rate of audio output.
+* @param out output stream device.
+*
+* When print mode is 1, the function prints all the informations (see below).
+* When print mode is 0, the fonction prints only the cpu loads.
+*
+* ------------------------------------------------------------------------------
+* Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond)
+* ------------------------------------------------------------------------------
+* Code under profiling       |Voices|       Duration (microsecond)   |  Load(%)
+*                            |   nbr|       min|       avg|       max|
+* ---------------------------|------|--------------------------------|----------
+* synth_write_* ------------>|   250|      3.91|   2188.82|   3275.00|  41.544
+* synth_one_block ---------->|   250|   1150.70|   2273.56|   3241.47|  41.100
+* synth_one_block:clear ---->|   250|      3.07|      4.62|     61.18|   0.084
+* synth_one_block:one voice->|     1|      4.19|      9.02|   1044.27|   0.163
+* synth_one_block:all voices>|   250|   1138.41|   2259.11|   3217.73|  40.839
+* synth_one_block:reverb --->| no profiling available
+* synth_one_block:chorus --->| no profiling available
+* voice:note --------------->| no profiling available
+* voice:release ------------>| no profiling available
+* ------------------------------------------------------------------------------
+* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices
+* ------------------------------------------------------------------------------
+* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices
+* -------|---------|---------|----------|---------|---------|-------------------
+*     250|   41.544|   41.544|     0.000|    0.000|    0.163|              612
+*/
+void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out)
+{
+    int i;
+
+    if(fluid_profile_print)
+    {
+        /* print all details: Duration(microsecond) and cpu loads(%) */
+        fluid_ostream_printf(out,
+                             " ------------------------------------------------------------------------------\n");
+        fluid_ostream_printf(out,
+                             " Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n",
+                             sample_rate, 1000000.0 / sample_rate);
+        fluid_ostream_printf(out,
+                             " ------------------------------------------------------------------------------\n");
+        fluid_ostream_printf(out,
+                             " Code under profiling       |Voices|       Duration (microsecond)   |  Load(%%)\n");
+        fluid_ostream_printf(out,
+                             "                            |   nbr|       min|       avg|       max|\n");
+        fluid_ostream_printf(out,
+                             " ---------------------------|------|--------------------------------|----------\n");
+
+        for(i = 0; i < FLUID_PROFILE_NBR; i++)
+        {
+            unsigned int count = fluid_profile_data[i].count;
+
+            if(count > 0)
+            {
+                /* data are available */
+
+                if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS)
+                {
+                    double load = fluid_profile_load(fluid_profile_data[i].total, sample_rate,
+                                                     fluid_profile_data[i].n_samples);
+                    fluid_ostream_printf(out, " %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n",
+                                         fluid_profile_data[i].description, /* code under profiling */
+                                         fluid_profile_data[i].n_voices / count, /* voices number */
+                                         fluid_profile_data[i].min,              /* minimum duration */
+                                         fluid_profile_data[i].total / count,    /* average duration */
+                                         fluid_profile_data[i].max,              /* maximum duration */
+                                         load);                                  /* cpu load */
+                }
+                else
+                {
+                    /* note and release duration */
+                    fluid_ostream_printf(out, " %s|%6d|%10.0f|%10.0f|%10.0f|\n",
+                                         fluid_profile_data[i].description, /* code under profiling */
+                                         fluid_profile_data[i].n_voices / count,
+                                         fluid_profile_data[i].min,              /* minimum duration */
+                                         fluid_profile_data[i].total / count,    /* average duration */
+                                         fluid_profile_data[i].max);             /* maximum duration */
+                }
+            }
+            else
+            {
+                /* data aren't available */
+                fluid_ostream_printf(out,
+                                     " %s| no profiling available\n", fluid_profile_data[i].description);
+            }
+        }
+    }
+
+    /* prints cpu loads only */
+    fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */
+}
+
+/*
+ Returns true if the user cancels the current profiling measurement.
+ Actually this is implemented using the <ENTER> key. To add this functionality:
+ 1) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h.
+ 2) Adds the necessary code inside fluid_profile_is_cancel().
+
+ When FLUID_PROFILE_CANCEL is not defined, the function return FALSE.
+*/
+int fluid_profile_is_cancel_req(void)
+{
+#ifdef FLUID_PROFILE_CANCEL
+
+#if defined(WIN32)      /* Windows specific stuff */
+    /* Profile cancellation is supported for Windows */
+    /* returns TRUE if key <ENTER> is depressed */
+    return(GetAsyncKeyState(VK_RETURN) & 0x1);
+
+#elif defined(__OS2__)  /* OS/2 specific stuff */
+    /* Profile cancellation isn't yet supported for OS2 */
+    /* For OS2, replaces the following  line with the function that returns
+    true when the keyboard key <ENTER> is depressed */
+    return FALSE; /* default value */
+
+#else   /* POSIX stuff */
+    /* Profile cancellation is supported for Linux */
+    /* returns true is <ENTER> is depressed */
+    {
+        /* Here select() is used to poll the standard input to see if an input
+         is ready. As the standard input is usually buffered, the user
+         needs to depress <ENTER> to set the input to a "ready" state.
+        */
+        struct timeval tv;
+        fd_set fds;    /* just one fds need to be polled */
+        tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */
+        tv.tv_usec = 0;
+        FD_ZERO(&fds); /* reset fds */
+        FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */
+        select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */
+        return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */
+    }
+#endif /* OS stuff */
+
+#else /* FLUID_PROFILE_CANCEL not defined */
+    return FALSE; /* default value */
+#endif /* FLUID_PROFILE_CANCEL */
+}
 
+/**
+* Returns status used in shell command "prof_start".
+* The function is an internal profiling API between the "profile" command
+* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
+*
+* @return status
+* - PROFILE_READY profiling data are ready.
+* - PROFILE_RUNNING, profiling data are still under acquisition.
+* - PROFILE_CANCELED, acquisition has been cancelled by the user.
+* - PROFILE_STOP, no acquisition in progress.
+*
+* When status is PROFILE_RUNNING, the caller can do passive waiting, or other
+* work before recalling the function later.
+*/
+int fluid_profile_get_status(void)
+{
+    /* Checks if user has requested to cancel the current measurement */
+    /* Cancellation must have precedence over other status */
+    if(fluid_profile_is_cancel_req())
+    {
+        fluid_profile_start_stop(0, 0); /* stops the measurement */
+        return PROFILE_CANCELED;
+    }
+
+    switch(fluid_profile_status)
+    {
+    case PROFILE_READY:
+        return PROFILE_READY; /* profiling data are ready */
+
+    case PROFILE_START:
+        return PROFILE_RUNNING;/* profiling data are under acquisition */
+
+    default:
+        return PROFILE_STOP;
+    }
+}
 
+/**
+*  Starts or stops profiling measurement.
+*  The function is an internal profiling API between the "profile" command
+*  prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
+*
+*  @param end_tick end position of the measure (in ticks).
+*  - If end_tick is greater then 0, the function starts a measure if a measure
+*    isn't running. If a measure is already running, the function does nothing
+*    and returns.
+*  - If end_tick is 0, the function stops a measure.
+*  @param clear_data,
+*  - If clear_data is 0, the function clears fluid_profile_data before starting
+*    a measure, otherwise, the data from the started measure will be accumulated
+*    within fluid_profile_data.
+*/
+void fluid_profile_start_stop(unsigned int end_ticks, short clear_data)
+{
+    if(end_ticks)    /* This is a "start" request */
+    {
+        /* Checks if a measure is already running */
+        if(fluid_profile_status != PROFILE_START)
+        {
+            short i;
+            fluid_profile_end_ticks = end_ticks;
+
+            /* Clears profile data */
+            if(clear_data == 0)
+                for(i = 0; i < FLUID_PROFILE_NBR; i++)
+                {
+                    fluid_profile_data[i].min = 1e10;/* min sets to max value */
+                    fluid_profile_data[i].max = 0;   /* maximum sets to min value */
+                    fluid_profile_data[i].total = 0; /* total duration microsecond */
+                    fluid_profile_data[i].count = 0;    /* data count */
+                    fluid_profile_data[i].n_voices = 0; /* voices number */
+                    fluid_profile_data[i].n_samples = 0;/* audio samples number */
+                }
+
+            fluid_profile_status = PROFILE_START;      /* starts profiling */
+        }
+
+        /* else do nothing when profiling is already started */
+    }
+    else /* This is a "stop" request */
+    {
+        /* forces the current running profile (if any) to stop */
+        fluid_profile_status = PROFILE_STOP;   /* stops profiling */
+    }
+}
+
+#endif /* WITH_PROFILING */
 
 /***************************************************************
  *
@@ -576,25 +967,29 @@ void fluid_profiling_print(void)
 /* 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)
+new_fluid_cond(void)
 {
-  if (!g_thread_supported ()) g_thread_init (NULL);
-  return g_cond_new ();
+    if(!g_thread_supported())
+    {
+        g_thread_init(NULL);
+    }
+
+    return g_cond_new();
 }
 
 #endif
 
 static gpointer
-fluid_thread_high_prio (gpointer data)
+fluid_thread_high_prio(gpointer data)
 {
-  fluid_thread_info_t *info = data;
+    fluid_thread_info_t *info = data;
 
-  fluid_thread_self_set_prio (info->prio_level);
+    fluid_thread_self_set_prio(info->prio_level);
 
-  info->func (info->data);
-  FLUID_FREE (info);
+    info->func(info->data);
+    FLUID_FREE(info);
 
-  return NULL;
+    return NULL;
 }
 
 /**
@@ -607,59 +1002,78 @@ fluid_thread_high_prio (gpointer data)
  * @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)
+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;
+    GThread *thread;
+    fluid_thread_info_t *info;
+    GError *err = NULL;
 
-  g_return_val_if_fail (func != NULL, 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)
+    /* 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())
     {
-      FLUID_LOG(FLUID_ERR, "Out of memory");
-      return NULL;
+        g_thread_init(NULL);
     }
 
-    info->func = func;
-    info->data = data;
-    info->prio_level = prio_level;
+#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);
+        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);
+        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
+    {
+        thread = g_thread_try_new(name, (GThreadFunc)func, data, &err);
+    }
+
 #else
-  else thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err);
+    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(!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
+
+    if(detach)
+    {
+        g_thread_unref(thread);    // Release thread reference, if caller wants to detach
+    }
+
 #endif
 
-  return thread;
+    return thread;
 }
 
 /**
@@ -667,9 +1081,9 @@ new_fluid_thread (const char *name, fluid_thread_func_t func, void *data, int pr
  * @param thread Thread to free
  */
 void
-delete_fluid_thread(fluid_thread_tthread)
+delete_fluid_thread(fluid_thread_t *thread)
 {
-  /* Threads free themselves when they quit, nothing to do */
+    /* Threads free themselves when they quit, nothing to do */
 }
 
 /**
@@ -678,115 +1092,142 @@ delete_fluid_thread(fluid_thread_t* thread)
  * @return FLUID_OK
  */
 int
-fluid_thread_join(fluid_thread_tthread)
+fluid_thread_join(fluid_thread_t *thread)
 {
-  g_thread_join (thread);
+    g_thread_join(thread);
 
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 
-static void
-fluid_timer_run (void *data)
+static fluid_thread_return_t
+fluid_timer_run(void *data)
 {
-  fluid_timer_t *timer;
-  int count = 0;
-  int cont;
-  long start;
-  long delay;
+    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();
 
-  timer = (fluid_timer_t *)data;
+    while(timer->cont)
+    {
+        cont = (*timer->callback)(timer->data, fluid_curtime() - start);
 
-  /* keep track of the start time for absolute positioning */
-  start = fluid_curtime ();
+        count++;
 
-  while (timer->cont)
-  {
-    cont = (*timer->callback)(timer->data, fluid_curtime() - start);
+        if(!cont)
+        {
+            break;
+        }
 
-    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);
 
-    /* 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);
-  }
+        if(delay > 0)
+        {
+            fluid_msleep(delay);
+        }
+    }
 
-  FLUID_LOG (FLUID_DBG, "Timer thread finished");
+    FLUID_LOG(FLUID_DBG, "Timer thread finished");
 
-  if (timer->auto_destroy)
-    FLUID_FREE (timer);
+    if(timer->auto_destroy)
+    {
+        FLUID_FREE(timer);
+    }
 
-  return;
+    return FLUID_THREAD_RETURN_VALUE;
 }
 
-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 *
+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;
+    fluid_timer_t *timer;
 
-  timer = FLUID_NEW (fluid_timer_t);
+    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)
+    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_FREE (timer);
-      return NULL;
+        fluid_timer_run(timer);   /* Run directly, instead of as a separate thread */
+
+        if(auto_destroy)
+        {
+            /* do NOT return freed memory */
+            return NULL;
+        }
     }
-  }
-  else fluid_timer_run (timer);  /* Run directly, instead of as a separate thread */
 
-  return timer;
+    return timer;
 }
 
-int
-delete_fluid_timer (fluid_timer_t *timer)
+void
+delete_fluid_timer(fluid_timer_t *timer)
 {
-  int auto_destroy = timer->auto_destroy;
+    int auto_destroy;
+    fluid_return_if_fail(timer != NULL);
 
-  timer->cont = 0;
-  fluid_timer_join (timer);
+    auto_destroy = timer->auto_destroy;
 
-  /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */
+    timer->cont = 0;
+    fluid_timer_join(timer);
 
-  if (!auto_destroy) FLUID_FREE (timer);
+    /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */
 
-  return FLUID_OK;
+    if(!auto_destroy)
+    {
+        FLUID_FREE(timer);
+    }
 }
 
 int
-fluid_timer_join (fluid_timer_t *timer)
+fluid_timer_join(fluid_timer_t *timer)
 {
-  int auto_destroy;
+    int auto_destroy;
 
-  if (timer->thread)
-  {
-    auto_destroy = timer->auto_destroy;
-    fluid_thread_join (timer->thread);
+    if(timer->thread)
+    {
+        auto_destroy = timer->auto_destroy;
+        fluid_thread_join(timer->thread);
 
-    if (!auto_destroy) timer->thread = NULL;
-  }
+        if(!auto_destroy)
+        {
+            timer->thread = NULL;
+        }
+    }
 
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 
@@ -801,9 +1242,9 @@ fluid_timer_join (fluid_timer_t *timer)
  * @return Standard in stream.
  */
 fluid_istream_t
-fluid_get_stdin (void)
+fluid_get_stdin(void)
 {
-  return STDIN_FILENO;
+    return STDIN_FILENO;
 }
 
 /**
@@ -811,9 +1252,9 @@ fluid_get_stdin (void)
  * @return Standard out stream.
  */
 fluid_ostream_t
-fluid_get_stdout (void)
+fluid_get_stdout(void)
 {
-  return STDOUT_FILENO;
+    return STDOUT_FILENO;
 }
 
 /**
@@ -821,31 +1262,34 @@ fluid_get_stdout (void)
  * @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)
+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(in == fluid_get_stdin())
+    {
+        char *line;
 
-    if (line == NULL)
-      return -1;
+        line = readline(prompt);
 
-    snprintf(buf, len, "%s", line);
-    buf[len - 1] = 0;
+        if(line == NULL)
+        {
+            return -1;
+        }
 
-    free(line);
-    return 1;
-  }
-  else
+        FLUID_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);
-  }
+    {
+        fluid_ostream_printf(out, "%s", prompt);
+        return fluid_istream_gets(in, buf, len);
+    }
 }
 
 /**
@@ -856,49 +1300,67 @@ fluid_istream_readline (fluid_istream_t in, fluid_ostream_t out, char* prompt,
  * @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)
+fluid_istream_gets(fluid_istream_t in, char *buf, int len)
 {
-  char c;
-  int n;
+    char c;
+    int n;
 
-  buf[len - 1] = 0;
+    buf[len - 1] = 0;
 
-  while (--len > 0)
-  {
+    while(--len > 0)
+    {
 #ifndef WIN32
-    n = read(in, &c, 1);
-    if (n == -1) return -1;
+        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;
-    }
+        /* Handle read differently depending on if its a socket or file descriptor */
+        if(!(in & FLUID_SOCKET_FLAG))
+        {
+            n = read(in, &c, 1);
+
+            if(n == -1)
+            {
+                return -1;
+            }
+        }
+        else
+        {
+            n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0);
+
+            if(n == SOCKET_ERROR)
+            {
+                return -1;
+            }
+        }
 
-    if ((c == '\n'))
-    {
-      *buf++ = 0;
-      return 1;
-    }
+#endif
 
-    /* Store all characters excluding CR */
-    if (c != '\r') *buf++ = c;
-  }
+        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;
+    return -1;
 }
 
 /**
@@ -909,390 +1371,297 @@ fluid_istream_gets (fluid_istream_t in, char* buf, int len)
  * @return Number of bytes written or -1 on error
  */
 int
-fluid_ostream_printf (fluid_ostream_t out, char* format, ...)
+fluid_ostream_printf(fluid_ostream_t out, const char *format, ...)
 {
-  char buf[4096];
-  va_list args;
-  int len;
+    char buf[4096];
+    va_list args;
+    int len;
 
-  va_start (args, format);
-  len = vsnprintf (buf, 4095, format, args);
-  va_end (args);
+    va_start(args, format);
+    len = FLUID_VSNPRINTF(buf, 4095, format, args);
+    va_end(args);
 
-  if (len == 0)
-  {
-    return 0;
-  }
+    if(len == 0)
+    {
+        return 0;
+    }
 
-  if (len < 0)
-  {
-    printf("fluid_ostream_printf: buffer overflow");
-    return -1;
-  }
+    if(len < 0)
+    {
+        printf("fluid_ostream_printf: buffer overflow");
+        return -1;
+    }
 
-  buf[4095] = 0;
+    buf[4095] = 0;
 
 #ifndef WIN32
-  return write (out, buf, strlen (buf));
+    return write(out, buf, FLUID_STRLEN(buf));
 #else
-  {
-    int retval;
+    {
+        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));
+        /* Handle write differently depending on if its a socket or file descriptor */
+        if(!(out & FLUID_SOCKET_FLAG))
+        {
+            return write(out, buf, FLUID_STRLEN(buf));
+        }
 
-    /* Socket */
-    retval = send (out & ~WIN32_SOCKET_FLAG, buf, strlen (buf), 0);
+        /* Socket */
+        retval = send(out & ~FLUID_SOCKET_FLAG, buf, FLUID_STRLEN(buf), 0);
 
-    return retval != SOCKET_ERROR ? retval : -1;
-  }
+        return retval != SOCKET_ERROR ? retval : -1;
+    }
 #endif
 }
 
-#if 0 // Ardour says: no, thanks
+#ifdef NETWORK_SUPPORT
+
 int fluid_server_socket_join(fluid_server_socket_t *server_socket)
 {
-  return fluid_thread_join (server_socket->thread);
+    return fluid_thread_join(server_socket->thread);
 }
 
+static int fluid_socket_init(void)
+{
+#ifdef _WIN32
+    WSADATA wsaData;
+    int res = WSAStartup(MAKEWORD(2, 2), &wsaData);
 
-#ifndef WIN32           // Not win32?
-
-#define SOCKET_ERROR -1
+    if(res != 0)
+    {
+        FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res);
+        return FLUID_FAILED;
+    }
 
-fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
-{
-  return sock;
-}
+#endif
 
-fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
-{
-  return sock;
+    return FLUID_OK;
 }
 
-void fluid_socket_close(fluid_socket_t sock)
+static void fluid_socket_cleanup(void)
 {
-  if (sock != INVALID_SOCKET)
-    close (sock);
+#ifdef _WIN32
+    WSACleanup();
+#endif
 }
 
-static void
-fluid_server_socket_run (void *data)
+static int fluid_socket_get_error(void)
 {
-  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];
+#ifdef _WIN32
+    return (int)WSAGetLastError();
 #else
-  struct sockaddr_in addr;
-  char straddr[INET_ADDRSTRLEN];
+    return errno;
 #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_istream_t fluid_socket_get_istream(fluid_socket_t sock)
+{
+    return sock | FLUID_SOCKET_FLAG;
+}
 
-    FLUID_LOG (FLUID_DBG, "New client connection");
+fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock)
+{
+    return sock | FLUID_SOCKET_FLAG;
+}
 
-    if (client_socket == INVALID_SOCKET)
+void fluid_socket_close(fluid_socket_t sock)
+{
+    if(sock != 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));
+#ifdef _WIN32
+        closesocket(sock);
+
 #else
-      inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+        close(sock);
 #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)
+static fluid_thread_return_t fluid_server_socket_run(void *data)
 {
-  fluid_server_socket_t* server_socket;
-#ifdef IPV6
-  struct sockaddr_in6 addr;
+    fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
+    fluid_socket_t client_socket;
+#ifdef IPV6_SUPPORT
+    struct sockaddr_in6 addr;
 #else
-  struct sockaddr_in addr;
+    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);
+#ifdef HAVE_INETNTOP
+#ifdef IPV6_SUPPORT
+    char straddr[INET6_ADDRSTRLEN];
 #else
+    char straddr[INET_ADDRSTRLEN];
+#endif /* IPV6_SUPPORT */
+#endif /* HAVE_INETNTOP */
 
-  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;
-  }
+    socklen_t addrlen = sizeof(addr);
+    int r;
+    FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
 
-  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"
+    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", fluid_socket_get_error());
+            }
+
+            server_socket->cont = 0;
+            return FLUID_THREAD_RETURN_VALUE;
+        }
+        else
+        {
+#ifdef HAVE_INETNTOP
+
+#ifdef IPV6_SUPPORT
+            inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
+#else
+            inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+#endif
 
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
+            r = server_socket->func(server_socket->data, client_socket,
+                                    straddr);
+#else
+            r = server_socket->func(server_socket->data, client_socket,
+                                    inet_ntoa(addr.sin_addr));
 #endif
 
-fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
-{
-  return sock | WIN32_SOCKET_FLAG;
-}
+            if(r != 0)
+            {
+                fluid_socket_close(client_socket);
+            }
+        }
+    }
 
-fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
-{
-  return sock | WIN32_SOCKET_FLAG;
-}
+    FLUID_LOG(FLUID_DBG, "Server closing");
 
-void fluid_socket_close (fluid_socket_t sock)
-{
-  if (sock != INVALID_SOCKET)
-    closesocket (sock);
+    return FLUID_THREAD_RETURN_VALUE;
 }
 
-static void fluid_server_socket_run (void *data)
+fluid_server_socket_t *
+new_fluid_server_socket(int port, fluid_server_func_t func, 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];
+    fluid_server_socket_t *server_socket;
+#ifdef IPV6_SUPPORT
+    struct sockaddr_in6 addr;
 #else
-  struct sockaddr_in addr;
-  char straddr[INET_ADDRSTRLEN];
+    struct sockaddr_in addr;
 #endif
-  socklen_t addrlen = sizeof (addr);
-  int r;
-  FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
 
-  FLUID_LOG(FLUID_DBG, "Server listening for connections");
+    fluid_socket_t sock;
 
-  while (server_socket->cont)
-  {
-    client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen);
+    fluid_return_val_if_fail(func != NULL, NULL);
 
-    FLUID_LOG (FLUID_DBG, "New client connection");
-
-    if (client_socket == INVALID_SOCKET)
+    if(fluid_socket_init() != FLUID_OK)
     {
-      if (server_socket->cont)
-       FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", WSAGetLastError ());
-
-      server_socket->cont = 0;
-      return;
+        return NULL;
     }
-    else
+
+#ifdef IPV6_SUPPORT
+    sock = socket(AF_INET6, SOCK_STREAM, 0);
+
+    if(sock == INVALID_SOCKET)
     {
-#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_ERR, "Failed to create server socket: %ld", fluid_socket_get_error());
+        fluid_socket_cleanup();
+        return NULL;
     }
-  }
 
-  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;
+    FLUID_MEMSET(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons((uint16_t)port);
+    addr.sin6_addr = in6addr_any;
 #else
-  struct sockaddr_in addr;
-#endif
-
-  fluid_socket_t sock;
-  WSADATA wsaData;
-  int retval;
 
-  g_return_val_if_fail (func != NULL, NULL);
+    sock = socket(AF_INET, SOCK_STREAM, 0);
 
-  // Win32 requires initialization of winsock
-  retval = WSAStartup (MAKEWORD (2,2), &wsaData);
+    if(sock == INVALID_SOCKET)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to create server socket: %ld", fluid_socket_get_error());
+        fluid_socket_cleanup();
+        return NULL;
+    }
 
-  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
+    FLUID_MEMSET(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons((uint16_t)port);
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
 
-  sock = socket (AF_INET, SOCK_STREAM, 0);
+    if(bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %ld", fluid_socket_get_error());
+        fluid_socket_close(sock);
+        fluid_socket_cleanup();
+        return NULL;
+    }
 
-  if (sock == INVALID_SOCKET)
-  {
-    FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
-    WSACleanup ();
-    return NULL;
-  }
+    if(listen(sock, SOMAXCONN) == SOCKET_ERROR)
+    {
+        FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %ld", fluid_socket_get_error());
+        fluid_socket_close(sock);
+        fluid_socket_cleanup();
+        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));
+    server_socket = FLUID_NEW(fluid_server_socket_t);
 
-  if (retval == SOCKET_ERROR)
-  {
-    FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", WSAGetLastError ());
-    fluid_socket_close (sock);
-    WSACleanup ();
-    return NULL;
-  }
+    if(server_socket == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        fluid_socket_close(sock);
+        fluid_socket_cleanup();
+        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->socket = sock;
+    server_socket->func = func;
+    server_socket->data = data;
+    server_socket->cont = 1;
 
-  server_socket = FLUID_NEW (fluid_server_socket_t);
+    server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
+                            0, FALSE);
 
-  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;
-  }
+    if(server_socket->thread == NULL)
+    {
+        FLUID_FREE(server_socket);
+        fluid_socket_close(sock);
+        fluid_socket_cleanup();
+        return NULL;
+    }
 
-  return server_socket;
+    return server_socket;
 }
 
-int delete_fluid_server_socket(fluid_server_socket_t *server_socket)
+void delete_fluid_server_socket(fluid_server_socket_t *server_socket)
 {
-  server_socket->cont = 0;
+    fluid_return_if_fail(server_socket != NULL);
 
-  if (server_socket->socket != INVALID_SOCKET)
-    fluid_socket_close (server_socket->socket);
+    server_socket->cont = 0;
 
-  if (server_socket->thread)
-    delete_fluid_thread (server_socket->thread);
+    if(server_socket->socket != INVALID_SOCKET)
+    {
+        fluid_socket_close(server_socket->socket);
+    }
 
-  FLUID_FREE (server_socket);
+    if(server_socket->thread)
+    {
+        fluid_thread_join(server_socket->thread);
+        delete_fluid_thread(server_socket->thread);
+    }
 
-  WSACleanup ();        // Should be called the same number of times as WSAStartup
+    FLUID_FREE(server_socket);
 
-  return FLUID_OK;
+    // Should be called the same number of times as fluid_socket_init()
+    fluid_socket_cleanup();
 }
 
-#endif
-#endif
+#endif // NETWORK_SUPPORT
index 4953515692682becdc243980182c14013eec73bc..d95f6159f2e5592da593d295cb0d588ec346c29e 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #ifndef _FLUID_SYS_H
 #define _FLUID_SYS_H
 
-#include <glib.h>
 #include "fluidsynth_priv.h"
 
+#ifdef LADSPA
+#include <gmodule.h>
+#endif
+
+#include <glib/gstdio.h>
 
 /**
  * Macro used for safely accessing a message from a GError and using a default
  */
 #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 */
+#if defined(__INTEL_COMPILER)
+#define FLUID_RESTRICT restrict
+#elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
+#define FLUID_RESTRICT __restrict__
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+#define FLUID_RESTRICT __restrict
+#else
+#warning "Dont know how this compiler handles restrict pointers, refuse to use them."
+#define FLUID_RESTRICT
+#endif
 
-#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_MEMBER_SIZE(struct, member)  ( sizeof (((struct *)0)->member) )
 
 #define FLUID_IS_BIG_ENDIAN       (G_BYTE_ORDER == G_BIG_ENDIAN)
 
-/*
- * Utility functions
- */
-char *fluid_strtok (char **str, char *delim);
+#define FLUID_LE32TOH(x)          GINT32_FROM_LE(x)
+#define FLUID_LE16TOH(x)          GINT16_FROM_LE(x)
 
 
-/**
+#define fluid_return_if_fail(cond) \
+if(cond) \
+    ; \
+else \
+    return
 
-  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
-};
+#define fluid_return_val_if_fail(cond, val) \
+ fluid_return_if_fail(cond) (val)
 
-int fluid_debug(int level, char * fmt, ...);
 
-#else
-#define fluid_debug
-#endif
+/*
+ * Utility functions
+ */
+char *fluid_strtok(char **str, const char *delim);
 
 
 #if defined(__OS2__)
@@ -112,17 +112,17 @@ double fluid_utime(void);
 
 /* if the callback function returns 1 the timer will continue; if it
    returns 0 it will stop */
-typedef int (*fluid_timer_callback_t)(voiddata, unsigned int msec);
+typedef int (*fluid_timer_callback_t)(void *data, unsigned int msec);
 
 typedef struct _fluid_timer_t fluid_timer_t;
 
-fluid_timer_tnew_fluid_timer(int msec, fluid_timer_callback_t callback,
-                               voiddata, int new_thread, int auto_destroy,
+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_ttimer);
-int fluid_timer_stop(fluid_timer_ttimer);
+void 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))
@@ -155,19 +155,20 @@ typedef GMutex    fluid_cond_mutex_t;
 #define fluid_cond_mutex_unlock(m)      g_mutex_unlock(m)
 
 static FLUID_INLINE fluid_cond_mutex_t *
-new_fluid_cond_mutex (void)
+new_fluid_cond_mutex(void)
 {
-  GMutex *mutex;
-  mutex = g_new (GMutex, 1);
-  g_mutex_init (mutex);
-  return (mutex);
+    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)
+delete_fluid_cond_mutex(fluid_cond_mutex_t *m)
 {
-  g_mutex_clear (m);
-  g_free (m);
+    fluid_return_if_fail(m != NULL);
+    g_mutex_clear(m);
+    g_free(m);
 }
 
 /* Thread condition signaling */
@@ -177,19 +178,20 @@ typedef GCond fluid_cond_t;
 #define fluid_cond_wait(cond, mutex)    g_cond_wait(cond, mutex)
 
 static FLUID_INLINE fluid_cond_t *
-new_fluid_cond (void)
+new_fluid_cond(void)
 {
-  GCond *cond;
-  cond = g_new (GCond, 1);
-  g_cond_init (cond);
-  return (cond);
+    GCond *cond;
+    cond = g_new(GCond, 1);
+    g_cond_init(cond);
+    return (cond);
 }
 
 static FLUID_INLINE void
-delete_fluid_cond (fluid_cond_t *cond)
+delete_fluid_cond(fluid_cond_t *cond)
 {
-  g_cond_clear (cond);
-  g_free (cond);
+    fluid_return_if_fail(cond != NULL);
+    g_cond_clear(cond);
+    g_free(cond);
 }
 
 /* Thread private data */
@@ -211,10 +213,10 @@ typedef GStaticMutex fluid_mutex_t;
 #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 { \
+#define fluid_mutex_init(_m)      do { \
   if (!g_thread_supported ()) g_thread_init (NULL); \
   g_static_mutex_init (&(_m)); \
-} G_STMT_END;
+} while(0)
 
 /* Recursive lock capable mutex */
 typedef GStaticRecMutex fluid_rec_mutex_t;
@@ -222,10 +224,10 @@ typedef GStaticRecMutex fluid_rec_mutex_t;
 #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 { \
+#define fluid_rec_mutex_init(_m)      do { \
   if (!g_thread_supported ()) g_thread_init (NULL); \
   g_static_rec_mutex_init (&(_m)); \
-} G_STMT_END;
+} while(0)
 
 /* Dynamically allocated mutex suitable for fluid_cond_t use */
 typedef GMutex    fluid_cond_mutex_t;
@@ -234,15 +236,19 @@ typedef GMutex    fluid_cond_mutex_t;
 #define fluid_cond_mutex_unlock(m)      g_mutex_unlock(m)
 
 static FLUID_INLINE fluid_cond_mutex_t *
-new_fluid_cond_mutex (void)
+new_fluid_cond_mutex(void)
 {
-  if (!g_thread_supported ()) g_thread_init (NULL);
-  return g_mutex_new ();
+    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);
+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)
@@ -254,10 +260,10 @@ typedef GStaticPrivate fluid_private_t;
 #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 { \
+#define fluid_private_init(_priv)                  do { \
   if (!g_thread_supported ()) g_thread_init (NULL); \
   g_static_private_init (&(_priv)); \
-} G_STMT_END;
+} while(0)
 
 #endif
 
@@ -265,7 +271,6 @@ typedef GStaticPrivate fluid_private_t;
 /* 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)
@@ -275,9 +280,13 @@ typedef GStaticPrivate fluid_private_t;
 #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)
+#define fluid_atomic_int_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)
+#define fluid_atomic_int_add(_pi, _add) \
+  g_atomic_int_exchange_and_add(_pi, _add)
 #endif
 
 #define fluid_atomic_pointer_get(_pp)           g_atomic_pointer_get(_pp)
@@ -288,115 +297,276 @@ typedef GStaticPrivate fluid_private_t;
 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);
+    int32_t 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;
+    int32_t ival;
+    float fval;
+    ival = fluid_atomic_int_get((volatile int *)fptr);
+    memcpy(&fval, &ival, 4);
+    return fval;
 }
 
 
 /* Threads */
 
+/* other thread implementations might change this for their needs */
+typedef void *fluid_thread_return_t;
+/* static return value for thread functions which requires a return value */
+#define FLUID_THREAD_RETURN_VALUE (NULL)
+
 typedef GThread fluid_thread_t;
-typedef void (*fluid_thread_func_t)(void* data);
+typedef fluid_thread_return_t (*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_tnew_fluid_thread(const char *name, fluid_thread_func_t func, void *data,
+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);
+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);
+
+/* Dynamic Module Loading, currently only used by LADSPA subsystem */
+#ifdef LADSPA
+
+typedef GModule fluid_module_t;
+
+#define fluid_module_open(_name)        g_module_open((_name), G_MODULE_BIND_LOCAL)
+#define fluid_module_close(_mod)        g_module_close(_mod)
+#define fluid_module_error()            g_module_error()
+#define fluid_module_name(_mod)         g_module_name(_mod)
+#define fluid_module_symbol(_mod, _name, _ptr) g_module_symbol((_mod), (_name), (_ptr))
+
+#endif /* LADSPA */
 
 /* 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, ...);
+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, const 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);
+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_tsock);
+fluid_server_socket_t *new_fluid_server_socket(int port, fluid_server_func_t func, void *data);
+void 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);
 
+/* File access */
+#if !GLIB_CHECK_VERSION(2, 26, 0)
+    /* GStatBuf has not been introduced yet, manually typedef to what they had at that time:
+     * https://github.com/GNOME/glib/blob/e7763678b56e3be073cc55d707a6e92fc2055ee0/glib/gstdio.h#L98-L115
+     */
+    #if defined(WIN32) || HAVE_WINDOWS_H // somehow reliably mock G_OS_WIN32??
+        #if defined (_MSC_VER) && !defined(_WIN64)
+        typedef struct _stat32 fluid_stat_buf_t;
+        #else
+        typedef struct _stat fluid_stat_buf_t;
+        #endif
+    #else
+    /* posix, OS/2, etc. */
+    typedef struct stat fluid_stat_buf_t;
+    #endif
+#else
+typedef GStatBuf fluid_stat_buf_t;
+#endif
+#define fluid_stat(_filename, _statbuf)   g_stat((_filename), (_statbuf))
 
 
 /* Profiling */
+#if WITH_PROFILING
+/** profiling interface beetween Profiling command shell and Audio
+    rendering  API (FluidProfile_0004.pdf- 3.2.2)
+*/
 
+/*
+  -----------------------------------------------------------------------------
+  Shell task side |    Profiling interface              |  Audio task side
+  -----------------------------------------------------------------------------
+  profiling       |    Internal    |      |             |      Audio
+  command   <---> |<-- profling -->| Data |<--macros -->| <--> rendering
+  shell           |    API         |      |             |      API
 
-/**
- * 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
-};
+*/
 
+/* default parameters for shell command "prof_start" in fluid_sys.c */
+#define FLUID_PROFILE_DEFAULT_BANK 0       /* default bank */
+#define FLUID_PROFILE_DEFAULT_PROG 16      /* default prog (organ) */
+#define FLUID_PROFILE_FIRST_KEY 12         /* first key generated */
+#define FLUID_PROFILE_LAST_KEY 108         /* last key generated */
+#define FLUID_PROFILE_DEFAULT_VEL 64       /* default note velocity */
+#define FLUID_PROFILE_VOICE_ATTEN -0.04f   /* gain attenuation per voice (dB) */
 
-#if WITH_PROFILING
 
-void fluid_profiling_print(void);
+#define FLUID_PROFILE_DEFAULT_PRINT 0      /* default print mode */
+#define FLUID_PROFILE_DEFAULT_N_PROF 1     /* default number of measures */
+#define FLUID_PROFILE_DEFAULT_DURATION 500 /* default duration (ms)  */
+
+
+extern unsigned short fluid_profile_notes; /* number of generated notes */
+extern unsigned char fluid_profile_bank;   /* bank,prog preset used by */
+extern unsigned char fluid_profile_prog;   /* generated notes */
+extern unsigned char fluid_profile_print;  /* print mode */
+
+extern unsigned short fluid_profile_n_prof;/* number of measures */
+extern unsigned short fluid_profile_dur;   /* measure duration in ms */
+extern fluid_atomic_int_t fluid_profile_lock ; /* lock between multiple shell */
+/**/
+
+/*----------------------------------------------
+  Internal profiling API (in fluid_sys.c)
+-----------------------------------------------*/
+/* Starts a profiling measure used in shell command "prof_start" */
+void fluid_profile_start_stop(unsigned int end_ticks, short clear_data);
+
+/* Returns status used in shell command "prof_start" */
+int fluid_profile_get_status(void);
+
+/* Prints profiling data used in shell command "prof_start" */
+void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out);
+
+/* Returns True if profiling cancellation has been requested */
+int fluid_profile_is_cancel_req(void);
+
+/* For OS that implement <ENTER> key for profile cancellation:
+ 1) Adds #define FLUID_PROFILE_CANCEL
+ 2) Adds the necessary code inside fluid_profile_is_cancel() see fluid_sys.c
+*/
+#if defined(WIN32)      /* Profile cancellation is supported for Windows */
+#define FLUID_PROFILE_CANCEL
+
+#elif defined(__OS2__)  /* OS/2 specific stuff */
+/* Profile cancellation isn't yet supported for OS2 */
 
+#else   /* POSIX stuff */
+#define FLUID_PROFILE_CANCEL /* Profile cancellation is supported for linux */
+#include <unistd.h> /* STDIN_FILENO */
+#include <sys/select.h> /* select() */
+#endif /* posix */
 
-/** Profiling data. Keep track of min/avg/max values to execute a
+/* logging profiling data (used on synthesizer instance deletion) */
+void fluid_profiling_print(void);
+
+/*----------------------------------------------
+  Profiling Data (in fluid_sys.c)
+-----------------------------------------------*/
+/** Profiling data. Keep track of min/avg/max values to profile a
     piece of code. */
-typedef struct _fluid_profile_data_t {
-  int num;
-  char* description;
-  double min, max, total;
-  unsigned int count;
+typedef struct _fluid_profile_data_t
+{
+    const char *description;        /* name of the piece of code under profiling */
+    double min, max, total;   /* duration (microsecond) */
+    unsigned int count;       /* total count */
+    unsigned int n_voices;    /* voices number */
+    unsigned int n_samples;   /* audio samples number */
 } fluid_profile_data_t;
 
-extern fluid_profile_data_t fluid_profile_data[];
+enum
+{
+    /* commands/status  (profiling interface) */
+    PROFILE_STOP,    /* command to stop a profiling measure */
+    PROFILE_START,   /* command to start a profile measure */
+    PROFILE_READY,   /* status to signal that a profiling measure has finished
+                           and ready to be printed */
+    /*- State returned by fluid_profile_get_status() -*/
+    /* between profiling commands and internal profiling API */
+    PROFILE_RUNNING, /* a profiling measure is running */
+    PROFILE_CANCELED,/* a profiling measure has been canceled */
+};
+
+/* Data interface */
+extern unsigned char fluid_profile_status ;       /* command and status */
+extern unsigned int fluid_profile_end_ticks;      /* ending position (in ticks) */
+extern fluid_profile_data_t fluid_profile_data[]; /* Profiling data */
 
-/** Macro to obtain a time refence used for the profiling */
+/*----------------------------------------------
+  Probes macros
+-----------------------------------------------*/
+/** Macro to obtain a time reference 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; \
+/**
+ * Profile identifier 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_PROFILE_NBR  /* number of profile probes */
+};
+/** Those macros are used to calculate the min/avg/max. Needs a profile number, a
+    time reference, the voices and samples number. */
+
+/* local macro : acquiere data */
+#define fluid_profile_data(_num, _ref, voices, samples)\
+{\
+       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++;\
+       fluid_profile_data[_num].n_voices += voices;\
+       fluid_profile_data[_num].n_samples += samples;\
+       _ref = _now;\
 }
 
+/** Macro to collect data, called from inner functions inside audio
+    rendering API */
+#define fluid_profile(_num, _ref, voices, samples)\
+{\
+       if ( fluid_profile_status == PROFILE_START)\
+       {       /* acquires data */\
+               fluid_profile_data(_num, _ref, voices, samples)\
+       }\
+}
+
+/** Macro to collect data, called from audio rendering API (fluid_write_xxxx()).
+ This macro control profiling ending position (in ticks).
+*/
+#define fluid_profile_write(_num, _ref, voices, samples)\
+{\
+       if (fluid_profile_status == PROFILE_START)\
+       {\
+               /* acquires data first: must be done before checking that profile is
+           finished to ensure at least one valid data sample.
+               */\
+               fluid_profile_data(_num, _ref, voices, samples)\
+               if (fluid_synth_get_ticks(synth) >= fluid_profile_end_ticks)\
+               {\
+                       /* profiling is finished */\
+                       fluid_profile_status = PROFILE_READY;\
+               }\
+       }\
+}
 
 #else
 
@@ -404,11 +574,9 @@ extern fluid_profile_data_t fluid_profile_data[];
 #define fluid_profiling_print()
 #define fluid_profile_ref()  0
 #define fluid_profile_ref_var(name)
-#define fluid_profile(_num,_ref)
-
-#endif
-
-
+#define fluid_profile(_num,_ref,voices, samples)
+#define fluid_profile_write(_num,_ref, voices, samples)
+#endif /* WITH_PROFILING */
 
 /**
 
@@ -442,7 +610,32 @@ extern fluid_profile_data_t fluid_profile_data[];
 #define fluid_clear_fpe()
 #endif
 
-unsigned int fluid_check_fpe_i386(char * explanation_in_case_of_fpe);
+unsigned int fluid_check_fpe_i386(char *explanation_in_case_of_fpe);
 void fluid_clear_fpe_i386(void);
 
+/* System control */
+void fluid_msleep(unsigned int msecs);
+
+/**
+ * Advances the given \c ptr to the next \c alignment byte boundary.
+ * Make sure you've allocated an extra of \c alignment bytes to avoid a buffer overflow.
+ *
+ * @note \c alignment must be a power of two
+ * @return Returned pointer is guarenteed to be aligned to \c alignment boundary and in range \f[ ptr <= returned_ptr < ptr + alignment \f].
+ */
+static FLUID_INLINE void *fluid_align_ptr(const void *ptr, unsigned int alignment)
+{
+    uintptr_t ptr_int = (uintptr_t)ptr;
+    unsigned int offset = ptr_int & (alignment - 1);
+    unsigned int add = (alignment - offset) & (alignment - 1); // advance the pointer to the next alignment boundary
+    ptr_int += add;
+
+    /* assert alignment is power of two */
+    FLUID_ASSERT(!(alignment == 0) && !(alignment & (alignment - 1)));
+
+    return (void *)ptr_int;
+}
+
+#define FLUID_DEFAULT_ALIGNMENT (64U)
+
 #endif /* _FLUID_SYS_H */
index 8977ed6728fef6333d254140b0176330dc1648e2..ee083116a2aadd6efae3ddd12dfd0ba3d9b80507 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_sys.h"
 
 
-fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog)
+fluid_tuning_t *new_fluid_tuning(const char *name, int bank, int prog)
 {
-  fluid_tuning_t* tuning;
-  int i;
+    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 = FLUID_NEW(fluid_tuning_t);
 
-  tuning->name = NULL;
+    if(tuning == NULL)
+    {
+        FLUID_LOG(FLUID_PANIC, "Out of memory");
+        return NULL;
+    }
 
-  if (name != NULL) {
-    tuning->name = FLUID_STRDUP(name);
-  }
+    FLUID_MEMSET(tuning, 0, sizeof(fluid_tuning_t));
 
-  tuning->bank = bank;
-  tuning->prog = prog;
+    if(fluid_tuning_set_name(tuning, name) != FLUID_OK)
+    {
+        delete_fluid_tuning(tuning);
+        return NULL;
+    }
 
-  for (i = 0; i < 128; i++) {
-    tuning->pitch[i] = i * 100.0;
-  }
+    tuning->bank = bank;
+    tuning->prog = prog;
 
-  tuning->refcount = 1;         /* Start with a refcount of 1 */
+    for(i = 0; i < 128; i++)
+    {
+        tuning->pitch[i] = i * 100.0;
+    }
+
+    fluid_atomic_int_set(&tuning->refcount, 1);         /* Start with a refcount of 1 */
 
-  return tuning;
+    return tuning;
 }
 
 /* Duplicate a tuning */
 fluid_tuning_t *
-fluid_tuning_duplicate (fluid_tuning_t *tuning)
+fluid_tuning_duplicate(fluid_tuning_t *tuning)
 {
-  fluid_tuning_t *new_tuning;
-  int i;
+    fluid_tuning_t *new_tuning;
+    int i;
 
-  new_tuning = FLUID_NEW (fluid_tuning_t);
+    new_tuning = FLUID_NEW(fluid_tuning_t);
 
-  if (!new_tuning) {
-    FLUID_LOG (FLUID_PANIC, "Out of memory");
-    return NULL;
-  }
+    if(!new_tuning)
+    {
+        FLUID_LOG(FLUID_PANIC, "Out of memory");
+        return NULL;
+    }
 
-  if (tuning->name)
-  {
-    new_tuning->name = FLUID_STRDUP (tuning->name);
+    FLUID_MEMSET(new_tuning, 0, sizeof(fluid_tuning_t));
 
-    if (!new_tuning->name)
+    if(fluid_tuning_set_name(new_tuning, tuning->name) != FLUID_OK)
     {
-      FLUID_FREE (new_tuning);
-      FLUID_LOG (FLUID_PANIC, "Out of memory");
-      return NULL;
+        delete_fluid_tuning(new_tuning);
+        return NULL;
     }
-  }
-  else new_tuning->name = NULL;
 
-  new_tuning->bank = tuning->bank;
-  new_tuning->prog = tuning->prog;
+    new_tuning->bank = tuning->bank;
+    new_tuning->prog = tuning->prog;
 
-  for (i = 0; i < 128; i++)
-    new_tuning->pitch[i] = tuning->pitch[i];
+    for(i = 0; i < 128; i++)
+    {
+        new_tuning->pitch[i] = tuning->pitch[i];
+    }
 
-  new_tuning->refcount = 1;     /* Start with a refcount of 1 */
+    fluid_atomic_int_set(&new_tuning->refcount, 1);     /* Start with a refcount of 1 */
 
-  return new_tuning;
+    return new_tuning;
 }
 
 void
-delete_fluid_tuning (fluid_tuning_t *tuning)
+delete_fluid_tuning(fluid_tuning_t *tuning)
 {
-  if (tuning->name) FLUID_FREE (tuning->name);
-  FLUID_FREE (tuning);
+    fluid_return_if_fail(tuning != NULL);
+
+    FLUID_FREE(tuning->name);
+    FLUID_FREE(tuning);
 }
 
 /* Add a reference to a tuning object */
 void
-fluid_tuning_ref (fluid_tuning_t *tuning)
+fluid_tuning_ref(fluid_tuning_t *tuning)
 {
-  fluid_return_if_fail (tuning != NULL);
+    fluid_return_if_fail(tuning != NULL);
 
-  fluid_atomic_int_inc (&tuning->refcount);
+    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_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;
-}
+    fluid_return_val_if_fail(tuning != NULL, 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);
-  }
+    /* 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;
+    }
 }
 
-char* fluid_tuning_get_name(fluid_tuning_t* tuning)
+int fluid_tuning_set_name(fluid_tuning_t *tuning, const char *name)
 {
-  return tuning->name;
+    if(tuning->name != NULL)
+    {
+        FLUID_FREE(tuning->name);
+        tuning->name = NULL;
+    }
+
+    if(name != NULL)
+    {
+        tuning->name = FLUID_STRDUP(name);
+
+        if(tuning->name == NULL)
+        {
+            FLUID_LOG(FLUID_ERR, "Out of memory");
+            return FLUID_FAILED;
+        }
+    }
+
+    return FLUID_OK;
 }
 
-static void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch)
+char *fluid_tuning_get_name(fluid_tuning_t *tuning)
 {
-  tuning->pitch[key] = pitch;
+    return tuning->name;
 }
 
-void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv)
+void fluid_tuning_set_octave(fluid_tuning_t *tuning, const double *pitch_deriv)
 {
-  int i;
+    int i;
 
-  for (i = 0; i < 128; i++) {
-    tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12];
-  }
+    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)
+void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch)
 {
-  int i;
+    int i;
 
-  for (i = 0; i < 128; i++) {
-    tuning->pitch[i] = pitch[i];
-  }
+    for(i = 0; i < 128; i++)
+    {
+        tuning->pitch[i] = pitch[i];
+    }
 }
 
-void fluid_tuning_set_pitch(fluid_tuning_ttuning, int key, double pitch)
+void fluid_tuning_set_pitch(fluid_tuning_t *tuning, int key, double pitch)
 {
-  if ((key >= 0) && (key < 128)) {
-    tuning->pitch[key] = pitch;
-  }
+    if((key >= 0) && (key < 128))
+    {
+        tuning->pitch[key] = pitch;
+    }
 }
index d974139276d80473ee0f71e392fea4df6d06d269..3afe2c65ad7d4bfec3bc28767161e79b0b006937 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 
 #include "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 */
+struct _fluid_tuning_t
+{
+    char *name;
+    int bank;
+    int prog;
+    double pitch[128];  /* the pitch of every key, in cents */
+    fluid_atomic_int_t 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);
+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);
+int fluid_tuning_set_name(fluid_tuning_t *tuning, const 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_ttuning, int key, double pitch);
+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_octave(fluid_tuning_t *tuning, const double *pitch_deriv);
 
-void fluid_tuning_set_all(fluid_tuning_t* tuning, const double* pitch);
+void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch);
 #define fluid_tuning_get_all(_t) (&(_t)->pitch[0])
 
 
index e6efbac89979eacb72c9997b105e6ee2c819ae73..51c1ebf655680ed090b229eeddfc3e00a5ef14a8 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -27,6 +27,7 @@
 #include "fluid_sys.h"
 #include "fluid_sfont.h"
 #include "fluid_rvoice_event.h"
+#include "fluid_defsfont.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 */
 /* 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,
+
+static const int32_t INT24_MAX = (1 << (16 + 8 - 1));
+
+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_tvoice);
+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); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, voice->rvoice, param); \
   } 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); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].real = rarg; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } 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); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].i = iarg; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } while (0)
 
-#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \
+#define UPDATE_RVOICE_GENERIC_I2(proc, obj, iarg1, iarg2) \
   do { \
-    if (voice->can_access_rvoice) proc(obj, iarg, rarg); \
-    else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
-      proc, obj, iarg, rarg); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].i = iarg1; \
+      param[1].i = iarg2; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } while (0)
 
-#define UPDATE_RVOICE_GENERIC_ALL(proc, obj, iarg, r1, r2, r3, r4, r5) \
+#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \
   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); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].i = iarg; \
+      param[1].real = rarg; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } 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) 
+#define UPDATE_RVOICE_BUFFERS_AMP(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,
+static FLUID_INLINE void
+fluid_voice_update_volenv(fluid_voice_t *voice,
+                          int enqueue,
+                          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);
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
+
+    param[0].i = section;
+    param[1].i = count;
+    param[2].real = coeff;
+    param[3].real = increment;
+    param[4].real = min;
+    param[5].real = max;
+
+    if(enqueue)
+    {
+        fluid_rvoice_eventhandler_push(voice->eventhandler,
+                                       fluid_adsr_env_set_data,
+                                       &voice->rvoice->envlfo.volenv,
+                                       param);
+    }
+    else
+    {
+        fluid_adsr_env_set_data(&voice->rvoice->envlfo.volenv, param);
+    }
 }
 
-static inline void
-fluid_voice_update_modenv(fluid_voice_t* voice, 
-                         fluid_adsr_env_section_t section,
+static FLUID_INLINE void
+fluid_voice_update_modenv(fluid_voice_t *voice,
+                          int enqueue,
+                          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);
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
+
+    param[0].i = section;
+    param[1].i = count;
+    param[2].real = coeff;
+    param[3].real = increment;
+    param[4].real = min;
+    param[5].real = max;
+
+    if(enqueue)
+    {
+        fluid_rvoice_eventhandler_push(voice->eventhandler,
+                                       fluid_adsr_env_set_data,
+                                       &voice->rvoice->envlfo.modenv,
+                                       param);
+    }
+    else
+    {
+        fluid_adsr_env_set_data(&voice->rvoice->envlfo.modenv, param);
+    }
 }
 
-static inline void fluid_sample_null_ptr(fluid_sample_t** sample)
+static FLUID_INLINE void fluid_voice_sample_unref(fluid_sample_t **sample)
 {
-  if (*sample != NULL) {
-    fluid_sample_decr_ref(*sample);
-    *sample = NULL;
-  }
+    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_tvoice)
+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;
+    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)
+static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate)
 {
-  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);
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
+
+    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, FALSE, FLUID_VOICE_ENVSUSTAIN,
+                              0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
+    fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVFINISHED,
+                              0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
+    fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN,
+                              0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
+    fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVFINISHED,
+                              0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
+
+    param[0].i = FLUID_IIR_LOWPASS;
+    param[1].i = 0;
+    fluid_iir_filter_init(&voice->rvoice->resonant_filter, param);
+
+    param[0].i = FLUID_IIR_DISABLED;
+    fluid_iir_filter_init(&voice->rvoice->resonant_custom_filter, param);
+
+    param[0].real = output_rate;
+    fluid_rvoice_set_output_rate(voice->rvoice, param);
 }
 
 /*
  * new_fluid_voice
  */
-fluid_voice_t*
-new_fluid_voice(fluid_real_t output_rate)
+fluid_voice_t *
+new_fluid_voice(fluid_rvoice_eventhandler_t *handler, 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;
+    fluid_voice_t *voice;
+    voice = FLUID_NEW(fluid_voice_t);
+
+    if(voice == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    voice->can_access_rvoice = TRUE;
+    voice->can_access_overflow_rvoice = TRUE;
+
+    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");
+        delete_fluid_voice(voice);
+        return NULL;
+    }
+
+    voice->status = FLUID_VOICE_CLEAN;
+    voice->chan = NO_CHANNEL;
+    voice->key = 0;
+    voice->vel = 0;
+    voice->eventhandler = handler;
+    voice->channel = NULL;
+    voice->sample = NULL;
+    voice->output_rate = output_rate;
+
+    /* Initialize both the rvoice and overflow_rvoice */
+    fluid_voice_initialize_rvoice(voice, output_rate);
+    fluid_voice_swap_rvoice(voice);
+    fluid_voice_initialize_rvoice(voice, output_rate);
+
+    return voice;
 }
 
 /*
  * delete_fluid_voice
  */
-int
-delete_fluid_voice(fluid_voice_tvoice)
+void
+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_return_if_fail(voice != NULL);
+
+    if(!voice->can_access_rvoice || !voice->can_access_overflow_rvoice)
+    {
+        FLUID_LOG(FLUID_WARN, "Deleting voice %u which has locked rvoices!", voice->id);
+    }
+
+    FLUID_FREE(voice->overflow_rvoice);
+    FLUID_FREE(voice->rvoice);
+    FLUID_FREE(voice);
 }
 
 /* fluid_voice_init
  *
  * Initialize the synthesis process
+ * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange
+ * of the voice.
+ * When playing legato (n1,n2) in mono mode, n2 will use n1 voices
+ * as far as n2 still enters in Keyrange,Velrange of n1.
  */
 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)
+fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample,
+                 fluid_zone_range_t *inst_zone_range,
+                 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;
+    /* 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;
+
+    /* We are now guaranteed to have access to the rvoice */
+
+    if(voice->sample)
+    {
+        fluid_voice_off(voice);
+    }
+
+    voice->zone_range = inst_zone_range; /* Instrument zone range for legato */
+    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->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);
+    fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample);
+    fluid_sample_incr_ref(sample);
+    voice->sample = sample;
+
+    i = fluid_channel_get_interp_method(channel);
+    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 = 2 * channel->synth->audio_groups;
+    i += (voice->chan % channel->synth->effects_groups) * channel->synth->effects_channels;
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i + SYNTH_REVERB_CHANNEL);
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i + SYNTH_CHORUS_CHANNEL);
+
+    i = 2 * (voice->chan % channel->synth->audio_groups);
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 0, i);
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 1, i + 1);
+
+    return FLUID_OK;
 }
 
 
 /**
- * Update sample rate. 
- * NOTE: If the voice is active, it will be turned off.
+ * Update sample rate.
+ * @note If the voice is active, it will be turned off.
  */
-int 
-fluid_voice_set_output_rate(fluid_voice_tvoice, fluid_real_t value)
+void
+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;
+    if(fluid_voice_is_playing(voice))
+    {
+        fluid_voice_off(voice);
+    }
+
+    voice->output_rate = value;
+    UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->rvoice, value);
+    UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->overflow_rvoice, value);
 }
 
 
@@ -339,12 +390,15 @@ fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value)
  * @param val Generator value
  */
 void
-fluid_voice_gen_set(fluid_voice_tvoice, int i, float val)
+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);
+    voice->gen[i].val = val;
+    voice->gen[i].flags = GEN_SET;
+
+    if(i == GEN_SAMPLEMODE)
+    {
+        UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val);
+    }
 }
 
 /**
@@ -354,10 +408,10 @@ fluid_voice_gen_set(fluid_voice_t* voice, int i, float val)
  * @param val Value to add to the existing value
  */
 void
-fluid_voice_gen_incr(fluid_voice_tvoice, int i, float val)
+fluid_voice_gen_incr(fluid_voice_t *voice, int i, float val)
 {
-  voice->gen[i].val += val;
-  voice->gen[i].flags = GEN_SET;
+    voice->gen[i].val += val;
+    voice->gen[i].flags = GEN_SET;
 }
 
 /**
@@ -367,128 +421,102 @@ fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val)
  * @return Current generator value
  */
 float
-fluid_voice_gen_get(fluid_voice_tvoice, int gen)
+fluid_voice_gen_get(fluid_voice_t *voice, int gen)
 {
-  return voice->gen[gen].val;
+    return voice->gen[gen].val;
 }
 
-fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num)
+fluid_real_t fluid_voice_gen_value(const 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);
-       }
+    /* 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).
+/*
+ * fluid_voice_start
  */
-int
-fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
+void fluid_voice_start(fluid_voice_t *voice)
 {
-  int result;
-  if (!voice->can_access_rvoice) 
-    return 0;
+    /* 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.*/
 
-  result = fluid_rvoice_write(voice->rvoice, dsp_buf);
+    fluid_voice_calculate_runtime_synthesis_parameters(voice);
 
-  if (result == -1)
-    return 0;
+#ifdef WITH_PROFILING
+    voice->ref = fluid_profile_ref();
+#endif
 
-  if ((result < FLUID_BUFSIZE) && _PLAYING(voice)) /* Voice finished by itself */
-    fluid_voice_off(voice);
+    voice->status = FLUID_VOICE_ON;
 
-  return result;
+    /* Increment voice count */
+    voice->channel->synth->active_voice_count++;
 }
 
-
 /**
- * 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
+ * Calculate the amplitude of a voice.
  *
+ * @param gain The gain value in the range [0.0 ; 1.0]
+ * @return An amplitude used by rvoice_mixer's buffers
  */
-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)
+static FLUID_INLINE fluid_real_t
+fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t gain)
 {
-  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");
+    /* we use 24bit samples in fluid_rvoice_dsp. in order to normalize float
+     * samples to [0.0;1.0] divide samples by the max. value of an int24 and
+     * amplify them with the gain */
+    return gain * voice->synth_gain / (INT24_MAX * 1.0f);
 }
 
-
-
-/*
- * fluid_voice_start
- */
-void fluid_voice_start(fluid_voice_t* voice)
+/* Useful to return the nominal pitch of a key */
+/* The nominal pitch is dependant of voice->root_pitch,tuning, and
+   GEN_SCALETUNE generator.
+   This is useful to set the value of GEN_PITCH generator on noteOn.
+   This is useful to get the beginning/ending pitch for portamento.
+*/
+fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key)
 {
-  /* 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;
+    fluid_tuning_t *tuning;
+    fluid_real_t x, pitch;
+
+    /* Now the nominal pitch of the key is returned.
+     * 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 fluid_voice_gen_value(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));
+        pitch = voice->gen[GEN_SCALETUNE].val / 100.0f *
+                (fluid_tuning_get_pitch(tuning, key) - x) + x;
+    }
+    else
+    {
+        pitch = voice->gen[GEN_SCALETUNE].val
+                * (key - voice->root_pitch / 100.0f) + voice->root_pitch;
+    }
 
-  /* Increment voice count */
-  voice->channel->synth->active_voice_count++;
+    return pitch;
 }
 
-void 
-fluid_voice_calculate_gen_pitch(fluid_voice_tvoice)
+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;
-  }
-
+    voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice));
 }
 
+
 /*
  * fluid_voice_calculate_runtime_synthesis_parameters
  *
@@ -500,176 +528,206 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t* voice)
  * example, for the pitch since it is modulated by the controllers in
  * cents. */
 static int
-fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_tvoice)
+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;
+    int i;
+    unsigned int n;
+
+    static int const list_of_generators_to_initialize[] =
+    {
+        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,                           /*                ---  */
+        GEN_CUSTOM_BALANCE,                  /*                ---  */
+        GEN_CUSTOM_FILTERFC,                 /*                ---  */
+        GEN_CUSTOM_FILTERQ                   /*                ---  */
+    };
+
+    /* 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);
+        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(n = 0; n < FLUID_N_ELEMENTS(list_of_generators_to_initialize); n++)
+    {
+        fluid_voice_update_param(voice, list_of_generators_to_initialize[n]);
+    }
+
+    /* Start portamento if enabled */
+    {
+        /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector.
+        When fromkey is set to ValidNote , portamento is started */
+        /* Return fromkey portamento */
+        int fromkey = voice->channel->synth->fromkey_portamento;
+
+        if(fluid_channel_is_valid_note(fromkey))
+        {
+            /* Send portamento parameters to the voice dsp */
+            fluid_voice_update_portamento(voice, fromkey, fluid_voice_get_actual_key(voice));
+        }
+    }
+
+    /* 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_tvoice, int gen_base,
+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;
+    /* 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 = (fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * (60.0 - fluid_voice_get_actual_key(voice)));
+
+    /* 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;
+    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.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;
     }
-  }
-  /* 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. */
+    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);
+    /* 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;
+    return buffers;
 }
 
 /*
@@ -684,7 +742,7 @@ calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
  *
  * 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
+ * NRPN system. fluid_voice_gen_value(voice, generator_enumerator) returns the sum
  * of all three.
  */
 /**
@@ -696,236 +754,245 @@ calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
  * Most applications will not need this function.
  */
 void
-fluid_voice_update_param(fluid_voice_tvoice, int gen)
+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;
+    unsigned int count, z;
+    fluid_real_t x = fluid_voice_gen_value(voice, gen);
+
+    switch(gen)
+    {
+
+    case GEN_PAN:
+    case GEN_CUSTOM_BALANCE:
+        /* range checking is done in the fluid_pan and fluid_balance functions */
+        voice->pan = fluid_voice_gen_value(voice, GEN_PAN);
+        voice->balance = fluid_voice_gen_value(voice, GEN_CUSTOM_BALANCE);
+
+        /* left amp */
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0,
+                                  fluid_voice_calculate_gain_amplitude(voice,
+                                          fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1)));
+
+        /* right amp */
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1,
+                                  fluid_voice_calculate_gain_amplitude(voice,
+                                          fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0)));
+        break;
+
+    case GEN_ATTENUATION:
+        voice->attenuation = x;
+
+        /* 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;
+    case GEN_PITCH:
+    case GEN_COARSETUNE:
+    case GEN_FINETUNE:
+        /* The testing for allowed range is done in 'fluid_ct2hz' */
+        voice->pitch = (fluid_voice_gen_value(voice, GEN_PITCH)
+                        + 100.0f * fluid_voice_gen_value(voice, GEN_COARSETUNE)
+                        + fluid_voice_gen_value(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 = x / 1000.0f;
+        fluid_clip(voice->reverb_send, 0.0, 1.0);
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send));
+        break;
+
+    case GEN_CHORUSSEND:
+        /* The generator unit is 'tenths of a percent'. */
+        voice->chorus_send = x / 1000.0f;
+        fluid_clip(voice->chorus_send, 0.0, 1.0);
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send));
+        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]
+         */
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_filter, x);
+        break;
+
+    case GEN_FILTERQ:
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_filter, x);
+        break;
+
+    /* same as the two above, only for the custom filter */
+    case GEN_CUSTOM_FILTERFC:
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_custom_filter, x);
+        break;
+
+    case GEN_CUSTOM_FILTERQ:
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_custom_filter, x);
+        break;
+
+    case GEN_MODLFOTOPITCH:
+        fluid_clip(x, -12000.0, 12000.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x);
+        break;
+
+    case GEN_MODLFOTOVOL:
+        fluid_clip(x, -960.0, 960.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x);
+        break;
+
+    case GEN_MODLFOTOFILTERFC:
+        fluid_clip(x, -12000, 12000);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x);
+        break;
+
+    case 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
+         */
+        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
+         */
+        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:
+        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:
+        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.
+         */
+
+        /* 2017-09-02: do not change the voice's key here, otherwise it will
+         * never be released on a noteoff event
+         */
+#if 0
+        x = fluid_voice_gen_value(voice, GEN_KEYNUM);
 
-    /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
-    fluid_clip(q_dB, 0.0f, 96.0f);
+        if(x >= 0)
+        {
+            voice->key = x;
+        }
 
-    /* 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;
+#endif
+        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.
+         */
+        /* 2017-09-02: do not change the voice's velocity here, use
+         * fluid_voice_get_actual_velocity() to get the value of this generator
+         * if active.
+         */
+#if 0
+        x = fluid_voice_gen_value(voice, GEN_VELOCITY);
 
-  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;
+        if(x > 0)
+        {
+            voice->vel = x;
+        }
 
-  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;
+#endif
+        break;
 
-  case GEN_MODENVTOFILTERFC:
-    x = _GEN(voice,GEN_MODENVTOFILTERFC);
+    case GEN_MODENVTOPITCH:
+        fluid_clip(x, -12000.0, 12000.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x);
+        break;
 
-    /* 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;
+    case 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
@@ -939,45 +1006,59 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
      * 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 */
+    case GEN_STARTADDROFS:              /* SF2.01 section 8.1.3 # 0 */
+    case GEN_STARTADDRCOARSEOFS:        /* SF2.01 section 8.1.3 # 4 */
+        if(voice->sample != NULL)
+        {
+            fluid_real_t start_fine = fluid_voice_gen_value(voice, GEN_STARTADDROFS);
+            fluid_real_t start_coar = fluid_voice_gen_value(voice, GEN_STARTADDRCOARSEOFS);
+
+            z = voice->sample->start + (int)start_fine + 32768 * (int)start_coar;
+            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)
+        {
+            fluid_real_t end_fine = fluid_voice_gen_value(voice, GEN_ENDADDROFS);
+            fluid_real_t end_coar = fluid_voice_gen_value(voice, GEN_ENDADDRCOARSEOFS);
+
+            z = voice->sample->end + (int)end_fine + 32768 * (int)end_coar;
+            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)
+        {
+            fluid_real_t lstart_fine = fluid_voice_gen_value(voice, GEN_STARTLOOPADDROFS);
+            fluid_real_t lstart_coar = fluid_voice_gen_value(voice, GEN_STARTLOOPADDRCOARSEOFS);
+
+            z = voice->sample->loopstart + (int)lstart_fine + 32768 * (int)lstart_coar;
+            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)
+        {
+            fluid_real_t lend_fine = fluid_voice_gen_value(voice, GEN_ENDLOOPADDROFS);
+            fluid_real_t lend_coar = fluid_voice_gen_value(voice, GEN_ENDLOOPADDRCOARSEOFS);
+
+            z = voice->sample->loopend + (int)lend_fine + 32768 * (int)lend_coar;
+            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)
@@ -988,90 +1069,84 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
      * - 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;
+    case GEN_VOLENVDELAY:                /* SF2.01 section 8.1.3 # 33 */
+        fluid_clip(x, -12000.0f, 5000.0f);
+        count = NUM_BUFFERS_DELAY(x);
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDELAY,
+                                  count, 0.0f, 0.0f, -1.0f, 1.0f);
+        break;
+
+    case GEN_VOLENVATTACK:               /* SF2.01 section 8.1.3 # 34 */
+        fluid_clip(x, -12000.0f, 8000.0f);
+        count = 1 + NUM_BUFFERS_ATTACK(x);
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVATTACK,
+                                  count, 1.0f, 1.0f / count, -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, TRUE, 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 */
+        x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_VOLENVSUSTAIN);
+        fluid_clip(x, 0.0f, 1.0f);
+        count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDECAY,
+                                  count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f);
+        break;
+
+    case GEN_VOLENVRELEASE:             /* SF2.01 section 8.1.3 # 38 */
+        fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f);
+        count = 1 + NUM_BUFFERS_RELEASE(x);
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVRELEASE,
+                                  count, 1.0f, -1.0f / count, 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 */
+    case GEN_MODENVDELAY:               /* SF2.01 section 8.1.3 # 25 */
+        fluid_clip(x, -12000.0f, 5000.0f);
+        fluid_voice_update_modenv(voice, TRUE, 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 */
+        fluid_clip(x, -12000.0f, 8000.0f);
+        count = 1 + NUM_BUFFERS_ATTACK(x);
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVATTACK,
+                                  count, 1.0f, 1.0f / count, -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, TRUE, 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 */
+        x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_MODENVSUSTAIN);
+        fluid_clip(x, 0.0f, 1.0f);
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDECAY,
+                                  count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f);
+        break;
+
+    case GEN_MODENVRELEASE:                                  /* SF 2.01 section 8.1.3 # 30 */
+        fluid_clip(x, -12000.0f, 8000.0f);
+        count = 1 + NUM_BUFFERS_RELEASE(x);
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVRELEASE,
+                                  count, 1.0f, -1.0f / count, 0.0f, 2.0f);
+
+        break;
+
+    } /* switch gen */
 }
 
 /**
@@ -1099,43 +1174,48 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
  * - For every changed generator, convert its value to the correct
  * unit of the corresponding DSP parameter
  */
-int fluid_voice_modulate(fluid_voice_tvoice, int cc, int ctrl)
+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);
+    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);
+                }
+            }
+
+            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;
+
+    return FLUID_OK;
 }
 
 /**
@@ -1143,44 +1223,113 @@ int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl)
  * ALL_CTRL_OFF MIDI message has been received (CC 121).
  *
  */
-int fluid_voice_modulate_all(fluid_voice_tvoice)
+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_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);
+            }
+        }
+
+        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);
     }
 
-    fluid_gen_set_mod(&voice->gen[gen], modval);
+    return FLUID_OK;
+}
 
-    /* Update the parameter values that are depend on the generator
-     * 'gen' */
-    fluid_voice_update_param(voice, gen);
-  }
+/** legato update functions --------------------------------------------------*/
+/* Updates voice portamento parameters
+ *
+ * @voice voice the synthesis voice
+ * @fromkey the beginning pitch of portamento.
+ * @tokey the ending pitch of portamento.
+ *
+ * The function calculates pitch offset and increment, then these parameters
+ * are send to the dsp.
+*/
+void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey)
+
+{
+    fluid_channel_t *channel = voice->channel;
+
+    /* calculates pitch offset */
+    fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice, fromkey);
+    fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice, tokey);
+    fluid_real_t pitchoffset = PitchBeg - PitchEnd;
+
+    /* Calculates increment countinc */
+    /* Increment is function of PortamentoTime (ms)*/
+    unsigned int countinc = (unsigned int)(((fluid_real_t)voice->output_rate *
+                                            0.001f *
+                                            (fluid_real_t)fluid_channel_portamentotime(channel))  /
+                                           (fluid_real_t)FLUID_BUFSIZE  + 0.5);
+
+    /* Send portamento parameters to the voice dsp */
+    UPDATE_RVOICE_GENERIC_IR(fluid_rvoice_set_portamento, voice->rvoice, countinc, pitchoffset);
+}
+
+/*---------------------------------------------------------------*/
+/*legato mode 1: multi_retrigger
+ *
+ * Modulates all generators dependent of key,vel.
+ * Forces the voice envelopes in the attack section (legato mode 1).
+ *
+ * @voice voice the synthesis voice
+ * @tokey the new key to be applied to this voice.
+ * @vel the new velocity to be applied to this voice.
+ */
+void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice,
+        int tokey, int vel)
+{
+    voice->key = tokey;  /* new note */
+    voice->vel = vel; /* new velocity */
+    /* Updates generators dependent of velocity */
+    /* Modulates GEN_ATTENUATION (and others ) before calling
+       fluid_rvoice_multi_retrigger_attack().*/
+    fluid_voice_modulate(voice, FALSE, FLUID_MOD_VELOCITY);
+
+    /* Updates generator dependent of voice->key */
+    fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD);
+    fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY);
+    fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD);
+    fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY);
+
+    /* Updates pitch generator  */
+    fluid_voice_calculate_gen_pitch(voice);
+    fluid_voice_update_param(voice, GEN_PITCH);
 
-  return FLUID_OK;
+    /* updates adsr generator */
+    UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack);
 }
+/** end of legato update functions */
 
 /*
  Force the voice into release stage. Useful anywhere a voice
@@ -1190,40 +1339,45 @@ int fluid_voice_modulate_all(fluid_voice_t* voice)
  fluid_voice_noteoff().
 */
 void
-fluid_voice_release(fluid_voice_tvoice)
+fluid_voice_release(fluid_voice_t *voice)
 {
-    unsigned int at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
+    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
+ *
+ * Sending a noteoff event will advance the envelopes to section 5 (release).
+ * The function is convenient for polyphonic or monophonic note
  */
-int
-fluid_voice_noteoff(fluid_voice_tvoice)
+void
+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_channel_t *channel;
+
+    fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref, 0, 0);
+
+    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);
+    }
 }
 
 /*
@@ -1239,73 +1393,84 @@ fluid_voice_noteoff(fluid_voice_t* voice)
  */
 
 int
-fluid_voice_kill_excl(fluid_voice_t* voice){
+fluid_voice_kill_excl(fluid_voice_t *voice)
+{
 
-  unsigned int at_tick;
+    unsigned int at_tick;
 
-  if (!_PLAYING(voice)) {
-    return FLUID_OK;
-  }
+    if(!fluid_voice_is_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);
+    /* 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 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);
+    /* 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);
+    at_tick = fluid_channel_get_min_note_length_ticks(voice->channel);
+    UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
 
 
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 /*
- * Called by fluid_synth when the overflow rvoice can be reclaimed. 
+ * Called by fluid_synth when the overflow rvoice can be reclaimed.
  */
-void fluid_voice_overflow_rvoice_finished(fluid_voice_tvoice)
+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);
+    voice->can_access_overflow_rvoice = 1;
+    fluid_voice_sample_unref(&voice->overflow_rvoice->dsp.sample);
 }
 
-
 /*
  * fluid_voice_off
  *
+ * Force the voice into finished stage. Useful anywhere a voice
+ * needs to be cancelled from MIDI API.
+ */
+void fluid_voice_off(fluid_voice_t *voice)
+{
+    UPDATE_RVOICE0(fluid_rvoice_voiceoff); /* request to finish the voice */
+}
+
+/*
+ * fluid_voice_stop
+ *
  * Purpose:
- * Turns off a voice, meaning that it is not processed
- * anymore by the DSP loop.
+ * Turns off a voice, meaning that it is not processed anymore by the
+ * DSP loop, i.e. contrary part to fluid_voice_start().
  */
-int
-fluid_voice_off(fluid_voice_t* voice)
+void
+fluid_voice_stop(fluid_voice_t *voice)
 {
-  fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref);
+    fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref, 0, 0);
 
-  voice->chan = NO_CHANNEL;
-  UPDATE_RVOICE0(fluid_rvoice_voiceoff);
-  
-  if (voice->can_access_rvoice)
-    fluid_sample_null_ptr(&voice->rvoice->dsp.sample);
+    voice->chan = NO_CHANNEL;
 
-  voice->status = FLUID_VOICE_OFF;
-  voice->has_noteoff = 1;
+    if(voice->can_access_rvoice)
+    {
+        fluid_voice_sample_unref(&voice->rvoice->dsp.sample);
+    }
 
-  /* Decrement the reference count of the sample. */
-  fluid_sample_null_ptr(&voice->sample);
+    voice->status = FLUID_VOICE_OFF;
+    voice->has_noteoff = 1;
 
-  /* Decrement voice count */
-  voice->channel->synth->active_voice_count--;
+    /* Decrement the reference count of the sample. */
+    fluid_voice_sample_unref(&voice->sample);
 
-  return FLUID_OK;
+    /* Decrement voice count */
+    voice->channel->synth->active_voice_count--;
 }
 
 /**
@@ -1319,57 +1484,70 @@ fluid_voice_off(fluid_voice_t* voice)
  *   exist so don't check.
  */
 void
-fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode)
+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;
-      }
+    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 != FLUID_MOD_NONE)            /* SF2.01 section 8.2.1: Constant value */
+                && (mod->src1 != FLUID_MOD_VELOCITY)        /* Note-on velocity */
+                && (mod->src1 != FLUID_MOD_KEY)             /* Note-on key number */
+                && (mod->src1 != FLUID_MOD_KEYPRESSURE)     /* Poly pressure */
+                && (mod->src1 != FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
+                && (mod->src1 != FLUID_MOD_PITCHWHEEL)      /* Pitch wheel */
+                && (mod->src1 != FLUID_MOD_PITCHWHEELSENS)))/* Pitch wheel sensitivity */
+    {
+        FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
+        return;
     }
 
-  } else if (mode == FLUID_VOICE_OVERWRITE) {
+    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;
+            }
+        }
 
-    /* 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);
-  }
+    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);
+    }
+    else
+    {
+        FLUID_LOG(FLUID_WARN, "Voice %i has more modulators than supported, ignoring.", voice->id);
+    }
 }
 
 /**
@@ -1388,19 +1566,134 @@ fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode)
  *
  * Otherwise the voice has finished playing.
  */
-unsigned int fluid_voice_get_id(fluid_voice_t* voice)
+unsigned int fluid_voice_get_id(const fluid_voice_t *voice)
 {
-  return voice->id;
+    return voice->id;
 }
 
 /**
- * Check if a voice is still playing.
+ * Check if a voice is producing sound. This is also true after a voice received a noteoff as it may be playing in release phase.
  * @param voice Voice instance
  * @return TRUE if playing, FALSE otherwise
  */
-int fluid_voice_is_playing(fluid_voice_t* voice)
+int fluid_voice_is_playing(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_ON)
+           || fluid_voice_is_sustained(voice)
+           || fluid_voice_is_sostenuto(voice);
+
+}
+
+/**
+ * Check if a voice is ON. A voice is ON, if it has not yet received a noteoff event.
+ * @param voice Voice instance
+ * @return TRUE if on, FALSE otherwise
+ * @since 1.1.7
+ */
+int fluid_voice_is_on(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_ON && !voice->has_noteoff);
+}
+
+/**
+ * Check if a voice keeps playing after it has received a noteoff due to being held by sustain.
+ * @param voice Voice instance
+ * @return TRUE if sustained, FALSE otherwise
+ * @since 1.1.7
+ */
+int fluid_voice_is_sustained(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_SUSTAINED);
+}
+
+/**
+ * Check if a voice keeps playing after it has received a noteoff due to being held by sostenuto.
+ * @param voice Voice instance
+ * @return TRUE if sostenuto, FALSE otherwise
+ * @since 1.1.7
+ */
+int fluid_voice_is_sostenuto(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_HELD_BY_SOSTENUTO);
+}
+
+/**
+ * If the voice is playing, gets the midi channel the voice is playing on. Else the result is undefined.
+ * @param voice Voice instance
+ * @return The channel assigned to this voice
+ * @since 1.1.7
+ */
+int fluid_voice_get_channel(const fluid_voice_t *voice)
+{
+    return voice->chan;
+}
+
+/**
+ * If the voice is playing, gets the midi key the voice is actually playing at. Else the result is undefined.
+ * If the voice was started from an instrument which uses a fixed key generator, it returns that.
+ * Else returns the same as \c fluid_voice_get_key.
+ * @param voice Voice instance
+ * @return The midi key this voice is playing at
+ * @since 1.1.7
+ */
+int fluid_voice_get_actual_key(const fluid_voice_t *voice)
+{
+    fluid_real_t x = fluid_voice_gen_value(voice, GEN_KEYNUM);
+
+    if(x >= 0)
+    {
+        return (int)x;
+    }
+    else
+    {
+        return fluid_voice_get_key(voice);
+    }
+}
+
+/**
+ * If the voice is playing, gets the midi key from the noteon event, by which the voice was initially turned on with.
+ * Else the result is undefined.
+ * @param voice Voice instance
+ * @return The midi key of the noteon event that originally turned on this voice
+ * @since 1.1.7
+ */
+int fluid_voice_get_key(const fluid_voice_t *voice)
+{
+    return voice->key;
+}
+
+/**
+ * If the voice is playing, gets the midi velocity the voice is actually playing at. Else the result is undefined.
+ * If the voice was started from an instrument which uses a fixed velocity generator, it returns that.
+ * Else returns the same as \c fluid_voice_get_velocity.
+ * @param voice Voice instance
+ * @return The midi velocity this voice is playing at
+ * @since 1.1.7
+ */
+int fluid_voice_get_actual_velocity(const fluid_voice_t *voice)
+{
+    fluid_real_t x = fluid_voice_gen_value(voice, GEN_VELOCITY);
+
+    if(x > 0)
+    {
+        return (int)x;
+    }
+    else
+    {
+        return fluid_voice_get_velocity(voice);
+    }
+}
+
+/**
+ * If the voice is playing, gets the midi velocity from the noteon event, by which the voice was initially
+ * turned on with. Else the result is undefined.
+ * @param voice Voice instance
+ * @return The midi velocity which originally turned on this voice
+ * @since 1.1.7
+ */
+int fluid_voice_get_velocity(const fluid_voice_t *voice)
 {
-  return _PLAYING(voice);
+    return voice->vel;
 }
 
 /*
@@ -1416,85 +1709,105 @@ int fluid_voice_is_playing(fluid_voice_t* voice)
  * voice->attenuation has to be initialized.
  */
 static fluid_real_t
-fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_tvoice)
+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);
-      }
+    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)
+                    || (mod->src1 == FLUID_MOD_CHANNELPRESSURE)
+                    || (mod->src1 == FLUID_MOD_KEYPRESSURE)
+                    || (mod->src1 == FLUID_MOD_PITCHWHEEL)
+                    || (mod->src2 == FLUID_MOD_CHANNELPRESSURE)
+                    || (mod->src2 == FLUID_MOD_KEYPRESSURE)
+                    || (mod->src2 == FLUID_MOD_PITCHWHEEL)))
+        {
+
+            fluid_real_t current_val = fluid_mod_get_value(mod, 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;
+    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;
+    /* 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_tvoice, int gen, fluid_real_t nrpn_value, int abs)
+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;
+    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_tvoice, fluid_real_t gain)
+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;
+    fluid_real_t left, right, reverb, chorus;
+
+    /* avoid division by zero*/
+    if(gain < 0.0000001)
+    {
+        gain = 0.0000001;
+    }
+
+    voice->synth_gain = gain;
+    left = fluid_voice_calculate_gain_amplitude(voice,
+            fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1));
+    right = fluid_voice_calculate_gain_amplitude(voice,
+            fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0));
+    reverb = fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send);
+    chorus = fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send);
+
+    UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, left);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, right);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, reverb);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, chorus);
+
+    return FLUID_OK;
 }
 
 /* - Scan the loop
@@ -1514,113 +1827,162 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
  * fluid_voice_optimize_sample() on each sample once.
  */
 int
-fluid_voice_optimize_sample(fluid_sample_ts)
+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;
-      }
+    int32_t peak_max = 0;
+    int32_t peak_min = 0;
+    int32_t peak;
+    fluid_real_t normalized_amplitude_during_loop;
+    double result;
+    unsigned int i;
+
+    /* ignore disabled samples */
+    if(s->start == s->end)
+    {
+        return (FLUID_OK);
     }
 
-    /* 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(!s->amplitude_that_reaches_noise_floor_is_valid)    /* Only once */
+    {
+        /* Scan the loop */
+        for(i = s->loopstart; i < s->loopend; i++)
+        {
+            int32_t val = fluid_rvoice_get_sample(s->data, s->data24, 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) / (INT24_MAX * 1.0f);
+        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);
+        printf("Sample peak detection: factor %f\n", (double)result);
 #endif
-  };
-  return FLUID_OK;
+    }
+
+    return FLUID_OK;
 }
 
-fluid_real_t 
-fluid_voice_get_overflow_prio(fluid_voice_t* voice, 
-                              fluid_overflow_prio_tscore,
-                              unsigned int cur_time)
+float
+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.
+    float this_voice_prio = 0;
+    int channel;
+
+    /* 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.
      */
-    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(voice->channel->channel_type == CHANNEL_TYPE_DRUM)
+    {
+        this_voice_prio += score->percussion;
     }
-    if (a < 0.1) 
-      a = 0.1; // Avoid div by zero
-      this_voice_prio += score->volume / a;
+    else if(voice->has_noteoff)
+    {
+        /* Noteoff has */
+        this_voice_prio += score->released;
     }
-    
-  return this_voice_prio;
+    else if(fluid_voice_is_sustained(voice) || fluid_voice_is_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;
+    }
+
+    /* Check if this voice is on an important channel. If so, then add the
+     * score for important channels */
+    channel = fluid_voice_get_channel(voice);
+
+    if(channel < score->num_important_channels && score->important_channels[channel])
+    {
+        this_voice_prio += score->important;
+    }
+
+    return this_voice_prio;
+}
+
+
+void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags)
+{
+    UPDATE_RVOICE_GENERIC_I2(fluid_iir_filter_init, &voice->rvoice->resonant_custom_filter, type, flags);
 }
+
index c43fe597382f226d425b0853fbacde021112a31e..6038a1a9f3a00ebea50e5561b873cc1d78bbaa0b 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
 #include "fluid_adsr_env.h"
 #include "fluid_lfo.h"
 #include "fluid_rvoice.h"
+#include "fluid_rvoice_event.h"
 #include "fluid_sys.h"
 
 #define NO_CHANNEL             0xff
 
 typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t;
 
-struct _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 */
+    float percussion; /**< Is this voice on the drum channel? Then add this score */
+    float released; /**< Is this voice in release stage? Then add this score (usually negative) */
+    float sustained; /**< Is this voice sustained? Then add this score (usually negative) */
+    float volume; /**< Multiply current (or future) volume (a value between 0 and 1) */
+    float age; /**< This score will be divided by the number of seconds the voice has lasted */
+    float important; /**< This score will be added to all important channels */
+    char *important_channels; /**< "important" flags indexed by MIDI channel number */
+    int num_important_channels; /**< Number of elements in the important_channels array */
 };
 
 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_CLEAN,
+    FLUID_VOICE_ON,
+    FLUID_VOICE_SUSTAINED,         /* Sustained by Sustain pedal */
+    FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */
+    FLUID_VOICE_OFF
 };
 
 
@@ -59,170 +63,135 @@ enum fluid_voice_status
  */
 struct _fluid_voice_t
 {
-       unsigned int id;                /* the id is incremented for every new noteon.
+    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;
+    unsigned char status;
+    unsigned char chan;             /* the channel number, quick access for channel messages */
+    unsigned char key;              /* the key of the noteon event, quick access for noteoff */
+    unsigned char vel;              /* the velocity of the noteon event */
+    fluid_channel_t *channel;
+    fluid_rvoice_eventhandler_t *eventhandler;
+    fluid_zone_range_t *zone_range;  /* instrument zone range*/
+    fluid_sample_t *sample;         /* Pointer to sample (dupe in rvoice) */
+
+    unsigned int start_time;
+    int mod_count;
+    fluid_mod_t mod[FLUID_NUM_MOD];
+    fluid_gen_t gen[GEN_LAST];
+
+    /* basic parameters */
+    fluid_real_t output_rate;        /* the sample rate of the synthesizer (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;
+
+    /* balance */
+    fluid_real_t balance;
+
+    /* reverb */
+    fluid_real_t reverb_send;
+
+    /* chorus */
+    fluid_real_t chorus_send;
+
+    /* rvoice control */
+    fluid_rvoice_t *rvoice;
+    fluid_rvoice_t *overflow_rvoice; /* Used temporarily and only in overflow situations */
+    char can_access_rvoice; /* False if rvoice is being rendered in separate thread */
+    char can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */
+    char has_noteoff; /* Flag set when noteoff has been sent */
+
+#ifdef WITH_PROFILING
+    /* for debugging */
+    double ref;
+#endif
 };
 
 
-fluid_voice_t* new_fluid_voice(fluid_real_t output_rate);
-int delete_fluid_voice(fluid_voice_t* voice);
+fluid_voice_t *new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate);
+void delete_fluid_voice(fluid_voice_t *voice);
 
-void fluid_voice_start(fluid_voice_tvoice);
-void  fluid_voice_calculate_gen_pitch(fluid_voice_tvoice);
+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_zone_range_t *inst_zone_range,
+                     fluid_channel_t *channel, int key, int vel,
+                     unsigned int id, unsigned int time, fluid_real_t gain);
 
-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);
+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_tvoice, int gen, fluid_real_t value, int abs);
+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_tvoice, fluid_real_t 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);
+void 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);
+void fluid_voice_update_param(fluid_voice_t *voice, int gen);
+
+/** legato modes */
+/* force in the attack section for legato mode multi_retrigger: 1 */
+void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, int tokey, int vel);
+/* Update portamento parameter */
+void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey);
+
+
+void fluid_voice_release(fluid_voice_t *voice);
+void fluid_voice_noteoff(fluid_voice_t *voice);
+void fluid_voice_off(fluid_voice_t *voice);
+void fluid_voice_stop(fluid_voice_t *voice);
+void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice);
+
+int fluid_voice_kill_excl(fluid_voice_t *voice);
+float 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_tvoice)
+static FLUID_INLINE void
+fluid_voice_lock_rvoice(fluid_voice_t *voice)
 {
-  voice->can_access_rvoice = 0;
-  return voice->rvoice;
+    voice->can_access_rvoice = 0;
 }
 
 /**
  * Unlocks the rvoice for rendering, so it can be modified directly
  */
-static FLUID_INLINE void 
-fluid_voice_unlock_rvoice(fluid_voice_tvoice)
+static FLUID_INLINE void
+fluid_voice_unlock_rvoice(fluid_voice_t *voice)
 {
-  voice->can_access_rvoice = 1;
+    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 */
+fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num);
+void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags);
 
-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 */
index b01618df26e7bd6c59f6ce1701efb0c8dceb7478..d500f6174eeaeb6bc07b0fae8b0b09a60acda2fc 100644 (file)
@@ -3,16 +3,16 @@
  * 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
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -24,9 +24,7 @@
 
 #include <glib.h>
 
-#if HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #if HAVE_STRING_H
 #include <string.h>
 #include <pthread.h>
 #endif
 
+#if HAVE_OPENMP
+#include <omp.h>
+#endif
+
 #if HAVE_IO_H
 #include <io.h>
 #endif
 
-#if HAVE_WINDOWS_H
-#include <windows.h>
+#if HAVE_SIGNAL_H
+#include <signal.h>
 #endif
 
-/* MinGW32 special defines */
-#ifdef MINGW32
-
+/** Integer types  */
+#if HAVE_STDINT_H
 #include <stdint.h>
-#define snprintf _snprintf
-#define vsnprintf _vsnprintf
 
+#else
+
+/* Assume GLIB types */
+typedef gint8    int8_t;
+typedef guint8   uint8_t;
+typedef gint16   int16_t;
+typedef guint16  uint16_t;
+typedef gint32   int32_t;
+typedef guint32  uint32_t;
+typedef gint64   int64_t;
+typedef guint64  uint64_t;
+
+#endif
+
+#if defined(WIN32) &&  HAVE_WINDOWS_H
+//#include <winsock2.h>
+//#include <ws2tcpip.h>        /* Provides also socklen_t */
+#include <windows.h>
+
+/* WIN32 special defines */
 #define DSOUND_SUPPORT 1
 #define WINMIDI_SUPPORT 1
 #define STDIN_FILENO 0
 #define STDOUT_FILENO 1
 #define STDERR_FILENO 2
 
-#elif defined _MSC_VER
-
-#define STDIN_FILENO  _fileno(stdin)
-#define STDOUT_FILENO _fileno(stdout)
-#define STDERR_FILENO _fileno(stderr)
+#ifdef _MSC_VER
+#pragma warning(disable : 4244)
+#pragma warning(disable : 4101)
+#pragma warning(disable : 4305)
+#pragma warning(disable : 4996)
+#endif
 
 #endif
 
 /* Darwin special defines (taken from config_macosx.h) */
 #ifdef DARWIN
-#define MACINTOSH
-#define __Types__
-#define WITHOUT_SERVER 1
+# define MACINTOSH
+# define __Types__
 #endif
 
 
@@ -156,7 +175,6 @@ typedef double fluid_real_t;
 typedef SOCKET fluid_socket_t;
 #else
 typedef int fluid_socket_t;
-#define INVALID_SOCKET -1
 #endif
 
 #if defined(SUPPORTS_VLA)
@@ -168,15 +186,10 @@ typedef int fluid_socket_t;
 #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;
+/** Atomic types  */
+typedef int fluid_atomic_int_t;
+typedef unsigned int fluid_atomic_uint_t;
+typedef float fluid_atomic_float_t;
 
 
 /***************************************************************
@@ -191,6 +204,25 @@ 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;
+typedef struct _fluid_zone_range_t fluid_zone_range_t;
+typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t;
+
+/* Declare rvoice related typedefs here instead of fluid_rvoice.h, as it's needed
+ * in fluid_lfo.c and fluid_adsr.c as well */
+typedef union _fluid_rvoice_param_t
+{
+    void *ptr;
+    int i;
+    fluid_real_t real;
+} fluid_rvoice_param_t;
+enum { MAX_EVENT_PARAMS = 6 }; /**< Maximum number of #fluid_rvoice_param_t to be passed to an #fluid_rvoice_function_t */
+typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]);
+
+/* Macro for declaring an rvoice event function (#fluid_rvoice_function_t). The functions may only access
+ * those params that were previously set in fluid_voice.c
+ */
+#define DECLARE_FLUID_RVOICE_FUNCTION(name) void name(void* obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS])
+
 
 /***************************************************************
  *
@@ -198,48 +230,84 @@ typedef struct _fluid_sample_timer_t fluid_sample_timer_t;
  */
 
 #define FLUID_BUFSIZE                64         /**< FluidSynth internal buffer size (in samples) */
+#define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE) /**< Number of buffers that can be processed in one rendering run */
 #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
+#define FLUID_NUM_MOD                64         /**< Maximum number of modulators in a voice */
 
 /***************************************************************
  *
  *                      SYSTEM INTERFACE
  */
-typedef FILE*  fluid_file;
+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_ARRAY_ALIGNED(_t,_n,_a) (_t*)malloc((_n)*sizeof(_t) + ((unsigned int)_a - 1u))
+#define FLUID_ARRAY(_t,_n)           FLUID_ARRAY_ALIGNED(_t,_n,1u)
 #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_FTELL(_f)              ftell(_f)
 #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_STRNCPY(_dst,_src,_n) \
+do { strncpy(_dst,_src,_n); \
+    (_dst)[(_n)-1]=0; \
+}while(0)
+
 #define FLUID_STRCHR(_s,_c)          strchr(_s,_c)
 #define FLUID_STRRCHR(_s,_c)         strrchr(_s,_c)
+
 #ifdef strdup
-#define FLUID_STRDUP(s)              strdup(s)
+#define FLUID_STRDUP(s)          strdup(s)
 #else
-#define FLUID_STRDUP(s)                    FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s)
+#define FLUID_STRDUP(s)          FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s)
 #endif
+
 #define FLUID_SPRINTF                sprintf
 #define FLUID_FPRINTF                fprintf
 
+#if (defined(WIN32) && _MSC_VER < 1900) || defined(MINGW32)
+/* need to make sure we use a C99 compliant implementation of (v)snprintf(),
+ * i.e. not microsofts non compliant extension _snprintf() as it doesnt
+ * reliably null-terminates the buffer
+ */
+#define FLUID_SNPRINTF           g_snprintf
+#else
+#define FLUID_SNPRINTF           snprintf
+#endif
+
+#if (defined(WIN32) && _MSC_VER < 1500) || defined(MINGW32)
+#define FLUID_VSNPRINTF          g_vsnprintf
+#else
+#define FLUID_VSNPRINTF          vsnprintf
+#endif
+
+#if defined(WIN32) && !defined(MINGW32)
+#define FLUID_STRCASECMP         _stricmp
+#else
+#define FLUID_STRCASECMP         strcasecmp
+#endif
+
+#if defined(WIN32) && !defined(MINGW32)
+#define FLUID_STRNCASECMP         _strnicmp
+#else
+#define FLUID_STRNCASECMP         strncasecmp
+#endif
+
+
 #define fluid_clip(_val, _min, _max) \
 { (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); }
 
@@ -251,21 +319,38 @@ typedef FILE*  fluid_file;
 #define FLUID_FLUSH()                fflush(stdout)
 #endif
 
+/* People who want to reduce the size of the may do this by entirely
+ * removing the logging system. This will cause all log messages to
+ * be discarded at compile time, allowing to save about 80 KiB for
+ * the compiled binary.
+ */
+#if 0
+#define FLUID_LOG                    (void)sizeof
+#else
 #define FLUID_LOG                    fluid_log
+#endif
 
 #ifndef M_PI
 #define M_PI 3.1415926535897932384626433832795
 #endif
 
+#ifndef M_LN2
+#define M_LN2 0.69314718055994530941723212145818
+#endif
 
-#define FLUID_ASSERT(a,b)
-#define FLUID_ASSERT_P(a,b)
-
-char* fluid_error(void);
+#ifndef M_LN10
+#define M_LN10 2.3025850929940456840179914546844
+#endif
 
+#ifdef DEBUG
+#define FLUID_ASSERT(a) g_assert(a)
+#else
+#define FLUID_ASSERT(a)
+#endif
 
-/* Internationalization */
-#define _(s) s
+#define FLUID_LIKELY G_LIKELY
+#define FLUID_UNLIKELY G_UNLIKELY
 
+char *fluid_error(void);
 
 #endif /* _FLUIDSYNTH_PRIV_H */
index ebce6efcb4b600c9e42fc75c0570564c7d714da5..9b6422c341c25b29a16e520f364f698369e2afd9 100644 (file)
@@ -5,9 +5,9 @@ import os
 import sys
 
 # Version of this package (even if built as a child)
-MAJOR = '1'
-MINOR = '1'
-MICRO = '6'
+MAJOR = '2'
+MINOR = '0'
+MICRO = '1'
 LIBFLUIDSYNTH_VERSION = "%s.%s.%s" % (MAJOR, MINOR, MICRO)
 
 # Variables for 'waf dist'
@@ -58,7 +58,11 @@ def build(bld):
         'src/fluid_hash.c',
         'src/fluid_list.c',
         'src/fluid_ringbuffer.c',
+        'src/fluid_samplecache.c',
         'src/fluid_settings.c',
+        'src/fluid_sffile.c',
+        'src/fluid_sfont.c',
+        'src/fluid_synth_monopoly.c',
         'src/fluid_sys.c'
         ],
         cflags = [ bld.env['compiler_flags_dict']['pic'], '-fvisibility=hidden' ],
index 52db8337762f54ff031534644ae427723635eaf0..64bc813a6efe31a6c817cabbeaf993a8711e0657 100644 (file)
@@ -1,73 +1,74 @@
-diff --git b/libs/fluidsynth/src/fluid_defsfont.c a/libs/fluidsynth/src/fluid_defsfont.c
-index 3eea95c..c395218 100644
---- b/libs/fluidsynth/src/fluid_defsfont.c
-+++ a/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);
- }
+diff --git b/libs/fluidsynth/fluidsynth/synth.h a/libs/fluidsynth/fluidsynth/synth.h
+index 1a0046fe1..a4afb9094 100644
+--- b/libs/fluidsynth/fluidsynth/synth.h
++++ a/libs/fluidsynth/fluidsynth/synth.h
+@@ -265,7 +265,7 @@ FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t *synth, int len,
+ FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t *synth, int len,
+         void *lout, int loff, int lincr,
+         void *rout, int roff, int rincr);
+-FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
++FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
+         float **left, float **right,
+         float **fx_left, float **fx_right);
+ FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t *synth, int len,
+@@ -310,7 +310,9 @@ FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int
+ /* LADSPA */
++#ifdef LADSPA
+ FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth);
++#endif
  
-+#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;
- }
+ /* API: Poly mono mode */
+diff --git b/libs/fluidsynth/fluidsynth/types.h a/libs/fluidsynth/fluidsynth/types.h
+index 47ef18336..5ad29281a 100644
+--- b/libs/fluidsynth/fluidsynth/types.h
++++ a/libs/fluidsynth/fluidsynth/types.h
+@@ -56,7 +56,9 @@ typedef struct _fluid_sequencer_t fluid_sequencer_t;            /**< Sequencer i
+ typedef struct _fluid_ramsfont_t fluid_ramsfont_t;              /**< RAM SoundFont */
+ typedef struct _fluid_rampreset_t fluid_rampreset_t;            /**< RAM SoundFont preset */
+ typedef struct _fluid_cmd_handler_t fluid_cmd_handler_t;        /**< Shell Command Handler */
++#ifdef LADSPA
+ typedef struct _fluid_ladspa_fx_t fluid_ladspa_fx_t;            /**< LADSPA effects instance */
 +#endif
+ typedef struct _fluid_file_callbacks_t fluid_file_callbacks_t;  /**< Callback struct to perform custom file loading of soundfonts */
  
- fluid_preset_t*
- fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum)
+ typedef int fluid_istream_t;    /**< Input stream descriptor */
 diff --git b/libs/fluidsynth/src/fluid_hash.c a/libs/fluidsynth/src/fluid_hash.c
-index a063e29..9d5a920 100644
+index 37b0a06a4..b6586895b 100644
 --- b/libs/fluidsynth/src/fluid_hash.c
 +++ a/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;
+@@ -991,6 +991,7 @@ fluid_hashtable_remove_all(fluid_hashtable_t *hashtable)
+     fluid_hashtable_maybe_resize(hashtable);
  }
  
 +#if 0
  /**
-  * fluid_hashtable_foreach_remove:
+  * fluid_hashtable_steal_all:
   * @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);
+@@ -1008,6 +1009,7 @@ fluid_hashtable_steal_all(fluid_hashtable_t *hashtable)
+     fluid_hashtable_remove_all_nodes(hashtable, FALSE);
+     fluid_hashtable_maybe_resize(hashtable);
  }
 +#endif
  
- /**
-  * fluid_hashtable_foreach_steal:
+ /*
+  * fluid_hashtable_foreach_remove_or_steal:
 diff --git b/libs/fluidsynth/src/fluid_midi.c a/libs/fluidsynth/src/fluid_midi.c
-index 5ceab01..171952f 100644
+index c05f994ce..bdf72dd68 100644
 --- b/libs/fluidsynth/src/fluid_midi.c
 +++ a/libs/fluidsynth/src/fluid_midi.c
-@@ -34,7 +34,7 @@ static int fluid_midi_event_length(unsigned char event);
- static char* fluid_file_read_full(fluid_file fp, size_t* length);
- #define READ_FULL_INITIAL_BUFLEN 1024
+@@ -75,7 +75,7 @@ static int fluid_midi_file_read_tracklen(fluid_midi_file *mf);
+ static int fluid_midi_file_eot(fluid_midi_file *mf);
+ static int fluid_midi_file_get_division(fluid_midi_file *midifile);
  
 -
 +#if 0 // disable file I/O with Ardour
  /***************************************************************
   *
   *                      MIDIFILE
-@@ -760,6 +760,7 @@ fluid_midi_file_get_division(fluid_midi_file *midifile)
+@@ -1011,6 +1011,7 @@ fluid_midi_file_get_division(fluid_midi_file *midifile)
  {
      return midifile->division;
  }
@@ -75,7 +76,7 @@ index 5ceab01..171952f 100644
  
  /******************************************************
   *
-@@ -1030,7 +1031,7 @@ fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dy
+@@ -1330,7 +1331,7 @@ static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type,
   *
   *     fluid_track_t
   */
@@ -84,228 +85,196 @@ index 5ceab01..171952f 100644
  /*
   * new_fluid_track
   */
-@@ -1118,7 +1119,7 @@ fluid_track_get_duration(fluid_track_t *track)
- /*
-  * 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;
-@@ -1533,7 +1534,7 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
-     return FLUID_OK;
- }
+@@ -2434,3 +2435,4 @@ fluid_midi_event_length(unsigned char event)
  
--void
-+static void
- fluid_player_advancefile(fluid_player_t *player)
- {
-     if (player->playlist == NULL) {
-@@ -1553,7 +1554,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;
-@@ -1938,3 +1939,4 @@ fluid_midi_event_length(unsigned char event)
-     }
      return 1;
  }
 +#endif
 diff --git b/libs/fluidsynth/src/fluid_rev.c a/libs/fluidsynth/src/fluid_rev.c
-index 51b0e79..166007d 100644
+index 8a58d1e85..51b4faa25 100644
 --- b/libs/fluidsynth/src/fluid_rev.c
 +++ a/libs/fluidsynth/src/fluid_rev.c
-@@ -75,7 +75,7 @@ void fluid_allpass_init(fluid_allpass* allpass);
- void fluid_allpass_setfeedback(fluid_allpassallpass, fluid_real_t val);
- fluid_real_t fluid_allpass_getfeedback(fluid_allpassallpass);
+@@ -51,7 +51,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_allpassallpass, int size)
+ 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;
+     allpass->bufidx = 0;
+@@ -59,7 +59,7 @@ fluid_allpass_setbuffer(fluid_allpass *allpass, int size)
+     allpass->bufsize = size;
  }
  
 -void
 +static void
- fluid_allpass_release(fluid_allpassallpass)
+ fluid_allpass_release(fluid_allpass *allpass)
  {
-   FLUID_FREE(allpass->buffer);
+     FLUID_FREE(allpass->buffer);
 diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.c a/libs/fluidsynth/src/fluid_rvoice_mixer.c
-index 4672cb8..d5369aa 100644
+index 3b264e4d9..8c5254f26 100644
 --- b/libs/fluidsynth/src/fluid_rvoice_mixer.c
 +++ a/libs/fluidsynth/src/fluid_rvoice_mixer.c
-@@ -24,12 +24,12 @@
+@@ -24,11 +24,9 @@
  #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
+ #include "fluid_synth.h"
  
+-
 -#define ENABLE_MIXER_THREADS 1
 +#undef ENABLE_MIXER_THREADS // Ardour does the multithreading -- synth.cpu-cores defaults to 1
  
  // If less than x voices, the thread overhead is larger than the gain,
  // so don't activate the thread(s).
 diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.h a/libs/fluidsynth/src/fluid_rvoice_mixer.h
-index eeb49ec..d4e41ca 100644
+index 4ee072e4b..1b3fceb34 100644
 --- b/libs/fluidsynth/src/fluid_rvoice_mixer.h
 +++ a/libs/fluidsynth/src/fluid_rvoice_mixer.h
-@@ -24,7 +24,7 @@
+@@ -24,7 +24,6 @@
  
  #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 b/libs/fluidsynth/src/fluid_settings.c a/libs/fluidsynth/src/fluid_settings.c
-index 78725fb..56de8c7 100644
+index d77d5ed79..05423384e 100644
 --- b/libs/fluidsynth/src/fluid_settings.c
 +++ a/libs/fluidsynth/src/fluid_settings.c
-@@ -22,9 +22,9 @@
+@@ -21,9 +21,6 @@
  #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);
+@@ -328,11 +325,13 @@ fluid_settings_init(fluid_settings_t *settings)
+     fluid_return_if_fail(settings != NULL);
  
-   fluid_synth_settings(settings);
+     fluid_synth_settings(settings);
 +#if 0
-   fluid_shell_settings(settings);
-   fluid_player_settings(settings);
-   fluid_file_renderer_settings(settings);
-   fluid_audio_driver_settings(settings);
-   fluid_midi_driver_settings(settings);
+     fluid_shell_settings(settings);
+     fluid_player_settings(settings);
+     fluid_file_renderer_settings(settings);
+     fluid_audio_driver_settings(settings);
+     fluid_midi_driver_settings(settings);
 +#endif
  }
  
  static int
 diff --git b/libs/fluidsynth/src/fluid_synth.c a/libs/fluidsynth/src/fluid_synth.c
-index 84ee289..a12260c 100644
+index a40ba2eaa..e8845632f 100644
 --- b/libs/fluidsynth/src/fluid_synth.c
 +++ a/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
+@@ -267,7 +267,7 @@ void fluid_version(int *major, int *minor, int *micro)
+  * @return FluidSynth version string, which is internal and should not be
+  *   modified or freed.
   */
--void fluid_sample_timer_process(fluid_synth_t* synth)
-+static void fluid_sample_timer_process(fluid_synth_t* synth)
+-char *
++const char *
+ fluid_version_str(void)
  {
-       fluid_sample_timer_t* st;
-       long msec;
+     return FLUIDSYNTH_VERSION;
+@@ -6435,6 +6435,7 @@ int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type)
+     FLUID_API_RETURN(FLUID_OK);
+ }
++#ifdef LADSPA
+ /**
+  * Return the LADSPA effects instance used by FluidSynth
+  *
+@@ -6447,6 +6448,7 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)
+     return synth->ladspa_fx;
+ }
++#endif
+ /**
+  * Configure a general-purpose IIR biquad filter.
 diff --git b/libs/fluidsynth/src/fluid_synth.h a/libs/fluidsynth/src/fluid_synth.h
-index 3af336d..019a8e0 100644
+index 95e2c2e5f..96dc54574 100644
 --- b/libs/fluidsynth/src/fluid_synth.h
 +++ a/libs/fluidsynth/src/fluid_synth.h
-@@ -37,8 +37,8 @@
+@@ -33,8 +33,6 @@
  #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"
  
+ /***************************************************************
+@@ -165,7 +163,9 @@ struct _fluid_synth_t
+     fluid_mod_t *default_mod;          /**< the (dynamic) list of default modulators */
++#ifdef LADSPA
+     fluid_ladspa_fx_t *ladspa_fx;      /**< Effects unit for LADSPA support */
++#endif
+     enum fluid_iir_filter_type custom_filter_type; /**< filter type of the user-defined filter currently used for all voices */
+     enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */
+ };
 diff --git b/libs/fluidsynth/src/fluid_sys.c a/libs/fluidsynth/src/fluid_sys.c
-index ee7d8d9..328f255 100644
+index 5a4a2dd93..c9662f778 100644
 --- b/libs/fluidsynth/src/fluid_sys.c
 +++ a/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;
-@@ -950,6 +950,7 @@ fluid_ostream_printf (fluid_ostream_t out, char* format, ...)
- #endif
- }
-+#if 0 // Ardour says: no, thanks
- int fluid_server_socket_join(fluid_server_socket_t *server_socket)
+@@ -202,9 +202,10 @@ fluid_log(int level, const char *fmt, ...)
+  * @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 *fluid_strtok(char **str, const char *delim)
  {
-   return fluid_thread_join (server_socket->thread);
-@@ -1294,3 +1295,4 @@ int delete_fluid_server_socket(fluid_server_socket_t *server_socket)
- }
+-    char *s, *d, *token;
++    char *s,  *token;
++              const char *d;
+     char c;
+     if(str == NULL || delim == NULL || !*delim)
+diff --git b/libs/fluidsynth/src/fluid_sys.h a/libs/fluidsynth/src/fluid_sys.h
+index 47cc95c11..d95f6159f 100644
+--- b/libs/fluidsynth/src/fluid_sys.h
++++ a/libs/fluidsynth/src/fluid_sys.h
+@@ -91,7 +91,7 @@ else \
+ /*
+  * Utility functions
+  */
+-char *fluid_strtok(char **str, char *delim);
++char *fluid_strtok(char **str, const char *delim);
  
- #endif
-+#endif
-diff --git b/libs/fluidsynth/src/fluid_tuning.c a/libs/fluidsynth/src/fluid_tuning.c
-index cc440aa..8977ed6 100644
---- b/libs/fluidsynth/src/fluid_tuning.c
-+++ a/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;
- }
+ #if defined(__OS2__)
 diff --git b/libs/fluidsynth/src/fluidsynth_priv.h a/libs/fluidsynth/src/fluidsynth_priv.h
-index faf2772..b01618d 100644
+index 41e398398..d500f6174 100644
 --- b/libs/fluidsynth/src/fluidsynth_priv.h
 +++ a/libs/fluidsynth/src/fluidsynth_priv.h
-@@ -28,14 +28,6 @@
+@@ -26,10 +26,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
-@@ -113,8 +105,6 @@
+@@ -133,8 +129,9 @@ typedef guint64  uint64_t;
  #endif
  
- #if HAVE_WINDOWS_H
+ #if defined(WIN32) &&  HAVE_WINDOWS_H
 -#include <winsock2.h>
--#include <ws2tcpip.h>
- #include <windows.h>
- #endif
-@@ -131,6 +121,12 @@
- #define STDOUT_FILENO 1
- #define STDERR_FILENO 2
-+#elif defined _MSC_VER
-+
-+#define STDIN_FILENO  _fileno(stdin)
-+#define STDOUT_FILENO _fileno(stdout)
-+#define STDERR_FILENO _fileno(stderr)
-+
- #endif
+-#include <ws2tcpip.h> /* Provides also socklen_t */
++//#include <winsock2.h>
++//#include <ws2tcpip.h>       /* Provides also socklen_t */
++#include <windows.h>
  
- /* Darwin special defines (taken from config_macosx.h) */
+ /* WIN32 special defines */
+ #define DSOUND_SUPPORT 1
index 785d68bc0fedf510e833f67c4b477b3eaf4c5d38..c64e8adf6f23e1931a45d30204ff3fd898ae1578 100755 (executable)
@@ -23,7 +23,7 @@ cd $TMP
 #git clone git://git.code.sf.net/p/fluidsynth/code-git fs-git
 git clone git://github.com/FluidSynth/fluidsynth.git fs-git
 
-FSR=fs-git/fluidsynth/
+FSR=fs-git/
 
 rsync -auc --info=progress2 \
        ${FSR}src/midi/fluid_midi.c \
@@ -48,18 +48,23 @@ rsync -auc --info=progress2 \
        ${FSR}src/rvoice/fluid_rvoice_mixer.h \
        ${FSR}src/sfloader/fluid_defsfont.c \
        ${FSR}src/sfloader/fluid_defsfont.h \
+       ${FSR}src/sfloader/fluid_samplecache.c \
+       ${FSR}src/sfloader/fluid_samplecache.h \
+       ${FSR}src/sfloader/fluid_sffile.c \
+       ${FSR}src/sfloader/fluid_sffile.h \
+       ${FSR}src/sfloader/fluid_sfont.c \
        ${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_event.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_synth_monopoly.c \
        ${FSR}src/synth/fluid_tuning.c \
        ${FSR}src/synth/fluid_tuning.h \
        ${FSR}src/synth/fluid_voice.c \