+
+| 0.000182 |
+
+Audio Decoder for extension .wav registered.
+ |
+
+
+| 0.00021 |
+
+Audio Decoder for extension .raw registered.
+ |
+
+
+| 0.000222 |
+
+Data Source named FileSystem registered (Priority 0).
+ |
+
+
+| 0.022694 |
+
+OpenAL Version: 1.1 ALSOFT 1.17.2
+ |
+
+
+| 0.022856 |
+
+Vendor: OpenAL Community
+ |
+
+
+| 0.022917 |
+
+Renderer: OpenAL Soft
+ |
+
+
+| 0.023275 |
+
+EFX Version: 1.0
+ |
+
+
+| 0.023342 |
+
+EFX supported and enabled.
+ |
+
+
+| 0.02339 |
+
+Supported Extensions: AL_EXT_ALAW AL_EXT_BFORMAT AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW AL_EXT_MULAW_BFORMAT AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model AL_LOKI_quadriphonic AL_SOFT_block_alignment AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates AL_SOFT_direct_channels AL_SOFT_loop_points AL_SOFT_MSADPCM AL_SOFT_source_latency AL_SOFT_source_length
+ |
+
+
+| 0.044849 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
+
+| 0.09862 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
+
+| 0.144129 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
+
+| 0.206226 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
+
+| 0.257775 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
+
+| 0.33304 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
+
+| 0.398748 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
+
+| 0.474654 |
+
+Audio Source (bling) created from Data Source cMemorySource.
+ |
+
diff --git a/Examples/Tutorial10Fluidlite/src/sitar.sf2 b/Examples/Tutorial10Fluidlite/src/sitar.sf2
new file mode 100644
index 0000000..bf926be
Binary files /dev/null and b/Examples/Tutorial10Fluidlite/src/sitar.sf2 differ
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/LICENSE b/Examples/Tutorial10Fluidlite/src/standalone/LICENSE
new file mode 100644
index 0000000..10cc81d
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/LICENSE
@@ -0,0 +1,15 @@
+FluidLite (c) 2016 Robin Lobel
+
+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
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/Makefile.os2 b/Examples/Tutorial10Fluidlite/src/standalone/Makefile.os2
new file mode 100644
index 0000000..2534abe
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/Makefile.os2
@@ -0,0 +1,53 @@
+# Makefile for OS/2 using Open Watcom
+
+INCLUDES = -I"../include" -I. -I"../stb"
+CPPFLAGS = -DHAVE_CONFIG_H=1 -DHAVE_ALLOCA_H=1 -DNDEBUG
+CPPFLAGS+= -DSF3_SUPPORT=SF3_STB_VORBIS
+
+CFLAGS = -zq -bt=os2 -bm -fp5 -fpi87 -mf -oeatxh -wx -wcd=303 -ei -j -zp8
+# -5s : Pentium stack calling conventions
+CFLAGS+= -5s
+DLLFLAGS=-bd
+
+DLLNAME=fluidlit.dll
+EXPNAME=fluidlit.exp
+LIBNAME=fluidlit.lib
+LIBSTATIC=fluidlite_static.lib
+
+!ifeq target static
+BLD_TARGET=$(LIBSTATIC)
+!else
+CPPFLAGS+= -DFLUIDSYNTH_DLL_EXPORTS
+CFLAGS+= $(DLLFLAGS)
+BLD_TARGET=$(DLLNAME)
+!endif
+
+COMPILE=wcc386 $(CFLAGS) $(CPPFLAGS) $(INCLUDES)
+
+OBJ=fluid_sys.obj fluid_conv.obj fluid_hash.obj fluid_list.obj fluid_settings.obj &
+ stb_vorbis.obj &
+ fluid_defsfont.obj fluid_ramsfont.obj &
+ fluid_chorus.obj fluid_dsp_float.obj fluid_rev.obj &
+ fluid_chan.obj fluid_gen.obj fluid_mod.obj fluid_synth.obj fluid_tuning.obj fluid_voice.obj
+
+all: $(BLD_TARGET)
+
+.SUFFIXES:
+.SUFFIXES: .obj .c .asm
+
+.c: ../stb
+
+$(DLLNAME): $(OBJ)
+ wlink NAM $@ OP q SYSTEM os2v2_dll INITINSTANCE TERMINSTANCE FIL {$(OBJ)} OPTION IMPF=$(EXPNAME)
+ wlib -q -b -n -c -pa -s -t -zld -ii -io -inn $(LIBNAME) +$(DLLNAME)
+
+$(LIBSTATIC): $(OBJ)
+ wlib -q -b -n -c -pa -s -t -zld -ii -io $@ $(OBJ)
+
+.c.obj:
+ $(COMPILE) -fo=$^@ $<
+
+distclean: clean .symbolic
+ rm -f *.err $(LIBSTATIC) $(DLLNAME) $(LIBNAME) $(EXPNAME)
+clean: .symbolic
+ rm -f *.obj
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/README.md b/Examples/Tutorial10Fluidlite/src/standalone/README.md
new file mode 100644
index 0000000..cd35bc1
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/README.md
@@ -0,0 +1,65 @@
+# FluidLite
+
+[](https://opensource.org/licenses/LGPL-2.1)
+[](https://travis-ci.com/katyo/fluidlite)
+
+FluidLite (c) 2016 Robin Lobel
+
+FluidLite is a very light version of FluidSynth
+designed to be hardware, platform and external dependency independant.
+It only uses standard C libraries.
+
+It also adds support for SF3 files (SF2 files compressed with ogg vorbis)
+and an additional setting to remove the constraint of channel 9 (drums):
+fluid_settings_setstr(settings, "synth.drums-channel.active", "no");
+you can still select bank 128 on any channel to use drum kits.
+
+FluidLite keeps very minimal functionnalities (settings and synth),
+therefore MIDI file reading, realtime MIDI events and audio output must be
+implemented externally.
+
+## Config
+
+By default SF3 support is disabled. To enable it use `-DENABLE_SF3=YES` with cmake.
+
+Alternatively it can be configured to use [stb_vorbis](https://github.com/nothings/stb) to decompress SF3 instead of Xiph's [libogg](https://github.com/xiph/ogg)/[libvorbis](https://github.com/xiph/vorbis). You can pass `-DSTB_VORBIS=YES` to cmake to do it.
+
+## Usage
+
+```c
+#include
+#include
+
+#include "fluidlite.h"
+
+#define SAMPLE_RATE 44100
+#define SAMPLE_SIZE sizeof(float)
+#define NUM_FRAMES SAMPLE_RATE
+#define NUM_CHANNELS 2
+#define NUM_SAMPLES (NUM_FRAMES * NUM_CHANNELS)
+
+int main() {
+ fluid_settings_t* settings = new_fluid_settings();
+ fluid_synth_t* synth = new_fluid_synth(settings);
+ fluid_synth_sfload(synth, "soundfont.sf2", 1);
+
+ float* buffer = calloc(SAMPLE_SIZE, NUM_SAMPLES);
+
+ FILE* file = fopen("float32output.pcm", "wb");
+
+ fluid_synth_noteon(synth, 0, 60, 127);
+ fluid_synth_write_float(synth, NUM_FRAMES, buffer, 0, NUM_CHANNELS, buffer, 1, NUM_CHANNELS);
+ fwrite(buffer, SAMPLE_SIZE, NUM_SAMPLES, file);
+
+ fluid_synth_noteoff(synth, 0, 60);
+ fluid_synth_write_float(synth, NUM_FRAMES, buffer, 0, NUM_CHANNELS, buffer, 1, NUM_CHANNELS);
+ fwrite(buffer, SAMPLE_SIZE, NUM_SAMPLES, file);
+
+ fclose(file);
+
+ free(buffer);
+
+ delete_fluid_synth(synth);
+ delete_fluid_settings(settings);
+}
+```
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.c
new file mode 100644
index 0000000..4e73f6c
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.c
@@ -0,0 +1,455 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluid_chan.h"
+#include "fluid_mod.h"
+#include "fluid_synth.h"
+#include "fluid_sfont.h"
+
+#define SETCC(_c,_n,_v) _c->cc[_n] = _v
+
+/*
+ * new_fluid_channel
+ */
+fluid_channel_t*
+new_fluid_channel(fluid_synth_t* synth, int num)
+{
+ fluid_channel_t* chan;
+
+ chan = FLUID_NEW(fluid_channel_t);
+ if (chan == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ chan->synth = synth;
+ chan->channum = num;
+ chan->preset = NULL;
+
+ fluid_channel_init(chan);
+ fluid_channel_init_ctrl(chan,0);
+
+ return chan;
+}
+
+void
+fluid_channel_init(fluid_channel_t* chan)
+{
+ chan->prognum = 0;
+ chan->banknum = 0;
+ chan->sfontnum = 0;
+
+ if (chan->preset) delete_fluid_preset (chan->preset);
+ chan->preset = fluid_synth_find_preset(chan->synth, chan->banknum, chan->prognum);
+
+ chan->interp_method = FLUID_INTERP_DEFAULT;
+ chan->tuning = NULL;
+ chan->nrpn_select = 0;
+ chan->nrpn_active = 0;
+}
+
+/*
+ @param is_all_ctrl_off if nonzero, only resets some controllers, according to
+ http://www.midi.org/techspecs/rp15.php
+*/
+void
+fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off)
+{
+ int i;
+
+ chan->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;
+ }
+
+ SETCC(chan, i, 0);
+ }
+ }
+ else {
+ for (i = 0; i < 128; i++) {
+ SETCC(chan, i, 0);
+ }
+ }
+
+ /* 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 */
+ SETCC(chan, RPN_LSB, 127);
+ SETCC(chan, RPN_MSB, 127);
+
+ /* Set NRPN controllers to NULL state */
+ SETCC(chan, NRPN_LSB, 127);
+ SETCC(chan, NRPN_MSB, 127);
+
+ /* Expression (MSB & LSB) */
+ SETCC(chan, EXPRESSION_MSB, 127);
+ SETCC(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++) {
+ SETCC(chan, i, 64);
+ }
+
+ /* Volume / initial attenuation (MSB & LSB) */
+ SETCC(chan, VOLUME_MSB, 100);
+ SETCC(chan, VOLUME_LSB, 0);
+
+ /* Pan (MSB & LSB) */
+ SETCC(chan, PAN_MSB, 64);
+ SETCC(chan, PAN_LSB, 0);
+
+ /* Reverb */
+ /* SETCC(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 */
+ }
+}
+
+void
+fluid_channel_reset(fluid_channel_t* chan)
+{
+ fluid_channel_init(chan);
+ fluid_channel_init_ctrl(chan,0);
+}
+
+/*
+ * delete_fluid_channel
+ */
+int
+delete_fluid_channel(fluid_channel_t* chan)
+{
+ if (chan->preset) delete_fluid_preset (chan->preset);
+ FLUID_FREE(chan);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_set_preset
+ */
+int
+fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset)
+{
+ fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum);
+ fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum);
+
+ if (chan->preset) delete_fluid_preset (chan->preset);
+ chan->preset = preset;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_get_preset
+ */
+fluid_preset_t*
+fluid_channel_get_preset(fluid_channel_t* chan)
+{
+ return chan->preset;
+}
+
+/*
+ * fluid_channel_get_banknum
+ */
+unsigned int
+fluid_channel_get_banknum(fluid_channel_t* chan)
+{
+ return chan->banknum;
+}
+
+/*
+ * fluid_channel_set_prognum
+ */
+int
+fluid_channel_set_prognum(fluid_channel_t* chan, int prognum)
+{
+ chan->prognum = prognum;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_get_prognum
+ */
+int
+fluid_channel_get_prognum(fluid_channel_t* chan)
+{
+ return chan->prognum;
+}
+
+/*
+ * fluid_channel_set_banknum
+ */
+int
+fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int banknum)
+{
+ chan->banknum = banknum;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_cc
+ */
+int
+fluid_channel_cc(fluid_channel_t* chan, int num, int value)
+{
+ chan->cc[num] = value;
+
+ switch (num) {
+
+ case SUSTAIN_SWITCH:
+ {
+ if (value < 64) {
+/* printf("** sustain off\n"); */
+ fluid_synth_damp_voices(chan->synth, chan->channum);
+ } else {
+/* printf("** sustain on\n"); */
+ }
+ }
+ break;
+
+ case BANK_SELECT_MSB:
+ {
+ if (chan->channum == 9 && fluid_settings_str_equal(chan->synth->settings, "synth.drums-channel.active", "yes")) {
+ return FLUID_OK; /* ignored */
+ }
+
+ chan->bank_msb = (unsigned char) (value & 0x7f);
+/* printf("** bank select msb recieved: %d\n", value); */
+
+ /* I fixed the handling of a MIDI bank select controller 0,
+ e.g., bank select MSB (or "coarse" bank select according to
+ my spec). Prior to this fix a channel's bank number was only
+ changed upon reception of MIDI bank select controller 32,
+ e.g, bank select LSB (or "fine" bank-select according to my
+ spec). [KLE]
+
+ FIXME: is this correct? [PH] */
+ fluid_channel_set_banknum(chan, (unsigned int)(value & 0x7f)); /* KLE */
+ }
+ break;
+
+ case BANK_SELECT_LSB:
+ {
+ if (chan->channum == 9 && fluid_settings_str_equal(chan->synth->settings, "synth.drums-channel.active", "yes")) {
+ return FLUID_OK; /* ignored */
+ }
+ /* FIXME: according to the Downloadable Sounds II specification,
+ bit 31 should be set when we receive the message on channel
+ 10 (drum channel) */
+ fluid_channel_set_banknum(chan, (((unsigned int) value & 0x7f)
+ + ((unsigned int) chan->bank_msb << 7)));
+ }
+ break;
+
+ case ALL_NOTES_OFF:
+ fluid_synth_all_notes_off(chan->synth, chan->channum);
+ break;
+
+ case ALL_SOUND_OFF:
+ fluid_synth_all_sounds_off(chan->synth, chan->channum);
+ break;
+
+ case ALL_CTRL_OFF:
+ fluid_channel_init_ctrl(chan,1);
+ fluid_synth_modulate_voices_all(chan->synth, chan->channum);
+ break;
+
+ case DATA_ENTRY_MSB:
+ {
+ int data = (value << 7) + chan->cc[DATA_ENTRY_LSB];
+
+ if (chan->nrpn_active) /* NRPN is active? */
+ {
+ /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */
+ if ((chan->cc[NRPN_MSB] == 120) && (chan->cc[NRPN_LSB] < 100))
+ {
+ if (chan->nrpn_select < GEN_LAST)
+ {
+ float val = fluid_gen_scale_nrpn(chan->nrpn_select, data);
+ fluid_synth_set_gen(chan->synth, chan->channum, chan->nrpn_select, val);
+ }
+
+ chan->nrpn_select = 0; /* Reset to 0 */
+ }
+ }
+ else if (chan->cc[RPN_MSB] == 0) /* RPN is active: MSB = 0? */
+ {
+ switch (chan->cc[RPN_LSB])
+ {
+ case RPN_PITCH_BEND_RANGE:
+ fluid_channel_pitch_wheel_sens (chan, value); /* Set bend range in semitones */
+ /* 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(chan->synth, chan->channum, GEN_FINETUNE,
+ (data - 8192) / 8192.0 * 100.0);
+ break;
+ case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */
+ fluid_synth_set_gen(chan->synth, chan->channum, GEN_COARSETUNE,
+ value - 64);
+ break;
+ case RPN_TUNING_PROGRAM_CHANGE:
+ break;
+ case RPN_TUNING_BANK_SELECT:
+ break;
+ case RPN_MODULATION_DEPTH_RANGE:
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case NRPN_MSB:
+ chan->cc[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 (chan->cc[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:
+ fluid_synth_modulate_voices(chan->synth, chan->channum, 1, num);
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_get_cc
+ */
+int
+fluid_channel_get_cc(fluid_channel_t* chan, int num)
+{
+ return ((num >= 0) && (num < 128))? chan->cc[num] : 0;
+}
+
+/*
+ * fluid_channel_pressure
+ */
+int
+fluid_channel_pressure(fluid_channel_t* chan, int val)
+{
+ chan->channel_pressure = val;
+ fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_CHANNELPRESSURE);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_pitch_bend
+ */
+int
+fluid_channel_pitch_bend(fluid_channel_t* chan, int val)
+{
+ chan->pitch_bend = val;
+ fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEEL);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_pitch_wheel_sens
+ */
+int
+fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val)
+{
+ chan->pitch_wheel_sensitivity = val;
+ fluid_synth_modulate_voices(chan->synth, chan->channum, 0, FLUID_MOD_PITCHWHEELSENS);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_channel_get_num
+ */
+int
+fluid_channel_get_num(fluid_channel_t* chan)
+{
+ return chan->channum;
+}
+
+/* Purpose:
+ * Sets the index of the interpolation method used on this channel,
+ * as in fluid_interp in fluidlite.h
+ */
+void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method)
+{
+ chan->interp_method = new_method;
+}
+
+/* Purpose:
+ * Returns the index of the interpolation method used on this channel,
+ * as in fluid_interp in fluidlite.h
+ */
+int fluid_channel_get_interp_method(fluid_channel_t* chan)
+{
+ return chan->interp_method;
+}
+
+unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan)
+{
+ return chan->sfontnum;
+}
+
+int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfontnum)
+{
+ chan->sfontnum = sfontnum;
+ return FLUID_OK;
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.h
new file mode 100644
index 0000000..f458a7a
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chan.h
@@ -0,0 +1,114 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUID_CHAN_H
+#define _FLUID_CHAN_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_midi.h"
+#include "fluid_tuning.h"
+
+/*
+ * fluid_channel_t
+ */
+struct _fluid_channel_t
+{
+ int channum;
+ unsigned int sfontnum;
+ unsigned int banknum;
+ unsigned int prognum;
+ fluid_preset_t* preset;
+ fluid_synth_t* synth;
+ char key_pressure[128];
+ short channel_pressure;
+ short pitch_bend;
+ short pitch_wheel_sensitivity;
+
+ /* controller values */
+ short cc[128];
+
+ /* cached values of last MSB values of MSB/LSB controllers */
+ unsigned char bank_msb;
+ int interp_method;
+
+ /* the micro-tuning */
+ fluid_tuning_t* tuning;
+
+ /* NRPN system */
+ short nrpn_select;
+ short 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(), wihich 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 sound font
+ * 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);
+int delete_fluid_channel(fluid_channel_t* chan);
+void fluid_channel_init(fluid_channel_t* chan);
+void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off);
+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);
+unsigned int fluid_channel_get_sfontnum(fluid_channel_t* chan);
+int fluid_channel_set_sfontnum(fluid_channel_t* chan, unsigned int sfont);
+unsigned int fluid_channel_get_banknum(fluid_channel_t* chan);
+int fluid_channel_set_banknum(fluid_channel_t* chan, unsigned int bank);
+int fluid_channel_set_prognum(fluid_channel_t* chan, int prognum);
+int fluid_channel_get_prognum(fluid_channel_t* chan);
+int fluid_channel_cc(fluid_channel_t* chan, int ctrl, int val);
+int fluid_channel_pressure(fluid_channel_t* chan, int val);
+int fluid_channel_pitch_bend(fluid_channel_t* chan, int val);
+int fluid_channel_pitch_wheel_sens(fluid_channel_t* chan, int val);
+int fluid_channel_get_cc(fluid_channel_t* chan, int num);
+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_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_set_tuning(_c, _t) { (_c)->tuning = _t; }
+#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL)
+#define fluid_channel_get_tuning(_c) ((_c)->tuning)
+#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64)
+#define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; }
+#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n])
+#define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n])
+
+#define fluid_channel_get_min_note_length_ticks(chan) \
+ ((chan)->synth->min_note_length_ticks)
+
+#endif /* _FLUID_CHAN_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.c
new file mode 100644
index 0000000..0f19995
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.c
@@ -0,0 +1,606 @@
+/*
+ * August 24, 1998
+ * Copyright (C) 1998 Juergen Mueller And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Juergen Mueller And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+/*
+
+ CHANGES
+
+ - Adapted for fluidsynth, Peter Hanappe, March 2002
+
+ - Variable delay line implementation using bandlimited
+ interpolation, code reorganization: Markus Nentwig May 2002
+
+ */
+
+
+/*
+ * Chorus effect.
+ *
+ * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ):
+ *
+ * * gain-in ___
+ * ibuff -----+--------------------------------------------->| |
+ * | _________ | |
+ * | | | * level 1 | |
+ * +---->| delay 1 |----------------------------->| |
+ * | |_________| | |
+ * | /|\ | |
+ * : | | |
+ * : +-----------------+ +--------------+ | + |
+ * : | Delay control 1 |<--| mod. speed 1 | | |
+ * : +-----------------+ +--------------+ | |
+ * | _________ | |
+ * | | | * level n | |
+ * +---->| delay n |----------------------------->| |
+ * |_________| | |
+ * /|\ |___|
+ * | |
+ * +-----------------+ +--------------+ | * gain-out
+ * | Delay control n |<--| mod. speed n | |
+ * +-----------------+ +--------------+ +----->obuff
+ *
+ *
+ * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n).
+ *
+ * The delay of each block is modulated between 0..depth ms
+ *
+ */
+
+
+/* Variable delay line implementation
+ * ==================================
+ *
+ * The modulated delay needs the value of the delayed signal between
+ * samples. A lowpass filter is used to obtain intermediate values
+ * between samples (bandlimited interpolation). The sample pulse
+ * train is convoluted with the impulse response of the low pass
+ * filter (sinc function). To make it work with a small number of
+ * samples, the sinc function is windowed (Hamming window).
+ *
+ */
+
+#include "fluid_chorus.h"
+#include "fluid_sys.h"
+
+#define MAX_CHORUS 99
+#define MAX_DELAY 100
+#define MAX_DEPTH 10
+#define MIN_SPEED_HZ 0.29
+#define MAX_SPEED_HZ 5
+
+/* Length of one delay line in samples:
+ * Set through MAX_SAMPLES_LN2.
+ * For example:
+ * MAX_SAMPLES_LN2=12
+ * => MAX_SAMPLES=pow(2,12)=4096
+ * => MAX_SAMPLES_ANDMASK=4095
+ */
+#define MAX_SAMPLES_LN2 12
+
+#define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1))
+#define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1)
+
+
+/* Interpolate how many steps between samples? Must be power of two
+ For example: 8 => use a resolution of 256 steps between any two
+ samples
+*/
+#define INTERPOLATION_SUBSAMPLES_LN2 8
+#define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1))
+#define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1)
+
+/* Use how many samples for interpolation? Must be odd. '7' sounds
+ relatively clean, when listening to the modulated delay signal
+ alone. For a demo on aliasing try '1' With '3', the aliasing is
+ still quite pronounced for some input frequencies
+*/
+#define INTERPOLATION_SAMPLES 5
+
+/* Private data for SKEL file */
+struct _fluid_chorus_t {
+ /* Store the values between fluid_chorus_set_xxx and fluid_chorus_update
+ * Logic behind this:
+ * - both 'parameter' and 'new_parameter' hold the same value.
+ * - To change the chorus settings, 'new_parameter' is modified and
+ * fluid_chorus_update is called.
+ * - If the new value is valid, it is copied to 'parameter'.
+ * - If it is invalid, 'new_parameter' is restored to 'parameter'.
+ */
+ int type; /* current value */
+ int new_type; /* next value, if parameter check is OK */
+ fluid_real_t depth_ms; /* current value */
+ fluid_real_t new_depth_ms; /* next value, if parameter check is OK */
+ fluid_real_t level; /* current value */
+ fluid_real_t new_level; /* next value, if parameter check is OK */
+ fluid_real_t speed_Hz; /* current value */
+ fluid_real_t new_speed_Hz; /* next value, if parameter check is OK */
+ int number_blocks; /* current value */
+ int new_number_blocks; /* next value, if parameter check is OK */
+
+ 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];
+};
+
+void fluid_chorus_triangle(int *buf, int len, int depth);
+void fluid_chorus_sine(int *buf, int len, int depth);
+
+fluid_chorus_t*
+new_fluid_chorus(fluid_real_t sample_rate)
+{
+ int i; int ii;
+ fluid_chorus_t* chorus;
+
+ chorus = FLUID_NEW(fluid_chorus_t);
+ if (chorus == NULL) {
+ fluid_log(FLUID_PANIC, "chorus: Out of memory");
+ return NULL;
+ }
+
+ FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t));
+
+ chorus->sample_rate = sample_rate;
+
+ /* Lookup table for the SI function (impulse response of an ideal low pass) */
+
+ /* i: Offset in terms of whole samples */
+ for (i = 0; i < INTERPOLATION_SAMPLES; i++){
+
+ /* ii: Offset in terms of fractional samples ('subsamples') */
+ for (ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++){
+ /* Move the origin into the center of the table */
+ double i_shifted = ((double) i- ((double) INTERPOLATION_SAMPLES) / 2.
+ + (double) ii / (double) INTERPOLATION_SUBSAMPLES);
+ if (fabs(i_shifted) < 0.000001) {
+ /* sinc(0) cannot be calculated straightforward (limit needed
+ for 0/0) */
+ chorus->sinc_table[i][ii] = (fluid_real_t)1.;
+
+ } else {
+ chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted);
+ /* Hamming window */
+ chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES));
+ };
+ };
+ };
+
+ /* allocate lookup tables */
+ chorus->lookup_tab = FLUID_ARRAY(int, (int) (chorus->sample_rate / MIN_SPEED_HZ));
+ if (chorus->lookup_tab == NULL) {
+ fluid_log(FLUID_PANIC, "chorus: Out of memory");
+ goto error_recovery;
+ }
+
+ /* allocate sample buffer */
+
+ chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES);
+ if (chorus->chorusbuf == NULL) {
+ fluid_log(FLUID_PANIC, "chorus: Out of memory");
+ goto error_recovery;
+ }
+
+ if (fluid_chorus_init(chorus) != FLUID_OK){
+ goto error_recovery;
+ };
+
+ return chorus;
+
+ error_recovery:
+ delete_fluid_chorus(chorus);
+ return NULL;
+}
+
+
+int
+fluid_chorus_init(fluid_chorus_t* chorus)
+{
+ int i;
+
+ for (i = 0; i < MAX_SAMPLES; i++) {
+ chorus->chorusbuf[i] = 0.0;
+ }
+
+ /* initialize the chorus with the default settings */
+ fluid_chorus_set_nr(chorus, FLUID_CHORUS_DEFAULT_N);
+ fluid_chorus_set_level(chorus, FLUID_CHORUS_DEFAULT_LEVEL);
+ fluid_chorus_set_speed_Hz(chorus, FLUID_CHORUS_DEFAULT_SPEED);
+ fluid_chorus_set_depth_ms(chorus, FLUID_CHORUS_DEFAULT_DEPTH);
+ fluid_chorus_set_type(chorus, FLUID_CHORUS_MOD_SINE);
+
+ return fluid_chorus_update(chorus);
+}
+
+/* Purpose:
+ * Sets the number of stages.
+ * Requires call to fluid_chorus_update afterwards.
+ * Range checking is performed there.*/
+void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr)
+{
+ chorus->new_number_blocks = nr;
+}
+
+/* Purpose:
+ * API function, read the current state of the chorus
+ */
+int fluid_chorus_get_nr(fluid_chorus_t* chorus)
+{
+ return chorus->number_blocks;
+};
+
+/* Purpose:
+ * Sets the mixing level of the signal from each delay line (linear).
+ * Requires calling fluid_chorus_update afterwards.*/
+void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level)
+{
+ chorus->new_level = level;
+}
+
+/* Purpose:
+ * API function, read the current state of the chorus
+ */
+fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus)
+{
+ return chorus->level;
+};
+
+/* Purpose:
+ * Sets the modulation frequency.
+ * Requires call to fluid_chorus_update afterwards.
+ * Range checking is performed there.*/
+void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz)
+{
+ chorus->new_speed_Hz = speed_Hz;
+}
+
+/* Purpose:
+ * API function, read the current state of the chorus
+ */
+fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus)
+{
+ return chorus->speed_Hz;
+};
+
+/* Purpose:
+ * Sets the modulation depth in ms.
+ * Requires call to fluid_chorus_update afterwards.
+ * Range checking is performed there.*/
+void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms)
+{
+ chorus->new_depth_ms=depth_ms;
+}
+
+/* Purpose:
+ * API function, read the current state of the chorus
+ */
+fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus)
+{
+ return chorus->depth_ms;
+};
+
+/* Purpose:
+ * Sets the type of the modulation waveform.
+ * Requires call to fluid_chorus_update afterwards.
+ * Check for meaningful values is performed there.*/
+void fluid_chorus_set_type(fluid_chorus_t* chorus, int type)
+{
+ chorus->new_type=type;
+}
+
+/* Purpose:
+ * API function, read the current state of the chorus
+ */
+int fluid_chorus_get_type(fluid_chorus_t* chorus)
+{
+ return chorus->type;
+};
+
+void
+delete_fluid_chorus(fluid_chorus_t* chorus)
+{
+ if (chorus == NULL) {
+ return;
+ }
+
+ if (chorus->chorusbuf != NULL) {
+ FLUID_FREE(chorus->chorusbuf);
+ }
+
+ if (chorus->lookup_tab != NULL) {
+ FLUID_FREE(chorus->lookup_tab);
+ }
+
+ FLUID_FREE(chorus);
+}
+
+
+/* Purpose:
+ * Calculates the internal chorus parameters using the settings from
+ * fluid_chorus_set_xxx. */
+int
+fluid_chorus_update(fluid_chorus_t* chorus)
+{
+ int i;
+ int modulation_depth_samples;
+
+ if (chorus->new_number_blocks < 0) {
+ fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0.");
+ chorus->new_number_blocks = 0;
+ } else if (chorus->new_number_blocks > MAX_CHORUS) {
+ fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.",
+ MAX_CHORUS);
+ chorus->new_number_blocks = MAX_CHORUS;
+ };
+
+ if (chorus->new_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->new_speed_Hz = MIN_SPEED_HZ;
+ } else if (chorus->new_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->new_speed_Hz = MAX_SPEED_HZ;
+ }
+ if (chorus->new_depth_ms < 0.0) {
+ fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0.");
+ chorus->new_depth_ms = 0.0;
+ }
+ /* Depth: Check for too high value through modulation_depth_samples. */
+
+ if (chorus->new_level < 0.0) {
+ fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0.");
+ chorus->new_level = 0.0;
+ } else if (chorus->new_level > 10) {
+ fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! "
+ "Setting it to 0.1.");
+ chorus->new_level = 0.1;
+ }
+
+ /* The modulating LFO goes through a full period every x samples: */
+ chorus->modulation_period_samples = chorus->sample_rate / chorus->new_speed_Hz;
+
+ /* The variation in delay time is x: */
+ modulation_depth_samples = (int)
+ (chorus->new_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;
+
+ chorus->type = chorus->new_type;
+ chorus->depth_ms = chorus->new_depth_ms;
+ chorus->level = chorus->new_level;
+ chorus->speed_Hz = chorus->new_speed_Hz;
+ chorus->number_blocks = chorus->new_number_blocks;
+ return FLUID_OK;
+
+/* failure: */
+ /* Note: This lives on the assumption, that the last chorus values were correct.
+ * If not, this will loop forever and a day. */
+/* fluid_log(FLUID_WARN, "chorus: Restoring last good settings"); */
+/* chorus->new_type = chorus->type; */
+/* chorus->new_depth_ms = chorus->depth_ms; */
+/* chorus->new_level = chorus->level; */
+/* chorus->new_speed_Hz = chorus->speed_Hz; */
+/* chorus->new_number_blocks = chorus->number_blocks; */
+/* return FLUID_FAILED; */
+}
+
+
+void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int sample_index;
+ int i;
+ fluid_real_t d_in, d_out;
+
+ for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
+
+ d_in = in[sample_index];
+ d_out = 0.0f;
+
+# if 0
+ /* Debug: Listen to the chorus signal only */
+ left_out[sample_index]=0;
+ right_out[sample_index]=0;
+#endif
+
+ /* Write the current sample into the circular buffer */
+ chorus->chorusbuf[chorus->counter] = d_in;
+
+ for (i = 0; i < chorus->number_blocks; i++) {
+ int ii;
+ /* Calculate the delay in subsamples for the delay line of chorus block nr. */
+
+ /* The value in the lookup table is so, that this expression
+ * will always be positive. It will always include a number of
+ * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
+ * remain positive at all times. */
+ int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
+ - chorus->lookup_tab[chorus->phase[i]]);
+
+ int pos_samples = pos_subsamples/INTERPOLATION_SUBSAMPLES;
+
+ /* modulo divide by INTERPOLATION_SUBSAMPLES */
+ pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+
+ for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
+ /* Add the delayed signal to the chorus sum d_out Note: The
+ * delay in the delay line moves backwards for increasing
+ * delay!*/
+
+ /* The & in chorusbuf[...] is equivalent to a division modulo
+ MAX_SAMPLES, only faster. */
+ d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
+ * chorus->sinc_table[ii][pos_subsamples];
+
+ pos_samples--;
+ };
+ /* Cycle the phase of the modulating LFO */
+ chorus->phase[i]++;
+ chorus->phase[i] %= (chorus->modulation_period_samples);
+ } /* foreach chorus block */
+
+ d_out *= chorus->level;
+
+ /* Add the chorus sum d_out to output */
+ left_out[sample_index] += d_out;
+ right_out[sample_index] += d_out;
+
+ /* Move forward in circular buffer */
+ chorus->counter++;
+ chorus->counter %= MAX_SAMPLES;
+
+ } /* foreach sample */
+}
+
+/* Duplication of code ... (replaces sample data instead of mixing) */
+void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int sample_index;
+ int i;
+ fluid_real_t d_in, d_out;
+
+ for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
+
+ d_in = in[sample_index];
+ d_out = 0.0f;
+
+# if 0
+ /* Debug: Listen to the chorus signal only */
+ left_out[sample_index]=0;
+ right_out[sample_index]=0;
+#endif
+
+ /* Write the current sample into the circular buffer */
+ chorus->chorusbuf[chorus->counter] = d_in;
+
+ for (i = 0; i < chorus->number_blocks; i++) {
+ int ii;
+ /* Calculate the delay in subsamples for the delay line of chorus block nr. */
+
+ /* The value in the lookup table is so, that this expression
+ * will always be positive. It will always include a number of
+ * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
+ * remain positive at all times. */
+ int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
+ - chorus->lookup_tab[chorus->phase[i]]);
+
+ int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
+
+ /* modulo divide by INTERPOLATION_SUBSAMPLES */
+ pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+
+ for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
+ /* Add the delayed signal to the chorus sum d_out Note: The
+ * delay in the delay line moves backwards for increasing
+ * delay!*/
+
+ /* The & in chorusbuf[...] is equivalent to a division modulo
+ MAX_SAMPLES, only faster. */
+ d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
+ * chorus->sinc_table[ii][pos_subsamples];
+
+ pos_samples--;
+ };
+ /* Cycle the phase of the modulating LFO */
+ chorus->phase[i]++;
+ chorus->phase[i] %= (chorus->modulation_period_samples);
+ } /* foreach chorus block */
+
+ d_out *= chorus->level;
+
+ /* Store the chorus sum d_out to output */
+ left_out[sample_index] = d_out;
+ right_out[sample_index] = d_out;
+
+ /* Move forward in circular buffer */
+ chorus->counter++;
+ chorus->counter %= MAX_SAMPLES;
+
+ } /* foreach sample */
+}
+
+/* Purpose:
+ *
+ * Calculates a modulation waveform (sine) Its value ( modulo
+ * MAXSAMPLES) varies between 0 and depth*INTERPOLATION_SUBSAMPLES.
+ * Its period length is len. The waveform data will be used modulo
+ * MAXSAMPLES only. Since MAXSAMPLES is substracted from the waveform
+ * a couple of times here, the resulting (current position in
+ * buffer)-(waveform sample) will always be positive.
+ */
+void fluid_chorus_sine(int *buf, int len, int depth)
+{
+ int i;
+ double val;
+
+ for (i = 0; i < len; i++) {
+ val = sin((double) i / (double)len * 2.0 * M_PI);
+ buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES);
+ buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
+ // printf("%i %i\n",i,buf[i]);
+ }
+}
+
+/* Purpose:
+ * Calculates a modulation waveform (triangle)
+ * See fluid_chorus_sine for comments.
+ */
+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;
+ }
+}
+
+void
+fluid_chorus_reset(fluid_chorus_t* chorus)
+{
+ fluid_chorus_init(chorus);
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.h
new file mode 100644
index 0000000..e82b2ff
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_chorus.h
@@ -0,0 +1,56 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_CHORUS_H
+#define _FLUID_CHORUS_H
+
+#include "fluidsynth_priv.h"
+
+
+typedef struct _fluid_chorus_t fluid_chorus_t;
+
+/*
+ * chorus
+ */
+fluid_chorus_t* new_fluid_chorus(fluid_real_t sample_rate);
+void delete_fluid_chorus(fluid_chorus_t* chorus);
+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);
+
+int fluid_chorus_init(fluid_chorus_t* chorus);
+void fluid_chorus_reset(fluid_chorus_t* chorus);
+
+void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr);
+void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level);
+void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz);
+void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms);
+void fluid_chorus_set_type(fluid_chorus_t* chorus, int type);
+int fluid_chorus_update(fluid_chorus_t* chorus);
+int fluid_chorus_get_nr(fluid_chorus_t* chorus);
+fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus);
+fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus);
+fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus);
+int fluid_chorus_get_type(fluid_chorus_t* chorus);
+
+
+#endif /* _FLUID_CHORUS_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_config.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_config.h
new file mode 100644
index 0000000..faaf29d
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_config.h
@@ -0,0 +1,38 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+
+/* Define to activate debugging message */
+#undef DEBUG
+
+/* Version number of package */
+#define VERSION "1.0.9"
+
+/* Define to 1 if your processor stores words with the most significant byte
+ first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
+#define SF3_DISABLED 0
+#define SF3_XIPH_VORBIS 1
+#define SF3_STB_VORBIS 2
+
+#ifndef SF3_SUPPORT
+#define SF3_SUPPORT SF3_DISABLED
+#endif
+
+#ifndef WITH_FLOAT
+#define WITH_FLOAT 1
+#endif
+
+#define HAVE_STRING_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STDIO_H 1
+#define HAVE_MATH_H 1
+#define HAVE_STDARG_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_LIMITS_H 1
+
+
+//#pragma warning(disable : 4244)
+//#pragma warning(disable : 4101)
+//#pragma warning(disable : 4305)
+//#pragma warning(disable : 4996)
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.c
new file mode 100644
index 0000000..cf83550
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.c
@@ -0,0 +1,320 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluid_conv.h"
+
+
+/* conversion tables */
+fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
+fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
+fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE];
+fluid_real_t fluid_posbp_tab[128];
+fluid_real_t fluid_concave_tab[128];
+fluid_real_t fluid_convex_tab[128];
+fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
+
+/*
+ * void fluid_synth_init
+ *
+ * Does all the initialization for this module.
+ */
+void
+fluid_conversion_config(void)
+{
+ int i;
+ double x;
+
+ for (i = 0; i < FLUID_CENTS_HZ_SIZE; i++) {
+ fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0);
+ }
+
+ /* centibels to amplitude conversion
+ * Note: SF2.01 section 8.1.3: Initial attenuation range is
+ * between 0 and 144 dB. Therefore a negative attenuation is
+ * not allowed.
+ */
+ for (i = 0; i < FLUID_CB_AMP_SIZE; i++) {
+ fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0);
+ }
+
+ /* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont
+ * specification in regards to volume attenuation. The below calculation
+ * is an approx. equation for generating a table equivelant to the
+ * cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told
+ * was generated from device testing. By the spec this should be centibels.
+ */
+ for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) {
+ fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR);
+ }
+
+ /* initialize the conversion tables (see fluid_mod.c
+ fluid_mod_get_value cases 4 and 8) */
+
+ /* concave unipolar positive transform curve */
+ fluid_concave_tab[0] = 0.0;
+ fluid_concave_tab[127] = 1.0;
+
+ /* convex unipolar positive transform curve */
+ fluid_convex_tab[0] = 0;
+ fluid_convex_tab[127] = 1.0;
+ x = log10(128.0 / 127.0);
+
+ /* There seems to be an error in the specs. The equations are
+ implemented according to the pictures on SF2.01 page 73. */
+
+ for (i = 1; i < 127; i++) {
+ x = -20.0 / 96.0 * log((i * i) / (127.0 * 127.0)) / log(10.0);
+ fluid_convex_tab[i] = (fluid_real_t) (1.0 - x);
+ fluid_concave_tab[127 - i] = (fluid_real_t) x;
+ }
+
+ /* initialize the pan conversion table */
+ x = PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
+ for (i = 0; i < FLUID_PAN_SIZE; i++) {
+ fluid_pan_tab[i] = (fluid_real_t) sin(i * x);
+ }
+}
+
+/*
+ * fluid_ct2hz
+ */
+fluid_real_t
+fluid_ct2hz_real(fluid_real_t cents)
+{
+ if (cents < 0)
+ return (fluid_real_t) 1.0;
+ else if (cents < 900) {
+ return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int) (cents + 300)];
+ } else if (cents < 2100) {
+ return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int) (cents - 900)];
+ } else if (cents < 3300) {
+ return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int) (cents - 2100)];
+ } else if (cents < 4500) {
+ return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int) (cents - 3300)];
+ } else if (cents < 5700) {
+ return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int) (cents - 4500)];
+ } else if (cents < 6900) {
+ return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int) (cents - 5700)];
+ } else if (cents < 8100) {
+ return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int) (cents - 6900)];
+ } else if (cents < 9300) {
+ return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int) (cents - 8100)];
+ } else if (cents < 10500) {
+ return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int) (cents - 9300)];
+ } else if (cents < 11700) {
+ return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int) (cents - 10500)];
+ } else if (cents < 12900) {
+ return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int) (cents - 11700)];
+ } else if (cents < 14100) {
+ return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int) (cents - 12900)];
+ } else {
+ return (fluid_real_t) 1.0; /* some loony trying to make you deaf */
+ }
+}
+
+/*
+ * fluid_ct2hz
+ */
+fluid_real_t
+fluid_ct2hz(fluid_real_t cents)
+{
+ /* Filter fc limit: SF2.01 page 48 # 8 */
+ if (cents >= 13500){
+ cents = 13500; /* 20 kHz */
+ } else if (cents < 1500){
+ cents = 1500; /* 20 Hz */
+ }
+ return fluid_ct2hz_real(cents);
+}
+
+/*
+ * fluid_cb2amp
+ *
+ * in: a value between 0 and 960, 0 is no attenuation
+ * out: a value between 1 and 0
+ */
+fluid_real_t
+fluid_cb2amp(fluid_real_t cb)
+{
+ /*
+ * cb: an attenuation in 'centibels' (1/10 dB)
+ * SF2.01 page 49 # 48 limits it to 144 dB.
+ * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit.
+ */
+
+ /* minimum attenuation: 0 dB */
+ if (cb < 0) {
+ return 1.0;
+ }
+ if (cb >= FLUID_CB_AMP_SIZE) {
+ return 0.0;
+ }
+ return fluid_cb2amp_tab[(int) cb];
+}
+
+/*
+ * fluid_atten2amp
+ *
+ * in: a value between 0 and 1440, 0 is no attenuation
+ * out: a value between 1 and 0
+ *
+ * Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't
+ * follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp.
+ */
+fluid_real_t
+fluid_atten2amp(fluid_real_t atten)
+{
+ if (atten < 0) return 1.0;
+ else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0;
+ else return fluid_atten2amp_tab[(int) atten];
+}
+
+/*
+ * fluid_tc2sec_delay
+ */
+fluid_real_t
+fluid_tc2sec_delay(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 21, 23, 25, 33
+ * SF2.01 section 8.1.3 items 21, 23, 25, 33
+ *
+ * The most negative number indicates a delay of 0. Range is limited
+ * from -12000 to 5000 */
+ if (tc <= -32768.0f) {
+ return (fluid_real_t) 0.0f;
+ };
+ if (tc < -12000.) {
+ tc = (fluid_real_t) -12000.0f;
+ }
+ if (tc > 5000.0f) {
+ tc = (fluid_real_t) 5000.0f;
+ }
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec_attack
+ */
+fluid_real_t
+fluid_tc2sec_attack(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 26, 34
+ * SF2.01 section 8.1.3 items 26, 34
+ * The most negative number indicates a delay of 0
+ * Range is limited from -12000 to 8000 */
+ if (tc<=-32768.){return (fluid_real_t) 0.0;};
+ if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
+ if (tc>8000.){tc=(fluid_real_t) 8000.0;};
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec
+ */
+fluid_real_t
+fluid_tc2sec(fluid_real_t tc)
+{
+ /* No range checking here! */
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec_release
+ */
+fluid_real_t
+fluid_tc2sec_release(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 30, 38
+ * SF2.01 section 8.1.3 items 30, 38
+ * No 'most negative number' rule here!
+ * Range is limited from -12000 to 8000 */
+ if (tc<=-32768.){return (fluid_real_t) 0.0;};
+ if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
+ if (tc>8000.){tc=(fluid_real_t) 8000.0;};
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_act2hz
+ *
+ * Convert from absolute cents to Hertz
+ */
+fluid_real_t
+fluid_act2hz(fluid_real_t c)
+{
+ return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0));
+}
+
+/*
+ * fluid_hz2ct
+ *
+ * Convert from Hertz to cents
+ */
+fluid_real_t
+fluid_hz2ct(fluid_real_t f)
+{
+ return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0));
+}
+
+/*
+ * fluid_pan
+ */
+fluid_real_t
+fluid_pan(fluid_real_t c, int left)
+{
+ if (left) {
+ c = -c;
+ }
+ if (c < -500) {
+ return (fluid_real_t) 0.0;
+ } else if (c > 500) {
+ return (fluid_real_t) 1.0;
+ } else {
+ return fluid_pan_tab[(int) (c + 500)];
+ }
+}
+
+/*
+ * fluid_concave
+ */
+fluid_real_t
+fluid_concave(fluid_real_t val)
+{
+ if (val < 0) {
+ return 0;
+ } else if (val > 127) {
+ return 1;
+ }
+ return fluid_concave_tab[(int) val];
+}
+
+/*
+ * fluid_convex
+ */
+fluid_real_t
+fluid_convex(fluid_real_t val)
+{
+ if (val < 0) {
+ return 0;
+ } else if (val > 127) {
+ return 1;
+ }
+ return fluid_convex_tab[(int) val];
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.h
new file mode 100644
index 0000000..83bb21e
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_conv.h
@@ -0,0 +1,63 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUID_CONV_H
+#define _FLUID_CONV_H
+
+#include "fluidsynth_priv.h"
+
+#define FLUID_CENTS_HZ_SIZE 1200
+#define FLUID_VEL_CB_SIZE 128
+#define FLUID_CB_AMP_SIZE 961
+#define FLUID_ATTEN_AMP_SIZE 1441
+#define FLUID_PAN_SIZE 1002
+
+/* EMU 8k/10k don't follow spec in regards to volume attenuation.
+ * This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR).
+ * By the standard this should be -200.0. */
+/* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity. Now it equals the response of EMU10K1 programming.*/
+#define FLUID_ATTEN_POWER_FACTOR (-200.0) /* was (-531.509)*/
+
+void fluid_conversion_config(void);
+
+fluid_real_t fluid_ct2hz_real(fluid_real_t cents);
+fluid_real_t fluid_ct2hz(fluid_real_t cents);
+fluid_real_t fluid_cb2amp(fluid_real_t cb);
+fluid_real_t fluid_atten2amp(fluid_real_t atten);
+fluid_real_t fluid_tc2sec(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_delay(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_attack(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_release(fluid_real_t tc);
+fluid_real_t fluid_act2hz(fluid_real_t c);
+fluid_real_t fluid_hz2ct(fluid_real_t c);
+fluid_real_t fluid_pan(fluid_real_t c, int left);
+fluid_real_t fluid_concave(fluid_real_t val);
+fluid_real_t fluid_convex(fluid_real_t val);
+
+extern fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
+extern fluid_real_t fluid_vel2cb_tab[FLUID_VEL_CB_SIZE];
+extern fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
+extern fluid_real_t fluid_posbp_tab[128];
+extern fluid_real_t fluid_concave_tab[128];
+extern fluid_real_t fluid_convex_tab[128];
+extern fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
+
+
+#endif /* _FLUID_CONV_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.c
new file mode 100644
index 0000000..0ba89f4
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.c
@@ -0,0 +1,3419 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * SoundFont file loading code borrowed from Smurf SoundFont Editor
+ * Copyright (C) 1999-2001 Josh Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include "fluid_defsfont.h"
+#include "fluid_sfont.h"
+/* Todo: Get rid of that 'include' */
+#include "fluid_sys.h"
+
+#if SF3_SUPPORT == SF3_XIPH_VORBIS
+#include "vorbis/codec.h"
+#include "vorbis/vorbisenc.h"
+#include "vorbis/vorbisfile.h"
+
+struct VorbisData {
+ int pos; // current position in audio->data()
+ char* data;
+ int datasize;
+};
+
+static struct VorbisData vorbisData;
+
+static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource);
+static int ovSeek(void* datasource, ogg_int64_t offset, int whence);
+static long ovTell(void* datasource);
+
+static ov_callbacks ovCallbacks = { ovRead, ovSeek, 0, ovTell };
+
+//---------------------------------------------------------
+// ovRead
+//---------------------------------------------------------
+
+static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource)
+{
+ struct VorbisData* vd = (struct VorbisData*)datasource;
+ size_t n = size * nmemb;
+ if (vd->datasize < (int)vd->pos + (int)n)
+ n = vd->datasize - vd->pos;
+ if (n) {
+ const char* src = vd->data + vd->pos;
+ memcpy(ptr, src, n);
+ vd->pos += n;
+ }
+
+ return n;
+}
+
+//---------------------------------------------------------
+// ovSeek
+//---------------------------------------------------------
+
+static int ovSeek(void* datasource, ogg_int64_t offset, int whence)
+{
+ struct VorbisData* vd = (struct VorbisData*)datasource;
+ switch(whence) {
+ case SEEK_SET:
+ vd->pos = offset;
+ break;
+ case SEEK_CUR:
+ vd->pos += offset;
+ break;
+ case SEEK_END:
+ vd->pos = vd->datasize - offset;
+ break;
+ }
+ return 0;
+}
+
+//---------------------------------------------------------
+// ovTell
+//---------------------------------------------------------
+
+static long ovTell(void* datasource)
+{
+ struct VorbisData* vd = (struct VorbisData*)datasource;
+ return vd->pos;
+}
+#endif
+
+#if SF3_SUPPORT == SF3_STB_VORBIS
+#define STB_VORBIS_HEADER_ONLY
+#include "stb_vorbis.c"
+#endif
+
+/***************************************************************
+ *
+ * SFONT LOADER
+ */
+
+static void* default_fopen(fluid_fileapi_t *fileapi, const char * path)
+{
+ return FLUID_FOPEN(path, "rb");
+}
+
+static int default_fclose(void* handle)
+{
+ return FLUID_FCLOSE((FILE *)handle);
+}
+
+static long default_ftell(void* handle)
+{
+ return FLUID_FTELL((FILE *)handle);
+}
+
+static int safe_fread(void *buf, int count, void* handle)
+{
+ if (FLUID_FREAD(buf, count, 1, (FILE *)handle) != 1)
+ {
+ if (feof ((FILE *)handle))
+ gerr (ErrEof, _("EOF while attemping to read %d bytes"), count);
+ else
+ FLUID_LOG (FLUID_ERR, _("File read failed"));
+
+ return FLUID_FAILED;
+ }
+ return FLUID_OK;
+}
+
+static int safe_fseek(void* handle, long ofs, int whence)
+{
+ if (FLUID_FSEEK((FILE *)handle, ofs, whence) != 0) {
+ FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence);
+ return FLUID_FAILED;
+ }
+ return FLUID_OK;
+}
+
+static const fluid_fileapi_t default_fileapi =
+{
+ NULL,
+ NULL,
+ default_fopen,
+ safe_fread,
+ safe_fseek,
+ default_fclose,
+ default_ftell
+};
+
+static fluid_fileapi_t* fluid_default_fileapi = (fluid_fileapi_t*)&default_fileapi;
+
+void fluid_init_default_fileapi(fluid_fileapi_t* fileapi) {
+ fileapi->data = NULL;
+ fileapi->free = NULL;
+ fileapi->fopen = default_fopen;
+ fileapi->fread = safe_fread;
+ fileapi->fseek = safe_fseek;
+ fileapi->fclose = default_fclose;
+ fileapi->ftell = default_ftell;
+}
+
+void fluid_set_default_fileapi(fluid_fileapi_t* fileapi) {
+ fluid_fileapi_delete(fluid_default_fileapi);
+ fluid_default_fileapi = fileapi == NULL ? (fluid_fileapi_t*)&default_fileapi : fileapi;
+}
+
+fluid_sfloader_t* new_fluid_defsfloader()
+{
+ fluid_sfloader_t* loader;
+
+ loader = FLUID_NEW(fluid_sfloader_t);
+ if (loader == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ loader->data = NULL;
+ loader->fileapi = fluid_default_fileapi;
+ loader->free = delete_fluid_defsfloader;
+ loader->load = fluid_defsfloader_load;
+
+ return loader;
+}
+
+int delete_fluid_defsfloader(fluid_sfloader_t* loader)
+{
+ if (loader) {
+ FLUID_FREE(loader);
+ }
+ return FLUID_OK;
+}
+
+fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename)
+{
+ fluid_defsfont_t* defsfont;
+ fluid_sfont_t* sfont;
+
+ defsfont = new_fluid_defsfont();
+
+ if (defsfont == NULL) {
+ return NULL;
+ }
+
+ sfont = loader->data ? (fluid_sfont_t*)loader->data : FLUID_NEW(fluid_sfont_t);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont->data = defsfont;
+ sfont->free = fluid_defsfont_sfont_delete;
+ sfont->get_name = fluid_defsfont_sfont_get_name;
+ sfont->get_preset = fluid_defsfont_sfont_get_preset;
+ sfont->iteration_start = fluid_defsfont_sfont_iteration_start;
+ sfont->iteration_next = fluid_defsfont_sfont_iteration_next;
+
+ if (fluid_defsfont_load(defsfont, filename, loader->fileapi) == FLUID_FAILED) {
+ delete_fluid_defsfont(defsfont);
+ return NULL;
+ }
+
+ return sfont;
+}
+
+
+
+/***************************************************************
+ *
+ * PUBLIC INTERFACE
+ */
+
+int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont)
+{
+ if (delete_fluid_defsfont(sfont->data) != 0) {
+ return -1;
+ }
+ FLUID_FREE(sfont);
+ return 0;
+}
+
+char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont)
+{
+ return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data);
+}
+
+fluid_preset_t*
+fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum)
+{
+ fluid_preset_t* preset;
+ fluid_defpreset_t* defpreset;
+
+ defpreset = fluid_defsfont_get_preset((fluid_defsfont_t*) sfont->data, bank, prenum);
+
+ if (defpreset == NULL) {
+ return NULL;
+ }
+
+ preset = FLUID_NEW(fluid_preset_t);
+ if (preset == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ preset->sfont = sfont;
+ preset->data = defpreset;
+ preset->free = fluid_defpreset_preset_delete;
+ preset->get_name = fluid_defpreset_preset_get_name;
+ preset->get_banknum = fluid_defpreset_preset_get_banknum;
+ preset->get_num = fluid_defpreset_preset_get_num;
+ preset->noteon = fluid_defpreset_preset_noteon;
+ preset->notify = NULL;
+
+ return preset;
+}
+
+void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont)
+{
+ fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data);
+}
+
+int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset)
+{
+ preset->free = fluid_defpreset_preset_delete;
+ preset->get_name = fluid_defpreset_preset_get_name;
+ preset->get_banknum = fluid_defpreset_preset_get_banknum;
+ preset->get_num = fluid_defpreset_preset_get_num;
+ preset->noteon = fluid_defpreset_preset_noteon;
+ preset->notify = NULL;
+
+ return fluid_defsfont_iteration_next((fluid_defsfont_t*) sfont->data, preset);
+}
+
+int fluid_defpreset_preset_delete(fluid_preset_t* preset)
+{
+ FLUID_FREE(preset);
+
+ /* TODO: free modulators */
+
+ return 0;
+}
+
+char* fluid_defpreset_preset_get_name(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_get_num(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth,
+ int chan, int key, int vel)
+{
+ return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel);
+}
+
+
+
+
+/***************************************************************
+ *
+ * SFONT
+ */
+
+/*
+ * new_fluid_defsfont
+ */
+fluid_defsfont_t* new_fluid_defsfont()
+{
+ fluid_defsfont_t* sfont;
+
+ 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;
+
+ return sfont;
+}
+
+/*
+ * delete_fluid_defsfont
+ */
+int delete_fluid_defsfont(fluid_defsfont_t* sfont)
+{
+ fluid_list_t *list;
+ fluid_defpreset_t* preset;
+ fluid_sample_t* sample;
+
+ /* Check that no samples are currently used */
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ sample = (fluid_sample_t*) fluid_list_get(list);
+ if (fluid_sample_refcount(sample) != 0) {
+ return -1;
+ }
+ }
+
+ if (sfont->filename != NULL) {
+ FLUID_FREE(sfont->filename);
+ }
+
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ delete_fluid_sample((fluid_sample_t*) fluid_list_get(list));
+ }
+
+ if (sfont->sample) {
+ delete_fluid_list(sfont->sample);
+ }
+
+ if (sfont->sampledata != NULL) {
+ FLUID_FREE(sfont->sampledata);
+ }
+
+ preset = sfont->preset;
+ while (preset != NULL) {
+ sfont->preset = preset->next;
+ delete_fluid_defpreset(preset);
+ preset = sfont->preset;
+ }
+
+ FLUID_FREE(sfont);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defsfont_get_name
+ */
+char* fluid_defsfont_get_name(fluid_defsfont_t* sfont)
+{
+ return sfont->filename;
+}
+
+void (*preset_callback) (unsigned int bank, unsigned int num, char* name)=NULL;
+void fluid_synth_set_preset_callback(void* callback)
+{
+ preset_callback=callback;
+}
+
+/*
+ * fluid_defsfont_load
+ */
+int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file, fluid_fileapi_t* fapi)
+{
+ SFData* sfdata;
+ fluid_list_t *p;
+ SFPreset* sfpreset;
+ SFSample* sfsample;
+ fluid_sample_t* sample;
+ fluid_defpreset_t* preset;
+
+ sfont->filename = FLUID_MALLOC(1 + FLUID_STRLEN(file));
+ if (sfont->filename == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_STRCPY(sfont->filename, file);
+
+ /* The actual loading is done in the sfont and sffile files */
+ sfdata = sfload_file(file, fapi);
+ if (sfdata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file");
+ return FLUID_FAILED;
+ }
+
+ /* Keep track of the position and size of the sample data because
+ it's loaded separately (and might be unoaded/reloaded in future) */
+ sfont->samplepos = sfdata->samplepos;
+ sfont->samplesize = sfdata->samplesize;
+
+ /* load sample data in one block */
+ if (fluid_defsfont_load_sampledata(sfont, fapi) != FLUID_OK)
+ goto err_exit;
+
+ /* Create all the sample headers */
+ p = sfdata->sample;
+ while (p != NULL) {
+ sfsample = (SFSample *) p->data;
+
+ sample = new_fluid_sample();
+ if (sample == NULL) goto err_exit;
+
+ if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK)
+ goto err_exit;
+
+ fluid_defsfont_add_sample(sfont, sample);
+ fluid_voice_optimize_sample(sample);
+ p = fluid_list_next(p);
+ }
+
+ /* Load all the presets */
+ p = sfdata->preset;
+ while (p != NULL) {
+ sfpreset = (SFPreset *) p->data;
+ preset = new_fluid_defpreset(sfont);
+ if (preset == NULL) goto err_exit;
+
+ if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK)
+ goto err_exit;
+
+ fluid_defsfont_add_preset(sfont, preset);
+ if(preset_callback) preset_callback(preset->bank,preset->num,preset->name);
+ p = fluid_list_next(p);
+ }
+ sfont_close (sfdata, fapi);
+
+ return FLUID_OK;
+
+err_exit:
+ sfont_close (sfdata, fapi);
+ return FLUID_FAILED;
+}
+
+/* fluid_defsfont_add_sample
+ *
+ * Add a sample to the SoundFont
+ */
+int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample)
+{
+ sfont->sample = fluid_list_append(sfont->sample, sample);
+ return FLUID_OK;
+}
+
+/* fluid_defsfont_add_preset
+ *
+ * Add a preset to the SoundFont
+ */
+int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset)
+{
+ fluid_defpreset_t *cur, *prev;
+ if (sfont->preset == NULL) {
+ preset->next = NULL;
+ sfont->preset = preset;
+ } else {
+ /* sort them as we go along. very basic sorting trick. */
+ cur = sfont->preset;
+ prev = NULL;
+ while (cur != NULL) {
+ if ((preset->bank < cur->bank)
+ || ((preset->bank == cur->bank) && (preset->num < cur->num))) {
+ if (prev == NULL) {
+ preset->next = cur;
+ sfont->preset = preset;
+ } else {
+ preset->next = cur;
+ prev->next = preset;
+ }
+ return FLUID_OK;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ preset->next = NULL;
+ prev->next = preset;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defsfont_load_sampledata
+ */
+int
+fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont, fluid_fileapi_t* fapi)
+{
+ fluid_file fd;
+ unsigned short endian;
+ fd = fapi->fopen(fapi, sfont->filename);
+ if (fd == NULL) {
+ FLUID_LOG(FLUID_ERR, "Can't open soundfont file");
+ return FLUID_FAILED;
+ }
+ if (fapi->fseek(fd, sfont->samplepos, SEEK_SET) == FLUID_FAILED) {
+ perror("error");
+ FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
+ return FLUID_FAILED;
+ }
+ sfont->sampledata = (short*) FLUID_MALLOC(sfont->samplesize);
+ if (sfont->sampledata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ if (fapi->fread(sfont->sampledata, sfont->samplesize, fd) == FLUID_FAILED) {
+ FLUID_LOG(FLUID_ERR, "Failed to read sample data");
+ return FLUID_FAILED;
+ }
+ fapi->fclose(fd);
+
+ /* I'm not sure this endian test is waterproof... */
+ endian = 0x0100;
+
+ /* If this machine is big endian, the sample have to byte swapped */
+ if (((char *) &endian)[0]) {
+ unsigned char* cbuf;
+ unsigned char hi, lo;
+ unsigned int i, j;
+ short s;
+ cbuf = (unsigned char*) sfont->sampledata;
+ for (i = 0, j = 0; j < sfont->samplesize; i++) {
+ lo = cbuf[j++];
+ hi = cbuf[j++];
+ s = (hi << 8) | lo;
+ sfont->sampledata[i] = s;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defsfont_get_sample
+ */
+fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s)
+{
+ fluid_list_t* list;
+ fluid_sample_t* sample;
+
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+
+ sample = (fluid_sample_t*) fluid_list_get(list);
+
+ if (FLUID_STRCMP(sample->name, s) == 0) {
+
+#if SF3_SUPPORT
+ if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) {
+ short *sampledata = NULL;
+ int sampleframes = 0;
+
+#if SF3_SUPPORT == SF3_XIPH_VORBIS
+ int sampledata_size = 0;
+ OggVorbis_File vf;
+
+ vorbisData.pos = 0;
+ vorbisData.data = (char*)sample->data + sample->start;
+ vorbisData.datasize = sample->end + 1 - sample->start;
+
+ if (ov_open_callbacks(&vorbisData, &vf, 0, 0, ovCallbacks) == 0) {
+#define BUFFER_SIZE 4096
+ int bytes_read = 0;
+ int section = 0;
+ for (;;) {
+ // allocate additional memory for samples
+ sampledata = realloc(sampledata, sampledata_size + BUFFER_SIZE);
+ bytes_read = ov_read(&vf, (char*)sampledata + sampledata_size, BUFFER_SIZE, 0, sizeof(short), 1, §ion);
+ if (bytes_read > 0) {
+ sampledata_size += bytes_read;
+ } else {
+ // shrink sampledata to actual size
+ sampledata = realloc(sampledata, sampledata_size);
+ break;
+ }
+ }
+
+ ov_clear(&vf);
+ }
+
+ // because we actually need num of frames so we should divide num of bytes to frame size
+ sampleframes = sampledata_size / sizeof(short);
+#endif
+
+#if SF3_SUPPORT == SF3_STB_VORBIS
+ const uint8 *data = (uint8*)sample->data + sample->start;
+ const int datasize = sample->end + 1 - sample->start;
+
+ int channels;
+ sampleframes = stb_vorbis_decode_memory(data, datasize, &channels, NULL, &sampledata);
+#endif
+ // point sample data to uncompressed data stream
+ sample->data = sampledata;
+ sample->start = 0;
+ sample->end = sampleframes - 1;
+
+ /* loop is fowled?? (cluck cluck :) */
+ if (sample->loopend > sample->end ||
+ sample->loopstart >= sample->loopend ||
+ sample->loopstart <= sample->start) {
+ /* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */
+ if ((sample->end - sample->start) >= 20) {
+ sample->loopstart = sample->start + 8;
+ sample->loopend = sample->end - 8;
+ } else { /* loop is fowled, sample is tiny (can't pad 8 samples) */
+ sample->loopstart = sample->start + 1;
+ sample->loopend = sample->end - 1;
+ }
+ }
+
+ sample->sampletype &= ~FLUID_SAMPLETYPE_OGG_VORBIS;
+ sample->sampletype |= FLUID_SAMPLETYPE_OGG_VORBIS_UNPACKED;
+
+ fluid_voice_optimize_sample(sample);
+ }
+#endif
+
+ return sample;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * fluid_defsfont_get_preset
+ */
+fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num)
+{
+ fluid_defpreset_t* preset = sfont->preset;
+ while (preset != NULL) {
+ if ((preset->bank == bank) && ((preset->num == num))) {
+ return preset;
+ }
+ preset = preset->next;
+ }
+ return NULL;
+}
+
+/*
+ * fluid_defsfont_iteration_start
+ */
+void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont)
+{
+ sfont->iter_cur = sfont->preset;
+}
+
+/*
+ * fluid_defsfont_iteration_next
+ */
+int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset)
+{
+ if (sfont->iter_cur == NULL) {
+ return 0;
+ }
+
+ preset->data = (void*) sfont->iter_cur;
+ sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur);
+ return 1;
+}
+
+/***************************************************************
+ *
+ * PRESET
+ */
+
+/*
+ * new_fluid_defpreset
+ */
+fluid_defpreset_t*
+new_fluid_defpreset(fluid_defsfont_t* sfont)
+{
+ fluid_defpreset_t* preset = FLUID_NEW(fluid_defpreset_t);
+ if (preset == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ preset->next = NULL;
+ preset->sfont = sfont;
+ preset->name[0] = 0;
+ preset->bank = 0;
+ preset->num = 0;
+ preset->global_zone = NULL;
+ preset->zone = NULL;
+ return preset;
+}
+
+/*
+ * delete_fluid_defpreset
+ */
+int
+delete_fluid_defpreset(fluid_defpreset_t* preset)
+{
+ int err = FLUID_OK;
+ fluid_preset_zone_t* zone;
+ if (preset->global_zone != NULL) {
+ if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ preset->global_zone = NULL;
+ }
+ zone = preset->zone;
+ while (zone != NULL) {
+ preset->zone = zone->next;
+ if (delete_fluid_preset_zone(zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ zone = preset->zone;
+ }
+ FLUID_FREE(preset);
+ return err;
+}
+
+int
+fluid_defpreset_get_banknum(fluid_defpreset_t* preset)
+{
+ return preset->bank;
+}
+
+int
+fluid_defpreset_get_num(fluid_defpreset_t* preset)
+{
+ return preset->num;
+}
+
+char*
+fluid_defpreset_get_name(fluid_defpreset_t* preset)
+{
+ return preset->name;
+}
+
+/*
+ * fluid_defpreset_next
+ */
+fluid_defpreset_t*
+fluid_defpreset_next(fluid_defpreset_t* preset)
+{
+ return preset->next;
+}
+
+
+/*
+ * fluid_defpreset_noteon
+ */
+int
+fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel)
+{
+ fluid_preset_zone_t *preset_zone, *global_preset_zone;
+ fluid_inst_t* inst;
+ fluid_inst_zone_t *inst_zone, *global_inst_zone, *z;
+ fluid_sample_t* sample;
+ fluid_voice_t* voice;
+ fluid_mod_t * mod;
+ fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */
+ int mod_list_count;
+ int i;
+
+ global_preset_zone = fluid_defpreset_get_global_zone(preset);
+
+ /* run thru all the zones of this preset */
+ preset_zone = fluid_defpreset_get_zone(preset);
+ while (preset_zone != NULL) {
+
+ /* check if the note falls into the key and velocity range of this
+ preset */
+ if (fluid_preset_zone_inside_range(preset_zone, key, vel)) {
+
+ inst = fluid_preset_zone_get_inst(preset_zone);
+ global_inst_zone = fluid_inst_get_global_zone(inst);
+
+ /* run thru all the zones of this instrument */
+ inst_zone = fluid_inst_get_zone(inst);
+ while (inst_zone != NULL) {
+
+ /* make sure this instrument zone has a valid sample */
+ sample = fluid_inst_zone_get_sample(inst_zone);
+ if (fluid_sample_in_rom(sample) || (sample == NULL)) {
+ inst_zone = fluid_inst_zone_next(inst_zone);
+ continue;
+ }
+
+ /* check if the note falls into the key and velocity range of this
+ instrument */
+
+ if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) {
+
+ /* this is a good zone. allocate a new synthesis process and
+ initialize it */
+
+ voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel);
+ if (voice == NULL) {
+ return FLUID_FAILED;
+ }
+
+
+ z = inst_zone;
+
+ /* Instrument level, generators */
+
+ for (i = 0; i < GEN_LAST; i++) {
+
+ /* SF 2.01 section 9.4 'bullet' 4:
+ *
+ * A generator in a local instrument zone supersedes a
+ * global instrument zone generator. Both cases supersede
+ * the default generator -> voice_gen_set */
+
+ if (inst_zone->gen[i].flags){
+ fluid_voice_gen_set(voice, i, inst_zone->gen[i].val);
+
+ } else if ((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) {
+ fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val);
+
+ } else {
+ /* The generator has not been defined in this instrument.
+ * Do nothing, leave it at the default.
+ */
+ }
+
+ } /* for all generators */
+
+ /* global instrument zone, modulators: Put them all into a
+ * list. */
+
+ mod_list_count = 0;
+
+ if (global_inst_zone){
+ mod = global_inst_zone->mod;
+ while (mod){
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+ }
+
+ /* local instrument zone, modulators.
+ * Replace modulators with the same definition in the list:
+ * SF 2.01 page 69, 'bullet' 8
+ */
+ mod = inst_zone->mod;
+
+ while (mod){
+
+ /* 'Identical' modulators will be deleted by setting their
+ * list entry to NULL. The list length is known, NULL
+ * entries will be ignored later. SF2.01 section 9.5.1
+ * page 69, 'bullet' 3 defines 'identical'. */
+
+ for (i = 0; i < mod_list_count; i++){
+ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){
+ mod_list[i] = NULL;
+ }
+ }
+
+ /* Finally add the new modulator to to the list. */
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+
+ /* Add instrument modulators (global / local) to the voice. */
+ for (i = 0; i < mod_list_count; i++){
+
+ mod = mod_list[i];
+
+ if (mod != NULL){ /* disabled modulators CANNOT be skipped. */
+
+ /* Instrument modulators -supersede- existing (default)
+ * modulators. SF 2.01 page 69, 'bullet' 6 */
+ fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE);
+ }
+ }
+
+ /* Preset level, generators */
+
+ for (i = 0; i < GEN_LAST; i++) {
+
+ /* SF 2.01 section 8.5 page 58: If some generators are
+ * encountered at preset level, they should be ignored */
+ if ((i != GEN_STARTADDROFS)
+ && (i != GEN_ENDADDROFS)
+ && (i != GEN_STARTLOOPADDROFS)
+ && (i != GEN_ENDLOOPADDROFS)
+ && (i != GEN_STARTADDRCOARSEOFS)
+ && (i != GEN_ENDADDRCOARSEOFS)
+ && (i != GEN_STARTLOOPADDRCOARSEOFS)
+ && (i != GEN_KEYNUM)
+ && (i != GEN_VELOCITY)
+ && (i != GEN_ENDLOOPADDRCOARSEOFS)
+ && (i != GEN_SAMPLEMODE)
+ && (i != GEN_EXCLUSIVECLASS)
+ && (i != GEN_OVERRIDEROOTKEY)) {
+
+ /* SF 2.01 section 9.4 'bullet' 9: A generator in a
+ * local preset zone supersedes a global preset zone
+ * generator. The effect is -added- to the destination
+ * summing node -> voice_gen_incr */
+
+ if (preset_zone->gen[i].flags) {
+ fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val);
+ } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) {
+ fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val);
+ } else {
+ /* The generator has not been defined in this preset
+ * Do nothing, leave it unchanged.
+ */
+ }
+ } /* if available at preset level */
+ } /* for all generators */
+
+
+ /* Global preset zone, modulators: put them all into a
+ * list. */
+ mod_list_count = 0;
+ if (global_preset_zone){
+ mod = global_preset_zone->mod;
+ while (mod){
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+ }
+
+ /* Process the modulators of the local preset zone. Kick
+ * out all identical modulators from the global preset zone
+ * (SF 2.01 page 69, second-last bullet) */
+
+ mod = preset_zone->mod;
+ while (mod){
+ for (i = 0; i < mod_list_count; i++){
+ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){
+ mod_list[i] = NULL;
+ }
+ }
+
+ /* Finally add the new modulator to the list. */
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+
+ /* Add preset modulators (global / local) to the voice. */
+ for (i = 0; i < mod_list_count; i++){
+ mod = mod_list[i];
+ if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */
+
+ /* Preset modulators -add- to existing instrument /
+ * default modulators. SF2.01 page 70 first bullet on
+ * page */
+ fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD);
+ }
+ }
+
+ /* add the synthesis process to the synthesis loop. */
+ fluid_synth_start_voice(synth, voice);
+
+ /* Store the ID of the first voice that was created by this noteon event.
+ * Exclusive class may only terminate older voices.
+ * That avoids killing voices, which have just been created.
+ * (a noteon event can create several voice processes with the same exclusive
+ * class - for example when using stereo samples)
+ */
+ }
+
+ inst_zone = fluid_inst_zone_next(inst_zone);
+ }
+ }
+ preset_zone = fluid_preset_zone_next(preset_zone);
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_set_global_zone
+ */
+int
+fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone)
+{
+ preset->global_zone = zone;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_import_sfont
+ */
+int
+fluid_defpreset_import_sfont(fluid_defpreset_t* preset,
+ SFPreset* sfpreset,
+ fluid_defsfont_t* sfont)
+{
+ fluid_list_t *p;
+ SFZone* sfzone;
+ fluid_preset_zone_t* zone;
+ int count;
+ char zone_name[256];
+ if (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) {
+ return FLUID_FAILED;
+ }
+ if ((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) {
+ fluid_defpreset_set_global_zone(preset, zone);
+ } else if (fluid_defpreset_add_zone(preset, zone) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ p = fluid_list_next(p);
+ count++;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_add_zone
+ */
+int
+fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone)
+{
+ if (preset->zone == NULL) {
+ zone->next = NULL;
+ preset->zone = zone;
+ } else {
+ zone->next = preset->zone;
+ preset->zone = zone;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_get_zone
+ */
+fluid_preset_zone_t*
+fluid_defpreset_get_zone(fluid_defpreset_t* preset)
+{
+ return preset->zone;
+}
+
+/*
+ * fluid_defpreset_get_global_zone
+ */
+fluid_preset_zone_t*
+fluid_defpreset_get_global_zone(fluid_defpreset_t* preset)
+{
+ return preset->global_zone;
+}
+
+/*
+ * fluid_preset_zone_next
+ */
+fluid_preset_zone_t*
+fluid_preset_zone_next(fluid_preset_zone_t* preset)
+{
+ return preset->next;
+}
+
+/*
+ * new_fluid_preset_zone
+ */
+fluid_preset_zone_t*
+new_fluid_preset_zone(char *name)
+{
+ int size;
+ fluid_preset_zone_t* zone = NULL;
+ zone = FLUID_NEW(fluid_preset_zone_t);
+ if (zone == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ zone->next = NULL;
+ size = 1 + FLUID_STRLEN(name);
+ zone->name = FLUID_MALLOC(size);
+ if (zone->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(zone);
+ return NULL;
+ }
+ FLUID_STRCPY(zone->name, name);
+ zone->inst = NULL;
+ zone->keylo = 0;
+ zone->keyhi = 128;
+ zone->vello = 0;
+ zone->velhi = 128;
+
+ /* Flag all generators as unused (default, they will be set when they are found
+ * in the sound font).
+ * This also sets the generator values to default, but that is of no concern here.*/
+ fluid_gen_set_default_values(&zone->gen[0]);
+ zone->mod = NULL; /* list of modulators */
+ return zone;
+}
+
+/***************************************************************
+ *
+ * PRESET_ZONE
+ */
+
+/*
+ * delete_fluid_preset_zone
+ */
+int
+delete_fluid_preset_zone(fluid_preset_zone_t* zone)
+{
+ fluid_mod_t *mod, *tmp;
+
+ mod = zone->mod;
+ while (mod) /* delete the modulators */
+ {
+ tmp = mod;
+ mod = mod->next;
+ fluid_mod_delete (tmp);
+ }
+
+ if (zone->name) FLUID_FREE (zone->name);
+ if (zone->inst) delete_fluid_inst (zone->inst);
+ FLUID_FREE(zone);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_preset_zone_import_sfont
+ */
+int
+fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *r;
+ SFGen* sfgen;
+ int count;
+ for (count = 0, r = sfzone->gen; r != NULL; count++) {
+ sfgen = (SFGen *) r->data;
+ switch (sfgen->id) {
+ case GEN_KEYRANGE:
+ zone->keylo = (int) sfgen->amount.range.lo;
+ zone->keyhi = (int) sfgen->amount.range.hi;
+ break;
+ case GEN_VELRANGE:
+ zone->vello = (int) sfgen->amount.range.lo;
+ zone->velhi = (int) sfgen->amount.range.hi;
+ break;
+ default:
+ /* FIXME: some generators have an unsigne word amount value but i don't know which ones */
+ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+ zone->gen[sfgen->id].flags = GEN_SET;
+ break;
+ }
+ r = fluid_list_next(r);
+ }
+ if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) {
+ zone->inst = (fluid_inst_t*) new_fluid_inst();
+ if (zone->inst == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+
+ /* Import the modulators (only SF2.1 and higher) */
+ for (count = 0, r = sfzone->mod; r != NULL; count++) {
+
+ SFMod* mod_src = (SFMod *)r->data;
+ fluid_mod_t * mod_dest = fluid_mod_new();
+ int type;
+
+ if (mod_dest == NULL){
+ return FLUID_FAILED;
+ }
+ mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
+
+ /* *** Amount *** */
+ mod_dest->amount = mod_src->amount;
+
+ /* *** Source *** */
+ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags1 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->src & (1<<7)){
+ mod_dest->flags1 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->src & (1<<8)){
+ mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->src & (1<<9)){
+ mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type=(mod_src->src) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags1 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags1 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags1 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags1 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount=0;
+ }
+
+ /* *** Dest *** */
+ mod_dest->dest = mod_src->dest; /* index of controlled generator */
+
+ /* *** Amount source *** */
+ mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */
+ mod_dest->flags2 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->amtsrc & (1<<7)){
+ mod_dest->flags2 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->amtsrc & (1<<8)){
+ mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->amtsrc & (1<<9)){
+ mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type = (mod_src->amtsrc) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags2 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags2 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags2 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags2 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount=0;
+ }
+
+ /* *** Transform *** */
+ /* SF2.01 only uses the 'linear' transform (0).
+ * Deactivate the modulator by setting the amount to 0 in any other case.
+ */
+ if (mod_src->trans !=0){
+ mod_dest->amount = 0;
+ }
+
+ /* Store the new modulator in the zone The order of modulators
+ * will make a difference, at least in an instrument context: The
+ * second modulator overwrites the first one, if they only differ
+ * in amount. */
+ if (count == 0){
+ zone->mod = mod_dest;
+ } else {
+ fluid_mod_t * last_mod = zone->mod;
+
+ /* Find the end of the list */
+ while (last_mod->next != NULL){
+ last_mod=last_mod->next;
+ }
+
+ last_mod->next = mod_dest;
+ }
+
+ r = fluid_list_next(r);
+ } /* foreach modulator */
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_preset_zone_get_inst
+ */
+fluid_inst_t*
+fluid_preset_zone_get_inst(fluid_preset_zone_t* zone)
+{
+ return zone->inst;
+}
+
+/*
+ * fluid_preset_zone_inside_range
+ */
+int
+fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel)
+{
+ return ((zone->keylo <= key) &&
+ (zone->keyhi >= key) &&
+ (zone->vello <= vel) &&
+ (zone->velhi >= vel));
+}
+
+/***************************************************************
+ *
+ * INST
+ */
+
+/*
+ * new_fluid_inst
+ */
+fluid_inst_t*
+new_fluid_inst()
+{
+ fluid_inst_t* inst = FLUID_NEW(fluid_inst_t);
+ if (inst == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ inst->name[0] = 0;
+ inst->global_zone = NULL;
+ inst->zone = NULL;
+ return inst;
+}
+
+/*
+ * delete_fluid_inst
+ */
+int
+delete_fluid_inst(fluid_inst_t* inst)
+{
+ fluid_inst_zone_t* zone;
+ int err = FLUID_OK;
+ if (inst->global_zone != NULL) {
+ if (delete_fluid_inst_zone(inst->global_zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ inst->global_zone = NULL;
+ }
+ zone = inst->zone;
+ while (zone != NULL) {
+ inst->zone = zone->next;
+ if (delete_fluid_inst_zone(zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ zone = inst->zone;
+ }
+ FLUID_FREE(inst);
+ return err;
+}
+
+/*
+ * fluid_inst_set_global_zone
+ */
+int
+fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone)
+{
+ inst->global_zone = zone;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_import_sfont
+ */
+int
+fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *p;
+ SFZone* sfzone;
+ fluid_inst_zone_t* zone;
+ char zone_name[256];
+ int count;
+
+ p = sfinst->zone;
+ if (FLUID_STRLEN(sfinst->name) > 0) {
+ FLUID_STRCPY(inst->name, sfinst->name);
+ } else {
+ FLUID_STRCPY(inst->name, "");
+ }
+
+ count = 0;
+ while (p != NULL) {
+
+ sfzone = (SFZone *) p->data;
+ FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count);
+
+ zone = new_fluid_inst_zone(zone_name);
+ if (zone == NULL) {
+ return FLUID_FAILED;
+ }
+
+ if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) {
+ fluid_inst_set_global_zone(inst, zone);
+
+ } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ p = fluid_list_next(p);
+ count++;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_add_zone
+ */
+int
+fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone)
+{
+ if (inst->zone == NULL) {
+ zone->next = NULL;
+ inst->zone = zone;
+ } else {
+ zone->next = inst->zone;
+ inst->zone = zone;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_get_zone
+ */
+fluid_inst_zone_t*
+fluid_inst_get_zone(fluid_inst_t* inst)
+{
+ return inst->zone;
+}
+
+/*
+ * fluid_inst_get_global_zone
+ */
+fluid_inst_zone_t*
+fluid_inst_get_global_zone(fluid_inst_t* inst)
+{
+ return inst->global_zone;
+}
+
+/***************************************************************
+ *
+ * INST_ZONE
+ */
+
+/*
+ * new_fluid_inst_zone
+ */
+fluid_inst_zone_t*
+new_fluid_inst_zone(char* name)
+{
+ int size;
+ fluid_inst_zone_t* zone = NULL;
+ zone = FLUID_NEW(fluid_inst_zone_t);
+ if (zone == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ zone->next = NULL;
+ size = 1 + FLUID_STRLEN(name);
+ zone->name = FLUID_MALLOC(size);
+ if (zone->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(zone);
+ return NULL;
+ }
+ FLUID_STRCPY(zone->name, name);
+ zone->sample = NULL;
+ zone->keylo = 0;
+ zone->keyhi = 128;
+ zone->vello = 0;
+ zone->velhi = 128;
+
+ /* Flag the generators as unused.
+ * This also sets the generator values to default, but they will be overwritten anyway, if used.*/
+ fluid_gen_set_default_values(&zone->gen[0]);
+ zone->mod=NULL; /* list of modulators */
+ return zone;
+}
+
+/*
+ * delete_fluid_inst_zone
+ */
+int
+delete_fluid_inst_zone(fluid_inst_zone_t* zone)
+{
+ fluid_mod_t *mod, *tmp;
+
+ mod = zone->mod;
+ while (mod) /* delete the modulators */
+ {
+ tmp = mod;
+ mod = mod->next;
+ fluid_mod_delete (tmp);
+ }
+
+ if (zone->name) FLUID_FREE (zone->name);
+ FLUID_FREE(zone);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_zone_next
+ */
+fluid_inst_zone_t*
+fluid_inst_zone_next(fluid_inst_zone_t* zone)
+{
+ return zone->next;
+}
+
+/*
+ * fluid_inst_zone_import_sfont
+ */
+int
+fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *r;
+ SFGen* sfgen;
+ int count;
+
+ for (count = 0, r = sfzone->gen; r != NULL; count++) {
+ sfgen = (SFGen *) r->data;
+ switch (sfgen->id) {
+ case GEN_KEYRANGE:
+ zone->keylo = (int) sfgen->amount.range.lo;
+ zone->keyhi = (int) sfgen->amount.range.hi;
+ break;
+ case GEN_VELRANGE:
+ zone->vello = (int) sfgen->amount.range.lo;
+ zone->velhi = (int) sfgen->amount.range.hi;
+ break;
+ default:
+ /* FIXME: some generators have an unsigned word amount value but
+ i don't know which ones */
+ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+ zone->gen[sfgen->id].flags = GEN_SET;
+ break;
+ }
+ r = fluid_list_next(r);
+ }
+
+ /* FIXME */
+/* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */
+/* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */
+/* } */
+
+ if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) {
+ zone->sample = fluid_defsfont_get_sample(sfont, ((SFSample *) sfzone->instsamp->data)->name);
+ if (zone->sample == NULL) {
+ FLUID_LOG(FLUID_ERR, "Couldn't find sample name");
+ 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;
+ int type;
+ fluid_mod_t* mod_dest;
+
+ mod_dest = fluid_mod_new();
+ if (mod_dest == NULL){
+ return FLUID_FAILED;
+ }
+
+ mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
+
+ /* *** Amount *** */
+ mod_dest->amount = mod_src->amount;
+
+ /* *** Source *** */
+ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags1 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->src & (1<<7)){
+ mod_dest->flags1 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->src & (1<<8)){
+ mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->src & (1<<9)){
+ mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type = (mod_src->src) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags1 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags1 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags1 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags1 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount = 0;
+ }
+
+ /* *** Dest *** */
+ mod_dest->dest=mod_src->dest; /* index of controlled generator */
+
+ /* *** Amount source *** */
+ mod_dest->src2=mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags2 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->amtsrc & (1<<7)){
+ mod_dest->flags2 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->amtsrc & (1<<8)){
+ mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->amtsrc & (1<<9)){
+ mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type=(mod_src->amtsrc) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags2 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags2 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags2 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags2 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount = 0;
+ }
+
+ /* *** Transform *** */
+ /* SF2.01 only uses the 'linear' transform (0).
+ * Deactivate the modulator by setting the amount to 0 in any other case.
+ */
+ if (mod_src->trans !=0){
+ mod_dest->amount = 0;
+ }
+
+ /* Store the new modulator in the zone
+ * The order of modulators will make a difference, at least in an instrument context:
+ * The second modulator overwrites the first one, if they only differ in amount. */
+ if (count == 0){
+ zone->mod=mod_dest;
+ } else {
+ fluid_mod_t * last_mod=zone->mod;
+ /* Find the end of the list */
+ while (last_mod->next != NULL){
+ last_mod=last_mod->next;
+ }
+ last_mod->next=mod_dest;
+ }
+
+ r = fluid_list_next(r);
+ } /* foreach modulator */
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_zone_get_sample
+ */
+fluid_sample_t*
+fluid_inst_zone_get_sample(fluid_inst_zone_t* zone)
+{
+ return zone->sample;
+}
+
+/*
+ * fluid_inst_zone_inside_range
+ */
+int
+fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel)
+{
+ return ((zone->keylo <= key) &&
+ (zone->keyhi >= key) &&
+ (zone->vello <= vel) &&
+ (zone->velhi >= vel));
+}
+
+/***************************************************************
+ *
+ * SAMPLE
+ */
+
+/*
+ * new_fluid_sample
+ */
+fluid_sample_t*
+new_fluid_sample()
+{
+ fluid_sample_t* sample = NULL;
+
+ sample = FLUID_NEW(fluid_sample_t);
+ if (sample == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ memset(sample, 0, sizeof(fluid_sample_t));
+ sample->valid = 1;
+
+ return sample;
+}
+
+/*
+ * delete_fluid_sample
+ */
+int
+delete_fluid_sample(fluid_sample_t* sample)
+{
+#if SF3_SUPPORT
+ if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS_UNPACKED) {
+ if (sample->data != NULL) FLUID_FREE(sample->data);
+ }
+#endif
+
+ FLUID_FREE(sample);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_sample_in_rom
+ */
+int
+fluid_sample_in_rom(fluid_sample_t* sample)
+{
+ return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
+}
+
+/*
+ * fluid_sample_import_sfont
+ */
+int
+fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont)
+{
+ FLUID_STRCPY(sample->name, sfsample->name);
+ sample->data = sfont->sampledata;
+ sample->start = sfsample->start;
+ sample->end = sfsample->start + sfsample->end;
+ sample->loopstart = sfsample->start + sfsample->loopstart;
+ sample->loopend = sfsample->start + sfsample->loopend;
+ sample->samplerate = sfsample->samplerate;
+ sample->origpitch = sfsample->origpitch;
+ sample->pitchadj = sfsample->pitchadj;
+ sample->sampletype = sfsample->sampletype;
+
+ if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)
+ {
+
+ }
+
+ if (sample->sampletype & FLUID_SAMPLETYPE_ROM) {
+ sample->valid = 0;
+ FLUID_LOG(FLUID_WARN, "Ignoring sample %s: can't use ROM samples", sample->name);
+ }
+ if (sample->end - sample->start < 8) {
+ sample->valid = 0;
+ FLUID_LOG(FLUID_WARN, "Ignoring sample %s: too few sample data points", sample->name);
+ } else {
+/* if (sample->loopstart < sample->start + 8) { */
+/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required before loop start", sample->name); */
+/* sample->loopstart = sample->start + 8; */
+/* } */
+/* if (sample->loopend > sample->end - 8) { */
+/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required after loop end", sample->name); */
+/* sample->loopend = sample->end - 8; */
+/* } */
+ }
+ return FLUID_OK;
+}
+
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+
+
+/*=================================sfload.c========================
+ Borrowed from Smurf SoundFont Editor by Josh Green
+ =================================================================*/
+
+/*
+ functions for loading data from sfont files, with appropriate byte swapping
+ on big endian machines. Sfont IDs are not swapped because the ID read is
+ equivalent to the matching ID list in memory regardless of LE/BE machine
+*/
+
+#ifdef WORDS_BIGENDIAN
+#define READCHUNK(var,fd,fapi) G_STMT_START { \
+ if (fapi->fread(var, 8, fd) == FLUID_FAILED) \
+ return(FAIL); \
+ ((SFChunk *)(var))->size = GUINT32_FROM_BE(((SFChunk *)(var))->size); \
+} G_STMT_END
+#else
+#define READCHUNK(var,fd,fapi) G_STMT_START { \
+ if (fapi->fread(var, 8, fd) == FLUID_FAILED) \
+ return(FAIL); \
+ ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \
+} G_STMT_END
+#endif
+#define READID(var,fd,fapi) G_STMT_START { \
+ if (fapi->fread(var, 4, fd) == FLUID_FAILED) \
+ return(FAIL); \
+} G_STMT_END
+#define READSTR(var,fd,fapi) G_STMT_START { \
+ if (fapi->fread(var, 20, fd) == FLUID_FAILED) \
+ return(FAIL); \
+ (var)[20] = '\0'; \
+} G_STMT_END
+#ifdef WORDS_BIGENDIAN
+#define READD(var,fd,fapi) G_STMT_START { \
+ unsigned int _temp; \
+ if (fapi->fread(&_temp, 4, fd) == FLUID_FAILED) \
+ return(FAIL); \
+ var = GINT32_FROM_BE(_temp); \
+} G_STMT_END
+#else
+#define READD(var,fd,fapi) G_STMT_START { \
+ unsigned int _temp; \
+ if (fapi->fread(&_temp, 4, fd) == FLUID_FAILED) \
+ return(FAIL); \
+ var = GINT32_FROM_LE(_temp); \
+} G_STMT_END
+#endif
+#ifdef WORDS_BIGENDIAN
+#define READW(var,fd,fapi) G_STMT_START { \
+ unsigned short _temp; \
+ if (fapi->fread(&_temp, 2, fd) == FLUID_FAILED) \
+ return(FAIL); \
+var = GINT16_FROM_BE(_temp); \
+} G_STMT_END
+#else
+#define READW(var,fd,fapi) G_STMT_START { \
+ unsigned short _temp; \
+ if (fapi->fread(&_temp, 2, fd) == FLUID_FAILED) \
+ return(FAIL); \
+ var = GINT16_FROM_LE(_temp); \
+} G_STMT_END
+#endif
+#define READB(var,fd,fapi) G_STMT_START { \
+ if (fapi->fread(&var, 1, fd) == FLUID_FAILED) \
+ return(FAIL); \
+} G_STMT_END
+#define FSKIP(size,fd,fapi) G_STMT_START { \
+ if (fapi->fseek(fd, size, SEEK_CUR) == FLUID_FAILED) \
+ return(FAIL); \
+} G_STMT_END
+#define FSKIPW(fd,fapi) G_STMT_START { \
+ if (fapi->fseek(fd, 2, SEEK_CUR) == FLUID_FAILED) \
+ 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, void * fd, fluid_fileapi_t* fapi);
+static int read_listchunk (SFChunk * chunk, void * fd, fluid_fileapi_t* fapi);
+static int process_info (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int process_sdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk,
+ int * size, void * fd, fluid_fileapi_t* fapi);
+static int process_pdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_phdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_pbag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_pmod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_pgen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_ihdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_ibag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_imod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_igen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int load_shdr (unsigned int size, SFData * sf, void * fd, fluid_fileapi_t* fapi);
+static int fixup_pgen (SFData * sf);
+static int fixup_igen (SFData * sf);
+static int fixup_sample (SFData * sf);
+
+char idlist[] = {
+ "RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
+ "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdr"
+};
+
+static unsigned int sdtachunk_size;
+
+/* sound font file load functions */
+static int
+chunkid (unsigned int id)
+{
+ unsigned int i;
+ unsigned int *p;
+
+ p = (unsigned int *) idlist;
+ for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1)
+ if (*p == id)
+ return (i + 1);
+
+ return (UNKN_ID);
+}
+
+SFData *
+sfload_file (const char * fname, fluid_fileapi_t* fapi)
+{
+ SFData *sf = NULL;
+ void *fd;
+ int fsize = 0;
+ int err = FALSE;
+
+ if ((fd = fapi->fopen (fapi, fname)) == NULL)
+ {
+ FLUID_LOG (FLUID_ERR, _("Unable to open file \"%s\""), fname);
+ return (NULL);
+ }
+
+ if (!(sf = FLUID_NEW (SFData)))
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ err = TRUE;
+ }
+
+ if (!err)
+ {
+ memset (sf, 0, sizeof (SFData)); /* zero sfdata */
+ sf->fname = FLUID_STRDUP (fname); /* copy file name */
+ sf->sffd = fd;
+ }
+
+ /* get size of file */
+ if (!err && fapi->fseek (fd, 0L, SEEK_END) == FLUID_FAILED)
+ { /* seek to end of file */
+ err = TRUE;
+ FLUID_LOG (FLUID_ERR, _("Seek to end of file failed"));
+ }
+ if (!err && (fsize = fapi->ftell (fd)) == FLUID_FAILED)
+ { /* position = size */
+ err = TRUE;
+ FLUID_LOG (FLUID_ERR, _("Get end of file position failed"));
+ }
+ if (!err)
+ fapi->fseek (fd, 0, SEEK_SET);
+
+ if (!err && !load_body (fsize, sf, fd, fapi))
+ err = TRUE; /* load the sfont */
+
+ if (err)
+ {
+ if (sf)
+ sfont_close (sf, fapi);
+ return (NULL);
+ }
+
+ return (sf);
+}
+
+static int
+load_body (unsigned int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ SFChunk chunk;
+
+ READCHUNK (&chunk, fd, fapi); /* 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, fapi); /* load file ID */
+ if (chunkid (chunk.id) != SFBK_ID) { /* error if not SFBK_ID */
+ FLUID_LOG (FLUID_ERR, _("Not a sound font file"));
+ return (FAIL);
+ }
+
+ if (chunk.size != size - 8) {
+ gerr (ErrCorr, _("Sound font file size mismatch"));
+ return (FAIL);
+ }
+
+ /* Process INFO block */
+ if (!read_listchunk (&chunk, fd, fapi))
+ 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, fapi))
+ return (FAIL);
+
+ /* Process sample chunk */
+ if (!read_listchunk (&chunk, fd, fapi))
+ 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, fapi))
+ return (FAIL);
+
+ /* process HYDRA chunk */
+ if (!read_listchunk (&chunk, fd, fapi))
+ 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, fapi))
+ return (FAIL);
+
+ if (!fixup_pgen (sf))
+ return (FAIL);
+ if (!fixup_igen (sf))
+ return (FAIL);
+ if (!fixup_sample (sf))
+ return (FAIL);
+
+ /* sort preset list by bank, preset # */
+ sf->preset = fluid_list_sort (sf->preset,
+ (fluid_compare_func_t) sfont_preset_compare_func);
+
+ return (OK);
+}
+
+static int
+read_listchunk (SFChunk * chunk, void * fd, fluid_fileapi_t* fapi)
+{
+ READCHUNK (chunk, fd, fapi); /* 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, fapi); /* read id string */
+ chunk->size -= 4;
+ return (OK);
+}
+
+static int
+process_info (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ SFChunk chunk;
+ unsigned char id;
+ char *item;
+ unsigned short ver;
+
+ while (size > 0)
+ {
+ READCHUNK (&chunk, fd, fapi);
+ 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, fapi);
+ sf->version.major = ver;
+ READW (ver, fd, fapi);
+ 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 SF3_SUPPORT
+ if (sf->version.major == 3) {}
+ else
+#endif
+ 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, fapi);
+ sf->romver.major = ver;
+ READW (ver, fd, fapi);
+ 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 (fapi->fread (&item[1], chunk.size, fd) == FLUID_FAILED)
+ return (FAIL);
+
+ /* force terminate info item (don't forget uint8 info ID) */
+ *(item + chunk.size) = '\0';
+ }
+ else
+ return (gerr (ErrCorr, _("Invalid chunk id in INFO chunk")));
+ size -= chunk.size;
+ }
+
+ if (size < 0)
+ return (gerr (ErrCorr, _("INFO chunk size mismatch")));
+
+ return (OK);
+}
+
+static int
+process_sdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ SFChunk chunk;
+
+ if (size == 0)
+ return (OK); /* no sample data? */
+
+ /* read sub chunk */
+ READCHUNK (&chunk, fd, fapi);
+ size -= 8;
+
+ if (chunkid (chunk.id) != SMPL_ID)
+ return (gerr (ErrCorr,
+ _("Expected SMPL chunk found invalid id instead")));
+
+ if ((size - chunk.size) != 0)
+ return (gerr (ErrCorr, _("SDTA chunk size mismatch")));
+
+ /* sample data follows */
+ sf->samplepos = fapi->ftell (fd);
+
+ /* used in fixup_sample() to check validity of sample headers */
+ sdtachunk_size = chunk.size;
+ sf->samplesize = chunk.size;
+
+ FSKIP (chunk.size, fd, fapi);
+
+ return (OK);
+}
+
+static int
+pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk,
+ int * size, void * fd, fluid_fileapi_t* fapi)
+{
+ unsigned int id;
+ char *expstr;
+
+ expstr = CHNKIDSTR (expid); /* in case we need it */
+
+ READCHUNK (chunk, fd, fapi);
+ *size -= 8;
+
+ if ((id = chunkid (chunk->id)) != expid)
+ return (gerr (ErrCorr, _("Expected"
+ " PDTA sub-chunk \"%.4s\" found invalid id instead"), expstr));
+
+ if (chunk->size % reclen) /* valid chunk size? */
+ return (gerr (ErrCorr,
+ _("\"%.4s\" chunk size is not a multiple of %d bytes"), expstr,
+ reclen));
+ if ((*size -= chunk->size) < 0)
+ return (gerr (ErrCorr,
+ _("\"%.4s\" chunk size exceeds remaining PDTA chunk size"), expstr));
+ return (OK);
+}
+
+static int
+process_pdta (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ SFChunk chunk;
+
+ if (!pdtahelper (PHDR_ID, SFPHDRSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_phdr (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (PBAG_ID, SFBAGSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_pbag (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (PMOD_ID, SFMODSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_pmod (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (PGEN_ID, SFGENSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_pgen (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (IHDR_ID, SFIHDRSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_ihdr (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (IBAG_ID, SFBAGSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_ibag (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (IMOD_ID, SFMODSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_imod (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (IGEN_ID, SFGENSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_igen (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ if (!pdtahelper (SHDR_ID, SFSHDRSIZE, &chunk, &size, fd, fapi))
+ return (FAIL);
+ if (!load_shdr (chunk.size, sf, fd, fapi))
+ return (FAIL);
+
+ return (OK);
+}
+
+/* preset header loader */
+static int
+load_phdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi);
+ 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, fapi); /* possible read failure ^ */
+ READW (p->prenum, fd, fapi);
+ READW (p->bank, fd, fapi);
+ READW (zndx, fd, fapi);
+ READD (p->libr, fd, fapi);
+ READD (p->genre, fd, fapi);
+ READD (p->morph, fd, fapi);
+
+ 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, fapi);
+ READW (zndx, fd, fapi); /* Read terminal generator index */
+ FSKIP (12, fd, fapi);
+
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Preset header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ {
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+ }
+
+ return (OK);
+}
+
+/* preset bag loader */
+static int
+load_pbag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi); /* possible read failure ^ */
+ READW (modndx, fd, fapi);
+ 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, fapi);
+ READW (modndx, fd, fapi);
+
+ if (!pz)
+ {
+ if (genndx > 0)
+ FLUID_LOG (FLUID_WARN, _("No preset generators and terminal index not 0"));
+ if (modndx > 0)
+ FLUID_LOG (FLUID_WARN, _("No preset modulators and terminal index not 0"));
+ return (OK);
+ }
+
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr, _("Preset bag generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr, _("Preset bag modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+
+ return (OK);
+}
+
+/* preset modulator loader */
+static int
+load_pmod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi);
+ READW (m->dest, fd, fapi);
+ READW (m->amount, fd, fapi);
+ READW (m->amtsrc, fd, fapi);
+ READW (m->trans, fd, fapi);
+ 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, fapi); /* terminal mod */
+
+ return (OK);
+}
+
+/* -------------------------------------------------------------------
+ * preset generator loader
+ * generator (per preset) loading rules:
+ * Zones with no generators or modulators shall be annihilated
+ * Global zone must be 1st zone, discard additional ones (instrumentless zones)
+ *
+ * generator (per zone) loading rules (in order of decreasing precedence):
+ * KeyRange is 1st in list (if exists), else discard
+ * if a VelRange exists only preceded by a KeyRange, else discard
+ * if a generator follows an instrument discard it
+ * if a duplicate generator exists replace previous one
+ * ------------------------------------------------------------------- */
+static int
+load_pgen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi);
+
+ if (genid == Gen_KeyRange)
+ { /* nothing precedes */
+ if (level == 0)
+ {
+ level = 1;
+ READB (genval.range.lo, fd, fapi);
+ READB (genval.range.hi, fd, fapi);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_VelRange)
+ { /* only KeyRange precedes */
+ if (level <= 1)
+ {
+ level = 2;
+ READB (genval.range.lo, fd, fapi);
+ READB (genval.range.hi, fd, fapi);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_Instrument)
+ { /* inst is last gen */
+ level = 3;
+ READW (genval.uword, fd, fapi);
+ ((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, fapi);
+ 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, fapi);
+ }
+
+ 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, fapi);
+ 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, fapi); /* terminal gen */
+
+ return (OK);
+}
+
+/* instrument header loader */
+static int
+load_ihdr (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi);
+ 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, fapi); /* Possible read failure ^ */
+ READW (zndx, fd, fapi);
+
+ 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, fapi);
+ READW (zndx, fd, fapi);
+
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Instrument header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+
+ return (OK);
+}
+
+/* instrument bag loader */
+static int
+load_ibag (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi); /* READW = possible read failure */
+ READW (modndx, fd, fapi);
+ 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, fapi);
+ READW (modndx, fd, fapi);
+
+ if (!pz)
+ { /* in case that all are no zoners */
+ if (genndx > 0)
+ FLUID_LOG (FLUID_WARN,
+ _("No instrument generators and terminal index not 0"));
+ if (modndx > 0)
+ FLUID_LOG (FLUID_WARN,
+ _("No instrument modulators and terminal index not 0"));
+ return (OK);
+ }
+
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr, _("Instrument generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr, _("Instrument modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+
+ return (OK);
+}
+
+/* instrument modulator loader */
+static int
+load_imod (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi);
+ READW (m->dest, fd, fapi);
+ READW (m->amount, fd, fapi);
+ READW (m->amtsrc, fd, fapi);
+ READW (m->trans, fd, fapi);
+ 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, fapi); /* terminal mod */
+
+ return (OK);
+}
+
+/* load instrument generators (see load_pgen for loading rules) */
+static int
+load_igen (int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ 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, fapi);
+
+ if (genid == Gen_KeyRange)
+ { /* nothing precedes */
+ if (level == 0)
+ {
+ level = 1;
+ READB (genval.range.lo, fd, fapi);
+ READB (genval.range.hi, fd, fapi);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_VelRange)
+ { /* only KeyRange precedes */
+ if (level <= 1)
+ {
+ level = 2;
+ READB (genval.range.lo, fd, fapi);
+ READB (genval.range.hi, fd, fapi);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_SampleId)
+ { /* sample is last gen */
+ level = 3;
+ READW (genval.uword, fd, fapi);
+ ((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, fapi);
+ 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, fapi);
+ }
+
+ 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, fapi);
+ 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, fapi); /* terminal gen */
+
+ return (OK);
+}
+
+/* sample header loader */
+static int
+load_shdr (unsigned int size, SFData * sf, void * fd, fluid_fileapi_t* fapi)
+{
+ unsigned int i;
+ SFSample *p;
+
+ if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */
+ return (gerr (ErrCorr, _("Sample header has invalid size")));
+
+ size = size / SFSHDRSIZE - 1;
+ if (size == 0)
+ { /* at least one sample + term record? */
+ FLUID_LOG (FLUID_WARN, _("File contains no samples"));
+ FSKIP (SFSHDRSIZE, fd, fapi);
+ return (OK);
+ }
+
+ /* load all sample headers */
+ for (i = 0; i < size; i++)
+ {
+ p = FLUID_NEW (SFSample);
+ sf->sample = fluid_list_append (sf->sample, p);
+ READSTR (p->name, fd, fapi);
+ READD (p->start, fd, fapi);
+ READD (p->end, fd, fapi); /* - end, loopstart and loopend */
+ READD (p->loopstart, fd, fapi); /* - will be checked and turned into */
+ READD (p->loopend, fd, fapi); /* - offsets in fixup_sample() */
+ READD (p->samplerate, fd, fapi);
+ READB (p->origpitch, fd, fapi);
+ READB (p->pitchadj, fd, fapi);
+ FSKIPW (fd, fapi); /* skip sample link */
+ READW (p->sampletype, fd, fapi);
+ p->samfile = 0;
+ }
+
+ FSKIP (SFSHDRSIZE, fd, fapi); /* skip terminal shdr */
+
+ return (OK);
+}
+
+/* "fixup" (inst # -> inst ptr) instrument references in preset list */
+static int
+fixup_pgen (SFData * sf)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFZone *z;
+ uintptr i;
+
+ p = sf->preset;
+ while (p)
+ {
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* traverse this preset's zones */
+ z = (SFZone *) (p2->data);
+ if ((i = GPOINTER_TO_INT (z->instsamp)))
+ { /* load instrument # */
+ p3 = fluid_list_nth (sf->inst, i - 1);
+ if (!p3)
+ return (gerr (ErrCorr,
+ _("Preset %03d %03d: Invalid instrument reference"),
+ ((SFPreset *) (p->data))->bank,
+ ((SFPreset *) (p->data))->prenum));
+ z->instsamp = p3;
+ }
+ else
+ z->instsamp = NULL;
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/* "fixup" (sample # -> sample ptr) sample references in instrument list */
+static int
+fixup_igen (SFData * sf)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFZone *z;
+ uintptr i;
+
+ p = sf->inst;
+ while (p)
+ {
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* traverse instrument's zones */
+ z = (SFZone *) (p2->data);
+ if ((i = GPOINTER_TO_INT (z->instsamp)))
+ { /* load sample # */
+ p3 = fluid_list_nth (sf->sample, i - 1);
+ if (!p3)
+ return (gerr (ErrCorr,
+ _("Instrument \"%s\": Invalid sample reference"),
+ ((SFInst *) (p->data))->name));
+ z->instsamp = p3;
+ }
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/* convert sample end, loopstart and loopend to offsets and check if valid */
+static int
+fixup_sample (SFData * sf)
+{
+ fluid_list_t *p;
+ SFSample *sam;
+
+ p = sf->sample;
+ while (p)
+ {
+ sam = (SFSample *) (p->data);
+
+ /* if sample is not a ROM sample and end is over the sample data chunk
+ or sam start is greater than 4 less than the end (at least 4 samples) */
+ if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM)
+ && sam->end > sdtachunk_size) || sam->start > (sam->end - 4)) {
+ FLUID_LOG (FLUID_WARN, _("Sample '%s' start/end file positions are invalid,"
+ " disabling and will not be saved"), sam->name);
+
+ /* disable sample by setting all sample markers to 0 */
+ sam->start = sam->end = sam->loopstart = sam->loopend = 0;
+
+ return (OK);
+ }
+ /* compressed samples get fixed up after decompression */
+ else if (sam->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)
+ {}
+ else if (sam->loopend > sam->end || sam->loopstart >= sam->loopend
+ || sam->loopstart <= sam->start)
+ { /* loop is fowled?? (cluck cluck :) */
+ /* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */
+ if ((sam->end - sam->start) >= 20)
+ {
+ sam->loopstart = sam->start + 8;
+ sam->loopend = sam->end - 8;
+ }
+ else
+ { /* loop is fowled, sample is tiny (can't pad 8 samples) */
+ sam->loopstart = sam->start + 1;
+ sam->loopend = sam->end - 1;
+ }
+ }
+
+ /* convert sample end, loopstart, loopend to offsets from sam->start */
+ sam->end -= sam->start + 1; /* marks last sample, contrary to SF spec. */
+ sam->loopstart -= sam->start;
+ sam->loopend -= sam->start;
+
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/*=================================sfont.c========================
+ Smurf SoundFont Editor
+ ================================================================*/
+
+
+/* optimum chunk area sizes (could be more optimum) */
+#define PRESET_CHUNK_OPTIMUM_AREA 256
+#define INST_CHUNK_OPTIMUM_AREA 256
+#define SAMPLE_CHUNK_OPTIMUM_AREA 256
+#define ZONE_CHUNK_OPTIMUM_AREA 256
+#define MOD_CHUNK_OPTIMUM_AREA 256
+#define GEN_CHUNK_OPTIMUM_AREA 256
+
+unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4,
+ Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0
+};
+
+unsigned short badpgen[] = { Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs,
+ Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_EndAddrCoarseOfs,
+ Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity,
+ Gen_EndLoopAddrCoarseOfs, Gen_SampleModes, Gen_ExclusiveClass,
+ Gen_OverrideRootKey, 0
+};
+
+/* close SoundFont file and delete a SoundFont structure */
+void
+sfont_close (SFData * sf, fluid_fileapi_t* fapi)
+{
+ fluid_list_t *p, *p2;
+
+ if (sf->sffd)
+ fapi->fclose (sf->sffd);
+
+ if (sf->fname)
+ free (sf->fname);
+
+ p = sf->info;
+ while (p)
+ {
+ free (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list(sf->info);
+ sf->info = NULL;
+
+ p = sf->preset;
+ while (p)
+ { /* loop over presets */
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* loop over preset's zones */
+ sfont_free_zone (p2->data);
+ p2 = fluid_list_next (p2);
+ } /* free preset's zone list */
+ delete_fluid_list (((SFPreset *) (p->data))->zone);
+ FLUID_FREE (p->data); /* free preset chunk */
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->preset);
+ sf->preset = NULL;
+
+ p = sf->inst;
+ while (p)
+ { /* loop over instruments */
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* loop over inst's zones */
+ sfont_free_zone (p2->data);
+ p2 = fluid_list_next (p2);
+ } /* free inst's zone list */
+ delete_fluid_list (((SFInst *) (p->data))->zone);
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->inst);
+ sf->inst = NULL;
+
+ p = sf->sample;
+ while (p)
+ {
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->sample);
+ sf->sample = NULL;
+
+ FLUID_FREE (sf);
+}
+
+/* free all elements of a zone (Preset or Instrument) */
+void
+sfont_free_zone (SFZone * zone)
+{
+ fluid_list_t *p;
+
+ if (!zone)
+ return;
+
+ p = zone->gen;
+ while (p)
+ { /* Free gen chunks for this zone */
+ if (p->data)
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (zone->gen); /* free genlist */
+
+ p = zone->mod;
+ while (p)
+ { /* Free mod chunks for this zone */
+ if (p->data)
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (zone->mod); /* free modlist */
+
+ FLUID_FREE (zone); /* free zone chunk */
+}
+
+/* preset sort function, first by bank, then by preset # */
+int
+sfont_preset_compare_func (void* a, void* b)
+{
+ int aval, bval;
+
+ aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum;
+ bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum;
+
+ return (aval - bval);
+}
+
+/* delete zone from zone list */
+void
+sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone)
+{
+ *zlist = fluid_list_remove (*zlist, (void*) zone);
+ sfont_free_zone (zone);
+}
+
+/* Find generator in gen list */
+fluid_list_t *
+gen_inlist (int gen, fluid_list_t * genlist)
+{ /* is generator in gen list? */
+ fluid_list_t *p;
+
+ p = genlist;
+ while (p)
+ {
+ if (p->data == NULL)
+ return (NULL);
+ if (gen == ((SFGen *) p->data)->id)
+ break;
+ p = fluid_list_next (p);
+ }
+ return (p);
+}
+
+/* check validity of instrument generator */
+int
+gen_valid (int gen)
+{ /* is generator id valid? */
+ int i = 0;
+
+ if (gen > Gen_MaxValid)
+ return (FALSE);
+ while (badgen[i] && badgen[i] != gen)
+ i++;
+ return (badgen[i] == 0);
+}
+
+/* check validity of preset generator */
+int
+gen_validp (int gen)
+{ /* is preset generator valid? */
+ int i = 0;
+
+ if (!gen_valid (gen))
+ return (FALSE);
+ while (badpgen[i] && badpgen[i] != (unsigned short) gen)
+ i++;
+ return (badpgen[i] == 0);
+}
+
+/*================================util.c===========================*/
+
+/* Logging function, returns FAIL to use as a return value in calling funcs */
+int
+gerr (int ev, char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vprintf(fmt, args);
+ va_end (args);
+
+ printf("\n");
+
+ return (FAIL);
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.h
new file mode 100644
index 0000000..b626935
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_defsfont.h
@@ -0,0 +1,603 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_DEFSFONT_H
+#define _FLUID_DEFSFONT_H
+
+
+#include "fluidlite.h"
+#include "fluidsynth_priv.h"
+#include "fluid_list.h"
+
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+/*-----------------------------------sfont.h----------------------------*/
+
+#define SF_SAMPMODES_LOOP 1
+#define SF_SAMPMODES_UNROLL 2
+
+#define SF_MIN_SAMPLERATE 400
+#define SF_MAX_SAMPLERATE 50000
+
+#define SF_MIN_SAMPLE_LENGTH 32
+
+/* Sound Font structure defines */
+
+typedef struct _SFVersion
+{ /* version structure */
+ unsigned short major;
+ unsigned short minor;
+}
+SFVersion;
+
+typedef struct _SFMod
+{ /* Modulator structure */
+ unsigned short src; /* source modulator */
+ unsigned short dest; /* destination generator */
+ signed short amount; /* signed, degree of modulation */
+ unsigned short amtsrc; /* second source controls amnt of first */
+ unsigned short trans; /* transform applied to source */
+}
+SFMod;
+
+typedef union _SFGenAmount
+{ /* Generator amount structure */
+ signed short sword; /* signed 16 bit value */
+ unsigned short uword; /* unsigned 16 bit value */
+ struct
+ {
+ unsigned char lo; /* low value for ranges */
+ unsigned char hi; /* high value for ranges */
+ }
+ range;
+}
+SFGenAmount;
+
+typedef struct _SFGen
+{ /* Generator structure */
+ unsigned short id; /* generator ID */
+ SFGenAmount amount; /* generator value */
+}
+SFGen;
+
+typedef struct _SFZone
+{ /* Sample/instrument zone structure */
+ fluid_list_t *instsamp; /* instrument/sample pointer for zone */
+ fluid_list_t *gen; /* list of generators */
+ fluid_list_t *mod; /* list of modulators */
+}
+SFZone;
+
+typedef struct _SFSample
+{ /* Sample structure */
+ char name[21]; /* Name of sample */
+ unsigned char samfile; /* Loaded sfont/sample buffer = 0/1 */
+ unsigned int start; /* Offset in sample area to start of sample */
+ unsigned int end; /* Offset from start to end of sample,
+ this is the last point of the
+ sample, the SF spec has this as the
+ 1st point after, corrected on
+ load/save */
+ unsigned int loopstart; /* Offset from start to start of loop */
+ unsigned int loopend; /* Offset from start to end of loop,
+ marks the first point after loop,
+ whose sample value is ideally
+ equivalent to loopstart */
+ unsigned int samplerate; /* Sample rate recorded at */
+ unsigned char origpitch; /* root midi key number */
+ signed char pitchadj; /* pitch correction in cents */
+ unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */
+}
+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, fluid_fileapi_t * fileapi);
+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, fluid_fileapi_t * fileapi);
+
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+/* 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 02111-1307, USA.
+ */
+
+
+/* Provide definitions for some commonly used macros.
+ * Some of them are only provided if they haven't already
+ * been defined. It is assumed that if they are already
+ * defined then the current definition is correct.
+ */
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#define GPOINTER_TO_INT(p) ((uintptr) (p))
+#define GINT_TO_POINTER(i) ((void *) (uintptr)(i))
+
+char* g_strdup (const char *str);
+
+
+
+
+
+/* Provide simple macro statement wrappers (adapted from Perl):
+ * G_STMT_START { statements; } G_STMT_END;
+ * can be used as a single statement, as in
+ * if (x) G_STMT_START { ... } G_STMT_END; else ...
+ *
+ * For gcc we will wrap the statements within `({' and `})' braces.
+ * For SunOS they will be wrapped within `if (1)' and `else (void) 0',
+ * and otherwise within `do' and `while (0)'.
+ */
+#if !(defined (G_STMT_START) && defined (G_STMT_END))
+# if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus)
+# define G_STMT_START (void)(
+# define G_STMT_END )
+# else
+# if (defined (sun) || defined (__sun__))
+# define G_STMT_START if (1)
+# define G_STMT_END else (void)0
+# else
+# define G_STMT_START do
+# define G_STMT_END while (0)
+# endif
+# endif
+#endif
+
+
+/* Basic bit swapping functions
+ */
+#define GUINT16_SWAP_LE_BE_CONSTANT(val) ((unsigned short) ( \
+ (((unsigned short) (val) & (unsigned short) 0x00ffU) << 8) | \
+ (((unsigned short) (val) & (unsigned short) 0xff00U) >> 8)))
+#define GUINT32_SWAP_LE_BE_CONSTANT(val) ((unsigned int) ( \
+ (((unsigned int) (val) & (unsigned int) 0x000000ffU) << 24) | \
+ (((unsigned int) (val) & (unsigned int) 0x0000ff00U) << 8) | \
+ (((unsigned int) (val) & (unsigned int) 0x00ff0000U) >> 8) | \
+ (((unsigned int) (val) & (unsigned int) 0xff000000U) >> 24)))
+
+#define GUINT16_SWAP_LE_BE(val) (GUINT16_SWAP_LE_BE_CONSTANT (val))
+#define GUINT32_SWAP_LE_BE(val) (GUINT32_SWAP_LE_BE_CONSTANT (val))
+
+#define GINT16_TO_LE(val) ((signed short) (val))
+#define GUINT16_TO_LE(val) ((unsigned short) (val))
+#define GINT16_TO_BE(val) ((signed short) GUINT16_SWAP_LE_BE (val))
+#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val))
+#define GINT32_TO_LE(val) ((signed int) (val))
+#define GUINT32_TO_LE(val) ((unsigned int) (val))
+#define GINT32_TO_BE(val) ((signed int) GUINT32_SWAP_LE_BE (val))
+#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val))
+
+/* The G*_TO_?E() macros are defined in glibconfig.h.
+ * The transformation is symmetric, so the FROM just maps to the TO.
+ */
+#define GINT16_FROM_LE(val) (GINT16_TO_LE (val))
+#define GUINT16_FROM_LE(val) (GUINT16_TO_LE (val))
+#define GINT16_FROM_BE(val) (GINT16_TO_BE (val))
+#define GUINT16_FROM_BE(val) (GUINT16_TO_BE (val))
+#define GINT32_FROM_LE(val) (GINT32_TO_LE (val))
+#define GUINT32_FROM_LE(val) (GUINT32_TO_LE (val))
+#define GINT32_FROM_BE(val) (GINT32_TO_BE (val))
+#define GUINT32_FROM_BE(val) (GUINT32_TO_BE (val))
+
+
+/*-----------------------------------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, ...);
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+
+
+/***************************************************************
+ *
+ * FORWARD DECLARATIONS
+ */
+typedef struct _fluid_defsfont_t fluid_defsfont_t;
+typedef struct _fluid_defpreset_t fluid_defpreset_t;
+typedef struct _fluid_preset_zone_t fluid_preset_zone_t;
+typedef struct _fluid_inst_t fluid_inst_t;
+typedef struct _fluid_inst_zone_t fluid_inst_zone_t;
+
+/*
+
+ Public interface
+
+ */
+
+fluid_sfloader_t* new_fluid_defsfloader(void);
+int delete_fluid_defsfloader(fluid_sfloader_t* loader);
+fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename);
+
+
+int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont);
+char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont);
+fluid_preset_t* fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
+void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont);
+int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset);
+
+
+int fluid_defpreset_preset_delete(fluid_preset_t* preset);
+char* fluid_defpreset_preset_get_name(fluid_preset_t* preset);
+int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset);
+int fluid_defpreset_preset_get_num(fluid_preset_t* preset);
+int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+
+/*
+ * fluid_defsfont_t
+ */
+struct _fluid_defsfont_t
+{
+ char* filename; /* the filename of this soundfont */
+ unsigned int samplepos; /* the position in the file at which the sample data starts */
+ unsigned int samplesize; /* the size of the sample data */
+ short* sampledata; /* the sample data, loaded in ram */
+ fluid_list_t* sample; /* the samples in this soundfont */
+ fluid_defpreset_t* preset; /* the presets of this soundfont */
+
+ fluid_preset_t iter_preset; /* preset interface used in the iteration */
+ fluid_defpreset_t* iter_cur; /* the current preset in the iteration */
+};
+
+
+fluid_defsfont_t* new_fluid_defsfont(void);
+int delete_fluid_defsfont(fluid_defsfont_t* sfont);
+int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file, fluid_fileapi_t * fileapi);
+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, fluid_fileapi_t * fileapi);
+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_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s);
+
+
+/*
+ * fluid_preset_t
+ */
+struct _fluid_defpreset_t
+{
+ fluid_defpreset_t* next;
+ fluid_defsfont_t* sfont; /* the soundfont this preset belongs to */
+ char name[21]; /* the name of the preset */
+ unsigned int bank; /* the bank number */
+ unsigned int num; /* the preset number */
+ fluid_preset_zone_t* global_zone; /* the global zone of the preset */
+ fluid_preset_zone_t* zone; /* the chained list of preset zones */
+};
+
+fluid_defpreset_t* new_fluid_defpreset(fluid_defsfont_t* sfont);
+int delete_fluid_defpreset(fluid_defpreset_t* preset);
+fluid_defpreset_t* fluid_defpreset_next(fluid_defpreset_t* preset);
+int fluid_defpreset_import_sfont(fluid_defpreset_t* preset, SFPreset* sfpreset, fluid_defsfont_t* sfont);
+int fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone);
+int fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone);
+fluid_preset_zone_t* fluid_defpreset_get_zone(fluid_defpreset_t* preset);
+fluid_preset_zone_t* fluid_defpreset_get_global_zone(fluid_defpreset_t* preset);
+int fluid_defpreset_get_banknum(fluid_defpreset_t* preset);
+int fluid_defpreset_get_num(fluid_defpreset_t* preset);
+char* fluid_defpreset_get_name(fluid_defpreset_t* preset);
+int fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+/*
+ * fluid_preset_zone
+ */
+struct _fluid_preset_zone_t
+{
+ fluid_preset_zone_t* next;
+ char* name;
+ fluid_inst_t* inst;
+ int keylo;
+ int keyhi;
+ int vello;
+ int velhi;
+ fluid_gen_t gen[GEN_LAST];
+ fluid_mod_t * mod; /* List of modulators */
+};
+
+fluid_preset_zone_t* new_fluid_preset_zone(char* name);
+int delete_fluid_preset_zone(fluid_preset_zone_t* zone);
+fluid_preset_zone_t* fluid_preset_zone_next(fluid_preset_zone_t* preset);
+int fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone* sfzone, fluid_defsfont_t* sfont);
+int fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel);
+fluid_inst_t* fluid_preset_zone_get_inst(fluid_preset_zone_t* zone);
+
+/*
+ * fluid_inst_t
+ */
+struct _fluid_inst_t
+{
+ char name[21];
+ fluid_inst_zone_t* global_zone;
+ fluid_inst_zone_t* zone;
+};
+
+fluid_inst_t* new_fluid_inst(void);
+int delete_fluid_inst(fluid_inst_t* inst);
+int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont);
+int fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone);
+int fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone);
+fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst);
+fluid_inst_zone_t* fluid_inst_get_global_zone(fluid_inst_t* inst);
+
+/*
+ * fluid_inst_zone_t
+ */
+struct _fluid_inst_zone_t
+{
+ fluid_inst_zone_t* next;
+ char* name;
+ fluid_sample_t* sample;
+ int keylo;
+ int keyhi;
+ int vello;
+ int velhi;
+ fluid_gen_t gen[GEN_LAST];
+ fluid_mod_t * mod; /* List of modulators */
+};
+
+fluid_inst_zone_t* new_fluid_inst_zone(char* name);
+int delete_fluid_inst_zone(fluid_inst_zone_t* zone);
+fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone);
+int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont);
+int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel);
+fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone);
+
+
+
+fluid_sample_t* new_fluid_sample(void);
+int delete_fluid_sample(fluid_sample_t* sample);
+int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont);
+int fluid_sample_in_rom(fluid_sample_t* sample);
+
+
+#endif /* _FLUID_SFONT_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_float.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_float.c
new file mode 100644
index 0000000..38d9a60
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_float.c
@@ -0,0 +1,685 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_phase.h"
+
+/* Purpose:
+ *
+ * Interpolates audio data (obtains values between the samples of the original
+ * waveform data).
+ *
+ * Variables loaded from the voice structure (assigned in fluid_voice_write()):
+ * - dsp_data: Pointer to the original waveform data
+ * - dsp_phase: The position in the original waveform data.
+ * This has an integer and a fractional part (between samples).
+ * - dsp_phase_incr: For each output sample, the position in the original
+ * waveform advances by dsp_phase_incr. This also has an integer
+ * part and a fractional part.
+ * If a sample is played at root pitch (no pitch change),
+ * dsp_phase_incr is integer=1 and fractional=0.
+ * - dsp_amp: The current amplitude envelope value.
+ * - dsp_amp_incr: The changing rate of the amplitude envelope.
+ *
+ * A couple of variables are used internally, their results are discarded:
+ * - dsp_i: Index through the output buffer
+ * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length)
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_synth.h"
+#include "fluid_voice.h"
+
+
+/* Interpolation (find a value between two samples of the original waveform) */
+
+/* Linear interpolation table (2 coefficients centered on 1st) */
+static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2];
+
+/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
+static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4];
+
+/* 7th order interpolation (7 coefficients centered on 3rd) */
+static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7];
+
+
+#define SINC_INTERP_ORDER 7 /* 7th order constant */
+
+
+/* Initializes interpolation tables */
+void fluid_dsp_float_config (void)
+{
+ int i, i2;
+ double x, v;
+ double i_shifted;
+
+ /* Initialize the coefficients for the interpolation. The math comes
+ * from a mail, posted by Olli Niemitalo to the music-dsp mailing
+ * list (I found it in the music-dsp archives
+ * http://www.smartelectronix.com/musicdsp/). */
+
+ for (i = 0; i < FLUID_INTERP_MAX; i++)
+ {
+ x = (double) i / (double) FLUID_INTERP_MAX;
+
+ interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x)));
+ interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5));
+ interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x)));
+ interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0));
+
+ interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x);
+ interp_coeff_linear[i][1] = (fluid_real_t)x;
+ }
+
+ /* i: Offset in terms of whole samples */
+ for (i = 0; i < SINC_INTERP_ORDER; i++)
+ { /* i2: Offset in terms of fractional samples ('subsamples') */
+ for (i2 = 0; i2 < FLUID_INTERP_MAX; i2++)
+ {
+ /* center on middle of table */
+ i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0)
+ + (double)i2 / (double)FLUID_INTERP_MAX;
+
+ /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */
+ if (fabs (i_shifted) > 0.000001)
+ {
+ v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted);
+ /* Hamming window */
+ v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (fluid_real_t)SINC_INTERP_ORDER));
+ }
+ else v = 1.0;
+
+ sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
+ }
+ }
+
+#if 0
+ for (i = 0; i < FLUID_INTERP_MAX; i++)
+ {
+ printf ("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n",
+ i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i],
+ sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]);
+ }
+#endif
+}
+
+
+/* No interpolation. Just take the sample, which is closest to
+ * the playback pointer. Questionable quality, but very
+ * efficient. */
+int
+fluid_dsp_float_interpolate_none (fluid_voice_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int end_index;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* voice is currently looping? */
+ looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
+ || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
+ && voice->volenv_section < FLUID_VOICE_ENVRELEASE);
+
+ end_index = looping ? voice->loopend - 1 : voice->end;
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
+
+ /* interpolate sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index];
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if not looping (buffer may not be full) */
+ if (!looping) break;
+
+ /* go back to loop start */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+ voice->has_looped = 1;
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+ }
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
+
+/* Straight line interpolation.
+ * Returns number of samples processed (usually FLUID_BUFSIZE but could be
+ * smaller if end of sample occurs).
+ */
+int
+fluid_dsp_float_interpolate_linear (fluid_voice_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int end_index;
+ short int point;
+ fluid_real_t *coeffs;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* voice is currently looping? */
+ looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
+ || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
+ && voice->volenv_section < FLUID_VOICE_ENVRELEASE);
+
+ /* last index before 2nd interpolation point must be specially handled */
+ end_index = (looping ? voice->loopend - 1 : voice->end) - 1;
+
+ /* 2nd interpolation point to use at end of loop or sample */
+ if (looping) point = dsp_data[voice->loopstart]; /* loop start */
+ else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+
+ /* interpolate the sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ + coeffs[1] * dsp_data[dsp_phase_index+1]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if buffer filled */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index++; /* we're now interpolating the last point */
+
+ /* interpolate within last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ + coeffs[1] * point);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr; /* increment amplitude */
+ }
+
+ if (!looping) break; /* break out if not looping (end of sample) */
+
+ /* go back to loop start (if past */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+ voice->has_looped = 1;
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index--; /* set end back to second to last sample point */
+ }
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
+
+/* 4th order (cubic) interpolation.
+ * Returns number of samples processed (usually FLUID_BUFSIZE but could be
+ * smaller if end of sample occurs).
+ */
+int
+fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int start_index, end_index;
+ short int start_point, end_point1, end_point2;
+ fluid_real_t *coeffs;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* voice is currently looping? */
+ looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
+ || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
+ && voice->volenv_section < FLUID_VOICE_ENVRELEASE);
+
+ /* last index before 4th interpolation point must be specially handled */
+ end_index = (looping ? voice->loopend - 1 : voice->end) - 2;
+
+ if (voice->has_looped) /* set start_index and start point if looped or not */
+ {
+ start_index = voice->loopstart;
+ start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */
+ }
+ else
+ {
+ start_index = voice->start;
+ start_point = dsp_data[voice->start]; /* just duplicate the point */
+ }
+
+ /* get points off the end (loop start if looping, duplicate point if end) */
+ if (looping)
+ {
+ end_point1 = dsp_data[voice->loopstart];
+ end_point2 = dsp_data[voice->loopstart + 1];
+ }
+ else
+ {
+ end_point1 = dsp_data[voice->end];
+ end_point2 = end_point1;
+ }
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+
+ /* interpolate first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * dsp_data[dsp_phase_index+1]
+ + coeffs[3] * dsp_data[dsp_phase_index+2]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* interpolate the sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * dsp_data[dsp_phase_index+1]
+ + coeffs[3] * dsp_data[dsp_phase_index+2]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if buffer filled */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index++; /* we're now interpolating the 2nd to last point */
+
+ /* interpolate within 2nd to last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * dsp_data[dsp_phase_index+1]
+ + coeffs[3] * end_point1);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ end_index++; /* we're now interpolating the last point */
+
+ /* interpolate within the last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
+ dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ + coeffs[1] * dsp_data[dsp_phase_index]
+ + coeffs[2] * end_point1
+ + coeffs[3] * end_point2);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ if (!looping) break; /* break out if not looping (end of sample) */
+
+ /* go back to loop start */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+
+ if (!voice->has_looped)
+ {
+ voice->has_looped = 1;
+ start_index = voice->loopstart;
+ start_point = dsp_data[voice->loopend - 1];
+ }
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index -= 2; /* set end back to third to last sample point */
+ }
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
+
+/* 7th order interpolation.
+ * Returns number of samples processed (usually FLUID_BUFSIZE but could be
+ * smaller if end of sample occurs).
+ */
+int
+fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice)
+{
+ fluid_phase_t dsp_phase = voice->phase;
+ fluid_phase_t dsp_phase_incr;
+ short int *dsp_data = voice->sample->data;
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+ fluid_real_t dsp_amp = voice->amp;
+ fluid_real_t dsp_amp_incr = voice->amp_incr;
+ unsigned int dsp_i = 0;
+ unsigned int dsp_phase_index;
+ unsigned int start_index, end_index;
+ short int start_points[3];
+ short int end_points[3];
+ fluid_real_t *coeffs;
+ int looping;
+
+ /* Convert playback "speed" floating point value to phase index/fract */
+ fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
+
+ /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on
+ * the 4th sample point */
+ fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000);
+
+ /* voice is currently looping? */
+ looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
+ || (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
+ && voice->volenv_section < FLUID_VOICE_ENVRELEASE);
+
+ /* last index before 7th interpolation point must be specially handled */
+ end_index = (looping ? voice->loopend - 1 : voice->end) - 3;
+
+ if (voice->has_looped) /* set start_index and start point if looped or not */
+ {
+ start_index = voice->loopstart;
+ start_points[0] = dsp_data[voice->loopend - 1];
+ start_points[1] = dsp_data[voice->loopend - 2];
+ start_points[2] = dsp_data[voice->loopend - 3];
+ }
+ else
+ {
+ start_index = voice->start;
+ start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */
+ start_points[1] = start_points[0];
+ start_points[2] = start_points[0];
+ }
+
+ /* get the 3 points off the end (loop start if looping, duplicate point if end) */
+ if (looping)
+ {
+ end_points[0] = dsp_data[voice->loopstart];
+ end_points[1] = dsp_data[voice->loopstart + 1];
+ end_points[2] = dsp_data[voice->loopstart + 2];
+ }
+ else
+ {
+ end_points[0] = dsp_data[voice->end];
+ end_points[1] = end_points[0];
+ end_points[2] = end_points[0];
+ }
+
+ while (1)
+ {
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+
+ /* interpolate first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)start_points[2]
+ + coeffs[1] * (fluid_real_t)start_points[1]
+ + coeffs[2] * (fluid_real_t)start_points[0]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ start_index++;
+
+ /* interpolate 2nd to first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)start_points[1]
+ + coeffs[1] * (fluid_real_t)start_points[0]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ start_index++;
+
+ /* interpolate 3rd to first sample point (start or loop start) if needed */
+ for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)start_points[0]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ start_index -= 2; /* set back to original start index */
+
+
+ /* interpolate the sequence of sample points */
+ for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ /* break out if buffer filled */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index++; /* we're now interpolating the 3rd to last point */
+
+ /* interpolate within 3rd to last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ + coeffs[6] * (fluid_real_t)end_points[0]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ end_index++; /* we're now interpolating the 2nd to last point */
+
+ /* interpolate within 2nd to last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ + coeffs[5] * (fluid_real_t)end_points[0]
+ + coeffs[6] * (fluid_real_t)end_points[1]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ end_index++; /* we're now interpolating the last point */
+
+ /* interpolate within last point */
+ for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
+ {
+ coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
+
+ dsp_buf[dsp_i] = dsp_amp
+ * (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ + coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ + coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ + coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ + coeffs[4] * (fluid_real_t)end_points[0]
+ + coeffs[5] * (fluid_real_t)end_points[1]
+ + coeffs[6] * (fluid_real_t)end_points[2]);
+
+ /* increment phase and amplitude */
+ fluid_phase_incr (dsp_phase, dsp_phase_incr);
+ dsp_phase_index = fluid_phase_index (dsp_phase);
+ dsp_amp += dsp_amp_incr;
+ }
+
+ if (!looping) break; /* break out if not looping (end of sample) */
+
+ /* go back to loop start */
+ if (dsp_phase_index > end_index)
+ {
+ fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
+
+ if (!voice->has_looped)
+ {
+ voice->has_looped = 1;
+ start_index = voice->loopstart;
+ start_points[0] = dsp_data[voice->loopend - 1];
+ start_points[1] = dsp_data[voice->loopend - 2];
+ start_points[2] = dsp_data[voice->loopend - 3];
+ }
+ }
+
+ /* break out if filled buffer */
+ if (dsp_i >= FLUID_BUFSIZE) break;
+
+ end_index -= 3; /* set end back to 4th to last sample point */
+ }
+
+ /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on
+ * the 4th sample point (correct back to real value) */
+ fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000);
+
+ voice->phase = dsp_phase;
+ voice->amp = dsp_amp;
+
+ return (dsp_i);
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_simple.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_simple.c
new file mode 100644
index 0000000..663f5a6
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_dsp_simple.c
@@ -0,0 +1,120 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+/* Purpose:
+ * Low-level voice processing:
+ *
+ * - interpolates (obtains values between the samples of the original waveform data)
+ * - filters (applies a lowpass filter with variable cutoff frequency and quality factor)
+ * - mixes the processed sample to left and right output using the pan setting
+ * - sends the processed sample to chorus and reverb
+ *
+ *
+ * This file does -not- generate an object file.
+ * Instead, it is #included in several places in fluid_voice.c.
+ * The motivation for this is
+ * - Calling it as a subroutine may be time consuming, especially with optimization off
+ * - The previous implementation as a macro was clumsy to handle
+ *
+ *
+ * Fluid_voice.c sets a couple of variables before #including this:
+ * - dsp_data: Pointer to the original waveform data
+ * - dsp_left_buf: The generated signal goes here, left channel
+ * - dsp_right_buf: right channel
+ * - dsp_reverb_buf: Send to reverb unit
+ * - dsp_chorus_buf: Send to chorus unit
+ * - dsp_start: Start processing at this output buffer index
+ * - dsp_end: End processing just before this output buffer index
+ * - dsp_a1: Coefficient for the filter
+ * - dsp_a2: same
+ * - dsp_b0: same
+ * - dsp_b1: same
+ * - dsp_b2: same
+ * - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use
+ * the filter at all. If it is left at its default setting
+ * of roughly 20 kHz, there is no need to apply filterling.)
+ * - dsp_interp_method: Which interpolation method to use.
+ * - voice holds the voice structure
+ *
+ * Some variables are set and modified:
+ * - dsp_phase: The position in the original waveform data.
+ * This has an integer and a fractional part (between samples).
+ * - dsp_phase_incr: For each output sample, the position in the original
+ * waveform advances by dsp_phase_incr. This also has an integer
+ * part and a fractional part.
+ * If a sample is played at root pitch (no pitch change),
+ * dsp_phase_incr is integer=1 and fractional=0.
+ * - dsp_amp: The current amplitude envelope value.
+ * - dsp_amp_incr: The changing rate of the amplitude envelope.
+ *
+ * A couple of variables are used internally, their results are discarded:
+ * - dsp_i: Index through the output buffer
+ * - dsp_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
+ *
+ */
+
+
+/* Nonoptimized DSP loop */
+#warning "This code is meant for experiments only.";
+
+/* wave table interpolation */
+for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
+
+ dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)];
+ dsp_phase_index = fluid_phase_index(dsp_phase);
+ dsp_sample = (dsp_amp *
+ (dsp_coeff->a0 * dsp_data[dsp_phase_index]
+ + dsp_coeff->a1 * dsp_data[dsp_phase_index+1]
+ + dsp_coeff->a2 * dsp_data[dsp_phase_index+2]
+ + dsp_coeff->a3 * dsp_data[dsp_phase_index+3]));
+
+ /* increment phase and amplitude */
+ fluid_phase_incr(dsp_phase, dsp_phase_incr);
+ dsp_amp += dsp_amp_incr;
+
+ /* filter */
+ /* The filter is implemented in Direct-II form. */
+ dsp_centernode = dsp_sample - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
+ dsp_sample = dsp_b0 * dsp_centernode + dsp_b1 * dsp_hist1 + dsp_b2 * dsp_hist2;
+ dsp_hist2 = dsp_hist1;
+ dsp_hist1 = dsp_centernode;
+
+ /* pan */
+ dsp_left_buf[dsp_i] += voice->amp_left * dsp_sample;
+ dsp_right_buf[dsp_i] += voice->amp_right * dsp_sample;
+
+ /* reverb */
+ if (dsp_reverb_buf){
+ dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_sample;
+ }
+
+ /* chorus */
+ if (dsp_chorus_buf){
+ dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_sample;
+ }
+}
+
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.c
new file mode 100644
index 0000000..4f9bb02
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.c
@@ -0,0 +1,149 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include "fluid_gen.h"
+#include "fluid_chan.h"
+
+
+/* See SFSpec21 $8.1.3 */
+fluid_gen_info_t fluid_gen_info[] = {
+ /* number/name init scale min max def */
+ { GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f },
+ { GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f },
+ { GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },
+ { GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },
+ { GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f },
+ { GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
+ { GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
+ { GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f },
+ { GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f },
+ { GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f },
+ { GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f },
+ { GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f },
+ { GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f },
+ { GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f },
+ { GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f },
+ { GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f },
+ { GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f },
+ { GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
+ { GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f },
+ { GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
+ { GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f },
+ { GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
+ { GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f },
+ { GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f },
+ { GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f },
+ { GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f },
+ { GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f },
+ { GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f },
+ { GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f },
+ { GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f },
+ { GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f },
+ { GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f },
+ { GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f },
+ { GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f },
+ { GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f },
+ { GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f },
+ { GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f },
+ { GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f },
+ { GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f },
+ { GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f },
+ { GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f },
+ { GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f },
+ { GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f },
+ { GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f },
+ { GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f },
+ { GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f },
+ { GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f },
+ { GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f },
+ { GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f },
+ { GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f }
+};
+
+
+/**
+ * Set an array of generators to their default values.
+ * @param gen Array of generators (should be #GEN_LAST in size).
+ * @return Always returns 0
+ */
+int
+fluid_gen_set_default_values(fluid_gen_t* gen)
+{
+ int i;
+
+ for (i = 0; i < GEN_LAST; i++) {
+ gen[i].flags = GEN_UNUSED;
+ gen[i].mod = 0.0;
+ gen[i].nrpn = 0.0;
+ gen[i].val = fluid_gen_info[i].def;
+ }
+
+ return FLUID_OK;
+}
+
+
+/* fluid_gen_init
+ *
+ * Set an array of generators to their initial value
+ */
+int
+fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel)
+{
+ int i;
+
+ fluid_gen_set_default_values(gen);
+
+ for (i = 0; i < GEN_LAST; i++) {
+ gen[i].nrpn = fluid_channel_get_gen(channel, i);
+
+ /* This is an extension to the SoundFont standard. More
+ * documentation is available at the fluid_synth_set_gen2()
+ * function. */
+ if (fluid_channel_get_gen_abs(channel, i)) {
+ gen[i].flags = GEN_ABS_NRPN;
+ }
+ }
+
+ return FLUID_OK;
+}
+
+fluid_real_t fluid_gen_scale(int gen, float value)
+{
+ return (fluid_gen_info[gen].min
+ + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min));
+}
+
+fluid_real_t fluid_gen_scale_nrpn(int gen, int data)
+{
+ fluid_real_t value = (float) data - 8192.0f;
+ fluid_clip(value, -8192, 8192);
+ return value * (float) fluid_gen_info[gen].nrpn_scale;
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.h
new file mode 100644
index 0000000..c35193a
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_gen.h
@@ -0,0 +1,44 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_GEN_H
+#define _FLUID_GEN_H
+
+#include "fluidsynth_priv.h"
+
+typedef struct _fluid_gen_info_t {
+ char num; /* Generator number */
+ char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */
+ char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */
+ float min; /* The minimum value */
+ float max; /* The maximum value */
+ float def; /* The default value (cfr. fluid_gen_set_default_values()) */
+} fluid_gen_info_t;
+
+#define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); }
+#define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); }
+
+fluid_real_t fluid_gen_scale(int gen, float value);
+fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn);
+int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel);
+
+
+#endif /* _FLUID_GEN_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.c
new file mode 100644
index 0000000..9094908
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.c
@@ -0,0 +1,388 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_hash.h"
+
+
+#define HASH_TABLE_MIN_SIZE 7
+#define HASH_TABLE_MAX_SIZE 13845163
+
+
+typedef struct _fluid_hashnode_t fluid_hashnode_t;
+
+struct _fluid_hashnode_t {
+ char* key;
+ void* value;
+ int type;
+ fluid_hashnode_t *next;
+};
+
+static fluid_hashnode_t* new_fluid_hashnode(char* key, void* value, int type);
+static void delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del);
+static void delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del);
+
+struct _fluid_hashtable_t {
+ unsigned int size;
+ unsigned int nnodes;
+ fluid_hashnode_t **nodes;
+ fluid_hash_delete_t del;
+};
+
+#define FLUID_HASHTABLE_RESIZE(hash_table) \
+ if ((3 * hash_table->size <= hash_table->nnodes) \
+ && (hash_table->size < HASH_TABLE_MAX_SIZE)) { \
+ fluid_hashtable_resize(hash_table); \
+ }
+
+static void fluid_hashtable_resize(fluid_hashtable_t *hash_table);
+static fluid_hashnode_t** fluid_hashtable_lookup_node(fluid_hashtable_t *hash_table, char* key);
+
+/**
+ * new_fluid_hashtable:
+ *
+ * Creates a new #fluid_hashtable_t.
+ *
+ * Return value: a new #fluid_hashtable_t.
+ **/
+fluid_hashtable_t*
+new_fluid_hashtable(fluid_hash_delete_t del)
+{
+ fluid_hashtable_t *hash_table;
+ unsigned int i;
+
+ hash_table = FLUID_NEW(fluid_hashtable_t);
+ hash_table->size = HASH_TABLE_MIN_SIZE;
+ hash_table->nnodes = 0;
+ hash_table->nodes = FLUID_ARRAY(fluid_hashnode_t*, hash_table->size);
+ hash_table->del = del;
+
+ for (i = 0; i < hash_table->size; i++) {
+ hash_table->nodes[i] = NULL;
+ }
+
+ return hash_table;
+}
+
+/**
+ * delete_fluid_hashtable:
+ * @hash_table: a #fluid_hashtable_t.
+ *
+ * Destroys the #fluid_hashtable_t. If keys and/or values are dynamically
+ * allocated, you should either free them first or create the #fluid_hashtable_t
+ * using fluid_hashtable_new_full(). In the latter case the destroy functions
+ * you supplied will be called on all keys and values before destroying
+ * the #fluid_hashtable_t.
+ **/
+void
+delete_fluid_hashtable(fluid_hashtable_t *hash_table)
+{
+ unsigned int i;
+
+ if (hash_table == NULL) {
+ return;
+ }
+
+ for (i = 0; i < hash_table->size; i++) {
+ delete_fluid_hashnodes(hash_table->nodes[i], hash_table->del);
+ }
+
+ FLUID_FREE(hash_table->nodes);
+ FLUID_FREE(hash_table);
+}
+
+
+static /*inline*/ fluid_hashnode_t**
+fluid_hashtable_lookup_node (fluid_hashtable_t* hash_table, char* key)
+{
+ fluid_hashnode_t **node;
+
+ node = &hash_table->nodes[fluid_str_hash(key) % hash_table->size];
+
+ while (*node && (FLUID_STRCMP((*node)->key, key) != 0)) {
+ node = &(*node)->next;
+ }
+
+ return node;
+}
+
+/**
+ * fluid_hashtable_lookup:
+ * @hash_table: a #fluid_hashtable_t.
+ * @key: the key to look up.
+ *
+ * Looks up a key in a #fluid_hashtable_t.
+ *
+ * Return value: the associated value, or %NULL if the key is not found.
+ **/
+int
+fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type)
+{
+ fluid_hashnode_t *node;
+
+ node = *fluid_hashtable_lookup_node(hash_table, key);
+
+ if (node) {
+ if (value) {
+ *value = node->value;
+ }
+ if (type) {
+ *type = node->type;
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * fluid_hashtable_insert:
+ * @hash_table: a #fluid_hashtable_t.
+ * @key: a key to insert.
+ * @value: the value to associate with the key.
+ *
+ * Inserts a new key and value into a #fluid_hashtable_t.
+ *
+ * If the key already exists in the #fluid_hashtable_t its current value is replaced
+ * with the new value. If you supplied a @value_destroy_func when creating the
+ * #fluid_hashtable_t, the old value is freed using that function. If you supplied
+ * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed
+ * using that function.
+ **/
+void
+fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type)
+{
+ fluid_hashnode_t **node;
+
+ node = fluid_hashtable_lookup_node(hash_table, key);
+
+ if (*node) {
+ (*node)->value = value;
+ (*node)->type = type;
+ } else {
+ *node = new_fluid_hashnode(key, value, type);
+ hash_table->nnodes++;
+ FLUID_HASHTABLE_RESIZE(hash_table);
+ }
+}
+
+
+/**
+ * fluid_hashtable_replace:
+ * @hash_table: a #GHashTable.
+ * @key: a key to insert.
+ * @value: the value to associate with the key.
+ *
+ * Inserts a new key and value into a #GHashTable similar to
+ * fluid_hashtable_insert(). The difference is that if the key already exists
+ * in the #GHashTable, it gets replaced by the new key. If you supplied a
+ * @value_destroy_func when creating the #GHashTable, the old value is freed
+ * using that function. If you supplied a @key_destroy_func when creating the
+ * #GHashTable, the old key is freed using that function.
+ **/
+void
+fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type)
+{
+ fluid_hashnode_t **node;
+
+ node = fluid_hashtable_lookup_node(hash_table, key);
+
+ if (*node) {
+ if (hash_table->del) {
+ hash_table->del((*node)->value, (*node)->type);
+ }
+ (*node)->value = value;
+
+ } else {
+ *node = new_fluid_hashnode(key, value, type);
+ hash_table->nnodes++;
+ FLUID_HASHTABLE_RESIZE(hash_table);
+ }
+}
+
+/**
+ * fluid_hashtable_remove:
+ * @hash_table: a #fluid_hashtable_t.
+ * @key: the key to remove.
+ *
+ * Removes a key and its associated value from a #fluid_hashtable_t.
+ *
+ * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the
+ * key and value are freed using the supplied destroy functions, otherwise
+ * you have to make sure that any dynamically allocated values are freed
+ * yourself.
+ *
+ * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.
+ **/
+int
+fluid_hashtable_remove (fluid_hashtable_t *hash_table, char* key)
+{
+ fluid_hashnode_t **node, *dest;
+
+ node = fluid_hashtable_lookup_node(hash_table, key);
+ if (*node) {
+ dest = *node;
+ (*node) = dest->next;
+ delete_fluid_hashnode(dest, hash_table->del);
+ hash_table->nnodes--;
+
+ FLUID_HASHTABLE_RESIZE (hash_table);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * fluid_hashtable_foreach:
+ * @hash_table: a #fluid_hashtable_t.
+ * @func: the function to call for each key/value pair.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for each of the key/value pairs in the
+ * #fluid_hashtable_t. The function is passed the key and value of each
+ * pair, and the given @user_data parameter. The hash table may not
+ * be modified while iterating over it (you can't add/remove
+ * items). To remove all items matching a predicate, use
+ * fluid_hashtable_remove().
+ **/
+void
+fluid_hashtable_foreach(fluid_hashtable_t *hash_table, fluid_hash_iter_t func, void* data)
+{
+ fluid_hashnode_t *node = NULL;
+ unsigned int i;
+
+ for (i = 0; i < hash_table->size; i++) {
+ for (node = hash_table->nodes[i]; node != NULL; node = node->next) {
+ (*func)(node->key, node->value, node->type, data);
+ }
+ }
+}
+
+/**
+ * fluid_hashtable_size:
+ * @hash_table: a #fluid_hashtable_t.
+ *
+ * Returns the number of elements contained in the #fluid_hashtable_t.
+ *
+ * Return value: the number of key/value pairs in the #fluid_hashtable_t.
+ **/
+unsigned int
+fluid_hashtable_size(fluid_hashtable_t *hash_table)
+{
+ return hash_table->nnodes;
+}
+
+static void
+fluid_hashtable_resize(fluid_hashtable_t *hash_table)
+{
+ fluid_hashnode_t **new_nodes;
+ fluid_hashnode_t *node;
+ fluid_hashnode_t *next;
+ unsigned int hash_val;
+ int new_size;
+ unsigned int i;
+
+ new_size = 3 * hash_table->size + 1;
+ new_size = (new_size > HASH_TABLE_MAX_SIZE)? HASH_TABLE_MAX_SIZE : new_size;
+
+/* printf("%s: %d: resizing, new size = %d\n", __FILE__, __LINE__, new_size); */
+
+ new_nodes = FLUID_ARRAY(fluid_hashnode_t*, new_size);
+ FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t*));
+
+ for (i = 0; i < hash_table->size; i++) {
+ for (node = hash_table->nodes[i]; node; node = next) {
+ next = node->next;
+ hash_val = fluid_str_hash(node->key) % new_size;
+ node->next = new_nodes[hash_val];
+ new_nodes[hash_val] = node;
+ }
+ }
+
+ FLUID_FREE(hash_table->nodes);
+ hash_table->nodes = new_nodes;
+ hash_table->size = new_size;
+}
+
+static fluid_hashnode_t*
+new_fluid_hashnode(char* key, void* value, int type)
+{
+ fluid_hashnode_t *hash_node;
+
+ hash_node = FLUID_NEW(fluid_hashnode_t);
+
+ hash_node->key = FLUID_STRDUP(key);
+ hash_node->value = value;
+ hash_node->type = type;
+ hash_node->next = NULL;
+
+ return hash_node;
+}
+
+static void
+delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del)
+{
+ if (del) {
+ (*del)(hash_node->value, hash_node->type);
+ }
+ if (hash_node->key) {
+ FLUID_FREE(hash_node->key);
+ }
+ FLUID_FREE(hash_node);
+}
+
+static void
+delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del)
+{
+ while (hash_node) {
+ fluid_hashnode_t *next = hash_node->next;
+ delete_fluid_hashnode(hash_node, del);
+ hash_node = next;
+ }
+}
+
+
+/* 31 bit hash function */
+unsigned int
+fluid_str_hash(char* key)
+{
+ char *p = key;
+ unsigned int h = *p;
+
+ if (h) {
+ for (p += 1; *p != '\0'; p++) {
+ h = (h << 5) - h + *p;
+ }
+ }
+
+ return h;
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.h
new file mode 100644
index 0000000..af0b5db
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_hash.h
@@ -0,0 +1,64 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * Demolished by Peter Hanappe [December 2002]
+ *
+ * - only string as key
+ * - stores additional type info
+ * - removed use of GLib types (gpointer, gint, ...)
+ * - reduced the number of API functions
+ * - changed names to fluid_hashtable_...
+ */
+
+#ifndef _FLUID_HASH_H
+#define _FLUID_HASH_H
+
+
+typedef int (*fluid_hash_iter_t)(char* key, void* value, int type, void* data);
+typedef void (*fluid_hash_delete_t)(void* value, int type);
+
+fluid_hashtable_t* new_fluid_hashtable(fluid_hash_delete_t delete_func);
+void delete_fluid_hashtable(fluid_hashtable_t *hash_table);
+
+void fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type);
+
+void fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type);
+
+/* Returns non-zero if found, 0 if not found */
+int fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type);
+
+/* Returns non-zero if removed, 0 if not removed */
+int fluid_hashtable_remove(fluid_hashtable_t *hash_table, char* key);
+
+void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hash_iter_t fun, void* data);
+
+unsigned int fluid_hashtable_size(fluid_hashtable_t *hash_table);
+
+unsigned int fluid_str_hash(char* v);
+
+#endif /* _FLUID_HASH_H */
+
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_init.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_init.c
new file mode 100644
index 0000000..d145c7d
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_init.c
@@ -0,0 +1,5 @@
+#ifdef __ANDROID__
+// To make upx work for Android
+void _init(void) __attribute__((constructor));
+void _init(void) {}
+#endif
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.c
new file mode 100644
index 0000000..4d1e64f
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.c
@@ -0,0 +1,257 @@
+/* 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 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-1999. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+
+
+#include "fluid_list.h"
+
+
+fluid_list_t*
+new_fluid_list(void)
+{
+ fluid_list_t* list;
+ list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t));
+ list->data = NULL;
+ list->next = NULL;
+ return list;
+}
+
+void
+delete_fluid_list(fluid_list_t *list)
+{
+ fluid_list_t *next;
+ while (list) {
+ next = list->next;
+ FLUID_FREE(list);
+ list = next;
+ }
+}
+
+void
+delete1_fluid_list(fluid_list_t *list)
+{
+ if (list) {
+ FLUID_FREE(list);
+ }
+}
+
+fluid_list_t*
+fluid_list_append(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *last;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ if (list)
+ {
+ last = fluid_list_last(list);
+ /* g_assert (last != NULL); */
+ last->next = new_list;
+
+ return list;
+ }
+ else
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_prepend(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+ new_list->next = list;
+
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_nth(fluid_list_t *list, int n)
+{
+ while ((n-- > 0) && list) {
+ list = list->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove(fluid_list_t *list, void* data)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp->data == data) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ delete_fluid_list(tmp);
+
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp == link) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+static fluid_list_t*
+fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func)
+{
+ fluid_list_t list, *l;
+
+ l = &list;
+
+ while (l1 && l2) {
+ if (compare_func(l1->data,l2->data) < 0) {
+ l = l->next = l1;
+ l1 = l1->next;
+ } else {
+ l = l->next = l2;
+ l2 = l2->next;
+ }
+ }
+ l->next= l1 ? l1 : l2;
+
+ return list.next;
+}
+
+fluid_list_t*
+fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func)
+{
+ fluid_list_t *l1, *l2;
+
+ if (!list) {
+ return NULL;
+ }
+ if (!list->next) {
+ return list;
+ }
+
+ l1 = list;
+ l2 = list->next;
+
+ while ((l2 = l2->next) != NULL) {
+ if ((l2 = l2->next) == NULL)
+ break;
+ l1=l1->next;
+ }
+ l2 = l1->next;
+ l1->next = NULL;
+
+ return fluid_list_sort_merge(fluid_list_sort(list, compare_func),
+ fluid_list_sort(l2, compare_func),
+ compare_func);
+}
+
+
+fluid_list_t*
+fluid_list_last(fluid_list_t *list)
+{
+ if (list) {
+ while (list->next)
+ list = list->next;
+ }
+
+ return list;
+}
+
+int
+fluid_list_size(fluid_list_t *list)
+{
+ int n = 0;
+ while (list) {
+ n++;
+ list = list->next;
+ }
+ return n;
+}
+
+fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *cur;
+ fluid_list_t *prev = NULL;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ cur = list;
+ while ((n-- > 0) && cur) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ new_list->next = cur;
+
+ if (prev) {
+ prev->next = new_list;
+ return list;
+ } else {
+ return new_list;
+ }
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.h
new file mode 100644
index 0000000..6d5779b
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_list.h
@@ -0,0 +1,61 @@
+/* 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 02111-1307, USA.
+ */
+
+#ifndef _FLUID_LIST_H
+#define _FLUID_LIST_H
+
+#include "fluidsynth_priv.h"
+
+/*
+ *
+ * Lists
+ *
+ * A sound font loader has to pack the data from the .SF2 file into
+ * list structures of this type.
+ *
+ */
+
+typedef struct _fluid_list_t fluid_list_t;
+
+typedef int (*fluid_compare_func_t)(void* a, void* b);
+
+struct _fluid_list_t
+{
+ void* data;
+ fluid_list_t *next;
+};
+
+fluid_list_t* new_fluid_list(void);
+void delete_fluid_list(fluid_list_t *list);
+void delete1_fluid_list(fluid_list_t *list);
+fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func);
+fluid_list_t* fluid_list_append(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink);
+fluid_list_t* fluid_list_nth(fluid_list_t *list, int n);
+fluid_list_t* fluid_list_last(fluid_list_t *list);
+fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data);
+int fluid_list_size(fluid_list_t *list);
+
+#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL)
+#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL)
+
+
+#endif /* _FLUID_LIST_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_midi.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_midi.h
new file mode 100644
index 0000000..c31be78
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_midi.h
@@ -0,0 +1,247 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUID_MIDI_H
+#define _FLUID_MIDI_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_sys.h"
+#include "fluid_list.h"
+
+typedef struct _fluid_midi_parser_t fluid_midi_parser_t;
+
+fluid_midi_parser_t* new_fluid_midi_parser(void);
+int delete_fluid_midi_parser(fluid_midi_parser_t* parser);
+fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c);
+
+int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* evt);
+
+
+/***************************************************************
+ *
+ * CONSTANTS & ENUM
+ */
+
+
+#define MAX_NUMBER_OF_TRACKS 128
+
+enum fluid_midi_event_type {
+ /* channel messages */
+ NOTE_OFF = 0x80,
+ NOTE_ON = 0x90,
+ KEY_PRESSURE = 0xa0,
+ CONTROL_CHANGE = 0xb0,
+ PROGRAM_CHANGE = 0xc0,
+ CHANNEL_PRESSURE = 0xd0,
+ PITCH_BEND = 0xe0,
+ /* system exclusive */
+ MIDI_SYSEX = 0xf0,
+ /* system common - never in midi files */
+ MIDI_TIME_CODE = 0xf1,
+ MIDI_SONG_POSITION = 0xf2,
+ MIDI_SONG_SELECT = 0xf3,
+ MIDI_TUNE_REQUEST = 0xf6,
+ MIDI_EOX = 0xf7,
+ /* system real-time - never in midi files */
+ MIDI_SYNC = 0xf8,
+ MIDI_TICK = 0xf9,
+ MIDI_START = 0xfa,
+ MIDI_CONTINUE = 0xfb,
+ MIDI_STOP = 0xfc,
+ MIDI_ACTIVE_SENSING = 0xfe,
+ MIDI_SYSTEM_RESET = 0xff,
+ /* meta event - for midi files only */
+ MIDI_META_EVENT = 0xff
+};
+
+enum fluid_midi_control_change {
+ BANK_SELECT_MSB = 0x00,
+ MODULATION_MSB = 0x01,
+ BREATH_MSB = 0x02,
+ FOOT_MSB = 0x04,
+ PORTAMENTO_TIME_MSB = 0x05,
+ DATA_ENTRY_MSB = 0x06,
+ VOLUME_MSB = 0x07,
+ BALANCE_MSB = 0x08,
+ PAN_MSB = 0x0A,
+ EXPRESSION_MSB = 0x0B,
+ EFFECTS1_MSB = 0x0C,
+ EFFECTS2_MSB = 0x0D,
+ GPC1_MSB = 0x10, /* general purpose controller */
+ GPC2_MSB = 0x11,
+ GPC3_MSB = 0x12,
+ GPC4_MSB = 0x13,
+ BANK_SELECT_LSB = 0x20,
+ MODULATION_WHEEL_LSB = 0x21,
+ BREATH_LSB = 0x22,
+ FOOT_LSB = 0x24,
+ PORTAMENTO_TIME_LSB = 0x25,
+ DATA_ENTRY_LSB = 0x26,
+ VOLUME_LSB = 0x27,
+ BALANCE_LSB = 0x28,
+ PAN_LSB = 0x2A,
+ EXPRESSION_LSB = 0x2B,
+ EFFECTS1_LSB = 0x2C,
+ EFFECTS2_LSB = 0x2D,
+ GPC1_LSB = 0x30,
+ GPC2_LSB = 0x31,
+ GPC3_LSB = 0x32,
+ GPC4_LSB = 0x33,
+ SUSTAIN_SWITCH = 0x40,
+ PORTAMENTO_SWITCH = 0x41,
+ SOSTENUTO_SWITCH = 0x42,
+ SOFT_PEDAL_SWITCH = 0x43,
+ LEGATO_SWITCH = 0x45,
+ HOLD2_SWITCH = 0x45,
+ SOUND_CTRL1 = 0x46,
+ SOUND_CTRL2 = 0x47,
+ SOUND_CTRL3 = 0x48,
+ SOUND_CTRL4 = 0x49,
+ SOUND_CTRL5 = 0x4A,
+ SOUND_CTRL6 = 0x4B,
+ SOUND_CTRL7 = 0x4C,
+ SOUND_CTRL8 = 0x4D,
+ SOUND_CTRL9 = 0x4E,
+ SOUND_CTRL10 = 0x4F,
+ GPC5 = 0x50,
+ GPC6 = 0x51,
+ GPC7 = 0x52,
+ GPC8 = 0x53,
+ PORTAMENTO_CTRL = 0x54,
+ EFFECTS_DEPTH1 = 0x5B,
+ EFFECTS_DEPTH2 = 0x5C,
+ EFFECTS_DEPTH3 = 0x5D,
+ EFFECTS_DEPTH4 = 0x5E,
+ EFFECTS_DEPTH5 = 0x5F,
+ DATA_ENTRY_INCR = 0x60,
+ DATA_ENTRY_DECR = 0x61,
+ NRPN_LSB = 0x62,
+ NRPN_MSB = 0x63,
+ RPN_LSB = 0x64,
+ RPN_MSB = 0x65,
+ ALL_SOUND_OFF = 0x78,
+ ALL_CTRL_OFF = 0x79,
+ LOCAL_CONTROL = 0x7A,
+ ALL_NOTES_OFF = 0x7B,
+ OMNI_OFF = 0x7C,
+ OMNI_ON = 0x7D,
+ POLY_OFF = 0x7E,
+ POLY_ON = 0x7F
+};
+
+/* General MIDI RPN event numbers (LSB, MSB = 0) */
+enum midi_rpn_event {
+ RPN_PITCH_BEND_RANGE = 0x00,
+ RPN_CHANNEL_FINE_TUNE = 0x01,
+ RPN_CHANNEL_COARSE_TUNE = 0x02,
+ RPN_TUNING_PROGRAM_CHANGE = 0x03,
+ RPN_TUNING_BANK_SELECT = 0x04,
+ RPN_MODULATION_DEPTH_RANGE = 0x05
+};
+
+enum midi_meta_event {
+ MIDI_COPYRIGHT = 0x02,
+ MIDI_TRACK_NAME = 0x03,
+ MIDI_INST_NAME = 0x04,
+ MIDI_LYRIC = 0x05,
+ MIDI_MARKER = 0x06,
+ MIDI_CUE_POINT = 0x07,
+ MIDI_EOT = 0x2f,
+ MIDI_SET_TEMPO = 0x51,
+ MIDI_SMPTE_OFFSET = 0x54,
+ MIDI_TIME_SIGNATURE = 0x58,
+ MIDI_KEY_SIGNATURE = 0x59,
+ MIDI_SEQUENCER_EVENT = 0x7f
+};
+
+/* MIDI SYSEX useful manufacturer values */
+enum midi_sysex_manuf {
+ MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */
+ MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */
+ MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */
+};
+
+#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */
+
+/* SYSEX sub-ID #1 which follows device ID */
+#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */
+#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */
+
+/**
+ * SYSEX tuning message IDs.
+ */
+enum midi_sysex_tuning_msg_id {
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */
+};
+
+/* General MIDI sub-ID #2 */
+#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */
+#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */
+
+
+enum fluid_player_status
+{
+ FLUID_PLAYER_READY,
+ FLUID_PLAYER_PLAYING,
+ FLUID_PLAYER_DONE
+};
+
+enum fluid_driver_status
+{
+ FLUID_MIDI_READY,
+ FLUID_MIDI_LISTENING,
+ FLUID_MIDI_DONE
+};
+
+/***************************************************************
+ *
+ * TYPE DEFINITIONS & FUNCTION DECLARATIONS
+ */
+
+/* From ctype.h */
+#define fluid_isascii(c) (((c) & ~0x7f) == 0)
+
+
+
+/*
+ * fluid_midi_event_t
+ */
+struct _fluid_midi_event_t {
+ fluid_midi_event_t* next; /* Don't use it, it will dissappear. Used in midi tracks. */
+ unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */
+ unsigned char type; /* MIDI event type */
+ unsigned char channel; /* MIDI channel */
+ unsigned int param1; /* First parameter */
+ unsigned int param2; /* Second parameter */
+};
+
+
+
+
+#endif /* _FLUID_MIDI_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.c
new file mode 100644
index 0000000..21862f3
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.c
@@ -0,0 +1,434 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluid_mod.h"
+#include "fluid_chan.h"
+#include "fluid_voice.h"
+
+/*
+ * fluid_mod_clone
+ */
+void
+fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src)
+{
+ mod->dest = src->dest;
+ mod->src1 = src->src1;
+ mod->flags1 = src->flags1;
+ mod->src2 = src->src2;
+ mod->flags2 = src->flags2;
+ mod->amount = src->amount;
+}
+
+/*
+ * fluid_mod_set_source1
+ */
+void
+fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags)
+{
+ mod->src1 = src;
+ mod->flags1 = flags;
+}
+
+/*
+ * fluid_mod_set_source2
+ */
+void
+fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags)
+{
+ mod->src2 = src;
+ mod->flags2 = flags;
+}
+
+/*
+ * fluid_mod_set_dest
+ */
+void
+fluid_mod_set_dest(fluid_mod_t* mod, int dest)
+{
+ mod->dest = dest;
+}
+
+/*
+ * fluid_mod_set_amount
+ */
+void
+fluid_mod_set_amount(fluid_mod_t* mod, double amount)
+{
+ mod->amount = (double) amount;
+}
+
+int fluid_mod_get_source1(fluid_mod_t* mod)
+{
+ return mod->src1;
+}
+
+int fluid_mod_get_flags1(fluid_mod_t* mod)
+{
+ return mod->flags1;
+}
+
+int fluid_mod_get_source2(fluid_mod_t* mod)
+{
+ return mod->src2;
+}
+
+int fluid_mod_get_flags2(fluid_mod_t* mod)
+{
+ return mod->flags2;
+}
+
+int fluid_mod_get_dest(fluid_mod_t* mod)
+{
+ return mod->dest;
+}
+
+double fluid_mod_get_amount(fluid_mod_t* mod)
+{
+ return (fluid_real_t) mod->amount;
+}
+
+
+/*
+ * fluid_mod_get_value
+ */
+fluid_real_t
+fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice)
+{
+ fluid_real_t v1 = 0.0, v2 = 1.0;
+ fluid_real_t range1 = 127.0, range2 = 127.0;
+
+ if (chan == NULL) {
+ return 0.0f;
+ }
+
+ /* 'special treatment' for default controller
+ *
+ * Reference: SF2.01 section 8.4.2
+ *
+ * The GM default controller 'vel-to-filter cut off' is not clearly
+ * defined: If implemented according to the specs, the filter
+ * frequency jumps between vel=63 and vel=64. To maintain
+ * compatibility with existing sound fonts, the implementation is
+ * 'hardcoded', it is impossible to implement using only one
+ * modulator otherwise.
+ *
+ * I assume here, that the 'intention' of the paragraph is one
+ * octave (1200 cents) filter frequency shift between vel=127 and
+ * vel=64. 'amount' is (-2400), at least as long as the controller
+ * is set to default.
+ *
+ * Further, the 'appearance' of the modulator (source enumerator,
+ * destination enumerator, flags etc) is different from that
+ * described in section 8.4.2, but it matches the definition used in
+ * several SF2.1 sound fonts (where it is used only to turn it off).
+ * */
+ if ((mod->src2 == FLUID_MOD_VELOCITY) &&
+ (mod->src1 == FLUID_MOD_VELOCITY) &&
+ (mod->flags1 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR
+ | FLUID_MOD_NEGATIVE | FLUID_MOD_LINEAR)) &&
+ (mod->flags2 == (FLUID_MOD_GC | FLUID_MOD_UNIPOLAR
+ | FLUID_MOD_POSITIVE | FLUID_MOD_SWITCH)) &&
+ (mod->dest == GEN_FILTERFC)) {
+// S. Christian Collins' mod, to stop forcing velocity based filtering
+/*
+ if (voice->vel < 64){
+ return (fluid_real_t) mod->amount / 2.0;
+ } else {
+ return (fluid_real_t) mod->amount * (127 - voice->vel) / 127;
+ }
+*/
+ return 0; // (fluid_real_t) mod->amount / 2.0;
+ }
+// end S. Christian Collins' mod
+
+ /* get the initial value of the first source */
+ if (mod->src1 > 0) {
+ if (mod->flags1 & FLUID_MOD_CC) {
+ v1 = fluid_channel_get_cc(chan, mod->src1);
+ } else { /* source 1 is one of the direct controllers */
+ switch (mod->src1) {
+ case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */
+ v1 = range1;
+ break;
+ case FLUID_MOD_VELOCITY:
+ v1 = voice->vel;
+ break;
+ case FLUID_MOD_KEY:
+ v1 = voice->key;
+ break;
+ case FLUID_MOD_KEYPRESSURE:
+ v1 = fluid_channel_get_key_pressure(chan, voice->key);
+ break;
+ case FLUID_MOD_CHANNELPRESSURE:
+ v1 = chan->channel_pressure;
+ break;
+ case FLUID_MOD_PITCHWHEEL:
+ v1 = chan->pitch_bend;
+ range1 = 0x4000;
+ break;
+ case FLUID_MOD_PITCHWHEELSENS:
+ v1 = chan->pitch_wheel_sensitivity;
+ break;
+ default:
+ v1 = 0.0;
+ }
+ }
+
+ /* transform the input value */
+ switch (mod->flags1 & 0x0f) {
+ case 0: /* linear, unipolar, positive */
+ v1 /= range1;
+ break;
+ case 1: /* linear, unipolar, negative */
+ v1 = 1.0f - v1 / range1;
+ break;
+ case 2: /* linear, bipolar, positive */
+ v1 = -1.0f + 2.0f * v1 / range1;
+ break;
+ case 3: /* linear, bipolar, negative */
+ v1 = 1.0f - 2.0f * v1 / range1;
+ break;
+ case 4: /* concave, unipolar, positive */
+ v1 = fluid_concave(v1);
+ break;
+ case 5: /* concave, unipolar, negative */
+ v1 = fluid_concave(127 - v1);
+ break;
+ case 6: /* concave, bipolar, positive */
+ v1 = (v1 > 64)? fluid_concave(2 * (v1 - 64)) : -fluid_concave(2 * (64 - v1));
+ break;
+ case 7: /* concave, bipolar, negative */
+ v1 = (v1 > 64)? -fluid_concave(2 * (v1 - 64)) : fluid_concave(2 * (64 - v1));
+ break;
+ case 8: /* convex, unipolar, positive */
+ v1 = fluid_convex(v1);
+ break;
+ case 9: /* convex, unipolar, negative */
+ v1 = fluid_convex(127 - v1);
+ break;
+ case 10: /* convex, bipolar, positive */
+ v1 = (v1 > 64)? fluid_convex(2 * (v1 - 64)) : -fluid_convex(2 * (64 - v1));
+ break;
+ case 11: /* convex, bipolar, negative */
+ v1 = (v1 > 64)? -fluid_convex(2 * (v1 - 64)) : fluid_convex(2 * (64 - v1));
+ break;
+ case 12: /* switch, unipolar, positive */
+ v1 = (v1 >= 64)? 1.0f : 0.0f;
+ break;
+ case 13: /* switch, unipolar, negative */
+ v1 = (v1 >= 64)? 0.0f : 1.0f;
+ break;
+ case 14: /* switch, bipolar, positive */
+ v1 = (v1 >= 64)? 1.0f : -1.0f;
+ break;
+ case 15: /* switch, bipolar, negative */
+ v1 = (v1 >= 64)? -1.0f : 1.0f;
+ break;
+ }
+ } else {
+ return 0.0;
+ }
+
+ /* no need to go further */
+ if (v1 == 0.0f) {
+ return 0.0f;
+ }
+
+ /* get the second input source */
+ if (mod->src2 > 0) {
+ if (mod->flags2 & FLUID_MOD_CC) {
+ v2 = fluid_channel_get_cc(chan, mod->src2);
+ } else {
+ switch (mod->src2) {
+ case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */
+ v2 = range2;
+ break;
+ case FLUID_MOD_VELOCITY:
+ v2 = voice->vel;
+ break;
+ case FLUID_MOD_KEY:
+ v2 = voice->key;
+ break;
+ case FLUID_MOD_KEYPRESSURE:
+ v2 = fluid_channel_get_key_pressure(chan, voice->key);
+ break;
+ case FLUID_MOD_CHANNELPRESSURE:
+ v2 = chan->channel_pressure;
+ break;
+ case FLUID_MOD_PITCHWHEEL:
+ v2 = chan->pitch_bend;
+ break;
+ case FLUID_MOD_PITCHWHEELSENS:
+ v2 = chan->pitch_wheel_sensitivity;
+ break;
+ default:
+ v1 = 0.0f;
+ }
+ }
+
+ /* transform the second input value */
+ switch (mod->flags2 & 0x0f) {
+ case 0: /* linear, unipolar, positive */
+ v2 /= range2;
+ break;
+ case 1: /* linear, unipolar, negative */
+ v2 = 1.0f - v2 / range2;
+ break;
+ case 2: /* linear, bipolar, positive */
+ v2 = -1.0f + 2.0f * v2 / range2;
+ break;
+ case 3: /* linear, bipolar, negative */
+ v2 = -1.0f + 2.0f * v2 / range2;
+ break;
+ case 4: /* concave, unipolar, positive */
+ v2 = fluid_concave(v2);
+ break;
+ case 5: /* concave, unipolar, negative */
+ v2 = fluid_concave(127 - v2);
+ break;
+ case 6: /* concave, bipolar, positive */
+ v2 = (v2 > 64)? fluid_concave(2 * (v2 - 64)) : -fluid_concave(2 * (64 - v2));
+ break;
+ case 7: /* concave, bipolar, negative */
+ v2 = (v2 > 64)? -fluid_concave(2 * (v2 - 64)) : fluid_concave(2 * (64 - v2));
+ break;
+ case 8: /* convex, unipolar, positive */
+ v2 = fluid_convex(v2);
+ break;
+ case 9: /* convex, unipolar, negative */
+ v2 = 1.0f - fluid_convex(v2);
+ break;
+ case 10: /* convex, bipolar, positive */
+ v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2));
+ break;
+ case 11: /* convex, bipolar, negative */
+ v2 = (v2 > 64)? -fluid_convex(2 * (v2 - 64)) : fluid_convex(2 * (64 - v2));
+ break;
+ case 12: /* switch, unipolar, positive */
+ v2 = (v2 >= 64)? 1.0f : 0.0f;
+ break;
+ case 13: /* switch, unipolar, negative */
+ v2 = (v2 >= 64)? 0.0f : 1.0f;
+ break;
+ case 14: /* switch, bipolar, positive */
+ v2 = (v2 >= 64)? 1.0f : -1.0f;
+ break;
+ case 15: /* switch, bipolar, negative */
+ v2 = (v2 >= 64)? -1.0f : 1.0f;
+ break;
+ }
+ } else {
+ v2 = 1.0f;
+ }
+
+ /* it's as simple as that: */
+ return (fluid_real_t) mod->amount * v1 * v2;
+}
+
+/*
+ * fluid_mod_new
+ */
+fluid_mod_t*
+fluid_mod_new()
+{
+ fluid_mod_t* mod = FLUID_NEW(fluid_mod_t);
+ if (mod == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ return mod;
+};
+
+/*
+ * fluid_mod_delete
+ */
+void
+fluid_mod_delete(fluid_mod_t * mod)
+{
+ FLUID_FREE(mod);
+};
+
+/*
+ * fluid_mod_test_identity
+ */
+/* Purpose:
+ * Checks, if two modulators are identical.
+ * 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){
+ if (mod1->dest != mod2->dest){return 0;};
+ if (mod1->src1 != mod2->src1){return 0;};
+ if (mod1->src2 != mod2->src2){return 0;};
+ if (mod1->flags1 != mod2->flags1){return 0;}
+ if (mod1->flags2 != mod2->flags2){return 0;}
+ return 1;
+};
+
+/* debug function: Prints the contents of a modulator */
+void fluid_dump_modulator(fluid_mod_t * mod){
+ int src1=mod->src1;
+ int dest=mod->dest;
+ int src2=mod->src2;
+ int flags1=mod->flags1;
+ int flags2=mod->flags2;
+ fluid_real_t amount=(fluid_real_t)mod->amount;
+
+ printf("Src: ");
+ if (flags1 & FLUID_MOD_CC){
+ printf("MIDI CC=%i",src1);
+ } else {
+ switch(src1){
+ case FLUID_MOD_NONE:
+ printf("None"); break;
+ case FLUID_MOD_VELOCITY:
+ printf("note-on velocity"); break;
+ case FLUID_MOD_KEY:
+ printf("Key nr"); break;
+ case FLUID_MOD_KEYPRESSURE:
+ printf("Poly pressure"); break;
+ case FLUID_MOD_CHANNELPRESSURE:
+ printf("Chan pressure"); break;
+ case FLUID_MOD_PITCHWHEEL:
+ printf("Pitch Wheel"); break;
+ case FLUID_MOD_PITCHWHEELSENS:
+ printf("Pitch Wheel sens"); break;
+ default:
+ printf("(unknown: %i)", src1);
+ }; /* switch src1 */
+ }; /* if not CC */
+ if (flags1 & FLUID_MOD_NEGATIVE){printf("- ");} else {printf("+ ");};
+ if (flags1 & FLUID_MOD_BIPOLAR){printf("bip ");} else {printf("unip ");};
+ printf("-> ");
+ switch(dest){
+ case GEN_FILTERQ: printf("Q"); break;
+ case GEN_FILTERFC: printf("fc"); break;
+ case GEN_VIBLFOTOPITCH: printf("VibLFO-to-pitch"); break;
+ case GEN_MODENVTOPITCH: printf("ModEnv-to-pitch"); break;
+ case GEN_MODLFOTOPITCH: printf("ModLFO-to-pitch"); break;
+ case GEN_CHORUSSEND: printf("Chorus send"); break;
+ case GEN_REVERBSEND: printf("Reverb send"); break;
+ case GEN_PAN: printf("pan"); break;
+ case GEN_ATTENUATION: printf("att"); break;
+ default: printf("dest %i",dest);
+ }; /* switch dest */
+ printf(", amount %f flags %i src2 %i flags2 %i\n",amount, flags1, src2, flags2);
+};
+
+
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.h
new file mode 100644
index 0000000..31fb124
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_mod.h
@@ -0,0 +1,40 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUID_MOD_H
+#define _FLUID_MOD_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_conv.h"
+
+void fluid_mod_clone(fluid_mod_t* mod, fluid_mod_t* src);
+fluid_real_t fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voice);
+void fluid_dump_modulator(fluid_mod_t * mod);
+
+#define fluid_mod_has_source(mod,cc,ctrl) \
+( ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) \
+ || ((((mod)->src1 == ctrl) && (((mod)->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)))) \
+|| ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) \
+ || ((((mod)->src2 == ctrl) && (((mod)->flags2 & FLUID_MOD_CC) == 0) && (cc == 0)))))
+
+#define fluid_mod_has_dest(mod,gen) ((mod)->dest == gen)
+
+
+#endif /* _FLUID_MOD_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_phase.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_phase.h
new file mode 100644
index 0000000..5d808ca
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_phase.h
@@ -0,0 +1,115 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_PHASE_H
+#define _FLUID_PHASE_H
+
+#include "fluid_config.h"
+
+/*
+ * phase
+ */
+
+#define FLUID_INTERP_BITS 8
+#define FLUID_INTERP_BITS_MASK 0xff000000
+#define FLUID_INTERP_BITS_SHIFT 24
+#define FLUID_INTERP_MAX 256
+
+#define FLUID_FRACT_MAX ((double)4294967296.0)
+
+/* fluid_phase_t
+* Purpose:
+* Playing pointer for voice playback
+*
+* When a sample is played back at a different pitch, the playing pointer in the
+* source sample will not advance exactly one sample per output sample.
+* This playing pointer is implemented using fluid_phase_t.
+* It is a 64 bit number. The higher 32 bits contain the 'index' (number of
+* the current sample), the lower 32 bits the fractional part.
+*/
+typedef unsigned long long fluid_phase_t;
+
+/* Purpose:
+ * Set a to b.
+ * a: fluid_phase_t
+ * b: fluid_phase_t
+ */
+#define fluid_phase_set(a,b) a=b;
+
+#define fluid_phase_set_int(a, b) ((a) = ((unsigned long long)(b)) << 32)
+
+/* Purpose:
+ * Sets the phase a to a phase increment given in b.
+ * For example, assume b is 0.9. After setting a to it, adding a to
+ * the playing pointer will advance it by 0.9 samples. */
+#define fluid_phase_set_float(a, b) \
+ (a) = (((unsigned long long)(b)) << 32) \
+ | (uint32) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX)
+
+/* create a fluid_phase_t from an index and a fraction value */
+#define fluid_phase_from_index_fract(index, fract) \
+ ((((unsigned long long)(index)) << 32) + (fract))
+
+/* Purpose:
+ * Return the index and the fractional part, respectively. */
+#define fluid_phase_index(_x) \
+ ((unsigned int)((_x) >> 32))
+#define fluid_phase_fract(_x) \
+ ((uint32)((_x) & 0xFFFFFFFF))
+
+/* Get the phase index with fractional rounding */
+#define fluid_phase_index_round(_x) \
+ ((unsigned int)(((_x) + 0x80000000) >> 32))
+
+
+/* Purpose:
+ * Takes the fractional part of the argument phase and
+ * calculates the corresponding position in the interpolation table.
+ * The fractional position of the playing pointer is calculated with a quite high
+ * resolution (32 bits). It would be unpractical to keep a set of interpolation
+ * coefficients for each possible fractional part...
+ */
+#define fluid_phase_fract_to_tablerow(_x) \
+ ((unsigned int)(fluid_phase_fract(_x) & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT)
+
+#define fluid_phase_double(_x) \
+ ((double)(fluid_phase_index(_x)) + ((double)fluid_phase_fract(_x) / FLUID_FRACT_MAX))
+
+/* Purpose:
+ * Advance a by a step of b (both are fluid_phase_t).
+ */
+#define fluid_phase_incr(a, b) a += b
+
+/* Purpose:
+ * Subtract b from a (both are fluid_phase_t).
+ */
+#define fluid_phase_decr(a, b) a -= b
+
+/* Purpose:
+ * Subtract b samples from a.
+ */
+#define fluid_phase_sub_int(a, b) ((a) -= (unsigned long long)(b) << 32)
+
+/* Purpose:
+ * Creates the expression a.index++. */
+#define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL)
+
+#endif /* _FLUID_PHASE_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.c
new file mode 100644
index 0000000..5df20eb
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.c
@@ -0,0 +1,1117 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluid_ramsfont.h"
+#include "fluid_sys.h"
+#include "fluid_synth.h"
+
+/* thenumber of samples before the start and after the end */
+#define SAMPLE_LOOP_MARGIN 8
+
+/* Prototypes */
+int fluid_rampreset_add_sample(fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey);
+int fluid_rampreset_izone_set_gen(fluid_rampreset_t* preset, fluid_sample_t* sample, int gen_type, float value);
+int fluid_rampreset_izone_set_loop(fluid_rampreset_t* preset, fluid_sample_t* sample, int on, float loopstart, float loopend);
+int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample);
+void fluid_rampreset_updatevoices(fluid_rampreset_t* preset, int gen_type, float val);
+
+/*
+ * fluid_ramsfont_create_sfont
+ */
+fluid_sfont_t*
+fluid_ramsfont_create_sfont()
+{
+ fluid_sfont_t* sfont;
+ fluid_ramsfont_t* ramsfont;
+
+ ramsfont = new_fluid_ramsfont();
+ if (ramsfont == NULL) {
+ return NULL;
+ }
+
+ sfont = FLUID_NEW(fluid_sfont_t);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont->data = ramsfont;
+ sfont->free = fluid_ramsfont_sfont_delete;
+ sfont->get_name = fluid_ramsfont_sfont_get_name;
+ sfont->get_preset = fluid_ramsfont_sfont_get_preset;
+ sfont->iteration_start = fluid_ramsfont_sfont_iteration_start;
+ sfont->iteration_next = fluid_ramsfont_sfont_iteration_next;
+
+ return sfont;
+
+}
+
+/***************************************************************
+ *
+ * PUBLIC INTERFACE
+ */
+
+int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont)
+{
+ if (delete_fluid_ramsfont(sfont->data) != 0)
+ return -1;
+ FLUID_FREE(sfont);
+ return 0;
+}
+
+char* fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont)
+{
+ return fluid_ramsfont_get_name((fluid_ramsfont_t*) sfont->data);
+}
+
+fluid_preset_t* fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum)
+{
+ fluid_preset_t* preset;
+ fluid_rampreset_t* rampreset;
+
+ rampreset = fluid_ramsfont_get_preset((fluid_ramsfont_t*) sfont->data, bank, prenum);
+
+ if (rampreset == NULL) {
+ return NULL;
+ }
+
+ preset = FLUID_NEW(fluid_preset_t);
+ if (preset == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ preset->sfont = sfont;
+ preset->data = rampreset;
+ preset->free = fluid_rampreset_preset_delete;
+ preset->get_name = fluid_rampreset_preset_get_name;
+ preset->get_banknum = fluid_rampreset_preset_get_banknum;
+ preset->get_num = fluid_rampreset_preset_get_num;
+ preset->noteon = fluid_rampreset_preset_noteon;
+ preset->notify = NULL;
+
+ return preset;
+}
+
+void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont)
+{
+ fluid_ramsfont_iteration_start((fluid_ramsfont_t*) sfont->data);
+}
+
+int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset)
+{
+ preset->free = fluid_rampreset_preset_delete;
+ preset->get_name = fluid_rampreset_preset_get_name;
+ preset->get_banknum = fluid_rampreset_preset_get_banknum;
+ preset->get_num = fluid_rampreset_preset_get_num;
+ preset->noteon = fluid_rampreset_preset_noteon;
+ preset->notify = NULL;
+
+ return fluid_ramsfont_iteration_next((fluid_ramsfont_t*) sfont->data, preset);
+}
+
+int fluid_rampreset_preset_delete(fluid_preset_t* preset)
+{
+ FLUID_FREE(preset);
+
+/* TODO: free modulators */
+
+ return 0;
+}
+
+char* fluid_rampreset_preset_get_name(fluid_preset_t* preset)
+{
+ return fluid_rampreset_get_name((fluid_rampreset_t*) preset->data);
+}
+
+int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset)
+{
+ return fluid_rampreset_get_banknum((fluid_rampreset_t*) preset->data);
+}
+
+int fluid_rampreset_preset_get_num(fluid_preset_t* preset)
+{
+ return fluid_rampreset_get_num((fluid_rampreset_t*) preset->data);
+}
+
+int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel)
+{
+ return fluid_rampreset_noteon((fluid_rampreset_t*) preset->data, synth, chan, key, vel);
+}
+
+
+
+
+/***************************************************************
+ *
+ * SFONT
+ */
+
+/*
+ * new_fluid_ramsfont
+ */
+fluid_ramsfont_t* new_fluid_ramsfont()
+{
+ fluid_ramsfont_t* sfont;
+
+ sfont = FLUID_NEW(fluid_ramsfont_t);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont->name[0] = 0;
+ sfont->sample = NULL;
+ sfont->preset = NULL;
+
+ return sfont;
+}
+
+/*
+ * delete_fluid_ramsfont
+ */
+int delete_fluid_ramsfont(fluid_ramsfont_t* sfont)
+{
+ fluid_list_t *list;
+ fluid_rampreset_t* preset;
+
+ /* Check that no samples are currently used */
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ fluid_sample_t* sam = (fluid_sample_t*) fluid_list_get(list);
+ if (fluid_sample_refcount(sam) != 0) {
+ return -1;
+ }
+ }
+
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ /* in ram soundfonts, the samples hold their data : so we should free it ourselves */
+ fluid_sample_t* sam = (fluid_sample_t*)fluid_list_get(list);
+ delete_fluid_ramsample(sam);
+ }
+
+ if (sfont->sample) {
+ delete_fluid_list(sfont->sample);
+ }
+
+ preset = sfont->preset;
+ while (preset != NULL) {
+ sfont->preset = preset->next;
+ delete_fluid_rampreset(preset);
+ preset = sfont->preset;
+ }
+
+ FLUID_FREE(sfont);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_ramsfont_get_name
+ */
+char* fluid_ramsfont_get_name(fluid_ramsfont_t* sfont)
+{
+ return sfont->name;
+}
+
+/*
+ * fluid_ramsfont_set_name
+ */
+int
+fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, char * name)
+{
+ FLUID_MEMCPY(sfont->name, name, 20);
+ return FLUID_OK;
+}
+
+
+/* fluid_ramsfont_add_preset
+ *
+ * Add a preset to the SoundFont
+ */
+int fluid_ramsfont_add_preset(fluid_ramsfont_t* sfont, fluid_rampreset_t* preset)
+{
+ fluid_rampreset_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_ramsfont_add_ramsample
+ */
+int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample,
+ int lokey, int hikey)
+{
+ /*- find or create a preset
+ - add it the sample using the fluid_rampreset_add_sample fucntion
+ - add the sample to the list of samples
+ */
+ int err;
+
+ fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num);
+ if (preset == NULL) {
+ // Create it
+ preset = new_fluid_rampreset(sfont);
+ if (preset == NULL) {
+ return FLUID_FAILED;
+ }
+
+ preset->bank = bank;
+ preset->num = num;
+
+ err = fluid_rampreset_add_sample(preset, sample, lokey, hikey);
+ if (err != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ // sort the preset
+ fluid_ramsfont_add_preset(sfont, preset);
+
+ } else {
+
+ // just add it
+ err = fluid_rampreset_add_sample(preset, sample, lokey, hikey);
+ if (err != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+
+ sfont->sample = fluid_list_append(sfont->sample, sample);
+ return FLUID_OK;
+}
+
+int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample) {
+ int err;
+ fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num);
+ if (preset == NULL) {
+ return FLUID_FAILED;
+ }
+
+ // Fixed a crash bug : remove the sample from the sfont list after
+ // removing the izone (aschmitt august 2005)
+ err = fluid_rampreset_remove_izone(preset, sample);
+ if (err != FLUID_OK)
+ return err;
+
+ // now we must remove the sample from sfont->sample
+ sfont->sample = fluid_list_remove(sfont->sample, sample);
+
+ return FLUID_OK;
+}
+
+
+/* Note for version 2.0 : missing API fluid_ramsfont_izone_get_gen - Antoine Schmitt May 2003 */
+int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample,
+ int gen_type, float value) {
+
+ fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num);
+ if (preset == NULL) {
+ return FLUID_FAILED;
+ }
+
+ return fluid_rampreset_izone_set_gen(preset, sample, gen_type, value);
+}
+
+/* Note for version 2.0 : missing API fluid_ramsfont_izone_get_loop - Antoine Schmitt May 2003 */
+int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample,
+ int on, float loopstart, float loopend) {
+ fluid_rampreset_t* preset = fluid_ramsfont_get_preset(sfont, bank, num);
+ if (preset == NULL) {
+ return FLUID_FAILED;
+ }
+
+ return fluid_rampreset_izone_set_loop(preset, sample, on, loopstart, loopend);
+}
+
+/*
+ * fluid_ramsfont_get_preset
+ */
+fluid_rampreset_t* fluid_ramsfont_get_preset(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int num)
+{
+ fluid_rampreset_t* preset = sfont->preset;
+ while (preset != NULL) {
+ if ((preset->bank == bank) && ((preset->num == num))) {
+ return preset;
+ }
+ preset = preset->next;
+ }
+ return NULL;
+}
+
+/*
+ * fluid_ramsfont_iteration_start
+ */
+void fluid_ramsfont_iteration_start(fluid_ramsfont_t* sfont)
+{
+ sfont->iter_cur = sfont->preset;
+}
+
+/*
+ * fluid_ramsfont_iteration_next
+ */
+int fluid_ramsfont_iteration_next(fluid_ramsfont_t* sfont, fluid_preset_t* preset)
+{
+ if (sfont->iter_cur == NULL) {
+ return 0;
+ }
+
+ preset->data = (void*) sfont->iter_cur;
+ sfont->iter_cur = fluid_rampreset_next(sfont->iter_cur);
+ return 1;
+}
+
+/***************************************************************
+ *
+ * PRESET
+ */
+
+typedef struct _fluid_rampreset_voice_t fluid_rampreset_voice_t;
+struct _fluid_rampreset_voice_t {
+ fluid_voice_t *voice;
+ unsigned int voiceID;
+};
+
+/*
+ * new_fluid_rampreset
+ */
+fluid_rampreset_t*
+new_fluid_rampreset(fluid_ramsfont_t* sfont)
+{
+ fluid_rampreset_t* preset = FLUID_NEW(fluid_rampreset_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;
+ preset->presetvoices = NULL;
+ return preset;
+}
+
+/*
+ * delete_fluid_rampreset
+ */
+int
+delete_fluid_rampreset(fluid_rampreset_t* preset)
+{
+ int err = FLUID_OK;
+ fluid_preset_zone_t* zone;
+ fluid_rampreset_voice_t *data;
+
+ 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;
+ }
+
+ if (preset->presetvoices != NULL) {
+ fluid_list_t *tmp = preset->presetvoices, *next;
+ while (tmp) {
+ data = (fluid_rampreset_voice_t *)(tmp->data);
+ FLUID_FREE(data);
+
+ next = tmp->next;
+ FLUID_FREE(tmp);
+ tmp = next;
+ }
+ }
+ preset->presetvoices = NULL;
+
+ FLUID_FREE(preset);
+ return err;
+}
+
+int
+fluid_rampreset_get_banknum(fluid_rampreset_t* preset)
+{
+ return preset->bank;
+}
+
+int
+fluid_rampreset_get_num(fluid_rampreset_t* preset)
+{
+ return preset->num;
+}
+
+char*
+fluid_rampreset_get_name(fluid_rampreset_t* preset)
+{
+ return preset->name;
+}
+
+/*
+ * fluid_rampreset_next
+ */
+fluid_rampreset_t*
+fluid_rampreset_next(fluid_rampreset_t* preset)
+{
+ return preset->next;
+}
+
+
+/*
+ * fluid_rampreset_add_zone
+ */
+int
+fluid_rampreset_add_zone(fluid_rampreset_t* preset, fluid_preset_zone_t* zone)
+{
+ if (preset->zone == NULL) {
+ zone->next = NULL;
+ preset->zone = zone;
+ } else {
+ zone->next = preset->zone;
+ preset->zone = zone;
+ }
+ return FLUID_OK;
+}
+
+
+/*
+ * fluid_rampreset_add_sample
+ */
+int fluid_rampreset_add_sample(fluid_rampreset_t* preset, fluid_sample_t* sample, int lokey, int hikey)
+{
+ /* create a new instrument zone, with the given sample */
+
+ /* one preset zone */
+ if (preset->zone == NULL) {
+ fluid_preset_zone_t* zone;
+ zone = new_fluid_preset_zone("");
+ if (zone == NULL) {
+ return FLUID_FAILED;
+ }
+
+ /* its instrument */
+ zone->inst = (fluid_inst_t*) new_fluid_inst();
+ if (zone->inst == NULL) {
+ delete_fluid_preset_zone(zone);
+ return FLUID_FAILED;
+ }
+
+ fluid_rampreset_add_zone(preset, zone);
+ }
+
+ /* add an instrument zone for each sample */
+ {
+ fluid_inst_t* inst = fluid_preset_zone_get_inst(preset->zone);
+ fluid_inst_zone_t* izone = new_fluid_inst_zone("");
+ if (izone == NULL) {
+ return FLUID_FAILED;
+ }
+
+ if (fluid_inst_add_zone(inst, izone) != FLUID_OK) {
+ delete_fluid_inst_zone(izone);
+ return FLUID_FAILED;
+ }
+
+ izone->sample = sample;
+ izone->keylo = lokey;
+ izone->keyhi = hikey;
+
+ // give the preset the name of the sample
+ FLUID_MEMCPY(preset->name, sample->name, 20);
+ }
+
+ return FLUID_OK;
+}
+
+fluid_inst_zone_t* fluid_rampreset_izoneforsample(fluid_rampreset_t* preset, fluid_sample_t* sample)
+{
+ fluid_inst_t* inst;
+ fluid_inst_zone_t* izone;
+
+ if (preset->zone == NULL) return NULL;
+
+ inst = fluid_preset_zone_get_inst(preset->zone);
+ izone = inst->zone;
+ while (izone) {
+ if (izone->sample == sample)
+ return izone;
+ izone = izone->next;
+ }
+ return NULL;
+}
+
+int fluid_rampreset_izone_set_loop(fluid_rampreset_t* preset, fluid_sample_t* sample,
+ int on, float loopstart, float loopend) {
+ fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample);
+ short coarse, fine;
+
+ if (izone == NULL)
+ return FLUID_FAILED;
+
+ if (!on) {
+ izone->gen[GEN_SAMPLEMODE].flags = GEN_SET;
+ izone->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED;
+ fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_UNLOOPED);
+ return FLUID_OK;
+}
+
+ /* NOTE : We should check that (sample->startloop + loopStart <= sample->endloop - loopend - 32) */
+
+ /* loopstart */
+ if (loopstart > 32767. || loopstart < -32767.) {
+ coarse = (short)(loopstart/32768.);
+ fine = (short)(loopstart - (float)(coarse)*32768.);
+ } else {
+ coarse = 0;
+ fine = (short)loopstart;
+ }
+ izone->gen[GEN_STARTLOOPADDROFS].flags = GEN_SET;
+ izone->gen[GEN_STARTLOOPADDROFS].val = fine;
+ fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDROFS, fine);
+ if (coarse) {
+ izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_SET;
+ izone->gen[GEN_STARTLOOPADDRCOARSEOFS].val = coarse;
+ } else {
+ izone->gen[GEN_STARTLOOPADDRCOARSEOFS].flags = GEN_UNUSED;
+ }
+ fluid_rampreset_updatevoices(preset, GEN_STARTLOOPADDRCOARSEOFS, coarse);
+
+ /* loopend */
+ if (loopend > 32767. || loopend < -32767.) {
+ coarse = (short)(loopend/32768.);
+ fine = (short)(loopend - (float)(coarse)*32768.);
+ } else {
+ coarse = 0;
+ fine = (short)loopend;
+ }
+ izone->gen[GEN_ENDLOOPADDROFS].flags = GEN_SET;
+ izone->gen[GEN_ENDLOOPADDROFS].val = fine;
+ fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDROFS, fine);
+ if (coarse) {
+ izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_SET;
+ izone->gen[GEN_ENDLOOPADDRCOARSEOFS].val = coarse;
+ } else {
+ izone->gen[GEN_ENDLOOPADDRCOARSEOFS].flags = GEN_UNUSED;
+ }
+ fluid_rampreset_updatevoices(preset, GEN_ENDLOOPADDRCOARSEOFS, coarse);
+
+ izone->gen[GEN_SAMPLEMODE].flags = GEN_SET;
+ izone->gen[GEN_SAMPLEMODE].val = FLUID_LOOP_DURING_RELEASE;
+ fluid_rampreset_updatevoices(preset, GEN_SAMPLEMODE, FLUID_LOOP_DURING_RELEASE);
+
+ /* If the loop points are the whole samples, we are supposed to
+ copy the frames around in the margins (the start to the end margin and
+ the end to the start margin), but it works fine without this. Maybe some time
+ it will be needed (see SAMPLE_LOOP_MARGIN) -- Antoie Schmitt May 2003 */
+
+ return FLUID_OK;
+}
+
+int fluid_rampreset_izone_set_gen(fluid_rampreset_t* preset, fluid_sample_t* sample,
+ int gen_type, float value) {
+ fluid_inst_zone_t* izone = fluid_rampreset_izoneforsample(preset, sample);
+ if (izone == NULL)
+ return FLUID_FAILED;
+
+ izone->gen[gen_type].flags = GEN_SET;
+ izone->gen[gen_type].val = value;
+
+ fluid_rampreset_updatevoices(preset, gen_type, value);
+
+ return FLUID_OK;
+}
+
+int fluid_rampreset_remove_izone(fluid_rampreset_t* preset, fluid_sample_t* sample) {
+ fluid_inst_t* inst;
+ fluid_inst_zone_t* izone, * prev;
+ int found = 0;
+
+ if (preset->zone == NULL) return FLUID_FAILED;
+ inst = fluid_preset_zone_get_inst(preset->zone);
+ izone = inst->zone;
+ prev = NULL;
+ while (izone && !found) {
+ if (izone->sample == sample) {
+ if (prev == NULL) {
+ inst->zone = izone->next;
+ } else {
+ prev->next = izone->next;
+ }
+ izone->next = NULL;
+ delete_fluid_inst_zone(izone);
+ found = 1;
+ } else {
+ prev = izone;
+ izone = izone->next;
+ }
+ }
+ if (!found)
+ return FLUID_FAILED;
+
+ // stop all the voices that use this sample, so that
+ // the sample can be cleared up
+ {
+ fluid_list_t *tmp = preset->presetvoices;
+ while (tmp) {
+ fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data);
+ fluid_voice_t *voice = presetvoice->voice;
+ if (fluid_voice_is_playing(voice) && (fluid_voice_get_id(voice) == presetvoice->voiceID)) {
+ // still belongs to the preset
+ if (voice->sample == sample) {
+ // uses this sample : turn it off.
+ // our presetvoices struct will be cleaneup at the next update
+ fluid_voice_off(voice);
+ }
+ }
+ tmp = tmp->next;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_rampreset_remembervoice
+ */
+int
+fluid_rampreset_remembervoice(fluid_rampreset_t* preset, fluid_voice_t* voice) {
+ /* stores the voice and the its ID in the preset for later update on gen_set */
+ fluid_rampreset_voice_t *presetvoice = FLUID_NEW(fluid_rampreset_voice_t);
+ if (presetvoice == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ presetvoice->voice = voice;
+ presetvoice->voiceID = fluid_voice_get_id(voice);
+
+ preset->presetvoices = fluid_list_append(preset->presetvoices, (void *)presetvoice);
+ if (preset->presetvoices == NULL) {
+ FLUID_FREE(presetvoice);
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_rampreset_updatevoice
+ */
+void
+fluid_rampreset_updatevoices(fluid_rampreset_t* preset, int gen_type, float val) {
+ fluid_list_t *tmp = preset->presetvoices, *prev = NULL, *next;
+
+ /* walk the presetvoice to update them if they are still active and ours.
+ If their ID has changed or their state is not playing, they are not ours, so we forget them
+ */
+ while (tmp) {
+ fluid_rampreset_voice_t *presetvoice = (fluid_rampreset_voice_t *)(tmp->data);
+ fluid_voice_t *voice = presetvoice->voice;
+ if (!fluid_voice_is_playing(voice) || (fluid_voice_get_id(voice) != presetvoice->voiceID)) {
+ /* forget it */
+ FLUID_FREE(presetvoice);
+
+ /* unlink it */
+ next = tmp->next;
+ FLUID_FREE(tmp);
+ if (prev) {
+ prev->next = next;
+ } else {
+ preset->presetvoices = next;
+ }
+ tmp = next;
+
+ } else {
+
+ /* update */
+ fluid_voice_gen_set(voice, gen_type, val);
+ fluid_voice_update_param(voice, gen_type);
+
+ /* next */
+ prev = tmp;
+ tmp = tmp->next;
+ }
+ }
+}
+
+
+/*
+ * fluid_rampreset_noteon
+ */
+int
+fluid_rampreset_noteon(fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel)
+{
+ fluid_preset_zone_t *preset_zone;
+ fluid_inst_t* inst;
+ fluid_inst_zone_t *inst_zone, *global_inst_zone, *z;
+ 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;
+
+ /* run thru all the zones of this preset */
+ preset_zone = preset->zone;
+ while (preset_zone != NULL) {
+
+ /* check if the note falls into the key and velocity range of this
+ preset */
+ if (fluid_preset_zone_inside_range(preset_zone, key, vel)) {
+
+ inst = fluid_preset_zone_get_inst(preset_zone);
+ global_inst_zone = fluid_inst_get_global_zone(inst);
+
+ /* run thru all the zones of this instrument */
+ inst_zone = fluid_inst_get_zone(inst);
+ while (inst_zone != NULL) {
+
+ /* make sure this instrument zone has a valid sample */
+ sample = fluid_inst_zone_get_sample(inst_zone);
+ if (fluid_sample_in_rom(sample) || (sample == NULL)) {
+ inst_zone = fluid_inst_zone_next(inst_zone);
+ continue;
+ }
+
+ /* check if the note falls into the key and velocity range of this
+ instrument */
+
+ if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) {
+
+ /* this is a good zone. allocate a new synthesis process and
+ initialize it */
+
+ voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel);
+ if (voice == NULL) {
+ return FLUID_FAILED;
+ }
+
+ if (fluid_rampreset_remembervoice(preset, voice) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ z = inst_zone;
+
+ /* 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 (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 {
+ /* 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;
+
+ /* 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 (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;
+}
+
+
+
+/***************************************************************
+ *
+ * SAMPLE
+ */
+
+
+
+/*
+ * fluid_sample_set_name
+ */
+int
+fluid_sample_set_name(fluid_sample_t* sample, char * name)
+{
+ FLUID_MEMCPY(sample->name, name, 20);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_sample_set_sound_data
+ */
+int
+fluid_sample_set_sound_data(fluid_sample_t* sample, short *data, unsigned int nbframes, short copy_data, int rootkey)
+{
+ /* 16 bit mono 44.1KHz data in */
+ /* in all cases, the sample has ownership of the data : it will release it in the end */
+ unsigned int storedNbFrames;
+
+ /* in case we already have some data */
+ if (sample->data != NULL) {
+ FLUID_FREE(sample->data);
+ }
+
+ if (copy_data) {
+
+ /* nbframes should be >= 48 (SoundFont specs) */
+ storedNbFrames = nbframes;
+ if (storedNbFrames < 48) storedNbFrames = 48;
+
+ sample->data = FLUID_MALLOC(storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN);
+ if (sample->data == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_MEMSET(sample->data, 0, storedNbFrames*2 + 4*SAMPLE_LOOP_MARGIN);
+ FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN, data, nbframes*2);
+
+#if 0
+ /* this would do the fill of the margins */
+ FLUID_MEMCPY((char*)(sample->data) + 2*SAMPLE_LOOP_MARGIN + storedNbFrames*2, (char*)data, 2*SAMPLE_LOOP_MARGIN);
+ FLUID_MEMCPY((char*)(sample->data), (char*)data + nbframes*2 - 2*SAMPLE_LOOP_MARGIN, 2*SAMPLE_LOOP_MARGIN);
+#endif
+
+ /* pointers */
+ /* all from the start of data */
+ sample->start = SAMPLE_LOOP_MARGIN;
+ sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames;
+ } else {
+ /* we cannot assure the SAMPLE_LOOP_MARGIN */
+ sample->data = data;
+ sample->start = 0;
+ sample->end = nbframes;
+ }
+
+ /* only used as markers for the LOOP generators : set them on the first real frame */
+ sample->loopstart = sample->start;
+ sample->loopend = sample->end;
+
+ sample->samplerate = 44100;
+ sample->origpitch = rootkey;
+ sample->pitchadj = 0;
+ sample->sampletype = FLUID_SAMPLETYPE_MONO;
+ sample->valid = 1;
+
+ return FLUID_OK;
+}
+
+/*
+ * new_fluid_ramsample
+ */
+fluid_sample_t*
+new_fluid_ramsample()
+{
+ /* same as new_fluid_sample. Only here so that it is exported */
+ fluid_sample_t* sample = NULL;
+
+ sample = FLUID_NEW(fluid_sample_t);
+ if (sample == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ memset(sample, 0, sizeof(fluid_sample_t));
+
+ return sample;
+}
+
+/*
+ * delete_fluid_ramsample
+ */
+int
+delete_fluid_ramsample(fluid_sample_t* sample)
+{
+ /* same as delete_fluid_sample, plus frees the data */
+ if (sample->data != NULL) {
+ FLUID_FREE(sample->data);
+ }
+ sample->data = NULL;
+ FLUID_FREE(sample);
+ return FLUID_OK;
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.h
new file mode 100644
index 0000000..3c3cd1a
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_ramsfont.h
@@ -0,0 +1,114 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_RAMSFONT_H
+#define _FLUID_RAMSFONT_H
+
+
+#include "fluidlite.h"
+#include "fluidsynth_priv.h"
+
+#include "fluid_defsfont.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+/*
+
+ Public interface
+
+ */
+
+int fluid_ramsfont_sfont_delete(fluid_sfont_t* sfont);
+char* fluid_ramsfont_sfont_get_name(fluid_sfont_t* sfont);
+fluid_preset_t* fluid_ramsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
+void fluid_ramsfont_sfont_iteration_start(fluid_sfont_t* sfont);
+int fluid_ramsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset);
+
+
+int fluid_rampreset_preset_delete(fluid_preset_t* preset);
+char* fluid_rampreset_preset_get_name(fluid_preset_t* preset);
+int fluid_rampreset_preset_get_banknum(fluid_preset_t* preset);
+int fluid_rampreset_preset_get_num(fluid_preset_t* preset);
+int fluid_rampreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+
+/*
+ * fluid_ramsfont_t
+ */
+struct _fluid_ramsfont_t
+{
+ char name[21]; /* the name of the soundfont */
+ fluid_list_t* sample; /* the samples in this soundfont */
+ fluid_rampreset_t* preset; /* the presets of this soundfont */
+
+ fluid_preset_t iter_preset; /* preset interface used in the iteration */
+ fluid_rampreset_t* iter_cur; /* the current preset in the iteration */
+};
+
+/* interface */
+fluid_ramsfont_t* new_fluid_ramsfont(void);
+int delete_fluid_ramsfont(fluid_ramsfont_t* sfont);
+char* fluid_ramsfont_get_name(fluid_ramsfont_t* sfont);
+fluid_rampreset_t* fluid_ramsfont_get_preset(fluid_ramsfont_t* sfont, unsigned int bank, unsigned int prenum);
+void fluid_ramsfont_iteration_start(fluid_ramsfont_t* sfont);
+int fluid_ramsfont_iteration_next(fluid_ramsfont_t* sfont, fluid_preset_t* preset);
+/* specific */
+
+
+
+/*
+ * fluid_preset_t
+ */
+struct _fluid_rampreset_t
+{
+ fluid_rampreset_t* next;
+ fluid_ramsfont_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_list_t *presetvoices; /* chained list of used voices */
+};
+
+/* interface */
+fluid_rampreset_t* new_fluid_rampreset(fluid_ramsfont_t* sfont);
+int delete_fluid_rampreset(fluid_rampreset_t* preset);
+fluid_rampreset_t* fluid_rampreset_next(fluid_rampreset_t* preset);
+char* fluid_rampreset_get_name(fluid_rampreset_t* preset);
+int fluid_rampreset_get_banknum(fluid_rampreset_t* preset);
+int fluid_rampreset_get_num(fluid_rampreset_t* preset);
+int fluid_rampreset_noteon(fluid_rampreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUID_SFONT_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.c
new file mode 100644
index 0000000..3f09b6d
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.c
@@ -0,0 +1,561 @@
+/*
+
+ Freeverb
+
+ Written by Jezar at Dreampoint, June 2000
+ http://www.dreampoint.co.uk
+ This code is public domain
+
+ Translated to C by Peter Hanappe, Mai 2001
+*/
+
+#include "fluid_rev.h"
+
+/***************************************************************
+ *
+ * REVERB
+ */
+
+/* Denormalising:
+ *
+ * According to music-dsp thread 'Denormalise', Pentium processors
+ * have a hardware 'feature', that is of interest here, related to
+ * numeric underflow. We have a recursive filter. The output decays
+ * exponentially, if the input stops. So the numbers get smaller and
+ * smaller... At some point, they reach 'denormal' level. This will
+ * lead to drastic spikes in the CPU load. The effect was reproduced
+ * with the reverb - sometimes the average load over 10 s doubles!!.
+ *
+ * The 'undenormalise' macro fixes the problem: As soon as the number
+ * is close enough to denormal level, the macro forces the number to
+ * 0.0f. The original macro is:
+ *
+ * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
+ *
+ * This will zero out a number when it reaches the denormal level.
+ * Advantage: Maximum dynamic range Disadvantage: We'll have to check
+ * every sample, expensive. The alternative macro comes from a later
+ * mail from Jon Watte. It will zap a number before it reaches
+ * denormal level. Jon suggests to run it once per block instead of
+ * every sample.
+ */
+
+# if defined(WITH_FLOATX)
+# define zap_almost_zero(sample) (((*(unsigned int*)&(sample))&0x7f800000) < 0x08000000)?0.0f:(sample)
+# else
+/* 1e-20 was chosen as an arbitrary (small) threshold. */
+#define zap_almost_zero(sample) fabs(sample)<1e-10 ? 0 : sample;
+#endif
+
+/* Denormalising part II:
+ *
+ * Another method fixes the problem cheaper: Use a small DC-offset in
+ * the filter calculations. Now the signals converge not against 0,
+ * but against the offset. The constant offset is invisible from the
+ * outside world (i.e. it does not appear at the output. There is a
+ * very small turn-on transient response, which should not cause
+ * problems.
+ */
+
+
+//#define DC_OFFSET 0
+#define DC_OFFSET 1e-8
+//#define DC_OFFSET 0.001f
+typedef struct _fluid_allpass fluid_allpass;
+typedef struct _fluid_comb fluid_comb;
+
+struct _fluid_allpass {
+ fluid_real_t feedback;
+ fluid_real_t *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+void fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size);
+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
+fluid_allpass_setbuffer(fluid_allpass* allpass, fluid_real_t *buf, int size)
+{
+ allpass->bufidx = 0;
+ allpass->buffer = buf;
+ allpass->bufsize = size;
+}
+
+void
+fluid_allpass_init(fluid_allpass* allpass)
+{
+ int i;
+ int len = allpass->bufsize;
+ fluid_real_t* buf = allpass->buffer;
+ for (i = 0; i < len; i++) {
+ buf[i] = DC_OFFSET; /* this is not 100 % correct. */
+ }
+}
+
+void
+fluid_allpass_setfeedback(fluid_allpass* allpass, fluid_real_t val)
+{
+ allpass->feedback = val;
+}
+
+fluid_real_t
+fluid_allpass_getfeedback(fluid_allpass* allpass)
+{
+ return allpass->feedback;
+}
+
+#define fluid_allpass_process(_allpass, _input) \
+{ \
+ fluid_real_t output; \
+ fluid_real_t bufout; \
+ bufout = _allpass.buffer[_allpass.bufidx]; \
+ output = bufout-_input; \
+ _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \
+ if (++_allpass.bufidx >= _allpass.bufsize) { \
+ _allpass.bufidx = 0; \
+ } \
+ _input = output; \
+}
+
+/* fluid_real_t fluid_allpass_process(fluid_allpass* allpass, fluid_real_t input) */
+/* { */
+/* fluid_real_t output; */
+/* fluid_real_t bufout; */
+/* bufout = allpass->buffer[allpass->bufidx]; */
+/* undenormalise(bufout); */
+/* output = -input + bufout; */
+/* allpass->buffer[allpass->bufidx] = input + (bufout * allpass->feedback); */
+/* if (++allpass->bufidx >= allpass->bufsize) { */
+/* allpass->bufidx = 0; */
+/* } */
+/* return output; */
+/* } */
+
+struct _fluid_comb {
+ fluid_real_t feedback;
+ fluid_real_t filterstore;
+ fluid_real_t damp1;
+ fluid_real_t damp2;
+ fluid_real_t *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+void fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size);
+void fluid_comb_init(fluid_comb* comb);
+void fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val);
+fluid_real_t fluid_comb_getdamp(fluid_comb* comb);
+void fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val);
+fluid_real_t fluid_comb_getfeedback(fluid_comb* comb);
+
+void
+fluid_comb_setbuffer(fluid_comb* comb, fluid_real_t *buf, int size)
+{
+ comb->filterstore = 0;
+ comb->bufidx = 0;
+ comb->buffer = buf;
+ comb->bufsize = size;
+}
+
+void
+fluid_comb_init(fluid_comb* comb)
+{
+ int i;
+ fluid_real_t* buf = comb->buffer;
+ int len = comb->bufsize;
+ for (i = 0; i < len; i++) {
+ buf[i] = DC_OFFSET; /* This is not 100 % correct. */
+ }
+}
+
+void
+fluid_comb_setdamp(fluid_comb* comb, fluid_real_t val)
+{
+ comb->damp1 = val;
+ comb->damp2 = 1 - val;
+}
+
+fluid_real_t
+fluid_comb_getdamp(fluid_comb* comb)
+{
+ return comb->damp1;
+}
+
+void
+fluid_comb_setfeedback(fluid_comb* comb, fluid_real_t val)
+{
+ comb->feedback = val;
+}
+
+fluid_real_t
+fluid_comb_getfeedback(fluid_comb* comb)
+{
+ return comb->feedback;
+}
+
+#define fluid_comb_process(_comb, _input, _output) \
+{ \
+ fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \
+ _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \
+ _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \
+ if (++_comb.bufidx >= _comb.bufsize) { \
+ _comb.bufidx = 0; \
+ } \
+ _output += _tmp; \
+}
+
+/* fluid_real_t fluid_comb_process(fluid_comb* comb, fluid_real_t input) */
+/* { */
+/* fluid_real_t output; */
+
+/* output = comb->buffer[comb->bufidx]; */
+/* undenormalise(output); */
+/* comb->filterstore = (output * comb->damp2) + (comb->filterstore * comb->damp1); */
+/* undenormalise(comb->filterstore); */
+/* comb->buffer[comb->bufidx] = input + (comb->filterstore * comb->feedback); */
+/* if (++comb->bufidx >= comb->bufsize) { */
+/* comb->bufidx = 0; */
+/* } */
+
+/* return output; */
+/* } */
+
+#define numcombs 8
+#define numallpasses 4
+#define fixedgain 0.015f
+#define scalewet 3.0f
+#define scaledamp 1.0f
+#define scaleroom 0.28f
+#define offsetroom 0.7f
+#define initialroom 0.5f
+#define initialdamp 0.2f
+#define initialwet 1
+#define initialdry 0
+#define initialwidth 1
+#define stereospread 23
+
+/*
+ These values assume 44.1KHz sample rate
+ they will probably be OK for 48KHz sample rate
+ but would need scaling for 96KHz (or other) sample rates.
+ The values were obtained by listening tests.
+*/
+#define combtuningL1 1116
+#define combtuningR1 1116 + stereospread
+#define combtuningL2 1188
+#define combtuningR2 1188 + stereospread
+#define combtuningL3 1277
+#define combtuningR3 1277 + stereospread
+#define combtuningL4 1356
+#define combtuningR4 1356 + stereospread
+#define combtuningL5 1422
+#define combtuningR5 1422 + stereospread
+#define combtuningL6 1491
+#define combtuningR6 1491 + stereospread
+#define combtuningL7 1557
+#define combtuningR7 1557 + stereospread
+#define combtuningL8 1617
+#define combtuningR8 1617 + stereospread
+#define allpasstuningL1 556
+#define allpasstuningR1 556 + stereospread
+#define allpasstuningL2 441
+#define allpasstuningR2 441 + stereospread
+#define allpasstuningL3 341
+#define allpasstuningR3 341 + stereospread
+#define allpasstuningL4 225
+#define allpasstuningR4 225 + stereospread
+
+struct _fluid_revmodel_t {
+ fluid_real_t roomsize;
+ fluid_real_t damp;
+ fluid_real_t wet, wet1, wet2;
+ fluid_real_t width;
+ fluid_real_t gain;
+ /*
+ The following are all declared inline
+ to remove the need for dynamic allocation
+ with its subsequent error-checking messiness
+ */
+ /* Comb filters */
+ fluid_comb combL[numcombs];
+ fluid_comb combR[numcombs];
+ /* Allpass filters */
+ fluid_allpass allpassL[numallpasses];
+ fluid_allpass allpassR[numallpasses];
+ /* Buffers for the combs */
+ fluid_real_t bufcombL1[combtuningL1];
+ fluid_real_t bufcombR1[combtuningR1];
+ fluid_real_t bufcombL2[combtuningL2];
+ fluid_real_t bufcombR2[combtuningR2];
+ fluid_real_t bufcombL3[combtuningL3];
+ fluid_real_t bufcombR3[combtuningR3];
+ fluid_real_t bufcombL4[combtuningL4];
+ fluid_real_t bufcombR4[combtuningR4];
+ fluid_real_t bufcombL5[combtuningL5];
+ fluid_real_t bufcombR5[combtuningR5];
+ fluid_real_t bufcombL6[combtuningL6];
+ fluid_real_t bufcombR6[combtuningR6];
+ fluid_real_t bufcombL7[combtuningL7];
+ fluid_real_t bufcombR7[combtuningR7];
+ fluid_real_t bufcombL8[combtuningL8];
+ fluid_real_t bufcombR8[combtuningR8];
+ /* Buffers for the allpasses */
+ fluid_real_t bufallpassL1[allpasstuningL1];
+ fluid_real_t bufallpassR1[allpasstuningR1];
+ fluid_real_t bufallpassL2[allpasstuningL2];
+ fluid_real_t bufallpassR2[allpasstuningR2];
+ fluid_real_t bufallpassL3[allpasstuningL3];
+ fluid_real_t bufallpassR3[allpasstuningR3];
+ fluid_real_t bufallpassL4[allpasstuningL4];
+ fluid_real_t bufallpassR4[allpasstuningR4];
+};
+
+void fluid_revmodel_update(fluid_revmodel_t* rev);
+void fluid_revmodel_init(fluid_revmodel_t* rev);
+
+fluid_revmodel_t*
+new_fluid_revmodel()
+{
+ fluid_revmodel_t* rev;
+ rev = FLUID_NEW(fluid_revmodel_t);
+ if (rev == NULL) {
+ return NULL;
+ }
+
+ /* Tie the components to their buffers */
+ fluid_comb_setbuffer(&rev->combL[0], rev->bufcombL1, combtuningL1);
+ fluid_comb_setbuffer(&rev->combR[0], rev->bufcombR1, combtuningR1);
+ fluid_comb_setbuffer(&rev->combL[1], rev->bufcombL2, combtuningL2);
+ fluid_comb_setbuffer(&rev->combR[1], rev->bufcombR2, combtuningR2);
+ fluid_comb_setbuffer(&rev->combL[2], rev->bufcombL3, combtuningL3);
+ fluid_comb_setbuffer(&rev->combR[2], rev->bufcombR3, combtuningR3);
+ fluid_comb_setbuffer(&rev->combL[3], rev->bufcombL4, combtuningL4);
+ fluid_comb_setbuffer(&rev->combR[3], rev->bufcombR4, combtuningR4);
+ fluid_comb_setbuffer(&rev->combL[4], rev->bufcombL5, combtuningL5);
+ fluid_comb_setbuffer(&rev->combR[4], rev->bufcombR5, combtuningR5);
+ fluid_comb_setbuffer(&rev->combL[5], rev->bufcombL6, combtuningL6);
+ fluid_comb_setbuffer(&rev->combR[5], rev->bufcombR6, combtuningR6);
+ fluid_comb_setbuffer(&rev->combL[6], rev->bufcombL7, combtuningL7);
+ fluid_comb_setbuffer(&rev->combR[6], rev->bufcombR7, combtuningR7);
+ fluid_comb_setbuffer(&rev->combL[7], rev->bufcombL8, combtuningL8);
+ fluid_comb_setbuffer(&rev->combR[7], rev->bufcombR8, combtuningR8);
+ fluid_allpass_setbuffer(&rev->allpassL[0], rev->bufallpassL1, allpasstuningL1);
+ fluid_allpass_setbuffer(&rev->allpassR[0], rev->bufallpassR1, allpasstuningR1);
+ fluid_allpass_setbuffer(&rev->allpassL[1], rev->bufallpassL2, allpasstuningL2);
+ fluid_allpass_setbuffer(&rev->allpassR[1], rev->bufallpassR2, allpasstuningR2);
+ fluid_allpass_setbuffer(&rev->allpassL[2], rev->bufallpassL3, allpasstuningL3);
+ fluid_allpass_setbuffer(&rev->allpassR[2], rev->bufallpassR3, allpasstuningR3);
+ fluid_allpass_setbuffer(&rev->allpassL[3], rev->bufallpassL4, allpasstuningL4);
+ fluid_allpass_setbuffer(&rev->allpassR[3], rev->bufallpassR4, allpasstuningR4);
+ /* 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);
+
+ /* set values manually, since calling set functions causes update
+ and all values should be initialized for an update */
+ rev->roomsize = initialroom * scaleroom + offsetroom;
+ rev->damp = initialdamp * scaledamp;
+ rev->wet = initialwet * scalewet;
+ rev->width = initialwidth;
+ rev->gain = fixedgain;
+
+ /* now its okay to update reverb */
+ fluid_revmodel_update(rev);
+
+ /* Clear all buffers */
+ fluid_revmodel_init(rev);
+ return rev;
+}
+
+void
+delete_fluid_revmodel(fluid_revmodel_t* rev)
+{
+ FLUID_FREE(rev);
+}
+
+void
+fluid_revmodel_init(fluid_revmodel_t* rev)
+{
+ int i;
+ for (i = 0; i < numcombs;i++) {
+ fluid_comb_init(&rev->combL[i]);
+ fluid_comb_init(&rev->combR[i]);
+ }
+ for (i = 0; i < numallpasses; i++) {
+ fluid_allpass_init(&rev->allpassL[i]);
+ fluid_allpass_init(&rev->allpassR[i]);
+ }
+}
+
+void
+fluid_revmodel_reset(fluid_revmodel_t* rev)
+{
+ fluid_revmodel_init(rev);
+}
+
+void
+fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int i, k = 0;
+ fluid_real_t outL, outR, input;
+
+ for (k = 0; k < FLUID_BUFSIZE; k++) {
+
+ outL = outR = 0;
+
+ /* The original Freeverb code expects a stereo signal and 'input'
+ * is set to the sum of the left and right input sample. Since
+ * this code works on a mono signal, 'input' is set to twice the
+ * input sample. */
+ input = (2 * in[k] + DC_OFFSET) * rev->gain;
+
+ /* Accumulate comb filters in parallel */
+ for (i = 0; i < numcombs; i++) {
+ fluid_comb_process(rev->combL[i], input, outL);
+ fluid_comb_process(rev->combR[i], input, outR);
+ }
+ /* Feed through allpasses in series */
+ for (i = 0; i < numallpasses; i++) {
+ fluid_allpass_process(rev->allpassL[i], outL);
+ fluid_allpass_process(rev->allpassR[i], outR);
+ }
+
+ /* Remove the DC offset */
+ outL -= DC_OFFSET;
+ outR -= DC_OFFSET;
+
+ /* Calculate output REPLACING anything already there */
+ left_out[k] = outL * rev->wet1 + outR * rev->wet2;
+ right_out[k] = outR * rev->wet1 + outL * rev->wet2;
+ }
+}
+
+void
+fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int i, k = 0;
+ fluid_real_t outL, outR, input;
+
+ for (k = 0; k < FLUID_BUFSIZE; k++) {
+
+ outL = outR = 0;
+
+ /* The original Freeverb code expects a stereo signal and 'input'
+ * is set to the sum of the left and right input sample. Since
+ * this code works on a mono signal, 'input' is set to twice the
+ * input sample. */
+ input = (2 * 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;
+ }
+}
+
+void
+fluid_revmodel_update(fluid_revmodel_t* rev)
+{
+ /* Recalculate internal values after parameter change */
+ int i;
+
+ rev->wet1 = rev->wet * (rev->width / 2 + 0.5f);
+ rev->wet2 = rev->wet * ((1 - rev->width) / 2);
+
+ 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);
+ }
+}
+
+/*
+ The following get/set functions are not inlined, because
+ speed is never an issue when calling them, and also
+ because as you develop the reverb model, you may
+ wish to take dynamic action when they are called.
+*/
+void
+fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value)
+{
+/* fluid_clip(value, 0.0f, 1.0f); */
+ rev->roomsize = (value * scaleroom) + offsetroom;
+ fluid_revmodel_update(rev);
+}
+
+fluid_real_t
+fluid_revmodel_getroomsize(fluid_revmodel_t* rev)
+{
+ return (rev->roomsize - offsetroom) / scaleroom;
+}
+
+void
+fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value)
+{
+/* fluid_clip(value, 0.0f, 1.0f); */
+ rev->damp = value * scaledamp;
+ fluid_revmodel_update(rev);
+}
+
+fluid_real_t
+fluid_revmodel_getdamp(fluid_revmodel_t* rev)
+{
+ return rev->damp / scaledamp;
+}
+
+void
+fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value)
+{
+ fluid_clip(value, 0.0f, 1.0f);
+ rev->wet = value * scalewet;
+ fluid_revmodel_update(rev);
+}
+
+fluid_real_t
+fluid_revmodel_getlevel(fluid_revmodel_t* rev)
+{
+ return rev->wet / scalewet;
+}
+
+void
+fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value)
+{
+/* fluid_clip(value, 0.0f, 1.0f); */
+ rev->width = value;
+ fluid_revmodel_update(rev);
+}
+
+fluid_real_t
+fluid_revmodel_getwidth(fluid_revmodel_t* rev)
+{
+ return rev->width;
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.h
new file mode 100644
index 0000000..1d0fd2e
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_rev.h
@@ -0,0 +1,67 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_REV_H
+#define _FLUID_REV_H
+
+#include "fluidsynth_priv.h"
+
+typedef struct _fluid_revmodel_t fluid_revmodel_t;
+
+
+/*
+ * reverb
+ */
+fluid_revmodel_t* new_fluid_revmodel(void);
+void delete_fluid_revmodel(fluid_revmodel_t* rev);
+
+void fluid_revmodel_processmix(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out);
+
+void fluid_revmodel_processreplace(fluid_revmodel_t* rev, fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out);
+
+void fluid_revmodel_reset(fluid_revmodel_t* rev);
+
+void fluid_revmodel_setroomsize(fluid_revmodel_t* rev, fluid_real_t value);
+void fluid_revmodel_setdamp(fluid_revmodel_t* rev, fluid_real_t value);
+void fluid_revmodel_setlevel(fluid_revmodel_t* rev, fluid_real_t value);
+void fluid_revmodel_setwidth(fluid_revmodel_t* rev, fluid_real_t value);
+void fluid_revmodel_setmode(fluid_revmodel_t* rev, fluid_real_t value);
+
+fluid_real_t fluid_revmodel_getroomsize(fluid_revmodel_t* rev);
+fluid_real_t fluid_revmodel_getdamp(fluid_revmodel_t* rev);
+fluid_real_t fluid_revmodel_getlevel(fluid_revmodel_t* rev);
+fluid_real_t fluid_revmodel_getwidth(fluid_revmodel_t* rev);
+
+/*
+ * reverb preset
+ */
+typedef struct _fluid_revmodel_presets_t {
+ char* name;
+ fluid_real_t roomsize;
+ fluid_real_t damp;
+ fluid_real_t width;
+ fluid_real_t level;
+} fluid_revmodel_presets_t;
+
+
+#endif /* _FLUID_REV_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.c
new file mode 100644
index 0000000..574f451
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.c
@@ -0,0 +1,822 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_sys.h"
+#include "fluid_hash.h"
+#include "fluid_synth.h"
+#include "fluid_settings.h"
+
+/* maximum allowed components of a settings variable (separated by '.') */
+#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */
+#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */
+
+static void fluid_settings_init(fluid_settings_t* settings);
+static void fluid_settings_hash_delete(void* value, int type);
+static int fluid_settings_tokenize(const char* s, char *buf, char** ptr);
+
+
+typedef struct {
+ char* value;
+ char* def;
+ int hints;
+ fluid_list_t* options;
+ fluid_str_update_t update;
+ void* data;
+} fluid_str_setting_t;
+
+static fluid_str_setting_t*
+new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data)
+{
+ fluid_str_setting_t* str;
+ str = FLUID_NEW(fluid_str_setting_t);
+ str->value = value? FLUID_STRDUP(value) : NULL;
+ str->def = def? FLUID_STRDUP(def) : NULL;
+ str->hints = hints;
+ str->options = NULL;
+ str->update = fun;
+ str->data = data;
+ return str;
+}
+
+static void delete_fluid_str_setting(fluid_str_setting_t* str)
+{
+ if (str) {
+ if (str->value) {
+ FLUID_FREE(str->value);
+ }
+ if (str->def) {
+ FLUID_FREE(str->def);
+ }
+ if (str->options) {
+ fluid_list_t* list = str->options;
+
+ while (list) {
+ FLUID_FREE (list->data);
+ list = fluid_list_next(list);
+ }
+
+ delete_fluid_list(str->options);
+ }
+ FLUID_FREE(str);
+ }
+}
+
+
+
+
+typedef struct {
+ double value;
+ double def;
+ double min;
+ double max;
+ int hints;
+ fluid_num_update_t update;
+ void* data;
+} fluid_num_setting_t;
+
+
+static fluid_num_setting_t*
+new_fluid_num_setting(double min, double max, double def,
+ int hints, fluid_num_update_t fun, void* data)
+{
+ fluid_num_setting_t* setting;
+ setting = FLUID_NEW(fluid_num_setting_t);
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void delete_fluid_num_setting(fluid_num_setting_t* setting)
+{
+ if (setting) {
+ FLUID_FREE(setting);
+ }
+}
+
+
+
+
+typedef struct {
+ int value;
+ int def;
+ int min;
+ int max;
+ int hints;
+ fluid_int_update_t update;
+ void* data;
+} fluid_int_setting_t;
+
+
+static fluid_int_setting_t*
+new_fluid_int_setting(int min, int max, int def,
+ int hints, fluid_int_update_t fun, void* data)
+{
+ fluid_int_setting_t* setting;
+ setting = FLUID_NEW(fluid_int_setting_t);
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void delete_fluid_int_setting(fluid_int_setting_t* setting)
+{
+ if (setting) {
+ FLUID_FREE(setting);
+ }
+}
+
+
+
+fluid_settings_t* new_fluid_settings()
+{
+ fluid_settings_t* settings = new_fluid_hashtable(fluid_settings_hash_delete);
+ if (settings == NULL) {
+ return NULL;
+ }
+ fluid_settings_init(settings);
+ return settings;
+}
+
+void delete_fluid_settings(fluid_settings_t* settings)
+{
+ delete_fluid_hashtable(settings);
+}
+
+void fluid_settings_hash_delete(void* value, int type)
+{
+ switch (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_hashtable((fluid_hashtable_t*) value);
+ break;
+ }
+}
+
+void fluid_settings_init(fluid_settings_t* settings)
+{
+ fluid_synth_settings(settings);
+}
+
+static int fluid_settings_tokenize(const char* s, char *buf, char** ptr)
+{
+ char *tokstr, *tok;
+ int n = 0;
+
+ if (strlen (s) > MAX_SETTINGS_LABEL)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars",
+ MAX_SETTINGS_LABEL);
+ return 0;
+ }
+
+ FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */
+ tokstr = buf;
+
+ while ((tok = fluid_strtok (&tokstr, ".")))
+ {
+ if (n > MAX_SETTINGS_TOKENS)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",
+ MAX_SETTINGS_TOKENS);
+ return 0;
+ }
+
+ ptr[n++] = tok;
+ }
+
+ return n;
+}
+
+/** returns 1 if the value exists, 0 otherwise */
+static int fluid_settings_get(fluid_settings_t* settings,
+ char** name, int len,
+ void** value, int* type)
+{
+ fluid_hashtable_t* table = settings;
+ int t;
+ void* v = NULL;
+ int n;
+
+ for (n = 0; n < len; n++) {
+
+ if (table == NULL) {
+ return 0;
+ }
+
+ if (!fluid_hashtable_lookup(table, name[n], &v, &t)) {
+ return 0;
+ }
+
+ table = (t == FLUID_SET_TYPE)? (fluid_hashtable_t*) v : NULL;
+ }
+
+ if (value) {
+ *value = v;
+ }
+
+ if (type) {
+ *type = t;
+ }
+
+ return 1;
+}
+
+/** returns 1 if the value has been set, zero otherwise */
+static int fluid_settings_set(fluid_settings_t* settings,
+ char** name, int len,
+ void* value, int type)
+{
+ fluid_hashtable_t* table = settings;
+ int t;
+ void* v;
+ int n, num = len - 1;
+
+ for (n = 0; n < num; n++) {
+
+ if (fluid_hashtable_lookup(table, name[n], &v, &t)) {
+
+ if (t == FLUID_SET_TYPE) {
+ table = (fluid_hashtable_t*) v;
+ } else {
+ /* path ends prematurely */
+ FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]);
+ return 0;
+ }
+
+ } else {
+ /* create a new node */
+ fluid_hashtable_t* tmp;
+ tmp = new_fluid_hashtable(fluid_settings_hash_delete);
+ fluid_hashtable_insert(table, name[n], tmp, FLUID_SET_TYPE);
+ table = tmp;
+ }
+ }
+
+ fluid_hashtable_replace(table, name[num], value, type);
+
+ return 1;
+}
+
+/** returns 1 if the value has been registered correctly, 0
+ otherwise */
+int fluid_settings_register_str(fluid_settings_t* settings, const char* name, char* def, int hints,
+ fluid_str_update_t fun, void* data)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+ fluid_str_setting_t* setting;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+ setting = new_fluid_str_setting(def, def, hints, fun, data);
+ return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE);
+
+ } else {
+ /* if variable already exists, don't change its value. */
+ if (type == FLUID_STR_TYPE) {
+ setting = (fluid_str_setting_t*) value;
+ setting->update = fun;
+ setting->data = data;
+ setting->def = def? FLUID_STRDUP(def) : NULL;
+ setting->hints = hints;
+ return 1;
+ } else {
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ return 1;
+ }
+ }
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise */
+int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double def,
+ double min, double max, int hints,
+ fluid_num_update_t fun, void* data)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(min, max, def, hints, fun, data);
+ return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE);
+
+ } else {
+ if (type == FLUID_NUM_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ return 1;
+
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ return 0;
+ }
+ }
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise */
+int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int def,
+ int min, int max, int hints,
+ fluid_int_update_t fun, void* data)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(min, max, def, hints, fun, data);
+ return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE);
+
+ } else {
+ if (type == FLUID_INT_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ return 1;
+
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ return 0;
+ }
+ }
+}
+
+int fluid_settings_get_type(fluid_settings_t* settings, const char* name)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ return (fluid_settings_get(settings, tokens, ntokens, &value, &type))? type : FLUID_NO_TYPE;
+}
+
+int fluid_settings_get_hints(fluid_settings_t* settings, const char* name)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+ if (type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
+ return setting->hints;
+ } else if (type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
+ return setting->hints;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+int fluid_settings_is_realtime(fluid_settings_t* settings, const char* name)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+ if (type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
+ return setting->update != NULL;
+
+ } else if (type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
+ return setting->update != NULL;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+int fluid_settings_setstr(fluid_settings_t* settings, const char* name, const char* str)
+{
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+ int type;
+ void* value;
+ fluid_str_setting_t* setting;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+
+ if (type != FLUID_STR_TYPE) {
+ return 0;
+ }
+
+ setting = (fluid_str_setting_t*) value;
+
+ if (setting->value) {
+ FLUID_FREE(setting->value);
+ }
+ setting->value = str? FLUID_STRDUP(str) : NULL;
+
+ if (setting->update) {
+ (*setting->update)(setting->data, name, setting->value);
+ }
+
+ return 1;
+
+ } else {
+ /* insert a new setting */
+ fluid_str_setting_t* setting;
+ setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL);
+ return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE);
+ }
+}
+
+int fluid_settings_getstr(fluid_settings_t* settings, const char* name, char** str)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_STR_TYPE)) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
+ *str = setting->value;
+ return 1;
+ }
+ *str = NULL;
+ return 0;
+}
+
+int fluid_settings_str_equal(fluid_settings_t* settings, const char* name, char* s)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_STR_TYPE)) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
+ return FLUID_STRCMP(setting->value, s) == 0;
+ }
+ return 0;
+}
+
+char*
+fluid_settings_getstr_default(fluid_settings_t* settings, const char* name)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_STR_TYPE)) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
+ return setting->def;
+ } else {
+ return NULL;
+ }
+}
+
+int fluid_settings_add_option(fluid_settings_t* settings, const char* name, char* s)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_STR_TYPE)) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
+ char* copy = FLUID_STRDUP(s);
+ setting->options = fluid_list_append(setting->options, copy);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, char* s)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_STR_TYPE)) {
+
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) value;
+ 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);
+ return 1;
+ }
+ list = fluid_list_next(list);
+ }
+
+ return 0;
+ } else {
+ return 0;
+ }
+}
+
+int fluid_settings_setnum(fluid_settings_t* settings, const char* name, double val)
+{
+ int type;
+ void* value;
+ fluid_num_setting_t* setting;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+
+ if (type != FLUID_NUM_TYPE) {
+ return 0;
+ }
+
+ setting = (fluid_num_setting_t*) value;
+
+ if (val < setting->min) {
+ val = setting->min;
+ } else if (val > setting->max) {
+ val = setting->max;
+ }
+
+ setting->value = val;
+
+ if (setting->update) {
+ (*setting->update)(setting->data, name, val);
+ }
+
+ return 1;
+
+ } else {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL);
+ setting->value = val;
+ return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE);
+ }
+}
+
+int fluid_settings_getnum(fluid_settings_t* settings, const char* name, double* val)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
+ *val = setting->value;
+ return 1;
+ }
+ return 0;
+}
+
+
+void fluid_settings_getnum_range(fluid_settings_t* settings, const char* name, double* min, double* max)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
+ *min = setting->min;
+ *max = setting->max;
+ }
+}
+
+double
+fluid_settings_getnum_default(fluid_settings_t* settings, const char* name)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) value;
+ return setting->def;
+ } else {
+ return 0.0f;
+ }
+}
+
+
+int fluid_settings_setint(fluid_settings_t* settings, const char* name, int val)
+{
+ int type;
+ void* value;
+ fluid_int_setting_t* setting;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) {
+
+ if (type != FLUID_INT_TYPE) {
+ return 0;
+ }
+
+ setting = (fluid_int_setting_t*) value;
+
+ if (val < setting->min) {
+ val = setting->min;
+ } else if (val > setting->max) {
+ val = setting->max;
+ }
+
+ setting->value = val;
+
+ if (setting->update) {
+ (*setting->update)(setting->data, name, val);
+ }
+
+ return 1;
+
+ } else {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL);
+ setting->value = val;
+ return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE);
+ }
+}
+
+int fluid_settings_getint(fluid_settings_t* settings, const char* name, int* val)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
+ *val = setting->value;
+ return 1;
+ }
+ return 0;
+}
+
+
+void fluid_settings_getint_range(fluid_settings_t* settings, const char* name, int* min, int* max)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
+ *min = setting->min;
+ *max = setting->max;
+ }
+}
+
+int
+fluid_settings_getint_default(fluid_settings_t* settings, const char* name)
+{
+ int type;
+ void* value;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+
+ ntokens = fluid_settings_tokenize(name, buf, tokens);
+
+ if (fluid_settings_get(settings, tokens, ntokens, &value, &type)
+ && (type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) value;
+ return setting->def;
+ } else {
+ return 0.0f;
+ }
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.h
new file mode 100644
index 0000000..1108948
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_settings.h
@@ -0,0 +1,55 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_SETTINGS_H
+#define _FLUID_SETTINGS_H
+
+
+
+/** returns 1 if the option was added, 0 otherwise */
+int fluid_settings_add_option(fluid_settings_t* settings, const char* name, char* s);
+
+/** returns 1 if the option was added, 0 otherwise */
+int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, char* s);
+
+
+typedef int (*fluid_num_update_t)(void* data, const char* name, double value);
+typedef int (*fluid_str_update_t)(void* data, const char* name, char* value);
+typedef int (*fluid_int_update_t)(void* data, const char* name, int value);
+
+/** returns 0 if the value has been resgister correctly, non-zero
+ otherwise */
+int fluid_settings_register_str(fluid_settings_t* settings, const char* name, char* def, int hints,
+ fluid_str_update_t fun, void* data);
+
+/** returns 0 if the value has been resgister correctly, non-zero
+ otherwise */
+int fluid_settings_register_num(fluid_settings_t* settings, const char* name, double min, double max,
+ double def, int hints, fluid_num_update_t fun, void* data);
+
+
+/** returns 0 if the value has been resgister correctly, non-zero
+ otherwise */
+int fluid_settings_register_int(fluid_settings_t* settings, const char* name, int min, int max,
+ int def, int hints, fluid_int_update_t fun, void* data);
+
+
+#endif /* _FLUID_SETTINGS_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_sfont.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sfont.h
new file mode 100644
index 0000000..74f33e2
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sfont.h
@@ -0,0 +1,77 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _PRIV_FLUID_SFONT_H
+#define _PRIV_FLUID_SFONT_H
+
+
+/*
+ * Utility macros to access soundfonts, presets, and samples
+ */
+
+#define fluid_fileapi_delete(_fileapi) { \
+ if ((_fileapi) && (_fileapi)->free) \
+ (*(_fileapi)->free)(_fileapi); \
+}
+#define fluid_sfloader_delete(_loader) { \
+ if (_loader) { \
+ fluid_fileapi_delete((_loader)->fileapi); \
+ if ((_loader)->free) (*(_loader)->free)(_loader); \
+ } \
+ }
+#define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename)
+
+
+#define delete_fluid_sfont(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0)
+#define fluid_sfont_get_name(_sf) (*(_sf)->get_name)(_sf)
+#define fluid_sfont_get_preset(_sf,_bank,_prenum) (*(_sf)->get_preset)(_sf,_bank,_prenum)
+#define fluid_sfont_iteration_start(_sf) (*(_sf)->iteration_start)(_sf)
+#define fluid_sfont_iteration_next(_sf,_pr) (*(_sf)->iteration_next)(_sf,_pr)
+#define fluid_sfont_get_data(_sf) (_sf)->data
+#define fluid_sfont_set_data(_sf,_p) { (_sf)->data = (void*) (_p); }
+
+
+#define delete_fluid_preset(_preset) \
+ { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }}
+
+#define fluid_preset_get_data(_preset) (_preset)->data
+#define fluid_preset_set_data(_preset,_p) { (_preset)->data = (void*) (_p); }
+#define fluid_preset_get_name(_preset) (*(_preset)->get_name)(_preset)
+#define fluid_preset_get_banknum(_preset) (*(_preset)->get_banknum)(_preset)
+#define fluid_preset_get_num(_preset) (*(_preset)->get_num)(_preset)
+
+#define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \
+ (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel)
+
+#define fluid_preset_notify(_preset,_reason,_chan) \
+ { if ((_preset) && (_preset)->notify) { (*(_preset)->notify)(_preset,_reason,_chan); }}
+
+
+#define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; }
+
+#define fluid_sample_decr_ref(_sample) \
+ (_sample)->refcount--; \
+ if (((_sample)->refcount == 0) && ((_sample)->notify)) \
+ (*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE);
+
+
+
+#endif /* _PRIV_FLUID_SFONT_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.c
new file mode 100644
index 0000000..06b3aaa
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.c
@@ -0,0 +1,3550 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include
+
+#include "fluid_synth.h"
+#include "fluid_sys.h"
+#include "fluid_chan.h"
+#include "fluid_tuning.h"
+#include "fluid_settings.h"
+#include "fluid_sfont.h"
+
+fluid_sfloader_t* new_fluid_defsfloader(void);
+
+/************************************************************************
+ *
+ * These functions were added after the v1.0 API freeze. They are not
+ * in synth.h. They should be added as soon as a new development
+ * version is started.
+ *
+ ************************************************************************/
+
+int fluid_synth_program_select2(fluid_synth_t* synth,
+ int chan,
+ char* sfont_name,
+ unsigned int bank_num,
+ unsigned int preset_num);
+
+fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name);
+
+int fluid_synth_set_gen2(fluid_synth_t* synth, int chan,
+ int param, float value,
+ int absolute, int normalized);
+
+
+/***************************************************************
+ *
+ * GLOBAL
+ */
+
+/* has the synth module been initialized? */
+static int fluid_synth_initialized = 0;
+static void fluid_synth_init(void);
+static void init_dither(void);
+
+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);
+
+/* default modulators
+ * SF2.01 page 52 ff:
+ *
+ * There is a set of predefined default modulators. They have to be
+ * explicitly overridden by the sound font in order to turn them off.
+ */
+
+fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */
+fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */
+fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */
+fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */
+fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */
+fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */
+fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */
+fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */
+fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */
+fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */
+
+/* reverb presets */
+static fluid_revmodel_presets_t revmodel_preset[] = {
+ /* name */ /* roomsize */ /* damp */ /* width */ /* level */
+ { "Test 1", 0.2f, 0.0f, 0.5f, 0.9f },
+ { "Test 2", 0.4f, 0.2f, 0.5f, 0.8f },
+ { "Test 3", 0.6f, 0.4f, 0.5f, 0.7f },
+ { "Test 4", 0.8f, 0.7f, 0.5f, 0.6f },
+ { "Test 5", 0.8f, 1.0f, 0.5f, 0.5f },
+ { NULL, 0.0f, 0.0f, 0.0f, 0.0f }
+};
+
+
+/***************************************************************
+ *
+ * INITIALIZATION & UTILITIES
+ */
+
+
+void fluid_synth_settings(fluid_settings_t* settings)
+{
+ fluid_settings_register_str(settings, "synth.verbose", "no", 0, NULL, NULL);
+ fluid_settings_register_str(settings, "synth.dump", "no", 0, NULL, NULL);
+ fluid_settings_register_str(settings, "synth.reverb.active", "yes", 0, NULL, NULL);
+ fluid_settings_register_str(settings, "synth.chorus.active", "yes", 0, NULL, NULL);
+ fluid_settings_register_str(settings, "synth.ladspa.active", "no", 0, NULL, NULL);
+ fluid_settings_register_str(settings, "midi.portname", "", 0, NULL, NULL);
+ fluid_settings_register_str(settings, "synth.drums-channel.active", "yes", 0, NULL, NULL);
+
+ fluid_settings_register_int(settings, "synth.polyphony",
+ 256, 16, 4096, 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, 256, 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.audio-groups",
+ 1, 1, 256, 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, 22050.0f, 96000.0f,
+ 0, NULL, NULL);
+ fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0, NULL, NULL);
+}
+
+/*
+ * fluid_version
+ */
+void fluid_version(int *major, int *minor, int *micro)
+{
+ *major = FLUIDSYNTH_VERSION_MAJOR;
+ *minor = FLUIDSYNTH_VERSION_MINOR;
+ *micro = FLUIDSYNTH_VERSION_MICRO;
+}
+
+/*
+ * fluid_version_str
+ */
+char* fluid_version_str(void)
+{
+ return FLUIDSYNTH_VERSION;
+}
+
+
+/*
+ * void fluid_synth_init
+ *
+ * Does all the initialization for this module.
+ */
+static void
+fluid_synth_init()
+{
+ fluid_synth_initialized++;
+
+ fluid_conversion_config();
+
+ fluid_dsp_float_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 Reverb 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 */
+}
+
+
+int fluid_synth_verify_settings(fluid_settings_t *settings)
+{
+ return 0;
+}
+
+/***************************************************************
+ *
+ * FLUID SYNTH
+ */
+
+/*
+ * new_fluid_synth
+ */
+fluid_synth_t*
+new_fluid_synth(fluid_settings_t *settings)
+{
+ int i;
+ fluid_synth_t* synth;
+ fluid_sfloader_t* loader;
+
+ /* initialize all the conversion tables and other stuff */
+ if (fluid_synth_initialized == 0) {
+ fluid_synth_init();
+ }
+
+ fluid_synth_verify_settings(settings);
+
+ /* 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_mutex_init(synth->busy);
+
+ synth->settings = settings;
+
+ synth->with_reverb = fluid_settings_str_equal(settings, "synth.reverb.active", "yes");
+ synth->with_chorus = fluid_settings_str_equal(settings, "synth.chorus.active", "yes");
+ synth->verbose = fluid_settings_str_equal(settings, "synth.verbose", "yes");
+ synth->dump = fluid_settings_str_equal(settings, "synth.dump", "yes");
+
+ 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", &synth->gain);
+ fluid_settings_getint(settings, "synth.min-note-length", &i);
+ synth->min_note_length_ticks = (unsigned int) (i*synth->sample_rate/1000.0f);
+
+
+ /* register the callbacks */
+ 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, 16, 4096, 0,
+ (fluid_int_update_t) fluid_synth_update_polyphony,
+ 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. */
+ synth->nbuf = synth->audio_channels;
+ if (synth->audio_groups > synth->nbuf) {
+ synth->nbuf = synth->audio_groups;
+ }
+
+#ifdef LADSPA
+ /* Create and initialize the Fx unit.*/
+ synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth);
+#endif
+
+ /* as soon as the synth is created it starts playing. */
+ synth->state = FLUID_SYNTH_PLAYING;
+ synth->sfont = NULL;
+ synth->noteid = 0;
+ synth->ticks = 0;
+ synth->tuning = NULL;
+
+ /* allocate and add the default sfont loader */
+ loader = new_fluid_defsfloader();
+
+ 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;
+ }
+ }
+
+ /* Allocate the sample buffers */
+ synth->left_buf = NULL;
+ synth->right_buf = NULL;
+ synth->fx_left_buf = NULL;
+ synth->fx_right_buf = NULL;
+
+ /* Left and right audio buffers */
+
+ synth->left_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf);
+ synth->right_buf = FLUID_ARRAY(fluid_real_t*, synth->nbuf);
+
+ if ((synth->left_buf == NULL) || (synth->right_buf == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+
+ FLUID_MEMSET(synth->left_buf, 0, synth->nbuf * sizeof(fluid_real_t*));
+ FLUID_MEMSET(synth->right_buf, 0, synth->nbuf * sizeof(fluid_real_t*));
+
+ for (i = 0; i < synth->nbuf; i++) {
+
+ synth->left_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
+ synth->right_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
+
+ if ((synth->left_buf[i] == NULL) || (synth->right_buf[i] == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+ }
+
+ /* Effects audio buffers */
+
+ synth->fx_left_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels);
+ synth->fx_right_buf = FLUID_ARRAY(fluid_real_t*, synth->effects_channels);
+
+ if ((synth->fx_left_buf == NULL) || (synth->fx_right_buf == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+
+ FLUID_MEMSET(synth->fx_left_buf, 0, 2 * sizeof(fluid_real_t*));
+ FLUID_MEMSET(synth->fx_right_buf, 0, 2 * sizeof(fluid_real_t*));
+
+ for (i = 0; i < synth->effects_channels; i++) {
+ synth->fx_left_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
+ synth->fx_right_buf[i] = FLUID_ARRAY(fluid_real_t, FLUID_BUFSIZE);
+
+ if ((synth->fx_left_buf[i] == NULL) || (synth->fx_right_buf[i] == NULL)) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+ }
+
+
+ synth->cur = FLUID_BUFSIZE;
+ synth->dither_index = 0;
+
+ /* allocate the reverb module */
+ synth->reverb = new_fluid_revmodel();
+ if (synth->reverb == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+
+ fluid_synth_set_reverb(synth,
+ FLUID_REVERB_DEFAULT_ROOMSIZE,
+ FLUID_REVERB_DEFAULT_DAMP,
+ FLUID_REVERB_DEFAULT_WIDTH,
+ FLUID_REVERB_DEFAULT_LEVEL);
+
+ /* allocate the chorus module */
+ synth->chorus = new_fluid_chorus(synth->sample_rate);
+ if (synth->chorus == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_recovery;
+ }
+
+ if(fluid_settings_str_equal(settings, "synth.drums-channel.active", "yes"))
+ fluid_synth_bank_select(synth,9,DRUM_INST_BANK);
+
+ return synth;
+
+ error_recovery:
+ delete_fluid_synth(synth);
+ return NULL;
+}
+
+/**
+ * Set sample rate of the synth.
+ * NOTE: This function is currently experimental and should only be
+ * used when no voices or notes are active, and before any rendering calls.
+ * @param synth FluidSynth instance
+ * @param sample_rate New sample rate (Hz)
+ * @since 1.1.2
+ */
+void
+fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate)
+{
+ int i;
+ for (i = 0; i < synth->nvoice; i++) {
+ delete_fluid_voice(synth->voice[i]);
+ synth->voice[i] = new_fluid_voice(synth->sample_rate);
+ }
+
+ delete_fluid_chorus(synth->chorus);
+ synth->chorus = new_fluid_chorus(synth->sample_rate);
+}
+
+/*
+ * delete_fluid_synth
+ */
+int
+delete_fluid_synth(fluid_synth_t* synth)
+{
+ int i, k;
+ fluid_list_t *list;
+ fluid_sfont_t* sfont;
+ fluid_bank_offset_t* bank_offset;
+ fluid_sfloader_t* loader;
+
+ if (synth == NULL) {
+ return FLUID_OK;
+ }
+
+ synth->state = FLUID_SYNTH_STOPPED;
+
+ /* turn off all voices, needed to unload SoundFont data */
+ if (synth->voice != NULL) {
+ for (i = 0; i < synth->nvoice; i++) {
+ if (synth->voice[i] && fluid_voice_is_playing (synth->voice[i]))
+ fluid_voice_off (synth->voice[i]);
+ }
+ }
+
+ /* delete all the SoundFonts */
+ for (list = synth->sfont; list; list = fluid_list_next(list)) {
+ sfont = (fluid_sfont_t*) fluid_list_get(list);
+ delete_fluid_sfont(sfont);
+ }
+
+ delete_fluid_list(synth->sfont);
+
+ /* and the SoundFont offsets */
+ for (list = synth->bank_offsets; list; list = fluid_list_next(list)) {
+ bank_offset = (fluid_bank_offset_t*) fluid_list_get(list);
+ FLUID_FREE(bank_offset);
+ }
+
+ delete_fluid_list(synth->bank_offsets);
+
+
+ /* delete all the SoundFont loaders */
+
+ for (list = synth->loaders; list; list = fluid_list_next(list)) {
+ loader = (fluid_sfloader_t*) fluid_list_get(list);
+ fluid_sfloader_delete(loader);
+ }
+
+ delete_fluid_list(synth->loaders);
+
+
+ if (synth->channel != NULL) {
+ for (i = 0; i < synth->midi_channels; i++) {
+ if (synth->channel[i] != NULL) {
+ delete_fluid_channel(synth->channel[i]);
+ }
+ }
+ FLUID_FREE(synth->channel);
+ }
+
+ if (synth->voice != NULL) {
+ for (i = 0; i < synth->nvoice; i++) {
+ if (synth->voice[i] != NULL) {
+ delete_fluid_voice(synth->voice[i]);
+ }
+ }
+ FLUID_FREE(synth->voice);
+ }
+
+ /* free all the sample buffers */
+ if (synth->left_buf != NULL) {
+ for (i = 0; i < synth->nbuf; i++) {
+ if (synth->left_buf[i] != NULL) {
+ FLUID_FREE(synth->left_buf[i]);
+ }
+ }
+ FLUID_FREE(synth->left_buf);
+ }
+
+ if (synth->right_buf != NULL) {
+ for (i = 0; i < synth->nbuf; i++) {
+ if (synth->right_buf[i] != NULL) {
+ FLUID_FREE(synth->right_buf[i]);
+ }
+ }
+ FLUID_FREE(synth->right_buf);
+ }
+
+ if (synth->fx_left_buf != NULL) {
+ for (i = 0; i < 2; i++) {
+ if (synth->fx_left_buf[i] != NULL) {
+ FLUID_FREE(synth->fx_left_buf[i]);
+ }
+ }
+ FLUID_FREE(synth->fx_left_buf);
+ }
+
+ if (synth->fx_right_buf != NULL) {
+ for (i = 0; i < 2; i++) {
+ if (synth->fx_right_buf[i] != NULL) {
+ FLUID_FREE(synth->fx_right_buf[i]);
+ }
+ }
+ FLUID_FREE(synth->fx_right_buf);
+ }
+
+ /* release the reverb module */
+ if (synth->reverb != NULL) {
+ delete_fluid_revmodel(synth->reverb);
+ }
+
+ /* release the chorus module */
+ if (synth->chorus != NULL) {
+ delete_fluid_chorus(synth->chorus);
+ }
+
+ /* 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) {
+ FLUID_FREE(synth->tuning[i][k]);
+ }
+ }
+ FLUID_FREE(synth->tuning[i]);
+ }
+ }
+ FLUID_FREE(synth->tuning);
+ }
+
+#ifdef LADSPA
+ /* Release the LADSPA Fx unit */
+ fluid_LADSPA_shutdown(synth->LADSPA_FxUnit);
+ FLUID_FREE(synth->LADSPA_FxUnit);
+#endif
+
+ //fluid_mutex_destroy(synth->busy);
+
+ FLUID_FREE(synth);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_error
+ *
+ * The error messages are not thread-save, yet. They are still stored
+ * in a global message buffer (see fluid_sys.c).
+ * */
+char*
+fluid_synth_error(fluid_synth_t* synth)
+{
+ return fluid_error();
+}
+
+/*
+ * fluid_synth_noteon
+ */
+int
+fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel)
+{
+ fluid_channel_t* channel;
+
+ /* check the ranges of the arguments */
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ /* notes with velocity zero go to noteoff */
+ if (vel == 0) {
+ return fluid_synth_noteoff(synth, chan, key);
+ }
+
+ channel = synth->channel[chan];
+
+ /* make sure this channel has a preset */
+ if (channel->preset == NULL) {
+ if (synth->verbose) {
+ FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t\t%.3f\t%d\t%s",
+ chan, key, vel, 0,
+ (float) synth->ticks / 44100.0f,
+ 0.0f, 0, "channel has no preset");
+ }
+ return FLUID_FAILED;
+ }
+
+ /* If there is another voice process on the same channel and key,
+ advance it to the release phase. */
+ fluid_synth_release_voice_on_same_note(synth, chan, key);
+
+ return fluid_synth_start(synth, synth->noteid++, channel->preset, 0, chan, key, vel);
+}
+
+/*
+ * fluid_synth_noteoff
+ */
+int
+fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key)
+{
+ int i;
+ fluid_voice_t* voice;
+ int status = FLUID_FAILED;
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ 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\t%.3f\t%d",
+ voice->chan, voice->key, 0, voice->id,
+ (float) (voice->start_time + voice->ticks) / 44100.0f,
+ (float) voice->ticks / 44100.0f,
+ used_voices);
+ } /* if verbose */
+ fluid_voice_noteoff(voice);
+ status = FLUID_OK;
+ } /* if voice on */
+ } /* for all voices */
+ return status;
+}
+
+/*
+ * fluid_synth_damp_voices
+ */
+int
+fluid_synth_damp_voices(fluid_synth_t* synth, int chan)
+{
+ int i;
+ fluid_voice_t* voice;
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if ((voice->chan == chan) && _SUSTAINED(voice)) {
+/* printf("turned off sustained note: chan=%d, key=%d, vel=%d\n", voice->chan, voice->key, voice->vel); */
+ fluid_voice_noteoff(voice);
+ }
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_cc
+ */
+int
+fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val)
+{
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ /* check the ranges of the arguments */
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+ if ((num < 0) || (num >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Ctrl out of range");
+ return FLUID_FAILED;
+ }
+ if ((val < 0) || (val >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Value out of range");
+ return FLUID_FAILED;
+ }
+
+ if (synth->verbose) {
+ FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val);
+ }
+
+ /* set the controller value in the channel */
+ fluid_channel_cc(synth->channel[chan], num, val);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_cc
+ */
+int
+fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval)
+{
+ /* check the ranges of the arguments */
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+ if ((num < 0) || (num >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Ctrl out of range");
+ return FLUID_FAILED;
+ }
+
+ *pval = synth->channel[chan]->cc[num];
+ return FLUID_OK;
+}
+
+/**
+ * Process a MIDI SYSEX (system exclusive) message.
+ * @param synth FluidSynth instance
+ * @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7)
+ * @param len Length of data in buffer
+ * @param response Buffer to store response to or NULL to ignore
+ * @param response_len IN/OUT parameter, in: size of response buffer, out:
+ * amount of data written to response buffer (if FLUID_FAILED is returned and
+ * this value is non-zero, it indicates the response buffer is too small)
+ * @param handled Optional location to store boolean value if message was
+ * recognized and handled or not (set to TRUE if it was handled)
+ * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX
+ * command (useful for checking if a SYSEX message would be handled)
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ */
+/* SYSEX format (0xF0 and 0xF7 not passed to this function):
+ * Non-realtime: 0xF0 0x7E [BODY] 0xF7
+ * Realtime: 0xF0 0x7F [BODY] 0xF7
+ * Tuning messages: 0xF0 0x7E/0x7F 0x08 [BODY] 0xF7
+ */
+int
+fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len,
+ char *response, int *response_len, int *handled, int dryrun)
+{
+ int avail_response = 0;
+
+ if (handled) *handled = 0; //FALSE
+
+ if (response_len)
+ {
+ avail_response = *response_len;
+ *response_len = 0;
+ }
+
+ if(!(synth != NULL)) return FLUID_FAILED;
+ if(!(data != NULL)) return FLUID_FAILED;
+ if(!(len > 0)) return FLUID_FAILED;
+ if(!(!response || response_len)) return FLUID_FAILED;
+
+ if (len < 4) return FLUID_OK;
+
+ /* MIDI tuning SYSEX message? */
+ if ((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME)
+ && data[2] == MIDI_SYSEX_MIDI_TUNING_ID)
+ //&& (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) -> we don't handle device id
+ {
+ int result;
+ //fluid_synth_api_enter(synth); -> we don't handle mutex
+ result = fluid_synth_sysex_midi_tuning (synth, data, len, response,
+ response_len, avail_response,
+ handled, dryrun);
+
+ return result;
+ }
+ return FLUID_OK;
+}
+
+/* Handler for MIDI tuning SYSEX messages */
+static int
+fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, const char *data, int len,
+ char *response, int *response_len, int avail_response,
+ int *handled, int dryrun)
+{
+ int realtime, msgid;
+ int bank = 0, prog, channels;
+ double tunedata[128];
+ int keys[128];
+ char name[17];
+ int note, frac, frac2;
+ uint8 chksum;
+ int i, count, index;
+ const char *dataptr;
+ char *resptr;
+
+ realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME;
+ msgid = data[3];
+
+ switch (msgid)
+ {
+ case MIDI_SYSEX_TUNING_BULK_DUMP_REQ:
+ case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK:
+ if (data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)
+ {
+ if (len != 5 || data[4] & 0x80 || !response)
+ return FLUID_OK;
+
+ *response_len = 406;
+ prog = data[4];
+ }
+ else
+ {
+ if (len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response)
+ return FLUID_OK;
+
+ *response_len = 407;
+ bank = data[4];
+ prog = data[5];
+ }
+
+ if (dryrun)
+ {
+ if (handled) *handled = 1; //TRUE
+ return FLUID_OK;
+ }
+
+ if (avail_response < *response_len) return FLUID_FAILED;
+
+ /* Get tuning data, return if tuning not found */
+ if (fluid_synth_tuning_dump (synth, bank, prog, name, 17, tunedata) == FLUID_FAILED)
+ {
+ *response_len = 0;
+ return FLUID_OK;
+ }
+
+ resptr = response;
+
+ *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME;
+ *resptr++ = 0; //no synth->device_id
+ *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID;
+ *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP;
+
+ if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK)
+ *resptr++ = bank;
+
+ *resptr++ = prog;
+ strncpy(resptr, name, 16); //FLUID_STRNCPY
+ resptr += 16;
+
+ for (i = 0; i < 128; i++)
+ {
+ note = tunedata[i] / 100.0;
+ fluid_clip (note, 0, 127);
+
+ frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0;
+ fluid_clip (frac, 0, 16383);
+
+ *resptr++ = note;
+ *resptr++ = frac >> 7;
+ *resptr++ = frac & 0x7F;
+ }
+
+ if (msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ)
+ { /* NOTE: Checksum is not as straight forward as the bank based messages */
+ chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID
+ ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog;
+
+ for (i = 21; i < 128 * 3 + 21; i++)
+ chksum ^= response[i];
+ }
+ else
+ {
+ for (i = 1, chksum = 0; i < 406; i++)
+ chksum ^= response[i];
+ }
+
+ *resptr++ = chksum & 0x7F;
+
+ if (handled) *handled = 1; //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 = 1; //TRUE
+ return FLUID_OK;
+ }
+
+ prog = *dataptr++;
+ count = *dataptr++;
+
+ for (i = 0, index = 0; i < count; i++)
+ {
+ note = *dataptr++;
+ if (note & 0x80) return FLUID_OK;
+ keys[index] = note;
+
+ note = *dataptr++;
+ frac = *dataptr++;
+ frac2 = *dataptr++;
+
+ if (note & 0x80 || frac & 0x80 || frac2 & 0x80)
+ return FLUID_OK;
+
+ frac = frac << 7 | frac2;
+
+ /* No change pitch value? Doesn't really make sense to send that, but.. */
+ if (note == 0x7F && frac == 16383) continue;
+
+ tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0);
+ index++;
+ }
+
+ if (index > 0)
+ {
+ if (fluid_synth_tune_notes (synth, bank, prog, index, keys, tunedata,
+ realtime) == FLUID_FAILED)
+ return FLUID_FAILED;
+ }
+
+ if (handled) *handled = 1; //TRUE
+ break;
+ case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE:
+ case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE:
+ if ((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19)
+ || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31))
+ return FLUID_OK;
+
+ if (data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80)
+ return FLUID_OK;
+
+ if (dryrun)
+ {
+ if (handled) *handled = 1; //TRUE
+ return FLUID_OK;
+ }
+
+ channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6];
+
+ if (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE)
+ {
+ for (i = 0; i < 12; i++)
+ {
+ frac = data[i + 7];
+ if (frac & 0x80) return FLUID_OK;
+ tunedata[i] = (int)frac - 64;
+ }
+ }
+ else
+ {
+ for (i = 0; i < 12; i++)
+ {
+ frac = data[i * 2 + 7];
+ frac2 = data[i * 2 + 8];
+ if (frac & 0x80 || frac2 & 0x80) return FLUID_OK;
+ tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0);
+ }
+ }
+
+ if (fluid_synth_activate_octave_tuning (synth, 0, 0, "SYSEX",
+ tunedata, realtime) == FLUID_FAILED)
+ return FLUID_FAILED;
+
+ if (channels)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ if (channels & (1 << i))
+ fluid_synth_activate_tuning (synth, i, 0, 0, realtime);
+ }
+ }
+
+ if (handled) *handled = 1; //TRUE
+ break;
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_all_notes_off
+ *
+ * put all notes on this channel into released state.
+ */
+int
+fluid_synth_all_notes_off(fluid_synth_t* synth, int chan)
+{
+ int i;
+ fluid_voice_t* voice;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (_PLAYING(voice) && (voice->chan == chan)) {
+ fluid_voice_noteoff(voice);
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_all_sounds_off
+ *
+ * immediately stop all notes on this channel.
+ */
+int
+fluid_synth_all_sounds_off(fluid_synth_t* synth, int chan)
+{
+ int i;
+ fluid_voice_t* voice;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (_PLAYING(voice) && (voice->chan == chan)) {
+ fluid_voice_off(voice);
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_system_reset
+ *
+ * Purpose:
+ * Respond to the MIDI command 'system reset' (0xFF, big red 'panic' button)
+ */
+int
+fluid_synth_system_reset(fluid_synth_t* synth)
+{
+ int i;
+ fluid_voice_t* voice;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (_PLAYING(voice)) {
+ fluid_voice_off(voice);
+ }
+ }
+
+ for (i = 0; i < synth->midi_channels; i++) {
+ fluid_channel_reset(synth->channel[i]);
+ }
+
+ fluid_chorus_reset(synth->chorus);
+ fluid_revmodel_reset(synth->reverb);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_modulate_voices
+ *
+ * tell all synthesis processes on this channel to update their
+ * synthesis parameters after a control change.
+ */
+int
+fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl)
+{
+ int i;
+ fluid_voice_t* voice;
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (voice->chan == chan) {
+ fluid_voice_modulate(voice, is_cc, ctrl);
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_modulate_voices_all
+ *
+ * Tell all synthesis processes on this channel to update their
+ * synthesis parameters after an all control off message (i.e. all
+ * controller have been reset to their default value).
+ */
+int
+fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan)
+{
+ int i;
+ fluid_voice_t* voice;
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (voice->chan == chan) {
+ fluid_voice_modulate_all(voice);
+ }
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Set the MIDI channel pressure controller value.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number
+ * @param val MIDI channel pressure value (7 bit, 0-127)
+ * @return FLUID_OK on success
+ *
+ * Assign to the MIDI channel pressure controller value on a specific MIDI channel
+ * in real time.
+ */
+int
+fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val)
+{
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ /* check the ranges of the arguments */
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ if (synth->verbose) {
+ FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val);
+ }
+
+ /* set the channel pressure value in the channel */
+ fluid_channel_pressure(synth->channel[chan], val);
+
+ return FLUID_OK;
+}
+
+/**
+ * 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
+ */
+int
+fluid_synth_key_pressure(fluid_synth_t* synth, int chan, int key, int val)
+{
+ int result = FLUID_OK;
+ if (key < 0 || key > 127) {
+ return FLUID_FAILED;
+ }
+ if (val < 0 || val > 127) {
+ return 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);
+
+ // fluid_synth_update_key_pressure_LOCAL
+ {
+ fluid_voice_t* voice;
+ int i;
+
+ 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)
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Set the MIDI pitch bend controller value.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number
+ * @param val MIDI pitch bend value (14 bit, 0-16383 with 8192 being center)
+ * @return FLUID_OK on success
+ *
+ * Assign to the MIDI pitch bend controller value on a specific MIDI channel
+ * in real time.
+ */
+int
+fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val)
+{
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ /* check the ranges of the arguments */
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ if (synth->verbose) {
+ FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val);
+ }
+
+ /* set the pitch-bend value in the channel */
+ fluid_channel_pitch_bend(synth->channel[chan], val);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_pitch_bend
+ */
+int
+fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend)
+{
+ /* check the ranges of the arguments */
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ *ppitch_bend = synth->channel[chan]->pitch_bend;
+ return FLUID_OK;
+}
+
+/*
+ * Fluid_synth_pitch_wheel_sens
+ */
+int
+fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val)
+{
+
+ /* check the ranges of the arguments */
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ if (synth->verbose) {
+ FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val);
+ }
+
+ /* set the pitch-bend value in the channel */
+ fluid_channel_pitch_wheel_sens(synth->channel[chan], val);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_get_pitch_wheel_sens
+ *
+ * Note : this function was added after version 1.0 API freeze.
+ * So its API is not in the synth.h file. It should be added in some later
+ * version of fluidsynth. Maybe v2.0 ? -- Antoine Schmitt May 2003
+ */
+
+int
+fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval)
+{
+
+ // check the ranges of the arguments
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ // get the pitch-bend value in the channel
+ *pval = synth->channel[chan]->pitch_wheel_sensitivity;
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_get_preset
+ */
+fluid_preset_t*
+fluid_synth_get_preset(fluid_synth_t* synth, unsigned int sfontnum,
+ unsigned int banknum, unsigned int prognum)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_sfont_t* sfont = NULL;
+ int offset;
+
+ sfont = fluid_synth_get_sfont_by_id(synth, sfontnum);
+
+ if (sfont != NULL) {
+ offset = fluid_synth_get_bank_offset(synth, sfontnum);
+ preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum);
+ if (preset != NULL) {
+ return preset;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * fluid_synth_get_preset2
+ */
+fluid_preset_t*
+fluid_synth_get_preset2(fluid_synth_t* synth, char* sfont_name,
+ unsigned int banknum, unsigned int prognum)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_sfont_t* sfont = NULL;
+ int offset;
+
+ sfont = fluid_synth_get_sfont_by_name(synth, sfont_name);
+
+ if (sfont != NULL) {
+ offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont));
+ preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum);
+ if (preset != NULL) {
+ return preset;
+ }
+ }
+ return NULL;
+}
+
+fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth,
+ unsigned int banknum,
+ unsigned int prognum)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_sfont_t* sfont = NULL;
+ fluid_list_t* list = synth->sfont;
+ int offset;
+
+ while (list) {
+
+ sfont = (fluid_sfont_t*) fluid_list_get(list);
+ offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont));
+ preset = fluid_sfont_get_preset(sfont, banknum - offset, prognum);
+
+ if (preset != NULL) {
+ preset->sfont = sfont; /* FIXME */
+ return preset;
+ }
+
+ list = fluid_list_next(list);
+
+ }
+ return NULL;
+}
+
+
+/*
+ * fluid_synth_program_change
+ */
+int
+fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_channel_t* channel;
+ unsigned int banknum;
+ unsigned int sfont_id;
+ int subst_bank, subst_prog;
+
+ if ((prognum < 0) || (prognum >= FLUID_NUM_PROGRAMS) ||
+ (chan < 0) || (chan >= synth->midi_channels))
+ {
+ FLUID_LOG(FLUID_ERR, "Index out of range (chan=%d, prog=%d)", chan, prognum);
+ return FLUID_FAILED;
+ }
+
+ channel = synth->channel[chan];
+ banknum = fluid_channel_get_banknum(channel);
+
+ /* inform the channel of the new program number */
+ fluid_channel_set_prognum(channel, prognum);
+
+ if (synth->verbose)
+ FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum);
+
+ if (channel->channum == 9 && fluid_settings_str_equal(synth->settings, "synth.drums-channel.active", "yes")) {
+ preset = fluid_synth_find_preset(synth, DRUM_INST_BANK, prognum);
+ }
+ else
+ {
+ preset = fluid_synth_find_preset(synth, banknum, prognum);
+ }
+
+ /* Fallback to another preset if not found */
+ if (!preset)
+ {
+ subst_bank = banknum;
+ subst_prog = prognum;
+
+ /* Melodic instrument? */
+ if (banknum != DRUM_INST_BANK)
+ {
+ subst_bank = 0;
+
+ /* Fallback first to bank 0:prognum */
+ preset = fluid_synth_find_preset(synth, 0, prognum);
+
+ /* Fallback to first preset in bank 0 */
+ if (!preset && prognum != 0)
+ {
+ preset = fluid_synth_find_preset(synth, 0, 0);
+ subst_prog = 0;
+ }
+ }
+ else /* Percussion: Fallback to preset 0 in percussion bank */
+ {
+ preset = fluid_synth_find_preset(synth, DRUM_INST_BANK, 0);
+ subst_prog = 0;
+ }
+
+ 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);
+ }
+
+ sfont_id = preset? fluid_sfont_get_id(preset->sfont) : 0;
+ fluid_channel_set_sfontnum(channel, sfont_id);
+ fluid_channel_set_preset(channel, preset);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_bank_select
+ */
+int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank)
+{
+ if ((chan >= 0) && (chan < synth->midi_channels)) {
+ fluid_channel_set_banknum(synth->channel[chan], bank);
+ return FLUID_OK;
+ }
+ return FLUID_FAILED;
+}
+
+
+/*
+ * fluid_synth_sfont_select
+ */
+int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id)
+{
+ if ((chan >= 0) && (chan < synth->midi_channels)) {
+ fluid_channel_set_sfontnum(synth->channel[chan], sfont_id);
+ return FLUID_OK;
+ }
+ return FLUID_FAILED;
+}
+
+/*
+ * fluid_synth_get_program
+ */
+int
+fluid_synth_get_program(fluid_synth_t* synth, int chan,
+ unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num)
+{
+ fluid_channel_t* channel;
+ if ((chan >= 0) && (chan < synth->midi_channels)) {
+ channel = synth->channel[chan];
+ *sfont_id = fluid_channel_get_sfontnum(channel);
+ *bank_num = fluid_channel_get_banknum(channel);
+ *preset_num = fluid_channel_get_prognum(channel);
+ return FLUID_OK;
+ }
+ return FLUID_FAILED;
+}
+
+/*
+ * fluid_synth_program_select
+ */
+int fluid_synth_program_select(fluid_synth_t* synth,
+ int chan,
+ unsigned int sfont_id,
+ unsigned int bank_num,
+ unsigned int preset_num)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_channel_t* channel;
+
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan);
+ 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);
+ return FLUID_FAILED;
+ }
+
+ /* inform the channel of the new bank and program number */
+ fluid_channel_set_sfontnum(channel, sfont_id);
+ fluid_channel_set_banknum(channel, bank_num);
+ fluid_channel_set_prognum(channel, preset_num);
+
+ fluid_channel_set_preset(channel, preset);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_program_select2
+ */
+int fluid_synth_program_select2(fluid_synth_t* synth,
+ int chan,
+ char* sfont_name,
+ unsigned int bank_num,
+ unsigned int preset_num)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_channel_t* channel;
+ fluid_sfont_t* sfont = NULL;
+ int offset;
+
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_ERR, "Channel number out of range (chan=%d)", chan);
+ return FLUID_FAILED;
+ }
+ channel = synth->channel[chan];
+
+ sfont = fluid_synth_get_sfont_by_name(synth, sfont_name);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Could not find SoundFont %s", sfont_name);
+ return FLUID_FAILED;
+ }
+
+ offset = fluid_synth_get_bank_offset(synth, fluid_sfont_get_id(sfont));
+ preset = fluid_sfont_get_preset(sfont, bank_num - offset, 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);
+ return FLUID_FAILED;
+ }
+
+ /* inform the channel of the new bank and program number */
+ fluid_channel_set_sfontnum(channel, fluid_sfont_get_id(sfont));
+ fluid_channel_set_banknum(channel, bank_num);
+ fluid_channel_set_prognum(channel, preset_num);
+
+ fluid_channel_set_preset(channel, preset);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_update_presets
+ */
+void fluid_synth_update_presets(fluid_synth_t* synth)
+{
+ int chan;
+ fluid_channel_t* channel;
+
+ for (chan = 0; chan < synth->midi_channels; chan++) {
+ channel = synth->channel[chan];
+ fluid_channel_set_preset(channel,
+ fluid_synth_get_preset(synth,
+ fluid_channel_get_sfontnum(channel),
+ fluid_channel_get_banknum(channel),
+ fluid_channel_get_prognum(channel)));
+ }
+}
+
+
+/*
+ * fluid_synth_update_gain
+ */
+int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value)
+{
+ fluid_synth_set_gain(synth, (float) value);
+ return 0;
+}
+
+/*
+ * fluid_synth_set_gain
+ */
+void fluid_synth_set_gain(fluid_synth_t* synth, float gain)
+{
+ int i;
+
+ fluid_clip(gain, 0.0f, 10.0f);
+ synth->gain = gain;
+
+ for (i = 0; i < synth->polyphony; i++) {
+ fluid_voice_t* voice = synth->voice[i];
+ if (_PLAYING(voice)) {
+ fluid_voice_set_gain(voice, gain);
+ }
+ }
+}
+
+/*
+ * fluid_synth_get_gain
+ */
+float fluid_synth_get_gain(fluid_synth_t* synth)
+{
+ return synth->gain;
+}
+
+/*
+ * fluid_synth_update_polyphony
+ */
+int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value)
+{
+ fluid_synth_set_polyphony(synth, value);
+ return 0;
+}
+
+/*
+ * fluid_synth_set_polyphony
+ */
+int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony)
+{
+ int i;
+
+ if (polyphony < 1 || polyphony > synth->nvoice) {
+ return FLUID_FAILED;
+ }
+
+ /* turn off any voices above the new limit */
+ for (i = polyphony; i < synth->nvoice; i++) {
+ fluid_voice_t* voice = synth->voice[i];
+ if (_PLAYING(voice)) {
+ fluid_voice_off(voice);
+ }
+ }
+
+ synth->polyphony = polyphony;
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_get_polyphony
+ */
+int fluid_synth_get_polyphony(fluid_synth_t* synth)
+{
+ return synth->polyphony;
+}
+
+/*
+ * fluid_synth_get_internal_buffer_size
+ */
+int fluid_synth_get_internal_bufsize(fluid_synth_t* synth)
+{
+ return FLUID_BUFSIZE;
+}
+
+/*
+ * fluid_synth_program_reset
+ *
+ * Resend a bank select and a program change for every channel. This
+ * function is called mainly after a SoundFont has been loaded,
+ * unloaded or reloaded. */
+int
+fluid_synth_program_reset(fluid_synth_t* synth)
+{
+ int i;
+ /* try to set the correct presets */
+ for (i = 0; i < synth->midi_channels; i++){
+ fluid_synth_program_change(synth, i, fluid_channel_get_prognum(synth->channel[i]));
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_synth_set_reverb_preset
+ */
+int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num)
+{
+ int i = 0;
+ while (revmodel_preset[i].name != NULL) {
+ if (i == num) {
+ fluid_revmodel_setroomsize(synth->reverb, revmodel_preset[i].roomsize);
+ fluid_revmodel_setdamp(synth->reverb, revmodel_preset[i].damp);
+ fluid_revmodel_setwidth(synth->reverb, revmodel_preset[i].width);
+ fluid_revmodel_setlevel(synth->reverb, revmodel_preset[i].level);
+ return FLUID_OK;
+ }
+ i++;
+ }
+ return FLUID_FAILED;
+}
+
+/*
+ * fluid_synth_set_reverb
+ */
+void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping,
+ double width, double level)
+{
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ fluid_revmodel_setroomsize(synth->reverb, roomsize);
+ fluid_revmodel_setdamp(synth->reverb, damping);
+ fluid_revmodel_setwidth(synth->reverb, width);
+ fluid_revmodel_setlevel(synth->reverb, level);
+}
+
+/*
+ * fluid_synth_set_chorus
+ */
+void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
+ double speed, double depth_ms, int type)
+{
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ fluid_chorus_set_nr(synth->chorus, nr);
+ fluid_chorus_set_level(synth->chorus, (fluid_real_t)level);
+ fluid_chorus_set_speed_Hz(synth->chorus, (fluid_real_t)speed);
+ fluid_chorus_set_depth_ms(synth->chorus, (fluid_real_t)depth_ms);
+ fluid_chorus_set_type(synth->chorus, type);
+ fluid_chorus_update(synth->chorus);
+}
+
+/******************************************************
+
+#define COMPRESS 1
+#define COMPRESS_X1 4.0
+#define COMPRESS_Y1 0.6
+#define COMPRESS_X2 10.0
+#define COMPRESS_Y2 1.0
+
+ len2 = 2 * len;
+ alpha1 = COMPRESS_Y1 / COMPRESS_X1;
+ alpha2 = (COMPRESS_Y2 - COMPRESS_Y1) / (COMPRESS_X2 - COMPRESS_X1);
+ if (COMPRESS_X1 == COMPRESS_Y1) {
+ for (j = 0; j < len2; j++) {
+ if (buf[j] > COMPRESS_X1) {
+ if (buf[j] > COMPRESS_X2) {
+ buf[j] = COMPRESS_Y2;
+ } else {
+ buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1);
+ }
+ } else if (buf[j] < -COMPRESS_X1) {
+ if (buf[j] < -COMPRESS_X2) {
+ buf[j] = -COMPRESS_Y2;
+ } else {
+ buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1);
+ }
+ }
+ }
+ } else {
+ for (j = 0; j < len2; j++) {
+ if ((buf[j] >= -COMPRESS_X1) && (buf[j] <= COMPRESS_X1)) {
+ buf[j] *= alpha1;
+ } else if (buf[j] > COMPRESS_X1) {
+ if (buf[j] > COMPRESS_X2) {
+ buf[j] = COMPRESS_Y2;
+ } else {
+ buf[j] = COMPRESS_Y1 + alpha2 * (buf[j] - COMPRESS_X1);
+ }
+ } else {
+ if (buf[j] < -COMPRESS_X2) {
+ buf[j] = -COMPRESS_Y2;
+ } else {
+ buf[j] = -COMPRESS_Y1 + alpha2 * (buf[j] + COMPRESS_X1);
+ }
+ }
+ }
+ }
+
+***************************************************/
+
+/*
+ * fluid_synth_nwrite_float
+ */
+int
+fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
+ float** left, float** right,
+ float** fx_left, float** fx_right)
+{
+ fluid_real_t** left_in = synth->left_buf;
+ fluid_real_t** right_in = synth->right_buf;
+ int i, num, available, count, bytes;
+
+ /* make sure we're playing */
+ if (synth->state != FLUID_SYNTH_PLAYING) {
+ return 0;
+ }
+
+ /* First, take what's still available in the buffer */
+ count = 0;
+ num = synth->cur;
+ if (synth->cur < FLUID_BUFSIZE) {
+ available = FLUID_BUFSIZE - synth->cur;
+
+ num = (available > len)? len : available;
+ bytes = num * sizeof(float);
+
+ for (i = 0; i < synth->audio_channels; i++) {
+ FLUID_MEMCPY(left[i], left_in[i] + synth->cur, bytes);
+ FLUID_MEMCPY(right[i], right_in[i] + synth->cur, bytes);
+ }
+ 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_synth_one_block(synth, 1);
+
+ num = (FLUID_BUFSIZE > len - count)? len - count : FLUID_BUFSIZE;
+ bytes = num * sizeof(float);
+
+ for (i = 0; i < synth->audio_channels; i++) {
+ FLUID_MEMCPY(left[i] + count, left_in[i], bytes);
+ FLUID_MEMCPY(right[i] + count, right_in[i], bytes);
+ }
+
+ count += num;
+ }
+
+ synth->cur = num;
+
+/* printf("CPU: %.2f\n", synth->cpu_load); */
+
+ return 0;
+}
+
+
+int fluid_synth_process(fluid_synth_t* synth, int len,
+ int nin, float** in,
+ int nout, float** out)
+{
+ if (nout==2) {
+ return fluid_synth_write_float(synth, len, out[0], 0, 1, out[1], 0, 1);
+ }
+ else {
+ float **left, **right;
+ int i;
+ left = FLUID_ARRAY(float*, nout/2);
+ right = FLUID_ARRAY(float*, nout/2);
+ for(i=0; ileft_buf[0];
+ fluid_real_t* right_in = synth->right_buf[0];
+
+ /* make sure we're playing */
+ if (synth->state != FLUID_SYNTH_PLAYING) {
+ return 0;
+ }
+
+ l = synth->cur;
+
+ for (i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr) {
+ /* fill up the buffers as needed */
+ if (l == FLUID_BUFSIZE) {
+ fluid_synth_one_block(synth, 0);
+ l = 0;
+ }
+
+ left_out[j] = (float) left_in[l];
+ right_out[k] = (float) right_in[l];
+ }
+
+ synth->cur = l;
+
+/* printf("CPU: %.2f\n", synth->cpu_load); */
+
+ return 0;
+}
+
+#define DITHER_SIZE 48000
+#define DITHER_CHANNELS 2
+
+static float rand_table[DITHER_CHANNELS][DITHER_SIZE];
+
+static void init_dither(void)
+{
+ float d, dp;
+ int c, i;
+
+ for (c = 0; c < DITHER_CHANNELS; c++) {
+ dp = 0;
+ for (i = 0; i < DITHER_SIZE-1; i++) {
+ d = rand() / (float)RAND_MAX - 0.5f;
+ rand_table[c][i] = d - dp;
+ dp = d;
+ }
+ rand_table[c][DITHER_SIZE-1] = 0 - dp;
+ }
+}
+
+/* A portable replacement for roundf(), seems it may actually be faster too! */
+//removed inline
+static int
+roundi (float x)
+{
+ if (x >= 0.0f)
+ return (int)(x+0.5f);
+ else
+ return (int)(x-0.5f);
+}
+
+/*
+ * fluid_synth_write_s16
+ */
+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 = synth->left_buf[0];
+ fluid_real_t* right_in = synth->right_buf[0];
+ fluid_real_t left_sample;
+ fluid_real_t right_sample;
+ int di = synth->dither_index;
+
+ /* make sure we're playing */
+ if (synth->state != FLUID_SYNTH_PLAYING) {
+ return 0;
+ }
+
+ cur = synth->cur;
+
+ for (i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr) {
+
+ /* fill up the buffers as needed */
+ if (cur == FLUID_BUFSIZE) {
+ fluid_synth_one_block(synth, 0);
+ cur = 0;
+ }
+
+ left_sample = roundi (left_in[cur] * 32766.0f + rand_table[0][di]);
+ right_sample = roundi (right_in[cur] * 32766.0f + rand_table[1][di]);
+
+ di++;
+ if (di >= DITHER_SIZE) di = 0;
+
+ /* digital clipping */
+ if (left_sample > 32767.0f) left_sample = 32767.0f;
+ if (left_sample < -32768.0f) left_sample = -32768.0f;
+ if (right_sample > 32767.0f) right_sample = 32767.0f;
+ if (right_sample < -32768.0f) right_sample = -32768.0f;
+
+ left_out[j] = (signed short) left_sample;
+ right_out[k] = (signed short) right_sample;
+ }
+
+ synth->cur = cur;
+ synth->dither_index = di; /* keep dither buffer continous */
+
+/* printf("CPU: %.2f\n", synth->cpu_load); */
+
+ return 0;
+}
+
+/*
+ * fluid_synth_dither_s16
+ * Converts stereo floating point sample data to signed 16 bit data with
+ * dithering. 'dither_index' parameter is a caller supplied pointer to an
+ * integer which should be initialized to 0 before the first call and passed
+ * unmodified to additional calls which are part of the same synthesis output.
+ * Only used internally currently.
+ */
+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;
+
+ for (i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) {
+
+ left_sample = roundi (lin[i] * 32766.0f + rand_table[0][di]);
+ right_sample = roundi (rin[i] * 32766.0f + rand_table[1][di]);
+
+ di++;
+ if (di >= DITHER_SIZE) di = 0;
+
+ /* digital clipping */
+ if (left_sample > 32767.0f) left_sample = 32767.0f;
+ if (left_sample < -32768.0f) left_sample = -32768.0f;
+ if (right_sample > 32767.0f) right_sample = 32767.0f;
+ if (right_sample < -32768.0f) right_sample = -32768.0f;
+
+ left_out[j] = (signed short) left_sample;
+ right_out[k] = (signed short) right_sample;
+ }
+
+ *dither_index = di; /* keep dither buffer continous */
+}
+
+/*
+ * fluid_synth_one_block
+ */
+int
+fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
+{
+ int i, auchan;
+ fluid_voice_t* voice;
+ fluid_real_t* left_buf;
+ fluid_real_t* right_buf;
+ fluid_real_t* reverb_buf;
+ fluid_real_t* chorus_buf;
+ int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t);
+
+/* fluid_mutex_lock(synth->busy); /\* Here comes the audio thread. Lock the synth. *\/ */
+
+ /* clean the audio buffers */
+ for (i = 0; i < synth->nbuf; i++) {
+ FLUID_MEMSET(synth->left_buf[i], 0, byte_size);
+ FLUID_MEMSET(synth->right_buf[i], 0, byte_size);
+ }
+
+ for (i = 0; i < synth->effects_channels; i++) {
+ FLUID_MEMSET(synth->fx_left_buf[i], 0, byte_size);
+ FLUID_MEMSET(synth->fx_right_buf[i], 0, byte_size);
+ }
+
+ /* 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 = synth->with_reverb ? synth->fx_left_buf[0] : NULL;
+ chorus_buf = synth->with_chorus ? synth->fx_left_buf[1] : NULL;
+
+ /* call all playing synthesis processes */
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+
+ if (_PLAYING(voice)) {
+ /* 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.
+ */
+ auchan = fluid_channel_get_num(fluid_voice_get_channel(voice));
+ auchan %= synth->audio_groups;
+ left_buf = synth->left_buf[auchan];
+ right_buf = synth->right_buf[auchan];
+
+ fluid_voice_write(voice, left_buf, right_buf, reverb_buf, chorus_buf);
+ }
+ }
+
+ /* if multi channel output, don't mix the output of the chorus and
+ reverb in the final output. The effects outputs are send
+ separately. */
+
+ if (do_not_mix_fx_to_out) {
+
+ /* send to reverb */
+ if (reverb_buf) {
+ fluid_revmodel_processreplace(synth->reverb, reverb_buf,
+ synth->fx_left_buf[0], synth->fx_right_buf[0]);
+ }
+
+ /* send to chorus */
+ if (chorus_buf) {
+ fluid_chorus_processreplace(synth->chorus, chorus_buf,
+ synth->fx_left_buf[1], synth->fx_right_buf[1]);
+ }
+
+ } else {
+
+ /* send to reverb */
+ if (reverb_buf) {
+ fluid_revmodel_processmix(synth->reverb, reverb_buf,
+ synth->left_buf[0], synth->right_buf[0]);
+ }
+
+ /* send to chorus */
+ if (chorus_buf) {
+ fluid_chorus_processmix(synth->chorus, chorus_buf,
+ synth->left_buf[0], synth->right_buf[0]);
+ }
+ }
+
+
+#ifdef LADSPA
+ /* Run the signal through the LADSPA Fx unit */
+ fluid_LADSPA_run(synth->LADSPA_FxUnit, synth->left_buf, synth->right_buf, synth->fx_left_buf, synth->fx_right_buf);
+ fluid_check_fpe("LADSPA");
+#endif
+
+ synth->ticks += FLUID_BUFSIZE;
+
+ /* Testcase, that provokes a denormal floating point error */
+#if 0
+ {float num=1;while (num != 0){num*=0.5;};};
+#endif
+
+/* fluid_mutex_unlock(synth->busy); /\* Allow other threads to touch the synth *\/ */
+
+ return 0;
+}
+
+
+/*
+ * fluid_synth_free_voice_by_kill
+ *
+ * selects a voice for killing. the selection algorithm is a refinement
+ * of the algorithm previously in fluid_synth_alloc_voice.
+ */
+fluid_voice_t*
+fluid_synth_free_voice_by_kill(fluid_synth_t* synth)
+{
+ int i;
+ fluid_real_t best_prio = 999999.;
+ fluid_real_t this_voice_prio;
+ fluid_voice_t* voice;
+ int best_voice_index=-1;
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ for (i = 0; i < synth->polyphony; i++) {
+
+ voice = synth->voice[i];
+
+ /* safeguard against an available voice. */
+ if (_AVAILABLE(voice)) {
+ return voice;
+ }
+
+ /* Determine, how 'important' a voice is.
+ * Start with an arbitrary number */
+ this_voice_prio = 10000.;
+
+ /* Is this voice on the drum channel?
+ * Then it is very important.
+ * Also, forget about the released-note condition:
+ * Typically, drum notes are triggered only very briefly, they run most
+ * of the time in release phase.
+ */
+ if (_RELEASED(voice)){
+ /* The key for this voice has been released. Consider it much less important
+ * than a voice, which is still held.
+ */
+ this_voice_prio -= 2000.;
+ }
+
+ if (_SUSTAINED(voice)){
+ /* The sustain pedal is held down on this channel.
+ * 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 -= 1000;
+ }
+
+ /* 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 subtract the age of the voice from the priority - an older voice is just a little
+ * bit less important than a younger voice.
+ * This is a number between roughly 0 and 100.*/
+ this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice));
+
+ /* take a rough estimate of loudness into account. Louder voices are more important. */
+ if (voice->volenv_section != FLUID_VOICE_ENVATTACK){
+ this_voice_prio += voice->volenv_val * 1000.;
+ }
+
+ /* 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_voice_off(voice);
+
+ return voice;
+}
+
+/*
+ * fluid_synth_alloc_voice
+ */
+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;
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ /* 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) {
+ voice = fluid_synth_free_voice_by_kill(synth);
+ }
+
+ if (voice == NULL) {
+ FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key);
+ return NULL;
+ }
+
+ 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\t%.3f\t%d",
+ chan, key, vel, synth->storeid,
+ (float) synth->ticks / 44100.0f,
+ 0.0f,
+ k);
+ }
+
+ if (chan >= 0) {
+ channel = synth->channel[chan];
+ } else {
+ FLUID_LOG(FLUID_WARN, "Channel should be valid");
+ return NULL;
+ }
+
+ if (fluid_voice_init(voice, sample, channel, key, vel,
+ synth->storeid, synth->ticks, synth->gain) != FLUID_OK) {
+ FLUID_LOG(FLUID_WARN, "Failed to initialize voice");
+ 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 */
+
+ return voice;
+}
+
+/*
+ * fluid_synth_kill_by_exclusive_class
+ */
+void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* new_voice)
+{
+ /** Kill all voices on a given channel, which belong into
+ excl_class. This function is called by a SoundFont's preset in
+ response to a noteon event. If one noteon event results in
+ several voice processes (stereo samples), ignore_ID must name
+ the voice ID of the first generated voice (so that it is not
+ stopped). The first voice uses ignore_ID=-1, which will
+ terminate all voices on a channel belonging into the exclusive
+ class excl_class.
+ */
+
+ int i;
+ int excl_class = _GEN(new_voice,GEN_EXCLUSIVECLASS);
+
+ /* Check if the voice belongs to an exclusive class. In that case,
+ previous notes from the same class are released. */
+
+ /* Excl. class 0: No exclusive class */
+ if (excl_class == 0) {
+ return;
+ }
+
+ // FLUID_LOG(FLUID_INFO, "Voice belongs to exclusive class (class=%d, ignore_id=%d)", excl_class, ignore_ID);
+
+ /* 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];
+
+ /* Existing voice does not play? Leave it alone. */
+ if (!_PLAYING(existing_voice)) {
+ continue;
+ }
+
+ /* An exclusive class is valid for a whole channel (or preset).
+ * Is the voice on a different channel? Leave it alone. */
+ if (existing_voice->chan != new_voice->chan) {
+ continue;
+ }
+
+ /* Existing voice has a different (or no) exclusive class? Leave it alone. */
+ if ((int)_GEN(existing_voice, GEN_EXCLUSIVECLASS) != excl_class) {
+ continue;
+ }
+
+ /* Existing voice is a voice process belonging to this noteon
+ * event (for example: stereo sample)? Leave it alone. */
+ if (fluid_voice_get_id(existing_voice) == fluid_voice_get_id(new_voice)) {
+ continue;
+ }
+
+ // FLUID_LOG(FLUID_INFO, "Releasing previous voice of exclusive class (class=%d, id=%d)",
+ // (int)_GEN(existing_voice, GEN_EXCLUSIVECLASS), (int)fluid_voice_get_id(existing_voice));
+
+ fluid_voice_kill_excl(existing_voice);
+ };
+};
+
+/*
+ * fluid_synth_start_voice
+ */
+void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice)
+{
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ /* 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(synth, voice);
+
+ /* Start the new voice */
+
+ fluid_voice_start(voice);
+}
+
+/*
+ * fluid_synth_add_sfloader
+ */
+void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader)
+{
+ synth->loaders = fluid_list_prepend(synth->loaders, loader);
+}
+
+
+/*
+ * fluid_synth_sfload
+ */
+int
+fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets)
+{
+ fluid_sfont_t* sfont;
+ fluid_list_t* list;
+ fluid_sfloader_t* loader;
+
+#if defined(MACOS9)
+ fluid_synth_sfunload_macos9(synth);
+#endif
+
+ if (filename == NULL) {
+ FLUID_LOG(FLUID_ERR, "Invalid filename");
+ return FLUID_FAILED;
+ }
+
+ for (list = synth->loaders; list; list = fluid_list_next(list)) {
+ loader = (fluid_sfloader_t*) fluid_list_get(list);
+
+ sfont = fluid_sfloader_load(loader, filename);
+ if (sfont == NULL)
+ return -1;
+
+ sfont->id = ++synth->sfont_id;
+ synth->sfont = fluid_list_prepend(synth->sfont, sfont);
+
+ if (reset_presets) {
+ fluid_synth_program_reset(synth);
+ }
+ return (int) sfont->id;
+ }
+
+ FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
+ return -1;
+}
+
+
+/*
+ * fluid_synth_sfunload_macos9
+ */
+void fluid_synth_sfunload_macos9(fluid_synth_t* synth)
+{
+#if defined(MACOS9)
+ fluid_list_t *list, *next;
+ fluid_sfont_t* sfont;
+
+ list = synth->unloading;
+ while (list) {
+ next = fluid_list_next(list);
+ sfont = (fluid_sfont_t*) fluid_list_get(list);
+ if (delete_fluid_sfont(sfont) == 0) {
+ synth->unloading = fluid_list_remove(synth->unloading, sfont);
+ }
+ list = next;
+ }
+#endif
+}
+
+/*
+ * fluid_synth_sfunload
+ */
+int
+fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets)
+{
+ fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, id);
+
+#if defined(MACOS9)
+ fluid_synth_sfunload_macos9(synth);
+#endif
+
+ if (!sfont) {
+ FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
+ return FLUID_FAILED;
+ }
+
+ /* remove the SoundFont from the list */
+ synth->sfont = fluid_list_remove(synth->sfont, sfont);
+
+ /* reset the presets for all channels */
+ if (reset_presets) {
+ fluid_synth_program_reset(synth);
+ } else {
+ fluid_synth_update_presets(synth);
+ }
+
+ if (delete_fluid_sfont(sfont) != 0) {
+#if defined(MACOS9)
+ synth->unloading = fluid_list_prepend(synth->unloading, sfont);
+#else
+ int r = delete_fluid_sfont(sfont);
+ if (r == 0) {
+ FLUID_LOG(FLUID_DBG,"Unloaded SoundFont");
+ }
+#endif
+ }
+
+ return FLUID_OK;
+}
+
+/* fluid_synth_sfreload
+ *
+ */
+int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id)
+{
+ char filename[1024];
+ fluid_sfont_t* sfont;
+ int index = 0;
+ fluid_list_t *list;
+ fluid_sfloader_t* loader;
+
+
+ sfont = fluid_synth_get_sfont_by_id(synth, id);
+ if (!sfont) {
+ FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id);
+ return FLUID_FAILED;
+ }
+
+ /* find the index of the SoundFont */
+ list = synth->sfont;
+ while (list) {
+ if (sfont == (fluid_sfont_t*) fluid_list_get(list)) {
+ break;
+ }
+ list = fluid_list_next(list);
+ index++;
+ }
+
+ /* keep a copy of the SoundFont's filename */
+ FLUID_STRCPY(filename, fluid_sfont_get_name(sfont));
+
+ if (fluid_synth_sfunload(synth, id, 0) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ for (list = synth->loaders; list; list = fluid_list_next(list)) {
+ loader = (fluid_sfloader_t*) fluid_list_get(list);
+
+ sfont = fluid_sfloader_load(loader, filename);
+
+ if (sfont != NULL) {
+
+ sfont->id = id;
+
+ /* insert the sfont at the same index */
+ synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont);
+
+ /* reset the presets for all channels */
+ fluid_synth_update_presets(synth);
+
+ return sfont->id;
+ }
+ }
+
+ FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename);
+ return -1;
+}
+
+
+/*
+ * fluid_synth_add_sfont
+ */
+int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
+{
+ sfont->id = ++synth->sfont_id;
+
+ /* insert the sfont as the first one on the list */
+ synth->sfont = fluid_list_prepend(synth->sfont, sfont);
+
+ /* reset the presets for all channels */
+ fluid_synth_program_reset(synth);
+
+ return sfont->id;
+}
+
+
+/*
+ * fluid_synth_remove_sfont
+ */
+void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont)
+{
+ int sfont_id = fluid_sfont_get_id(sfont);
+
+ synth->sfont = fluid_list_remove(synth->sfont, sfont);
+
+ /* remove a possible bank offset */
+ fluid_synth_remove_bank_offset(synth, sfont_id);
+
+ /* reset the presets for all channels */
+ fluid_synth_program_reset(synth);
+}
+
+
+/* fluid_synth_sfcount
+ *
+ * Returns the number of loaded SoundFonts
+ */
+int
+fluid_synth_sfcount(fluid_synth_t* synth)
+{
+ return fluid_list_size(synth->sfont);
+}
+
+/* fluid_synth_get_sfont
+ *
+ * Returns SoundFont num
+ */
+fluid_sfont_t*
+fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num)
+{
+ return (fluid_sfont_t*) fluid_list_get(fluid_list_nth(synth->sfont, num));
+}
+
+/* fluid_synth_get_sfont_by_id
+ *
+ */
+fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id)
+{
+ fluid_list_t* list = synth->sfont;
+ fluid_sfont_t* sfont;
+
+ while (list) {
+ sfont = (fluid_sfont_t*) fluid_list_get(list);
+ if (fluid_sfont_get_id(sfont) == id) {
+ return sfont;
+ }
+ list = fluid_list_next(list);
+ }
+ return NULL;
+}
+
+/* fluid_synth_get_sfont_by_name
+ *
+ */
+fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name)
+{
+ fluid_list_t* list = synth->sfont;
+ fluid_sfont_t* sfont;
+
+ while (list) {
+ sfont = (fluid_sfont_t*) fluid_list_get(list);
+ if (FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) {
+ return sfont;
+ }
+ list = fluid_list_next(list);
+ }
+ return NULL;
+}
+
+/*
+ * fluid_synth_get_channel_preset
+ */
+fluid_preset_t*
+fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan)
+{
+ if ((chan >= 0) && (chan < synth->midi_channels)) {
+ return fluid_channel_get_preset(synth->channel[chan]);
+ }
+
+ return NULL;
+}
+
+/*
+ * fluid_synth_get_voicelist
+ */
+void
+fluid_synth_get_voicelist(fluid_synth_t* synth, fluid_voice_t* buf[], int bufsize, int ID)
+{
+ int i;
+ int count = 0;
+ for (i = 0; i < synth->polyphony; i++) {
+ fluid_voice_t* voice = synth->voice[i];
+ if (count >= bufsize) {
+ return;
+ }
+
+ if (_PLAYING(voice) && ((int)voice->id == ID || ID < 0)) {
+ buf[count++] = voice;
+ }
+ }
+ if (count >= bufsize) {
+ return;
+ }
+ buf[count++] = NULL;
+}
+
+/* Purpose:
+ * Turns on / off the reverb unit in the synth */
+void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on)
+{
+ synth->with_reverb = on;
+}
+
+/* Purpose:
+ * Turns on / off the chorus unit in the synth */
+void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on)
+{
+ synth->with_chorus = on;
+}
+
+/* Purpose:
+ * Reports the current setting of the chorus unit. */
+int fluid_synth_get_chorus_nr(fluid_synth_t* synth)
+{
+ return fluid_chorus_get_nr(synth->chorus);
+}
+
+double fluid_synth_get_chorus_level(fluid_synth_t* synth)
+{
+ return (double)fluid_chorus_get_level(synth->chorus);
+}
+
+double fluid_synth_get_chorus_speed_Hz(fluid_synth_t* synth)
+{
+ return (double)fluid_chorus_get_speed_Hz(synth->chorus);
+}
+
+double fluid_synth_get_chorus_depth_ms(fluid_synth_t* synth)
+{
+ return (double)fluid_chorus_get_depth_ms(synth->chorus);
+}
+
+int fluid_synth_get_chorus_type(fluid_synth_t* synth)
+{
+ return fluid_chorus_get_type(synth->chorus);
+}
+
+/* Purpose:
+ * Returns the current settings_old of the reverb unit */
+double fluid_synth_get_reverb_roomsize(fluid_synth_t* synth)
+{
+ return (double)fluid_revmodel_getroomsize(synth->reverb);
+}
+
+double fluid_synth_get_reverb_damp(fluid_synth_t* synth)
+{
+ return (double) fluid_revmodel_getdamp(synth->reverb);
+}
+
+double fluid_synth_get_reverb_level(fluid_synth_t* synth)
+{
+ return (double) fluid_revmodel_getlevel(synth->reverb);
+}
+
+double fluid_synth_get_reverb_width(fluid_synth_t* synth)
+{
+ return (double) fluid_revmodel_getwidth(synth->reverb);
+}
+
+/* Purpose:
+ *
+ * If the same note is hit twice on the same channel, then the older
+ * voice process is advanced to the release stage. Using a mechanical
+ * MIDI controller, the only way this can happen is when the sustain
+ * pedal is held. In this case the behaviour implemented here is
+ * natural for many instruments. Note: One noteon event can trigger
+ * several voice processes, for example a stereo sample. Don't
+ * release those...
+ */
+void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key){
+ int i;
+ fluid_voice_t* voice;
+
+/* fluid_mutex_lock(synth->busy); /\* Don't interfere with the audio thread *\/ */
+/* fluid_mutex_unlock(synth->busy); */
+
+ 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)) {
+ fluid_voice_noteoff(voice);
+ }
+ }
+}
+
+/* Purpose:
+ * Sets the interpolation method to use on channel chan.
+ * If chan is < 0, then set the interpolation method on all channels.
+ */
+int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method){
+ int i;
+ for (i = 0; i < synth->midi_channels; i++) {
+ if (synth->channel[i] == NULL){
+ FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!");
+ return FLUID_FAILED;
+ };
+ if (chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan){
+ fluid_channel_set_interp_method(synth->channel[i], interp_method);
+ };
+ };
+ return FLUID_OK;
+};
+
+/* Purpose:
+ * Returns the number of allocated midi channels
+ */
+int
+fluid_synth_count_midi_channels(fluid_synth_t* synth)
+{
+ return synth->midi_channels;
+}
+
+/* Purpose:
+ * Returns the number of allocated audio channels
+ */
+int
+fluid_synth_count_audio_channels(fluid_synth_t* synth)
+{
+ return synth->audio_channels;
+}
+
+/* Purpose:
+ * Returns the number of allocated audio channels
+ */
+int
+fluid_synth_count_audio_groups(fluid_synth_t* synth)
+{
+ return synth->audio_groups;
+}
+
+/* Purpose:
+ * Returns the number of allocated effects channels
+ */
+int
+fluid_synth_count_effects_channels(fluid_synth_t* synth)
+{
+ return synth->effects_channels;
+}
+
+static fluid_tuning_t*
+fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog)
+{
+ if ((bank < 0) || (bank >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Bank number out of range");
+ return NULL;
+ }
+ if ((prog < 0) || (prog >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Program number out of range");
+ return NULL;
+ }
+ if ((synth->tuning == NULL) ||
+ (synth->tuning[bank] == NULL) ||
+ (synth->tuning[bank][prog] == NULL)) {
+ FLUID_LOG(FLUID_WARN, "No tuning at bank %d, prog %d", bank, prog);
+ return NULL;
+ }
+ return synth->tuning[bank][prog];
+}
+
+static fluid_tuning_t*
+fluid_synth_create_tuning(fluid_synth_t* synth, int bank, int prog, const char* name)
+{
+ if ((bank < 0) || (bank >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Bank number out of range");
+ return NULL;
+ }
+ if ((prog < 0) || (prog >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Program number out of range");
+ return NULL;
+ }
+ if (synth->tuning == NULL) {
+ synth->tuning = FLUID_ARRAY(fluid_tuning_t**, 128);
+ if (synth->tuning == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+ 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 NULL;
+ }
+ FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t*));
+ }
+
+ if (synth->tuning[bank][prog] == NULL) {
+ synth->tuning[bank][prog] = new_fluid_tuning(name, bank, prog);
+ if (synth->tuning[bank][prog] == NULL) {
+ return NULL;
+ }
+ }
+
+ if ((fluid_tuning_get_name(synth->tuning[bank][prog]) == NULL)
+ || (FLUID_STRCMP(fluid_tuning_get_name(synth->tuning[bank][prog]), name) != 0)) {
+ fluid_tuning_set_name(synth->tuning[bank][prog], name);
+ }
+
+ return synth->tuning[bank][prog];
+}
+
+int fluid_synth_create_key_tuning(fluid_synth_t* synth,
+ int bank, int prog,
+ const char* name, double* pitch)
+{
+ fluid_tuning_t* tuning = fluid_synth_create_tuning(synth, bank, prog, name);
+ if (tuning == NULL) {
+ return FLUID_FAILED;
+ }
+ if (pitch) {
+ fluid_tuning_set_all(tuning, pitch);
+ }
+ return FLUID_OK;
+}
+
+
+int fluid_synth_create_octave_tuning(fluid_synth_t* synth,
+ int bank, int prog,
+ const char* name, const double* pitch)
+{
+ fluid_tuning_t* tuning;
+
+ if(!(synth != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(bank >= 0 && bank < 128)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(prog >= 0 && prog < 128)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(name != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(pitch != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail
+
+ tuning = fluid_synth_create_tuning(synth, bank, prog, name);
+ if (tuning == NULL) {
+ return FLUID_FAILED;
+ }
+ fluid_tuning_set_octave(tuning, pitch);
+ return FLUID_OK;
+}
+
+/**
+ * Activate an octave tuning on every octave in the MIDI note scale.
+ * @param synth FluidSynth instance
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param name Label name for this tuning
+ * @param pitch Array of pitch values (length of 12 for each note of an octave
+ * starting at note C, values are number of offset cents to add to the normal
+ * tuning amount)
+ * @param apply TRUE to apply new tuning in realtime to existing notes which
+ * are using the replaced tuning (if any), FALSE otherwise
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ */
+int
+fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch, int apply)
+{
+ return fluid_synth_create_octave_tuning(synth,bank,prog,name,pitch);
+}
+
+
+int fluid_synth_tune_notes(fluid_synth_t* synth, int bank, int prog,
+ int len, int *key, double* pitch, int apply)
+{
+ fluid_tuning_t* tuning;
+ int i;
+
+ if(!(synth != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(bank >= 0 && bank < 128)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(prog >= 0 && prog < 128)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(len > 0)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(key != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(pitch != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail
+
+ tuning = fluid_synth_get_tuning (synth, bank, prog);
+
+ if(!tuning)
+ tuning = new_fluid_tuning ("Unnamed", bank, prog);
+
+ if (tuning == NULL) {
+ return FLUID_FAILED;
+ }
+
+ for (i = 0; i < len; i++) {
+ fluid_tuning_set_pitch(tuning, key[i], pitch[i]);
+ }
+
+ return FLUID_OK;
+}
+
+int fluid_synth_select_tuning(fluid_synth_t* synth, int chan,
+ int bank, int prog)
+{
+ fluid_tuning_t* tuning;
+
+ if(!(synth != NULL)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(bank >= 0 && bank < 128)) return FLUID_FAILED; //fluid_return_val_if_fail
+ if(!(prog >= 0 && prog < 128)) return FLUID_FAILED; //fluid_return_val_if_fail
+
+ tuning = fluid_synth_get_tuning(synth, bank, prog);
+
+ if (tuning == NULL) {
+ return FLUID_FAILED;
+ }
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ fluid_channel_set_tuning(synth->channel[chan], synth->tuning[bank][prog]);
+
+ return FLUID_OK;
+}
+
+/**
+ * Activate a tuning scale on a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param bank Tuning bank number (0-127), not related to MIDI instrument bank
+ * @param prog Tuning preset number (0-127), not related to MIDI instrument program
+ * @param apply TRUE to apply tuning change to active notes, FALSE otherwise
+ * @return FLUID_OK on success, FLUID_FAILED otherwise
+ * @since 1.1.0
+ *
+ * NOTE: A default equal tempered scale will be created, if no tuning exists
+ * on the given bank and prog.
+ */
+int
+fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog,
+ int apply)
+{
+ return fluid_synth_select_tuning(synth,chan,bank,prog);
+}
+
+int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan)
+{
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ fluid_channel_set_tuning(synth->channel[chan], NULL);
+
+ return FLUID_OK;
+}
+
+void fluid_synth_tuning_iteration_start(fluid_synth_t* synth)
+{
+ synth->cur_tuning = NULL;
+}
+
+int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog)
+{
+ int b = 0, p = 0;
+
+ if (synth->tuning == NULL) {
+ return 0;
+ }
+
+ if (synth->cur_tuning != NULL) {
+ /* get the next program number */
+ b = fluid_tuning_get_bank(synth->cur_tuning);
+ p = 1 + fluid_tuning_get_prog(synth->cur_tuning);
+ if (p >= 128) {
+ p = 0;
+ b++;
+ }
+ }
+
+ while (b < 128) {
+ if (synth->tuning[b] != NULL) {
+ while (p < 128) {
+ if (synth->tuning[b][p] != NULL) {
+ synth->cur_tuning = synth->tuning[b][p];
+ *bank = b;
+ *prog = p;
+ return 1;
+ }
+ p++;
+ }
+ }
+ p = 0;
+ b++;
+ }
+
+ return 0;
+}
+
+//workaround for snprint on MSVC <2015 from http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf c99_snprintf
+#define vsnprintf c99_vsnprintf
+
+__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
+{
+ int count = -1;
+
+ if (size != 0)
+ count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+ if (count == -1)
+ count = _vscprintf(format, ap);
+
+ return count;
+}
+
+__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
+{
+ int count;
+ va_list ap;
+
+ va_start(ap, format);
+ count = c99_vsnprintf(outBuf, size, format, ap);
+ va_end(ap);
+
+ return count;
+}
+#endif
+
+int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog,
+ char* name, int len, double* pitch)
+{
+ fluid_tuning_t* tuning = fluid_synth_get_tuning(synth, bank, prog);
+
+ if (tuning == NULL) {
+ return FLUID_FAILED;
+ }
+
+ if (name) {
+ snprintf(name, len - 1, "%s", fluid_tuning_get_name(tuning));
+ name[len - 1] = 0; /* make sure the string is null terminated */
+ }
+ if (pitch) {
+ FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double));
+ }
+
+ return FLUID_OK;
+}
+
+fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth)
+{
+ return synth->settings;
+}
+
+int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str)
+{
+ return fluid_settings_setstr(synth->settings, name, str);
+}
+
+int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str)
+{
+ return fluid_settings_getstr(synth->settings, name, str);
+}
+
+int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val)
+{
+ return fluid_settings_setnum(synth->settings, name, val);
+}
+
+int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val)
+{
+ return fluid_settings_getnum(synth->settings, name, val);
+}
+
+int fluid_synth_setint(fluid_synth_t* synth, char* name, int val)
+{
+ return fluid_settings_setint(synth->settings, name, val);
+}
+
+int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val)
+{
+ return fluid_settings_getint(synth->settings, name, val);
+}
+
+int
+fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value)
+{
+ int i;
+ fluid_voice_t* voice;
+
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ if ((param < 0) || (param >= GEN_LAST)) {
+ FLUID_LOG(FLUID_WARN, "Parameter number out of range");
+ return FLUID_FAILED;
+ }
+
+ fluid_channel_set_gen(synth->channel[chan], param, value, 0);
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (voice->chan == chan) {
+ fluid_voice_set_param(voice, param, value, 0);
+ }
+ }
+
+ return FLUID_OK;
+}
+
+/** Change the value of a generator. This function allows to control
+ all synthesis parameters in real-time. The changes are additive,
+ i.e. they add up to the existing parameter value. This function is
+ similar to sending an NRPN message to the synthesizer. The
+ function accepts a float as the value of the parameter. The
+ parameter numbers and ranges are described in the SoundFont 2.01
+ specification, paragraph 8.1.3, page 48. See also
+ 'fluid_gen_type'.
+
+ Using the fluid_synth_set_gen2() function, it is possible to set
+ the absolute value of a generator. This is an extension to the
+ SoundFont standard. If 'absolute' is non-zero, the value of the
+ generator specified in the SoundFont is completely ignored and the
+ generator is fixed to the value passed as argument. To undo this
+ behavior, you must call fluid_synth_set_gen2 again, with
+ 'absolute' set to 0 (and possibly 'value' set to zero).
+
+ If 'normalized' is non-zero, the value is supposed to be
+ normalized between 0 and 1. Before applying the value, it will be
+ scaled and shifted to the range defined in the SoundFont
+ specifications.
+
+ */
+int
+fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param,
+ float value, int absolute, int normalized)
+{
+ int i;
+ fluid_voice_t* voice;
+ float v;
+
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ if ((param < 0) || (param >= GEN_LAST)) {
+ FLUID_LOG(FLUID_WARN, "Parameter number out of range");
+ return FLUID_FAILED;
+ }
+
+ v = (normalized)? fluid_gen_scale(param, value) : value;
+
+ fluid_channel_set_gen(synth->channel[chan], param, v, absolute);
+
+ for (i = 0; i < synth->polyphony; i++) {
+ voice = synth->voice[i];
+ if (voice->chan == chan) {
+ fluid_voice_set_param(voice, param, v, absolute);
+ }
+ }
+
+ return FLUID_OK;
+}
+
+float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param)
+{
+ if ((chan < 0) || (chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return 0.0;
+ }
+
+ if ((param < 0) || (param >= GEN_LAST)) {
+ FLUID_LOG(FLUID_WARN, "Parameter number out of range");
+ return 0.0;
+ }
+
+ return fluid_channel_get_gen(synth->channel[chan], param);
+}
+
+//midi_router disabled
+/* The synth needs to know the router for the command line handlers (they only
+ * supply the synth as argument)
+ */
+//void fluid_synth_set_midi_router(fluid_synth_t* synth, fluid_midi_router_t* router){
+// synth->midi_router=router;
+//};
+
+/* Purpose:
+ * Any MIDI event from the MIDI router arrives here and is handed
+ * to the appropriate function.
+ */
+
+//fluid_synth_handle_midi_event disabled
+
+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)
+{
+ int r;
+
+ /* check the ranges of the arguments */
+ if ((midi_chan < 0) || (midi_chan >= synth->midi_channels)) {
+ FLUID_LOG(FLUID_WARN, "Channel out of range");
+ return FLUID_FAILED;
+ }
+
+ if ((key < 0) || (key >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Key out of range");
+ return FLUID_FAILED;
+ }
+
+ if ((vel <= 0) || (vel >= 128)) {
+ FLUID_LOG(FLUID_WARN, "Velocity out of range");
+ return FLUID_FAILED;
+ }
+
+ //mutex disabled, must render in a single thread
+ //fluid_mutex_lock(synth->busy); /* One at a time, please */
+
+ synth->storeid = id;
+ r = fluid_preset_noteon(preset, synth, midi_chan, key, vel);
+
+ //fluid_mutex_unlock(synth->busy);
+
+ return r;
+}
+
+int fluid_synth_stop(fluid_synth_t* synth, unsigned int id)
+{
+ int i;
+ fluid_voice_t* voice;
+ int status = FLUID_FAILED;
+ int count = 0;
+
+ for (i = 0; i < synth->polyphony; i++) {
+
+ voice = synth->voice[i];
+
+ if (_ON(voice) && (fluid_voice_get_id(voice) == id)) {
+ count++;
+ fluid_voice_noteoff(voice);
+ status = FLUID_OK;
+ }
+ }
+
+ return status;
+}
+
+fluid_bank_offset_t*
+fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id)
+{
+ fluid_list_t* list = synth->bank_offsets;
+ fluid_bank_offset_t* offset;
+
+ while (list) {
+
+ offset = (fluid_bank_offset_t*) fluid_list_get(list);
+ if (offset->sfont_id == sfont_id) {
+ return offset;
+ }
+
+ list = fluid_list_next(list);
+ }
+
+ return NULL;
+}
+
+int
+fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset)
+{
+ fluid_bank_offset_t* bank_offset;
+
+ bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id);
+
+ if (bank_offset == NULL) {
+ bank_offset = FLUID_NEW(fluid_bank_offset_t);
+ if (bank_offset == NULL) {
+ return -1;
+ }
+ bank_offset->sfont_id = sfont_id;
+ bank_offset->offset = offset;
+ synth->bank_offsets = fluid_list_prepend(synth->bank_offsets, bank_offset);
+ } else {
+ bank_offset->offset = offset;
+ }
+
+ return 0;
+}
+
+int
+fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id)
+{
+ fluid_bank_offset_t* bank_offset;
+
+ bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id);
+ return (bank_offset == NULL)? 0 : bank_offset->offset;
+}
+
+void
+fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id)
+{
+ fluid_bank_offset_t* bank_offset;
+
+ bank_offset = fluid_synth_get_bank_offset0(synth, sfont_id);
+ if (bank_offset != NULL) {
+ synth->bank_offsets = fluid_list_remove(synth->bank_offsets, bank_offset);
+ }
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.h
new file mode 100644
index 0000000..ee1f08d
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_synth.h
@@ -0,0 +1,204 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_SYNTH_H
+#define _FLUID_SYNTH_H
+
+
+/***************************************************************
+ *
+ * INCLUDES
+ */
+
+#include "fluid_config.h"
+#include "fluidsynth_priv.h"
+#include "fluid_list.h"
+#include "fluid_rev.h"
+#include "fluid_voice.h"
+#include "fluid_chorus.h"
+#include "fluid_sys.h"
+
+/***************************************************************
+ *
+ * DEFINES
+ */
+#define FLUID_NUM_PROGRAMS 128
+#define DRUM_INST_BANK 128
+
+#if defined(WITH_FLOAT)
+#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_FLOAT
+#else
+#define FLUID_SAMPLE_FORMAT FLUID_SAMPLE_DOUBLE
+#endif
+
+
+/***************************************************************
+ *
+ * ENUM
+ */
+enum fluid_loop {
+ FLUID_UNLOOPED = 0,
+ FLUID_LOOP_DURING_RELEASE = 1,
+ FLUID_NOTUSED = 2,
+ FLUID_LOOP_UNTIL_RELEASE = 3
+};
+
+enum fluid_synth_status
+{
+ FLUID_SYNTH_CLEAN,
+ FLUID_SYNTH_PLAYING,
+ FLUID_SYNTH_QUIET,
+ FLUID_SYNTH_STOPPED
+};
+
+
+typedef struct _fluid_bank_offset_t fluid_bank_offset_t;
+
+struct _fluid_bank_offset_t {
+ int sfont_id;
+ int offset;
+};
+
+
+/*
+ * fluid_synth_t
+ */
+
+struct _fluid_synth_t
+{
+ /* fluid_settings_old_t settings_old; the old synthesizer settings */
+ fluid_settings_t* settings; /** the synthesizer settings */
+ int polyphony; /** maximum polyphony */
+ char with_reverb; /** Should the synth use the built-in reverb unit? */
+ char with_chorus; /** Should the synth use the built-in chorus unit? */
+ char verbose; /** Turn verbose mode on? */
+ char 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 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) */
+ unsigned int state; /** the synthesizer state */
+ unsigned int ticks; /** the number of audio samples since the start */
+
+ fluid_list_t *loaders; /** the soundfont loaders */
+ fluid_list_t* sfont; /** the loaded soundfont */
+ unsigned int sfont_id;
+ fluid_list_t* bank_offsets; /** the offsets of the soundfont banks */
+
+#if defined(MACOS9)
+ fluid_list_t* unloading; /** the soundfonts that need to be unloaded */
+#endif
+
+ double gain; /** master gain */
+ fluid_channel_t** channel; /** the channels */
+ int num_channels; /** the number of channels */
+ int nvoice; /** the length of the synthesis process array */
+ fluid_voice_t** voice; /** the synthesis processes */
+ unsigned int noteid; /** the id is incremented for every new note. it's used for noteoff's */
+ unsigned int storeid;
+ int nbuf; /** How many audio buffers are used? (depends on nr of audio channels / groups)*/
+
+ fluid_real_t** left_buf;
+ fluid_real_t** right_buf;
+ fluid_real_t** fx_left_buf;
+ fluid_real_t** fx_right_buf;
+
+ fluid_revmodel_t* reverb;
+ fluid_chorus_t* chorus;
+ int cur; /** the current sample in the audio buffers to be output */
+ int dither_index; /* current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */
+
+ char outbuf[256]; /** buffer for message output */
+
+ fluid_tuning_t*** tuning; /** 128 banks of 128 programs for the tunings */
+ fluid_tuning_t* cur_tuning; /** current tuning in the iteration */
+
+ unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */
+};
+
+/** returns 1 if the value has been set, 0 otherwise */
+int fluid_synth_setstr(fluid_synth_t* synth, char* name, char* str);
+
+/** returns 1 if the value exists, 0 otherwise */
+int fluid_synth_getstr(fluid_synth_t* synth, char* name, char** str);
+
+/** returns 1 if the value has been set, 0 otherwise */
+int fluid_synth_setnum(fluid_synth_t* synth, char* name, double val);
+
+/** returns 1 if the value exists, 0 otherwise */
+int fluid_synth_getnum(fluid_synth_t* synth, char* name, double* val);
+
+/** returns 1 if the value has been set, 0 otherwise */
+int fluid_synth_setint(fluid_synth_t* synth, char* name, int val);
+
+/** returns 1 if the value exists, 0 otherwise */
+int fluid_synth_getint(fluid_synth_t* synth, char* name, int* val);
+
+
+int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num);
+
+int fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out);
+
+fluid_preset_t* fluid_synth_get_preset(fluid_synth_t* synth,
+ unsigned int sfontnum,
+ unsigned int banknum,
+ unsigned int prognum);
+
+fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth,
+ unsigned int banknum,
+ unsigned int prognum);
+
+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_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl);
+int fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan);
+int fluid_synth_damp_voices(fluid_synth_t* synth, int chan);
+int fluid_synth_kill_voice(fluid_synth_t* synth, fluid_voice_t * voice);
+void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* voice);
+void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key);
+void fluid_synth_sfunload_macos9(fluid_synth_t* synth);
+
+void fluid_synth_print_voice(fluid_synth_t* synth);
+
+/** This function assures that every MIDI channels has a valid preset
+ * (NULL is okay). This function is called after a SoundFont is
+ * unloaded or reloaded. */
+void fluid_synth_update_presets(fluid_synth_t* synth);
+
+
+int fluid_synth_update_gain(fluid_synth_t* synth, char* name, double value);
+int fluid_synth_update_polyphony(fluid_synth_t* synth, char* name, int value);
+
+fluid_bank_offset_t* fluid_synth_get_bank_offset0(fluid_synth_t* synth, int sfont_id);
+void fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id);
+
+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);
+/*
+ * misc
+ */
+
+void fluid_synth_settings(fluid_settings_t* settings);
+
+#endif /* _FLUID_SYNTH_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.c
new file mode 100644
index 0000000..f5345a7
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.c
@@ -0,0 +1,364 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include "fluid_sys.h"
+
+static char fluid_errbuf[512]; /* buffer for error message */
+
+static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL];
+static void* fluid_log_user_data[LAST_LOG_LEVEL];
+static int fluid_log_initialized = 0;
+
+static char* fluid_libname = "fluidsynth";
+
+
+void fluid_sys_config()
+{
+ fluid_log_config();
+}
+
+
+unsigned int fluid_debug_flags = 0;
+
+#if DEBUG
+/*
+ * fluid_debug
+ */
+int fluid_debug(int level, char * fmt, ...)
+{
+ if (fluid_debug_flags & level) {
+ fluid_log_function_t fun;
+ va_list args;
+
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ fun = fluid_log_function[FLUID_DBG];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]);
+ }
+ }
+ return 0;
+}
+#endif
+
+/**
+ * Installs a new log function for a specified log level.
+ * @param level Log level to install handler for.
+ * @param fun Callback function handler to call for logged messages
+ * @param data User supplied data pointer to pass to log function
+ * @return The previously installed function.
+ */
+fluid_log_function_t
+fluid_set_log_function(int level, fluid_log_function_t fun, void* data)
+{
+ fluid_log_function_t old = NULL;
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ old = fluid_log_function[level];
+ fluid_log_function[level] = fun;
+ fluid_log_user_data[level] = data;
+ }
+ return old;
+}
+
+/**
+ * Default log function which prints to the stderr.
+ * @param level Log level
+ * @param message Log message
+ * @param data User supplied data (not used)
+ */
+void
+fluid_default_log_function(int level, char* message, void* data)
+{
+ FILE* out;
+
+#if defined(WIN32)
+ out = stdout;
+#else
+ out = stderr;
+#endif
+
+ if (fluid_log_initialized == 0) {
+ fluid_log_config();
+ }
+
+ switch (level) {
+ case FLUID_PANIC:
+ FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
+ break;
+ case FLUID_ERR:
+ FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
+ break;
+ case FLUID_WARN:
+ FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
+ break;
+ case FLUID_INFO:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ case FLUID_DBG:
+#if DEBUG
+ FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
+#endif
+ break;
+ default:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ }
+ fflush(out);
+}
+
+/*
+ * fluid_init_log
+ */
+void
+fluid_log_config(void)
+{
+ if (fluid_log_initialized == 0) {
+
+ fluid_log_initialized = 1;
+
+ if (fluid_log_function[FLUID_PANIC] == NULL) {
+ fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_ERR] == NULL) {
+ fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_WARN] == NULL) {
+ fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_INFO] == NULL) {
+ fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_DBG] == NULL) {
+ fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL);
+ }
+ }
+}
+
+/**
+ * Print a message to the log.
+ * @param level Log level (#fluid_log_level).
+ * @param fmt Printf style format string for log message
+ * @param ... Arguments for printf 'fmt' message string
+ * @return Always returns -1
+ */
+int
+fluid_log(int level, char* fmt, ...)
+{
+ fluid_log_function_t fun = NULL;
+
+ va_list args;
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ fun = fluid_log_function[level];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[level]);
+ }
+ }
+ return FLUID_FAILED;
+}
+
+/**
+ * An improved strtok, still trashes the input string, but is portable and
+ * thread safe. Also skips token chars at beginning of token string and never
+ * returns an empty token (will return NULL if source ends in token chars though).
+ * NOTE: NOT part of public API
+ * @internal
+ * @param str Pointer to a string pointer of source to tokenize. Pointer gets
+ * updated on each invocation to point to beginning of next token. Note that
+ * token char get's overwritten with a 0 byte. String pointer is set to NULL
+ * when final token is returned.
+ * @param delim String of delimiter chars.
+ * @return Pointer to the next token or NULL if no more tokens.
+ */
+char *fluid_strtok (char **str, char *delim)
+{
+ char *s, *d, *token;
+ char c;
+
+ if (str == NULL || delim == NULL || !*delim)
+ {
+ FLUID_LOG(FLUID_ERR, "Null pointer");
+ return NULL;
+ }
+
+ s = *str;
+ if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */
+
+ /* skip delimiter chars at beginning of token */
+ do
+ {
+ c = *s;
+ if (!c) /* end of source string? */
+ {
+ *str = NULL;
+ return NULL;
+ }
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ s++; /* advance to next source char */
+ break;
+ }
+ }
+ } while (*d); /* while token char match */
+
+ token = s; /* start of token found */
+
+ /* search for next token char or end of source string */
+ for (s = s+1; *s; s++)
+ {
+ c = *s;
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ *s = '\0'; /* overwrite token char with zero byte to terminate token */
+ *str = s+1; /* update str to point to beginning of next token */
+ return token;
+ }
+ }
+ }
+
+ /* we get here only if source string ended */
+ *str = NULL;
+ return token;
+}
+
+/*
+ * fluid_error
+ */
+char*
+fluid_error()
+{
+ return fluid_errbuf;
+}
+
+
+/*
+ *
+ * fluid_is_midifile
+ */
+int
+fluid_is_midifile(char* filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "MThd", 4) == 0;
+}
+
+/*
+ * fluid_is_soundfont
+ *
+ */
+int
+fluid_is_soundfont(char* filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "RIFF", 4) == 0;
+}
+
+/*=============================================================*/
+/* */
+/* Win32 */
+/* */
+/*=============================================================*/
+
+/***************************************************************
+ *
+ * Timer
+ *
+ */
+
+//timer disabled
+
+
+/***************************************************************
+ *
+ * Floating point exceptions
+ *
+ * The floating point exception functions were taken from Ircam's
+ * jMax source code. http://www.ircam.fr/jmax
+ *
+ * FIXME: check in config for i386 machine
+ *
+ * Currently not used. I leave the code here in case we want to pick
+ * this up again some time later.
+ */
+
+
+
+/***************************************************************
+ *
+ * Profiling (Linux, i586 only)
+ *
+ */
+
+
+/***************************************************************
+ *
+ * Threads
+ *
+ */
+
+//thread disabled
+
+
+/***************************************************************
+ *
+ * Sockets
+ *
+ */
+
+//socket disabled
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.h
new file mode 100644
index 0000000..d57a411
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_sys.h
@@ -0,0 +1,141 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+/**
+
+ This header contains a bunch of (mostly) system and machine
+ dependent functions:
+
+ - timers
+ - current time in milliseconds and microseconds
+ - debug logging
+ - profiling
+ - memory locking
+ - checking for floating point exceptions
+
+ */
+
+#ifndef _FLUID_SYS_H
+#define _FLUID_SYS_H
+
+#include "fluidsynth_priv.h"
+
+
+void fluid_sys_config(void);
+void fluid_log_config(void);
+
+
+/*
+ * Utility functions
+ */
+char *fluid_strtok (char **str, char *delim);
+
+
+/**
+
+ Additional debugging system, separate from the log system. This
+ allows to print selected debug messages of a specific subsystem.
+
+ */
+
+extern unsigned int fluid_debug_flags;
+
+#if DEBUG
+
+enum fluid_debug_level {
+ FLUID_DBG_DRIVER = 1
+};
+
+int fluid_debug(int level, char * fmt, ...);
+
+#else
+#define fluid_debug
+#endif
+
+//timer disabled
+
+/**
+
+ Muteces
+
+*/
+
+
+
+/**
+ Threads
+
+*/
+
+
+/**
+ Sockets
+
+*/
+
+
+/**
+
+ Profiling
+ */
+
+
+/**
+ Profile numbers. List all the pieces of code you want to profile
+ here. Be sure to add an entry in the fluid_profile_data table in
+ fluid_sys.c
+*/
+enum {
+ FLUID_PROF_WRITE_S16,
+ 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
+};
+
+
+/* Profiling */
+
+
+/**
+
+ Memory locking
+
+ Memory locking is used to avoid swapping of the large block of
+ sample data.
+ */
+
+
+/**
+
+ Floating point exceptions
+
+ fluid_check_fpe() checks for "unnormalized numbers" and other
+ exceptions of the floating point processsor.
+*/
+
+
+#endif /* _FLUID_SYS_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.c
new file mode 100644
index 0000000..ec1fc6e
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.c
@@ -0,0 +1,144 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include "fluid_tuning.h"
+#include "fluidsynth_priv.h"
+
+
+fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog)
+{
+ fluid_tuning_t* tuning;
+ int i;
+
+ tuning = FLUID_NEW(fluid_tuning_t);
+ if (tuning == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+
+ tuning->name = NULL;
+
+ if (name != NULL) {
+ tuning->name = FLUID_STRDUP(name);
+ }
+
+ tuning->bank = bank;
+ tuning->prog = prog;
+
+ for (i = 0; i < 128; i++) {
+ tuning->pitch[i] = i * 100.0;
+ }
+
+ return tuning;
+}
+
+/* Duplicate a tuning */
+fluid_tuning_t *
+fluid_tuning_duplicate (fluid_tuning_t *tuning)
+{
+ fluid_tuning_t *new_tuning;
+ int i;
+
+ new_tuning = FLUID_NEW (fluid_tuning_t);
+
+ if (!new_tuning) {
+ FLUID_LOG (FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+
+ if (tuning->name)
+ {
+ new_tuning->name = FLUID_STRDUP (tuning->name);
+
+ if (!new_tuning->name)
+ {
+ FLUID_FREE (new_tuning);
+ FLUID_LOG (FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+ }
+ else new_tuning->name = NULL;
+
+ new_tuning->bank = tuning->bank;
+ new_tuning->prog = tuning->prog;
+
+ for (i = 0; i < 128; i++)
+ new_tuning->pitch[i] = tuning->pitch[i];
+
+ return new_tuning;
+}
+
+void delete_fluid_tuning(fluid_tuning_t* tuning)
+{
+ if (tuning == NULL) {
+ return;
+ }
+ if (tuning->name != NULL) {
+ FLUID_FREE(tuning->name);
+ }
+ FLUID_FREE(tuning);
+}
+
+void fluid_tuning_set_name(fluid_tuning_t* tuning, const char* name)
+{
+ if (tuning->name != NULL) {
+ FLUID_FREE(tuning->name);
+ tuning->name = NULL;
+ }
+ if (name != NULL) {
+ tuning->name = FLUID_STRDUP(name);
+ }
+}
+
+char* fluid_tuning_get_name(fluid_tuning_t* tuning)
+{
+ return tuning->name;
+}
+
+void fluid_tuning_set_key(fluid_tuning_t* tuning, int key, double pitch)
+{
+ tuning->pitch[key] = pitch;
+}
+
+void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv)
+{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12];
+ }
+}
+
+void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch)
+{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ tuning->pitch[i] = pitch[i];
+ }
+}
+
+void fluid_tuning_set_pitch(fluid_tuning_t* tuning, int key, double pitch)
+{
+ if ((key >= 0) && (key < 128)) {
+ tuning->pitch[key] = pitch;
+ }
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.h
new file mode 100644
index 0000000..f1b90b3
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_tuning.h
@@ -0,0 +1,65 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+/*
+
+ More information about micro tuning can be found at:
+
+ http://www.midi.org/about-midi/tuning.htm
+ http://www.midi.org/about-midi/tuning-scale.htm
+ http://www.midi.org/about-midi/tuning_extens.htm
+
+*/
+
+#ifndef _FLUID_TUNING_H
+#define _FLUID_TUNING_H
+
+#include "fluidsynth_priv.h"
+
+struct _fluid_tuning_t {
+ char* name;
+ int bank;
+ int prog;
+ double pitch[128]; /* the pitch of every key, in cents */
+};
+
+fluid_tuning_t* new_fluid_tuning(const char* name, int bank, int prog);
+fluid_tuning_t* fluid_tuning_duplicate(fluid_tuning_t *tuning);
+void delete_fluid_tuning(fluid_tuning_t* tuning);
+
+void 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_t* tuning, int key, double pitch);
+#define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key])
+
+void fluid_tuning_set_octave(fluid_tuning_t* tuning, const double* pitch_deriv);
+
+void fluid_tuning_set_all(fluid_tuning_t* tuning, double* pitch);
+#define fluid_tuning_get_all(_t) (&(_t)->pitch[0])
+
+
+
+
+#endif /* _FLUID_TUNING_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.c b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.c
new file mode 100644
index 0000000..43f2cde
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.c
@@ -0,0 +1,1996 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_voice.h"
+#include "fluid_mod.h"
+#include "fluid_chan.h"
+#include "fluid_conv.h"
+#include "fluid_synth.h"
+#include "fluid_sys.h"
+#include "fluid_sfont.h"
+
+/* used for filter turn off optimization - if filter cutoff is above the
+ specified value and filter q is below the other value, turn filter off */
+#define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f
+#define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f
+
+/* Smallest amplitude that can be perceived (full scale is +/- 0.5)
+ * 16 bits => 96+4=100 dB dynamic range => 0.00001
+ * 0.00001 * 2 is approximately 0.00003 :)
+ */
+#define FLUID_NOISE_FLOOR 0.00003
+
+/* these should be the absolute minimum that FluidSynth can deal with */
+#define FLUID_MIN_LOOP_SIZE 2
+#define FLUID_MIN_LOOP_PAD 0
+
+/* min vol envelope release (to stop clicks) in SoundFont timecents */
+#define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */
+
+//removed inline
+static void fluid_voice_effects (fluid_voice_t *voice, int count,
+ fluid_real_t* dsp_left_buf,
+ fluid_real_t* dsp_right_buf,
+ fluid_real_t* dsp_reverb_buf,
+ fluid_real_t* dsp_chorus_buf);
+/*
+ * new_fluid_voice
+ */
+fluid_voice_t*
+new_fluid_voice(fluid_real_t output_rate)
+{
+ fluid_voice_t* voice;
+ voice = FLUID_NEW(fluid_voice_t);
+ if (voice == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ voice->status = FLUID_VOICE_CLEAN;
+ voice->chan = NO_CHANNEL;
+ voice->key = 0;
+ voice->vel = 0;
+ voice->channel = NULL;
+ voice->sample = NULL;
+ voice->output_rate = output_rate;
+
+ /* 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.
+ */
+ voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff;
+ voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f;
+
+ voice->volenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff;
+ voice->volenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f;
+
+ voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff;
+ voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f;
+
+ voice->modenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff;
+ voice->modenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f;
+
+ return voice;
+}
+
+/*
+ * delete_fluid_voice
+ */
+int
+delete_fluid_voice(fluid_voice_t* voice)
+{
+ if (voice == NULL) {
+ return FLUID_OK;
+ }
+ FLUID_FREE(voice);
+ return FLUID_OK;
+}
+
+/* fluid_voice_init
+ *
+ * Initialize the synthesis process
+ */
+int
+fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
+ fluid_channel_t* channel, int key, int vel, unsigned int id,
+ unsigned int start_time, fluid_real_t gain)
+{
+ /* Note: The voice parameters will be initialized later, when the
+ * generators have been retrieved from the sound font. Here, only
+ * the 'working memory' of the voice (position in envelopes, history
+ * of IIR filters, position in sample etc) is initialized. */
+
+
+ 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->sample = sample;
+ voice->start_time = start_time;
+ voice->ticks = 0;
+ voice->noteoff_ticks = 0;
+ voice->debug = 0;
+ voice->has_looped = 0; /* Will be set during voice_write when the 2nd loop point is reached */
+ voice->last_fres = -1; /* The filter coefficients have to be calculated later in the DSP loop. */
+ voice->filter_startup = 1; /* Set the filter immediately, don't fade between old and new settings */
+ voice->interp_method = fluid_channel_get_interp_method(voice->channel);
+
+ /* vol env initialization */
+ voice->volenv_count = 0;
+ voice->volenv_section = 0;
+ voice->volenv_val = 0.0f;
+ voice->amp = 0.0f; /* The last value of the volume envelope, used to
+ calculate the volume increment during
+ processing */
+
+ /* mod env initialization*/
+ voice->modenv_count = 0;
+ voice->modenv_section = 0;
+ voice->modenv_val = 0.0f;
+
+ /* mod lfo */
+ voice->modlfo_val = 0.0;/* Fixme: Retrieve from any other existing
+ voice on this channel to keep LFOs in
+ unison? */
+
+ /* vib lfo */
+ voice->viblfo_val = 0.0f; /* Fixme: See mod lfo */
+
+ /* Clear sample history in filter */
+ voice->hist1 = 0;
+ voice->hist2 = 0;
+
+ /* 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);
+
+ voice->synth_gain = gain;
+ /* avoid division by zero later*/
+ if (voice->synth_gain < 0.0000001){
+ voice->synth_gain = 0.0000001;
+ }
+
+ /* 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->amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / voice->synth_gain;
+ voice->amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / voice->synth_gain;
+
+ /* Increment the reference count of the sample to prevent the
+ unloading of the soundfont while this voice is playing. */
+ fluid_sample_incr_ref(voice->sample);
+
+ return FLUID_OK;
+}
+
+void fluid_voice_gen_set(fluid_voice_t* voice, int i, float val)
+{
+ voice->gen[i].val = val;
+ voice->gen[i].flags = GEN_SET;
+}
+
+void fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val)
+{
+ voice->gen[i].val += val;
+ voice->gen[i].flags = GEN_SET;
+}
+
+float fluid_voice_gen_get(fluid_voice_t* voice, int gen)
+{
+ return voice->gen[gen].val;
+}
+
+fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num)
+{
+ /* This is an extension to the SoundFont standard. More
+ * documentation is available at the fluid_synth_set_gen2()
+ * function. */
+ if (voice->gen[num].flags == GEN_ABS_NRPN) {
+ return (fluid_real_t) voice->gen[num].nrpn;
+ } else {
+ return (fluid_real_t) (voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn);
+ }
+}
+
+
+/*
+ * fluid_voice_write
+ *
+ * This is where it all happens. This function is called by the
+ * synthesizer to generate the sound samples. The synthesizer passes
+ * four audio buffers: left, right, reverb out, and chorus out.
+ *
+ * The biggest part of this function sets the correct values for all
+ * the dsp parameters (all the control data boil down to only a few
+ * dsp parameters). The dsp routine is #included in several places (fluid_dsp_core.c).
+ */
+int
+fluid_voice_write(fluid_voice_t* voice,
+ fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf,
+ fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf)
+{
+ fluid_real_t fres;
+ fluid_real_t target_amp; /* target amplitude */
+ int count;
+
+ fluid_real_t dsp_buf[FLUID_BUFSIZE];
+ fluid_env_data_t* env_data;
+ fluid_real_t x;
+
+
+ /* make sure we're playing and that we have sample data */
+ if (!_PLAYING(voice)) return FLUID_OK;
+
+ /******************* sample **********************/
+
+ if (voice->sample == NULL)
+ {
+ fluid_voice_off(voice);
+ return FLUID_OK;
+ }
+
+ if (voice->noteoff_ticks != 0 && voice->ticks >= voice->noteoff_ticks)
+ {
+ fluid_voice_noteoff(voice);
+ }
+
+ /* Range checking for sample- and loop-related parameters
+ * Initial phase is calculated here*/
+ fluid_voice_check_sample_sanity (voice);
+
+ /******************* vol env **********************/
+
+ env_data = &voice->volenv_data[voice->volenv_section];
+
+ /* skip to the next section of the envelope if necessary */
+ while (voice->volenv_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
+ if (env_data && voice->volenv_section == FLUID_VOICE_ENVDECAY)
+ voice->volenv_val = env_data->min * env_data->coeff;
+
+ env_data = &voice->volenv_data[++voice->volenv_section];
+ voice->volenv_count = 0;
+ }
+
+ /* calculate the envelope value and check for valid range */
+ x = env_data->coeff * voice->volenv_val + env_data->incr;
+ if (x < env_data->min)
+ {
+ x = env_data->min;
+ voice->volenv_section++;
+ voice->volenv_count = 0;
+ }
+ else if (x > env_data->max)
+ {
+ x = env_data->max;
+ voice->volenv_section++;
+ voice->volenv_count = 0;
+ }
+
+ voice->volenv_val = x;
+ voice->volenv_count++;
+
+ if (voice->volenv_section == FLUID_VOICE_ENVFINISHED)
+ {
+ fluid_voice_off (voice);
+ return FLUID_OK;
+ }
+
+ /******************* mod env **********************/
+
+ env_data = &voice->modenv_data[voice->modenv_section];
+
+ /* skip to the next section of the envelope if necessary */
+ while (voice->modenv_count >= env_data->count)
+ {
+ env_data = &voice->modenv_data[++voice->modenv_section];
+ voice->modenv_count = 0;
+ }
+
+ /* calculate the envelope value and check for valid range */
+ x = env_data->coeff * voice->modenv_val + env_data->incr;
+
+ if (x < env_data->min)
+ {
+ x = env_data->min;
+ voice->modenv_section++;
+ voice->modenv_count = 0;
+ }
+ else if (x > env_data->max)
+ {
+ x = env_data->max;
+ voice->modenv_section++;
+ voice->modenv_count = 0;
+ }
+
+ voice->modenv_val = x;
+ voice->modenv_count++;
+
+ /******************* mod lfo **********************/
+
+ if (voice->ticks >= voice->modlfo_delay)
+ {
+ voice->modlfo_val += voice->modlfo_incr;
+
+ if (voice->modlfo_val > 1.0)
+ {
+ voice->modlfo_incr = -voice->modlfo_incr;
+ voice->modlfo_val = (fluid_real_t) 2.0 - voice->modlfo_val;
+ }
+ else if (voice->modlfo_val < -1.0)
+ {
+ voice->modlfo_incr = -voice->modlfo_incr;
+ voice->modlfo_val = (fluid_real_t) -2.0 - voice->modlfo_val;
+ }
+ }
+
+ /******************* vib lfo **********************/
+
+ if (voice->ticks >= voice->viblfo_delay)
+ {
+ voice->viblfo_val += voice->viblfo_incr;
+
+ if (voice->viblfo_val > (fluid_real_t) 1.0)
+ {
+ voice->viblfo_incr = -voice->viblfo_incr;
+ voice->viblfo_val = (fluid_real_t) 2.0 - voice->viblfo_val;
+ }
+ else if (voice->viblfo_val < -1.0)
+ {
+ voice->viblfo_incr = -voice->viblfo_incr;
+ voice->viblfo_val = (fluid_real_t) -2.0 - voice->viblfo_val;
+ }
+ }
+
+ /******************* amplitude **********************/
+
+ /* calculate final amplitude
+ * - initial gain
+ * - amplitude envelope
+ */
+
+ if (voice->volenv_section == FLUID_VOICE_ENVDELAY)
+ goto post_process; /* The volume amplitude is in hold phase. No sound is produced. */
+
+ if (voice->volenv_section == 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->attenuation)
+ * fluid_cb2amp (voice->modlfo_val * -voice->modlfo_to_vol)
+ * voice->volenv_val;
+ }
+ else
+ {
+ fluid_real_t amplitude_that_reaches_noise_floor;
+ fluid_real_t amp_max;
+
+ target_amp = fluid_atten2amp (voice->attenuation)
+ * fluid_cb2amp (960.0f * (1.0f - voice->volenv_val)
+ + voice->modlfo_val * -voice->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->has_looped)
+ amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_loop;
+ else
+ amplitude_that_reaches_noise_floor = voice->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->min_attenuation_cB) * voice->volenv_val;
+
+ /* 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)
+ {
+ fluid_voice_off (voice);
+ goto post_process;
+ }
+ }
+
+ /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */
+ voice->amp_incr = (target_amp - voice->amp) / FLUID_BUFSIZE;
+
+ /* no volume and not changing? - No need to process */
+ if ((voice->amp == 0.0f) && (voice->amp_incr == 0.0f))
+ goto post_process;
+
+ /* 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->phase_incr = fluid_ct2hz_real
+ (voice->pitch + voice->modlfo_val * voice->modlfo_to_pitch
+ + voice->viblfo_val * voice->viblfo_to_pitch
+ + voice->modenv_val * voice->modenv_to_pitch) / voice->root_pitch;
+
+ /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */
+ if (voice->phase_incr == 0) voice->phase_incr = 1;
+
+ /*************** resonant filter ******************/
+
+ /* calculate the frequency of the resonant filter in Hz */
+ fres = fluid_ct2hz(voice->fres
+ + voice->modlfo_val * voice->modlfo_to_fc
+ + voice->modenv_val * voice->modenv_to_fc);
+
+ /* 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 * voice->output_rate)
+ fres = 0.45f * voice->output_rate;
+ else if (fres < 5)
+ fres = 5;
+
+ /* if filter enabled and there is a significant frequency change.. */
+ if ((fabs(fres - voice->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.
+ *
+ * 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 * (fres / ((float) voice->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 * voice->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:
+ * voice->b0=(1.-cos_coeff)*a0_inv*0.5*voice->filter_gain;
+ * voice->b1=(1.-cos_coeff)*a0_inv*voice->filter_gain;
+ * voice->b2=(1.-cos_coeff)*a0_inv*0.5*voice->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 * voice->filter_gain;
+ /* both b0 -and- b2 */
+ fluid_real_t b02_temp = b1_temp * 0.5f;
+
+ if (voice->filter_startup)
+ {
+ /* The filter is calculated, because the voice was started up.
+ * In this case set the filter coefficients without delay.
+ */
+ voice->a1 = a1_temp;
+ voice->a2 = a2_temp;
+ voice->b02 = b02_temp;
+ voice->b1 = b1_temp;
+ voice->filter_coeff_incr_count = 0;
+ voice->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.
+ */
+
+#define FILTER_TRANSITION_SAMPLES (FLUID_BUFSIZE)
+
+ voice->a1_incr = (a1_temp - voice->a1) / FILTER_TRANSITION_SAMPLES;
+ voice->a2_incr = (a2_temp - voice->a2) / FILTER_TRANSITION_SAMPLES;
+ voice->b02_incr = (b02_temp - voice->b02) / FILTER_TRANSITION_SAMPLES;
+ voice->b1_incr = (b1_temp - voice->b1) / FILTER_TRANSITION_SAMPLES;
+ /* Have to add the increments filter_coeff_incr_count times. */
+ voice->filter_coeff_incr_count = FILTER_TRANSITION_SAMPLES;
+ }
+ voice->last_fres = fres;
+ }
+
+
+ /*********************** 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_buf = dsp_buf;
+
+ switch (voice->interp_method)
+ {
+ case FLUID_INTERP_NONE:
+ count = fluid_dsp_float_interpolate_none (voice);
+ break;
+ case FLUID_INTERP_LINEAR:
+ count = fluid_dsp_float_interpolate_linear (voice);
+ break;
+ case FLUID_INTERP_4THORDER:
+ default:
+ count = fluid_dsp_float_interpolate_4th_order (voice);
+ break;
+ case FLUID_INTERP_7THORDER:
+ count = fluid_dsp_float_interpolate_7th_order (voice);
+ break;
+ }
+
+ if (count > 0)
+ fluid_voice_effects (voice, count, dsp_left_buf, dsp_right_buf,
+ dsp_reverb_buf, dsp_chorus_buf);
+
+ /* turn off voice if short count (sample ended and not looping) */
+ if (count < FLUID_BUFSIZE)
+ {
+ fluid_voice_off(voice);
+ }
+
+ post_process:
+ voice->ticks += FLUID_BUFSIZE;
+ return FLUID_OK;
+}
+
+
+/* Purpose:
+ *
+ * - filters (applies a lowpass filter with variable cutoff frequency and quality factor)
+ * - mixes the processed sample to left and right output using the pan setting
+ * - sends the processed sample to chorus and reverb
+ *
+ * Variable description:
+ * - dsp_data: Pointer to the original waveform data
+ * - dsp_left_buf: The generated signal goes here, left channel
+ * - dsp_right_buf: right channel
+ * - dsp_reverb_buf: Send to reverb unit
+ * - dsp_chorus_buf: Send to chorus unit
+ * - dsp_a1: Coefficient for the filter
+ * - dsp_a2: same
+ * - dsp_b0: same
+ * - dsp_b1: same
+ * - dsp_b2: same
+ * - voice holds the voice structure
+ *
+ * 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
+ *
+ */
+static void
+fluid_voice_effects (fluid_voice_t *voice, int count,
+ fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf,
+ fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf)
+{
+ /* IIR filter sample history */
+ fluid_real_t dsp_hist1 = voice->hist1;
+ fluid_real_t dsp_hist2 = voice->hist2;
+
+ /* IIR filter coefficients */
+ fluid_real_t dsp_a1 = voice->a1;
+ fluid_real_t dsp_a2 = voice->a2;
+ fluid_real_t dsp_b02 = voice->b02;
+ fluid_real_t dsp_b1 = voice->b1;
+ fluid_real_t dsp_a1_incr = voice->a1_incr;
+ fluid_real_t dsp_a2_incr = voice->a2_incr;
+ fluid_real_t dsp_b02_incr = voice->b02_incr;
+ fluid_real_t dsp_b1_incr = voice->b1_incr;
+ int dsp_filter_coeff_incr_count = voice->filter_coeff_incr_count;
+
+ fluid_real_t *dsp_buf = voice->dsp_buf;
+
+ fluid_real_t dsp_centernode;
+ int dsp_i;
+ float v;
+
+ /* filter (implement the voice filter according to SoundFont standard) */
+
+ /* Check for denormal number (too close to zero). */
+ if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */
+
+ /* Two versions of the filter loop. One, while the filter is
+ * changing towards its new setting. The other, if the filter
+ * doesn't change.
+ */
+
+ if (dsp_filter_coeff_incr_count > 0)
+ {
+ /* 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)
+ {
+ dsp_a1 += dsp_a1_incr;
+ dsp_a2 += dsp_a2_incr;
+ dsp_b02 += dsp_b02_incr;
+ dsp_b1 += dsp_b1_incr;
+ }
+ } /* 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;
+ }
+ }
+
+ /* pan (Copy the signal to the left and right output buffer) The voice
+ * panning generator has a range of -500 .. 500. If it is centered,
+ * it's close to 0. voice->amp_left and voice->amp_right are then the
+ * same, and we can save one multiplication per voice and sample.
+ */
+ if ((-0.5 < voice->pan) && (voice->pan < 0.5))
+ {
+ /* The voice is centered. Use voice->amp_left twice. */
+ for (dsp_i = 0; dsp_i < count; dsp_i++)
+ {
+ v = voice->amp_left * dsp_buf[dsp_i];
+ dsp_left_buf[dsp_i] += v;
+ dsp_right_buf[dsp_i] += v;
+ }
+ }
+ else /* The voice is not centered. Stereo samples have one side zero. */
+ {
+ if (voice->amp_left != 0.0)
+ {
+ for (dsp_i = 0; dsp_i < count; dsp_i++)
+ dsp_left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i];
+ }
+
+ if (voice->amp_right != 0.0)
+ {
+ for (dsp_i = 0; dsp_i < count; dsp_i++)
+ dsp_right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i];
+ }
+ }
+
+ /* reverb send. Buffer may be NULL. */
+ if ((dsp_reverb_buf != NULL) && (voice->amp_reverb != 0.0))
+ {
+ for (dsp_i = 0; dsp_i < count; dsp_i++)
+ dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i];
+ }
+
+ /* chorus send. Buffer may be NULL. */
+ if ((dsp_chorus_buf != NULL) && (voice->amp_chorus != 0))
+ {
+ for (dsp_i = 0; dsp_i < count; dsp_i++)
+ dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i];
+ }
+
+ voice->hist1 = dsp_hist1;
+ voice->hist2 = dsp_hist2;
+ voice->a1 = dsp_a1;
+ voice->a2 = dsp_a2;
+ voice->b02 = dsp_b02;
+ voice->b1 = dsp_b1;
+ voice->filter_coeff_incr_count = dsp_filter_coeff_incr_count;
+}
+
+/*
+ * fluid_voice_get_channel
+ */
+fluid_channel_t*
+fluid_voice_get_channel(fluid_voice_t* voice)
+{
+ return voice->channel;
+}
+
+/*
+ * fluid_voice_start
+ */
+void fluid_voice_start(fluid_voice_t* voice)
+{
+ /* The maximum volume of the loop is calculated and cached once for each
+ * sample with its nominal loop settings. This happens, when the sample is used
+ * for the first time.*/
+
+ fluid_voice_calculate_runtime_synthesis_parameters(voice);
+
+ /* Force setting of the phase at the first DSP loop run
+ * This cannot be done earlier, because it depends on modulators.*/
+ voice->check_sample_sanity_flag=FLUID_SAMPLESANITY_STARTUP;
+
+ voice->status = FLUID_VOICE_ON;
+}
+
+/*
+ * fluid_voice_calculate_runtime_synthesis_parameters
+ *
+ * in this function we calculate the values of all the parameters. the
+ * parameters are converted to their most useful unit for the DSP
+ * algorithm, for example, number of samples instead of
+ * timecents. Some parameters keep their "perceptual" unit and
+ * conversion will be done in the DSP function. This is the case, for
+ * example, for the pitch since it is modulated by the controllers in
+ * cents. */
+int
+fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice)
+{
+ int i;
+
+ int list_of_generators_to_initialize[35] = {
+ GEN_STARTADDROFS, /* SF2.01 page 48 #0 */
+ GEN_ENDADDROFS, /* #1 */
+ GEN_STARTLOOPADDROFS, /* #2 */
+ GEN_ENDLOOPADDROFS, /* #3 */
+ /* GEN_STARTADDRCOARSEOFS see comment below [1] #4 */
+ GEN_MODLFOTOPITCH, /* #5 */
+ GEN_VIBLFOTOPITCH, /* #6 */
+ GEN_MODENVTOPITCH, /* #7 */
+ GEN_FILTERFC, /* #8 */
+ GEN_FILTERQ, /* #9 */
+ GEN_MODLFOTOFILTERFC, /* #10 */
+ GEN_MODENVTOFILTERFC, /* #11 */
+ /* GEN_ENDADDRCOARSEOFS [1] #12 */
+ GEN_MODLFOTOVOL, /* #13 */
+ /* not defined #14 */
+ GEN_CHORUSSEND, /* #15 */
+ GEN_REVERBSEND, /* #16 */
+ GEN_PAN, /* #17 */
+ /* not defined #18 */
+ /* not defined #19 */
+ /* not defined #20 */
+ GEN_MODLFODELAY, /* #21 */
+ GEN_MODLFOFREQ, /* #22 */
+ GEN_VIBLFODELAY, /* #23 */
+ GEN_VIBLFOFREQ, /* #24 */
+ GEN_MODENVDELAY, /* #25 */
+ GEN_MODENVATTACK, /* #26 */
+ GEN_MODENVHOLD, /* #27 */
+ GEN_MODENVDECAY, /* #28 */
+ /* GEN_MODENVSUSTAIN [1] #29 */
+ GEN_MODENVRELEASE, /* #30 */
+ /* GEN_KEYTOMODENVHOLD [1] #31 */
+ /* GEN_KEYTOMODENVDECAY [1] #32 */
+ GEN_VOLENVDELAY, /* #33 */
+ GEN_VOLENVATTACK, /* #34 */
+ GEN_VOLENVHOLD, /* #35 */
+ GEN_VOLENVDECAY, /* #36 */
+ /* GEN_VOLENVSUSTAIN [1] #37 */
+ GEN_VOLENVRELEASE, /* #38 */
+ /* GEN_KEYTOVOLENVHOLD [1] #39 */
+ /* GEN_KEYTOVOLENVDECAY [1] #40 */
+ /* GEN_STARTLOOPADDRCOARSEOFS [1] #45 */
+ GEN_KEYNUM, /* #46 */
+ GEN_VELOCITY, /* #47 */
+ GEN_ATTENUATION, /* #48 */
+ /* GEN_ENDLOOPADDRCOARSEOFS [1] #50 */
+ /* GEN_COARSETUNE [1] #51 */
+ /* GEN_FINETUNE [1] #52 */
+ GEN_OVERRIDEROOTKEY, /* #58 */
+ GEN_PITCH, /* --- */
+ -1}; /* end-of-list marker */
+
+ /* When the voice is made ready for the synthesis process, a lot of
+ * voice-internal parameters have to be calculated.
+ *
+ * At this point, the sound font has already set the -nominal- value
+ * for all generators (excluding GEN_PITCH). Most generators can be
+ * modulated - they include a nominal value and an offset (which
+ * changes with velocity, note number, channel parameters like
+ * aftertouch, mod wheel...) Now this offset will be calculated as
+ * follows:
+ *
+ * - Process each modulator once.
+ * - Calculate its output value.
+ * - Find the target generator.
+ * - Add the output value to the modulation value of the generator.
+ *
+ * Note: The generators have been initialized with
+ * fluid_gen_set_default_values.
+ */
+
+ for (i = 0; i < voice->mod_count; i++) {
+ fluid_mod_t* mod = &voice->mod[i];
+ fluid_real_t modval = fluid_mod_get_value(mod, voice->channel, voice);
+ int dest_gen_index = mod->dest;
+ fluid_gen_t* dest_gen = &voice->gen[dest_gen_index];
+ dest_gen->mod += modval;
+ /* fluid_dump_modulator(mod); */
+ }
+
+ /* 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)) {
+ /* pitch(60) + scale * (pitch(key) - pitch(60)) */
+ #define __pitch(_k) fluid_tuning_get_pitch(tuning, _k)
+ fluid_tuning_t* tuning = fluid_channel_get_tuning(voice->channel);
+ voice->gen[GEN_PITCH].val = (__pitch(60) + (voice->gen[GEN_SCALETUNE].val / 100.0f *
+ (__pitch(voice->key) - __pitch(60))));
+ } else {
+ voice->gen[GEN_PITCH].val = (voice->gen[GEN_SCALETUNE].val * (voice->key - 60.0f)
+ + 100.0f * 60.0f);
+ }
+
+ /* 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). */
+ voice->min_attenuation_cB = fluid_voice_get_lower_boundary_for_attenuation(voice);
+
+ return FLUID_OK;
+}
+
+/*
+ * calculate_hold_decay_buffers
+ */
+int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
+ int gen_key2base, int is_decay)
+{
+ /* Purpose:
+ *
+ * Returns the number of DSP loops, that correspond to the hold
+ * (is_decay=0) or decay (is_decay=1) time.
+ * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD,
+ * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD,
+ * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY
+ */
+
+ fluid_real_t timecents;
+ fluid_real_t seconds;
+ int buffers;
+
+ /* SF2.01 section 8.4.3 # 31, 32, 39, 40
+ * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'.
+ * The unit of the generator is timecents per key number.
+ * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72)
+ * will cause (60-72)*100=-1200 timecents of time variation.
+ * The time is cut in half.
+ */
+ timecents = (_GEN(voice, gen_base) + _GEN(voice, gen_key2base) * (60.0 - voice->key));
+
+ /* Range checking */
+ if (is_decay){
+ /* SF 2.01 section 8.1.3 # 28, 36 */
+ if (timecents > 8000.0) {
+ timecents = 8000.0;
+ }
+ } else {
+ /* SF 2.01 section 8.1.3 # 27, 35 */
+ if (timecents > 5000) {
+ timecents = 5000.0;
+ }
+ /* SF 2.01 section 8.1.2 # 27, 35:
+ * The most negative number indicates no hold time
+ */
+ if (timecents <= -32768.) {
+ return 0;
+ }
+ }
+ /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */
+ if (timecents < -12000.0) {
+ timecents = -12000.0;
+ }
+
+ seconds = fluid_tc2sec(timecents);
+ /* Each DSP loop processes FLUID_BUFSIZE samples. */
+
+ /* round to next full number of buffers */
+ buffers = (int)(((fluid_real_t)voice->output_rate * seconds)
+ / (fluid_real_t)FLUID_BUFSIZE
+ +0.5);
+
+ return buffers;
+}
+
+/*
+ * fluid_voice_update_param
+ *
+ * Purpose:
+ *
+ * The value of a generator (gen) has changed. (The different
+ * generators are listed in fluidlite.h, or in SF2.01 page 48-49)
+ * Now the dependent 'voice' parameters are calculated.
+ *
+ * fluid_voice_update_param can be called during the setup of the
+ * voice (to calculate the initial value for a voice parameter), or
+ * during its operation (a generator has been changed due to
+ * real-time parameter modifications like pitch-bend).
+ *
+ * Note: The generator holds three values: The base value .val, an
+ * offset caused by modulators .mod, and an offset caused by the
+ * NRPN system. _GEN(voice, generator_enumerator) returns the sum
+ * of all three.
+ */
+void
+fluid_voice_update_param(fluid_voice_t* voice, int gen)
+{
+ double q_dB;
+ fluid_real_t x;
+ fluid_real_t y;
+ unsigned int count;
+ // 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;
+ 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);
+ break;
+
+ /* The pitch is calculated from three different generators.
+ * Read comment in fluidlite.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));
+ 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;
+ 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;
+ 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->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;
+ }
+ voice->root_pitch = fluid_ct2hz(voice->root_pitch);
+ if (voice->sample != NULL) {
+ voice->root_pitch *= (fluid_real_t) voice->output_rate / voice->sample->samplerate;
+ }
+ 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]
+ */
+ voice->fres = _GEN(voice, GEN_FILTERFC);
+
+ /* The synthesis loop will have to recalculate the filter
+ * coefficients. */
+ voice->last_fres = -1.0f;
+ break;
+
+ case GEN_FILTERQ:
+ /* The generator contains 'centibels' (1/10 dB) => divide by 10 to
+ * obtain dB */
+ q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f;
+
+ /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
+ fluid_clip(q_dB, 0.0f, 96.0f);
+
+ /* Short version: Modify the Q definition in a way, that a Q of 0
+ * dB leads to no resonance hump in the freq. response.
+ *
+ * Long version: From SF2.01, page 39, item 9 (initialFilterQ):
+ * "The gain at the cutoff frequency may be less than zero when
+ * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave
+ * q as it is, then this results in a 3 dB hump slightly below
+ * fc. At fc, the gain is exactly the DC gain (0 dB). What is
+ * (probably) meant here is that the filter does not show a
+ * resonance hump for q_dB=0. In this case, the corresponding
+ * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of
+ * attenuation at fc now. In this case Q_dB is the height of the
+ * resonance peak not over the DC gain, but over the frequency
+ * response of a non-resonant filter. This idea is implemented as
+ * follows: */
+ q_dB -= 3.01f;
+
+ /* The 'sound font' Q is defined in dB. The filter needs a linear
+ q. Convert. */
+ voice->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f));
+
+ /* SF 2.01 page 59:
+ *
+ * The SoundFont specs ask for a gain reduction equal to half the
+ * height of the resonance peak (Q). For example, for a 10 dB
+ * resonance peak, the gain is reduced by 5 dB. This is done by
+ * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB
+ * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc)
+ * The gain is later factored into the 'b' coefficients
+ * (numerator of the filter equation). This gain factor depends
+ * only on Q, so this is the right place to calculate it.
+ */
+ voice->filter_gain = (fluid_real_t) (1.0 / sqrt(voice->q_lin));
+
+ /* The synthesis loop will have to recalculate the filter coefficients. */
+ voice->last_fres = -1.;
+ break;
+
+ case GEN_MODLFOTOPITCH:
+ voice->modlfo_to_pitch = _GEN(voice, GEN_MODLFOTOPITCH);
+ fluid_clip(voice->modlfo_to_pitch, -12000.0, 12000.0);
+ break;
+
+ case GEN_MODLFOTOVOL:
+ voice->modlfo_to_vol = _GEN(voice, GEN_MODLFOTOVOL);
+ fluid_clip(voice->modlfo_to_vol, -960.0, 960.0);
+ break;
+
+ case GEN_MODLFOTOFILTERFC:
+ voice->modlfo_to_fc = _GEN(voice, GEN_MODLFOTOFILTERFC);
+ fluid_clip(voice->modlfo_to_fc, -12000, 12000);
+ break;
+
+ case GEN_MODLFODELAY:
+ x = _GEN(voice, GEN_MODLFODELAY);
+ fluid_clip(x, -12000.0f, 5000.0f);
+ voice->modlfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
+ 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);
+ voice->modlfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
+ 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);
+ voice->viblfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
+ break;
+
+ case GEN_VIBLFODELAY:
+ x = _GEN(voice,GEN_VIBLFODELAY);
+ fluid_clip(x, -12000.0f, 5000.0f);
+ voice->viblfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
+ break;
+
+ case GEN_VIBLFOTOPITCH:
+ voice->viblfo_to_pitch = _GEN(voice, GEN_VIBLFOTOPITCH);
+ fluid_clip(voice->viblfo_to_pitch, -12000.0, 12000.0);
+ break;
+
+ case GEN_KEYNUM:
+ /* GEN_KEYNUM: SF2.01 page 46, item 46
+ *
+ * If this generator is active, it forces the key number to its
+ * value. Non-realtime controller.
+ *
+ * There is a flag, which should indicate, whether a generator is
+ * enabled or not. But here we rely on the default value of -1.
+ * */
+ x = _GEN(voice, GEN_KEYNUM);
+ if (x >= 0){
+ voice->key = x;
+ }
+ break;
+
+ case GEN_VELOCITY:
+ /* GEN_VELOCITY: SF2.01 page 46, item 47
+ *
+ * If this generator is active, it forces the velocity to its
+ * value. Non-realtime controller.
+ *
+ * There is a flag, which should indicate, whether a generator is
+ * enabled or not. But here we rely on the default value of -1. */
+ x = _GEN(voice, GEN_VELOCITY);
+ if (x > 0) {
+ voice->vel = x;
+ }
+ break;
+
+ case GEN_MODENVTOPITCH:
+ voice->modenv_to_pitch = _GEN(voice, GEN_MODENVTOPITCH);
+ fluid_clip(voice->modenv_to_pitch, -12000.0, 12000.0);
+ break;
+
+ case GEN_MODENVTOFILTERFC:
+ voice->modenv_to_fc = _GEN(voice,GEN_MODENVTOFILTERFC);
+
+ /* Range: SF2.01 section 8.1.3 # 1
+ * Motivation for range checking:
+ * Filter is reported to make funny noises now and then
+ */
+ fluid_clip(voice->modenv_to_fc, -12000.0, 12000.0);
+ break;
+
+
+ /* sample start and ends points
+ *
+ * Range checking is initiated via the
+ * voice->check_sample_sanity flag,
+ * because it is impossible to check here:
+ * During the voice setup, all modulators are processed, while
+ * the voice is inactive. Therefore, illegal settings may
+ * occur during the setup (for example: First move the loop
+ * end point ahead of the loop start point => invalid, then
+ * move the loop start point forward => valid again.
+ */
+ case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */
+ case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */
+ if (voice->sample != NULL) {
+ voice->start = (voice->sample->start
+ + (int) _GEN(voice, GEN_STARTADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS));
+ voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
+ }
+ 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) {
+ voice->end = (voice->sample->end
+ + (int) _GEN(voice, GEN_ENDADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS));
+ voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
+ }
+ 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) {
+ voice->loopstart = (voice->sample->loopstart
+ + (int) _GEN(voice, GEN_STARTLOOPADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS));
+ voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
+ }
+ 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) {
+ voice->loopend = (voice->sample->loopend
+ + (int) _GEN(voice, GEN_ENDLOOPADDROFS)
+ + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS));
+ voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
+ }
+ break;
+
+ /* Conversion functions differ in range limit */
+#define NUM_BUFFERS_DELAY(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE)
+#define NUM_BUFFERS_ATTACK(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE)
+#define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE)
+
+ /* volume envelope
+ *
+ * - delay and hold times are converted to absolute number of samples
+ * - sustain is converted to its absolute value
+ * - attack, decay and release are converted to their increment per sample
+ */
+ case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */
+ x = _GEN(voice, GEN_VOLENVDELAY);
+ fluid_clip(x, -12000.0f, 5000.0f);
+ count = NUM_BUFFERS_DELAY(x);
+ voice->volenv_data[FLUID_VOICE_ENVDELAY].count = count;
+ voice->volenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVDELAY].max = 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);
+ voice->volenv_data[FLUID_VOICE_ENVATTACK].count = count;
+ voice->volenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVATTACK].max = 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 */
+ voice->volenv_data[FLUID_VOICE_ENVHOLD].count = count;
+ voice->volenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVHOLD].max = 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 */
+ voice->volenv_data[FLUID_VOICE_ENVDECAY].count = count;
+ voice->volenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVDECAY].min = y;
+ voice->volenv_data[FLUID_VOICE_ENVDECAY].max = 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);
+ voice->volenv_data[FLUID_VOICE_ENVRELEASE].count = count;
+ voice->volenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f;
+ voice->volenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f;
+ voice->volenv_data[FLUID_VOICE_ENVRELEASE].max = 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);
+ voice->modenv_data[FLUID_VOICE_ENVDELAY].count = NUM_BUFFERS_DELAY(x);
+ voice->modenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVDELAY].max = 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);
+ voice->modenv_data[FLUID_VOICE_ENVATTACK].count = count;
+ voice->modenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVATTACK].max = 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 */
+ voice->modenv_data[FLUID_VOICE_ENVHOLD].count = count;
+ voice->modenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVHOLD].max = 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);
+ voice->modenv_data[FLUID_VOICE_ENVDECAY].count = count;
+ voice->modenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVDECAY].min = y;
+ voice->modenv_data[FLUID_VOICE_ENVDECAY].max = 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);
+ voice->modenv_data[FLUID_VOICE_ENVRELEASE].count = count;
+ voice->modenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f;
+ voice->modenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0;
+ voice->modenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f;
+ voice->modenv_data[FLUID_VOICE_ENVRELEASE].max = 2.0f;
+ break;
+
+ } /* switch gen */
+}
+
+/**
+ * fluid_voice_modulate
+ *
+ * In this implementation, I want to make sure that all controllers
+ * are event based: the parameter values of the DSP algorithm should
+ * only be updates when a controller event arrived and not at every
+ * iteration of the audio cycle (which would probably be feasible if
+ * the synth was made in silicon).
+ *
+ * The update is done in three steps:
+ *
+ * - first, we look for all the modulators that have the changed
+ * controller as a source. This will yield a list of generators that
+ * will be changed because of the controller event.
+ *
+ * - For every changed generator, calculate its new value. This is the
+ * sum of its original value plus the values of al the attached
+ * modulators.
+ *
+ * - For every changed generator, convert its value to the correct
+ * unit of the corresponding DSP parameter
+ *
+ * @fn int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl, int val)
+ * @param voice the synthesis voice
+ * @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...)
+ * @param ctrl the control number
+ * */
+int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl)
+{
+ int i, k;
+ fluid_mod_t* mod;
+ int gen;
+ fluid_real_t modval;
+
+/* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */
+
+ for (i = 0; i < voice->mod_count; i++) {
+
+ mod = &voice->mod[i];
+
+ /* step 1: find all the modulators that have the changed controller
+ * as input source. */
+ if (fluid_mod_has_source(mod, cc, ctrl)) {
+
+ gen = fluid_mod_get_dest(mod);
+ modval = 0.0;
+
+ /* step 2: for every changed modulator, calculate the modulation
+ * value of its associated generator */
+ for (k = 0; k < voice->mod_count; k++) {
+ if (fluid_mod_has_dest(&voice->mod[k], gen)) {
+ modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice);
+ }
+ }
+
+ fluid_gen_set_mod(&voice->gen[gen], modval);
+
+ /* step 3: now that we have the new value of the generator,
+ * recalculate the parameter values that are derived from the
+ * generator */
+ fluid_voice_update_param(voice, gen);
+ }
+ }
+ return FLUID_OK;
+}
+
+/**
+ * fluid_voice_modulate_all
+ *
+ * Update all the modulators. This function is called after a
+ * ALL_CTRL_OFF MIDI message has been received (CC 121).
+ *
+ */
+int fluid_voice_modulate_all(fluid_voice_t* voice)
+{
+ fluid_mod_t* mod;
+ int i, k, gen;
+ fluid_real_t modval;
+
+ /* Loop through all the modulators.
+
+ FIXME: we should loop through the set of generators instead of
+ the set of modulators. We risk to call 'fluid_voice_update_param'
+ several times for the same generator if several modulators have
+ that generator as destination. It's not an error, just a wast of
+ energy (think polution, global warming, unhappy musicians,
+ ...) */
+
+ for (i = 0; i < voice->mod_count; i++) {
+
+ mod = &voice->mod[i];
+ gen = fluid_mod_get_dest(mod);
+ modval = 0.0;
+
+ /* Accumulate the modulation values of all the modulators with
+ * destination generator 'gen' */
+ for (k = 0; k < voice->mod_count; k++) {
+ if (fluid_mod_has_dest(&voice->mod[k], gen)) {
+ modval += fluid_mod_get_value(&voice->mod[k], voice->channel, voice);
+ }
+ }
+
+ fluid_gen_set_mod(&voice->gen[gen], modval);
+
+ /* Update the parameter values that are depend on the generator
+ * 'gen' */
+ fluid_voice_update_param(voice, gen);
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_voice_noteoff
+ */
+int
+fluid_voice_noteoff(fluid_voice_t* voice)
+{
+ unsigned int at_tick;
+
+ at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
+
+ if (at_tick > voice->ticks) {
+ /* Delay noteoff */
+ voice->noteoff_ticks = at_tick;
+ return FLUID_OK;
+ }
+
+ if (voice->channel && fluid_channel_sustained(voice->channel)) {
+ voice->status = FLUID_VOICE_SUSTAINED;
+ } else {
+ if (voice->volenv_section == 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 (voice->volenv_val > 0){
+ fluid_real_t lfo = voice->modlfo_val * -voice->modlfo_to_vol;
+ fluid_real_t amp = voice->volenv_val * 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);
+ voice->volenv_val = env_value;
+ }
+ }
+ voice->volenv_section = FLUID_VOICE_ENVRELEASE;
+ voice->volenv_count = 0;
+ voice->modenv_section = FLUID_VOICE_ENVRELEASE;
+ voice->modenv_count = 0;
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_voice_kill_excl
+ *
+ * Percussion sounds can be mutually exclusive: for example, a 'closed
+ * hihat' sound will terminate an 'open hihat' sound ringing at the
+ * same time. This behaviour is modeled using 'exclusive classes',
+ * turning on a voice with an exclusive class other than 0 will kill
+ * all other voices having that exclusive class within the same preset
+ * or channel. fluid_voice_kill_excl gets called, when 'voice' is to
+ * be killed for that reason.
+ */
+int
+fluid_voice_kill_excl(fluid_voice_t* voice){
+
+ if (!_PLAYING(voice)) {
+ return FLUID_OK;
+ }
+
+ /* Turn off the exclusive class information for this voice,
+ so that it doesn't get killed twice
+ */
+ fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0);
+
+ /* If the voice is not yet in release state, put it into release state */
+ if (voice->volenv_section != FLUID_VOICE_ENVRELEASE){
+ voice->volenv_section = FLUID_VOICE_ENVRELEASE;
+ voice->volenv_count = 0;
+ voice->modenv_section = FLUID_VOICE_ENVRELEASE;
+ voice->modenv_count = 0;
+ }
+
+ /* Speed up the volume envelope */
+ /* The value was found through listening tests with hi-hat samples. */
+ fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200);
+ fluid_voice_update_param(voice, GEN_VOLENVRELEASE);
+
+ /* Speed up the modulation envelope */
+ fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200);
+ fluid_voice_update_param(voice, GEN_MODENVRELEASE);
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_voice_off
+ *
+ * Purpose:
+ * Turns off a voice, meaning that it is not processed
+ * anymore by the DSP loop.
+ */
+int
+fluid_voice_off(fluid_voice_t* voice)
+{
+ voice->chan = NO_CHANNEL;
+ voice->volenv_section = FLUID_VOICE_ENVFINISHED;
+ voice->volenv_count = 0;
+ voice->modenv_section = FLUID_VOICE_ENVFINISHED;
+ voice->modenv_count = 0;
+ voice->status = FLUID_VOICE_OFF;
+
+ /* Decrement the reference count of the sample. */
+ if (voice->sample) {
+ fluid_sample_decr_ref(voice->sample);
+ voice->sample = NULL;
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_voice_add_mod
+ *
+ * Adds a modulator to the voice. "mode" indicates, what to do, if
+ * an identical modulator exists already.
+ *
+ * mode == FLUID_VOICE_ADD: Identical modulators on preset level are added
+ * mode == FLUID_VOICE_OVERWRITE: Identical modulators on instrument level are overwritten
+ * mode == FLUID_VOICE_DEFAULT: This is a default modulator, there can be no identical modulator.
+ * Don't check.
+ */
+void
+fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode)
+{
+ int i;
+
+ /*
+ * Some soundfonts come with a huge number of non-standard
+ * controllers, because they have been designed for one particular
+ * sound card. Discard them, maybe print a warning.
+ */
+
+ if (((mod->flags1 & FLUID_MOD_CC) == 0)
+ && ((mod->src1 != 0) /* SF2.01 section 8.2.1: Constant value */
+ && (mod->src1 != 2) /* Note-on velocity */
+ && (mod->src1 != 3) /* Note-on key number */
+ && (mod->src1 != 10) /* Poly pressure */
+ && (mod->src1 != 13) /* Channel pressure */
+ && (mod->src1 != 14) /* Pitch wheel */
+ && (mod->src1 != 16))) { /* Pitch wheel sensitivity */
+ FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
+ return;
+ }
+
+ if (mode == FLUID_VOICE_ADD) {
+
+ /* if identical modulator exists, add them */
+ for (i = 0; i < voice->mod_count; i++) {
+ if (fluid_mod_test_identity(&voice->mod[i], mod)) {
+ // printf("Adding modulator...\n");
+ voice->mod[i].amount += mod->amount;
+ return;
+ }
+ }
+
+ } else if (mode == FLUID_VOICE_OVERWRITE) {
+
+ /* if identical modulator exists, replace it (only the amount has to be changed) */
+ for (i = 0; i < voice->mod_count; i++) {
+ if (fluid_mod_test_identity(&voice->mod[i], mod)) {
+ // printf("Replacing modulator...amount is %f\n",mod->amount);
+ voice->mod[i].amount = mod->amount;
+ return;
+ }
+ }
+ }
+
+ /* Add a new modulator (No existing modulator to add / overwrite).
+ Also, default modulators (FLUID_VOICE_DEFAULT) are added without
+ checking, if the same modulator already exists. */
+ if (voice->mod_count < FLUID_NUM_MOD) {
+ fluid_mod_clone(&voice->mod[voice->mod_count++], mod);
+ }
+}
+
+unsigned int fluid_voice_get_id(fluid_voice_t* voice)
+{
+ return voice->id;
+}
+
+int fluid_voice_is_playing(fluid_voice_t* voice)
+{
+ return _PLAYING(voice);
+}
+
+/*
+ * fluid_voice_get_lower_boundary_for_attenuation
+ *
+ * Purpose:
+ *
+ * A lower boundary for the attenuation (as in 'the minimum
+ * attenuation of this voice, with volume pedals, modulators
+ * etc. resulting in minimum attenuation, cannot fall below x cB) is
+ * calculated. This has to be called during fluid_voice_init, after
+ * all modulators have been run on the voice once. Also,
+ * voice->attenuation has to be initialized.
+ */
+fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice)
+{
+ int i;
+ fluid_mod_t* mod;
+ fluid_real_t possible_att_reduction_cB=0;
+ fluid_real_t lower_bound;
+
+ for (i = 0; i < voice->mod_count; i++) {
+ mod = &voice->mod[i];
+
+ /* Modulator has attenuation as target and can change over time? */
+ if ((mod->dest == GEN_ATTENUATION)
+ && ((mod->flags1 & FLUID_MOD_CC) || (mod->flags2 & FLUID_MOD_CC))) {
+
+ fluid_real_t current_val = fluid_mod_get_value(mod, voice->channel, voice);
+ fluid_real_t v = fabs(mod->amount);
+
+ if ((mod->src1 == FLUID_MOD_PITCHWHEEL)
+ || (mod->flags1 & FLUID_MOD_BIPOLAR)
+ || (mod->flags2 & FLUID_MOD_BIPOLAR)
+ || (mod->amount < 0)) {
+ /* Can this modulator produce a negative contribution? */
+ v *= -1.0;
+ } else {
+ /* No negative value possible. But still, the minimum contribution is 0. */
+ v = 0;
+ }
+
+ /* For example:
+ * - current_val=100
+ * - min_val=-4000
+ * - possible_att_reduction_cB += 4100
+ */
+ if (current_val > v){
+ possible_att_reduction_cB += (current_val - v);
+ }
+ }
+ }
+
+ lower_bound = voice->attenuation-possible_att_reduction_cB;
+
+ /* SF2.01 specs do not allow negative attenuation */
+ if (lower_bound < 0) {
+ lower_bound = 0;
+ }
+ return lower_bound;
+}
+
+
+/* Purpose:
+ *
+ * Make sure, that sample start / end point and loop points are in
+ * proper order. When starting up, calculate the initial phase.
+ */
+void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
+{
+ int min_index_nonloop=(int) voice->sample->start;
+ int max_index_nonloop=(int) voice->sample->end;
+
+ /* make sure we have enough samples surrounding the loop */
+ int min_index_loop=(int) voice->sample->start + FLUID_MIN_LOOP_PAD;
+ int max_index_loop=(int) voice->sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */
+
+ if (!voice->check_sample_sanity_flag){
+ return;
+ }
+
+#if 0
+ printf("Sample from %i to %i\n",voice->sample->start, voice->sample->end);
+ printf("Sample loop from %i %i\n",voice->sample->loopstart, voice->sample->loopend);
+ printf("Playback from %i to %i\n", voice->start, voice->end);
+ printf("Playback loop from %i to %i\n",voice->loopstart, voice->loopend);
+#endif
+
+ /* Keep the start point within the sample data */
+ if (voice->start < min_index_nonloop){
+ voice->start = min_index_nonloop;
+ } else if (voice->start > max_index_nonloop){
+ voice->start = max_index_nonloop;
+ }
+
+ /* Keep the end point within the sample data */
+ if (voice->end < min_index_nonloop){
+ voice->end = min_index_nonloop;
+ } else if (voice->end > max_index_nonloop){
+ voice->end = max_index_nonloop;
+ }
+
+ /* Keep start and end point in the right order */
+ if (voice->start > voice->end){
+ int temp = voice->start;
+ voice->start = voice->end;
+ voice->end = temp;
+ /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */
+ }
+
+ /* Zero length? */
+ if (voice->start == voice->end){
+ fluid_voice_off(voice);
+ return;
+ }
+
+ if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
+ || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
+ /* Keep the loop start point within the sample data */
+ if (voice->loopstart < min_index_loop){
+ voice->loopstart = min_index_loop;
+ } else if (voice->loopstart > max_index_loop){
+ voice->loopstart = max_index_loop;
+ }
+
+ /* Keep the loop end point within the sample data */
+ if (voice->loopend < min_index_loop){
+ voice->loopend = min_index_loop;
+ } else if (voice->loopend > max_index_loop){
+ voice->loopend = max_index_loop;
+ }
+
+ /* Keep loop start and end point in the right order */
+ if (voice->loopstart > voice->loopend){
+ int temp = voice->loopstart;
+ voice->loopstart = voice->loopend;
+ voice->loopend = temp;
+ /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */
+ }
+
+ /* Loop too short? Then don't loop. */
+ if (voice->loopend < voice->loopstart + FLUID_MIN_LOOP_SIZE){
+ voice->gen[GEN_SAMPLEMODE].val = 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->loopstart >= (int)voice->sample->loopstart
+ && (int)voice->loopend <= (int)voice->sample->loopend){
+ /* Is there a valid peak amplitude available for the loop? */
+ if (voice->sample->amplitude_that_reaches_noise_floor_is_valid){
+ voice->amplitude_that_reaches_noise_floor_loop=voice->sample->amplitude_that_reaches_noise_floor / voice->synth_gain;
+ } else {
+ /* Worst case */
+ voice->amplitude_that_reaches_noise_floor_loop=voice->amplitude_that_reaches_noise_floor_nonloop;
+ };
+ };
+
+ } /* if sample mode is looped */
+
+ /* Run startup specific code (only once, when the voice is started) */
+ if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){
+ if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){
+ if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
+ || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)){
+ voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED;
+ }
+ }
+
+ /* Set the initial phase of the voice (using the result from the
+ start offset modulators). */
+ fluid_phase_set_int(voice->phase, voice->start);
+ } /* if startup */
+
+ /* Is this voice run in loop mode, or does it run straight to the
+ end of the waveform data? */
+ if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
+ || (_SAMPLEMODE(voice) == 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->phase);
+ if (index_in_sample >= voice->loopend){
+ /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */
+ fluid_phase_set_int(voice->phase, voice->loopstart);
+ }
+ }
+/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->start, voice->end, voice->loopstart, voice->loopend); */
+
+ /* Sample sanity has been assured. Don't check again, until some
+ sample parameter is changed by modulation. */
+ voice->check_sample_sanity_flag=0;
+#if 0
+ printf("Sane? playback loop from %i to %i\n", voice->loopstart, voice->loopend);
+#endif
+}
+
+
+int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs)
+{
+ voice->gen[gen].nrpn = nrpn_value;
+ voice->gen[gen].flags = (abs)? GEN_ABS_NRPN : GEN_SET;
+ fluid_voice_update_param(voice, gen);
+ return FLUID_OK;
+}
+
+int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
+{
+ /* avoid division by zero*/
+ if (gain < 0.0000001){
+ gain = 0.0000001;
+ }
+
+ voice->synth_gain = gain;
+ voice->amp_left = fluid_pan(voice->pan, 1) * gain / 32768.0f;
+ voice->amp_right = fluid_pan(voice->pan, 0) * gain / 32768.0f;
+ voice->amp_reverb = voice->reverb_send * gain / 32768.0f;
+ voice->amp_chorus = voice->chorus_send * gain / 32768.0f;
+
+ return FLUID_OK;
+}
+
+/* - Scan the loop
+ * - determine the peak level
+ * - Calculate, what factor will make the loop inaudible
+ * - Store in sample
+ */
+int fluid_voice_optimize_sample(fluid_sample_t* s)
+{
+ signed short peak_max = 0;
+ signed short peak_min = 0;
+ signed short peak;
+ fluid_real_t normalized_amplitude_during_loop;
+ double result;
+ int i;
+
+ /* ignore ROM and other(?) invalid samples */
+ if (!s->valid || (s->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
+ return (FLUID_OK);
+
+ if (!s->amplitude_that_reaches_noise_floor_is_valid){ /* Only once */
+ /* Scan the loop */
+ for (i = (int)s->loopstart; i < (int) s->loopend; i ++){
+ signed short val = s->data[i];
+ if (val > peak_max) {
+ peak_max = val;
+ } else if (val < peak_min) {
+ peak_min = val;
+ }
+ }
+
+ /* Determine the peak level */
+ if (peak_max > -peak_min){
+ peak = peak_max;
+ } else {
+ peak = -peak_min;
+ };
+ if (peak == 0){
+ /* Avoid division by zero */
+ peak = 1;
+ };
+
+ /* Calculate what factor will make the loop inaudible
+ * For example: Take a peak of 3277 (10 % of 32768). The
+ * normalized amplitude is 0.1 (10 % of 32768). An amplitude
+ * factor of 0.0001 (as opposed to the default 0.00001) will
+ * drop this sample to the noise floor.
+ */
+
+ /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */
+ normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.;
+ result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop;
+
+ /* Store in sample */
+ s->amplitude_that_reaches_noise_floor = (double)result;
+ s->amplitude_that_reaches_noise_floor_is_valid = 1;
+#if 0
+ printf("Sample peak detection: factor %f\n", (double)result);
+#endif
+ };
+ return FLUID_OK;
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.h b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.h
new file mode 100644
index 0000000..40f490c
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluid_voice.h
@@ -0,0 +1,291 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUID_VOICE_H
+#define _FLUID_VOICE_H
+
+#include "fluid_phase.h"
+#include "fluid_gen.h"
+#include "fluid_mod.h"
+
+#define NO_CHANNEL 0xff
+
+enum fluid_voice_status
+{
+ FLUID_VOICE_CLEAN,
+ FLUID_VOICE_ON,
+ FLUID_VOICE_SUSTAINED,
+ FLUID_VOICE_OFF
+};
+
+
+/*
+ * envelope data
+ */
+struct _fluid_env_data_t {
+ unsigned int count;
+ fluid_real_t coeff;
+ fluid_real_t incr;
+ 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
+};
+
+/*
+ * fluid_voice_t
+ */
+struct _fluid_voice_t
+{
+ unsigned int id; /* the id is incremented for every new noteon.
+ it's used for noteoff's */
+ unsigned char status;
+ unsigned char chan; /* the channel number, quick access for channel messages */
+ unsigned char key; /* the key, quick acces 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;
+ int has_looped; /* Flag that is set as soon as the first loop is completed. */
+ fluid_sample_t* sample;
+ int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters
+ have to be checked. */
+#if 0
+ /* Instead of keeping a pointer to a fluid_sample_t structure,
+ * I think it would be better to copy the sample data in the
+ * voice structure. SoundFont loader then do not have to
+ * allocate and maintain the fluid_sample_t structure. [PH]
+ *
+ * The notify callback may be used also for streaming samples.
+ */
+ short* sample_data; /* pointer to the sample data */
+ int sample_data_offset; /* the offset of data[0] in the whole sample */
+ int sample_data_length; /* the length of the data array */
+ unsigned int sample_start;
+ unsigned int sample_end;
+ unsigned int sample_loopstart;
+ unsigned int sample_loopend;
+ unsigned int sample_rate;
+ int sample_origpitch;
+ int sample_pitchadj;
+ int sample_type;
+ int (*sample_notify)(fluid_voice_t* voice, int reason);
+ void* sample_userdata;
+#endif
+
+ /* basic parameters */
+ fluid_real_t output_rate; /* the sample rate of the synthesizer */
+
+ unsigned int start_time;
+ unsigned int ticks;
+ unsigned int noteoff_ticks; /* Delay note-off until this tick */
+
+ fluid_real_t amp; /* current linear amplitude */
+ fluid_phase_t phase; /* the phase of the sample wave */
+
+ /* Temporary variables used in fluid_voice_write() */
+
+ fluid_real_t phase_incr; /* the phase increment for the next 64 samples */
+ fluid_real_t amp_incr; /* amplitude increment value */
+ fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */
+
+ /* End temporary variables */
+
+ /* basic parameters */
+ fluid_real_t pitch; /* the pitch in midicents */
+ 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 root_pitch;
+
+ /* 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) */
+
+ /* master gain */
+ fluid_real_t synth_gain;
+
+ /* vol env */
+ fluid_env_data_t volenv_data[FLUID_VOICE_ENVLAST];
+ unsigned int volenv_count;
+ int volenv_section;
+ fluid_real_t volenv_val;
+ fluid_real_t amplitude_that_reaches_noise_floor_nonloop;
+ fluid_real_t amplitude_that_reaches_noise_floor_loop;
+
+ /* mod env */
+ fluid_env_data_t modenv_data[FLUID_VOICE_ENVLAST];
+ unsigned int modenv_count;
+ int modenv_section;
+ fluid_real_t modenv_val; /* the value of the modulation envelope */
+ fluid_real_t modenv_to_fc;
+ fluid_real_t modenv_to_pitch;
+
+ /* mod lfo */
+ fluid_real_t modlfo_val; /* the value of the modulation LFO */
+ unsigned int modlfo_delay; /* the delay of the lfo in samples */
+ fluid_real_t modlfo_incr; /* the lfo frequency is converted to a per-buffer increment */
+ fluid_real_t modlfo_to_fc;
+ fluid_real_t modlfo_to_pitch;
+ fluid_real_t modlfo_to_vol;
+
+ /* vib lfo */
+ fluid_real_t viblfo_val; /* the value of the vibrato LFO */
+ unsigned int viblfo_delay; /* the delay of the lfo in samples */
+ fluid_real_t viblfo_incr; /* the lfo frequency is converted to a per-buffer increment */
+ fluid_real_t viblfo_to_pitch;
+
+ /* resonant filter */
+ 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 hist1, hist2; /* Sample history for the IIR filter */
+ int filter_startup; /* Flag: If set, the filter will be set directly.
+ Else it changes smoothly. */
+
+ /* 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;
+
+ /* 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;
+
+ /* interpolation method, as in fluid_interp in fluidlite.h */
+ int interp_method;
+
+ /* for debugging */
+ int debug;
+};
+
+
+fluid_voice_t* new_fluid_voice(fluid_real_t output_rate);
+int delete_fluid_voice(fluid_voice_t* voice);
+
+void fluid_voice_start(fluid_voice_t* voice);
+
+int fluid_voice_write(fluid_voice_t* voice,
+ fluid_real_t* left, fluid_real_t* right,
+ fluid_real_t* reverb_buf, fluid_real_t* chorus_buf);
+
+int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
+ fluid_channel_t* channel, int key, int vel,
+ unsigned int id, unsigned int time, fluid_real_t gain);
+
+int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl);
+int fluid_voice_modulate_all(fluid_voice_t* voice);
+
+/** Set the NRPN value of a generator. */
+int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value, int abs);
+
+
+/** Set the gain. */
+int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain);
+
+
+/** 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);
+
+int fluid_voice_noteoff(fluid_voice_t* voice);
+int fluid_voice_off(fluid_voice_t* voice);
+int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice);
+fluid_channel_t* fluid_voice_get_channel(fluid_voice_t* voice);
+int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
+ int gen_key2base, int is_decay);
+int fluid_voice_kill_excl(fluid_voice_t* voice);
+fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
+fluid_real_t fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(fluid_voice_t* voice);
+void fluid_voice_check_sample_sanity(fluid_voice_t* voice);
+
+#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) || ((voice)->status == FLUID_VOICE_SUSTAINED))
+
+/* 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)->volenv_section < FLUID_VOICE_ENVRELEASE)
+#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
+#define _AVAILABLE(voice) (((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)
+
+
+fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num);
+
+#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)
+
+#define FLUID_SAMPLESANITY_CHECK (1 << 0)
+#define FLUID_SAMPLESANITY_STARTUP (1 << 1)
+
+
+/* defined in fluid_dsp_float.c */
+
+void fluid_dsp_float_config (void);
+int fluid_dsp_float_interpolate_none (fluid_voice_t *voice);
+int fluid_dsp_float_interpolate_linear (fluid_voice_t *voice);
+int fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice);
+int fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice);
+
+#endif /* _FLUID_VOICE_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth.c b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth.c
new file mode 100644
index 0000000..a2623d7
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth.c
@@ -0,0 +1,710 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "fluid_config.h"
+
+#include
+#include
+#include
+
+#include "fluidsynth_priv.h"
+
+#if !defined(WIN32) && !defined(MACINTOSH)
+#define _GNU_SOURCE
+#include
+#endif
+
+#if defined(WIN32)
+#include
+#endif
+
+#include "fluidsynth.h"
+
+#if defined(WIN32) && !defined(MINGW32)
+#include "config_win32.h"
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include "signal.h"
+#endif
+
+#include "fluid_lash.h"
+
+#ifndef WITH_MIDI
+#define WITH_MIDI 1
+#endif
+
+/* default audio fragment count (if none specified) */
+#ifdef WIN32
+#define DEFAULT_FRAG_COUNT 32
+#else
+#define DEFAULT_FRAG_COUNT 16
+#endif
+
+void print_usage(void);
+void print_help(void);
+void print_welcome(void);
+
+static fluid_cmd_handler_t* newclient(void* data, char* addr);
+
+/*
+ * the globals
+ */
+fluid_cmd_handler_t* cmd_handler = NULL;
+int option_help = 0; /* set to 1 if "-o help" is specified */
+
+/*
+ * support for the getopt function
+ */
+#if !defined(WIN32) && !defined(MACINTOSH)
+#define GETOPT_SUPPORT 1
+int getopt(int argc, char * const argv[], const char *optstring);
+extern char *optarg;
+extern int optind, opterr, optopt;
+#endif
+
+
+/* process_o_cmd_line_option
+ *
+ * Purpose:
+ * Process a command line option -o setting=value,
+ * for example: -o synth.polyhony=16
+ */
+void process_o_cmd_line_option(fluid_settings_t* settings, char* optarg){
+ char* val;
+ for (val = optarg; *val != '\0'; val++) {
+ if (*val == '=') {
+ *val++ = 0;
+ break;
+ }
+ }
+
+ /* did user request list of settings */
+ if (strcmp (optarg, "help") == 0)
+ {
+ option_help = 1;
+ return;
+ }
+
+ /* At this point:
+ * optarg => "synth.polyphony"
+ * val => "16"
+ */
+ switch(fluid_settings_get_type(settings, optarg)){
+ case FLUID_NUM_TYPE:
+ if (fluid_settings_setnum(settings, optarg, atof(val))){
+ break;
+ };
+ case FLUID_INT_TYPE:
+ if (fluid_settings_setint(settings, optarg, atoi(val))){
+ break;
+ };
+ case FLUID_STR_TYPE:
+ if (fluid_settings_setstr(settings, optarg, val)){
+ break;
+ };
+ default:
+ fprintf (stderr, "Settings argument on command line: Failed to set \"%s\" to \"%s\".\n"
+ "Most likely the parameter \"%s\" does not exist.\n", optarg, val, optarg);
+ }
+}
+
+static void
+print_pretty_int (int i)
+{
+ if (i == INT_MAX) printf ("MAXINT");
+ else if (i == INT_MIN) printf ("MININT");
+ else printf ("%d", i);
+}
+
+/* fluid_settings_foreach function for displaying option help "-o help" */
+static void
+settings_foreach_func (void *data, char *name, int type)
+{
+ fluid_settings_t *settings = (fluid_settings_t *)data;
+ double dmin, dmax, ddef;
+ int imin, imax, idef;
+ char *defstr;
+
+ switch (type)
+ {
+ case FLUID_NUM_TYPE:
+ fluid_settings_getnum_range (settings, name, &dmin, &dmax);
+ ddef = fluid_settings_getnum_default (settings, name);
+ printf ("%-24s FLOAT [min=%0.3f, max=%0.3f, def=%0.3f]\n",
+ name, dmin, dmax, ddef);
+ break;
+ case FLUID_INT_TYPE:
+ fluid_settings_getint_range (settings, name, &imin, &imax);
+ idef = fluid_settings_getint_default (settings, name);
+ printf ("%-24s INT [min=", name);
+ print_pretty_int (imin);
+ printf (", max=");
+ print_pretty_int (imax);
+ printf (", def=");
+ print_pretty_int (idef);
+ printf ("]\n");
+ break;
+ case FLUID_STR_TYPE:
+ defstr = fluid_settings_getstr_default (settings, name);
+ printf ("%-24s STR", name);
+ if (defstr) printf (" [def='%s']\n", defstr);
+ else printf ("\n");
+ break;
+ case FLUID_SET_TYPE:
+ printf ("%-24s SET\n", name);
+ break;
+ }
+}
+
+
+#ifdef HAVE_SIGNAL_H
+/*
+ * handle_signal
+ */
+void handle_signal(int sig_num)
+{
+}
+#endif
+
+
+/*
+ * main
+ */
+int main(int argc, char** argv)
+{
+ fluid_settings_t* settings;
+ int arg1 = 1;
+ char buf[512];
+ int c, i, fragcount = DEFAULT_FRAG_COUNT;
+ int interactive = 1;
+ int midi_in = 1;
+ fluid_player_t* player = NULL;
+ fluid_midi_router_t* router = NULL;
+ fluid_midi_driver_t* mdriver = NULL;
+ fluid_audio_driver_t* adriver = NULL;
+ fluid_synth_t* synth = NULL;
+ fluid_server_t* server = NULL;
+ char* midi_id = NULL;
+ char* midi_driver = NULL;
+ char* midi_device = NULL;
+ char* config_file = NULL;
+ int audio_groups = 0;
+ int audio_channels = 0;
+ int with_server = 0;
+ int dump = 0;
+ int connect_lash = 1;
+ char *optchars = "a:C:c:df:G:g:hijK:L:lm:no:p:R:r:sVvz:";
+#ifdef LASH_ENABLED
+ int enabled_lash = 0; /* set to TRUE if lash gets enabled */
+ fluid_lash_args_t *lash_args;
+
+ lash_args = fluid_lash_extract_args (&argc, &argv);
+#endif
+
+ settings = new_fluid_settings();
+
+#ifdef GETOPT_SUPPORT /* pre section of GETOPT supported argument handling */
+ opterr = 0;
+
+ while (1) {
+ int option_index = 0;
+
+ static struct option long_options[] = {
+ {"audio-bufcount", 1, 0, 'c'},
+ {"audio-bufsize", 1, 0, 'z'},
+ {"audio-channels", 1, 0, 'L'},
+ {"audio-driver", 1, 0, 'a'},
+ {"audio-groups", 1, 0, 'G'},
+ {"chorus", 1, 0, 'C'},
+ {"connect-jack-outputs", 0, 0, 'j'},
+ {"disable-lash", 0, 0, 'l'},
+ {"dump", 0, 0, 'd'},
+ {"gain", 1, 0, 'g'},
+ {"help", 0, 0, 'h'},
+ {"load-config", 1, 0, 'f'},
+ {"midi-channels", 1, 0, 'K'},
+ {"midi-driver", 1, 0, 'm'},
+ {"no-midi-in", 0, 0, 'n'},
+ {"no-shell", 0, 0, 'i'},
+ {"option", 1, 0, 'o'},
+ {"portname", 1, 0, 'p'},
+ {"reverb", 1, 0, 'R'},
+ {"sample-rate", 1, 0, 'r'},
+ {"server", 0, 0, 's'},
+ {"verbose", 0, 0, 'v'},
+ {"version", 0, 0, 'V'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, optchars, long_options, &option_index);
+ if (c == -1) {
+ break;
+ }
+#else /* "pre" section to non getopt argument handling */
+ for (i = 1; i < argc; i++) {
+ char *optarg;
+
+ /* Skip non switch arguments (assume they are file names) */
+ if ((argv[i][0] != '-') || (argv[i][1] == '\0')) break;
+
+ c = argv[i][1];
+
+ optarg = strchr (optchars, c); /* find the option character in optchars */
+ if (optarg && optarg[1] == ':') /* colon follows if switch argument expected */
+ {
+ if (++i >= argc)
+ {
+ printf ("Option -%c requires an argument\n", c);
+ print_usage();
+ exit(0);
+ }
+ else
+ {
+ optarg = argv[i];
+ if (optarg[0] == '-')
+ {
+ printf ("Expected argument to option -%c found switch instead\n", c);
+ print_usage();
+ exit(0);
+ }
+ }
+ }
+ else optarg = "";
+#endif
+
+ switch (c) {
+#ifdef GETOPT_SUPPORT
+ case 0: /* shouldn't normally happen, a long option's flag is set to NULL */
+ printf ("option %s", long_options[option_index].name);
+ if (optarg) {
+ printf (" with arg %s", optarg);
+ }
+ printf ("\n");
+ break;
+#endif
+ case 'a':
+ fluid_settings_setstr(settings, "audio.driver", optarg);
+ break;
+ case 'C':
+ if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) {
+ fluid_settings_setstr(settings, "synth.chorus.active", "no");
+ } else {
+ fluid_settings_setstr(settings, "synth.chorus.active", "yes");
+ }
+ break;
+ case 'c':
+ fluid_settings_setint(settings, "audio.periods", atoi(optarg));
+ break;
+ case 'd':
+ fluid_settings_setstr(settings, "synth.dump", "yes");
+ dump = 1;
+ break;
+ case 'f':
+ config_file = optarg;
+ break;
+ case 'G':
+ audio_groups = atoi(optarg);
+ break;
+ case 'g':
+ fluid_settings_setnum(settings, "synth.gain", atof(optarg));
+ break;
+ case 'h':
+ print_help();
+ break;
+ case 'i':
+ interactive = 0;
+ break;
+ case 'j':
+ fluid_settings_setint(settings, "audio.jack.autoconnect", 1);
+ break;
+ case 'K':
+ fluid_settings_setint(settings, "synth.midi-channels", atoi(optarg));
+ break;
+ case 'L':
+ audio_channels = atoi(optarg);
+ fluid_settings_setint(settings, "synth.audio-channels", audio_channels);
+ break;
+ case 'l': /* disable LASH */
+ connect_lash = 0;
+ break;
+ case 'm':
+ fluid_settings_setstr(settings, "midi.driver", optarg);
+ break;
+ case 'n':
+ midi_in = 0;
+ break;
+ case 'o':
+ process_o_cmd_line_option(settings, optarg);
+ break;
+ case 'p' :
+ fluid_settings_setstr(settings, "midi.portname", optarg);
+ break;
+ case 'R':
+ if ((optarg != NULL) && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "no") == 0))) {
+ fluid_settings_setstr(settings, "synth.reverb.active", "no");
+ } else {
+ fluid_settings_setstr(settings, "synth.reverb.active", "yes");
+ }
+ break;
+ case 'r':
+ fluid_settings_setnum(settings, "synth.sample-rate", atof(optarg));
+ break;
+ case 's':
+ with_server = 1;
+ break;
+ case 'V':
+ printf("FluidSynth %s\n", VERSION);
+ exit (0);
+ break;
+ case 'v':
+ fluid_settings_setstr(settings, "synth.verbose", "yes");
+ break;
+ case 'z':
+ fluid_settings_setint(settings, "audio.period-size", atoi(optarg));
+ break;
+#ifdef GETOPT_SUPPORT
+ case '?':
+ printf ("Unknown option %c\n", optopt);
+ print_usage();
+ exit(0);
+ break;
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ break;
+#else /* Non getopt default case */
+ default:
+ printf ("Unknown switch '%c'\n", c);
+ print_usage();
+ exit(0);
+ break;
+#endif
+ } /* end of switch statement */
+ } /* end of loop */
+
+#ifdef GETOPT_SUPPORT
+ arg1 = optind;
+#else
+ arg1 = i;
+#endif
+
+ /* option help requested? "-o help" */
+ if (option_help)
+ {
+ print_welcome ();
+ printf ("FluidSynth settings:\n");
+ fluid_settings_foreach (settings, settings, settings_foreach_func);
+ exit (0);
+ }
+
+#ifdef WIN32
+ SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
+#endif
+
+#ifdef LASH_ENABLED
+ /* connect to the lash server */
+ if (connect_lash)
+ {
+ enabled_lash = fluid_lash_connect (lash_args);
+ fluid_settings_setint (settings, "lash.enable", enabled_lash ? 1 : 0);
+ }
+#endif
+
+ /* The 'groups' setting is only relevant for LADSPA operation
+ * If not given, set number groups to number of audio channels, because
+ * they are the same (there is nothing between synth output and 'sound card')
+ */
+ if ((audio_groups == 0) && (audio_channels != 0)) {
+ audio_groups = audio_channels;
+ }
+ fluid_settings_setint(settings, "synth.audio-groups", audio_groups);
+
+ /* create the synthesizer */
+ synth = new_fluid_synth(settings);
+ if (synth == NULL) {
+ fprintf(stderr, "Failed to create the synthesizer\n");
+ exit(-1);
+ }
+
+ cmd_handler = new_fluid_cmd_handler(synth);
+ if (cmd_handler == NULL) {
+ fprintf(stderr, "Failed to create the command handler\n");
+ goto cleanup;
+ }
+
+ /* try to load the user or system configuration */
+ if (config_file != NULL) {
+ fluid_source(cmd_handler, config_file);
+ } else if (fluid_get_userconf(buf, 512) != NULL) {
+ fluid_source(cmd_handler, buf);
+ } else if (fluid_get_sysconf(buf, 512) != NULL) {
+ fluid_source(cmd_handler, buf);
+ }
+
+ /* load the soundfonts (check that all non options are SoundFont or MIDI files) */
+ for (i = arg1; i < argc; i++) {
+ if (fluid_is_soundfont(argv[i]))
+ {
+ if (fluid_synth_sfload(synth, argv[i], 1) == -1)
+ fprintf(stderr, "Failed to load the SoundFont %s\n", argv[i]);
+ }
+ else if (!fluid_is_midifile(argv[i]))
+ fprintf (stderr, "Parameter '%s' not a SoundFont or MIDI file or error occurred identifying it.\n",
+ argv[i]);
+ }
+
+#ifdef HAVE_SIGNAL_H
+/* signal(SIGINT, handle_signal); */
+#endif
+
+ /* start the synthesis thread */
+ adriver = new_fluid_audio_driver(settings, synth);
+ if (adriver == NULL) {
+ fprintf(stderr, "Failed to create the audio driver\n");
+ goto cleanup;
+ }
+
+
+ /* start the midi router and link it to the synth */
+#if WITH_MIDI
+ if (midi_in) {
+ /* In dump mode, text output is generated for events going into and out of the router.
+ * The example dump functions are put into the chain before and after the router..
+ */
+
+ router = new_fluid_midi_router(
+ settings,
+ dump ? fluid_midi_dump_postrouter : fluid_synth_handle_midi_event,
+ (void*)synth);
+
+ if (router == NULL) {
+ fprintf(stderr, "Failed to create the MIDI input router; no MIDI input\n"
+ "will be available. You can access the synthesizer \n"
+ "through the console.\n");
+ } else {
+ fluid_synth_set_midi_router(synth, router); /* Fixme, needed for command handler */
+ mdriver = new_fluid_midi_driver(
+ settings,
+ dump ? fluid_midi_dump_prerouter : fluid_midi_router_handle_midi_event,
+ (void*) router);
+ if (mdriver == NULL) {
+ fprintf(stderr, "Failed to create the MIDI thread; no MIDI input\n"
+ "will be available. You can access the synthesizer \n"
+ "through the console.\n");
+ }
+ }
+ }
+#endif
+
+ /* play the midi files, if any */
+ for (i = arg1; i < argc; i++) {
+ if ((argv[i][0] != '-') && fluid_is_midifile(argv[i])) {
+
+ if (player == NULL) {
+ player = new_fluid_player(synth);
+ if (player == NULL) {
+ fprintf(stderr, "Failed to create the midifile player.\n"
+ "Continuing without a player.\n");
+ break;
+ }
+ }
+
+ fluid_player_add(player, argv[i]);
+ }
+ }
+
+ if (player != NULL) {
+ fluid_player_play(player);
+ }
+
+ /* run the server, if requested */
+#if !defined(MACINTOSH) && !defined(WIN32)
+ if (with_server) {
+ server = new_fluid_server(settings, newclient, synth);
+ if (server == NULL) {
+ fprintf(stderr, "Failed to create the server.\n"
+ "Continuing without it.\n");
+ }
+ }
+#endif
+
+
+#ifdef LASH_ENABLED
+ if (enabled_lash)
+ fluid_lash_create_thread (synth);
+#endif
+
+ /* run the shell */
+ if (interactive) {
+ print_welcome();
+
+ printf ("Type 'help' for information on commands and 'help help' for help topics.\n\n");
+
+ /* In dump mode we set the prompt to "". The UI cannot easily
+ * handle lines, which don't end with CR. Changing the prompt
+ * cannot be done through a command, because the current shell
+ * does not handle empty arguments. The ordinary case is dump ==
+ * 0.
+ */
+ fluid_settings_setstr(settings, "shell.prompt", dump ? "" : "> ");
+ fluid_usershell(settings, cmd_handler);
+ }
+
+ cleanup:
+
+#if !defined(MACINTOSH) && !defined(WIN32)
+ if (server != NULL) {
+ /* if the user typed 'quit' in the shell, kill the server */
+ if (!interactive) {
+ fluid_server_join(server);
+ }
+ delete_fluid_server(server);
+ }
+#endif
+
+ if (cmd_handler != NULL) {
+ delete_fluid_cmd_handler(cmd_handler);
+ }
+
+ if (player != NULL) {
+ /* if the user typed 'quit' in the shell, stop the player */
+ if (interactive) {
+ fluid_player_stop(player);
+ }
+ fluid_player_join(player);
+ delete_fluid_player(player);
+ }
+
+ if (router) {
+#if WITH_MIDI
+ if (mdriver) {
+ delete_fluid_midi_driver(mdriver);
+ }
+ delete_fluid_midi_router(router);
+#endif
+ }
+
+ if (adriver) {
+ delete_fluid_audio_driver(adriver);
+ }
+
+ if (synth) {
+ delete_fluid_synth(synth);
+ }
+
+ if (settings) {
+ delete_fluid_settings(settings);
+ }
+
+ return 0;
+}
+
+static fluid_cmd_handler_t* newclient(void* data, char* addr)
+{
+ fluid_synth_t* synth = (fluid_synth_t*) data;
+ return new_fluid_cmd_handler(synth);
+}
+
+
+/*
+ * print_usage
+ */
+void
+print_usage()
+{
+ print_welcome ();
+ fprintf(stderr, "Usage: fluidsynth [options] [soundfonts]\n");
+ fprintf(stderr, "Try -h for help.\n");
+ exit(0);
+}
+
+/*
+ * print_welcome
+ */
+void
+print_welcome()
+{
+ printf("FluidSynth version %s\n"
+ "Copyright (C) 2000-2006 Peter Hanappe and others.\n"
+ "Distributed under the LGPL license.\n"
+ "SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n",
+ FLUIDSYNTH_VERSION);
+}
+
+/*
+ * print_help
+ */
+void
+print_help()
+{
+ print_welcome ();
+ printf("Usage: \n");
+ printf(" fluidsynth [options] [soundfonts] [midifiles]\n");
+ printf("Possible options:\n");
+ printf(" -a, --audio-driver=[label]\n"
+ " The audio driver [alsa,jack,oss,dsound,...]\n");
+ printf(" -C, --chorus\n"
+ " Turn the chorus on or off [0|1|yes|no, default = on]\n");
+ printf(" -c, --audio-bufcount=[count]\n"
+ " Number of audio buffers\n");
+ printf(" -d, --dump\n"
+ " Dump incoming and outgoing MIDI events to stdout\n");
+ printf(" -f, --load-config\n"
+ " Load command configuration file (shell commands)\n");
+ printf(" -G, --audio-groups\n"
+ " Defines the number of LADSPA audio nodes\n");
+ printf(" -g, --gain\n"
+ " Set the master gain [0 < gain < 10, default = 0.2]\n");
+ printf(" -h, --help\n"
+ " Print out this help summary\n");
+ printf(" -i, --no-shell\n"
+ " Don't read commands from the shell [default = yes]\n");
+ printf(" -j, --connect-jack-outputs\n"
+ " Attempt to connect the jack outputs to the physical ports\n");
+ printf(" -K, --midi-channels=[num]\n"
+ " The number of midi channels [default = 16]\n");
+ printf(" -L, --audio-channels=[num]\n"
+ " The number of stereo audio channels [default = 1]\n");
+#ifdef LASH_ENABLED
+ printf(" -l, --disable-lash\n"
+ " Don't connect to LASH server\n");
+#endif
+ printf(" -m, --midi-driver=[label]\n"
+ " The name of the midi driver to use [oss,alsa,alsa_seq,...]\n");
+ printf(" -n, --no-midi-in\n"
+ " Don't create a midi driver to read MIDI input events [default = yes]\n");
+ printf(" -p, --portname=[label]\n"
+ " Set MIDI port name (alsa_seq, coremidi drivers)\n");
+ printf(" -o\n"
+ " Define a setting, -o name=value (\"-o help\" to dump current values)\n");
+ printf(" -R, --reverb\n"
+ " Turn the reverb on or off [0|1|yes|no, default = on]\n");
+ printf(" -r, --sample-rate\n"
+ " Set the sample rate\n");
+ printf(" -s, --server\n"
+ " Start FluidSynth as a server process\n");
+ printf(" -V, --version\n"
+ " Show version of program\n");
+ printf(" -v, --verbose\n"
+ " Print out verbose messages about midi events\n");
+ printf(" -z, --audio-bufsize=[size]\n"
+ " Size of each audio buffer\n");
+ exit(0);
+}
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth_priv.h b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth_priv.h
new file mode 100644
index 0000000..2604326
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/fluidsynth_priv.h
@@ -0,0 +1,228 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#ifndef _FLUIDSYNTH_PRIV_H
+#define _FLUIDSYNTH_PRIV_H
+
+#include "fluid_config.h"
+
+#if HAVE_STRING_H
+#include
+#endif
+
+#if HAVE_STDLIB_H
+#include
+#endif
+
+#if HAVE_STDIO_H
+#include
+#endif
+
+#if HAVE_MATH_H
+#include
+#endif
+
+#if HAVE_STDARG_H
+#include
+#endif
+
+#if HAVE_FCNTL_H
+#include
+#endif
+
+#if HAVE_LIMITS_H
+#include
+#endif
+
+
+#include "fluidlite.h"
+
+
+/***************************************************************
+ *
+ * BASIC TYPES
+ */
+
+#if defined(WITH_FLOAT)
+typedef float fluid_real_t;
+#else
+typedef double fluid_real_t;
+#endif
+
+
+typedef enum {
+ FLUID_OK = 0,
+ FLUID_FAILED = -1
+} fluid_status;
+
+
+//socket disabled
+
+/** Integer types */
+
+#if 1 /**/
+typedef signed char sint8;
+typedef unsigned char uint8;
+typedef signed short sint16;
+typedef unsigned short uint16;
+typedef signed int sint32;
+typedef unsigned int uint32;
+typedef signed long long sint64;
+typedef unsigned long long uint64;
+
+#if defined(__LP64__) || defined(_WIN64)
+typedef uint64 uintptr;
+#else
+typedef uint32 uintptr;
+#endif
+
+#else /**/
+#if defined(MINGW32)
+
+/* Windows using MinGW32 */
+typedef int8_t sint8;
+typedef uint8_t uint8;
+typedef int16_t sint16;
+typedef uint16_t uint16;
+typedef int32_t sint32;
+typedef uint32_t uint32;
+typedef int64_t sint64;
+typedef uint64_t uint64;
+
+#elif defined(_WIN32)
+
+/* Windows */
+typedef signed __int8 sint8;
+typedef unsigned __int8 uint8;
+typedef signed __int16 sint16;
+typedef unsigned __int16 uint16;
+typedef signed __int32 sint32;
+typedef unsigned __int32 uint32;
+typedef signed __int64 sint64;
+typedef unsigned __int64 uint64;
+
+#elif defined(MACOS9)
+
+/* Macintosh */
+typedef signed char sint8;
+typedef unsigned char uint8;
+typedef signed short sint16;
+typedef unsigned short uint16;
+typedef signed int sint32;
+typedef unsigned int uint32;
+/* FIXME: needs to be verified */
+typedef long long sint64;
+typedef unsigned long long uint64;
+
+#else
+
+/* Linux & Darwin */
+typedef int8_t sint8;
+typedef u_int8_t uint8;
+typedef int16_t sint16;
+typedef u_int16_t uint16;
+typedef int32_t sint32;
+typedef u_int32_t uint32;
+typedef int64_t sint64;
+typedef u_int64_t uint64;
+
+#endif
+#endif /* */
+
+
+/***************************************************************
+ *
+ * FORWARD DECLARATIONS
+ */
+typedef struct _fluid_env_data_t fluid_env_data_t;
+typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t;
+typedef struct _fluid_channel_t fluid_channel_t;
+typedef struct _fluid_tuning_t fluid_tuning_t;
+typedef struct _fluid_hashtable_t fluid_hashtable_t;
+typedef struct _fluid_client_t fluid_client_t;
+
+/***************************************************************
+ *
+ * CONSTANTS
+ */
+
+#define FLUID_BUFSIZE 64
+
+#ifndef PI
+#define PI 3.141592654
+#endif
+
+/***************************************************************
+ *
+ * SYSTEM INTERFACE
+ */
+typedef FILE* fluid_file;
+
+#define FLUID_MALLOC(_n) malloc(_n)
+#define FLUID_REALLOC(_p,_n) realloc(_p,_n)
+#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t))
+#define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t))
+#define FLUID_FREE(_p) free(_p)
+#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
+#define FLUID_FCLOSE(_f) fclose(_f)
+#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f)
+#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set)
+#define FLUID_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_STRCHR(_s,_c) strchr(_s,_c)
+#define FLUID_STRDUP(s) FLUID_STRCPY((char*)FLUID_MALLOC(FLUID_STRLEN(s) + 1), s)
+#define FLUID_SPRINTF sprintf
+#define FLUID_FPRINTF fprintf
+
+#define fluid_clip(_val, _min, _max) \
+{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); }
+
+#if WITH_FTS
+#define FLUID_PRINTF post
+#define FLUID_FLUSH()
+#else
+#define FLUID_PRINTF printf
+#define FLUID_FLUSH() fflush(stdout)
+#endif
+
+#define FLUID_LOG fluid_log
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795
+#endif
+
+
+#define FLUID_ASSERT(a,b)
+#define FLUID_ASSERT_P(a,b)
+
+char* fluid_error(void);
+
+
+/* Internationalization */
+#define _(s) s
+
+
+#endif /* _FLUIDSYNTH_PRIV_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidlite.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidlite.h
new file mode 100644
index 0000000..efe8633
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidlite.h
@@ -0,0 +1,102 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_H
+#define _FLUIDSYNTH_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+#if defined(WIN32)
+#if defined(FLUIDSYNTH_DLL_EXPORTS)
+#define FLUIDSYNTH_API __declspec(dllexport)
+#elif defined(FLUIDSYNTH_NOT_A_DLL)
+#define FLUIDSYNTH_API
+#else
+#define FLUIDSYNTH_API __declspec(dllimport)
+#endif
+
+#elif defined(MACOS9)
+#define FLUIDSYNTH_API __declspec(export)
+
+#else
+#define FLUIDSYNTH_API
+#endif
+*/
+#if defined(__WATCOMC__) && defined(FLUIDSYNTH_DLL_EXPORTS)
+#define FLUIDSYNTH_API __declspec(dllexport) /* watcom needs the dllexport */
+#else
+#define FLUIDSYNTH_API
+#endif
+
+/**
+ * @file fluidsynth.h
+ * @brief FluidSynth is a real-time synthesizer designed for SoundFont(R) files.
+ *
+ * This is the header of the fluidsynth library and contains the
+ * synthesizer's public API.
+ *
+ * Depending on how you want to use or extend the synthesizer you
+ * will need different API functions. You probably do not need all
+ * of them. Here is what you might want to do:
+ *
+ * o Embedded synthesizer: create a new synthesizer and send MIDI
+ * events to it. The sound goes directly to the audio output of
+ * your system.
+ *
+ * o Plugin synthesizer: create a synthesizer and send MIDI events
+ * but pull the audio back into your application.
+ *
+ * o SoundFont plugin: create a new type of "SoundFont" and allow
+ * the synthesizer to load your type of SoundFonts.
+ *
+ * o MIDI input: Create a MIDI handler to read the MIDI input on your
+ * machine and send the MIDI events directly to the synthesizer.
+ *
+ * o MIDI files: Open MIDI files and send the MIDI events to the
+ * synthesizer.
+ *
+ * o Command lines: You can send textual commands to the synthesizer.
+ *
+ * SoundFont(R) is a registered trademark of E-mu Systems, Inc.
+ */
+
+#include "fluidsynth/types.h"
+#include "fluidsynth/settings.h"
+#include "fluidsynth/synth.h"
+#include "fluidsynth/sfont.h"
+#include "fluidsynth/ramsfont.h"
+#include "fluidsynth/log.h"
+#include "fluidsynth/misc.h"
+#include "fluidsynth/mod.h"
+#include "fluidsynth/gen.h"
+#include "fluidsynth/voice.h"
+#include "fluidsynth/version.h"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/gen.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/gen.h
new file mode 100644
index 0000000..055ea75
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/gen.h
@@ -0,0 +1,135 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_GEN_H
+#define _FLUIDSYNTH_GEN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file gen.h
+ * @brief Functions and defines for SoundFont generator effects.
+ */
+
+/**
+ * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3)
+ */
+enum fluid_gen_type {
+ GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */
+ GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */
+ GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */
+ GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */
+ GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */
+ GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */
+ GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */
+ GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */
+ GEN_FILTERFC, /**< Filter cutoff */
+ GEN_FILTERQ, /**< Filter Q */
+ GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */
+ GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */
+ GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */
+ GEN_MODLFOTOVOL, /**< Modulation LFO to volume */
+ GEN_UNUSED1, /**< Unused */
+ GEN_CHORUSSEND, /**< Chorus send amount */
+ GEN_REVERBSEND, /**< Reverb send amount */
+ GEN_PAN, /**< Stereo panning */
+ GEN_UNUSED2, /**< Unused */
+ GEN_UNUSED3, /**< Unused */
+ GEN_UNUSED4, /**< Unused */
+ GEN_MODLFODELAY, /**< Modulation LFO delay */
+ GEN_MODLFOFREQ, /**< Modulation LFO frequency */
+ GEN_VIBLFODELAY, /**< Vibrato LFO delay */
+ GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */
+ GEN_MODENVDELAY, /**< Modulation envelope delay */
+ GEN_MODENVATTACK, /**< Modulation envelope attack */
+ GEN_MODENVHOLD, /**< Modulation envelope hold */
+ GEN_MODENVDECAY, /**< Modulation envelope decay */
+ GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */
+ GEN_MODENVRELEASE, /**< Modulation envelope release */
+ GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */
+ GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */
+ GEN_VOLENVDELAY, /**< Volume envelope delay */
+ GEN_VOLENVATTACK, /**< Volume envelope attack */
+ GEN_VOLENVHOLD, /**< Volume envelope hold */
+ GEN_VOLENVDECAY, /**< Volume envelope decay */
+ GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */
+ GEN_VOLENVRELEASE, /**< Volume envelope release */
+ GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */
+ GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */
+ GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */
+ GEN_RESERVED1, /**< Reserved */
+ GEN_KEYRANGE, /**< MIDI note range */
+ GEN_VELRANGE, /**< MIDI velocity range */
+ GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */
+ GEN_KEYNUM, /**< Fixed MIDI note number */
+ GEN_VELOCITY, /**< Fixed MIDI velocity value */
+ GEN_ATTENUATION, /**< Initial volume attenuation */
+ GEN_RESERVED2, /**< Reserved */
+ GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */
+ GEN_COARSETUNE, /**< Coarse tuning */
+ GEN_FINETUNE, /**< Fine tuning */
+ GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */
+ GEN_SAMPLEMODE, /**< Sample mode flags */
+ GEN_RESERVED3, /**< Reserved */
+ GEN_SCALETUNE, /**< Scale tuning */
+ GEN_EXCLUSIVECLASS, /**< Exclusive class number */
+ GEN_OVERRIDEROOTKEY, /**< Sample root note override */
+
+ /* the initial pitch is not a "standard" generator. It is not
+ * mentioned in the list of generator in the SF2 specifications. It
+ * is used, however, as the destination for the default pitch wheel
+ * modulator. */
+ GEN_PITCH, /**< Pitch (NOTE: Not a real SoundFont generator) */
+ GEN_LAST /**< Value defines the count of generators (#fluid_gen_type) */
+};
+
+
+/**
+ * SoundFont generator structure.
+ */
+typedef struct _fluid_gen_t
+{
+ unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */
+ double val; /**< The nominal value */
+ double mod; /**< Change by modulators */
+ double nrpn; /**< Change by NRPN messages */
+} fluid_gen_t;
+
+/**
+ * Enum value for 'flags' field of #_fluid_gen_t (not really flags).
+ */
+enum fluid_gen_flags
+{
+ GEN_UNUSED, /**< Generator value is not set */
+ GEN_SET, /**< Generator value is set */
+ GEN_ABS_NRPN /**< DOCME */
+};
+
+FLUIDSYNTH_API int fluid_gen_set_default_values(fluid_gen_t* gen);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FLUIDSYNTH_GEN_H */
+
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/log.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/log.h
new file mode 100644
index 0000000..51fdebe
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/log.h
@@ -0,0 +1,83 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_LOG_H
+#define _FLUIDSYNTH_LOG_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @file log.h
+ * @brief Logging interface
+ *
+ * The default logging function of the fluidsynth prints its messages
+ * to the stderr. The synthesizer uses five level of messages: #FLUID_PANIC,
+ * #FLUID_ERR, #FLUID_WARN, #FLUID_INFO, and #FLUID_DBG.
+ *
+ * A client application can install a new log function to handle the
+ * messages differently. In the following example, the application
+ * sets a callback function to display #FLUID_PANIC messages in a dialog,
+ * and ignores all other messages by setting the log function to
+ * NULL:
+ *
+ * DOCME (formatting)
+ * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window);
+ * fluid_set_log_function(FLUID_ERR, NULL, NULL);
+ * fluid_set_log_function(FLUID_WARN, NULL, NULL);
+ * fluid_set_log_function(FLUID_DBG, NULL, NULL);
+ */
+
+/**
+ * FluidSynth log levels.
+ */
+enum fluid_log_level {
+ FLUID_PANIC, /**< The synth can't function correctly any more */
+ FLUID_ERR, /**< Serious error occurred */
+ FLUID_WARN, /**< Warning */
+ FLUID_INFO, /**< Verbose informational messages */
+ FLUID_DBG, /**< Debugging messages */
+ LAST_LOG_LEVEL
+};
+
+/**
+ * Log function handler callback type used by fluid_set_log_function().
+ * @param level Log level (#fluid_log_level)
+ * @param message Log message text
+ * @param data User data pointer supplied to fluid_set_log_function().
+ */
+typedef void (*fluid_log_function_t)(int level, char* message, void* data);
+
+FLUIDSYNTH_API
+fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data);
+
+FLUIDSYNTH_API void fluid_default_log_function(int level, char* message, void* data);
+
+FLUIDSYNTH_API int fluid_log(int level, char * fmt, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_LOG_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/misc.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/misc.h
new file mode 100644
index 0000000..b7d4fdf
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/misc.h
@@ -0,0 +1,65 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_MISC_H
+#define _FLUIDSYNTH_MISC_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ *
+ * Utility functions
+ */
+
+/**
+ * fluid_is_soundfont returns 1 if the specified filename is a
+ * soundfont. It retuns 0 otherwise. The current implementation only
+ * checks for the "RIFF" header in the file. It is useful only to
+ * distinguish between SoundFonts and MIDI files.
+ */
+FLUIDSYNTH_API int fluid_is_soundfont(char* filename);
+
+/**
+ * fluid_is_midifile returns 1 if the specified filename is a MIDI
+ * file. It retuns 0 otherwise. The current implementation only checks
+ * for the "MThd" header in the file.
+ */
+FLUIDSYNTH_API int fluid_is_midifile(char* filename);
+
+
+
+
+#ifdef WIN32
+/** Set the handle to the instance of the application on the Windows
+ platform. The handle is needed to open DirectSound. */
+FLUIDSYNTH_API void* fluid_get_hinstance(void);
+FLUIDSYNTH_API void fluid_set_hinstance(void* hinstance);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_MISC_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/mod.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/mod.h
new file mode 100644
index 0000000..5fe86cd
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/mod.h
@@ -0,0 +1,112 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_MOD_H
+#define _FLUIDSYNTH_MOD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Modulator-related definitions */
+
+ /* Maximum number of modulators in a voice */
+#define FLUID_NUM_MOD 64
+
+ /*
+ * fluid_mod_t
+ */
+struct _fluid_mod_t
+{
+ unsigned char dest;
+ unsigned char src1;
+ unsigned char flags1;
+ unsigned char src2;
+ unsigned char flags2;
+ double 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 telling the polarity of a modulator. Compare with SF2.01
+ section 8.2. Note: The numbers of the bits are different! (for
+ example: in the flags of a SF modulator, the polarity bit is bit
+ nr. 9) */
+enum fluid_mod_flags
+{
+ FLUID_MOD_POSITIVE = 0,
+ FLUID_MOD_NEGATIVE = 1,
+ FLUID_MOD_UNIPOLAR = 0,
+ FLUID_MOD_BIPOLAR = 2,
+ FLUID_MOD_LINEAR = 0,
+ FLUID_MOD_CONCAVE = 4,
+ FLUID_MOD_CONVEX = 8,
+ FLUID_MOD_SWITCH = 12,
+ FLUID_MOD_GC = 0,
+ FLUID_MOD_CC = 16
+};
+
+/* Flags telling the source of a modulator. This corresponds to
+ * SF2.01 section 8.2.1 */
+enum fluid_mod_src
+{
+ FLUID_MOD_NONE = 0,
+ FLUID_MOD_VELOCITY = 2,
+ FLUID_MOD_KEY = 3,
+ FLUID_MOD_KEYPRESSURE = 10,
+ FLUID_MOD_CHANNELPRESSURE = 13,
+ FLUID_MOD_PITCHWHEEL = 14,
+ FLUID_MOD_PITCHWHEELSENS = 16
+};
+
+/* Allocates memory for a new modulator */
+FLUIDSYNTH_API fluid_mod_t * fluid_mod_new(void);
+
+/* Frees the modulator */
+FLUIDSYNTH_API void fluid_mod_delete(fluid_mod_t * mod);
+
+
+FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t* mod, int src, int flags);
+FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t* mod, int src, int flags);
+FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t* mod, int dst);
+FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t* mod, double amount);
+
+FLUIDSYNTH_API int fluid_mod_get_source1(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_flags1(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_source2(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_flags2(fluid_mod_t* mod);
+FLUIDSYNTH_API int fluid_mod_get_dest(fluid_mod_t* mod);
+FLUIDSYNTH_API double fluid_mod_get_amount(fluid_mod_t* mod);
+
+
+/* Determines, if two modulators are 'identical' (all parameters
+ except the amount match) */
+FLUIDSYNTH_API int fluid_mod_test_identity(fluid_mod_t * mod1, fluid_mod_t * mod2);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FLUIDSYNTH_MOD_H */
+
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/ramsfont.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/ramsfont.h
new file mode 100644
index 0000000..75a8d67
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/ramsfont.h
@@ -0,0 +1,113 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_RAMSFONT_H
+#define _FLUIDSYNTH_RAMSFONT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/********************************************************************************/
+/********************************************************************************/
+/* ram soundfonts:
+ October 2002 - Antoine Schmitt
+
+ ram soundfonts live in ram. The samples are loaded from files
+ or from RAM. A minimal API manages a soundFont structure,
+ with presets, each preset having only one preset-zone, which
+ instrument has potentially many instrument-zones. No global
+ zones, and nor generator nor modulator other than the default
+ ones are permitted. This may be extensible in the future.
+*/
+/********************************************************************************/
+/********************************************************************************/
+
+/*
+ We are not using the sfloader protocol, as we need more arguments
+ than what it provides.
+*/
+
+/** Creates a fluid_sfont_t wrapping an fluid_ramsfont_t */
+FLUIDSYNTH_API fluid_sfont_t* fluid_ramsfont_create_sfont(void);
+
+/***********************
+ * ramsfont specific API
+ ***********************/
+FLUIDSYNTH_API int fluid_ramsfont_set_name(fluid_ramsfont_t* sfont, char * name);
+
+/* Creates one instrument zone for the sample inside the preset defined
+ * by bank/num
+ * \returns 0 if success
+ */
+FLUIDSYNTH_API
+int fluid_ramsfont_add_izone(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample,
+ int lokey, int hikey);
+
+/* Removes the instrument zone corresponding to bank/num and to the sample
+ * \returns 0 if success
+ */
+FLUIDSYNTH_API
+int fluid_ramsfont_remove_izone(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample);
+
+/* Sets a generator on an instrument zone
+ * \returns 0 if success
+ */
+FLUIDSYNTH_API
+int fluid_ramsfont_izone_set_gen(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample,
+ int gen_type, float value);
+
+/* Utility : sets the loop start/end values
+ * \on = 0 or 1; if 0, loopstart and loopend are not used
+ * \loopstart and loopend are floats, in frames
+ * \loopstart is counted from frame 0
+ * \loopend is counted from the last frame, thus is < 0
+ * \returns 0 if success
+ */
+FLUIDSYNTH_API
+int fluid_ramsfont_izone_set_loop(fluid_ramsfont_t* sfont,
+ unsigned int bank, unsigned int num, fluid_sample_t* sample,
+ int on, float loopstart, float loopend);
+
+/***************************************
+ * sample_t specific API for ramsfont
+ ***************************************/
+FLUIDSYNTH_API fluid_sample_t* new_fluid_ramsample(void);
+FLUIDSYNTH_API int delete_fluid_ramsample(fluid_sample_t* sample);
+FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t* sample, char * name);
+
+/* Sets the sound data of the sample
+ * Warning : if copy_data is FALSE, data should have 8 unused frames at start
+ * and 8 unused frames at the end.
+ */
+FLUIDSYNTH_API
+int fluid_sample_set_sound_data(fluid_sample_t* sample, short *data,
+ unsigned int nbframes, short copy_data, int rootkey);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_RAMSFONT_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/settings.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/settings.h
new file mode 100644
index 0000000..6ffaab2
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/settings.h
@@ -0,0 +1,202 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_SETTINGS_H
+#define _FLUIDSYNTH_SETTINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /**
+ *
+ * Synthesizer settings
+ *
+ *
+ * The create a synthesizer object you will have to specify its
+ * settings. These settings are stored in the structure below.
+
+ * void my_synthesizer()
+ * {
+ * fluid_settings_t* settings;
+ * fluid_synth_t* synth;
+ * fluid_audio_driver_t* adriver;
+ *
+ *
+ * settings = new_fluid_settings();
+ * fluid_settings_setstr(settings, "audio.driver", "alsa");
+ * // ... change settings ...
+ * synth = new_fluid_synth(settings);
+ * adriver = new_fluid_audio_driver(settings, synth);
+ *
+ * ...
+ *
+ * }
+ *
+ *
+ */
+
+
+
+
+/* Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field
+ of the FLUID_PortRangeHint should be considered meaningful. The
+ value in this field should be considered the (inclusive) lower
+ bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also
+ specified then the value of LowerBound should be multiplied by the
+ sample rate. */
+#define FLUID_HINT_BOUNDED_BELOW 0x1
+
+/* Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+ of the FLUID_PortRangeHint should be considered meaningful. The
+ value in this field should be considered the (inclusive) upper
+ bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also
+ specified then the value of UpperBound should be multiplied by the
+ sample rate. */
+#define FLUID_HINT_BOUNDED_ABOVE 0x2
+
+/* Hint FLUID_HINT_TOGGLED indicates that the data item should be
+ considered a Boolean toggle. Data less than or equal to zero should
+ be considered `off' or `false,' and data above zero should be
+ considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in
+ conjunction with any other hint except FLUID_HINT_DEFAULT_0 or
+ FLUID_HINT_DEFAULT_1. */
+#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. Any bounds set should be slightly wider than the actual
+ integer range required to avoid floating point rounding errors. For
+ instance, the integer set {0,1,2,3} might be described as [-0.1,
+ 3.1]. */
+#define FLUID_HINT_INTEGER 0x20
+
+
+#define FLUID_HINT_FILENAME 0x01
+#define FLUID_HINT_OPTIONLIST 0x02
+
+
+
+enum fluid_types_enum {
+ FLUID_NO_TYPE = -1,
+ FLUID_NUM_TYPE,
+ FLUID_INT_TYPE,
+ FLUID_STR_TYPE,
+ FLUID_SET_TYPE
+};
+
+
+FLUIDSYNTH_API fluid_settings_t* new_fluid_settings(void);
+FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t* settings);
+
+
+
+FLUIDSYNTH_API
+int fluid_settings_get_type(fluid_settings_t* settings, const char* name);
+
+FLUIDSYNTH_API
+int fluid_settings_get_hints(fluid_settings_t* settings, const char* name);
+
+/** Returns whether the setting is changeable in real-time. */
+FLUIDSYNTH_API int fluid_settings_is_realtime(fluid_settings_t* settings, const char* name);
+
+
+/** returns 1 if the value has been set, 0 otherwise */
+FLUIDSYNTH_API
+int fluid_settings_setstr(fluid_settings_t* settings, const char* name, const char* str);
+
+/**
+ Get the value of a string setting. 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. Instead,
+ the application should make a copy of the value if it needs it
+ later.
+
+ \returns 1 if the value exists, 0 otherwise
+*/
+FLUIDSYNTH_API
+int fluid_settings_getstr(fluid_settings_t* settings, const char* name, char** str);
+
+/** Get the default value of a string setting. */
+FLUIDSYNTH_API
+char* fluid_settings_getstr_default(fluid_settings_t* settings, const char* name);
+
+/** Get the value of a numeric setting.
+
+ \returns 1 if the value exists and is equal to 'value', 0
+ otherwise
+*/
+FLUIDSYNTH_API
+int fluid_settings_str_equal(fluid_settings_t* settings, const char* name, char* value);
+
+
+/** returns 1 if the value has been set, 0 otherwise */
+FLUIDSYNTH_API
+int fluid_settings_setnum(fluid_settings_t* settings, const char* name, double val);
+
+/** returns 1 if the value exists, 0 otherwise */
+FLUIDSYNTH_API
+int fluid_settings_getnum(fluid_settings_t* settings, const char* name, double* val);
+
+/** Get the default value of a string setting. */
+FLUIDSYNTH_API
+double fluid_settings_getnum_default(fluid_settings_t* settings, const char* name);
+
+/** Get the range of values of a numeric settings. */
+FLUIDSYNTH_API
+void fluid_settings_getnum_range(fluid_settings_t* settings, const char* name,
+ double* min, double* max);
+
+
+/** returns 1 if the value has been set, 0 otherwise */
+FLUIDSYNTH_API
+int fluid_settings_setint(fluid_settings_t* settings, const char* name, int val);
+
+/** returns 1 if the value exists, 0 otherwise */
+FLUIDSYNTH_API
+int fluid_settings_getint(fluid_settings_t* settings, const char* name, int* val);
+
+/** Get the default value of a string setting. */
+FLUIDSYNTH_API
+int fluid_settings_getint_default(fluid_settings_t* settings, const char* name);
+
+/** Get the range of values of a numeric settings. */
+FLUIDSYNTH_API
+void fluid_settings_getint_range(fluid_settings_t* settings, const char* name,
+ int* min, int* max);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_SETTINGS_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/sfont.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/sfont.h
new file mode 100644
index 0000000..1d5120b
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/sfont.h
@@ -0,0 +1,252 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_SFONT_H
+#define _FLUIDSYNTH_SFONT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+ /**
+ *
+ * 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.
+ *
+ * To add a new SoundFont loader to the synthesizer, call
+ * fluid_synth_add_sfloader() and pass a pointer to an
+ * fluid_sfloader_t structure. The important callback function in
+ * this structure is "load", which should try to load a file and
+ * returns a fluid_sfont_t structure, or NULL if it fails.
+ *
+ * The fluid_sfont_t structure contains a callback to obtain the
+ * name of the soundfont. It contains two functions to iterate
+ * though the contained presets, and one function to obtain a
+ * preset corresponding to a bank and preset number. This
+ * function should return an fluid_preset_t structure.
+ *
+ * The fluid_preset_t structure contains some functions to obtain
+ * information from the preset (name, bank, number). The most
+ * important callback is the noteon function. The noteon function
+ * should call fluid_synth_alloc_voice() for every sample that has
+ * to be played. fluid_synth_alloc_voice() expects a pointer to a
+ * fluid_sample_t structure and returns a pointer to the opaque
+ * fluid_voice_t structure. To set or increments the values of a
+ * generator, use fluid_voice_gen_{set,incr}. When you are
+ * finished initializing the voice call fluid_voice_start() to
+ * start playing the synthesis voice.
+ * */
+
+ enum {
+ FLUID_PRESET_SELECTED,
+ FLUID_PRESET_UNSELECTED,
+ FLUID_SAMPLE_DONE
+ };
+
+
+/*
+ * fluid_sfloader_t
+ */
+
+struct _fluid_sfloader_t {
+ /** Private data */
+ void* data;
+
+ /** The free must free the memory allocated for the loader in
+ * addition to any private data. It should return 0 if no error
+ * occured, non-zero otherwise.*/
+ int (*free)(fluid_sfloader_t* loader);
+
+ /** Load a file. Returns NULL if an error occured. */
+ fluid_sfont_t* (*load)(fluid_sfloader_t* loader, const char* filename);
+
+ /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */
+ fluid_fileapi_t* fileapi;
+};
+
+/**
+ * File callback structure to enable custom soundfont loading (e.g. from memory).
+ */
+struct _fluid_fileapi_t {
+ /** Private data */
+ void* data;
+
+ /**
+ * The free must free the memory allocated for the loader in
+ * addition to any private data. It should return 0 if no error
+ * occured, non-zero otherwise.
+ */
+ int (*free)(fluid_fileapi_t* fileapi);
+
+ /**
+ * Opens the file or memory indicated by \c filename in binary read mode.
+ * \c filename matches the one provided during the fluid_synth_sfload() call.
+ *
+ * @return returns a file handle on success, NULL otherwise
+ */
+ void *(*fopen)(fluid_fileapi_t* fileapi, const char * filename);
+
+ /**
+ * Reads \c count bytes to the specified buffer \c buf.
+ *
+ * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else #FLUID_FAILED
+ */
+ int (*fread)(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 */
+ int (*fseek)(void* handle, long offset, int origin);
+
+ /**
+ * Closes the handle and frees used ressources.
+ *
+ * @return returns #FLUID_OK on success, #FLUID_FAILED on error */
+ int (*fclose)(void* handle);
+
+ /** @return returns current file offset or #FLUID_FAILED on error */
+ long (*ftell)(void* handle);
+};
+
+FLUIDSYNTH_API void fluid_init_default_fileapi(fluid_fileapi_t* fileapi);
+
+FLUIDSYNTH_API void fluid_set_default_fileapi(fluid_fileapi_t* fileapi);
+
+FLUIDSYNTH_API fluid_sfloader_t* new_fluid_defsfloader();
+
+FLUIDSYNTH_API int delete_fluid_defsfloader(fluid_sfloader_t* loader);
+
+/*
+ * fluid_sfont_t
+ */
+
+struct _fluid_sfont_t {
+ void* data;
+ unsigned int id;
+
+ /** The 'free' callback function should return 0 when it was able to
+ free all resources. It should return a non-zero value if some of
+ the samples could not be freed because they are still in use. */
+ int (*free)(fluid_sfont_t* sfont);
+
+ /** Return the name of the sfont */
+ char* (*get_name)(fluid_sfont_t* sfont);
+
+ /** Return the preset with the specified bank and preset number. All
+ * the fields, including the 'sfont' field, should * be filled
+ * in. If the preset cannot be found, the function returns NULL. */
+ fluid_preset_t* (*get_preset)(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum);
+
+ void (*iteration_start)(fluid_sfont_t* sfont);
+
+ /* return 0 when no more presets are available, 1 otherwise */
+ int (*iteration_next)(fluid_sfont_t* sfont, fluid_preset_t* preset);
+};
+
+#define fluid_sfont_get_id(_sf) ((_sf)->id)
+
+
+/*
+ * fluid_preset_t
+ */
+
+struct _fluid_preset_t {
+ void* data;
+ fluid_sfont_t* sfont;
+ int (*free)(fluid_preset_t* preset);
+ char* (*get_name)(fluid_preset_t* preset);
+ int (*get_banknum)(fluid_preset_t* preset);
+ int (*get_num)(fluid_preset_t* preset);
+
+ /** handle a noteon event. Returns 0 if no error occured. */
+ int (*noteon)(fluid_preset_t* preset, fluid_synth_t* synth, int chan, int key, int vel);
+
+ /** Implement this function if the preset needs to be notified about
+ preset select and unselect events. */
+ int (*notify)(fluid_preset_t* preset, int reason, int chan);
+};
+
+
+/*
+ * fluid_sample_t
+ */
+
+struct _fluid_sample_t
+{
+ char name[21];
+ unsigned int start;
+ unsigned int end; /* Note: Index of last valid sample point (contrary to SF spec) */
+ unsigned int loopstart;
+ unsigned int loopend; /* Note: first point following the loop (superimposed on loopstart) */
+ unsigned int samplerate;
+ int origpitch;
+ int pitchadj;
+ int sampletype;
+ int valid;
+ short* data;
+
+ /** The amplitude, that will lower the level of the sample's loop to
+ the noise floor. Needed for note turnoff optimization, will be
+ filled out automatically */
+ /* Set this to zero, when submitting a new sample. */
+ int amplitude_that_reaches_noise_floor_is_valid;
+ double amplitude_that_reaches_noise_floor;
+
+ /** Count the number of playing voices that use this sample. */
+ unsigned int refcount;
+
+ /** Implement this function if the sample or SoundFont needs to be
+ notified when the sample is no longer used. */
+ int (*notify)(fluid_sample_t* sample, int reason);
+
+ /** Pointer to SoundFont specific data */
+ void* userdata;
+};
+
+
+#define fluid_sample_refcount(_sample) ((_sample)->refcount)
+
+
+/** Sample types */
+
+#define FLUID_SAMPLETYPE_MONO 1
+#define FLUID_SAMPLETYPE_RIGHT 2
+#define FLUID_SAMPLETYPE_LEFT 4
+#define FLUID_SAMPLETYPE_LINKED 8
+#define FLUID_SAMPLETYPE_OGG_VORBIS 0x10 /**< Flag for #fluid_sample_t \a sampletype field for Ogg Vorbis compressed samples */
+#define FLUID_SAMPLETYPE_OGG_VORBIS_UNPACKED 0x20
+#define FLUID_SAMPLETYPE_ROM 0x8000
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_SFONT_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/synth.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/synth.h
new file mode 100644
index 0000000..9462725
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/synth.h
@@ -0,0 +1,713 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_SYNTH_H
+#define _FLUIDSYNTH_SYNTH_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+ /** Embedded synthesizer
+ *
+ * You create a new synthesizer with new_fluid_synth() and you destroy
+ * if with delete_fluid_synth(). Use the settings structure to specify
+ * the synthesizer characteristics.
+ *
+ * You have to load a SoundFont in order to hear any sound. For that
+ * you use the fluid_synth_sfload() function.
+ *
+ * You can use the audio driver functions described below to open
+ * the audio device and create a background audio thread.
+ *
+ * The API for sending MIDI events is probably what you expect:
+ * fluid_synth_noteon(), fluid_synth_noteoff(), ...
+ *
+ */
+
+
+ /** Creates a new synthesizer object.
+ *
+ * Creates a new synthesizer object. As soon as the synthesizer is
+ * created, it will start playing.
+ *
+ * \param settings a pointer to a settings structure
+ * \return a newly allocated synthesizer or NULL in case of error
+ */
+FLUIDSYNTH_API fluid_synth_t* new_fluid_synth(fluid_settings_t* settings);
+
+FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t* synth, float sample_rate);
+
+
+ /**
+ * Deletes the synthesizer previously created with new_fluid_synth.
+ *
+ * \param synth the synthesizer object
+ * \return 0 if no error occured, -1 otherwise
+ */
+FLUIDSYNTH_API int delete_fluid_synth(fluid_synth_t* synth);
+
+
+ /** Get a reference to the settings of the synthesizer.
+ *
+ * \param synth the synthesizer object
+ * \return pointer to the settings
+ */
+FLUIDSYNTH_API fluid_settings_t* fluid_synth_get_settings(fluid_synth_t* synth);
+
+
+ /*
+ *
+ * MIDI channel messages
+ *
+ */
+
+ /** Send a noteon message. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel);
+
+ /** Send a noteoff message. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key);
+
+ /** Send a control change message. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t* synth, int chan, int ctrl, int val);
+
+ /** Get a control value. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int ctrl, int* pval);
+
+ /** Send a pitch bend message. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val);
+
+ /** Get the pitch bend value. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API
+int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend);
+
+ /** Set the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, int val);
+
+ /** Get the pitch wheel sensitivity. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval);
+
+ /** Send a program change message. Returns 0 if no error occurred, -1 otherwise. */
+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_sysex(fluid_synth_t *synth, const char *data, int len,
+ char *response, int *response_len, int *handled, int dryrun);
+
+ /** Select a bank. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API
+int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank);
+
+ /** Select a sfont. Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API
+int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id);
+
+ /** Select a preset for a channel. The preset is specified by the
+ SoundFont ID, the bank number, and the preset number. This
+ allows any preset to be selected and circumvents preset masking
+ due to previously loaded SoundFonts on the SoundFont stack.
+
+ \param synth The synthesizer
+ \param chan The channel on which to set the preset
+ \param sfont_id The ID of the SoundFont
+ \param bank_num The bank number
+ \param preset_num The preset number
+ \return 0 if no errors occured, -1 otherwise
+ */
+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);
+
+ /** Returns the program, bank, and SoundFont number of the preset on
+ a given channel. Returns 0 if no error occurred, -1 otherwise. */
+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);
+
+ /** Send a bank select and a program change to every channel to
+ * reinitialize the preset of the channel. This function is useful
+ * mainly after a SoundFont has been loaded, unloaded or
+ * reloaded. . Returns 0 if no error occurred, -1 otherwise. */
+FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t* synth);
+
+ /** Send a reset. A reset turns all the notes off and resets the
+ controller values. */
+FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t* synth);
+
+
+ /*
+ *
+ * Low level access
+ *
+ */
+
+ /** Create and start voices using a preset. The id passed as
+ * argument will be used as the voice group id. */
+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);
+
+ /** Stop the voices in the voice group defined by id. */
+FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t* synth, unsigned int id);
+
+ /** Change the value of a generator of the voices in the voice group
+ * defined by id. */
+/* FLUIDSYNTH_API int fluid_synth_ctrl(fluid_synth_t* synth, int id, */
+/* int gen, float value, */
+/* int absolute, int normalized); */
+
+
+ /*
+ *
+ * SoundFont management
+ *
+ */
+
+ /** Set an optional function callback each time a preset has finished loading.
+ This can be useful when calling fluid_synth_sfload asynchronously.
+ The function must be formatted like this:
+ void my_callback_function(int bank, int num, char* name)
+
+ \param callback Pointer to the function
+ */
+FLUIDSYNTH_API
+void fluid_synth_set_preset_callback(void* callback);
+
+ /** Loads a SoundFont file and creates a new SoundFont. The newly
+ loaded SoundFont will be put on top of the SoundFont
+ stack. Presets are searched starting from the SoundFont on the
+ top of the stack, working the way down the stack until a preset
+ is found.
+
+ \param synth The synthesizer object
+ \param filename The file name
+ \param reset_presets If non-zero, the presets on the channels will be reset
+ \returns The ID of the loaded SoundFont, or -1 in case of error
+ */
+FLUIDSYNTH_API
+int fluid_synth_sfload(fluid_synth_t* synth, const char* filename, int reset_presets);
+
+ /** Reload a SoundFont. The reloaded SoundFont retains its ID and
+ index on the stack.
+
+ \param synth The synthesizer object
+ \param id The id of the SoundFont
+ \returns The ID of the loaded SoundFont, or -1 in case of error
+ */
+FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t* synth, unsigned int id);
+
+ /** Removes a SoundFont from the stack and deallocates it.
+
+ \param synth The synthesizer object
+ \param id The id of the SoundFont
+ \param reset_presets If TRUE then presets will be reset for all channels
+ \returns 0 if no error, -1 otherwise
+ */
+FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t* synth, unsigned int id, int reset_presets);
+
+ /** Add a SoundFont. The SoundFont will be put on top of
+ the SoundFont stack.
+
+ \param synth The synthesizer object
+ \param sfont The SoundFont
+ \returns The ID of the loaded SoundFont, or -1 in case of error
+ */
+FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont);
+
+ /** Remove a SoundFont that was previously added using
+ * fluid_synth_add_sfont(). The synthesizer does not delete the
+ * SoundFont; this is responsability of the caller.
+
+ \param synth The synthesizer object
+ \param sfont The SoundFont
+ */
+FLUIDSYNTH_API void fluid_synth_remove_sfont(fluid_synth_t* synth, fluid_sfont_t* sfont);
+
+ /** Count the number of loaded SoundFonts.
+
+ \param synth The synthesizer object
+ \returns The number of loaded SoundFonts
+ */
+FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t* synth);
+
+ /** Get a SoundFont. The SoundFont is specified by its index on the
+ stack. The top of the stack has index zero.
+
+ \param synth The synthesizer object
+ \param num The number of the SoundFont (0 <= num < sfcount)
+ \returns A pointer to the SoundFont
+ */
+FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont(fluid_synth_t* synth, unsigned int num);
+
+ /** Get a SoundFont. The SoundFont is specified by its ID.
+
+ \param synth The synthesizer object
+ \param id The id of the sfont
+ \returns A pointer to the SoundFont
+ */
+FLUIDSYNTH_API fluid_sfont_t* fluid_synth_get_sfont_by_id(fluid_synth_t* synth, unsigned int id);
+
+
+ /** Get the preset of a channel */
+FLUIDSYNTH_API fluid_preset_t* fluid_synth_get_channel_preset(fluid_synth_t* synth, int chan);
+
+ /** Offset the bank numbers in a SoundFont. Returns -1 if an error
+ * occured (out of memory or negative offset) */
+FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t* synth, int sfont_id, int offset);
+
+ /** Get the offset of the bank numbers in a SoundFont. */
+FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t* synth, int sfont_id);
+
+
+
+ /*
+ *
+ * Reverb
+ *
+ */
+
+ /** Set the parameters for the built-in reverb unit */
+FLUIDSYNTH_API void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize,
+ double damping, double width, double level);
+
+ /** Turn on (1) / off (0) the built-in reverb unit */
+FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t* synth, int on);
+
+
+ /** Query the current state of the reverb. */
+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);
+
+ /* Those are the default settings for the reverb */
+#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f
+#define FLUID_REVERB_DEFAULT_DAMP 0.0f
+#define FLUID_REVERB_DEFAULT_WIDTH 0.5f
+#define FLUID_REVERB_DEFAULT_LEVEL 0.9f
+
+
+
+ /*
+ *
+ * Chorus
+ *
+ */
+
+enum fluid_chorus_mod {
+ FLUID_CHORUS_MOD_SINE = 0,
+ FLUID_CHORUS_MOD_TRIANGLE = 1
+};
+
+ /** Set up the chorus. It should be turned on with fluid_synth_set_chorus_on.
+ * If faulty parameters are given, all new settings are discarded.
+ * Keep in mind, that the needed CPU time is proportional to 'nr'.
+ */
+FLUIDSYNTH_API void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level,
+ double speed, double depth_ms, int type);
+
+ /** Turn on (1) / off (0) the built-in chorus unit */
+FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t* synth, int on);
+
+ /** Query the current state of the chorus. */
+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 */
+
+ /* Those are the default settings for the chorus. */
+#define FLUID_CHORUS_DEFAULT_N 3
+#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f
+#define FLUID_CHORUS_DEFAULT_SPEED 0.3f
+#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f
+#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE
+
+
+
+ /*
+ *
+ * Audio and MIDI channels
+ *
+ */
+
+ /** Returns the number of MIDI channels that the synthesizer uses
+ internally */
+FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t* synth);
+
+ /** Returns the number of audio channels that the synthesizer uses
+ internally */
+FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t* synth);
+
+ /** Returns the number of audio groups that the synthesizer uses
+ internally. This is usually identical to audio_channels. */
+FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t* synth);
+
+ /** Returns the number of effects channels that the synthesizer uses
+ internally */
+FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t* synth);
+
+
+
+ /*
+ *
+ * Synthesis parameters
+ *
+ */
+
+ /** Set the master gain */
+FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t* synth, float gain);
+
+ /** Get the master gain */
+FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t* synth);
+
+ /** Set the polyphony limit (FluidSynth >= 1.0.6) */
+FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t* synth, int polyphony);
+
+ /** Get the polyphony limit (FluidSynth >= 1.0.6) */
+FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t* synth);
+
+ /** Get the internal buffer size. The internal buffer size if not the
+ same thing as the buffer size specified in the
+ settings. Internally, the synth *always* uses a specific buffer
+ size independent of the buffer size used by the audio driver. The
+ internal buffer size is normally 64 samples. The reason why it
+ uses an internal buffer size is to allow audio drivers to call the
+ synthesizer with a variable buffer length. The internal buffer
+ size is useful for client who want to optimize their buffer sizes.
+ */
+FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t* synth);
+
+ /** Set the interpolation method for one channel or all channels (chan = -1) */
+FLUIDSYNTH_API
+int fluid_synth_set_interp_method(fluid_synth_t* synth, int chan, int interp_method);
+
+ /* Flags to choose the interpolation method */
+enum fluid_interp {
+ /* no interpolation: Fastest, but questionable audio quality */
+ FLUID_INTERP_NONE = 0,
+ /* Straight-line interpolation: A bit slower, reasonable audio quality */
+ FLUID_INTERP_LINEAR = 1,
+ /* Fourth-order interpolation: Requires 50 % of the whole DSP processing time, good quality
+ * Default. */
+ FLUID_INTERP_DEFAULT = 4,
+ FLUID_INTERP_4THORDER = 4,
+ FLUID_INTERP_7THORDER = 7,
+ FLUID_INTERP_HIGHEST=7
+};
+
+
+
+
+ /*
+ *
+ * Generator interface
+ *
+ */
+
+ /** Change the value of a generator. This function allows to control
+ all synthesis parameters in real-time. The changes are additive,
+ i.e. they add up to the existing parameter value. This function is
+ similar to sending an NRPN message to the synthesizer. The
+ function accepts a float as the value of the parameter. The
+ parameter numbers and ranges are described in the SoundFont 2.01
+ specification, paragraph 8.1.3, page 48. See also 'fluid_gen_type'.
+
+ \param synth The synthesizer object.
+ \param chan The MIDI channel number.
+ \param param The parameter number.
+ \param value The parameter value.
+ \returns Your favorite dish.
+ */
+FLUIDSYNTH_API
+int fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value);
+
+
+ /** Retreive the value of a generator. This function returns the value
+ set by a previous call 'fluid_synth_set_gen' or by an NRPN message.
+
+ \param synth The synthesizer object.
+ \param chan The MIDI channel number.
+ \param param The generator number.
+ \returns The value of the generator.
+ */
+FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param);
+
+
+
+
+ /*
+ *
+ * Tuning
+ *
+ */
+
+ /** Create a new key-based tuning with given name, number, and
+ pitches. The array 'pitches' should have length 128 and contains
+ the pitch in cents of every key in cents. However, if 'pitches' is
+ NULL, a new tuning is created with the well-tempered scale.
+
+ \param synth The synthesizer object
+ \param tuning_bank The tuning bank number [0-127]
+ \param tuning_prog The tuning program number [0-127]
+ \param name The name of the tuning
+ \param pitch The array of pitch values. The array length has to be 128.
+ */
+FLUIDSYNTH_API
+int fluid_synth_create_key_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog,
+ const char* name, double* pitch);
+
+ /** Create a new octave-based tuning with given name, number, and
+ pitches. The array 'pitches' should have length 12 and contains
+ derivation in cents from the well-tempered scale. For example, if
+ pitches[0] equals -33, then the C-keys will be tuned 33 cents
+ below the well-tempered C.
+
+ \param synth The synthesizer object
+ \param tuning_bank The tuning bank number [0-127]
+ \param tuning_prog The tuning program number [0-127]
+ \param name The name of the tuning
+ \param pitch The array of pitch derivations. The array length has to be 12.
+ */
+FLUIDSYNTH_API
+int fluid_synth_create_octave_tuning(fluid_synth_t* synth, int tuning_bank, int tuning_prog,
+ const char* name, const double* pitch);
+
+FLUIDSYNTH_API
+int fluid_synth_activate_octave_tuning(fluid_synth_t* synth, int bank, int prog,
+ const char* name, const double* pitch, int apply);
+
+ /** Request a note tuning changes. Both they 'keys' and 'pitches'
+ arrays should be of length 'num_pitches'. If 'apply' is non-zero,
+ the changes should be applied in real-time, i.e. sounding notes
+ will have their pitch updated. 'APPLY' IS CURRENTLY IGNORED. The
+ changes will be available for newly triggered notes only.
+
+ \param synth The synthesizer object
+ \param tuning_bank The tuning bank number [0-127]
+ \param tuning_prog The tuning program number [0-127]
+ \param len The length of the keys and pitch arrays
+ \param keys The array of keys values.
+ \param pitch The array of pitch values.
+ \param apply Flag to indicate whether to changes should be applied in real-time.
+ */
+FLUIDSYNTH_API
+int fluid_synth_tune_notes(fluid_synth_t* synth, int tuning_bank, int tuning_prog,
+ int len, int *keys, double* pitch, int apply);
+
+ /** Select a tuning for a channel.
+
+ \param synth The synthesizer object
+ \param chan The channel number [0-max channels]
+ \param tuning_bank The tuning bank number [0-127]
+ \param tuning_prog The tuning program number [0-127]
+ */
+FLUIDSYNTH_API
+int fluid_synth_select_tuning(fluid_synth_t* synth, int chan, int tuning_bank, int tuning_prog);
+
+int fluid_synth_activate_tuning(fluid_synth_t* synth, int chan, int bank, int prog, int apply);
+
+ /** Set the tuning to the default well-tempered tuning on a channel.
+
+ \param synth The synthesizer object
+ \param chan The channel number [0-max channels]
+ */
+FLUIDSYNTH_API int fluid_synth_reset_tuning(fluid_synth_t* synth, int chan);
+
+ /** Start the iteration throught the list of available tunings.
+
+ \param synth The synthesizer object
+ */
+FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t* synth);
+
+
+ /** Get the next tuning in the iteration. This functions stores the
+ bank and program number of the next tuning in the pointers given as
+ arguments.
+
+ \param synth The synthesizer object
+ \param bank Pointer to an int to store the bank number
+ \param prog Pointer to an int to store the program number
+ \returns 1 if there is a next tuning, 0 otherwise
+ */
+FLUIDSYNTH_API
+int fluid_synth_tuning_iteration_next(fluid_synth_t* synth, int* bank, int* prog);
+
+
+ /** Dump the data of a tuning. This functions stores the name and
+ pitch values of a tuning in the pointers given as arguments. Both
+ name and pitch can be NULL is the data is not needed.
+
+ \param synth The synthesizer object
+ \param bank The tuning bank number [0-127]
+ \param prog The tuning program number [0-127]
+ \param name Pointer to a buffer to store the name
+ \param len The length of the name buffer
+ \param pitch Pointer to buffer to store the pitch values
+ */
+FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t* synth, int bank, int prog,
+ char* name, int len, double* pitch);
+
+
+
+
+ /*
+ *
+ * Misc
+ *
+ */
+
+ /** Get a textual representation of the last error */
+FLUIDSYNTH_API char* fluid_synth_error(fluid_synth_t* synth);
+
+
+ /*
+ *
+ * Synthesizer plugin
+ *
+ *
+ * To create a synthesizer plugin, create the synthesizer as
+ * explained above. Once the synthesizer is created you can call
+ * any of the functions below to get the audio.
+ *
+ */
+
+ /** Generate a number of samples. This function expects two signed
+ * 16bits buffers (left and right channel) that will be filled with
+ * samples.
+ *
+ * \param synth The synthesizer
+ * \param len The number of samples to generate
+ * \param lout The sample buffer for the left channel
+ * \param loff The offset, in samples, in the left buffer where the writing pointer starts
+ * \param lincr The increment, in samples, of the writing pointer in the left buffer
+ * \param rout The sample buffer for the right channel
+ * \param roff The offset, in samples, in the right buffer where the writing pointer starts
+ * \param rincr The increment, in samples, of the writing pointer in the right buffer
+ * \returns 0 if no error occured, non-zero otherwise
+ */
+
+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);
+
+
+ /** Generate a number of samples. This function expects two floating
+ * point buffers (left and right channel) that will be filled with
+ * samples.
+ *
+ * \param synth The synthesizer
+ * \param len The number of samples to generate
+ * \param lout The sample buffer for the left channel
+ * \param loff The offset, in samples, in the left buffer where the writing pointer starts
+ * \param lincr The increment, in samples, of the writing pointer in the left buffer
+ * \param rout The sample buffer for the right channel
+ * \param roff The offset, in samples, in the right buffer where the writing pointer starts
+ * \param rincr The increment, in samples, of the writing pointer in the right buffer
+ * \returns 0 if no error occured, non-zero otherwise
+ */
+
+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);
+
+ /** Generate a number of samples. This function implements the
+ * default interface defined in fluidsynth/audio.h. This function
+ * ignores the input buffers and expects at least two output
+ * buffer.
+ *
+ * \param synth The synthesizer
+ * \param len The number of samples to generate
+ * \param nin The number of input buffers
+ * \param in The array of input buffers
+ * \param nout The number of output buffers
+ * \param out The array of output buffers
+ * \returns 0 if no error occured, non-zero otherwise
+ */
+
+FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t* synth, int len,
+ int nin, float** in,
+ int nout, float** out);
+
+
+
+ /* Type definition of the synthesizer's audio callback function. */
+typedef int (*fluid_audio_callback_t)(fluid_synth_t* synth, int len,
+ void* out1, int loff, int lincr,
+ void* out2, int roff, int rincr);
+
+
+
+
+
+ /*
+ * Synthesizer's interface to handle SoundFont loaders
+ */
+
+
+ /** Add a SoundFont loader to the synthesizer. Note that SoundFont
+ loader don't necessarily load SoundFonts. They can load any type
+ of wavetable data but export a SoundFont interface. */
+FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader);
+
+ /** Allocate a synthesis voice. This function is called by a
+ soundfont's preset in response to a noteon event.
+ The returned voice comes with default modulators installed (velocity-to-attenuation,
+ velocity to filter, ...)
+ Note: A single noteon event may create any number of voices, when the preset is layered.
+ Typically 1 (mono) or 2 (stereo).*/
+FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample,
+ int channum, int key, int vel);
+
+ /** Start a synthesis voice. This function is called by a
+ soundfont's preset in response to a noteon event after the voice
+ has been allocated with fluid_synth_alloc_voice() and
+ initialized.
+ Exclusive classes are processed here.*/
+FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice);
+
+
+ /** Write a list of all voices matching ID into buf, but not more than bufsize voices.
+ * If ID <0, return all voices. */
+FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth,
+ fluid_voice_t* buf[], int bufsize, int ID);
+
+
+//midi router disabled
+// /** This is a hack to get command handlers working */
+//FLUIDSYNTH_API void fluid_synth_set_midi_router(fluid_synth_t* synth,
+// fluid_midi_router_t* router);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_SYNTH_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/types.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/types.h
new file mode 100644
index 0000000..34f3ec7
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/types.h
@@ -0,0 +1,67 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_TYPES_H
+#define _FLUIDSYNTH_TYPES_H
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+
+ Forward declarations
+
+*/
+typedef struct _fluid_hashtable_t fluid_settings_t;
+typedef struct _fluid_synth_t fluid_synth_t;
+typedef struct _fluid_voice_t fluid_voice_t;
+typedef struct _fluid_sfloader_t fluid_sfloader_t;
+typedef struct _fluid_sfont_t fluid_sfont_t;
+typedef struct _fluid_preset_t fluid_preset_t;
+typedef struct _fluid_sample_t fluid_sample_t;
+typedef struct _fluid_mod_t fluid_mod_t;
+typedef struct _fluid_audio_driver_t fluid_audio_driver_t;
+typedef struct _fluid_player_t fluid_player_t;
+typedef struct _fluid_midi_event_t fluid_midi_event_t;
+typedef struct _fluid_midi_driver_t fluid_midi_driver_t;
+typedef struct _fluid_midi_router_t fluid_midi_router_t;
+typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t;
+typedef struct _fluid_hashtable_t fluid_cmd_handler_t;
+typedef struct _fluid_shell_t fluid_shell_t;
+typedef struct _fluid_server_t fluid_server_t;
+typedef struct _fluid_event_t fluid_event_t;
+typedef struct _fluid_sequencer_t fluid_sequencer_t;
+typedef struct _fluid_ramsfont_t fluid_ramsfont_t;
+typedef struct _fluid_rampreset_t fluid_rampreset_t;
+typedef struct _fluid_fileapi_t fluid_fileapi_t;
+
+typedef int fluid_istream_t;
+typedef int fluid_ostream_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_TYPES_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/version.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/version.h
new file mode 100644
index 0000000..69d621b
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/version.h
@@ -0,0 +1,44 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_VERSION_H
+#define _FLUIDSYNTH_VERSION_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FLUIDSYNTH_VERSION "1.2.1"
+#define FLUIDSYNTH_VERSION_MAJOR 1
+#define FLUIDSYNTH_VERSION_MINOR 2
+#define FLUIDSYNTH_VERSION_MICRO 1
+
+
+FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro);
+
+FLUIDSYNTH_API char* fluid_version_str(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLUIDSYNTH_VERSION_H */
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/voice.h b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/voice.h
new file mode 100644
index 0000000..13b6b80
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/include/fluidsynth/voice.h
@@ -0,0 +1,97 @@
+/* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _FLUIDSYNTH_VOICE_H
+#define _FLUIDSYNTH_VOICE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+ /*
+ * The interface to the synthesizer's voices
+ * Examples on using them can be found in fluid_defsfont.c
+ */
+
+ /** 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.*/
+
+FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t* voice, int gen);
+
+
+ /* for fluid_voice_add_mod */
+enum fluid_voice_add_mod{
+ FLUID_VOICE_OVERWRITE,
+ FLUID_VOICE_ADD,
+ FLUID_VOICE_DEFAULT
+};
+
+ /* Add a modulator to a voice (SF2.1 only). */
+FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode);
+
+ /** Set the value of a generator */
+FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val);
+
+ /** Get the value of a generator */
+FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen);
+
+ /** Modify the value of a generator by val */
+FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val);
+
+
+ /** Return the unique ID of the noteon-event. A sound font loader
+ * may store the voice processes it has created for * real-time
+ * control during the operation of a voice (for example: parameter
+ * changes in sound font editor). The synth uses a pool of
+ * voices, which are 'recycled' and never deallocated.
+ *
+ * Before modifying an existing voice, check
+ * - that its state is still 'playing'
+ * - that the ID is still the same
+ * Otherwise the voice has finished playing.
+ */
+FLUIDSYNTH_API unsigned int fluid_voice_get_id(fluid_voice_t* voice);
+
+
+FLUIDSYNTH_API int fluid_voice_is_playing(fluid_voice_t* voice);
+
+ /** If the peak volume during the loop is known, then the voice can
+ * be released earlier during the release phase. Otherwise, the
+ * voice will operate (inaudibly), until the envelope is at the
+ * nominal turnoff point. In many cases the loop volume is many dB
+ * below the maximum volume. For example, the loop volume for a
+ * typical acoustic piano is 20 dB below max. Taking that into
+ * account in the turn-off algorithm we can save 20 dB / 100 dB =>
+ * 1/5 of the total release time.
+ * So it's a good idea to call fluid_voice_optimize_sample
+ * on each sample once.
+ */
+
+FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FLUIDSYNTH_VOICE_H */
+
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/main.cbp b/Examples/Tutorial10Fluidlite/src/standalone/main.cbp
new file mode 100644
index 0000000..7b4481a
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/main.cbp
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Tutorial10Fluidlite/src/standalone/stb/stb_vorbis.c b/Examples/Tutorial10Fluidlite/src/standalone/stb/stb_vorbis.c
new file mode 100644
index 0000000..8bd7e90
--- /dev/null
+++ b/Examples/Tutorial10Fluidlite/src/standalone/stb/stb_vorbis.c
@@ -0,0 +1,5480 @@
+// Ogg Vorbis audio decoder - v1.20 - public domain
+// http://nothings.org/stb_vorbis/
+//
+// Original version written by Sean Barrett in 2007.
+//
+// Originally sponsored by RAD Game Tools. Seeking implementation
+// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
+// Elias Software, Aras Pranckevicius, and Sean Barrett.
+//
+// LICENSE
+//
+// See end of file for license information.
+//
+// Limitations:
+//
+// - floor 0 not supported (used in old ogg vorbis files pre-2004)
+// - lossless sample-truncation at beginning ignored
+// - cannot concatenate multiple vorbis streams
+// - sample positions are 32-bit, limiting seekable 192Khz
+// files to around 6 hours (Ogg supports 64-bit)
+//
+// Feature contributors:
+// Dougall Johnson (sample-exact seeking)
+//
+// Bugfix/warning contributors:
+// Terje Mathisen Niklas Frykholm Andy Hill
+// Casey Muratori John Bolton Gargaj
+// Laurent Gomila Marc LeBlanc Ronny Chevalier
+// Bernhard Wodo Evan Balster github:alxprd
+// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
+// Phillip Bennefall Rohit Thiago Goulart
+// github:manxorist saga musix github:infatum
+// Timur Gagiev Maxwell Koo Peter Waller
+// github:audinowho Dougall Johnson David Reid
+// github:Clownacy Pedro J. Estebanez Remi Verschelde
+//
+// Partial history:
+// 1.20 - 2020-07-11 - several small fixes
+// 1.19 - 2020-02-05 - warnings
+// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.
+// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)
+// 1.16 - 2019-03-04 - fix warnings
+// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
+// 1.14 - 2018-02-11 - delete bogus dealloca usage
+// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
+// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+// 1.11 - 2017-07-23 - fix MinGW compilation
+// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version
+// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
+// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
+// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
+// some crash fixes when out of memory or with corrupt files
+// fix some inappropriately signed shifts
+// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
+// 1.04 - 2014-08-27 - fix missing const-correct case in API
+// 1.03 - 2014-08-07 - warning fixes
+// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
+// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
+// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
+// (API change) report sample rate for decode-full-file funcs
+//
+// See end of file for full version history.
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// HEADER BEGINS HERE
+//
+
+#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
+#define STB_VORBIS_INCLUDE_STB_VORBIS_H
+
+#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
+#define STB_VORBIS_NO_STDIO 1
+#endif
+
+#ifndef STB_VORBIS_NO_STDIO
+#include
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/////////// THREAD SAFETY
+
+// Individual stb_vorbis* handles are not thread-safe; you cannot decode from
+// them from multiple threads at the same time. However, you can have multiple
+// stb_vorbis* handles and decode from them independently in multiple thrads.
+
+
+/////////// MEMORY ALLOCATION
+
+// normally stb_vorbis uses malloc() to allocate memory at startup,
+// and alloca() to allocate temporary memory during a frame on the
+// stack. (Memory consumption will depend on the amount of setup
+// data in the file and how you set the compile flags for speed
+// vs. size. In my test files the maximal-size usage is ~150KB.)
+//
+// You can modify the wrapper functions in the source (setup_malloc,
+// setup_temp_malloc, temp_malloc) to change this behavior, or you
+// can use a simpler allocation model: you pass in a buffer from
+// which stb_vorbis will allocate _all_ its memory (including the
+// temp memory). "open" may fail with a VORBIS_outofmem if you
+// do not pass in enough data; there is no way to determine how
+// much you do need except to succeed (at which point you can
+// query get_info to find the exact amount required. yes I know
+// this is lame).
+//
+// If you pass in a non-NULL buffer of the type below, allocation
+// will occur from it as described above. Otherwise just pass NULL
+// to use malloc()/alloca()
+
+typedef struct
+{
+ char *alloc_buffer;
+ int alloc_buffer_length_in_bytes;
+} stb_vorbis_alloc;
+
+
+/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES
+
+typedef struct stb_vorbis stb_vorbis;
+
+typedef struct
+{
+ unsigned int sample_rate;
+ int channels;
+
+ unsigned int setup_memory_required;
+ unsigned int setup_temp_memory_required;
+ unsigned int temp_memory_required;
+
+ int max_frame_size;
+} stb_vorbis_info;
+
+// get general information about the file
+extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
+
+// get the last error detected (clears it, too)
+extern int stb_vorbis_get_error(stb_vorbis *f);
+
+// close an ogg vorbis file and free all memory in use
+extern void stb_vorbis_close(stb_vorbis *f);
+
+// this function returns the offset (in samples) from the beginning of the
+// file that will be returned by the next decode, if it is known, or -1
+// otherwise. after a flush_pushdata() call, this may take a while before
+// it becomes valid again.
+// NOT WORKING YET after a seek with PULLDATA API
+extern int stb_vorbis_get_sample_offset(stb_vorbis *f);
+
+// returns the current seek point within the file, or offset from the beginning
+// of the memory buffer. In pushdata mode it returns 0.
+extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
+
+/////////// PUSHDATA API
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+
+// this API allows you to get blocks of data from any source and hand
+// them to stb_vorbis. you have to buffer them; stb_vorbis will tell
+// you how much it used, and you have to give it the rest next time;
+// and stb_vorbis may not have enough data to work with and you will
+// need to give it the same data again PLUS more. Note that the Vorbis
+// specification does not bound the size of an individual frame.
+
+extern stb_vorbis *stb_vorbis_open_pushdata(
+ const unsigned char * datablock, int datablock_length_in_bytes,
+ int *datablock_memory_consumed_in_bytes,
+ int *error,
+ const stb_vorbis_alloc *alloc_buffer);
+// create a vorbis decoder by passing in the initial data block containing
+// the ogg&vorbis headers (you don't need to do parse them, just provide
+// the first N bytes of the file--you're told if it's not enough, see below)
+// on success, returns an stb_vorbis *, does not set error, returns the amount of
+// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;
+// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed
+// if returns NULL and *error is VORBIS_need_more_data, then the input block was
+// incomplete and you need to pass in a larger block from the start of the file
+
+extern int stb_vorbis_decode_frame_pushdata(
+ stb_vorbis *f,
+ const unsigned char *datablock, int datablock_length_in_bytes,
+ int *channels, // place to write number of float * buffers
+ float ***output, // place to write float ** array of float * buffers
+ int *samples // place to write number of output samples
+ );
+// decode a frame of audio sample data if possible from the passed-in data block
+//
+// return value: number of bytes we used from datablock
+//
+// possible cases:
+// 0 bytes used, 0 samples output (need more data)
+// N bytes used, 0 samples output (resynching the stream, keep going)
+// N bytes used, M samples output (one frame of data)
+// note that after opening a file, you will ALWAYS get one N-bytes,0-sample
+// frame, because Vorbis always "discards" the first frame.
+//
+// Note that on resynch, stb_vorbis will rarely consume all of the buffer,
+// instead only datablock_length_in_bytes-3 or less. This is because it wants
+// to avoid missing parts of a page header if they cross a datablock boundary,
+// without writing state-machiney code to record a partial detection.
+//
+// The number of channels returned are stored in *channels (which can be
+// NULL--it is always the same as the number of channels reported by
+// get_info). *output will contain an array of float* buffers, one per
+// channel. In other words, (*output)[0][0] contains the first sample from
+// the first channel, and (*output)[1][0] contains the first sample from
+// the second channel.
+
+extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
+// inform stb_vorbis that your next datablock will not be contiguous with
+// previous ones (e.g. you've seeked in the data); future attempts to decode
+// frames will cause stb_vorbis to resynchronize (as noted above), and
+// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it
+// will begin decoding the _next_ frame.
+//
+// if you want to seek using pushdata, you need to seek in your file, then
+// call stb_vorbis_flush_pushdata(), then start calling decoding, then once
+// decoding is returning you data, call stb_vorbis_get_sample_offset, and
+// if you don't like the result, seek your file again and repeat.
+#endif
+
+
+////////// PULLING INPUT API
+
+#ifndef STB_VORBIS_NO_PULLDATA_API
+// This API assumes stb_vorbis is allowed to pull data from a source--
+// either a block of memory containing the _entire_ vorbis stream, or a
+// FILE * that you or it create, or possibly some other reading mechanism
+// if you go modify the source to replace the FILE * case with some kind
+// of callback to your code. (But if you don't support seeking, you may
+// just want to go ahead and use pushdata.)
+
+#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
+#endif
+#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
+#endif
+// decode an entire file and output the data interleaved into a malloc()ed
+// buffer stored in *output. The return value is the number of samples
+// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.
+// When you're done with it, just free() the pointer returned in *output.
+
+extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
+ int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
+// this must be the entire stream!). on failure, returns NULL and sets *error
+
+#ifndef STB_VORBIS_NO_STDIO
+extern stb_vorbis * stb_vorbis_open_filename(const char *filename,
+ int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from a filename via fopen(). on failure,
+// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
+
+extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
+ int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
+// note that stb_vorbis must "own" this stream; if you seek it in between
+// calls to stb_vorbis, it will become confused. Moreover, if you attempt to
+// perform stb_vorbis_seek_*() operations on this file, it will assume it
+// owns the _entire_ rest of the file after the start point. Use the next
+// function, stb_vorbis_open_file_section(), to limit it.
+
+extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
+ int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
+// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
+// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
+// this stream; if you seek it in between calls to stb_vorbis, it will become
+// confused.
+#endif
+
+extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
+extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
+// these functions seek in the Vorbis file to (approximately) 'sample_number'.
+// after calling seek_frame(), the next call to get_frame_*() will include
+// the specified sample. after calling stb_vorbis_seek(), the next call to
+// stb_vorbis_get_samples_* will start with the specified sample. If you
+// do not need to seek to EXACTLY the target sample when using get_samples_*,
+// you can also use seek_frame().
+
+extern int stb_vorbis_seek_start(stb_vorbis *f);
+// this function is equivalent to stb_vorbis_seek(f,0)
+
+extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
+extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
+// these functions return the total length of the vorbis stream
+
+extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
+// decode the next frame and return the number of samples. the number of
+// channels returned are stored in *channels (which can be NULL--it is always
+// the same as the number of channels reported by get_info). *output will
+// contain an array of float* buffers, one per channel. These outputs will
+// be overwritten on the next call to stb_vorbis_get_frame_*.
+//
+// You generally should not intermix calls to stb_vorbis_get_frame_*()
+// and stb_vorbis_get_samples_*(), since the latter calls the former.
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
+extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples);
+#endif
+// decode the next frame and return the number of *samples* per channel.
+// Note that for interleaved data, you pass in the number of shorts (the
+// size of your array), but the return value is the number of samples per
+// channel, not the total number of samples.
+//
+// The data is coerced to the number of channels you request according to the
+// channel coercion rules (see below). You must pass in the size of your
+// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
+// The maximum buffer size needed can be gotten from get_info(); however,
+// the Vorbis I specification implies an absolute maximum of 4096 samples
+// per channel.
+
+// Channel coercion rules:
+// Let M be the number of channels requested, and N the number of channels present,
+// and Cn be the nth channel; let stereo L be the sum of all L and center channels,
+// and stereo R be the sum of all R and center channels (channel assignment from the
+// vorbis spec).
+// M N output
+// 1 k sum(Ck) for all k
+// 2 * stereo L, stereo R
+// k l k > l, the first l channels, then 0s
+// k l k <= l, the first k channels
+// Note that this is not _good_ surround etc. mixing at all! It's just so
+// you get something useful.
+
+extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
+extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
+// gets num_samples samples, not necessarily on a frame boundary--this requires
+// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.
+// Returns the number of samples stored per channel; it may be less than requested
+// at the end of the file. If there are no more samples in the file, returns 0.
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
+extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
+#endif
+// gets num_samples samples, not necessarily on a frame boundary--this requires
+// buffering so you have to supply the buffers. Applies the coercion rules above
+// to produce 'channels' channels. Returns the number of samples stored per channel;
+// it may be less than requested at the end of the file. If there are no more
+// samples in the file, returns 0.
+
+#endif
+
+//////// ERROR CODES
+
+enum STBVorbisError
+{
+ VORBIS__no_error,
+
+ VORBIS_need_more_data=1, // not a real error
+
+ VORBIS_invalid_api_mixing, // can't mix API modes
+ VORBIS_outofmem, // not enough memory
+ VORBIS_feature_not_supported, // uses floor 0
+ VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small
+ VORBIS_file_open_failure, // fopen() failed
+ VORBIS_seek_without_length, // can't seek in unknown-length file
+
+ VORBIS_unexpected_eof=10, // file is truncated?
+ VORBIS_seek_invalid, // seek past EOF
+
+ // decoding errors (corrupt/invalid stream) -- you probably
+ // don't care about the exact details of these
+
+ // vorbis errors:
+ VORBIS_invalid_setup=20,
+ VORBIS_invalid_stream,
+
+ // ogg errors:
+ VORBIS_missing_capture_pattern=30,
+ VORBIS_invalid_stream_structure_version,
+ VORBIS_continued_packet_flag_invalid,
+ VORBIS_incorrect_stream_serial_number,
+ VORBIS_invalid_first_page,
+ VORBIS_bad_packet_type,
+ VORBIS_cant_find_last_page,
+ VORBIS_seek_failed,
+ VORBIS_ogg_skeleton_not_supported
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
+//
+// HEADER ENDS HERE
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef STB_VORBIS_HEADER_ONLY
+
+// global configuration settings (e.g. set these in the project/makefile),
+// or just set them in this file at the top (although ideally the first few
+// should be visible when the header file is compiled too, although it's not
+// crucial)
+
+// STB_VORBIS_NO_PUSHDATA_API
+// does not compile the code for the various stb_vorbis_*_pushdata()
+// functions
+// #define STB_VORBIS_NO_PUSHDATA_API
+
+// STB_VORBIS_NO_PULLDATA_API
+// does not compile the code for the non-pushdata APIs
+// #define STB_VORBIS_NO_PULLDATA_API
+
+// STB_VORBIS_NO_STDIO
+// does not compile the code for the APIs that use FILE *s internally
+// or externally (implied by STB_VORBIS_NO_PULLDATA_API)
+// #define STB_VORBIS_NO_STDIO
+
+// STB_VORBIS_NO_INTEGER_CONVERSION
+// does not compile the code for converting audio sample data from
+// float to integer (implied by STB_VORBIS_NO_PULLDATA_API)
+// #define STB_VORBIS_NO_INTEGER_CONVERSION
+
+// STB_VORBIS_NO_FAST_SCALED_FLOAT
+// does not use a fast float-to-int trick to accelerate float-to-int on
+// most platforms which requires endianness be defined correctly.
+//#define STB_VORBIS_NO_FAST_SCALED_FLOAT
+
+
+// STB_VORBIS_MAX_CHANNELS [number]
+// globally define this to the maximum number of channels you need.
+// The spec does not put a restriction on channels except that
+// the count is stored in a byte, so 255 is the hard limit.
+// Reducing this saves about 16 bytes per value, so using 16 saves
+// (255-16)*16 or around 4KB. Plus anything other memory usage
+// I forgot to account for. Can probably go as low as 8 (7.1 audio),
+// 6 (5.1 audio), or 2 (stereo only).
+#ifndef STB_VORBIS_MAX_CHANNELS
+#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone?
+#endif
+
+// STB_VORBIS_PUSHDATA_CRC_COUNT [number]
+// after a flush_pushdata(), stb_vorbis begins scanning for the
+// next valid page, without backtracking. when it finds something
+// that looks like a page, it streams through it and verifies its
+// CRC32. Should that validation fail, it keeps scanning. But it's
+// possible that _while_ streaming through to check the CRC32 of
+// one candidate page, it sees another candidate page. This #define
+// determines how many "overlapping" candidate pages it can search
+// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas
+// garbage pages could be as big as 64KB, but probably average ~16KB.
+// So don't hose ourselves by scanning an apparent 64KB page and
+// missing a ton of real ones in the interim; so minimum of 2
+#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT
+#define STB_VORBIS_PUSHDATA_CRC_COUNT 4
+#endif
+
+// STB_VORBIS_FAST_HUFFMAN_LENGTH [number]
+// sets the log size of the huffman-acceleration table. Maximum
+// supported value is 24. with larger numbers, more decodings are O(1),
+// but the table size is larger so worse cache missing, so you'll have
+// to probe (and try multiple ogg vorbis files) to find the sweet spot.
+#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH
+#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10
+#endif
+
+// STB_VORBIS_FAST_BINARY_LENGTH [number]
+// sets the log size of the binary-search acceleration table. this
+// is used in similar fashion to the fast-huffman size to set initial
+// parameters for the binary search
+
+// STB_VORBIS_FAST_HUFFMAN_INT
+// The fast huffman tables are much more efficient if they can be
+// stored as 16-bit results instead of 32-bit results. This restricts
+// the codebooks to having only 65535 possible outcomes, though.
+// (At least, accelerated by the huffman table.)
+#ifndef STB_VORBIS_FAST_HUFFMAN_INT
+#define STB_VORBIS_FAST_HUFFMAN_SHORT
+#endif
+
+// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls
+// back on binary searching for the correct one. This requires storing
+// extra tables with the huffman codes in sorted order. Defining this
+// symbol trades off space for speed by forcing a linear search in the
+// non-fast case, except for "sparse" codebooks.
+// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+
+// STB_VORBIS_DIVIDES_IN_RESIDUE
+// stb_vorbis precomputes the result of the scalar residue decoding
+// that would otherwise require a divide per chunk. you can trade off
+// space for time by defining this symbol.
+// #define STB_VORBIS_DIVIDES_IN_RESIDUE
+
+// STB_VORBIS_DIVIDES_IN_CODEBOOK
+// vorbis VQ codebooks can be encoded two ways: with every case explicitly
+// stored, or with all elements being chosen from a small range of values,
+// and all values possible in all elements. By default, stb_vorbis expands
+// this latter kind out to look like the former kind for ease of decoding,
+// because otherwise an integer divide-per-vector-element is required to
+// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can
+// trade off storage for speed.
+//#define STB_VORBIS_DIVIDES_IN_CODEBOOK
+
+#ifdef STB_VORBIS_CODEBOOK_SHORTS
+#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats"
+#endif
+
+// STB_VORBIS_DIVIDE_TABLE
+// this replaces small integer divides in the floor decode loop with
+// table lookups. made less than 1% difference, so disabled by default.
+
+// STB_VORBIS_NO_INLINE_DECODE
+// disables the inlining of the scalar codebook fast-huffman decode.
+// might save a little codespace; useful for debugging
+// #define STB_VORBIS_NO_INLINE_DECODE
+
+// STB_VORBIS_NO_DEFER_FLOOR
+// Normally we only decode the floor without synthesizing the actual
+// full curve. We can instead synthesize the curve immediately. This
+// requires more memory and is very likely slower, so I don't think
+// you'd ever want to do it except for debugging.
+// #define STB_VORBIS_NO_DEFER_FLOOR
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef STB_VORBIS_NO_PULLDATA_API
+ #define STB_VORBIS_NO_INTEGER_CONVERSION
+ #define STB_VORBIS_NO_STDIO
+#endif
+
+#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
+ #define STB_VORBIS_NO_STDIO 1
+#endif
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
+
+ // only need endianness for fast-float-to-int, which we don't
+ // use for pushdata
+
+ #ifndef STB_VORBIS_BIG_ENDIAN
+ #define STB_VORBIS_ENDIAN 0
+ #else
+ #define STB_VORBIS_ENDIAN 1
+ #endif
+
+#endif
+#endif
+
+
+#ifndef STB_VORBIS_NO_STDIO
+#include
+#endif
+
+#ifndef STB_VORBIS_NO_CRT
+ #include
+ #include
+ #include
+ #include
+#else // STB_VORBIS_NO_CRT
+ #define NULL 0
+ #define malloc(s) 0
+ #define free(s) ((void) 0)
+ #define realloc(s) 0
+#endif // STB_VORBIS_NO_CRT
+
+/* we need alloca() regardless of STB_VORBIS_NO_CRT,
+ * because there is not a corresponding 'dealloca' */
+#if !defined(alloca)
+# if defined(HAVE_ALLOCA_H)
+# include
+# elif defined(__GNUC__)
+# define alloca __builtin_alloca
+# elif defined(_MSC_VER)
+# include
+# define alloca _alloca
+# elif defined(__WATCOMC__)
+# include
+# endif
+#endif
+
+#include
+
+#ifndef STB_FORCEINLINE
+ #if defined(_MSC_VER)
+ #define STB_FORCEINLINE __forceinline
+ #elif defined(__GNUC__) || defined(__clang__)
+ #define STB_FORCEINLINE static __inline __attribute__((always_inline))
+ #else
+ #define STB_FORCEINLINE static __inline
+ #endif
+#endif
+
+#if STB_VORBIS_MAX_CHANNELS > 256
+#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range"
+#endif
+
+#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24
+#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range"
+#endif
+
+
+#if 0
+#include
+#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1])
+#else
+#define CHECK(f) ((void) 0)
+#endif
+
+#define MAX_BLOCKSIZE_LOG 13 // from specification
+#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG)
+
+
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef unsigned short uint16;
+typedef signed short int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+typedef float codetype;
+
+// @NOTE
+//
+// Some arrays below are tagged "//varies", which means it's actually
+// a variable-sized piece of data, but rather than malloc I assume it's
+// small enough it's better to just allocate it all together with the
+// main thing
+//
+// Most of the variables are specified with the smallest size I could pack
+// them into. It might give better performance to make them all full-sized
+// integers. It should be safe to freely rearrange the structures or change
+// the sizes larger--nothing relies on silently truncating etc., nor the
+// order of variables.
+
+#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH)
+#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1)
+
+typedef struct
+{
+ int dimensions, entries;
+ uint8 *codeword_lengths;
+ float minimum_value;
+ float delta_value;
+ uint8 value_bits;
+ uint8 lookup_type;
+ uint8 sequence_p;
+ uint8 sparse;
+ uint32 lookup_values;
+ codetype *multiplicands;
+ uint32 *codewords;
+ #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
+ int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
+ #else
+ int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
+ #endif
+ uint32 *sorted_codewords;
+ int *sorted_values;
+ int sorted_entries;
+} Codebook;
+
+typedef struct
+{
+ uint8 order;
+ uint16 rate;
+ uint16 bark_map_size;
+ uint8 amplitude_bits;
+ uint8 amplitude_offset;
+ uint8 number_of_books;
+ uint8 book_list[16]; // varies
+} Floor0;
+
+typedef struct
+{
+ uint8 partitions;
+ uint8 partition_class_list[32]; // varies
+ uint8 class_dimensions[16]; // varies
+ uint8 class_subclasses[16]; // varies
+ uint8 class_masterbooks[16]; // varies
+ int16 subclass_books[16][8]; // varies
+ uint16 Xlist[31*8+2]; // varies
+ uint8 sorted_order[31*8+2];
+ uint8 neighbors[31*8+2][2];
+ uint8 floor1_multiplier;
+ uint8 rangebits;
+ int values;
+} Floor1;
+
+typedef union
+{
+ Floor0 floor0;
+ Floor1 floor1;
+} Floor;
+
+typedef struct
+{
+ uint32 begin, end;
+ uint32 part_size;
+ uint8 classifications;
+ uint8 classbook;
+ uint8 **classdata;
+ int16 (*residue_books)[8];
+} Residue;
+
+typedef struct
+{
+ uint8 magnitude;
+ uint8 angle;
+ uint8 mux;
+} MappingChannel;
+
+typedef struct
+{
+ uint16 coupling_steps;
+ MappingChannel *chan;
+ uint8 submaps;
+ uint8 submap_floor[15]; // varies
+ uint8 submap_residue[15]; // varies
+} Mapping;
+
+typedef struct
+{
+ uint8 blockflag;
+ uint8 mapping;
+ uint16 windowtype;
+ uint16 transformtype;
+} Mode;
+
+typedef struct
+{
+ uint32 goal_crc; // expected crc if match
+ int bytes_left; // bytes left in packet
+ uint32 crc_so_far; // running crc
+ int bytes_done; // bytes processed in _current_ chunk
+ uint32 sample_loc; // granule pos encoded in page
+} CRCscan;
+
+typedef struct
+{
+ uint32 page_start, page_end;
+ uint32 last_decoded_sample;
+} ProbedPage;
+
+struct stb_vorbis
+{
+ // user-accessible info
+ unsigned int sample_rate;
+ int channels;
+
+ unsigned int setup_memory_required;
+ unsigned int temp_memory_required;
+ unsigned int setup_temp_memory_required;
+
+ // input config
+#ifndef STB_VORBIS_NO_STDIO
+ FILE *f;
+ uint32 f_start;
+ int close_on_free;
+#endif
+
+ uint8 *stream;
+ uint8 *stream_start;
+ uint8 *stream_end;
+
+ uint32 stream_len;
+
+ uint8 push_mode;
+
+ // the page to seek to when seeking to start, may be zero
+ uint32 first_audio_page_offset;
+
+ // p_first is the page on which the first audio packet ends
+ // (but not necessarily the page on which it starts)
+ ProbedPage p_first, p_last;
+
+ // memory management
+ stb_vorbis_alloc alloc;
+ int setup_offset;
+ int temp_offset;
+
+ // run-time results
+ int eof;
+ enum STBVorbisError error;
+
+ // user-useful data
+
+ // header info
+ int blocksize[2];
+ int blocksize_0, blocksize_1;
+ int codebook_count;
+ Codebook *codebooks;
+ int floor_count;
+ uint16 floor_types[64]; // varies
+ Floor *floor_config;
+ int residue_count;
+ uint16 residue_types[64]; // varies
+ Residue *residue_config;
+ int mapping_count;
+ Mapping *mapping;
+ int mode_count;
+ Mode mode_config[64]; // varies
+
+ uint32 total_samples;
+
+ // decode buffer
+ float *channel_buffers[STB_VORBIS_MAX_CHANNELS];
+ float *outputs [STB_VORBIS_MAX_CHANNELS];
+
+ float *previous_window[STB_VORBIS_MAX_CHANNELS];
+ int previous_length;
+
+ #ifndef STB_VORBIS_NO_DEFER_FLOOR
+ int16 *finalY[STB_VORBIS_MAX_CHANNELS];
+ #else
+ float *floor_buffers[STB_VORBIS_MAX_CHANNELS];
+ #endif
+
+ uint32 current_loc; // sample location of next frame to decode
+ int current_loc_valid;
+
+ // per-blocksize precomputed data
+
+ // twiddle factors
+ float *A[2],*B[2],*C[2];
+ float *window[2];
+ uint16 *bit_reverse[2];
+
+ // current page/packet/segment streaming info
+ uint32 serial; // stream serial number for verification
+ int last_page;
+ int segment_count;
+ uint8 segments[255];
+ uint8 page_flag;
+ uint8 bytes_in_seg;
+ uint8 first_decode;
+ int next_seg;
+ int last_seg; // flag that we're on the last segment
+ int last_seg_which; // what was the segment number of the last seg?
+ uint32 acc;
+ int valid_bits;
+ int packet_bytes;
+ int end_seg_with_known_loc;
+ uint32 known_loc_for_packet;
+ int discard_samples_deferred;
+ uint32 samples_output;
+
+ // push mode scanning
+ int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+ CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT];
+#endif
+
+ // sample-access
+ int channel_buffer_start;
+ int channel_buffer_end;
+};
+
+#if defined(STB_VORBIS_NO_PUSHDATA_API)
+ #define IS_PUSH_MODE(f) FALSE
+#elif defined(STB_VORBIS_NO_PULLDATA_API)
+ #define IS_PUSH_MODE(f) TRUE
+#else
+ #define IS_PUSH_MODE(f) ((f)->push_mode)
+#endif
+
+typedef struct stb_vorbis vorb;
+
+static int error(vorb *f, enum STBVorbisError e)
+{
+ f->error = e;
+ if (!f->eof && e != VORBIS_need_more_data) {
+ f->error=e; // breakpoint for debugging
+ }
+ return 0;
+}
+
+
+// these functions are used for allocating temporary memory
+// while decoding. if you can afford the stack space, use
+// alloca(); otherwise, provide a temp buffer and it will
+// allocate out of those.
+
+#define array_size_required(count,size) (count*(sizeof(void *)+(size)))
+
+#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size))
+#define temp_free(f,p) (void)0
+#define temp_alloc_save(f) ((f)->temp_offset)
+#define temp_alloc_restore(f,p) ((f)->temp_offset = (p))
+
+#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size)
+
+// given a sufficiently large block of memory, make an array of pointers to subblocks of it
+static void *make_block_array(void *mem, int count, int size)
+{
+ int i;
+ void ** p = (void **) mem;
+ char *q = (char *) (p + count);
+ for (i=0; i < count; ++i) {
+ p[i] = q;
+ q += size;
+ }
+ return p;
+}
+
+static void *setup_malloc(vorb *f, int sz)
+{
+ sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
+ f->setup_memory_required += sz;
+ if (f->alloc.alloc_buffer) {
+ void *p = (char *) f->alloc.alloc_buffer + f->setup_offset;
+ if (f->setup_offset + sz > f->temp_offset) return NULL;
+ f->setup_offset += sz;
+ return p;
+ }
+ return sz ? malloc(sz) : NULL;
+}
+
+static void setup_free(vorb *f, void *p)
+{
+ if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack
+ free(p);
+}
+
+static void *setup_temp_malloc(vorb *f, int sz)
+{
+ sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
+ if (f->alloc.alloc_buffer) {
+ if (f->temp_offset - sz < f->setup_offset) return NULL;
+ f->temp_offset -= sz;
+ return (char *) f->alloc.alloc_buffer + f->temp_offset;
+ }
+ return malloc(sz);
+}
+
+static void setup_temp_free(vorb *f, void *p, int sz)
+{
+ if (f->alloc.alloc_buffer) {
+ f->temp_offset += (sz+7)&~7;
+ return;
+ }
+ free(p);
+}
+
+#define CRC32_POLY 0x04c11db7 // from spec
+
+static uint32 crc_table[256];
+static void crc32_init(void)
+{
+ int i,j;
+ uint32 s;
+ for(i=0; i < 256; i++) {
+ for (s=(uint32) i << 24, j=0; j < 8; ++j)
+ s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0);
+ crc_table[i] = s;
+ }
+}
+
+STB_FORCEINLINE uint32 crc32_update(uint32 crc, uint8 byte)
+{
+ return (crc << 8) ^ crc_table[byte ^ (crc >> 24)];
+}
+
+
+// used in setup, and for huffman that doesn't go fast path
+static unsigned int bit_reverse(unsigned int n)
+{
+ n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1);
+ n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2);
+ n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4);
+ n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8);
+ return (n >> 16) | (n << 16);
+}
+
+static float square(float x)
+{
+ return x*x;
+}
+
+// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3
+// as required by the specification. fast(?) implementation from stb.h
+// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup
+static int ilog(int32 n)
+{
+ static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 };
+
+ if (n < 0) return 0; // signed n returns 0
+
+ // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29)
+ if (n < (1 << 14))
+ if (n < (1 << 4)) return 0 + log2_4[n ];
+ else if (n < (1 << 9)) return 5 + log2_4[n >> 5];
+ else return 10 + log2_4[n >> 10];
+ else if (n < (1 << 24))
+ if (n < (1 << 19)) return 15 + log2_4[n >> 15];
+ else return 20 + log2_4[n >> 20];
+ else if (n < (1 << 29)) return 25 + log2_4[n >> 25];
+ else return 30 + log2_4[n >> 30];
+}
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846264f // from CRC
+#endif
+
+// code length assigned to a value with no huffman encoding
+#define NO_CODE 255
+
+/////////////////////// LEAF SETUP FUNCTIONS //////////////////////////
+//
+// these functions are only called at setup, and only a few times
+// per file
+
+static float float32_unpack(uint32 x)
+{
+ // from the specification
+ uint32 mantissa = x & 0x1fffff;
+ uint32 sign = x & 0x80000000;
+ uint32 exp = (x & 0x7fe00000) >> 21;
+ double res = sign ? -(double)mantissa : (double)mantissa;
+ return (float) ldexp((float)res, exp-788);
+}
+
+
+// zlib & jpeg huffman tables assume that the output symbols
+// can either be arbitrarily arranged, or have monotonically
+// increasing frequencies--they rely on the lengths being sorted;
+// this makes for a very simple generation algorithm.
+// vorbis allows a huffman table with non-sorted lengths. This
+// requires a more sophisticated construction, since symbols in
+// order do not map to huffman codes "in order".
+static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values)
+{
+ if (!c->sparse) {
+ c->codewords [symbol] = huff_code;
+ } else {
+ c->codewords [count] = huff_code;
+ c->codeword_lengths[count] = len;
+ values [count] = symbol;
+ }
+}
+
+static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
+{
+ int i,k,m=0;
+ uint32 available[32];
+
+ memset(available, 0, sizeof(available));
+ // find the first entry
+ for (k=0; k < n; ++k) if (len[k] < NO_CODE) break;
+ if (k == n) { assert(c->sorted_entries == 0); return TRUE; }
+ // add to the list
+ add_entry(c, 0, k, m++, len[k], values);
+ // add all available leaves
+ for (i=1; i <= len[k]; ++i)
+ available[i] = 1U << (32-i);
+ // note that the above code treats the first case specially,
+ // but it's really the same as the following code, so they
+ // could probably be combined (except the initial code is 0,
+ // and I use 0 in available[] to mean 'empty')
+ for (i=k+1; i < n; ++i) {
+ uint32 res;
+ int z = len[i], y;
+ if (z == NO_CODE) continue;
+ // find lowest available leaf (should always be earliest,
+ // which is what the specification calls for)
+ // note that this property, and the fact we can never have
+ // more than one free leaf at a given level, isn't totally
+ // trivial to prove, but it seems true and the assert never
+ // fires, so!
+ while (z > 0 && !available[z]) --z;
+ if (z == 0) { return FALSE; }
+ res = available[z];
+ assert(z >= 0 && z < 32);
+ available[z] = 0;
+ add_entry(c, bit_reverse(res), i, m++, len[i], values);
+ // propagate availability up the tree
+ if (z != len[i]) {
+ assert(/*len[i] >= 0 &&*/ len[i] < 32);
+ for (y=len[i]; y > z; --y) {
+ assert(available[y] == 0);
+ available[y] = res + (1 << (32-y));
+ }
+ }
+ }
+ return TRUE;
+}
+
+// accelerated huffman table allows fast O(1) match of all symbols
+// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH
+static void compute_accelerated_huffman(Codebook *c)
+{
+ int i, len;
+ for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i)
+ c->fast_huffman[i] = -1;
+
+ len = c->sparse ? c->sorted_entries : c->entries;
+ #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
+ if (len > 32767) len = 32767; // largest possible value we can encode!
+ #endif
+ for (i=0; i < len; ++i) {
+ if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) {
+ uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i];
+ // set table entries for all bit combinations in the higher bits
+ while (z < FAST_HUFFMAN_TABLE_SIZE) {
+ c->fast_huffman[z] = i;
+ z += 1 << c->codeword_lengths[i];
+ }
+ }
+ }
+}
+
+#ifdef _MSC_VER
+#define STBV_CDECL __cdecl
+#else
+#define STBV_CDECL
+#endif
+
+static int STBV_CDECL uint32_compare(const void *p, const void *q)
+{
+ uint32 x = * (uint32 *) p;
+ uint32 y = * (uint32 *) q;
+ return x < y ? -1 : x > y;
+}
+
+static int include_in_sort(Codebook *c, uint8 len)
+{
+ if (c->sparse) { assert(len != NO_CODE); return TRUE; }
+ if (len == NO_CODE) return FALSE;
+ if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE;
+ return FALSE;
+}
+
+// if the fast table above doesn't work, we want to binary
+// search them... need to reverse the bits
+static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values)
+{
+ int i, len;
+ // build a list of all the entries
+ // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN.
+ // this is kind of a frivolous optimization--I don't see any performance improvement,
+ // but it's like 4 extra lines of code, so.
+ if (!c->sparse) {
+ int k = 0;
+ for (i=0; i < c->entries; ++i)
+ if (include_in_sort(c, lengths[i]))
+ c->sorted_codewords[k++] = bit_reverse(c->codewords[i]);
+ assert(k == c->sorted_entries);
+ } else {
+ for (i=0; i < c->sorted_entries; ++i)
+ c->sorted_codewords[i] = bit_reverse(c->codewords[i]);
+ }
+
+ qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare);
+ c->sorted_codewords[c->sorted_entries] = 0xffffffff;
+
+ len = c->sparse ? c->sorted_entries : c->entries;
+ // now we need to indicate how they correspond; we could either
+ // #1: sort a different data structure that says who they correspond to
+ // #2: for each sorted entry, search the original list to find who corresponds
+ // #3: for each original entry, find the sorted entry
+ // #1 requires extra storage, #2 is slow, #3 can use binary search!
+ for (i=0; i < len; ++i) {
+ int huff_len = c->sparse ? lengths[values[i]] : lengths[i];
+ if (include_in_sort(c,huff_len)) {
+ uint32 code = bit_reverse(c->codewords[i]);
+ int x=0, n=c->sorted_entries;
+ while (n > 1) {
+ // invariant: sc[x] <= code < sc[x+n]
+ int m = x + (n >> 1);
+ if (c->sorted_codewords[m] <= code) {
+ x = m;
+ n -= (n>>1);
+ } else {
+ n >>= 1;
+ }
+ }
+ assert(c->sorted_codewords[x] == code);
+ if (c->sparse) {
+ c->sorted_values[x] = values[i];
+ c->codeword_lengths[x] = huff_len;
+ } else {
+ c->sorted_values[x] = i;
+ }
+ }
+ }
+}
+
+// only run while parsing the header (3 times)
+static int vorbis_validate(uint8 *data)
+{
+ static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' };
+ return memcmp(data, vorbis, 6) == 0;
+}
+
+// called from setup only, once per code book
+// (formula implied by specification)
+static int lookup1_values(int entries, int dim)
+{
+ int r = (int) floor(exp((float) log((float) entries) / dim));
+ if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning;
+ ++r; // floor() to avoid _ftol() when non-CRT
+ if (pow((float) r+1, dim) <= entries)
+ return -1;
+ if ((int) floor(pow((float) r, dim)) > entries)
+ return -1;
+ return r;
+}
+
+// called twice per file
+static void compute_twiddle_factors(int n, float *A, float *B, float *C)
+{
+ int n4 = n >> 2, n8 = n >> 3;
+ int k,k2;
+
+ for (k=k2=0; k < n4; ++k,k2+=2) {
+ A[k2 ] = (float) cos(4*k*M_PI/n);
+ A[k2+1] = (float) -sin(4*k*M_PI/n);
+ B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f;
+ B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f;
+ }
+ for (k=k2=0; k < n8; ++k,k2+=2) {
+ C[k2 ] = (float) cos(2*(k2+1)*M_PI/n);
+ C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
+ }
+}
+
+static void compute_window(int n, float *window)
+{
+ int n2 = n >> 1, i;
+ for (i=0; i < n2; ++i)
+ window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI)));
+}
+
+static void compute_bitreverse(int n, uint16 *rev)
+{
+ int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+ int i, n8 = n >> 3;
+ for (i=0; i < n8; ++i)
+ rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2;
+}
+
+static int init_blocksize(vorb *f, int b, int n)
+{
+ int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3;
+ f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+ f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+ f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4);
+ if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem);
+ compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]);
+ f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+ if (!f->window[b]) return error(f, VORBIS_outofmem);
+ compute_window(n, f->window[b]);
+ f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8);
+ if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem);
+ compute_bitreverse(n, f->bit_reverse[b]);
+ return TRUE;
+}
+
+static void neighbors(uint16 *x, int n, int *plow, int *phigh)
+{
+ int low = -1;
+ int high = 65536;
+ int i;
+ for (i=0; i < n; ++i) {
+ if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; }
+ if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; }
+ }
+}
+
+// this has been repurposed so y is now the original index instead of y
+typedef struct
+{
+ uint16 x,id;
+} stbv__floor_ordering;
+
+static int STBV_CDECL point_compare(const void *p, const void *q)
+{
+ stbv__floor_ordering *a = (stbv__floor_ordering *) p;
+ stbv__floor_ordering *b = (stbv__floor_ordering *) q;
+ return a->x < b->x ? -1 : a->x > b->x;
+}
+
+//
+/////////////////////// END LEAF SETUP FUNCTIONS //////////////////////////
+
+
+#if defined(STB_VORBIS_NO_STDIO)
+ #define USE_MEMORY(z) TRUE
+#else
+ #define USE_MEMORY(z) ((z)->stream)
+#endif
+
+static uint8 get8(vorb *z)
+{
+ if (USE_MEMORY(z)) {
+ if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; }
+ return *z->stream++;
+ }
+
+ #ifndef STB_VORBIS_NO_STDIO
+ {
+ int c = fgetc(z->f);
+ if (c == EOF) { z->eof = TRUE; return 0; }
+ return c;
+ }
+ #endif
+}
+
+static uint32 get32(vorb *f)
+{
+ uint32 x;
+ x = get8(f);
+ x += get8(f) << 8;
+ x += get8(f) << 16;
+ x += (uint32) get8(f) << 24;
+ return x;
+}
+
+static int getn(vorb *z, uint8 *data, int n)
+{
+ if (USE_MEMORY(z)) {
+ if (z->stream+n > z->stream_end) { z->eof = 1; return 0; }
+ memcpy(data, z->stream, n);
+ z->stream += n;
+ return 1;
+ }
+
+ #ifndef STB_VORBIS_NO_STDIO
+ if (fread(data, n, 1, z->f) == 1)
+ return 1;
+ else {
+ z->eof = 1;
+ return 0;
+ }
+ #endif
+}
+
+static void skip(vorb *z, int n)
+{
+ if (USE_MEMORY(z)) {
+ z->stream += n;
+ if (z->stream >= z->stream_end) z->eof = 1;
+ return;
+ }
+ #ifndef STB_VORBIS_NO_STDIO
+ {
+ long x = ftell(z->f);
+ fseek(z->f, x+n, SEEK_SET);
+ }
+ #endif
+}
+
+static int set_file_offset(stb_vorbis *f, unsigned int loc)
+{
+ #ifndef STB_VORBIS_NO_PUSHDATA_API
+ if (f->push_mode) return 0;
+ #endif
+ f->eof = 0;
+ if (USE_MEMORY(f)) {
+ if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) {
+ f->stream = f->stream_end;
+ f->eof = 1;
+ return 0;
+ } else {
+ f->stream = f->stream_start + loc;
+ return 1;
+ }
+ }
+ #ifndef STB_VORBIS_NO_STDIO
+ if (loc + f->f_start < loc || loc >= 0x80000000) {
+ loc = 0x7fffffff;
+ f->eof = 1;
+ } else {
+ loc += f->f_start;
+ }
+ if (!fseek(f->f, loc, SEEK_SET))
+ return 1;
+ f->eof = 1;
+ fseek(f->f, f->f_start, SEEK_END);
+ return 0;
+ #endif
+}
+
+
+static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 };
+
+static int capture_pattern(vorb *f)
+{
+ if (0x4f != get8(f)) return FALSE;
+ if (0x67 != get8(f)) return FALSE;
+ if (0x67 != get8(f)) return FALSE;
+ if (0x53 != get8(f)) return FALSE;
+ return TRUE;
+}
+
+#define PAGEFLAG_continued_packet 1
+#define PAGEFLAG_first_page 2
+#define PAGEFLAG_last_page 4
+
+static int start_page_no_capturepattern(vorb *f)
+{
+ uint32 loc0,loc1,n;
+ if (f->first_decode && !IS_PUSH_MODE(f)) {
+ f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4;
+ }
+ // stream structure version
+ if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version);
+ // header flag
+ f->page_flag = get8(f);
+ // absolute granule position
+ loc0 = get32(f);
+ loc1 = get32(f);
+ // @TODO: validate loc0,loc1 as valid positions?
+ // stream serial number -- vorbis doesn't interleave, so discard
+ get32(f);
+ //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number);
+ // page sequence number
+ n = get32(f);
+ f->last_page = n;
+ // CRC32
+ get32(f);
+ // page_segments
+ f->segment_count = get8(f);
+ if (!getn(f, f->segments, f->segment_count))
+ return error(f, VORBIS_unexpected_eof);
+ // assume we _don't_ know any the sample position of any segments
+ f->end_seg_with_known_loc = -2;
+ if (loc0 != ~0U || loc1 != ~0U) {
+ int i;
+ // determine which packet is the last one that will complete
+ for (i=f->segment_count-1; i >= 0; --i)
+ if (f->segments[i] < 255)
+ break;
+ // 'i' is now the index of the _last_ segment of a packet that ends
+ if (i >= 0) {
+ f->end_seg_with_known_loc = i;
+ f->known_loc_for_packet = loc0;
+ }
+ }
+ if (f->first_decode) {
+ int i,len;
+ len = 0;
+ for (i=0; i < f->segment_count; ++i)
+ len += f->segments[i];
+ len += 27 + f->segment_count;
+ f->p_first.page_end = f->p_first.page_start + len;
+ f->p_first.last_decoded_sample = loc0;
+ }
+ f->next_seg = 0;
+ return TRUE;
+}
+
+static int start_page(vorb *f)
+{
+ if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern);
+ return start_page_no_capturepattern(f);
+}
+
+static int start_packet(vorb *f)
+{
+ while (f->next_seg == -1) {
+ if (!start_page(f)) return FALSE;
+ if (f->page_flag & PAGEFLAG_continued_packet)
+ return error(f, VORBIS_continued_packet_flag_invalid);
+ }
+ f->last_seg = FALSE;
+ f->valid_bits = 0;
+ f->packet_bytes = 0;
+ f->bytes_in_seg = 0;
+ // f->next_seg is now valid
+ return TRUE;
+}
+
+static int maybe_start_packet(vorb *f)
+{
+ if (f->next_seg == -1) {
+ int x = get8(f);
+ if (f->eof) return FALSE; // EOF at page boundary is not an error!
+ if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern);
+ if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+ if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+ if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+ if (!start_page_no_capturepattern(f)) return FALSE;
+ if (f->page_flag & PAGEFLAG_continued_packet) {
+ // set up enough state that we can read this packet if we want,
+ // e.g. during recovery
+ f->last_seg = FALSE;
+ f->bytes_in_seg = 0;
+ return error(f, VORBIS_continued_packet_flag_invalid);
+ }
+ }
+ return start_packet(f);
+}
+
+static int next_segment(vorb *f)
+{
+ int len;
+ if (f->last_seg) return 0;
+ if (f->next_seg == -1) {
+ f->last_seg_which = f->segment_count-1; // in case start_page fails
+ if (!start_page(f)) { f->last_seg = 1; return 0; }
+ if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid);
+ }
+ len = f->segments[f->next_seg++];
+ if (len < 255) {
+ f->last_seg = TRUE;
+ f->last_seg_which = f->next_seg-1;
+ }
+ if (f->next_seg >= f->segment_count)
+ f->next_seg = -1;
+ assert(f->bytes_in_seg == 0);
+ f->bytes_in_seg = len;
+ return len;
+}
+
+#define EOP (-1)
+#define INVALID_BITS (-1)
+
+static int get8_packet_raw(vorb *f)
+{
+ if (!f->bytes_in_seg) { // CLANG!
+ if (f->last_seg) return EOP;
+ else if (!next_segment(f)) return EOP;
+ }
+ assert(f->bytes_in_seg > 0);
+ --f->bytes_in_seg;
+ ++f->packet_bytes;
+ return get8(f);
+}
+
+static int get8_packet(vorb *f)
+{
+ int x = get8_packet_raw(f);
+ f->valid_bits = 0;
+ return x;
+}
+
+static void flush_packet(vorb *f)
+{
+ while (get8_packet_raw(f) != EOP);
+}
+
+// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important
+// as the huffman decoder?
+static uint32 get_bits(vorb *f, int n)
+{
+ uint32 z;
+
+ if (f->valid_bits < 0) return 0;
+ if (f->valid_bits < n) {
+ if (n > 24) {
+ // the accumulator technique below would not work correctly in this case
+ z = get_bits(f, 24);
+ z += get_bits(f, n-24) << 24;
+ return z;
+ }
+ if (f->valid_bits == 0) f->acc = 0;
+ while (f->valid_bits < n) {
+ int z = get8_packet_raw(f);
+ if (z == EOP) {
+ f->valid_bits = INVALID_BITS;
+ return 0;
+ }
+ f->acc += z << f->valid_bits;
+ f->valid_bits += 8;
+ }
+ }
+
+ assert(f->valid_bits >= n);
+ z = f->acc & ((1 << n)-1);
+ f->acc >>= n;
+ f->valid_bits -= n;
+ return z;
+}
+
+// @OPTIMIZE: primary accumulator for huffman
+// expand the buffer to as many bits as possible without reading off end of packet
+// it might be nice to allow f->valid_bits and f->acc to be stored in registers,
+// e.g. cache them locally and decode locally
+STB_FORCEINLINE void prep_huffman(vorb *f)
+{
+ if (f->valid_bits <= 24) {
+ if (f->valid_bits == 0) f->acc = 0;
+ do {
+ int z;
+ if (f->last_seg && !f->bytes_in_seg) return;
+ z = get8_packet_raw(f);
+ if (z == EOP) return;
+ f->acc += (unsigned) z << f->valid_bits;
+ f->valid_bits += 8;
+ } while (f->valid_bits <= 24);
+ }
+}
+
+enum
+{
+ VORBIS_packet_id = 1,
+ VORBIS_packet_comment = 3,
+ VORBIS_packet_setup = 5
+};
+
+static int codebook_decode_scalar_raw(vorb *f, Codebook *c)
+{
+ int i;
+ prep_huffman(f);
+
+ if (c->codewords == NULL && c->sorted_codewords == NULL)
+ return -1;
+
+ // cases to use binary search: sorted_codewords && !c->codewords
+ // sorted_codewords && c->entries > 8
+ if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) {
+ // binary search
+ uint32 code = bit_reverse(f->acc);
+ int x=0, n=c->sorted_entries, len;
+
+ while (n > 1) {
+ // invariant: sc[x] <= code < sc[x+n]
+ int m = x + (n >> 1);
+ if (c->sorted_codewords[m] <= code) {
+ x = m;
+ n -= (n>>1);
+ } else {
+ n >>= 1;
+ }
+ }
+ // x is now the sorted index
+ if (!c->sparse) x = c->sorted_values[x];
+ // x is now sorted index if sparse, or symbol otherwise
+ len = c->codeword_lengths[x];
+ if (f->valid_bits >= len) {
+ f->acc >>= len;
+ f->valid_bits -= len;
+ return x;
+ }
+
+ f->valid_bits = 0;
+ return -1;
+ }
+
+ // if small, linear search
+ assert(!c->sparse);
+ for (i=0; i < c->entries; ++i) {
+ if (c->codeword_lengths[i] == NO_CODE) continue;
+ if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) {
+ if (f->valid_bits >= c->codeword_lengths[i]) {
+ f->acc >>= c->codeword_lengths[i];
+ f->valid_bits -= c->codeword_lengths[i];
+ return i;
+ }
+ f->valid_bits = 0;
+ return -1;
+ }
+ }
+
+ error(f, VORBIS_invalid_stream);
+ f->valid_bits = 0;
+ return -1;
+}
+
+#ifndef STB_VORBIS_NO_INLINE_DECODE
+
+#define DECODE_RAW(var, f,c) \
+ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \
+ prep_huffman(f); \
+ var = f->acc & FAST_HUFFMAN_TABLE_MASK; \
+ var = c->fast_huffman[var]; \
+ if (var >= 0) { \
+ int n = c->codeword_lengths[var]; \
+ f->acc >>= n; \
+ f->valid_bits -= n; \
+ if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \
+ } else { \
+ var = codebook_decode_scalar_raw(f,c); \
+ }
+
+#else
+
+static int codebook_decode_scalar(vorb *f, Codebook *c)
+{
+ int i;
+ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH)
+ prep_huffman(f);
+ // fast huffman table lookup
+ i = f->acc & FAST_HUFFMAN_TABLE_MASK;
+ i = c->fast_huffman[i];
+ if (i >= 0) {
+ f->acc >>= c->codeword_lengths[i];
+ f->valid_bits -= c->codeword_lengths[i];
+ if (f->valid_bits < 0) { f->valid_bits = 0; return -1; }
+ return i;
+ }
+ return codebook_decode_scalar_raw(f,c);
+}
+
+#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c);
+
+#endif
+
+#define DECODE(var,f,c) \
+ DECODE_RAW(var,f,c) \
+ if (c->sparse) var = c->sorted_values[var];
+
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c)
+#else
+ #define DECODE_VQ(var,f,c) DECODE(var,f,c)
+#endif
+
+
+
+
+
+
+// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case
+// where we avoid one addition
+#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_BASE(c) (0)
+
+static int codebook_decode_start(vorb *f, Codebook *c)
+{
+ int z = -1;
+
+ // type 0 is only legal in a scalar context
+ if (c->lookup_type == 0)
+ error(f, VORBIS_invalid_stream);
+ else {
+ DECODE_VQ(z,f,c);
+ if (c->sparse) assert(z < c->sorted_entries);
+ if (z < 0) { // check for EOP
+ if (!f->bytes_in_seg)
+ if (f->last_seg)
+ return z;
+ error(f, VORBIS_invalid_stream);
+ }
+ }
+ return z;
+}
+
+static int codebook_decode(vorb *f, Codebook *c, float *output, int len)
+{
+ int i,z = codebook_decode_start(f,c);
+ if (z < 0) return FALSE;
+ if (len > c->dimensions) len = c->dimensions;
+
+#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ int div = 1;
+ for (i=0; i < len; ++i) {
+ int off = (z / div) % c->lookup_values;
+ float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+ output[i] += val;
+ if (c->sequence_p) last = val + c->minimum_value;
+ div *= c->lookup_values;
+ }
+ return TRUE;
+ }
+#endif
+
+ z *= c->dimensions;
+ if (c->sequence_p) {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ for (i=0; i < len; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ output[i] += val;
+ last = val + c->minimum_value;
+ }
+ } else {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ for (i=0; i < len; ++i) {
+ output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ }
+ }
+
+ return TRUE;
+}
+
+static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step)
+{
+ int i,z = codebook_decode_start(f,c);
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ if (z < 0) return FALSE;
+ if (len > c->dimensions) len = c->dimensions;
+
+#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ int div = 1;
+ for (i=0; i < len; ++i) {
+ int off = (z / div) % c->lookup_values;
+ float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+ output[i*step] += val;
+ if (c->sequence_p) last = val;
+ div *= c->lookup_values;
+ }
+ return TRUE;
+ }
+#endif
+
+ z *= c->dimensions;
+ for (i=0; i < len; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ output[i*step] += val;
+ if (c->sequence_p) last = val;
+ }
+
+ return TRUE;
+}
+
+static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode)
+{
+ int c_inter = *c_inter_p;
+ int p_inter = *p_inter_p;
+ int i,z, effective = c->dimensions;
+
+ // type 0 is only legal in a scalar context
+ if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream);
+
+ while (total_decode > 0) {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ DECODE_VQ(z,f,c);
+ #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ assert(!c->sparse || z < c->sorted_entries);
+ #endif
+ if (z < 0) {
+ if (!f->bytes_in_seg)
+ if (f->last_seg) return FALSE;
+ return error(f, VORBIS_invalid_stream);
+ }
+
+ // if this will take us off the end of the buffers, stop short!
+ // we check by computing the length of the virtual interleaved
+ // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter),
+ // and the length we'll be using (effective)
+ if (c_inter + p_inter*ch + effective > len * ch) {
+ effective = len*ch - (p_inter*ch - c_inter);
+ }
+
+ #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ int div = 1;
+ for (i=0; i < effective; ++i) {
+ int off = (z / div) % c->lookup_values;
+ float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+ if (outputs[c_inter])
+ outputs[c_inter][p_inter] += val;
+ if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+ if (c->sequence_p) last = val;
+ div *= c->lookup_values;
+ }
+ } else
+ #endif
+ {
+ z *= c->dimensions;
+ if (c->sequence_p) {
+ for (i=0; i < effective; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ if (outputs[c_inter])
+ outputs[c_inter][p_inter] += val;
+ if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+ last = val;
+ }
+ } else {
+ for (i=0; i < effective; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ if (outputs[c_inter])
+ outputs[c_inter][p_inter] += val;
+ if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+ }
+ }
+ }
+
+ total_decode -= effective;
+ }
+ *c_inter_p = c_inter;
+ *p_inter_p = p_inter;
+ return TRUE;
+}
+
+static int predict_point(int x, int x0, int x1, int y0, int y1)
+{
+ int dy = y1 - y0;
+ int adx = x1 - x0;
+ // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86?
+ int err = abs(dy) * (x - x0);
+ int off = err / adx;
+ return dy < 0 ? y0 - off : y0 + off;
+}
+
+// the following table is block-copied from the specification
+static float inverse_db_table[256] =
+{
+ 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f,
+ 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f,
+ 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f,
+ 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f,
+ 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f,
+ 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f,
+ 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f,
+ 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f,
+ 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f,
+ 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f,
+ 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f,
+ 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f,
+ 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f,
+ 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f,
+ 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f,
+ 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f,
+ 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f,
+ 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f,
+ 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f,
+ 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f,
+ 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f,
+ 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f,
+ 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f,
+ 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f,
+ 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f,
+ 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f,
+ 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f,
+ 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f,
+ 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f,
+ 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
+ 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f,
+ 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f,
+ 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f,
+ 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f,
+ 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f,
+ 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f,
+ 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f,
+ 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f,
+ 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f,
+ 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f,
+ 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f,
+ 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f,
+ 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f,
+ 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f,
+ 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f,
+ 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f,
+ 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f,
+ 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f,
+ 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f,
+ 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f,
+ 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f,
+ 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f,
+ 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f,
+ 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f,
+ 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f,
+ 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f,
+ 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f,
+ 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f,
+ 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f,
+ 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f,
+ 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f,
+ 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f,
+ 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f,
+ 0.82788260f, 0.88168307f, 0.9389798f, 1.0f
+};
+
+
+// @OPTIMIZE: if you want to replace this bresenham line-drawing routine,
+// note that you must produce bit-identical output to decode correctly;
+// this specific sequence of operations is specified in the spec (it's
+// drawing integer-quantized frequency-space lines that the encoder
+// expects to be exactly the same)
+// ... also, isn't the whole point of Bresenham's algorithm to NOT
+// have to divide in the setup? sigh.
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+#define LINE_OP(a,b) a *= b
+#else
+#define LINE_OP(a,b) a = b
+#endif
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+#define DIVTAB_NUMER 32
+#define DIVTAB_DENOM 64
+int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB
+#endif
+
+STB_FORCEINLINE void draw_line(float *output, int x0, int y0, int x1, int y1, int n)
+{
+ int dy = y1 - y0;
+ int adx = x1 - x0;
+ int ady = abs(dy);
+ int base;
+ int x=x0,y=y0;
+ int err = 0;
+ int sy;
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+ if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) {
+ if (dy < 0) {
+ base = -integer_divide_table[ady][adx];
+ sy = base-1;
+ } else {
+ base = integer_divide_table[ady][adx];
+ sy = base+1;
+ }
+ } else {
+ base = dy / adx;
+ if (dy < 0)
+ sy = base - 1;
+ else
+ sy = base+1;
+ }
+#else
+ base = dy / adx;
+ if (dy < 0)
+ sy = base - 1;
+ else
+ sy = base+1;
+#endif
+ ady -= abs(base) * adx;
+ if (x1 > n) x1 = n;
+ if (x < x1) {
+ LINE_OP(output[x], inverse_db_table[y&255]);
+ for (++x; x < x1; ++x) {
+ err += ady;
+ if (err >= adx) {
+ err -= adx;
+ y += sy;
+ } else
+ y += base;
+ LINE_OP(output[x], inverse_db_table[y&255]);
+ }
+ }
+}
+
+static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype)
+{
+ int k;
+ if (rtype == 0) {
+ int step = n / book->dimensions;
+ for (k=0; k < step; ++k)
+ if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step))
+ return FALSE;
+ } else {
+ for (k=0; k < n; ) {
+ if (!codebook_decode(f, book, target+offset, n-k))
+ return FALSE;
+ k += book->dimensions;
+ offset += book->dimensions;
+ }
+ }
+ return TRUE;
+}
+
+// n is 1/2 of the blocksize --
+// specification: "Correct per-vector decode length is [n]/2"
+static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode)
+{
+ int i,j,pass;
+ Residue *r = f->residue_config + rn;
+ int rtype = f->residue_types[rn];
+ int c = r->classbook;
+ int classwords = f->codebooks[c].dimensions;
+ unsigned int actual_size = rtype == 2 ? n*2 : n;
+ unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size);
+ unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size);
+ int n_read = limit_r_end - limit_r_begin;
+ int part_read = n_read / r->part_size;
+ int temp_alloc_point = temp_alloc_save(f);
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata));
+ #else
+ int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications));
+ #endif
+
+ CHECK(f);
+
+ for (i=0; i < ch; ++i)
+ if (!do_not_decode[i])
+ memset(residue_buffers[i], 0, sizeof(float) * n);
+
+ if (rtype == 2 && ch != 1) {
+ for (j=0; j < ch; ++j)
+ if (!do_not_decode[j])
+ break;
+ if (j == ch)
+ goto done;
+
+ for (pass=0; pass < 8; ++pass) {
+ int pcount = 0, class_set = 0;
+ if (ch == 2) {
+ while (pcount < part_read) {
+ int z = r->begin + pcount*r->part_size;
+ int c_inter = (z & 1), p_inter = z>>1;
+ if (pass == 0) {
+ Codebook *c = f->codebooks+r->classbook;
+ int q;
+ DECODE(q,f,c);
+ if (q == EOP) goto done;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ part_classdata[0][class_set] = r->classdata[q];
+ #else
+ for (i=classwords-1; i >= 0; --i) {
+ classifications[0][i+pcount] = q % r->classifications;
+ q /= r->classifications;
+ }
+ #endif
+ }
+ for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+ int z = r->begin + pcount*r->part_size;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ int c = part_classdata[0][class_set][i];
+ #else
+ int c = classifications[0][pcount];
+ #endif
+ int b = r->residue_books[c][pass];
+ if (b >= 0) {
+ Codebook *book = f->codebooks + b;
+ #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+ goto done;
+ #else
+ // saves 1%
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+ goto done;
+ #endif
+ } else {
+ z += r->part_size;
+ c_inter = z & 1;
+ p_inter = z >> 1;
+ }
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ ++class_set;
+ #endif
+ }
+ } else if (ch > 2) {
+ while (pcount < part_read) {
+ int z = r->begin + pcount*r->part_size;
+ int c_inter = z % ch, p_inter = z/ch;
+ if (pass == 0) {
+ Codebook *c = f->codebooks+r->classbook;
+ int q;
+ DECODE(q,f,c);
+ if (q == EOP) goto done;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ part_classdata[0][class_set] = r->classdata[q];
+ #else
+ for (i=classwords-1; i >= 0; --i) {
+ classifications[0][i+pcount] = q % r->classifications;
+ q /= r->classifications;
+ }
+ #endif
+ }
+ for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+ int z = r->begin + pcount*r->part_size;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ int c = part_classdata[0][class_set][i];
+ #else
+ int c = classifications[0][pcount];
+ #endif
+ int b = r->residue_books[c][pass];
+ if (b >= 0) {
+ Codebook *book = f->codebooks + b;
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+ goto done;
+ } else {
+ z += r->part_size;
+ c_inter = z % ch;
+ p_inter = z / ch;
+ }
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ ++class_set;
+ #endif
+ }
+ }
+ }
+ goto done;
+ }
+ CHECK(f);
+
+ for (pass=0; pass < 8; ++pass) {
+ int pcount = 0, class_set=0;
+ while (pcount < part_read) {
+ if (pass == 0) {
+ for (j=0; j < ch; ++j) {
+ if (!do_not_decode[j]) {
+ Codebook *c = f->codebooks+r->classbook;
+ int temp;
+ DECODE(temp,f,c);
+ if (temp == EOP) goto done;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ part_classdata[j][class_set] = r->classdata[temp];
+ #else
+ for (i=classwords-1; i >= 0; --i) {
+ classifications[j][i+pcount] = temp % r->classifications;
+ temp /= r->classifications;
+ }
+ #endif
+ }
+ }
+ }
+ for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+ for (j=0; j < ch; ++j) {
+ if (!do_not_decode[j]) {
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ int c = part_classdata[j][class_set][i];
+ #else
+ int c = classifications[j][pcount];
+ #endif
+ int b = r->residue_books[c][pass];
+ if (b >= 0) {
+ float *target = residue_buffers[j];
+ int offset = r->begin + pcount * r->part_size;
+ int n = r->part_size;
+ Codebook *book = f->codebooks + b;
+ if (!residue_decode(f, book, target, offset, n, rtype))
+ goto done;
+ }
+ }
+ }
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ ++class_set;
+ #endif
+ }
+ }
+ done:
+ CHECK(f);
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ temp_free(f,part_classdata);
+ #else
+ temp_free(f,classifications);
+ #endif
+ temp_alloc_restore(f,temp_alloc_point);
+}
+
+
+#if 0
+// slow way for debugging
+void inverse_mdct_slow(float *buffer, int n)
+{
+ int i,j;
+ int n2 = n >> 1;
+ float *x = (float *) malloc(sizeof(*x) * n2);
+ memcpy(x, buffer, sizeof(*x) * n2);
+ for (i=0; i < n; ++i) {
+ float acc = 0;
+ for (j=0; j < n2; ++j)
+ // formula from paper:
+ //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1));
+ // formula from wikipedia
+ //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5));
+ // these are equivalent, except the formula from the paper inverts the multiplier!
+ // however, what actually works is NO MULTIPLIER!?!
+ //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5));
+ acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1));
+ buffer[i] = acc;
+ }
+ free(x);
+}
+#elif 0
+// same as above, but just barely able to run in real time on modern machines
+void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
+{
+ float mcos[16384];
+ int i,j;
+ int n2 = n >> 1, nmask = (n << 2) -1;
+ float *x = (float *) malloc(sizeof(*x) * n2);
+ memcpy(x, buffer, sizeof(*x) * n2);
+ for (i=0; i < 4*n; ++i)
+ mcos[i] = (float) cos(M_PI / 2 * i / n);
+
+ for (i=0; i < n; ++i) {
+ float acc = 0;
+ for (j=0; j < n2; ++j)
+ acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask];
+ buffer[i] = acc;
+ }
+ free(x);
+}
+#elif 0
+// transform to use a slow dct-iv; this is STILL basically trivial,
+// but only requires half as many ops
+void dct_iv_slow(float *buffer, int n)
+{
+ float mcos[16384];
+ float x[2048];
+ int i,j;
+ int n2 = n >> 1, nmask = (n << 3) - 1;
+ memcpy(x, buffer, sizeof(*x) * n);
+ for (i=0; i < 8*n; ++i)
+ mcos[i] = (float) cos(M_PI / 4 * i / n);
+ for (i=0; i < n; ++i) {
+ float acc = 0;
+ for (j=0; j < n; ++j)
+ acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask];
+ buffer[i] = acc;
+ }
+}
+
+void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
+{
+ int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4;
+ float temp[4096];
+
+ memcpy(temp, buffer, n2 * sizeof(float));
+ dct_iv_slow(temp, n2); // returns -c'-d, a-b'
+
+ for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b'
+ for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d'
+ for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d
+}
+#endif
+
+#ifndef LIBVORBIS_MDCT
+#define LIBVORBIS_MDCT 0
+#endif
+
+#if LIBVORBIS_MDCT
+// directly call the vorbis MDCT using an interface documented
+// by Jeff Roberts... useful for performance comparison
+typedef struct
+{
+ int n;
+ int log2n;
+
+ float *trig;
+ int *bitrev;
+
+ float scale;
+} mdct_lookup;
+
+extern void mdct_init(mdct_lookup *lookup, int n);
+extern void mdct_clear(mdct_lookup *l);
+extern void mdct_backward(mdct_lookup *init, float *in, float *out);
+
+mdct_lookup M1,M2;
+
+void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
+{
+ mdct_lookup *M;
+ if (M1.n == n) M = &M1;
+ else if (M2.n == n) M = &M2;
+ else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; }
+ else {
+ if (M2.n) __asm int 3;
+ mdct_init(&M2, n);
+ M = &M2;
+ }
+
+ mdct_backward(M, buffer, buffer);
+}
+#endif
+
+
+// the following were split out into separate functions while optimizing;
+// they could be pushed back up but eh. __forceinline showed no change;
+// they're probably already being inlined.
+static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A)
+{
+ float *ee0 = e + i_off;
+ float *ee2 = ee0 + k_off;
+ int i;
+
+ assert((n & 3) == 0);
+ for (i=(n>>2); i > 0; --i) {
+ float k00_20, k01_21;
+ k00_20 = ee0[ 0] - ee2[ 0];
+ k01_21 = ee0[-1] - ee2[-1];
+ ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0];
+ ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1];
+ ee2[ 0] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-1] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+
+ k00_20 = ee0[-2] - ee2[-2];
+ k01_21 = ee0[-3] - ee2[-3];
+ ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2];
+ ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3];
+ ee2[-2] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-3] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+
+ k00_20 = ee0[-4] - ee2[-4];
+ k01_21 = ee0[-5] - ee2[-5];
+ ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4];
+ ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5];
+ ee2[-4] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-5] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+
+ k00_20 = ee0[-6] - ee2[-6];
+ k01_21 = ee0[-7] - ee2[-7];
+ ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6];
+ ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7];
+ ee2[-6] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-7] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+ ee0 -= 8;
+ ee2 -= 8;
+ }
+}
+
+static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1)
+{
+ int i;
+ float k00_20, k01_21;
+
+ float *e0 = e + d0;
+ float *e2 = e0 + k_off;
+
+ for (i=lim >> 2; i > 0; --i) {
+ k00_20 = e0[-0] - e2[-0];
+ k01_21 = e0[-1] - e2[-1];
+ e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0];
+ e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1];
+ e2[-0] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-1] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ A += k1;
+
+ k00_20 = e0[-2] - e2[-2];
+ k01_21 = e0[-3] - e2[-3];
+ e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2];
+ e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3];
+ e2[-2] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-3] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ A += k1;
+
+ k00_20 = e0[-4] - e2[-4];
+ k01_21 = e0[-5] - e2[-5];
+ e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4];
+ e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5];
+ e2[-4] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-5] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ A += k1;
+
+ k00_20 = e0[-6] - e2[-6];
+ k01_21 = e0[-7] - e2[-7];
+ e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6];
+ e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7];
+ e2[-6] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-7] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ e0 -= 8;
+ e2 -= 8;
+
+ A += k1;
+ }
+}
+
+static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0)
+{
+ int i;
+ float A0 = A[0];
+ float A1 = A[0+1];
+ float A2 = A[0+a_off];
+ float A3 = A[0+a_off+1];
+ float A4 = A[0+a_off*2+0];
+ float A5 = A[0+a_off*2+1];
+ float A6 = A[0+a_off*3+0];
+ float A7 = A[0+a_off*3+1];
+
+ float k00,k11;
+
+ float *ee0 = e +i_off;
+ float *ee2 = ee0+k_off;
+
+ for (i=n; i > 0; --i) {
+ k00 = ee0[ 0] - ee2[ 0];
+ k11 = ee0[-1] - ee2[-1];
+ ee0[ 0] = ee0[ 0] + ee2[ 0];
+ ee0[-1] = ee0[-1] + ee2[-1];
+ ee2[ 0] = (k00) * A0 - (k11) * A1;
+ ee2[-1] = (k11) * A0 + (k00) * A1;
+
+ k00 = ee0[-2] - ee2[-2];
+ k11 = ee0[-3] - ee2[-3];
+ ee0[-2] = ee0[-2] + ee2[-2];
+ ee0[-3] = ee0[-3] + ee2[-3];
+ ee2[-2] = (k00) * A2 - (k11) * A3;
+ ee2[-3] = (k11) * A2 + (k00) * A3;
+
+ k00 = ee0[-4] - ee2[-4];
+ k11 = ee0[-5] - ee2[-5];
+ ee0[-4] = ee0[-4] + ee2[-4];
+ ee0[-5] = ee0[-5] + ee2[-5];
+ ee2[-4] = (k00) * A4 - (k11) * A5;
+ ee2[-5] = (k11) * A4 + (k00) * A5;
+
+ k00 = ee0[-6] - ee2[-6];
+ k11 = ee0[-7] - ee2[-7];
+ ee0[-6] = ee0[-6] + ee2[-6];
+ ee0[-7] = ee0[-7] + ee2[-7];
+ ee2[-6] = (k00) * A6 - (k11) * A7;
+ ee2[-7] = (k11) * A6 + (k00) * A7;
+
+ ee0 -= k0;
+ ee2 -= k0;
+ }
+}
+
+STB_FORCEINLINE void iter_54(float *z)
+{
+ float k00,k11,k22,k33;
+ float y0,y1,y2,y3;
+
+ k00 = z[ 0] - z[-4];
+ y0 = z[ 0] + z[-4];
+ y2 = z[-2] + z[-6];
+ k22 = z[-2] - z[-6];
+
+ z[-0] = y0 + y2; // z0 + z4 + z2 + z6
+ z[-2] = y0 - y2; // z0 + z4 - z2 - z6
+
+ // done with y0,y2
+
+ k33 = z[-3] - z[-7];
+
+ z[-4] = k00 + k33; // z0 - z4 + z3 - z7
+ z[-6] = k00 - k33; // z0 - z4 - z3 + z7
+
+ // done with k33
+
+ k11 = z[-1] - z[-5];
+ y1 = z[-1] + z[-5];
+ y3 = z[-3] + z[-7];
+
+ z[-1] = y1 + y3; // z1 + z5 + z3 + z7
+ z[-3] = y1 - y3; // z1 + z5 - z3 - z7
+ z[-5] = k11 - k22; // z1 - z5 + z2 - z6
+ z[-7] = k11 + k22; // z1 - z5 - z2 + z6
+}
+
+static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n)
+{
+ int a_off = base_n >> 3;
+ float A2 = A[0+a_off];
+ float *z = e + i_off;
+ float *base = z - 16 * n;
+
+ while (z > base) {
+ float k00,k11;
+
+ k00 = z[-0] - z[-8];
+ k11 = z[-1] - z[-9];
+ z[-0] = z[-0] + z[-8];
+ z[-1] = z[-1] + z[-9];
+ z[-8] = k00;
+ z[-9] = k11 ;
+
+ k00 = z[ -2] - z[-10];
+ k11 = z[ -3] - z[-11];
+ z[ -2] = z[ -2] + z[-10];
+ z[ -3] = z[ -3] + z[-11];
+ z[-10] = (k00+k11) * A2;
+ z[-11] = (k11-k00) * A2;
+
+ k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation
+ k11 = z[ -5] - z[-13];
+ z[ -4] = z[ -4] + z[-12];
+ z[ -5] = z[ -5] + z[-13];
+ z[-12] = k11;
+ z[-13] = k00;
+
+ k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation
+ k11 = z[ -7] - z[-15];
+ z[ -6] = z[ -6] + z[-14];
+ z[ -7] = z[ -7] + z[-15];
+ z[-14] = (k00+k11) * A2;
+ z[-15] = (k00-k11) * A2;
+
+ iter_54(z);
+ iter_54(z-8);
+ z -= 16;
+ }
+}
+
+static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
+{
+ int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
+ int ld;
+ // @OPTIMIZE: reduce register pressure by using fewer variables?
+ int save_point = temp_alloc_save(f);
+ float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2));
+ float *u=NULL,*v=NULL;
+ // twiddle factors
+ float *A = f->A[blocktype];
+
+ // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio"
+ // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function.
+
+ // kernel from paper
+
+
+ // merged:
+ // copy and reflect spectral data
+ // step 0
+
+ // note that it turns out that the items added together during
+ // this step are, in fact, being added to themselves (as reflected
+ // by step 0). inexplicable inefficiency! this became obvious
+ // once I combined the passes.
+
+ // so there's a missing 'times 2' here (for adding X to itself).
+ // this propagates through linearly to the end, where the numbers
+ // are 1/2 too small, and need to be compensated for.
+
+ {
+ float *d,*e, *AA, *e_stop;
+ d = &buf2[n2-2];
+ AA = A;
+ e = &buffer[0];
+ e_stop = &buffer[n2];
+ while (e != e_stop) {
+ d[1] = (e[0] * AA[0] - e[2]*AA[1]);
+ d[0] = (e[0] * AA[1] + e[2]*AA[0]);
+ d -= 2;
+ AA += 2;
+ e += 4;
+ }
+
+ e = &buffer[n2-3];
+ while (d >= buf2) {
+ d[1] = (-e[2] * AA[0] - -e[0]*AA[1]);
+ d[0] = (-e[2] * AA[1] + -e[0]*AA[0]);
+ d -= 2;
+ AA += 2;
+ e -= 4;
+ }
+ }
+
+ // now we use symbolic names for these, so that we can
+ // possibly swap their meaning as we change which operations
+ // are in place
+
+ u = buffer;
+ v = buf2;
+
+ // step 2 (paper output is w, now u)
+ // this could be in place, but the data ends up in the wrong
+ // place... _somebody_'s got to swap it, so this is nominated
+ {
+ float *AA = &A[n2-8];
+ float *d0,*d1, *e0, *e1;
+
+ e0 = &v[n4];
+ e1 = &v[0];
+
+ d0 = &u[n4];
+ d1 = &u[0];
+
+ while (AA >= A) {
+ float v40_20, v41_21;
+
+ v41_21 = e0[1] - e1[1];
+ v40_20 = e0[0] - e1[0];
+ d0[1] = e0[1] + e1[1];
+ d0[0] = e0[0] + e1[0];
+ d1[1] = v41_21*AA[4] - v40_20*AA[5];
+ d1[0] = v40_20*AA[4] + v41_21*AA[5];
+
+ v41_21 = e0[3] - e1[3];
+ v40_20 = e0[2] - e1[2];
+ d0[3] = e0[3] + e1[3];
+ d0[2] = e0[2] + e1[2];
+ d1[3] = v41_21*AA[0] - v40_20*AA[1];
+ d1[2] = v40_20*AA[0] + v41_21*AA[1];
+
+ AA -= 8;
+
+ d0 += 4;
+ d1 += 4;
+ e0 += 4;
+ e1 += 4;
+ }
+ }
+
+ // step 3
+ ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+
+ // optimized step 3:
+
+ // the original step3 loop can be nested r inside s or s inside r;
+ // it's written originally as s inside r, but this is dumb when r
+ // iterates many times, and s few. So I have two copies of it and
+ // switch between them halfway.
+
+ // this is iteration 0 of step 3
+ imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A);
+ imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A);
+
+ // this is iteration 1 of step 3
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16);
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16);
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16);
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16);
+
+ l=2;
+ for (; l < (ld-3)>>1; ++l) {
+ int k0 = n >> (l+2), k0_2 = k0>>1;
+ int lim = 1 << (l+1);
+ int i;
+ for (i=0; i < lim; ++i)
+ imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3));
+ }
+
+ for (; l < ld-6; ++l) {
+ int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1;
+ int rlim = n >> (l+6), r;
+ int lim = 1 << (l+1);
+ int i_off;
+ float *A0 = A;
+ i_off = n2-1;
+ for (r=rlim; r > 0; --r) {
+ imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0);
+ A0 += k1*4;
+ i_off -= 8;
+ }
+ }
+
+ // iterations with count:
+ // ld-6,-5,-4 all interleaved together
+ // the big win comes from getting rid of needless flops
+ // due to the constants on pass 5 & 4 being all 1 and 0;
+ // combining them to be simultaneous to improve cache made little difference
+ imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n);
+
+ // output is u
+
+ // step 4, 5, and 6
+ // cannot be in-place because of step 5
+ {
+ uint16 *bitrev = f->bit_reverse[blocktype];
+ // weirdly, I'd have thought reading sequentially and writing
+ // erratically would have been better than vice-versa, but in
+ // fact that's not what my testing showed. (That is, with
+ // j = bitreverse(i), do you read i and write j, or read j and write i.)
+
+ float *d0 = &v[n4-4];
+ float *d1 = &v[n2-4];
+ while (d0 >= v) {
+ int k4;
+
+ k4 = bitrev[0];
+ d1[3] = u[k4+0];
+ d1[2] = u[k4+1];
+ d0[3] = u[k4+2];
+ d0[2] = u[k4+3];
+
+ k4 = bitrev[1];
+ d1[1] = u[k4+0];
+ d1[0] = u[k4+1];
+ d0[1] = u[k4+2];
+ d0[0] = u[k4+3];
+
+ d0 -= 4;
+ d1 -= 4;
+ bitrev += 2;
+ }
+ }
+ // (paper output is u, now v)
+
+
+ // data must be in buf2
+ assert(v == buf2);
+
+ // step 7 (paper output is v, now v)
+ // this is now in place
+ {
+ float *C = f->C[blocktype];
+ float *d, *e;
+
+ d = v;
+ e = v + n2 - 4;
+
+ while (d < e) {
+ float a02,a11,b0,b1,b2,b3;
+
+ a02 = d[0] - e[2];
+ a11 = d[1] + e[3];
+
+ b0 = C[1]*a02 + C[0]*a11;
+ b1 = C[1]*a11 - C[0]*a02;
+
+ b2 = d[0] + e[ 2];
+ b3 = d[1] - e[ 3];
+
+ d[0] = b2 + b0;
+ d[1] = b3 + b1;
+ e[2] = b2 - b0;
+ e[3] = b1 - b3;
+
+ a02 = d[2] - e[0];
+ a11 = d[3] + e[1];
+
+ b0 = C[3]*a02 + C[2]*a11;
+ b1 = C[3]*a11 - C[2]*a02;
+
+ b2 = d[2] + e[ 0];
+ b3 = d[3] - e[ 1];
+
+ d[2] = b2 + b0;
+ d[3] = b3 + b1;
+ e[0] = b2 - b0;
+ e[1] = b1 - b3;
+
+ C += 4;
+ d += 4;
+ e -= 4;
+ }
+ }
+
+ // data must be in buf2
+
+
+ // step 8+decode (paper output is X, now buffer)
+ // this generates pairs of data a la 8 and pushes them directly through
+ // the decode kernel (pushing rather than pulling) to avoid having
+ // to make another pass later
+
+ // this cannot POSSIBLY be in place, so we refer to the buffers directly
+
+ {
+ float *d0,*d1,*d2,*d3;
+
+ float *B = f->B[blocktype] + n2 - 8;
+ float *e = buf2 + n2 - 8;
+ d0 = &buffer[0];
+ d1 = &buffer[n2-4];
+ d2 = &buffer[n2];
+ d3 = &buffer[n-4];
+ while (e >= v) {
+ float p0,p1,p2,p3;
+
+ p3 = e[6]*B[7] - e[7]*B[6];
+ p2 = -e[6]*B[6] - e[7]*B[7];
+
+ d0[0] = p3;
+ d1[3] = - p3;
+ d2[0] = p2;
+ d3[3] = p2;
+
+ p1 = e[4]*B[5] - e[5]*B[4];
+ p0 = -e[4]*B[4] - e[5]*B[5];
+
+ d0[1] = p1;
+ d1[2] = - p1;
+ d2[1] = p0;
+ d3[2] = p0;
+
+ p3 = e[2]*B[3] - e[3]*B[2];
+ p2 = -e[2]*B[2] - e[3]*B[3];
+
+ d0[2] = p3;
+ d1[1] = - p3;
+ d2[2] = p2;
+ d3[1] = p2;
+
+ p1 = e[0]*B[1] - e[1]*B[0];
+ p0 = -e[0]*B[0] - e[1]*B[1];
+
+ d0[3] = p1;
+ d1[0] = - p1;
+ d2[3] = p0;
+ d3[0] = p0;
+
+ B -= 8;
+ e -= 8;
+ d0 += 4;
+ d2 += 4;
+ d1 -= 4;
+ d3 -= 4;
+ }
+ }
+
+ temp_free(f,buf2);
+ temp_alloc_restore(f,save_point);
+}
+
+#if 0
+// this is the original version of the above code, if you want to optimize it from scratch
+void inverse_mdct_naive(float *buffer, int n)
+{
+ float s;
+ float A[1 << 12], B[1 << 12], C[1 << 11];
+ int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
+ int n3_4 = n - n4, ld;
+ // how can they claim this only uses N words?!
+ // oh, because they're only used sparsely, whoops
+ float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13];
+ // set up twiddle factors
+
+ for (k=k2=0; k < n4; ++k,k2+=2) {
+ A[k2 ] = (float) cos(4*k*M_PI/n);
+ A[k2+1] = (float) -sin(4*k*M_PI/n);
+ B[k2 ] = (float) cos((k2+1)*M_PI/n/2);
+ B[k2+1] = (float) sin((k2+1)*M_PI/n/2);
+ }
+ for (k=k2=0; k < n8; ++k,k2+=2) {
+ C[k2 ] = (float) cos(2*(k2+1)*M_PI/n);
+ C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
+ }
+
+ // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio"
+ // Note there are bugs in that pseudocode, presumably due to them attempting
+ // to rename the arrays nicely rather than representing the way their actual
+ // implementation bounces buffers back and forth. As a result, even in the
+ // "some formulars corrected" version, a direct implementation fails. These
+ // are noted below as "paper bug".
+
+ // copy and reflect spectral data
+ for (k=0; k < n2; ++k) u[k] = buffer[k];
+ for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1];
+ // kernel from paper
+ // step 1
+ for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) {
+ v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1];
+ v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2];
+ }
+ // step 2
+ for (k=k4=0; k < n8; k+=1, k4+=4) {
+ w[n2+3+k4] = v[n2+3+k4] + v[k4+3];
+ w[n2+1+k4] = v[n2+1+k4] + v[k4+1];
+ w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4];
+ w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4];
+ }
+ // step 3
+ ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+ for (l=0; l < ld-3; ++l) {
+ int k0 = n >> (l+2), k1 = 1 << (l+3);
+ int rlim = n >> (l+4), r4, r;
+ int s2lim = 1 << (l+2), s2;
+ for (r=r4=0; r < rlim; r4+=4,++r) {
+ for (s2=0; s2 < s2lim; s2+=2) {
+ u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4];
+ u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4];
+ u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1]
+ - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1];
+ u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1]
+ + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1];
+ }
+ }
+ if (l+1 < ld-3) {
+ // paper bug: ping-ponging of u&w here is omitted
+ memcpy(w, u, sizeof(u));
+ }
+ }
+
+ // step 4
+ for (i=0; i < n8; ++i) {
+ int j = bit_reverse(i) >> (32-ld+3);
+ assert(j < n8);
+ if (i == j) {
+ // paper bug: original code probably swapped in place; if copying,
+ // need to directly copy in this case
+ int i8 = i << 3;
+ v[i8+1] = u[i8+1];
+ v[i8+3] = u[i8+3];
+ v[i8+5] = u[i8+5];
+ v[i8+7] = u[i8+7];
+ } else if (i < j) {
+ int i8 = i << 3, j8 = j << 3;
+ v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1];
+ v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3];
+ v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5];
+ v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7];
+ }
+ }
+ // step 5
+ for (k=0; k < n2; ++k) {
+ w[k] = v[k*2+1];
+ }
+ // step 6
+ for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) {
+ u[n-1-k2] = w[k4];
+ u[n-2-k2] = w[k4+1];
+ u[n3_4 - 1 - k2] = w[k4+2];
+ u[n3_4 - 2 - k2] = w[k4+3];
+ }
+ // step 7
+ for (k=k2=0; k < n8; ++k, k2 += 2) {
+ v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
+ v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
+ v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
+ v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
+ }
+ // step 8
+ for (k=k2=0; k < n4; ++k,k2 += 2) {
+ X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1];
+ X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ];
+ }
+
+ // decode kernel to output
+ // determined the following value experimentally
+ // (by first figuring out what made inverse_mdct_slow work); then matching that here
+ // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?)
+ s = 0.5; // theoretically would be n4
+
+ // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code,
+ // so it needs to use the "old" B values to behave correctly, or else
+ // set s to 1.0 ]]]
+ for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4];
+ for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1];
+ for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4];
+}
+#endif
+
+static float *get_window(vorb *f, int len)
+{
+ len <<= 1;
+ if (len == f->blocksize_0) return f->window[0];
+ if (len == f->blocksize_1) return f->window[1];
+ return NULL;
+}
+
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+typedef int16 YTYPE;
+#else
+typedef int YTYPE;
+#endif
+static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag)
+{
+ int n2 = n >> 1;
+ int s = map->chan[i].mux, floor;
+ floor = map->submap_floor[s];
+ if (f->floor_types[floor] == 0) {
+ return error(f, VORBIS_invalid_stream);
+ } else {
+ Floor1 *g = &f->floor_config[floor].floor1;
+ int j,q;
+ int lx = 0, ly = finalY[0] * g->floor1_multiplier;
+ for (q=1; q < g->values; ++q) {
+ j = g->sorted_order[q];
+ #ifndef STB_VORBIS_NO_DEFER_FLOOR
+ if (finalY[j] >= 0)
+ #else
+ if (step2_flag[j])
+ #endif
+ {
+ int hy = finalY[j] * g->floor1_multiplier;
+ int hx = g->Xlist[j];
+ if (lx != hx)
+ draw_line(target, lx,ly, hx,hy, n2);
+ CHECK(f);
+ lx = hx, ly = hy;
+ }
+ }
+ if (lx < n2) {
+ // optimization of: draw_line(target, lx,ly, n,ly, n2);
+ for (j=lx; j < n2; ++j)
+ LINE_OP(target[j], inverse_db_table[ly]);
+ CHECK(f);
+ }
+ }
+ return TRUE;
+}
+
+// The meaning of "left" and "right"
+//
+// For a given frame:
+// we compute samples from 0..n
+// window_center is n/2
+// we'll window and mix the samples from left_start to left_end with data from the previous frame
+// all of the samples from left_end to right_start can be output without mixing; however,
+// this interval is 0-length except when transitioning between short and long frames
+// all of the samples from right_start to right_end need to be mixed with the next frame,
+// which we don't have, so those get saved in a buffer
+// frame N's right_end-right_start, the number of samples to mix with the next frame,
+// has to be the same as frame N+1's left_end-left_start (which they are by
+// construction)
+
+static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
+{
+ Mode *m;
+ int i, n, prev, next, window_center;
+ f->channel_buffer_start = f->channel_buffer_end = 0;
+
+ retry:
+ if (f->eof) return FALSE;
+ if (!maybe_start_packet(f))
+ return FALSE;
+ // check packet type
+ if (get_bits(f,1) != 0) {
+ if (IS_PUSH_MODE(f))
+ return error(f,VORBIS_bad_packet_type);
+ while (EOP != get8_packet(f));
+ goto retry;
+ }
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+
+ i = get_bits(f, ilog(f->mode_count-1));
+ if (i == EOP) return FALSE;
+ if (i >= f->mode_count) return FALSE;
+ *mode = i;
+ m = f->mode_config + i;
+ if (m->blockflag) {
+ n = f->blocksize_1;
+ prev = get_bits(f,1);
+ next = get_bits(f,1);
+ } else {
+ prev = next = 0;
+ n = f->blocksize_0;
+ }
+
+// WINDOWING
+
+ window_center = n >> 1;
+ if (m->blockflag && !prev) {
+ *p_left_start = (n - f->blocksize_0) >> 2;
+ *p_left_end = (n + f->blocksize_0) >> 2;
+ } else {
+ *p_left_start = 0;
+ *p_left_end = window_center;
+ }
+ if (m->blockflag && !next) {
+ *p_right_start = (n*3 - f->blocksize_0) >> 2;
+ *p_right_end = (n*3 + f->blocksize_0) >> 2;
+ } else {
+ *p_right_start = window_center;
+ *p_right_end = n;
+ }
+
+ return TRUE;
+}
+
+static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left)
+{
+ Mapping *map;
+ int i,j,k,n,n2;
+ int zero_channel[256];
+ int really_zero_channel[256];
+
+// WINDOWING
+
+ n = f->blocksize[m->blockflag];
+ map = &f->mapping[m->mapping];
+
+// FLOORS
+ n2 = n >> 1;
+
+ CHECK(f);
+
+ for (i=0; i < f->channels; ++i) {
+ int s = map->chan[i].mux, floor;
+ zero_channel[i] = FALSE;
+ floor = map->submap_floor[s];
+ if (f->floor_types[floor] == 0) {
+ return error(f, VORBIS_invalid_stream);
+ } else {
+ Floor1 *g = &f->floor_config[floor].floor1;
+ if (get_bits(f, 1)) {
+ short *finalY;
+ uint8 step2_flag[256];
+ static int range_list[4] = { 256, 128, 86, 64 };
+ int range = range_list[g->floor1_multiplier-1];
+ int offset = 2;
+ finalY = f->finalY[i];
+ finalY[0] = get_bits(f, ilog(range)-1);
+ finalY[1] = get_bits(f, ilog(range)-1);
+ for (j=0; j < g->partitions; ++j) {
+ int pclass = g->partition_class_list[j];
+ int cdim = g->class_dimensions[pclass];
+ int cbits = g->class_subclasses[pclass];
+ int csub = (1 << cbits)-1;
+ int cval = 0;
+ if (cbits) {
+ Codebook *c = f->codebooks + g->class_masterbooks[pclass];
+ DECODE(cval,f,c);
+ }
+ for (k=0; k < cdim; ++k) {
+ int book = g->subclass_books[pclass][cval & csub];
+ cval = cval >> cbits;
+ if (book >= 0) {
+ int temp;
+ Codebook *c = f->codebooks + book;
+ DECODE(temp,f,c);
+ finalY[offset++] = temp;
+ } else
+ finalY[offset++] = 0;
+ }
+ }
+ if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec
+ step2_flag[0] = step2_flag[1] = 1;
+ for (j=2; j < g->values; ++j) {
+ int low, high, pred, highroom, lowroom, room, val;
+ low = g->neighbors[j][0];
+ high = g->neighbors[j][1];
+ //neighbors(g->Xlist, j, &low, &high);
+ pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]);
+ val = finalY[j];
+ highroom = range - pred;
+ lowroom = pred;
+ if (highroom < lowroom)
+ room = highroom * 2;
+ else
+ room = lowroom * 2;
+ if (val) {
+ step2_flag[low] = step2_flag[high] = 1;
+ step2_flag[j] = 1;
+ if (val >= room)
+ if (highroom > lowroom)
+ finalY[j] = val - lowroom + pred;
+ else
+ finalY[j] = pred - val + highroom - 1;
+ else
+ if (val & 1)
+ finalY[j] = pred - ((val+1)>>1);
+ else
+ finalY[j] = pred + (val>>1);
+ } else {
+ step2_flag[j] = 0;
+ finalY[j] = pred;
+ }
+ }
+
+#ifdef STB_VORBIS_NO_DEFER_FLOOR
+ do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag);
+#else
+ // defer final floor computation until _after_ residue
+ for (j=0; j < g->values; ++j) {
+ if (!step2_flag[j])
+ finalY[j] = -1;
+ }
+#endif
+ } else {
+ error:
+ zero_channel[i] = TRUE;
+ }
+ // So we just defer everything else to later
+
+ // at this point we've decoded the floor into buffer
+ }
+ }
+ CHECK(f);
+ // at this point we've decoded all floors
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+
+ // re-enable coupled channels if necessary
+ memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels);
+ for (i=0; i < map->coupling_steps; ++i)
+ if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) {
+ zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE;
+ }
+
+ CHECK(f);
+// RESIDUE DECODE
+ for (i=0; i < map->submaps; ++i) {
+ float *residue_buffers[STB_VORBIS_MAX_CHANNELS];
+ int r;
+ uint8 do_not_decode[256];
+ int ch = 0;
+ for (j=0; j < f->channels; ++j) {
+ if (map->chan[j].mux == i) {
+ if (zero_channel[j]) {
+ do_not_decode[ch] = TRUE;
+ residue_buffers[ch] = NULL;
+ } else {
+ do_not_decode[ch] = FALSE;
+ residue_buffers[ch] = f->channel_buffers[j];
+ }
+ ++ch;
+ }
+ }
+ r = map->submap_residue[i];
+ decode_residue(f, residue_buffers, ch, n2, r, do_not_decode);
+ }
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+ CHECK(f);
+
+// INVERSE COUPLING
+ for (i = map->coupling_steps-1; i >= 0; --i) {
+ int n2 = n >> 1;
+ float *m = f->channel_buffers[map->chan[i].magnitude];
+ float *a = f->channel_buffers[map->chan[i].angle ];
+ for (j=0; j < n2; ++j) {
+ float a2,m2;
+ if (m[j] > 0)
+ if (a[j] > 0)
+ m2 = m[j], a2 = m[j] - a[j];
+ else
+ a2 = m[j], m2 = m[j] + a[j];
+ else
+ if (a[j] > 0)
+ m2 = m[j], a2 = m[j] + a[j];
+ else
+ a2 = m[j], m2 = m[j] - a[j];
+ m[j] = m2;
+ a[j] = a2;
+ }
+ }
+ CHECK(f);
+
+ // finish decoding the floors
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+ for (i=0; i < f->channels; ++i) {
+ if (really_zero_channel[i]) {
+ memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
+ } else {
+ do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL);
+ }
+ }
+#else
+ for (i=0; i < f->channels; ++i) {
+ if (really_zero_channel[i]) {
+ memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
+ } else {
+ for (j=0; j < n2; ++j)
+ f->channel_buffers[i][j] *= f->floor_buffers[i][j];
+ }
+ }
+#endif
+
+// INVERSE MDCT
+ CHECK(f);
+ for (i=0; i < f->channels; ++i)
+ inverse_mdct(f->channel_buffers[i], n, f, m->blockflag);
+ CHECK(f);
+
+ // this shouldn't be necessary, unless we exited on an error
+ // and want to flush to get to the next packet
+ flush_packet(f);
+
+ if (f->first_decode) {
+ // assume we start so first non-discarded sample is sample 0
+ // this isn't to spec, but spec would require us to read ahead
+ // and decode the size of all current frames--could be done,
+ // but presumably it's not a commonly used feature
+ f->current_loc = -n2; // start of first frame is positioned for discard
+ // we might have to discard samples "from" the next frame too,
+ // if we're lapping a large block then a small at the start?
+ f->discard_samples_deferred = n - right_end;
+ f->current_loc_valid = TRUE;
+ f->first_decode = FALSE;
+ } else if (f->discard_samples_deferred) {
+ if (f->discard_samples_deferred >= right_start - left_start) {
+ f->discard_samples_deferred -= (right_start - left_start);
+ left_start = right_start;
+ *p_left = left_start;
+ } else {
+ left_start += f->discard_samples_deferred;
+ *p_left = left_start;
+ f->discard_samples_deferred = 0;
+ }
+ } else if (f->previous_length == 0 && f->current_loc_valid) {
+ // we're recovering from a seek... that means we're going to discard
+ // the samples from this packet even though we know our position from
+ // the last page header, so we need to update the position based on
+ // the discarded samples here
+ // but wait, the code below is going to add this in itself even
+ // on a discard, so we don't need to do it here...
+ }
+
+ // check if we have ogg information about the sample # for this packet
+ if (f->last_seg_which == f->end_seg_with_known_loc) {
+ // if we have a valid current loc, and this is final:
+ if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
+ uint32 current_end = f->known_loc_for_packet;
+ // then let's infer the size of the (probably) short final frame
+ if (current_end < f->current_loc + (right_end-left_start)) {
+ if (current_end < f->current_loc) {
+ // negative truncation, that's impossible!
+ *len = 0;
+ } else {
+ *len = current_end - f->current_loc;
+ }
+ *len += left_start; // this doesn't seem right, but has no ill effect on my test files
+ if (*len > right_end) *len = right_end; // this should never happen
+ f->current_loc += *len;
+ return TRUE;
+ }
+ }
+ // otherwise, just set our sample loc
+ // guess that the ogg granule pos refers to the _middle_ of the
+ // last frame?
+ // set f->current_loc to the position of left_start
+ f->current_loc = f->known_loc_for_packet - (n2-left_start);
+ f->current_loc_valid = TRUE;
+ }
+ if (f->current_loc_valid)
+ f->current_loc += (right_start - left_start);
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+ *len = right_end; // ignore samples after the window goes to 0
+ CHECK(f);
+
+ return TRUE;
+}
+
+static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right)
+{
+ int mode, left_end, right_end;
+ if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0;
+ return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left);
+}
+
+static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right)
+{
+ int prev,i,j;
+ // we use right&left (the start of the right- and left-window sin()-regions)
+ // to determine how much to return, rather than inferring from the rules
+ // (same result, clearer code); 'left' indicates where our sin() window
+ // starts, therefore where the previous window's right edge starts, and
+ // therefore where to start mixing from the previous buffer. 'right'
+ // indicates where our sin() ending-window starts, therefore that's where
+ // we start saving, and where our returned-data ends.
+
+ // mixin from previous window
+ if (f->previous_length) {
+ int i,j, n = f->previous_length;
+ float *w = get_window(f, n);
+ if (w == NULL) return 0;
+ for (i=0; i < f->channels; ++i) {
+ for (j=0; j < n; ++j)
+ f->channel_buffers[i][left+j] =
+ f->channel_buffers[i][left+j]*w[ j] +
+ f->previous_window[i][ j]*w[n-1-j];
+ }
+ }
+
+ prev = f->previous_length;
+
+ // last half of this data becomes previous window
+ f->previous_length = len - right;
+
+ // @OPTIMIZE: could avoid this copy by double-buffering the
+ // output (flipping previous_window with channel_buffers), but
+ // then previous_window would have to be 2x as large, and
+ // channel_buffers couldn't be temp mem (although they're NOT
+ // currently temp mem, they could be (unless we want to level
+ // performance by spreading out the computation))
+ for (i=0; i < f->channels; ++i)
+ for (j=0; right+j < len; ++j)
+ f->previous_window[i][j] = f->channel_buffers[i][right+j];
+
+ if (!prev)
+ // there was no previous packet, so this data isn't valid...
+ // this isn't entirely true, only the would-have-overlapped data
+ // isn't valid, but this seems to be what the spec requires
+ return 0;
+
+ // truncate a short frame
+ if (len < right) right = len;
+
+ f->samples_output += right-left;
+
+ return right - left;
+}
+
+static int vorbis_pump_first_frame(stb_vorbis *f)
+{
+ int len, right, left, res;
+ res = vorbis_decode_packet(f, &len, &left, &right);
+ if (res)
+ vorbis_finish_frame(f, len, left, right);
+ return res;
+}
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+static int is_whole_packet_present(stb_vorbis *f)
+{
+ // make sure that we have the packet available before continuing...
+ // this requires a full ogg parse, but we know we can fetch from f->stream
+
+ // instead of coding this out explicitly, we could save the current read state,
+ // read the next packet with get8() until end-of-packet, check f->eof, then
+ // reset the state? but that would be slower, esp. since we'd have over 256 bytes
+ // of state to restore (primarily the page segment table)
+
+ int s = f->next_seg, first = TRUE;
+ uint8 *p = f->stream;
+
+ if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag
+ for (; s < f->segment_count; ++s) {
+ p += f->segments[s];
+ if (f->segments[s] < 255) // stop at first short segment
+ break;
+ }
+ // either this continues, or it ends it...
+ if (s == f->segment_count)
+ s = -1; // set 'crosses page' flag
+ if (p > f->stream_end) return error(f, VORBIS_need_more_data);
+ first = FALSE;
+ }
+ for (; s == -1;) {
+ uint8 *q;
+ int n;
+
+ // check that we have the page header ready
+ if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data);
+ // validate the page
+ if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream);
+ if (p[4] != 0) return error(f, VORBIS_invalid_stream);
+ if (first) { // the first segment must NOT have 'continued_packet', later ones MUST
+ if (f->previous_length)
+ if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream);
+ // if no previous length, we're resynching, so we can come in on a continued-packet,
+ // which we'll just drop
+ } else {
+ if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream);
+ }
+ n = p[26]; // segment counts
+ q = p+27; // q points to segment table
+ p = q + n; // advance past header
+ // make sure we've read the segment table
+ if (p > f->stream_end) return error(f, VORBIS_need_more_data);
+ for (s=0; s < n; ++s) {
+ p += q[s];
+ if (q[s] < 255)
+ break;
+ }
+ if (s == n)
+ s = -1; // set 'crosses page' flag
+ if (p > f->stream_end) return error(f, VORBIS_need_more_data);
+ first = FALSE;
+ }
+ return TRUE;
+}
+#endif // !STB_VORBIS_NO_PUSHDATA_API
+
+static int start_decoder(vorb *f)
+{
+ uint8 header[6], x,y;
+ int len,i,j,k, max_submaps = 0;
+ int longest_floorlist=0;
+
+ // first page, first packet
+ f->first_decode = TRUE;
+
+ if (!start_page(f)) return FALSE;
+ // validate page flag
+ if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page);
+ if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page);
+ if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page);
+ // check for expected packet length
+ if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page);
+ if (f->segments[0] != 30) {
+ // check for the Ogg skeleton fishead identifying header to refine our error
+ if (f->segments[0] == 64 &&
+ getn(f, header, 6) &&
+ header[0] == 'f' &&
+ header[1] == 'i' &&
+ header[2] == 's' &&
+ header[3] == 'h' &&
+ header[4] == 'e' &&
+ header[5] == 'a' &&
+ get8(f) == 'd' &&
+ get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported);
+ else
+ return error(f, VORBIS_invalid_first_page);
+ }
+
+ // read packet
+ // check packet header
+ if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page);
+ if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof);
+ if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page);
+ // vorbis_version
+ if (get32(f) != 0) return error(f, VORBIS_invalid_first_page);
+ f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page);
+ if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels);
+ f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page);
+ get32(f); // bitrate_maximum
+ get32(f); // bitrate_nominal
+ get32(f); // bitrate_minimum
+ x = get8(f);
+ {
+ int log0,log1;
+ log0 = x & 15;
+ log1 = x >> 4;
+ f->blocksize_0 = 1 << log0;
+ f->blocksize_1 = 1 << log1;
+ if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup);
+ if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup);
+ if (log0 > log1) return error(f, VORBIS_invalid_setup);
+ }
+
+ // framing_flag
+ x = get8(f);
+ if (!(x & 1)) return error(f, VORBIS_invalid_first_page);
+
+ // second packet!
+ if (!start_page(f)) return FALSE;
+
+ if (!start_packet(f)) return FALSE;
+ do {
+ len = next_segment(f);
+ skip(f, len);
+ f->bytes_in_seg = 0;
+ } while (len);
+
+ // third packet!
+ if (!start_packet(f)) return FALSE;
+
+ #ifndef STB_VORBIS_NO_PUSHDATA_API
+ if (IS_PUSH_MODE(f)) {
+ if (!is_whole_packet_present(f)) {
+ // convert error in ogg header to write type
+ if (f->error == VORBIS_invalid_stream)
+ f->error = VORBIS_invalid_setup;
+ return FALSE;
+ }
+ }
+ #endif
+
+ crc32_init(); // always init it, to avoid multithread race conditions
+
+ if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup);
+ for (i=0; i < 6; ++i) header[i] = get8_packet(f);
+ if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup);
+
+ // codebooks
+
+ f->codebook_count = get_bits(f,8) + 1;
+ f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count);
+ if (f->codebooks == NULL) return error(f, VORBIS_outofmem);
+ memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count);
+ for (i=0; i < f->codebook_count; ++i) {
+ uint32 *values;
+ int ordered, sorted_count;
+ int total=0;
+ uint8 *lengths;
+ Codebook *c = f->codebooks+i;
+ CHECK(f);
+ x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup);
+ x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup);
+ x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup);
+ x = get_bits(f, 8);
+ c->dimensions = (get_bits(f, 8)<<8) + x;
+ x = get_bits(f, 8);
+ y = get_bits(f, 8);
+ c->entries = (get_bits(f, 8)<<16) + (y<<8) + x;
+ ordered = get_bits(f,1);
+ c->sparse = ordered ? 0 : get_bits(f,1);
+
+ if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup);
+
+ if (c->sparse)
+ lengths = (uint8 *) setup_temp_malloc(f, c->entries);
+ else
+ lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
+
+ if (!lengths) return error(f, VORBIS_outofmem);
+
+ if (ordered) {
+ int current_entry = 0;
+ int current_length = get_bits(f,5) + 1;
+ while (current_entry < c->entries) {
+ int limit = c->entries - current_entry;
+ int n = get_bits(f, ilog(limit));
+ if (current_length >= 32) return error(f, VORBIS_invalid_setup);
+ if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); }
+ memset(lengths + current_entry, current_length, n);
+ current_entry += n;
+ ++current_length;
+ }
+ } else {
+ for (j=0; j < c->entries; ++j) {
+ int present = c->sparse ? get_bits(f,1) : 1;
+ if (present) {
+ lengths[j] = get_bits(f, 5) + 1;
+ ++total;
+ if (lengths[j] == 32)
+ return error(f, VORBIS_invalid_setup);
+ } else {
+ lengths[j] = NO_CODE;
+ }
+ }
+ }
+
+ if (c->sparse && total >= c->entries >> 2) {
+ // convert sparse items to non-sparse!
+ if (c->entries > (int) f->setup_temp_memory_required)
+ f->setup_temp_memory_required = c->entries;
+
+ c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
+ if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem);
+ memcpy(c->codeword_lengths, lengths, c->entries);
+ setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs!
+ lengths = c->codeword_lengths;
+ c->sparse = 0;
+ }
+
+ // compute the size of the sorted tables
+ if (c->sparse) {
+ sorted_count = total;
+ } else {
+ sorted_count = 0;
+ #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+ for (j=0; j < c->entries; ++j)
+ if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE)
+ ++sorted_count;
+ #endif
+ }
+
+ c->sorted_entries = sorted_count;
+ values = NULL;
+
+ CHECK(f);
+ if (!c->sparse) {
+ c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries);
+ if (!c->codewords) return error(f, VORBIS_outofmem);
+ } else {
+ unsigned int size;
+ if (c->sorted_entries) {
+ c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries);
+ if (!c->codeword_lengths) return error(f, VORBIS_outofmem);
+ c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries);
+ if (!c->codewords) return error(f, VORBIS_outofmem);
+ values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries);
+ if (!values) return error(f, VORBIS_outofmem);
+ }
+ size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries;
+ if (size > f->setup_temp_memory_required)
+ f->setup_temp_memory_required = size;
+ }
+
+ if (!compute_codewords(c, lengths, c->entries, values)) {
+ if (c->sparse) setup_temp_free(f, values, 0);
+ return error(f, VORBIS_invalid_setup);
+ }
+
+ if (c->sorted_entries) {
+ // allocate an extra slot for sentinels
+ c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1));
+ if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem);
+ // allocate an extra slot at the front so that c->sorted_values[-1] is defined
+ // so that we can catch that case without an extra if
+ c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1));
+ if (c->sorted_values == NULL) return error(f, VORBIS_outofmem);
+ ++c->sorted_values;
+ c->sorted_values[-1] = -1;
+ compute_sorted_huffman(c, lengths, values);
+ }
+
+ if (c->sparse) {
+ setup_temp_free(f, values, sizeof(*values)*c->sorted_entries);
+ setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries);
+ setup_temp_free(f, lengths, c->entries);
+ c->codewords = NULL;
+ }
+
+ compute_accelerated_huffman(c);
+
+ CHECK(f);
+ c->lookup_type = get_bits(f, 4);
+ if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup);
+ if (c->lookup_type > 0) {
+ uint16 *mults;
+ c->minimum_value = float32_unpack(get_bits(f, 32));
+ c->delta_value = float32_unpack(get_bits(f, 32));
+ c->value_bits = get_bits(f, 4)+1;
+ c->sequence_p = get_bits(f,1);
+ if (c->lookup_type == 1) {
+ int values = lookup1_values(c->entries, c->dimensions);
+ if (values < 0) return error(f, VORBIS_invalid_setup);
+ c->lookup_values = (uint32) values;
+ } else {
+ c->lookup_values = c->entries * c->dimensions;
+ }
+ if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup);
+ mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values);
+ if (mults == NULL) return error(f, VORBIS_outofmem);
+ for (j=0; j < (int) c->lookup_values; ++j) {
+ int q = get_bits(f, c->value_bits);
+ if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); }
+ mults[j] = q;
+ }
+
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ int len, sparse = c->sparse;
+ float last=0;
+ // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop
+ if (sparse) {
+ if (c->sorted_entries == 0) goto skip;
+ c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions);
+ } else
+ c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions);
+ if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
+ len = sparse ? c->sorted_entries : c->entries;
+ for (j=0; j < len; ++j) {
+ unsigned int z = sparse ? c->sorted_values[j] : j;
+ unsigned int div=1;
+ for (k=0; k < c->dimensions; ++k) {
+ int off = (z / div) % c->lookup_values;
+ float val = mults[off];
+ val = mults[off]*c->delta_value + c->minimum_value + last;
+ c->multiplicands[j*c->dimensions + k] = val;
+ if (c->sequence_p)
+ last = val;
+ if (k+1 < c->dimensions) {
+ if (div > UINT_MAX / (unsigned int) c->lookup_values) {
+ setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
+ return error(f, VORBIS_invalid_setup);
+ }
+ div *= c->lookup_values;
+ }
+ }
+ }
+ c->lookup_type = 2;
+ }
+ else
+#endif
+ {
+ float last=0;
+ CHECK(f);
+ c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values);
+ if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
+ for (j=0; j < (int) c->lookup_values; ++j) {
+ float val = mults[j] * c->delta_value + c->minimum_value + last;
+ c->multiplicands[j] = val;
+ if (c->sequence_p)
+ last = val;
+ }
+ }
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ skip:;
+#endif
+ setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values);
+
+ CHECK(f);
+ }
+ CHECK(f);
+ }
+
+ // time domain transfers (notused)
+
+ x = get_bits(f, 6) + 1;
+ for (i=0; i < x; ++i) {
+ uint32 z = get_bits(f, 16);
+ if (z != 0) return error(f, VORBIS_invalid_setup);
+ }
+
+ // Floors
+ f->floor_count = get_bits(f, 6)+1;
+ f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config));
+ if (f->floor_config == NULL) return error(f, VORBIS_outofmem);
+ for (i=0; i < f->floor_count; ++i) {
+ f->floor_types[i] = get_bits(f, 16);
+ if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup);
+ if (f->floor_types[i] == 0) {
+ Floor0 *g = &f->floor_config[i].floor0;
+ g->order = get_bits(f,8);
+ g->rate = get_bits(f,16);
+ g->bark_map_size = get_bits(f,16);
+ g->amplitude_bits = get_bits(f,6);
+ g->amplitude_offset = get_bits(f,8);
+ g->number_of_books = get_bits(f,4) + 1;
+ for (j=0; j < g->number_of_books; ++j)
+ g->book_list[j] = get_bits(f,8);
+ return error(f, VORBIS_feature_not_supported);
+ } else {
+ stbv__floor_ordering p[31*8+2];
+ Floor1 *g = &f->floor_config[i].floor1;
+ int max_class = -1;
+ g->partitions = get_bits(f, 5);
+ for (j=0; j < g->partitions; ++j) {
+ g->partition_class_list[j] = get_bits(f, 4);
+ if (g->partition_class_list[j] > max_class)
+ max_class = g->partition_class_list[j];
+ }
+ for (j=0; j <= max_class; ++j) {
+ g->class_dimensions[j] = get_bits(f, 3)+1;
+ g->class_subclasses[j] = get_bits(f, 2);
+ if (g->class_subclasses[j]) {
+ g->class_masterbooks[j] = get_bits(f, 8);
+ if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ }
+ for (k=0; k < 1 << g->class_subclasses[j]; ++k) {
+ g->subclass_books[j][k] = get_bits(f,8)-1;
+ if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ }
+ }
+ g->floor1_multiplier = get_bits(f,2)+1;
+ g->rangebits = get_bits(f,4);
+ g->Xlist[0] = 0;
+ g->Xlist[1] = 1 << g->rangebits;
+ g->values = 2;
+ for (j=0; j < g->partitions; ++j) {
+ int c = g->partition_class_list[j];
+ for (k=0; k < g->class_dimensions[c]; ++k) {
+ g->Xlist[g->values] = get_bits(f, g->rangebits);
+ ++g->values;
+ }
+ }
+ // precompute the sorting
+ for (j=0; j < g->values; ++j) {
+ p[j].x = g->Xlist[j];
+ p[j].id = j;
+ }
+ qsort(p, g->values, sizeof(p[0]), point_compare);
+ for (j=0; j < g->values-1; ++j)
+ if (p[j].x == p[j+1].x)
+ return error(f, VORBIS_invalid_setup);
+ for (j=0; j < g->values; ++j)
+ g->sorted_order[j] = (uint8) p[j].id;
+ // precompute the neighbors
+ for (j=2; j < g->values; ++j) {
+ int low = 0,hi = 0;
+ neighbors(g->Xlist, j, &low,&hi);
+ g->neighbors[j][0] = low;
+ g->neighbors[j][1] = hi;
+ }
+
+ if (g->values > longest_floorlist)
+ longest_floorlist = g->values;
+ }
+ }
+
+ // Residue
+ f->residue_count = get_bits(f, 6)+1;
+ f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0]));
+ if (f->residue_config == NULL) return error(f, VORBIS_outofmem);
+ memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0]));
+ for (i=0; i < f->residue_count; ++i) {
+ uint8 residue_cascade[64];
+ Residue *r = f->residue_config+i;
+ f->residue_types[i] = get_bits(f, 16);
+ if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup);
+ r->begin = get_bits(f, 24);
+ r->end = get_bits(f, 24);
+ if (r->end < r->begin) return error(f, VORBIS_invalid_setup);
+ r->part_size = get_bits(f,24)+1;
+ r->classifications = get_bits(f,6)+1;
+ r->classbook = get_bits(f,8);
+ if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ for (j=0; j < r->classifications; ++j) {
+ uint8 high_bits=0;
+ uint8 low_bits=get_bits(f,3);
+ if (get_bits(f,1))
+ high_bits = get_bits(f,5);
+ residue_cascade[j] = high_bits*8 + low_bits;
+ }
+ r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications);
+ if (r->residue_books == NULL) return error(f, VORBIS_outofmem);
+ for (j=0; j < r->classifications; ++j) {
+ for (k=0; k < 8; ++k) {
+ if (residue_cascade[j] & (1 << k)) {
+ r->residue_books[j][k] = get_bits(f, 8);
+ if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ } else {
+ r->residue_books[j][k] = -1;
+ }
+ }
+ }
+ // precompute the classifications[] array to avoid inner-loop mod/divide
+ // call it 'classdata' since we already have r->classifications
+ r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
+ if (!r->classdata) return error(f, VORBIS_outofmem);
+ memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
+ for (j=0; j < f->codebooks[r->classbook].entries; ++j) {
+ int classwords = f->codebooks[r->classbook].dimensions;
+ int temp = j;
+ r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords);
+ if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem);
+ for (k=classwords-1; k >= 0; --k) {
+ r->classdata[j][k] = temp % r->classifications;
+ temp /= r->classifications;
+ }
+ }
+ }
+
+ f->mapping_count = get_bits(f,6)+1;
+ f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping));
+ if (f->mapping == NULL) return error(f, VORBIS_outofmem);
+ memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping));
+ for (i=0; i < f->mapping_count; ++i) {
+ Mapping *m = f->mapping + i;
+ int mapping_type = get_bits(f,16);
+ if (mapping_type != 0) return error(f, VORBIS_invalid_setup);
+ m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan));
+ if (m->chan == NULL) return error(f, VORBIS_outofmem);
+ if (get_bits(f,1))
+ m->submaps = get_bits(f,4)+1;
+ else
+ m->submaps = 1;
+ if (m->submaps > max_submaps)
+ max_submaps = m->submaps;
+ if (get_bits(f,1)) {
+ m->coupling_steps = get_bits(f,8)+1;
+ if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup);
+ for (k=0; k < m->coupling_steps; ++k) {
+ m->chan[k].magnitude = get_bits(f, ilog(f->channels-1));
+ m->chan[k].angle = get_bits(f, ilog(f->channels-1));
+ if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup);
+ if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup);
+ if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup);
+ }
+ } else
+ m->coupling_steps = 0;
+
+ // reserved field
+ if (get_bits(f,2)) return error(f, VORBIS_invalid_setup);
+ if (m->submaps > 1) {
+ for (j=0; j < f->channels; ++j) {
+ m->chan[j].mux = get_bits(f, 4);
+ if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup);
+ }
+ } else
+ // @SPECIFICATION: this case is missing from the spec
+ for (j=0; j < f->channels; ++j)
+ m->chan[j].mux = 0;
+
+ for (j=0; j < m->submaps; ++j) {
+ get_bits(f,8); // discard
+ m->submap_floor[j] = get_bits(f,8);
+ m->submap_residue[j] = get_bits(f,8);
+ if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup);
+ if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup);
+ }
+ }
+
+ // Modes
+ f->mode_count = get_bits(f, 6)+1;
+ for (i=0; i < f->mode_count; ++i) {
+ Mode *m = f->mode_config+i;
+ m->blockflag = get_bits(f,1);
+ m->windowtype = get_bits(f,16);
+ m->transformtype = get_bits(f,16);
+ m->mapping = get_bits(f,8);
+ if (m->windowtype != 0) return error(f, VORBIS_invalid_setup);
+ if (m->transformtype != 0) return error(f, VORBIS_invalid_setup);
+ if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup);
+ }
+
+ flush_packet(f);
+
+ f->previous_length = 0;
+
+ for (i=0; i < f->channels; ++i) {
+ f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1);
+ f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
+ f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
+ if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
+ memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1);
+ #ifdef STB_VORBIS_NO_DEFER_FLOOR
+ f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
+ if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
+ #endif
+ }
+
+ if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE;
+ if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE;
+ f->blocksize[0] = f->blocksize_0;
+ f->blocksize[1] = f->blocksize_1;
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+ if (integer_divide_table[1][1]==0)
+ for (i=0; i < DIVTAB_NUMER; ++i)
+ for (j=1; j < DIVTAB_DENOM; ++j)
+ integer_divide_table[i][j] = i / j;
+#endif
+
+ // compute how much temporary memory is needed
+
+ // 1.
+ {
+ uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1);
+ uint32 classify_mem;
+ int i,max_part_read=0;
+ for (i=0; i < f->residue_count; ++i) {
+ Residue *r = f->residue_config + i;
+ unsigned int actual_size = f->blocksize_1 / 2;
+ unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
+ unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
+ int n_read = limit_r_end - limit_r_begin;
+ int part_read = n_read / r->part_size;
+ if (part_read > max_part_read)
+ max_part_read = part_read;
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *));
+ #else
+ classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *));
+ #endif
+
+ // maximum reasonable partition size is f->blocksize_1
+
+ f->temp_memory_required = classify_mem;
+ if (imdct_mem > f->temp_memory_required)
+ f->temp_memory_required = imdct_mem;
+ }
+
+
+ if (f->alloc.alloc_buffer) {
+ assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes);
+ // check if there's enough temp memory so we don't error later
+ if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset)
+ return error(f, VORBIS_outofmem);
+ }
+
+ // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page
+ // without PAGEFLAG_continued_packet, so this either points to the first page, or
+ // the page after the end of the headers. It might be cleaner to point to a page
+ // in the middle of the headers, when that's the page where the first audio packet
+ // starts, but we'd have to also correctly skip the end of any continued packet in
+ // stb_vorbis_seek_start.
+ if (f->next_seg == -1) {
+ f->first_audio_page_offset = stb_vorbis_get_file_offset(f);
+ } else {
+ f->first_audio_page_offset = 0;
+ }
+
+ return TRUE;
+}
+
+static void vorbis_deinit(stb_vorbis *p)
+{
+ int i,j;
+ if (p->residue_config) {
+ for (i=0; i < p->residue_count; ++i) {
+ Residue *r = p->residue_config+i;
+ if (r->classdata) {
+ for (j=0; j < p->codebooks[r->classbook].entries; ++j)
+ setup_free(p, r->classdata[j]);
+ setup_free(p, r->classdata);
+ }
+ setup_free(p, r->residue_books);
+ }
+ }
+
+ if (p->codebooks) {
+ CHECK(p);
+ for (i=0; i < p->codebook_count; ++i) {
+ Codebook *c = p->codebooks + i;
+ setup_free(p, c->codeword_lengths);
+ setup_free(p, c->multiplicands);
+ setup_free(p, c->codewords);
+ setup_free(p, c->sorted_codewords);
+ // c->sorted_values[-1] is the first entry in the array
+ setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL);
+ }
+ setup_free(p, p->codebooks);
+ }
+ setup_free(p, p->floor_config);
+ setup_free(p, p->residue_config);
+ if (p->mapping) {
+ for (i=0; i < p->mapping_count; ++i)
+ setup_free(p, p->mapping[i].chan);
+ setup_free(p, p->mapping);
+ }
+ CHECK(p);
+ for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) {
+ setup_free(p, p->channel_buffers[i]);
+ setup_free(p, p->previous_window[i]);
+ #ifdef STB_VORBIS_NO_DEFER_FLOOR
+ setup_free(p, p->floor_buffers[i]);
+ #endif
+ setup_free(p, p->finalY[i]);
+ }
+ for (i=0; i < 2; ++i) {
+ setup_free(p, p->A[i]);
+ setup_free(p, p->B[i]);
+ setup_free(p, p->C[i]);
+ setup_free(p, p->window[i]);
+ setup_free(p, p->bit_reverse[i]);
+ }
+ #ifndef STB_VORBIS_NO_STDIO
+ if (p->close_on_free) fclose(p->f);
+ #endif
+}
+
+void stb_vorbis_close(stb_vorbis *p)
+{
+ if (p == NULL) return;
+ vorbis_deinit(p);
+ setup_free(p,p);
+}
+
+static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z)
+{
+ memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start
+ if (z) {
+ p->alloc = *z;
+ p->alloc.alloc_buffer_length_in_bytes &= ~7;
+ p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
+ }
+ p->eof = 0;
+ p->error = VORBIS__no_error;
+ p->stream = NULL;
+ p->codebooks = NULL;
+ p->page_crc_tests = -1;
+ #ifndef STB_VORBIS_NO_STDIO
+ p->close_on_free = FALSE;
+ p->f = NULL;
+ #endif
+}
+
+int stb_vorbis_get_sample_offset(stb_vorbis *f)
+{
+ if (f->current_loc_valid)
+ return f->current_loc;
+ else
+ return -1;
+}
+
+stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
+{
+ stb_vorbis_info d;
+ d.channels = f->channels;
+ d.sample_rate = f->sample_rate;
+ d.setup_memory_required = f->setup_memory_required;
+ d.setup_temp_memory_required = f->setup_temp_memory_required;
+ d.temp_memory_required = f->temp_memory_required;
+ d.max_frame_size = f->blocksize_1 >> 1;
+ return d;
+}
+
+int stb_vorbis_get_error(stb_vorbis *f)
+{
+ int e = f->error;
+ f->error = VORBIS__no_error;
+ return e;
+}
+
+static stb_vorbis * vorbis_alloc(stb_vorbis *f)
+{
+ stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p));
+ return p;
+}
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+
+void stb_vorbis_flush_pushdata(stb_vorbis *f)
+{
+ f->previous_length = 0;
+ f->page_crc_tests = 0;
+ f->discard_samples_deferred = 0;
+ f->current_loc_valid = FALSE;
+ f->first_decode = FALSE;
+ f->samples_output = 0;
+ f->channel_buffer_start = 0;
+ f->channel_buffer_end = 0;
+}
+
+static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len)
+{
+ int i,n;
+ for (i=0; i < f->page_crc_tests; ++i)
+ f->scan[i].bytes_done = 0;
+
+ // if we have room for more scans, search for them first, because
+ // they may cause us to stop early if their header is incomplete
+ if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) {
+ if (data_len < 4) return 0;
+ data_len -= 3; // need to look for 4-byte sequence, so don't miss
+ // one that straddles a boundary
+ for (i=0; i < data_len; ++i) {
+ if (data[i] == 0x4f) {
+ if (0==memcmp(data+i, ogg_page_header, 4)) {
+ int j,len;
+ uint32 crc;
+ // make sure we have the whole page header
+ if (i+26 >= data_len || i+27+data[i+26] >= data_len) {
+ // only read up to this page start, so hopefully we'll
+ // have the whole page header start next time
+ data_len = i;
+ break;
+ }
+ // ok, we have it all; compute the length of the page
+ len = 27 + data[i+26];
+ for (j=0; j < data[i+26]; ++j)
+ len += data[i+27+j];
+ // scan everything up to the embedded crc (which we must 0)
+ crc = 0;
+ for (j=0; j < 22; ++j)
+ crc = crc32_update(crc, data[i+j]);
+ // now process 4 0-bytes
+ for ( ; j < 26; ++j)
+ crc = crc32_update(crc, 0);
+ // len is the total number of bytes we need to scan
+ n = f->page_crc_tests++;
+ f->scan[n].bytes_left = len-j;
+ f->scan[n].crc_so_far = crc;
+ f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24);
+ // if the last frame on a page is continued to the next, then
+ // we can't recover the sample_loc immediately
+ if (data[i+27+data[i+26]-1] == 255)
+ f->scan[n].sample_loc = ~0;
+ else
+ f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24);
+ f->scan[n].bytes_done = i+j;
+ if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT)
+ break;
+ // keep going if we still have room for more
+ }
+ }
+ }
+ }
+
+ for (i=0; i < f->page_crc_tests;) {
+ uint32 crc;
+ int j;
+ int n = f->scan[i].bytes_done;
+ int m = f->scan[i].bytes_left;
+ if (m > data_len - n) m = data_len - n;
+ // m is the bytes to scan in the current chunk
+ crc = f->scan[i].crc_so_far;
+ for (j=0; j < m; ++j)
+ crc = crc32_update(crc, data[n+j]);
+ f->scan[i].bytes_left -= m;
+ f->scan[i].crc_so_far = crc;
+ if (f->scan[i].bytes_left == 0) {
+ // does it match?
+ if (f->scan[i].crc_so_far == f->scan[i].goal_crc) {
+ // Houston, we have page
+ data_len = n+m; // consumption amount is wherever that scan ended
+ f->page_crc_tests = -1; // drop out of page scan mode
+ f->previous_length = 0; // decode-but-don't-output one frame
+ f->next_seg = -1; // start a new page
+ f->current_loc = f->scan[i].sample_loc; // set the current sample location
+ // to the amount we'd have decoded had we decoded this page
+ f->current_loc_valid = f->current_loc != ~0U;
+ return data_len;
+ }
+ // delete entry
+ f->scan[i] = f->scan[--f->page_crc_tests];
+ } else {
+ ++i;
+ }
+ }
+
+ return data_len;
+}
+
+// return value: number of bytes we used
+int stb_vorbis_decode_frame_pushdata(
+ stb_vorbis *f, // the file we're decoding
+ const uint8 *data, int data_len, // the memory available for decoding
+ int *channels, // place to write number of float * buffers
+ float ***output, // place to write float ** array of float * buffers
+ int *samples // place to write number of output samples
+ )
+{
+ int i;
+ int len,right,left;
+
+ if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+ if (f->page_crc_tests >= 0) {
+ *samples = 0;
+ return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len);
+ }
+
+ f->stream = (uint8 *) data;
+ f->stream_end = (uint8 *) data + data_len;
+ f->error = VORBIS__no_error;
+
+ // check that we have the entire packet in memory
+ if (!is_whole_packet_present(f)) {
+ *samples = 0;
+ return 0;
+ }
+
+ if (!vorbis_decode_packet(f, &len, &left, &right)) {
+ // save the actual error we encountered
+ enum STBVorbisError error = f->error;
+ if (error == VORBIS_bad_packet_type) {
+ // flush and resynch
+ f->error = VORBIS__no_error;
+ while (get8_packet(f) != EOP)
+ if (f->eof) break;
+ *samples = 0;
+ return (int) (f->stream - data);
+ }
+ if (error == VORBIS_continued_packet_flag_invalid) {
+ if (f->previous_length == 0) {
+ // we may be resynching, in which case it's ok to hit one
+ // of these; just discard the packet
+ f->error = VORBIS__no_error;
+ while (get8_packet(f) != EOP)
+ if (f->eof) break;
+ *samples = 0;
+ return (int) (f->stream - data);
+ }
+ }
+ // if we get an error while parsing, what to do?
+ // well, it DEFINITELY won't work to continue from where we are!
+ stb_vorbis_flush_pushdata(f);
+ // restore the error that actually made us bail
+ f->error = error;
+ *samples = 0;
+ return 1;
+ }
+
+ // success!
+ len = vorbis_finish_frame(f, len, left, right);
+ for (i=0; i < f->channels; ++i)
+ f->outputs[i] = f->channel_buffers[i] + left;
+
+ if (channels) *channels = f->channels;
+ *samples = len;
+ *output = f->outputs;
+ return (int) (f->stream - data);
+}
+
+stb_vorbis *stb_vorbis_open_pushdata(
+ const unsigned char *data, int data_len, // the memory available for decoding
+ int *data_used, // only defined if result is not NULL
+ int *error, const stb_vorbis_alloc *alloc)
+{
+ stb_vorbis *f, p;
+ vorbis_init(&p, alloc);
+ p.stream = (uint8 *) data;
+ p.stream_end = (uint8 *) data + data_len;
+ p.push_mode = TRUE;
+ if (!start_decoder(&p)) {
+ if (p.eof)
+ *error = VORBIS_need_more_data;
+ else
+ *error = p.error;
+ return NULL;
+ }
+ f = vorbis_alloc(&p);
+ if (f) {
+ *f = p;
+ *data_used = (int) (f->stream - data);
+ *error = 0;
+ return f;
+ } else {
+ vorbis_deinit(&p);
+ return NULL;
+ }
+}
+#endif // STB_VORBIS_NO_PUSHDATA_API
+
+unsigned int stb_vorbis_get_file_offset(stb_vorbis *f)
+{
+ #ifndef STB_VORBIS_NO_PUSHDATA_API
+ if (f->push_mode) return 0;
+ #endif
+ if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start);
+ #ifndef STB_VORBIS_NO_STDIO
+ return (unsigned int) (ftell(f->f) - f->f_start);
+ #endif
+}
+
+#ifndef STB_VORBIS_NO_PULLDATA_API
+//
+// DATA-PULLING API
+//
+
+static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
+{
+ for(;;) {
+ int n;
+ if (f->eof) return 0;
+ n = get8(f);
+ if (n == 0x4f) { // page header candidate
+ unsigned int retry_loc = stb_vorbis_get_file_offset(f);
+ int i;
+ // check if we're off the end of a file_section stream
+ if (retry_loc - 25 > f->stream_len)
+ return 0;
+ // check the rest of the header
+ for (i=1; i < 4; ++i)
+ if (get8(f) != ogg_page_header[i])
+ break;
+ if (f->eof) return 0;
+ if (i == 4) {
+ uint8 header[27];
+ uint32 i, crc, goal, len;
+ for (i=0; i < 4; ++i)
+ header[i] = ogg_page_header[i];
+ for (; i < 27; ++i)
+ header[i] = get8(f);
+ if (f->eof) return 0;
+ if (header[4] != 0) goto invalid;
+ goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24);
+ for (i=22; i < 26; ++i)
+ header[i] = 0;
+ crc = 0;
+ for (i=0; i < 27; ++i)
+ crc = crc32_update(crc, header[i]);
+ len = 0;
+ for (i=0; i < header[26]; ++i) {
+ int s = get8(f);
+ crc = crc32_update(crc, s);
+ len += s;
+ }
+ if (len && f->eof) return 0;
+ for (i=0; i < len; ++i)
+ crc = crc32_update(crc, get8(f));
+ // finished parsing probable page
+ if (crc == goal) {
+ // we could now check that it's either got the last
+ // page flag set, OR it's followed by the capture
+ // pattern, but I guess TECHNICALLY you could have
+ // a file with garbage between each ogg page and recover
+ // from it automatically? So even though that paranoia
+ // might decrease the chance of an invalid decode by
+ // another 2^32, not worth it since it would hose those
+ // invalid-but-useful files?
+ if (end)
+ *end = stb_vorbis_get_file_offset(f);
+ if (last) {
+ if (header[5] & 0x04)
+ *last = 1;
+ else
+ *last = 0;
+ }
+ set_file_offset(f, retry_loc-1);
+ return 1;
+ }
+ }
+ invalid:
+ // not a valid page, so rewind and look for next one
+ set_file_offset(f, retry_loc);
+ }
+ }
+}
+
+
+#define SAMPLE_unknown 0xffffffff
+
+// seeking is implemented with a binary search, which narrows down the range to
+// 64K, before using a linear search (because finding the synchronization
+// pattern can be expensive, and the chance we'd find the end page again is
+// relatively high for small ranges)
+//
+// two initial interpolation-style probes are used at the start of the search
+// to try to bound either side of the binary search sensibly, while still
+// working in O(log n) time if they fail.
+
+static int get_seek_page_info(stb_vorbis *f, ProbedPage *z)
+{
+ uint8 header[27], lacing[255];
+ int i,len;
+
+ // record where the page starts
+ z->page_start = stb_vorbis_get_file_offset(f);
+
+ // parse the header
+ getn(f, header, 27);
+ if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S')
+ return 0;
+ getn(f, lacing, header[26]);
+
+ // determine the length of the payload
+ len = 0;
+ for (i=0; i < header[26]; ++i)
+ len += lacing[i];
+
+ // this implies where the page ends
+ z->page_end = z->page_start + 27 + header[26] + len;
+
+ // read the last-decoded sample out of the data
+ z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24);
+
+ // restore file state to where we were
+ set_file_offset(f, z->page_start);
+ return 1;
+}
+
+// rarely used function to seek back to the preceding page while finding the
+// start of a packet
+static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset)
+{
+ unsigned int previous_safe, end;
+
+ // now we want to seek back 64K from the limit
+ if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset)
+ previous_safe = limit_offset - 65536;
+ else
+ previous_safe = f->first_audio_page_offset;
+
+ set_file_offset(f, previous_safe);
+
+ while (vorbis_find_page(f, &end, NULL)) {
+ if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset)
+ return 1;
+ set_file_offset(f, end);
+ }
+
+ return 0;
+}
+
+// implements the search logic for finding a page and starting decoding. if
+// the function succeeds, current_loc_valid will be true and current_loc will
+// be less than or equal to the provided sample number (the closer the
+// better).
+static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number)
+{
+ ProbedPage left, right, mid;
+ int i, start_seg_with_known_loc, end_pos, page_start;
+ uint32 delta, stream_length, padding, last_sample_limit;
+ double offset = 0.0, bytes_per_sample = 0.0;
+ int probe = 0;
+
+ // find the last page and validate the target sample
+ stream_length = stb_vorbis_stream_length_in_samples(f);
+ if (stream_length == 0) return error(f, VORBIS_seek_without_length);
+ if (sample_number > stream_length) return error(f, VORBIS_seek_invalid);
+
+ // this is the maximum difference between the window-center (which is the
+ // actual granule position value), and the right-start (which the spec
+ // indicates should be the granule position (give or take one)).
+ padding = ((f->blocksize_1 - f->blocksize_0) >> 2);
+ if (sample_number < padding)
+ last_sample_limit = 0;
+ else
+ last_sample_limit = sample_number - padding;
+
+ left = f->p_first;
+ while (left.last_decoded_sample == ~0U) {
+ // (untested) the first page does not have a 'last_decoded_sample'
+ set_file_offset(f, left.page_end);
+ if (!get_seek_page_info(f, &left)) goto error;
+ }
+
+ right = f->p_last;
+ assert(right.last_decoded_sample != ~0U);
+
+ // starting from the start is handled differently
+ if (last_sample_limit <= left.last_decoded_sample) {
+ if (stb_vorbis_seek_start(f)) {
+ if (f->current_loc > sample_number)
+ return error(f, VORBIS_seek_failed);
+ return 1;
+ }
+ return 0;
+ }
+
+ while (left.page_end != right.page_start) {
+ assert(left.page_end < right.page_start);
+ // search range in bytes
+ delta = right.page_start - left.page_end;
+ if (delta <= 65536) {
+ // there's only 64K left to search - handle it linearly
+ set_file_offset(f, left.page_end);
+ } else {
+ if (probe < 2) {
+ if (probe == 0) {
+ // first probe (interpolate)
+ double data_bytes = right.page_end - left.page_start;
+ bytes_per_sample = data_bytes / right.last_decoded_sample;
+ offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample);
+ } else {
+ // second probe (try to bound the other side)
+ double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample;
+ if (error >= 0 && error < 8000) error = 8000;
+ if (error < 0 && error > -8000) error = -8000;
+ offset += error * 2;
+ }
+
+ // ensure the offset is valid
+ if (offset < left.page_end)
+ offset = left.page_end;
+ if (offset > right.page_start - 65536)
+ offset = right.page_start - 65536;
+
+ set_file_offset(f, (unsigned int) offset);
+ } else {
+ // binary search for large ranges (offset by 32K to ensure
+ // we don't hit the right page)
+ set_file_offset(f, left.page_end + (delta / 2) - 32768);
+ }
+
+ if (!vorbis_find_page(f, NULL, NULL)) goto error;
+ }
+
+ for (;;) {
+ if (!get_seek_page_info(f, &mid)) goto error;
+ if (mid.last_decoded_sample != ~0U) break;
+ // (untested) no frames end on this page
+ set_file_offset(f, mid.page_end);
+ assert(mid.page_start < right.page_start);
+ }
+
+ // if we've just found the last page again then we're in a tricky file,
+ // and we're close enough (if it wasn't an interpolation probe).
+ if (mid.page_start == right.page_start) {
+ if (probe >= 2 || delta <= 65536)
+ break;
+ } else {
+ if (last_sample_limit < mid.last_decoded_sample)
+ right = mid;
+ else
+ left = mid;
+ }
+
+ ++probe;
+ }
+
+ // seek back to start of the last packet
+ page_start = left.page_start;
+ set_file_offset(f, page_start);
+ if (!start_page(f)) return error(f, VORBIS_seek_failed);
+ end_pos = f->end_seg_with_known_loc;
+ assert(end_pos >= 0);
+
+ for (;;) {
+ for (i = end_pos; i > 0; --i)
+ if (f->segments[i-1] != 255)
+ break;
+
+ start_seg_with_known_loc = i;
+
+ if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet))
+ break;
+
+ // (untested) the final packet begins on an earlier page
+ if (!go_to_page_before(f, page_start))
+ goto error;
+
+ page_start = stb_vorbis_get_file_offset(f);
+ if (!start_page(f)) goto error;
+ end_pos = f->segment_count - 1;
+ }
+
+ // prepare to start decoding
+ f->current_loc_valid = FALSE;
+ f->last_seg = FALSE;
+ f->valid_bits = 0;
+ f->packet_bytes = 0;
+ f->bytes_in_seg = 0;
+ f->previous_length = 0;
+ f->next_seg = start_seg_with_known_loc;
+
+ for (i = 0; i < start_seg_with_known_loc; i++)
+ skip(f, f->segments[i]);
+
+ // start decoding (optimizable - this frame is generally discarded)
+ if (!vorbis_pump_first_frame(f))
+ return 0;
+ if (f->current_loc > sample_number)
+ return error(f, VORBIS_seek_failed);
+ return 1;
+
+error:
+ // try to restore the file to a valid state
+ stb_vorbis_seek_start(f);
+ return error(f, VORBIS_seek_failed);
+}
+
+// the same as vorbis_decode_initial, but without advancing
+static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
+{
+ int bits_read, bytes_read;
+
+ if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode))
+ return 0;
+
+ // either 1 or 2 bytes were read, figure out which so we can rewind
+ bits_read = 1 + ilog(f->mode_count-1);
+ if (f->mode_config[*mode].blockflag)
+ bits_read += 2;
+ bytes_read = (bits_read + 7) / 8;
+
+ f->bytes_in_seg += bytes_read;
+ f->packet_bytes -= bytes_read;
+ skip(f, -bytes_read);
+ if (f->next_seg == -1)
+ f->next_seg = f->segment_count - 1;
+ else
+ f->next_seg--;
+ f->valid_bits = 0;
+
+ return 1;
+}
+
+int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number)
+{
+ uint32 max_frame_samples;
+
+ if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+ // fast page-level search
+ if (!seek_to_sample_coarse(f, sample_number))
+ return 0;
+
+ assert(f->current_loc_valid);
+ assert(f->current_loc <= sample_number);
+
+ // linear search for the relevant packet
+ max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2;
+ while (f->current_loc < sample_number) {
+ int left_start, left_end, right_start, right_end, mode, frame_samples;
+ if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode))
+ return error(f, VORBIS_seek_failed);
+ // calculate the number of samples returned by the next frame
+ frame_samples = right_start - left_start;
+ if (f->current_loc + frame_samples > sample_number) {
+ return 1; // the next frame will contain the sample
+ } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) {
+ // there's a chance the frame after this could contain the sample
+ vorbis_pump_first_frame(f);
+ } else {
+ // this frame is too early to be relevant
+ f->current_loc += frame_samples;
+ f->previous_length = 0;
+ maybe_start_packet(f);
+ flush_packet(f);
+ }
+ }
+ // the next frame should start with the sample
+ if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed);
+ return 1;
+}
+
+int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)
+{
+ if (!stb_vorbis_seek_frame(f, sample_number))
+ return 0;
+
+ if (sample_number != f->current_loc) {
+ int n;
+ uint32 frame_start = f->current_loc;
+ stb_vorbis_get_frame_float(f, &n, NULL);
+ assert(sample_number > frame_start);
+ assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end);
+ f->channel_buffer_start += (sample_number - frame_start);
+ }
+
+ return 1;
+}
+
+int stb_vorbis_seek_start(stb_vorbis *f)
+{
+ if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); }
+ set_file_offset(f, f->first_audio_page_offset);
+ f->previous_length = 0;
+ f->first_decode = TRUE;
+ f->next_seg = -1;
+ return vorbis_pump_first_frame(f);
+}
+
+unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
+{
+ unsigned int restore_offset, previous_safe;
+ unsigned int end, last_page_loc;
+
+ if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+ if (!f->total_samples) {
+ unsigned int last;
+ uint32 lo,hi;
+ char header[6];
+
+ // first, store the current decode position so we can restore it
+ restore_offset = stb_vorbis_get_file_offset(f);
+
+ // now we want to seek back 64K from the end (the last page must
+ // be at most a little less than 64K, but let's allow a little slop)
+ if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset)
+ previous_safe = f->stream_len - 65536;
+ else
+ previous_safe = f->first_audio_page_offset;
+
+ set_file_offset(f, previous_safe);
+ // previous_safe is now our candidate 'earliest known place that seeking
+ // to will lead to the final page'
+
+ if (!vorbis_find_page(f, &end, &last)) {
+ // if we can't find a page, we're hosed!
+ f->error = VORBIS_cant_find_last_page;
+ f->total_samples = 0xffffffff;
+ goto done;
+ }
+
+ // check if there are more pages
+ last_page_loc = stb_vorbis_get_file_offset(f);
+
+ // stop when the last_page flag is set, not when we reach eof;
+ // this allows us to stop short of a 'file_section' end without
+ // explicitly checking the length of the section
+ while (!last) {
+ set_file_offset(f, end);
+ if (!vorbis_find_page(f, &end, &last)) {
+ // the last page we found didn't have the 'last page' flag
+ // set. whoops!
+ break;
+ }
+ previous_safe = last_page_loc+1;
+ last_page_loc = stb_vorbis_get_file_offset(f);
+ }
+
+ set_file_offset(f, last_page_loc);
+
+ // parse the header
+ getn(f, (unsigned char *)header, 6);
+ // extract the absolute granule position
+ lo = get32(f);
+ hi = get32(f);
+ if (lo == 0xffffffff && hi == 0xffffffff) {
+ f->error = VORBIS_cant_find_last_page;
+ f->total_samples = SAMPLE_unknown;
+ goto done;
+ }
+ if (hi)
+ lo = 0xfffffffe; // saturate
+ f->total_samples = lo;
+
+ f->p_last.page_start = last_page_loc;
+ f->p_last.page_end = end;
+ f->p_last.last_decoded_sample = lo;
+
+ done:
+ set_file_offset(f, restore_offset);
+ }
+ return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples;
+}
+
+float stb_vorbis_stream_length_in_seconds(stb_vorbis *f)
+{
+ return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate;
+}
+
+
+
+int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output)
+{
+ int len, right,left,i;
+ if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+ if (!vorbis_decode_packet(f, &len, &left, &right)) {
+ f->channel_buffer_start = f->channel_buffer_end = 0;
+ return 0;
+ }
+
+ len = vorbis_finish_frame(f, len, left, right);
+ for (i=0; i < f->channels; ++i)
+ f->outputs[i] = f->channel_buffers[i] + left;
+
+ f->channel_buffer_start = left;
+ f->channel_buffer_end = left+len;
+
+ if (channels) *channels = f->channels;
+ if (output) *output = f->outputs;
+ return len;
+}
+
+#ifndef STB_VORBIS_NO_STDIO
+
+stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length)
+{
+ stb_vorbis *f, p;
+ vorbis_init(&p, alloc);
+ p.f = file;
+ p.f_start = (uint32) ftell(file);
+ p.stream_len = length;
+ p.close_on_free = close_on_free;
+ if (start_decoder(&p)) {
+ f = vorbis_alloc(&p);
+ if (f) {
+ *f = p;
+ vorbis_pump_first_frame(f);
+ return f;
+ }
+ }
+ if (error) *error = p.error;
+ vorbis_deinit(&p);
+ return NULL;
+}
+
+stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc)
+{
+ unsigned int len, start;
+ start = (unsigned int) ftell(file);
+ fseek(file, 0, SEEK_END);
+ len = (unsigned int) (ftell(file) - start);
+ fseek(file, start, SEEK_SET);
+ return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len);
+}
+
+stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc)
+{
+ FILE *f;
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+ if (0 != fopen_s(&f, filename, "rb"))
+ f = NULL;
+#else
+ f = fopen(filename, "rb");
+#endif
+ if (f)
+ return stb_vorbis_open_file(f, TRUE, error, alloc);
+ if (error) *error = VORBIS_file_open_failure;
+ return NULL;
+}
+#endif // STB_VORBIS_NO_STDIO
+
+stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
+{
+ stb_vorbis *f, p;
+ if (data == NULL) return NULL;
+ vorbis_init(&p, alloc);
+ p.stream = (uint8 *) data;
+ p.stream_end = (uint8 *) data + len;
+ p.stream_start = (uint8 *) p.stream;
+ p.stream_len = len;
+ p.push_mode = FALSE;
+ if (start_decoder(&p)) {
+ f = vorbis_alloc(&p);
+ if (f) {
+ *f = p;
+ vorbis_pump_first_frame(f);
+ if (error) *error = VORBIS__no_error;
+ return f;
+ }
+ }
+ if (error) *error = p.error;
+ vorbis_deinit(&p);
+ return NULL;
+}
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+#define PLAYBACK_MONO 1
+#define PLAYBACK_LEFT 2
+#define PLAYBACK_RIGHT 4
+
+#define L (PLAYBACK_LEFT | PLAYBACK_MONO)
+#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO)
+#define R (PLAYBACK_RIGHT | PLAYBACK_MONO)
+
+static int8 channel_position[7][6] =
+{
+ { 0 },
+ { C },
+ { L, R },
+ { L, C, R },
+ { L, R, L, R },
+ { L, C, R, L, R },
+ { L, C, R, L, R, C },
+};
+
+
+#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
+ typedef union {
+ float f;
+ int i;
+ } float_conv;
+ typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4];
+ #define FASTDEF(x) float_conv x
+ // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round
+ #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT))
+ #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22))
+ #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s))
+ #define check_endianness()
+#else
+ #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s))))
+ #define check_endianness()
+ #define FASTDEF(x)
+#endif
+
+static void copy_samples(short *dest, float *src, int len)
+{
+ int i;
+ check_endianness();
+ for (i=0; i < len; ++i) {
+ FASTDEF(temp);
+ int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ dest[i] = v;
+ }
+}
+
+static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len)
+{
+ #define BUFFER_SIZE 32
+ float buffer[BUFFER_SIZE];
+ int i,j,o,n = BUFFER_SIZE;
+ check_endianness();
+ for (o = 0; o < len; o += BUFFER_SIZE) {
+ memset(buffer, 0, sizeof(buffer));
+ if (o + n > len) n = len - o;
+ for (j=0; j < num_c; ++j) {
+ if (channel_position[num_c][j] & mask) {
+ for (i=0; i < n; ++i)
+ buffer[i] += data[j][d_offset+o+i];
+ }
+ }
+ for (i=0; i < n; ++i) {
+ FASTDEF(temp);
+ int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ output[o+i] = v;
+ }
+ }
+}
+
+static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len)
+{
+ #define BUFFER_SIZE 32
+ float buffer[BUFFER_SIZE];
+ int i,j,o,n = BUFFER_SIZE >> 1;
+ // o is the offset in the source data
+ check_endianness();
+ for (o = 0; o < len; o += BUFFER_SIZE >> 1) {
+ // o2 is the offset in the output data
+ int o2 = o << 1;
+ memset(buffer, 0, sizeof(buffer));
+ if (o + n > len) n = len - o;
+ for (j=0; j < num_c; ++j) {
+ int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT);
+ if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) {
+ for (i=0; i < n; ++i) {
+ buffer[i*2+0] += data[j][d_offset+o+i];
+ buffer[i*2+1] += data[j][d_offset+o+i];
+ }
+ } else if (m == PLAYBACK_LEFT) {
+ for (i=0; i < n; ++i) {
+ buffer[i*2+0] += data[j][d_offset+o+i];
+ }
+ } else if (m == PLAYBACK_RIGHT) {
+ for (i=0; i < n; ++i) {
+ buffer[i*2+1] += data[j][d_offset+o+i];
+ }
+ }
+ }
+ for (i=0; i < (n<<1); ++i) {
+ FASTDEF(temp);
+ int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ output[o2+i] = v;
+ }
+ }
+}
+
+static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples)
+{
+ int i;
+ if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
+ static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} };
+ for (i=0; i < buf_c; ++i)
+ compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples);
+ } else {
+ int limit = buf_c < data_c ? buf_c : data_c;
+ for (i=0; i < limit; ++i)
+ copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples);
+ for ( ; i < buf_c; ++i)
+ memset(buffer[i]+b_offset, 0, sizeof(short) * samples);
+ }
+}
+
+int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples)
+{
+ float **output = NULL;
+ int len = stb_vorbis_get_frame_float(f, NULL, &output);
+ if (len > num_samples) len = num_samples;
+ if (len)
+ convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len);
+ return len;
+}
+
+static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len)
+{
+ int i;
+ check_endianness();
+ if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
+ assert(buf_c == 2);
+ for (i=0; i < buf_c; ++i)
+ compute_stereo_samples(buffer, data_c, data, d_offset, len);
+ } else {
+ int limit = buf_c < data_c ? buf_c : data_c;
+ int j;
+ for (j=0; j < len; ++j) {
+ for (i=0; i < limit; ++i) {
+ FASTDEF(temp);
+ float f = data[i][d_offset+j];
+ int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ *buffer++ = v;
+ }
+ for ( ; i < buf_c; ++i)
+ *buffer++ = 0;
+ }
+ }
+}
+
+int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts)
+{
+ float **output;
+ int len;
+ if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts);
+ len = stb_vorbis_get_frame_float(f, NULL, &output);
+ if (len) {
+ if (len*num_c > num_shorts) len = num_shorts / num_c;
+ convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len);
+ }
+ return len;
+}
+
+int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts)
+{
+ float **outputs;
+ int len = num_shorts / channels;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < len) {
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= len) k = len - n;
+ if (k)
+ convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k);
+ buffer += k*channels;
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == len) break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
+ }
+ return n;
+}
+
+int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len)
+{
+ float **outputs;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < len) {
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= len) k = len - n;
+ if (k)
+ convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k);
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == len) break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
+ }
+ return n;
+}
+
+#ifndef STB_VORBIS_NO_STDIO
+int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output)
+{
+ int data_len, offset, total, limit, error;
+ short *data;
+ stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
+ if (v == NULL) return -1;
+ limit = v->channels * 4096;
+ *channels = v->channels;
+ if (sample_rate)
+ *sample_rate = v->sample_rate;
+ offset = data_len = 0;
+ total = limit;
+ data = (short *) malloc(total * sizeof(*data));
+ if (data == NULL) {
+ stb_vorbis_close(v);
+ return -2;
+ }
+ for (;;) {
+ int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset);
+ if (n == 0) break;
+ data_len += n;
+ offset += n * v->channels;
+ if (offset + limit > total) {
+ short *data2;
+ total *= 2;
+ data2 = (short *) realloc(data, total * sizeof(*data));
+ if (data2 == NULL) {
+ free(data);
+ stb_vorbis_close(v);
+ return -2;
+ }
+ data = data2;
+ }
+ }
+ *output = data;
+ stb_vorbis_close(v);
+ return data_len;
+}
+#endif // NO_STDIO
+
+int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output)
+{
+ int data_len, offset, total, limit, error;
+ short *data;
+ stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL);
+ if (v == NULL) return -1;
+ limit = v->channels * 4096;
+ *channels = v->channels;
+ if (sample_rate)
+ *sample_rate = v->sample_rate;
+ offset = data_len = 0;
+ total = limit;
+ data = (short *) malloc(total * sizeof(*data));
+ if (data == NULL) {
+ stb_vorbis_close(v);
+ return -2;
+ }
+ for (;;) {
+ int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset);
+ if (n == 0) break;
+ data_len += n;
+ offset += n * v->channels;
+ if (offset + limit > total) {
+ short *data2;
+ total *= 2;
+ data2 = (short *) realloc(data, total * sizeof(*data));
+ if (data2 == NULL) {
+ free(data);
+ stb_vorbis_close(v);
+ return -2;
+ }
+ data = data2;
+ }
+ }
+ *output = data;
+ stb_vorbis_close(v);
+ return data_len;
+}
+#endif // STB_VORBIS_NO_INTEGER_CONVERSION
+
+int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats)
+{
+ float **outputs;
+ int len = num_floats / channels;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < len) {
+ int i,j;
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= len) k = len - n;
+ for (j=0; j < k; ++j) {
+ for (i=0; i < z; ++i)
+ *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j];
+ for ( ; i < channels; ++i)
+ *buffer++ = 0;
+ }
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == len)
+ break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
+ break;
+ }
+ return n;
+}
+
+int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples)
+{
+ float **outputs;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < num_samples) {
+ int i;
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= num_samples) k = num_samples - n;
+ if (k) {
+ for (i=0; i < z; ++i)
+ memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k);
+ for ( ; i < channels; ++i)
+ memset(buffer[i]+n, 0, sizeof(float) * k);
+ }
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == num_samples)
+ break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
+ break;
+ }
+ return n;
+}
+#endif // STB_VORBIS_NO_PULLDATA_API
+
+/* Version history
+ 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223
+ found with Mayhem by ForAllSecure
+ 1.16 - 2019-03-04 - fix warnings
+ 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
+ 1.14 - 2018-02-11 - delete bogus dealloca usage
+ 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
+ 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+ 1.11 - 2017-07-23 - fix MinGW compilation
+ 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+ 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version
+ 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks;
+ avoid discarding last frame of audio data
+ 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API
+ some more crash fixes when out of memory or with corrupt files
+ 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
+ some crash fixes when out of memory or with corrupt files
+ 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
+ 1.04 - 2014-08-27 - fix missing const-correct case in API
+ 1.03 - 2014-08-07 - Warning fixes
+ 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows
+ 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float
+ 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel
+ (API change) report sample rate for decode-full-file funcs
+ 0.99996 - bracket #include for macintosh compilation by Laurent Gomila
+ 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem
+ 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence
+ 0.99993 - remove assert that fired on legal files with empty tables
+ 0.99992 - rewind-to-start
+ 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo
+ 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++
+ 0.9998 - add a full-decode function with a memory source
+ 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition
+ 0.9996 - query length of vorbis stream in samples/seconds
+ 0.9995 - bugfix to another optimization that only happened in certain files
+ 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors
+ 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation
+ 0.9992 - performance improvement of IMDCT; now performs close to reference implementation
+ 0.9991 - performance improvement of IMDCT
+ 0.999 - (should have been 0.9990) performance improvement of IMDCT
+ 0.998 - no-CRT support from Casey Muratori
+ 0.997 - bugfixes for bugs found by Terje Mathisen
+ 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen
+ 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen
+ 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen
+ 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen
+ 0.992 - fixes for MinGW warning
+ 0.991 - turn fast-float-conversion on by default
+ 0.990 - fix push-mode seek recovery if you seek into the headers
+ 0.98b - fix to bad release of 0.98
+ 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode
+ 0.97 - builds under c++ (typecasting, don't use 'class' keyword)
+ 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code
+ 0.95 - clamping code for 16-bit functions
+ 0.94 - not publically released
+ 0.93 - fixed all-zero-floor case (was decoding garbage)
+ 0.92 - fixed a memory leak
+ 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION
+ 0.90 - first public release
+*/
+
+#endif // STB_VORBIS_HEADER_ONLY
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/Examples/Tutorial1_2DSound/src/Tutorial1_2DSound.cbp b/Examples/Tutorial1_2DSound/src/Tutorial1_2DSound.cbp
new file mode 100644
index 0000000..f1bbc93
--- /dev/null
+++ b/Examples/Tutorial1_2DSound/src/Tutorial1_2DSound.cbp
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Tutorial2_3DSound/src/Tutorial2_3DSound.cbp b/Examples/Tutorial2_3DSound/src/Tutorial2_3DSound.cbp
new file mode 100644
index 0000000..273d39c
--- /dev/null
+++ b/Examples/Tutorial2_3DSound/src/Tutorial2_3DSound.cbp
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cAudio/Headers/cAudioBuildSettings.h b/cAudio/Headers/cAudioBuildSettings.h
new file mode 100644
index 0000000..77bf979
--- /dev/null
+++ b/cAudio/Headers/cAudioBuildSettings.h
@@ -0,0 +1,41 @@
+#pragma once
+
+// CMake auto-generated configuration options
+
+#define CAUDIO_STATIC_LIB 0
+
+//! This define controls whether the Ogg/Vorbis decoder is compiled into the library.
+#define CAUDIO_COMPILE_WITH_OGG_DECODER 1
+
+//! This define controls whether the RIFF/Wav decoder is compiled into the library.
+#define CAUDIO_COMPILE_WITH_WAV_DECODER 1
+
+//! This define controls whether the default filesystem data source is compiled into the library
+#define CAUDIO_COMPILE_WITH_FILE_SOURCE 1
+
+//! This define controls whether the default file logger (html) is compiled into the library
+#define CAUDIO_COMPILE_WITH_FILE_LOG_RECEIVER 1
+
+//! This define controls whether the default console logger is compiled into the library
+#define CAUDIO_COMPILE_WITH_CONSOLE_LOG_RECEIVER 1
+
+//! Define for making the entire library Thread Safe, comment out to disable. Will also disable internal threading by the library.
+#define CAUDIO_MAKE_THREAD_SAFE 1
+
+//! EFX support
+#define CAUDIO_EFX_ENABLED 1
+
+//! Tells cAudio to use the C standard memory functions for allocations (memalloc and free)
+#define CAUDIO_MEMORY_USE_STD 1
+
+//! Tells cAudio to reroute memory allocations from stl containers into the defined memory provider for cAudio, otherwise the standard std::allocator is used.
+#define CAUDIO_REROUTE_STL_ALLOCATIONS 1
+
+//! Activates the internal memory tracker, which can be used to detect and locate memory leaks.
+#define CAUDIO_USE_MEMORYTRACKER 0
+
+//! Tells the memory tracker to generate statistics on memory usage by cAudio
+#define CAUDIO_MEMORYTRACKER_GENERATE_STATISTICS 0
+
+//! Tells the memory tracker to log each and every allocation done by cAudio. This can be very slow, use only if you are debugging an issue.
+#define CAUDIO_MEMORYTRACKER_LOG_ALL_ALLOCATIONS 1
\ No newline at end of file
diff --git a/cAudio/cAudio.cbp b/cAudio/cAudio.cbp
new file mode 100644
index 0000000..076bf42
--- /dev/null
+++ b/cAudio/cAudio.cbp
@@ -0,0 +1,306 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cAudioWorkSpace.workspace b/cAudioWorkSpace.workspace
new file mode 100755
index 0000000..d9126ef
--- /dev/null
+++ b/cAudioWorkSpace.workspace
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+