aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/midi
diff options
context:
space:
mode:
Diffstat (limited to 'Alc/midi')
-rw-r--r--Alc/midi/base.c278
-rw-r--r--Alc/midi/base.h128
-rw-r--r--Alc/midi/dummy.c95
-rw-r--r--Alc/midi/fluidsynth.c843
-rw-r--r--Alc/midi/sf2load.c1436
5 files changed, 2780 insertions, 0 deletions
diff --git a/Alc/midi/base.c b/Alc/midi/base.c
new file mode 100644
index 00000000..25dd19d9
--- /dev/null
+++ b/Alc/midi/base.c
@@ -0,0 +1,278 @@
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "midi/base.h"
+
+#include "alMidi.h"
+#include "alMain.h"
+#include "alError.h"
+#include "alThunk.h"
+#include "evtqueue.h"
+#include "rwlock.h"
+#include "alu.h"
+
+
+/* Microsecond resolution */
+#define TICKS_PER_SECOND (1000000)
+
+/* MIDI events */
+#define SYSEX_EVENT (0xF0)
+
+
+void InitEvtQueue(EvtQueue *queue)
+{
+ queue->events = NULL;
+ queue->maxsize = 0;
+ queue->size = 0;
+ queue->pos = 0;
+}
+
+void ResetEvtQueue(EvtQueue *queue)
+{
+ ALsizei i;
+ for(i = 0;i < queue->size;i++)
+ {
+ if(queue->events[i].event == SYSEX_EVENT)
+ {
+ free(queue->events[i].param.sysex.data);
+ queue->events[i].param.sysex.data = NULL;
+ }
+ }
+
+ free(queue->events);
+ queue->events = NULL;
+ queue->maxsize = 0;
+ queue->size = 0;
+ queue->pos = 0;
+}
+
+ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt)
+{
+ ALsizei pos;
+
+ if(queue->maxsize == queue->size)
+ {
+ if(queue->pos > 0)
+ {
+ /* Queue has some stale entries, remove them to make space for more
+ * events. */
+ for(pos = 0;pos < queue->pos;pos++)
+ {
+ if(queue->events[pos].event == SYSEX_EVENT)
+ {
+ free(queue->events[pos].param.sysex.data);
+ queue->events[pos].param.sysex.data = NULL;
+ }
+ }
+ memmove(&queue->events[0], &queue->events[queue->pos],
+ (queue->size-queue->pos)*sizeof(queue->events[0]));
+ queue->size -= queue->pos;
+ queue->pos = 0;
+ }
+ else
+ {
+ /* Queue is full, double the allocated space. */
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (queue->maxsize ? (queue->maxsize<<1) : 16);
+ if(newsize > queue->maxsize)
+ temp = realloc(queue->events, newsize * sizeof(queue->events[0]));
+ if(!temp)
+ return AL_OUT_OF_MEMORY;
+
+ queue->events = temp;
+ queue->maxsize = newsize;
+ }
+ }
+
+ pos = queue->pos;
+ if(queue->size > 0)
+ {
+ ALsizei high = queue->size - 1;
+ while(pos < high)
+ {
+ ALsizei mid = pos + (high-pos)/2;
+ if(queue->events[mid].time < evt->time)
+ pos = mid + 1;
+ else
+ high = mid;
+ }
+ while(pos < queue->size && queue->events[pos].time <= evt->time)
+ pos++;
+
+ if(pos < queue->size)
+ memmove(&queue->events[pos+1], &queue->events[pos],
+ (queue->size-pos)*sizeof(queue->events[0]));
+ }
+
+ queue->events[pos] = *evt;
+ queue->size++;
+
+ return AL_NO_ERROR;
+}
+
+
+void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
+{
+ InitEvtQueue(&self->EventQueue);
+
+ RWLockInit(&self->Lock);
+
+ self->Soundfonts = NULL;
+ self->NumSoundfonts = 0;
+
+ self->Gain = 1.0f;
+ self->State = AL_INITIAL;
+
+ self->LastEvtTime = 0;
+ self->NextEvtTime = UINT64_MAX;
+ self->SamplesSinceLast = 0.0;
+ self->SamplesToNext = 0.0;
+
+ self->SamplesPerTick = (ALdouble)device->Frequency / TICKS_PER_SECOND;
+}
+
+void MidiSynth_Destruct(MidiSynth *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumSoundfonts;i++)
+ DecrementRef(&self->Soundfonts[i]->ref);
+ free(self->Soundfonts);
+ self->Soundfonts = NULL;
+ self->NumSoundfonts = 0;
+
+ ResetEvtQueue(&self->EventQueue);
+}
+
+
+ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
+{
+ ALCdevice *device = context->Device;
+ ALsoundfont **sfonts;
+ ALsizei i;
+
+ if(self->State != AL_INITIAL && self->State != AL_STOPPED)
+ return AL_INVALID_OPERATION;
+
+ sfonts = calloc(1, count * sizeof(sfonts[0]));
+ if(!sfonts) return AL_OUT_OF_MEMORY;
+
+ for(i = 0;i < count;i++)
+ {
+ if(ids[i] == 0)
+ sfonts[i] = ALsoundfont_getDefSoundfont(context);
+ else if(!(sfonts[i]=LookupSfont(device, ids[i])))
+ {
+ free(sfonts);
+ return AL_INVALID_VALUE;
+ }
+ }
+
+ for(i = 0;i < count;i++)
+ IncrementRef(&sfonts[i]->ref);
+ sfonts = ExchangePtr((XchgPtr*)&self->Soundfonts, sfonts);
+ count = ExchangeInt(&self->NumSoundfonts, count);
+
+ for(i = 0;i < count;i++)
+ DecrementRef(&sfonts[i]->ref);
+ free(sfonts);
+
+ return AL_NO_ERROR;
+}
+
+extern inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain);
+extern inline ALfloat MidiSynth_getGain(const MidiSynth *self);
+extern inline void MidiSynth_setState(MidiSynth *self, ALenum state);
+extern inline ALenum MidiSynth_getState(const MidiSynth *self);
+
+void MidiSynth_stop(MidiSynth *self)
+{
+ ResetEvtQueue(&self->EventQueue);
+
+ self->LastEvtTime = 0;
+ self->NextEvtTime = UINT64_MAX;
+ self->SamplesSinceLast = 0.0;
+ self->SamplesToNext = 0.0;
+}
+
+extern inline void MidiSynth_reset(MidiSynth *self);
+
+ALuint64 MidiSynth_getTime(const MidiSynth *self)
+{
+ ALuint64 time = self->LastEvtTime + (self->SamplesSinceLast/self->SamplesPerTick);
+ return clampu64(time, self->LastEvtTime, self->NextEvtTime);
+}
+
+extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
+
+void MidiSynth_setSampleRate(MidiSynth *self, ALdouble srate)
+{
+ ALdouble sampletickrate = srate / TICKS_PER_SECOND;
+
+ self->SamplesSinceLast = self->SamplesSinceLast * sampletickrate / self->SamplesPerTick;
+ self->SamplesToNext = self->SamplesToNext * sampletickrate / self->SamplesPerTick;
+ self->SamplesPerTick = sampletickrate;
+}
+
+extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
+
+ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
+{
+ MidiEvent entry;
+ ALenum err;
+
+ entry.time = time;
+ entry.event = event;
+ entry.param.val[0] = param1;
+ entry.param.val[1] = param2;
+
+ err = InsertEvtQueue(&self->EventQueue, &entry);
+ if(err != AL_NO_ERROR) return err;
+
+ if(entry.time < self->NextEvtTime)
+ {
+ self->NextEvtTime = entry.time;
+
+ self->SamplesToNext = (self->NextEvtTime - self->LastEvtTime) * self->SamplesPerTick;
+ self->SamplesToNext -= self->SamplesSinceLast;
+ }
+
+ return AL_NO_ERROR;
+}
+
+ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
+{
+ MidiEvent entry;
+ ALenum err;
+
+ entry.time = time;
+ entry.event = SYSEX_EVENT;
+ entry.param.sysex.size = size;
+ entry.param.sysex.data = malloc(size);
+ if(!entry.param.sysex.data)
+ return AL_OUT_OF_MEMORY;
+ memcpy(entry.param.sysex.data, data, size);
+
+ err = InsertEvtQueue(&self->EventQueue, &entry);
+ if(err != AL_NO_ERROR)
+ {
+ free(entry.param.sysex.data);
+ return err;
+ }
+
+ if(entry.time < self->NextEvtTime)
+ {
+ self->NextEvtTime = entry.time;
+
+ self->SamplesToNext = (self->NextEvtTime - self->LastEvtTime) * self->SamplesPerTick;
+ self->SamplesToNext -= self->SamplesSinceLast;
+ }
+
+ return AL_NO_ERROR;
+}
diff --git a/Alc/midi/base.h b/Alc/midi/base.h
new file mode 100644
index 00000000..f900c941
--- /dev/null
+++ b/Alc/midi/base.h
@@ -0,0 +1,128 @@
+#ifndef AL_MIDI_BASE_H
+#define AL_MIDI_BASE_H
+
+#include "alMain.h"
+#include "atomic.h"
+#include "evtqueue.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALsoundfont;
+
+typedef size_t (*ReaderCb)(void *ptr, size_t size, void *stream);
+typedef struct Reader {
+ ReaderCb cb;
+ void *ptr;
+ int error;
+} Reader;
+#define READ(x_, buf_, len_) ((x_)->cb((buf_), (len_), (x_)->ptr))
+#define READERR(x_) ((x_)->error)
+
+ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
+
+
+struct MidiSynthVtable;
+
+typedef struct MidiSynth {
+ EvtQueue EventQueue;
+
+ ALuint64 LastEvtTime;
+ ALuint64 NextEvtTime;
+ ALdouble SamplesSinceLast;
+ ALdouble SamplesToNext;
+
+ ALdouble SamplesPerTick;
+
+ /* NOTE: This rwlock is for the state and soundfont. The EventQueue and
+ * related must instead use the device lock as they're used in the mixer
+ * thread.
+ */
+ RWLock Lock;
+
+ struct ALsoundfont **Soundfonts;
+ ALsizei NumSoundfonts;
+
+ volatile ALfloat Gain;
+ volatile ALenum State;
+
+ const struct MidiSynthVtable *vtbl;
+} MidiSynth;
+
+void MidiSynth_Construct(MidiSynth *self, ALCdevice *device);
+void MidiSynth_Destruct(MidiSynth *self);
+ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
+inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain) { self->Gain = gain; }
+inline ALfloat MidiSynth_getGain(const MidiSynth *self) { return self->Gain; }
+inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&self->State, state); }
+inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; }
+void MidiSynth_stop(MidiSynth *self);
+inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); }
+ALuint64 MidiSynth_getTime(const MidiSynth *self);
+inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self)
+{
+ if(self->EventQueue.pos == self->EventQueue.size)
+ return UINT64_MAX;
+ return self->EventQueue.events[self->EventQueue.pos].time;
+}
+void MidiSynth_setSampleRate(MidiSynth *self, ALdouble srate);
+inline void MidiSynth_update(MidiSynth *self, ALCdevice *device)
+{ MidiSynth_setSampleRate(self, device->Frequency); }
+ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
+ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size);
+
+
+struct MidiSynthVtable {
+ void (*const Destruct)(MidiSynth *self);
+
+ ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
+
+ void (*const setGain)(MidiSynth *self, ALfloat gain);
+ void (*const setState)(MidiSynth *self, ALenum state);
+
+ void (*const stop)(MidiSynth *self);
+ void (*const reset)(MidiSynth *self);
+
+ void (*const update)(MidiSynth *self, ALCdevice *device);
+ void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
+
+ void (*const Delete)(MidiSynth *self);
+};
+
+#define DEFINE_MIDISYNTH_VTABLE(T) \
+DECLARE_THUNK(T, MidiSynth, void, Destruct) \
+DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \
+DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \
+DECLARE_THUNK1(T, MidiSynth, void, setState, ALenum) \
+DECLARE_THUNK(T, MidiSynth, void, stop) \
+DECLARE_THUNK(T, MidiSynth, void, reset) \
+DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \
+DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \
+DECLARE_THUNK(T, MidiSynth, void, Delete) \
+ \
+static const struct MidiSynthVtable T##_MidiSynth_vtable = { \
+ T##_MidiSynth_Destruct, \
+ \
+ T##_MidiSynth_selectSoundfonts, \
+ T##_MidiSynth_setGain, \
+ T##_MidiSynth_setState, \
+ T##_MidiSynth_stop, \
+ T##_MidiSynth_reset, \
+ T##_MidiSynth_update, \
+ T##_MidiSynth_process, \
+ \
+ T##_MidiSynth_Delete, \
+}
+
+
+MidiSynth *FSynth_create(ALCdevice *device);
+MidiSynth *DSynth_create(ALCdevice *device);
+
+MidiSynth *SynthCreate(ALCdevice *device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AL_MIDI_BASE_H */
diff --git a/Alc/midi/dummy.c b/Alc/midi/dummy.c
new file mode 100644
index 00000000..71c03efb
--- /dev/null
+++ b/Alc/midi/dummy.c
@@ -0,0 +1,95 @@
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "alMain.h"
+#include "alError.h"
+#include "evtqueue.h"
+#include "rwlock.h"
+#include "alu.h"
+
+#include "midi/base.h"
+
+typedef struct DSynth {
+ DERIVE_FROM_TYPE(MidiSynth);
+} DSynth;
+
+static void DSynth_Construct(DSynth *self, ALCdevice *device);
+static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct)
+static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
+static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat)
+static DECLARE_FORWARD1(DSynth, MidiSynth, void, setState, ALenum)
+static DECLARE_FORWARD(DSynth, MidiSynth, void, stop)
+static DECLARE_FORWARD(DSynth, MidiSynth, void, reset)
+static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*)
+static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
+static void DSynth_Delete(DSynth *self);
+DEFINE_MIDISYNTH_VTABLE(DSynth);
+
+
+static void DSynth_Construct(DSynth *self, ALCdevice *device)
+{
+ MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
+ SET_VTABLE2(DSynth, MidiSynth, self);
+}
+
+
+static void DSynth_processQueue(DSynth *self, ALuint64 time)
+{
+ EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
+
+ while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
+ queue->pos++;
+}
+
+static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
+{
+ MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+
+ if(synth->State != AL_PLAYING)
+ return;
+
+ synth->SamplesSinceLast += SamplesToDo;
+ synth->SamplesToNext -= SamplesToDo;
+ while(synth->SamplesToNext < 1.0f)
+ {
+ ALuint64 time = synth->NextEvtTime;
+ if(time == UINT64_MAX)
+ {
+ synth->SamplesToNext = 0.0;
+ break;
+ }
+
+ synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
+ synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
+ synth->LastEvtTime = time;
+ DSynth_processQueue(self, time);
+
+ synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
+ if(synth->NextEvtTime != UINT64_MAX)
+ synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ }
+}
+
+
+static void DSynth_Delete(DSynth *self)
+{
+ free(self);
+}
+
+
+MidiSynth *DSynth_create(ALCdevice *device)
+{
+ DSynth *synth = calloc(1, sizeof(*synth));
+ if(!synth)
+ {
+ ERR("Failed to allocate DSynth\n");
+ return NULL;
+ }
+ DSynth_Construct(synth, device);
+ return STATIC_CAST(MidiSynth, synth);
+}
diff --git a/Alc/midi/fluidsynth.c b/Alc/midi/fluidsynth.c
new file mode 100644
index 00000000..9d58f87b
--- /dev/null
+++ b/Alc/midi/fluidsynth.c
@@ -0,0 +1,843 @@
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "midi/base.h"
+
+#include "alMain.h"
+#include "alError.h"
+#include "alMidi.h"
+#include "evtqueue.h"
+#include "rwlock.h"
+#include "alu.h"
+
+#ifdef HAVE_FLUIDSYNTH
+
+#include <fluidsynth.h>
+
+
+/* MIDI events */
+#define SYSEX_EVENT (0xF0)
+
+/* MIDI controllers */
+#define CTRL_BANKSELECT_MSB (0)
+#define CTRL_BANKSELECT_LSB (32)
+#define CTRL_ALLNOTESOFF (123)
+
+
+static int getGenInput(ALenum input)
+{
+ switch(input)
+ {
+ case AL_ONE_SOFT: return FLUID_MOD_NONE;
+ case AL_NOTEON_VELOCITY_SOFT: return FLUID_MOD_VELOCITY;
+ case AL_NOTEON_KEY_SOFT: return FLUID_MOD_KEY;
+ case AL_KEYPRESSURE_SOFT: return FLUID_MOD_KEYPRESSURE;
+ case AL_CHANNELPRESSURE_SOFT: return FLUID_MOD_CHANNELPRESSURE;
+ case AL_PITCHBEND_SOFT: return FLUID_MOD_PITCHWHEEL;
+ case AL_PITCHBEND_SENSITIVITY_SOFT: return FLUID_MOD_PITCHWHEELSENS;
+ }
+ return input&0x7F;
+}
+
+static int getGenFlags(ALenum input, ALenum type, ALenum form)
+{
+ int ret = 0;
+
+ switch(type)
+ {
+ case AL_UNORM_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE; break;
+ case AL_UNORM_REV_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE; break;
+ case AL_SNORM_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE; break;
+ case AL_SNORM_REV_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE; break;
+ }
+ switch(form)
+ {
+ case AL_LINEAR_SOFT: ret |= FLUID_MOD_LINEAR; break;
+ case AL_CONCAVE_SOFT: ret |= FLUID_MOD_CONCAVE; break;
+ case AL_CONVEX_SOFT: ret |= FLUID_MOD_CONVEX; break;
+ case AL_SWITCH_SOFT: ret |= FLUID_MOD_SWITCH; break;
+ }
+ /* Source input values less than 128 correspond to a MIDI continuous
+ * controller. Otherwise, it's a general controller. */
+ if(input < 128) ret |= FLUID_MOD_CC;
+ else ret |= FLUID_MOD_GC;
+
+ return ret;
+}
+
+static enum fluid_gen_type getSf2Gen(ALenum gen)
+{
+ switch(gen)
+ {
+ case AL_MOD_LFO_TO_PITCH_SOFT: return GEN_MODLFOTOPITCH;
+ case AL_VIBRATO_LFO_TO_PITCH_SOFT: return GEN_VIBLFOTOPITCH;
+ case AL_MOD_ENV_TO_PITCH_SOFT: return GEN_MODENVTOPITCH;
+ case AL_FILTER_CUTOFF_SOFT: return GEN_FILTERFC;
+ case AL_FILTER_RESONANCE_SOFT: return GEN_FILTERQ;
+ case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: return GEN_MODLFOTOFILTERFC;
+ case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: return GEN_MODENVTOFILTERFC;
+ case AL_MOD_LFO_TO_VOLUME_SOFT: return GEN_MODLFOTOVOL;
+ case AL_CHORUS_SEND_SOFT: return GEN_CHORUSSEND;
+ case AL_REVERB_SEND_SOFT: return GEN_REVERBSEND;
+ case AL_PAN_SOFT: return GEN_PAN;
+ case AL_MOD_LFO_DELAY_SOFT: return GEN_MODLFODELAY;
+ case AL_MOD_LFO_FREQUENCY_SOFT: return GEN_MODLFOFREQ;
+ case AL_VIBRATO_LFO_DELAY_SOFT: return GEN_VIBLFODELAY;
+ case AL_VIBRATO_LFO_FREQUENCY_SOFT: return GEN_VIBLFOFREQ;
+ case AL_MOD_ENV_DELAYTIME_SOFT: return GEN_MODENVDELAY;
+ case AL_MOD_ENV_ATTACKTIME_SOFT: return GEN_MODENVATTACK;
+ case AL_MOD_ENV_HOLDTIME_SOFT: return GEN_MODENVHOLD;
+ case AL_MOD_ENV_DECAYTIME_SOFT: return GEN_MODENVDECAY;
+ case AL_MOD_ENV_SUSTAINVOLUME_SOFT: return GEN_MODENVSUSTAIN;
+ case AL_MOD_ENV_RELEASETIME_SOFT: return GEN_MODENVRELEASE;
+ case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOMODENVHOLD;
+ case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOMODENVDECAY;
+ case AL_VOLUME_ENV_DELAYTIME_SOFT: return GEN_VOLENVDELAY;
+ case AL_VOLUME_ENV_ATTACKTIME_SOFT: return GEN_VOLENVATTACK;
+ case AL_VOLUME_ENV_HOLDTIME_SOFT: return GEN_VOLENVHOLD;
+ case AL_VOLUME_ENV_DECAYTIME_SOFT: return GEN_VOLENVDECAY;
+ case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: return GEN_VOLENVSUSTAIN;
+ case AL_VOLUME_ENV_RELEASETIME_SOFT: return GEN_VOLENVRELEASE;
+ case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOVOLENVHOLD;
+ case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOVOLENVDECAY;
+ case AL_ATTENUATION_SOFT: return GEN_ATTENUATION;
+ case AL_TUNING_COARSE_SOFT: return GEN_COARSETUNE;
+ case AL_TUNING_FINE_SOFT: return GEN_FINETUNE;
+ case AL_TUNING_SCALE_SOFT: return GEN_SCALETUNE;
+ }
+ ERR("Unhandled generator: 0x%04x\n", gen);
+ return 0;
+}
+
+static int getSf2LoopMode(ALenum mode)
+{
+ switch(mode)
+ {
+ case AL_NONE: return 0;
+ case AL_LOOP_CONTINUOUS_SOFT: return 1;
+ case AL_LOOP_UNTIL_RELEASE_SOFT: return 3;
+ }
+ return 0;
+}
+
+static int getSampleType(ALenum type)
+{
+ switch(type)
+ {
+ case AL_MONO_SOFT: return FLUID_SAMPLETYPE_MONO;
+ case AL_RIGHT_SOFT: return FLUID_SAMPLETYPE_RIGHT;
+ case AL_LEFT_SOFT: return FLUID_SAMPLETYPE_LEFT;
+ }
+ return FLUID_SAMPLETYPE_MONO;
+}
+
+typedef struct FSample {
+ DERIVE_FROM_TYPE(fluid_sample_t);
+
+ ALfontsound *Sound;
+
+ fluid_mod_t *Mods;
+ ALsizei NumMods;
+} FSample;
+
+static void FSample_Construct(FSample *self, ALfontsound *sound, ALsoundfont *sfont)
+{
+ fluid_sample_t *sample = STATIC_CAST(fluid_sample_t, self);
+ memset(sample->name, 0, sizeof(sample->name));
+ sample->start = sound->Start;
+ sample->end = sound->End;
+ sample->loopstart = sound->LoopStart;
+ sample->loopend = sound->LoopEnd;
+ sample->samplerate = sound->SampleRate;
+ sample->origpitch = sound->PitchKey;
+ sample->pitchadj = sound->PitchCorrection;
+ sample->sampletype = getSampleType(sound->SampleType);
+ sample->valid = 1;
+ sample->data = sfont->Samples;
+
+ sample->amplitude_that_reaches_noise_floor_is_valid = 0;
+ sample->amplitude_that_reaches_noise_floor = 0.0;
+
+ sample->refcount = 0;
+
+ sample->notify = NULL;
+
+ sample->userdata = self;
+
+ self->Sound = sound;
+
+ self->NumMods = 0;
+ self->Mods = calloc(sound->ModulatorMap.size, sizeof(self->Mods[0]));
+ if(self->Mods)
+ {
+ ALsizei i;
+
+ self->NumMods = sound->ModulatorMap.size;
+ for(i = 0;i < self->NumMods;i++)
+ {
+ ALsfmodulator *mod = sound->ModulatorMap.array[i].value;
+ fluid_mod_set_source1(&self->Mods[i], getGenInput(mod->Source[0].Input),
+ getGenFlags(mod->Source[0].Input, mod->Source[0].Type,
+ mod->Source[0].Form));
+ fluid_mod_set_source2(&self->Mods[i], getGenInput(mod->Source[1].Input),
+ getGenFlags(mod->Source[1].Input, mod->Source[1].Type,
+ mod->Source[1].Form));
+ fluid_mod_set_amount(&self->Mods[i], mod->Amount);
+ fluid_mod_set_dest(&self->Mods[i], getSf2Gen(mod->Dest));
+ self->Mods[i].next = NULL;
+ }
+ }
+}
+
+static void FSample_Destruct(FSample *self)
+{
+ free(self->Mods);
+ self->Mods = NULL;
+ self->NumMods = 0;
+}
+
+
+typedef struct FPreset {
+ DERIVE_FROM_TYPE(fluid_preset_t);
+
+ char Name[16];
+
+ int Preset;
+ int Bank;
+
+ FSample *Samples;
+ ALsizei NumSamples;
+} FPreset;
+
+static char* FPreset_getName(fluid_preset_t *preset);
+static int FPreset_getPreset(fluid_preset_t *preset);
+static int FPreset_getBank(fluid_preset_t *preset);
+static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int velocity);
+
+static void FPreset_Construct(FPreset *self, ALsfpreset *preset, fluid_sfont_t *parent, ALsoundfont *sfont)
+{
+ STATIC_CAST(fluid_preset_t, self)->data = self;
+ STATIC_CAST(fluid_preset_t, self)->sfont = parent;
+ STATIC_CAST(fluid_preset_t, self)->free = NULL;
+ STATIC_CAST(fluid_preset_t, self)->get_name = FPreset_getName;
+ STATIC_CAST(fluid_preset_t, self)->get_banknum = FPreset_getBank;
+ STATIC_CAST(fluid_preset_t, self)->get_num = FPreset_getPreset;
+ STATIC_CAST(fluid_preset_t, self)->noteon = FPreset_noteOn;
+ STATIC_CAST(fluid_preset_t, self)->notify = NULL;
+
+ memset(self->Name, 0, sizeof(self->Name));
+ self->Preset = preset->Preset;
+ self->Bank = preset->Bank;
+
+ self->NumSamples = 0;
+ self->Samples = calloc(1, preset->NumSounds * sizeof(self->Samples[0]));
+ if(self->Samples)
+ {
+ ALsizei i;
+ self->NumSamples = preset->NumSounds;
+ for(i = 0;i < self->NumSamples;i++)
+ FSample_Construct(&self->Samples[i], preset->Sounds[i], sfont);
+ }
+}
+
+static void FPreset_Destruct(FPreset *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumSamples;i++)
+ FSample_Destruct(&self->Samples[i]);
+ free(self->Samples);
+ self->Samples = NULL;
+ self->NumSamples = 0;
+}
+
+static ALboolean FPreset_canDelete(FPreset *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumSamples;i++)
+ {
+ if(fluid_sample_refcount(STATIC_CAST(fluid_sample_t, &self->Samples[i])) != 0)
+ return AL_FALSE;
+ }
+ return AL_TRUE;
+}
+
+static char* FPreset_getName(fluid_preset_t *preset)
+{
+ return ((FPreset*)preset->data)->Name;
+}
+
+static int FPreset_getPreset(fluid_preset_t *preset)
+{
+ return ((FPreset*)preset->data)->Preset;
+}
+
+static int FPreset_getBank(fluid_preset_t *preset)
+{
+ return ((FPreset*)preset->data)->Bank;
+}
+
+static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int vel)
+{
+ FPreset *self = ((FPreset*)preset->data);
+ ALsizei i;
+
+ for(i = 0;i < self->NumSamples;i++)
+ {
+ FSample *sample = &self->Samples[i];
+ ALfontsound *sound = sample->Sound;
+ fluid_voice_t *voice;
+ ALsizei m;
+
+ if(!(key >= sound->MinKey && key <= sound->MaxKey && vel >= sound->MinVelocity && vel <= sound->MaxVelocity))
+ continue;
+
+ voice = fluid_synth_alloc_voice(synth, STATIC_CAST(fluid_sample_t, sample), channel, key, vel);
+ if(voice == NULL)
+ return FLUID_FAILED;
+
+ fluid_voice_gen_set(voice, GEN_MODLFOTOPITCH, sound->ModLfoToPitch);
+ fluid_voice_gen_set(voice, GEN_VIBLFOTOPITCH, sound->VibratoLfoToPitch);
+ fluid_voice_gen_set(voice, GEN_MODENVTOPITCH, sound->ModEnvToPitch);
+ fluid_voice_gen_set(voice, GEN_FILTERFC, sound->FilterCutoff);
+ fluid_voice_gen_set(voice, GEN_FILTERQ, sound->FilterQ);
+ fluid_voice_gen_set(voice, GEN_MODLFOTOFILTERFC, sound->ModLfoToFilterCutoff);
+ fluid_voice_gen_set(voice, GEN_MODENVTOFILTERFC, sound->ModEnvToFilterCutoff);
+ fluid_voice_gen_set(voice, GEN_MODLFOTOVOL, sound->ModLfoToVolume);
+ fluid_voice_gen_set(voice, GEN_CHORUSSEND, sound->ChorusSend);
+ fluid_voice_gen_set(voice, GEN_REVERBSEND, sound->ReverbSend);
+ fluid_voice_gen_set(voice, GEN_PAN, sound->Pan);
+ fluid_voice_gen_set(voice, GEN_MODLFODELAY, sound->ModLfo.Delay);
+ fluid_voice_gen_set(voice, GEN_MODLFOFREQ, sound->ModLfo.Frequency);
+ fluid_voice_gen_set(voice, GEN_VIBLFODELAY, sound->VibratoLfo.Delay);
+ fluid_voice_gen_set(voice, GEN_VIBLFOFREQ, sound->VibratoLfo.Frequency);
+ fluid_voice_gen_set(voice, GEN_MODENVDELAY, sound->ModEnv.DelayTime);
+ fluid_voice_gen_set(voice, GEN_MODENVATTACK, sound->ModEnv.AttackTime);
+ fluid_voice_gen_set(voice, GEN_MODENVHOLD, sound->ModEnv.HoldTime);
+ fluid_voice_gen_set(voice, GEN_MODENVDECAY, sound->ModEnv.DecayTime);
+ fluid_voice_gen_set(voice, GEN_MODENVSUSTAIN, sound->ModEnv.SustainAttn);
+ fluid_voice_gen_set(voice, GEN_MODENVRELEASE, sound->ModEnv.ReleaseTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOMODENVHOLD, sound->ModEnv.KeyToHoldTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOMODENVDECAY, sound->ModEnv.KeyToDecayTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVDELAY, sound->VolEnv.DelayTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVATTACK, sound->VolEnv.AttackTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVHOLD, sound->VolEnv.HoldTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVDECAY, sound->VolEnv.DecayTime);
+ fluid_voice_gen_set(voice, GEN_VOLENVSUSTAIN, sound->VolEnv.SustainAttn);
+ fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, sound->VolEnv.ReleaseTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOVOLENVHOLD, sound->VolEnv.KeyToHoldTime);
+ fluid_voice_gen_set(voice, GEN_KEYTOVOLENVDECAY, sound->VolEnv.KeyToDecayTime);
+ fluid_voice_gen_set(voice, GEN_ATTENUATION, sound->Attenuation);
+ fluid_voice_gen_set(voice, GEN_COARSETUNE, sound->CoarseTuning);
+ fluid_voice_gen_set(voice, GEN_FINETUNE, sound->FineTuning);
+ fluid_voice_gen_set(voice, GEN_SAMPLEMODE, getSf2LoopMode(sound->LoopMode));
+ fluid_voice_gen_set(voice, GEN_SCALETUNE, sound->TuningScale);
+ fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, sound->ExclusiveClass);
+ for(m = 0;m < sample->NumMods;m++)
+ fluid_voice_add_mod(voice, &sample->Mods[m], FLUID_VOICE_OVERWRITE);
+
+ fluid_synth_start_voice(synth, voice);
+ }
+
+ return FLUID_OK;
+}
+
+
+typedef struct FSfont {
+ DERIVE_FROM_TYPE(fluid_sfont_t);
+
+ char Name[16];
+
+ FPreset *Presets;
+ ALsizei NumPresets;
+
+ ALsizei CurrentPos;
+} FSfont;
+
+static int FSfont_free(fluid_sfont_t *sfont);
+static char* FSfont_getName(fluid_sfont_t *sfont);
+static fluid_preset_t* FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum);
+static void FSfont_iterStart(fluid_sfont_t *sfont);
+static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset);
+
+static void FSfont_Construct(FSfont *self, ALsoundfont *sfont)
+{
+ STATIC_CAST(fluid_sfont_t, self)->data = self;
+ STATIC_CAST(fluid_sfont_t, self)->id = FLUID_FAILED;
+ STATIC_CAST(fluid_sfont_t, self)->free = FSfont_free;
+ STATIC_CAST(fluid_sfont_t, self)->get_name = FSfont_getName;
+ STATIC_CAST(fluid_sfont_t, self)->get_preset = FSfont_getPreset;
+ STATIC_CAST(fluid_sfont_t, self)->iteration_start = FSfont_iterStart;
+ STATIC_CAST(fluid_sfont_t, self)->iteration_next = FSfont_iterNext;
+
+ memset(self->Name, 0, sizeof(self->Name));
+ self->CurrentPos = 0;
+ self->NumPresets = 0;
+ self->Presets = calloc(1, sfont->NumPresets * sizeof(self->Presets[0]));
+ if(self->Presets)
+ {
+ ALsizei i;
+ self->NumPresets = sfont->NumPresets;
+ for(i = 0;i < self->NumPresets;i++)
+ FPreset_Construct(&self->Presets[i], sfont->Presets[i], STATIC_CAST(fluid_sfont_t, self), sfont);
+ }
+}
+
+static void FSfont_Destruct(FSfont *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumPresets;i++)
+ FPreset_Destruct(&self->Presets[i]);
+ free(self->Presets);
+ self->Presets = NULL;
+ self->NumPresets = 0;
+ self->CurrentPos = 0;
+}
+
+static int FSfont_free(fluid_sfont_t *sfont)
+{
+ FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
+ ALsizei i;
+
+ for(i = 0;i < self->NumPresets;i++)
+ {
+ if(!FPreset_canDelete(&self->Presets[i]))
+ return 1;
+ }
+
+ FSfont_Destruct(self);
+ free(self);
+ return 0;
+}
+
+static char* FSfont_getName(fluid_sfont_t *sfont)
+{
+ return STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->Name;
+}
+
+static fluid_preset_t *FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum)
+{
+ FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
+ ALsizei i;
+
+ for(i = 0;i < self->NumPresets;i++)
+ {
+ FPreset *preset = &self->Presets[i];
+ if(preset->Bank == (int)bank && preset->Preset == (int)prenum)
+ return STATIC_CAST(fluid_preset_t, preset);
+ }
+
+ return NULL;
+}
+
+static void FSfont_iterStart(fluid_sfont_t *sfont)
+{
+ STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->CurrentPos = 0;
+}
+
+static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset)
+{
+ FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
+ if(self->CurrentPos >= self->NumPresets)
+ return 0;
+ *preset = *STATIC_CAST(fluid_preset_t, &self->Presets[self->CurrentPos++]);
+ preset->free = NULL;
+ return 1;
+}
+
+
+typedef struct FSynth {
+ DERIVE_FROM_TYPE(MidiSynth);
+ DERIVE_FROM_TYPE(fluid_sfloader_t);
+
+ fluid_settings_t *Settings;
+ fluid_synth_t *Synth;
+ int *FontIDs;
+ ALsizei NumFontIDs;
+
+ ALboolean ForceGM2BankSelect;
+ ALfloat GainScale;
+} FSynth;
+
+static void FSynth_Construct(FSynth *self, ALCdevice *device);
+static void FSynth_Destruct(FSynth *self);
+static ALboolean FSynth_init(FSynth *self, ALCdevice *device);
+static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
+static void FSynth_setGain(FSynth *self, ALfloat gain);
+static void FSynth_setState(FSynth *self, ALenum state);
+static void FSynth_stop(FSynth *self);
+static void FSynth_reset(FSynth *self);
+static void FSynth_update(FSynth *self, ALCdevice *device);
+static void FSynth_processQueue(FSynth *self, ALuint64 time);
+static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
+static void FSynth_Delete(FSynth *self);
+DEFINE_MIDISYNTH_VTABLE(FSynth);
+
+static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename);
+
+
+static void FSynth_Construct(FSynth *self, ALCdevice *device)
+{
+ MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
+ SET_VTABLE2(FSynth, MidiSynth, self);
+
+ STATIC_CAST(fluid_sfloader_t, self)->data = self;
+ STATIC_CAST(fluid_sfloader_t, self)->free = NULL;
+ STATIC_CAST(fluid_sfloader_t, self)->load = FSynth_loadSfont;
+
+ self->Settings = NULL;
+ self->Synth = NULL;
+ self->FontIDs = NULL;
+ self->NumFontIDs = 0;
+ self->ForceGM2BankSelect = AL_FALSE;
+ self->GainScale = 0.2f;
+}
+
+static void FSynth_Destruct(FSynth *self)
+{
+ ALsizei i;
+
+ for(i = 0;i < self->NumFontIDs;i++)
+ fluid_synth_sfunload(self->Synth, self->FontIDs[i], 0);
+ free(self->FontIDs);
+ self->FontIDs = NULL;
+ self->NumFontIDs = 0;
+
+ if(self->Synth != NULL)
+ delete_fluid_synth(self->Synth);
+ self->Synth = NULL;
+
+ if(self->Settings != NULL)
+ delete_fluid_settings(self->Settings);
+ self->Settings = NULL;
+
+ MidiSynth_Destruct(STATIC_CAST(MidiSynth, self));
+}
+
+static ALboolean FSynth_init(FSynth *self, ALCdevice *device)
+{
+ ALfloat vol;
+
+ if(ConfigValueFloat("midi", "volume", &vol))
+ {
+ if(!(vol <= 0.0f))
+ {
+ ERR("MIDI volume %f clamped to 0\n", vol);
+ vol = 0.0f;
+ }
+ self->GainScale = powf(10.0f, vol / 20.0f);
+ }
+
+ self->Settings = new_fluid_settings();
+ if(!self->Settings)
+ {
+ ERR("Failed to create FluidSettings\n");
+ return AL_FALSE;
+ }
+
+ fluid_settings_setint(self->Settings, "synth.polyphony", 256);
+ fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale);
+ fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
+
+ self->Synth = new_fluid_synth(self->Settings);
+ if(!self->Synth)
+ {
+ ERR("Failed to create FluidSynth\n");
+ return AL_FALSE;
+ }
+
+ fluid_synth_add_sfloader(self->Synth, STATIC_CAST(fluid_sfloader_t, self));
+
+ return AL_TRUE;
+}
+
+
+static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename)
+{
+ FSynth *self = STATIC_UPCAST(FSynth, fluid_sfloader_t, loader);
+ FSfont *sfont;
+ int idx;
+
+ if(!filename || sscanf(filename, "_al_internal %d", &idx) != 1)
+ return NULL;
+ if(idx < 0 || idx >= STATIC_CAST(MidiSynth, self)->NumSoundfonts)
+ {
+ ERR("Received invalid soundfont index %d (max: %d)\n", idx, STATIC_CAST(MidiSynth, self)->NumSoundfonts);
+ return NULL;
+ }
+
+ sfont = calloc(1, sizeof(sfont[0]));
+ if(!sfont) return NULL;
+
+ FSfont_Construct(sfont, STATIC_CAST(MidiSynth, self)->Soundfonts[idx]);
+ return STATIC_CAST(fluid_sfont_t, sfont);
+}
+
+static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
+{
+ int *fontid;
+ ALenum ret;
+ ALsizei i;
+
+ ret = MidiSynth_selectSoundfonts(STATIC_CAST(MidiSynth, self), context, count, ids);
+ if(ret != AL_NO_ERROR) return ret;
+
+ ALCdevice_Lock(context->Device);
+ for(i = 0;i < 16;i++)
+ fluid_synth_all_sounds_off(self->Synth, i);
+ ALCdevice_Unlock(context->Device);
+
+ fontid = malloc(count * sizeof(fontid[0]));
+ if(fontid)
+ {
+ for(i = 0;i < STATIC_CAST(MidiSynth, self)->NumSoundfonts;i++)
+ {
+ char name[16];
+ snprintf(name, sizeof(name), "_al_internal %d", i);
+
+ fontid[i] = fluid_synth_sfload(self->Synth, name, 0);
+ if(fontid[i] == FLUID_FAILED)
+ ERR("Failed to load selected soundfont %d\n", i);
+ }
+
+ fontid = ExchangePtr((XchgPtr*)&self->FontIDs, fontid);
+ count = ExchangeInt(&self->NumFontIDs, count);
+ }
+ else
+ {
+ ERR("Failed to allocate space for %d font IDs!\n", count);
+ fontid = ExchangePtr((XchgPtr*)&self->FontIDs, NULL);
+ count = ExchangeInt(&self->NumFontIDs, 0);
+ }
+
+ for(i = 0;i < count;i++)
+ fluid_synth_sfunload(self->Synth, fontid[i], 0);
+ free(fontid);
+
+ return ret;
+}
+
+
+static void FSynth_setGain(FSynth *self, ALfloat gain)
+{
+ fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale * gain);
+ fluid_synth_set_gain(self->Synth, self->GainScale * gain);
+ MidiSynth_setGain(STATIC_CAST(MidiSynth, self), gain);
+}
+
+
+static void FSynth_setState(FSynth *self, ALenum state)
+{
+ MidiSynth_setState(STATIC_CAST(MidiSynth, self), state);
+}
+
+static void FSynth_stop(FSynth *self)
+{
+ MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALsizei chan;
+
+ /* Make sure all pending events are processed. */
+ while(!(synth->SamplesToNext >= 1.0))
+ {
+ ALuint64 time = synth->NextEvtTime;
+ if(time == UINT64_MAX)
+ break;
+
+ synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
+ synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
+ synth->LastEvtTime = time;
+ FSynth_processQueue(self, time);
+
+ synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
+ if(synth->NextEvtTime != UINT64_MAX)
+ synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ }
+
+ /* All notes off */
+ for(chan = 0;chan < 16;chan++)
+ fluid_synth_cc(self->Synth, chan, CTRL_ALLNOTESOFF, 0);
+
+ MidiSynth_stop(STATIC_CAST(MidiSynth, self));
+}
+
+static void FSynth_reset(FSynth *self)
+{
+ /* Reset to power-up status. */
+ fluid_synth_system_reset(self->Synth);
+
+ MidiSynth_reset(STATIC_CAST(MidiSynth, self));
+}
+
+
+static void FSynth_update(FSynth *self, ALCdevice *device)
+{
+ fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
+ fluid_synth_set_sample_rate(self->Synth, device->Frequency);
+ MidiSynth_update(STATIC_CAST(MidiSynth, self), device);
+}
+
+
+static void FSynth_processQueue(FSynth *self, ALuint64 time)
+{
+ EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
+
+ while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
+ {
+ const MidiEvent *evt = &queue->events[queue->pos];
+
+ if(evt->event == SYSEX_EVENT)
+ {
+ static const ALbyte gm2_on[] = { 0x7E, 0x7F, 0x09, 0x03 };
+ static const ALbyte gm2_off[] = { 0x7E, 0x7F, 0x09, 0x02 };
+ int handled = 0;
+
+ fluid_synth_sysex(self->Synth, evt->param.sysex.data, evt->param.sysex.size, NULL, NULL, &handled, 0);
+ if(!handled && evt->param.sysex.size >= (ALsizei)sizeof(gm2_on))
+ {
+ if(memcmp(evt->param.sysex.data, gm2_on, sizeof(gm2_on)) == 0)
+ self->ForceGM2BankSelect = AL_TRUE;
+ else if(memcmp(evt->param.sysex.data, gm2_off, sizeof(gm2_off)) == 0)
+ self->ForceGM2BankSelect = AL_FALSE;
+ }
+ }
+ else switch((evt->event&0xF0))
+ {
+ case AL_NOTEOFF_SOFT:
+ fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param.val[0]);
+ break;
+ case AL_NOTEON_SOFT:
+ fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
+ break;
+ case AL_KEYPRESSURE_SOFT:
+ break;
+
+ case AL_CONTROLLERCHANGE_SOFT:
+ if(self->ForceGM2BankSelect)
+ {
+ int chan = (evt->event&0x0F);
+ if(evt->param.val[0] == CTRL_BANKSELECT_MSB)
+ {
+ if(evt->param.val[1] == 120 && (chan == 9 || chan == 10))
+ fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_DRUM);
+ else if(evt->param.val[1] == 121)
+ fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_MELODIC);
+ break;
+ }
+ if(evt->param.val[0] == CTRL_BANKSELECT_LSB)
+ {
+ fluid_synth_bank_select(self->Synth, chan, evt->param.val[1]);
+ break;
+ }
+ }
+ fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
+ break;
+ case AL_PROGRAMCHANGE_SOFT:
+ fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param.val[0]);
+ break;
+
+ case AL_CHANNELPRESSURE_SOFT:
+ fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param.val[0]);
+ break;
+
+ case AL_PITCHBEND_SOFT:
+ fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param.val[0]&0x7F) |
+ ((evt->param.val[1]&0x7F)<<7));
+ break;
+ }
+
+ queue->pos++;
+ }
+}
+
+static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
+{
+ MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALenum state = synth->State;
+ ALuint total = 0;
+
+ if(state == AL_INITIAL)
+ return;
+ if(state != AL_PLAYING)
+ {
+ fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1,
+ DryBuffer[FrontRight], 0, 1);
+ return;
+ }
+
+ while(total < SamplesToDo)
+ {
+ if(synth->SamplesToNext >= 1.0)
+ {
+ ALuint todo = minu(SamplesToDo - total, fastf2u(synth->SamplesToNext));
+
+ fluid_synth_write_float(self->Synth, todo,
+ &DryBuffer[FrontLeft][total], 0, 1,
+ &DryBuffer[FrontRight][total], 0, 1);
+ total += todo;
+ synth->SamplesSinceLast += todo;
+ synth->SamplesToNext -= todo;
+ }
+ else
+ {
+ ALuint64 time = synth->NextEvtTime;
+ if(time == UINT64_MAX)
+ {
+ synth->SamplesSinceLast += SamplesToDo-total;
+ fluid_synth_write_float(self->Synth, SamplesToDo-total,
+ &DryBuffer[FrontLeft][total], 0, 1,
+ &DryBuffer[FrontRight][total], 0, 1);
+ break;
+ }
+
+ synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
+ synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
+ synth->LastEvtTime = time;
+ FSynth_processQueue(self, time);
+
+ synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
+ if(synth->NextEvtTime != UINT64_MAX)
+ synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ }
+ }
+}
+
+
+static void FSynth_Delete(FSynth *self)
+{
+ free(self);
+}
+
+
+MidiSynth *FSynth_create(ALCdevice *device)
+{
+ FSynth *synth = calloc(1, sizeof(*synth));
+ if(!synth)
+ {
+ ERR("Failed to allocate FSynth\n");
+ return NULL;
+ }
+ FSynth_Construct(synth, device);
+
+ if(FSynth_init(synth, device) == AL_FALSE)
+ {
+ DELETE_OBJ(STATIC_CAST(MidiSynth, synth));
+ return NULL;
+ }
+
+ return STATIC_CAST(MidiSynth, synth);
+}
+
+#else
+
+MidiSynth *FSynth_create(ALCdevice* UNUSED(device))
+{
+ return NULL;
+}
+
+#endif
diff --git a/Alc/midi/sf2load.c b/Alc/midi/sf2load.c
new file mode 100644
index 00000000..5bba345f
--- /dev/null
+++ b/Alc/midi/sf2load.c
@@ -0,0 +1,1436 @@
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alMidi.h"
+#include "alError.h"
+#include "alu.h"
+
+#include "midi/base.h"
+
+
+static ALuint read_le32(Reader *stream)
+{
+ ALubyte buf[4];
+ if(READ(stream, buf, 4) != 4)
+ {
+ READERR(stream) = 1;
+ return 0;
+ }
+ return (buf[3]<<24) | (buf[2]<<16) | (buf[1]<<8) | buf[0];
+}
+static ALushort read_le16(Reader *stream)
+{
+ ALubyte buf[2];
+ if(READ(stream, buf, 2) != 2)
+ {
+ READERR(stream) = 1;
+ return 0;
+ }
+ return (buf[1]<<8) | buf[0];
+}
+static ALubyte read_8(Reader *stream)
+{
+ ALubyte buf[1];
+ if(READ(stream, buf, 1) != 1)
+ {
+ READERR(stream) = 1;
+ return 0;
+ }
+ return buf[0];
+}
+static void skip(Reader *stream, ALuint amt)
+{
+ while(amt > 0 && !READERR(stream))
+ {
+ char buf[4096];
+ size_t got;
+
+ got = READ(stream, buf, minu(sizeof(buf), amt));
+ if(got == 0) READERR(stream) = 1;
+
+ amt -= got;
+ }
+}
+
+typedef struct Generator {
+ ALushort mGenerator;
+ ALushort mAmount;
+} Generator;
+static void Generator_read(Generator *self, Reader *stream)
+{
+ self->mGenerator = read_le16(stream);
+ self->mAmount = read_le16(stream);
+}
+
+static const ALint DefaultGenValue[60] = {
+ 0, /* 0 - startAddrOffset */
+ 0, /* 1 - endAddrOffset */
+ 0, /* 2 - startloopAddrOffset */
+ 0, /* 3 - endloopAddrOffset */
+ 0, /* 4 - startAddrCoarseOffset */
+ 0, /* 5 - modLfoToPitch */
+ 0, /* 6 - vibLfoToPitch */
+ 0, /* 7 - modEnvToPitch */
+ 13500, /* 8 - initialFilterFc */
+ 0, /* 9 - initialFilterQ */
+ 0, /* 10 - modLfoToFilterFc */
+ 0, /* 11 - modEnvToFilterFc */
+ 0, /* 12 - endAddrCoarseOffset */
+ 0, /* 13 - modLfoToVolume */
+ 0, /* 14 - */
+ 0, /* 15 - chorusEffectsSend */
+ 0, /* 16 - reverbEffectsSend */
+ 0, /* 17 - pan */
+ 0, /* 18 - */
+ 0, /* 19 - */
+ 0, /* 20 - */
+ -12000, /* 21 - delayModLFO */
+ 0, /* 22 - freqModLFO */
+ -12000, /* 23 - delayVibLFO */
+ 0, /* 24 - freqVibLFO */
+ -12000, /* 25 - delayModEnv */
+ -12000, /* 26 - attackModEnv */
+ -12000, /* 27 - holdModEnv */
+ -12000, /* 28 - decayModEnv */
+ 0, /* 29 - sustainModEnv */
+ -12000, /* 30 - releaseModEnv */
+ 0, /* 31 - keynumToModEnvHold */
+ 0, /* 32 - keynumToModEnvDecay */
+ -12000, /* 33 - delayVolEnv */
+ -12000, /* 34 - attackVolEnv */
+ -12000, /* 35 - holdVolEnv */
+ -12000, /* 36 - decayVolEnv */
+ 0, /* 37 - sustainVolEnv */
+ -12000, /* 38 - releaseVolEnv */
+ 0, /* 39 - keynumToVolEnvHold */
+ 0, /* 40 - keynumToVolEnvDecay */
+ 0, /* 41 - */
+ 0, /* 42 - */
+ 0, /* 43 - keyRange */
+ 0, /* 44 - velRange */
+ 0, /* 45 - startloopAddrCoarseOffset */
+ 0, /* 46 - keynum */
+ 0, /* 47 - velocity */
+ 0, /* 48 - initialAttenuation */
+ 0, /* 49 - */
+ 0, /* 50 - endloopAddrCoarseOffset */
+ 0, /* 51 - corseTune */
+ 0, /* 52 - fineTune */
+ 0, /* 53 - */
+ 0, /* 54 - sampleModes */
+ 0, /* 55 - */
+ 100, /* 56 - scaleTuning */
+ 0, /* 57 - exclusiveClass */
+ 0, /* 58 - overridingRootKey */
+ 0, /* 59 - */
+};
+
+typedef struct Modulator {
+ ALushort mSrcOp;
+ ALushort mDstOp;
+ ALshort mAmount;
+ ALushort mAmtSrcOp;
+ ALushort mTransOp;
+} Modulator;
+static void Modulator_read(Modulator *self, Reader *stream)
+{
+ self->mSrcOp = read_le16(stream);
+ self->mDstOp = read_le16(stream);
+ self->mAmount = read_le16(stream);
+ self->mAmtSrcOp = read_le16(stream);
+ self->mTransOp = read_le16(stream);
+}
+
+typedef struct Zone {
+ ALushort mGenIdx;
+ ALushort mModIdx;
+} Zone;
+static void Zone_read(Zone *self, Reader *stream)
+{
+ self->mGenIdx = read_le16(stream);
+ self->mModIdx = read_le16(stream);
+}
+
+typedef struct PresetHeader {
+ ALchar mName[20];
+ ALushort mPreset; /* MIDI program number */
+ ALushort mBank;
+ ALushort mZoneIdx;
+ ALuint mLibrary;
+ ALuint mGenre;
+ ALuint mMorphology;
+} PresetHeader;
+static void PresetHeader_read(PresetHeader *self, Reader *stream)
+{
+ if(READ(stream, self->mName, sizeof(self->mName)) != sizeof(self->mName))
+ READERR(stream) = 1;
+ self->mPreset = read_le16(stream);
+ self->mBank = read_le16(stream);
+ self->mZoneIdx = read_le16(stream);
+ self->mLibrary = read_le32(stream);
+ self->mGenre = read_le32(stream);
+ self->mMorphology = read_le32(stream);
+}
+
+typedef struct InstrumentHeader {
+ ALchar mName[20];
+ ALushort mZoneIdx;
+} InstrumentHeader;
+static void InstrumentHeader_read(InstrumentHeader *self, Reader *stream)
+{
+ if(READ(stream, self->mName, sizeof(self->mName)) != sizeof(self->mName))
+ READERR(stream) = 1;
+ self->mZoneIdx = read_le16(stream);
+}
+
+typedef struct SampleHeader {
+ ALchar mName[20]; // 20 bytes
+ ALuint mStart;
+ ALuint mEnd;
+ ALuint mStartloop;
+ ALuint mEndloop;
+ ALuint mSampleRate;
+ ALubyte mOriginalKey;
+ ALbyte mCorrection;
+ ALushort mSampleLink;
+ ALushort mSampleType;
+} SampleHeader;
+static void SampleHeader_read(SampleHeader *self, Reader *stream)
+{
+ if(READ(stream, self->mName, sizeof(self->mName)) != sizeof(self->mName))
+ READERR(stream) = 1;
+ self->mStart = read_le32(stream);
+ self->mEnd = read_le32(stream);
+ self->mStartloop = read_le32(stream);
+ self->mEndloop = read_le32(stream);
+ self->mSampleRate = read_le32(stream);
+ self->mOriginalKey = read_8(stream);
+ self->mCorrection = read_8(stream);
+ self->mSampleLink = read_le16(stream);
+ self->mSampleType = read_le16(stream);
+}
+
+
+typedef struct Soundfont {
+ ALuint ifil;
+ ALchar *irom;
+
+ PresetHeader *phdr;
+ ALsizei phdr_size;
+
+ Zone *pbag;
+ ALsizei pbag_size;
+ Modulator *pmod;
+ ALsizei pmod_size;
+ Generator *pgen;
+ ALsizei pgen_size;
+
+ InstrumentHeader *inst;
+ ALsizei inst_size;
+
+ Zone *ibag;
+ ALsizei ibag_size;
+ Modulator *imod;
+ ALsizei imod_size;
+ Generator *igen;
+ ALsizei igen_size;
+
+ SampleHeader *shdr;
+ ALsizei shdr_size;
+} Soundfont;
+
+static void Soundfont_Construct(Soundfont *self)
+{
+ self->ifil = 0;
+ self->irom = NULL;
+
+ self->phdr = NULL;
+ self->phdr_size = 0;
+
+ self->pbag = NULL;
+ self->pbag_size = 0;
+ self->pmod = NULL;
+ self->pmod_size = 0;
+ self->pgen = NULL;
+ self->pgen_size = 0;
+
+ self->inst = NULL;
+ self->inst_size = 0;
+
+ self->ibag = NULL;
+ self->ibag_size = 0;
+ self->imod = NULL;
+ self->imod_size = 0;
+ self->igen = NULL;
+ self->igen_size = 0;
+
+ self->shdr = NULL;
+ self->shdr_size = 0;
+}
+
+static void Soundfont_Destruct(Soundfont *self)
+{
+ free(self->irom);
+ self->irom = NULL;
+
+ free(self->phdr);
+ self->phdr = NULL;
+ self->phdr_size = 0;
+
+ free(self->pbag);
+ self->pbag = NULL;
+ self->pbag_size = 0;
+ free(self->pmod);
+ self->pmod = NULL;
+ self->pmod_size = 0;
+ free(self->pgen);
+ self->pgen = NULL;
+ self->pgen_size = 0;
+
+ free(self->inst);
+ self->inst = NULL;
+ self->inst_size = 0;
+
+ free(self->ibag);
+ self->ibag = NULL;
+ self->ibag_size = 0;
+ free(self->imod);
+ self->imod = NULL;
+ self->imod_size = 0;
+ free(self->igen);
+ self->igen = NULL;
+ self->igen_size = 0;
+
+ free(self->shdr);
+ self->shdr = NULL;
+ self->shdr_size = 0;
+}
+
+
+#define FOURCC(a,b,c,d) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a))
+#define FOURCCARGS(x) (char)((x)&0xff), (char)(((x)>>8)&0xff), (char)(((x)>>16)&0xff), (char)(((x)>>24)&0xff)
+typedef struct RiffHdr {
+ ALuint mCode;
+ ALuint mSize;
+} RiffHdr;
+static void RiffHdr_read(RiffHdr *self, Reader *stream)
+{
+ self->mCode = read_le32(stream);
+ self->mSize = read_le32(stream);
+}
+
+
+typedef struct GenModList {
+ Generator *gens;
+ ALsizei gens_size;
+ ALsizei gens_max;
+
+ Modulator *mods;
+ ALsizei mods_size;
+ ALsizei mods_max;
+} GenModList;
+
+static void GenModList_Construct(GenModList *self)
+{
+ self->gens = NULL;
+ self->gens_size = 0;
+ self->gens_max = 0;
+
+ self->mods = NULL;
+ self->mods_size = 0;
+ self->mods_max = 0;
+}
+
+static void GenModList_Destruct(GenModList *self)
+{
+ free(self->gens);
+ self->gens = NULL;
+ self->gens_size = 0;
+ self->gens_max = 0;
+
+ free(self->mods);
+ self->mods = NULL;
+ self->mods_size = 0;
+ self->mods_max = 0;
+}
+
+static GenModList GenModList_clone(const GenModList *self)
+{
+ GenModList ret;
+
+ ret.gens = malloc(self->gens_max * sizeof(ret.gens[0]));
+ memcpy(ret.gens, self->gens, self->gens_size * sizeof(ret.gens[0]));
+ ret.gens_size = self->gens_size;
+ ret.gens_max = self->gens_max;
+
+ ret.mods = malloc(self->mods_max * sizeof(ret.mods[0]));
+ memcpy(ret.mods, self->mods, self->mods_size * sizeof(ret.mods[0]));
+ ret.mods_size = self->mods_size;
+ ret.mods_max = self->mods_max;
+
+ return ret;
+}
+
+static void GenModList_insertGen(GenModList *self, const Generator *gen, ALboolean ispreset)
+{
+ Generator *i = self->gens;
+ Generator *end = i + self->gens_size;
+ for(;i != end;i++)
+ {
+ if(i->mGenerator == gen->mGenerator)
+ {
+ i->mAmount = gen->mAmount;
+ return;
+ }
+ }
+
+ if(ispreset &&
+ (gen->mGenerator == 0 || gen->mGenerator == 1 || gen->mGenerator == 2 ||
+ gen->mGenerator == 3 || gen->mGenerator == 4 || gen->mGenerator == 12 ||
+ gen->mGenerator == 45 || gen->mGenerator == 46 || gen->mGenerator == 47 ||
+ gen->mGenerator == 50 || gen->mGenerator == 54 || gen->mGenerator == 57 ||
+ gen->mGenerator == 58))
+ return;
+
+ if(self->gens_size == self->gens_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->gens_max ? self->gens_max<<1 : 1);
+ if(newsize > self->gens_max)
+ temp = realloc(self->gens, newsize * sizeof(self->gens[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase generator storage to %d elements (from %d)\n",
+ newsize, self->gens_max);
+ return;
+ }
+
+ self->gens = temp;
+ self->gens_max = newsize;
+ }
+
+ self->gens[self->gens_size] = *gen;
+ self->gens_size++;
+}
+static void GenModList_accumGen(GenModList *self, const Generator *gen)
+{
+ Generator *i = self->gens;
+ Generator *end = i + self->gens_size;
+ for(;i != end;i++)
+ {
+ if(i->mGenerator == gen->mGenerator)
+ {
+ if(gen->mGenerator == 43 || gen->mGenerator == 44)
+ {
+ /* Range generators accumulate by taking the intersection of
+ * the two ranges.
+ */
+ ALushort low = maxu(i->mAmount&0x00ff, gen->mAmount&0x00ff);
+ ALushort high = minu(i->mAmount&0xff00, gen->mAmount&0xff00);
+ i->mAmount = low | high;
+ }
+ else
+ i->mAmount += gen->mAmount;
+ return;
+ }
+ }
+
+ if(self->gens_size == self->gens_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->gens_max ? self->gens_max<<1 : 1);
+ if(newsize > self->gens_max)
+ temp = realloc(self->gens, newsize * sizeof(self->gens[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase generator storage to %d elements (from %d)\n",
+ newsize, self->gens_max);
+ return;
+ }
+
+ self->gens = temp;
+ self->gens_max = newsize;
+ }
+
+ self->gens[self->gens_size] = *gen;
+ if(gen->mGenerator < 60)
+ self->gens[self->gens_size].mAmount += DefaultGenValue[gen->mGenerator];
+ self->gens_size++;
+}
+
+static void GenModList_insertMod(GenModList *self, const Modulator *mod)
+{
+ Modulator *i = self->mods;
+ Modulator *end = i + self->mods_size;
+ for(;i != end;i++)
+ {
+ if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp &&
+ i->mAmtSrcOp == mod->mAmtSrcOp && i->mTransOp == mod->mTransOp)
+ {
+ i->mAmount = mod->mAmount;
+ return;
+ }
+ }
+
+ if(self->mods_size == self->mods_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->mods_max ? self->mods_max<<1 : 1);
+ if(newsize > self->mods_max)
+ temp = realloc(self->mods, newsize * sizeof(self->mods[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase modulator storage to %d elements (from %d)\n",
+ newsize, self->mods_max);
+ return;
+ }
+
+ self->mods = temp;
+ self->mods_max = newsize;
+ }
+
+ self->mods[self->mods_size] = *mod;
+ self->mods_size++;
+}
+static void GenModList_accumMod(GenModList *self, const Modulator *mod)
+{
+ Modulator *i = self->mods;
+ Modulator *end = i + self->mods_size;
+ for(;i != end;i++)
+ {
+ if(i->mDstOp == mod->mDstOp && i->mSrcOp == mod->mSrcOp &&
+ i->mAmtSrcOp == mod->mAmtSrcOp && i->mTransOp == mod->mTransOp)
+ {
+ i->mAmount += mod->mAmount;
+ return;
+ }
+ }
+
+ if(self->mods_size == self->mods_max)
+ {
+ void *temp = NULL;
+ ALsizei newsize;
+
+ newsize = (self->mods_max ? self->mods_max<<1 : 1);
+ if(newsize > self->mods_max)
+ temp = realloc(self->mods, newsize * sizeof(self->mods[0]));
+ if(!temp)
+ {
+ ERR("Failed to increase modulator storage to %d elements (from %d)\n",
+ newsize, self->mods_max);
+ return;
+ }
+
+ self->mods = temp;
+ self->mods_max = newsize;
+ }
+
+ self->mods[self->mods_size] = *mod;
+ if(mod->mSrcOp == 0x0502 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 960;
+ else if(mod->mSrcOp == 0x0102 && mod->mDstOp == 8 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += -2400;
+ else if(mod->mSrcOp == 0x000D && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 50;
+ else if(mod->mSrcOp == 0x0081 && mod->mDstOp == 6 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 50;
+ else if(mod->mSrcOp == 0x0582 && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 960;
+ else if(mod->mSrcOp == 0x028A && mod->mDstOp == 17 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 1000;
+ else if(mod->mSrcOp == 0x058B && mod->mDstOp == 48 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 960;
+ else if(mod->mSrcOp == 0x00DB && mod->mDstOp == 16 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 200;
+ else if(mod->mSrcOp == 0x00DD && mod->mDstOp == 15 && mod->mAmtSrcOp == 0 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 200;
+ /*else if(mod->mSrcOp == 0x020E && mod->mDstOp == ?initialpitch? && mod->mAmtSrcOp == 0x0010 && mod->mTransOp == 0)
+ self->mods[self->mods_size].mAmount += 12700;*/
+ self->mods_size++;
+}
+
+
+#define ERROR_GOTO(lbl_, ...) do { \
+ ERR(__VA_ARGS__); \
+ goto lbl_; \
+} while(0)
+
+static ALboolean ensureFontSanity(const Soundfont *sfont)
+{
+ ALsizei i;
+
+ for(i = 0;i < sfont->phdr_size-1;i++)
+ {
+ if(sfont->phdr[i].mZoneIdx >= sfont->pbag_size)
+ {
+ WARN("Preset %d has invalid zone index %d (max: %d)\n", i,
+ sfont->phdr[i].mZoneIdx, sfont->pbag_size);
+ return AL_FALSE;
+ }
+ if(sfont->phdr[i+1].mZoneIdx < sfont->phdr[i].mZoneIdx)
+ {
+ WARN("Preset %d has invalid zone index (%d does not follow %d)\n", i+1,
+ sfont->phdr[i+1].mZoneIdx, sfont->phdr[i].mZoneIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->phdr[i].mZoneIdx >= sfont->pbag_size)
+ {
+ WARN("Preset %d has invalid zone index %d (max: %d)\n", i,
+ sfont->phdr[i].mZoneIdx, sfont->pbag_size);
+ return AL_FALSE;
+ }
+
+ for(i = 0;i < sfont->pbag_size-1;i++)
+ {
+ if(sfont->pbag[i].mGenIdx >= sfont->pgen_size)
+ {
+ WARN("Preset zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->pbag[i].mGenIdx, sfont->pgen_size);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i+1].mGenIdx < sfont->pbag[i].mGenIdx)
+ {
+ WARN("Preset zone %d has invalid generator index (%d does not follow %d)\n", i+1,
+ sfont->pbag[i+1].mGenIdx, sfont->pbag[i].mGenIdx);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i].mModIdx >= sfont->pmod_size)
+ {
+ WARN("Preset zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->pbag[i].mModIdx, sfont->pmod_size);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i+1].mModIdx < sfont->pbag[i].mModIdx)
+ {
+ WARN("Preset zone %d has invalid modulator index (%d does not follow %d)\n", i+1,
+ sfont->pbag[i+1].mModIdx, sfont->pbag[i].mModIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->pbag[i].mGenIdx >= sfont->pgen_size)
+ {
+ WARN("Preset zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->pbag[i].mGenIdx, sfont->pgen_size);
+ return AL_FALSE;
+ }
+ if(sfont->pbag[i].mModIdx >= sfont->pmod_size)
+ {
+ WARN("Preset zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->pbag[i].mModIdx, sfont->pmod_size);
+ return AL_FALSE;
+ }
+
+
+ for(i = 0;i < sfont->inst_size-1;i++)
+ {
+ if(sfont->inst[i].mZoneIdx >= sfont->ibag_size)
+ {
+ WARN("Instrument %d has invalid zone index %d (max: %d)\n", i,
+ sfont->inst[i].mZoneIdx, sfont->ibag_size);
+ return AL_FALSE;
+ }
+ if(sfont->inst[i+1].mZoneIdx < sfont->inst[i].mZoneIdx)
+ {
+ WARN("Instrument %d has invalid zone index (%d does not follow %d)\n", i+1,
+ sfont->inst[i+1].mZoneIdx, sfont->inst[i].mZoneIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->inst[i].mZoneIdx >= sfont->ibag_size)
+ {
+ WARN("Instrument %d has invalid zone index %d (max: %d)\n", i,
+ sfont->inst[i].mZoneIdx, sfont->ibag_size);
+ return AL_FALSE;
+ }
+
+ for(i = 0;i < sfont->ibag_size-1;i++)
+ {
+ if(sfont->ibag[i].mGenIdx >= sfont->igen_size)
+ {
+ WARN("Instrument zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->ibag[i].mGenIdx, sfont->igen_size);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i+1].mGenIdx < sfont->ibag[i].mGenIdx)
+ {
+ WARN("Instrument zone %d has invalid generator index (%d does not follow %d)\n", i+1,
+ sfont->ibag[i+1].mGenIdx, sfont->ibag[i].mGenIdx);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i].mModIdx >= sfont->imod_size)
+ {
+ WARN("Instrument zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->ibag[i].mModIdx, sfont->imod_size);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i+1].mModIdx < sfont->ibag[i].mModIdx)
+ {
+ WARN("Instrument zone %d has invalid modulator index (%d does not follow %d)\n", i+1,
+ sfont->ibag[i+1].mModIdx, sfont->ibag[i].mModIdx);
+ return AL_FALSE;
+ }
+ }
+ if(sfont->ibag[i].mGenIdx >= sfont->igen_size)
+ {
+ WARN("Instrument zone %d has invalid generator index %d (max: %d)\n", i,
+ sfont->ibag[i].mGenIdx, sfont->igen_size);
+ return AL_FALSE;
+ }
+ if(sfont->ibag[i].mModIdx >= sfont->imod_size)
+ {
+ WARN("Instrument zone %d has invalid modulator index %d (max: %d)\n", i,
+ sfont->ibag[i].mModIdx, sfont->imod_size);
+ return AL_FALSE;
+ }
+
+
+ for(i = 0;i < sfont->shdr_size-1;i++)
+ {
+ if((sfont->shdr[i].mSampleType&0x8000) && sfont->irom == NULL)
+ {
+ WARN("Sample header %d has ROM sample type without an irom sub-chunk\n", i);
+ return AL_FALSE;
+ }
+ }
+
+
+ return AL_TRUE;
+}
+
+static ALboolean checkZone(const GenModList *zone, const PresetHeader *preset, const InstrumentHeader *inst, const SampleHeader *samp)
+{
+ ALsizei i;
+
+ for(i = 0;i < zone->gens_size;i++)
+ {
+ if(zone->gens[i].mGenerator == 43 || zone->gens[i].mGenerator == 44)
+ {
+ int high = zone->gens[i].mAmount>>8;
+ int low = zone->gens[i].mAmount&0xff;
+
+ if(!(low >= 0 && high <= 127 && high >= low))
+ {
+ TRACE("Preset \"%s\", inst \"%s\", sample \"%s\": invalid %s range %d...%d\n",
+ preset->mName, inst->mName, samp->mName,
+ (zone->gens[i].mGenerator == 43) ? "key" :
+ (zone->gens[i].mGenerator == 44) ? "velocity" : "(unknown)",
+ low, high);
+ return AL_FALSE;
+ }
+ }
+ }
+
+ return AL_TRUE;
+}
+
+static ALenum getModSrcInput(int input)
+{
+ if(input == 0) return AL_ONE_SOFT;
+ if(input == 2) return AL_NOTEON_VELOCITY_SOFT;
+ if(input == 3) return AL_NOTEON_KEY_SOFT;
+ if(input == 10) return AL_KEYPRESSURE_SOFT;
+ if(input == 13) return AL_CHANNELPRESSURE_SOFT;
+ if(input == 14) return AL_PITCHBEND_SOFT;
+ if(input == 16) return AL_PITCHBEND_SENSITIVITY_SOFT;
+ if((input&0x80))
+ {
+ input ^= 0x80;
+ if(input > 0 && input < 120 && !(input == 6 || (input >= 32 && input <= 63) ||
+ (input >= 98 && input <= 101)))
+ return input;
+ input ^= 0x80;
+ }
+ ERR("Unhandled modulator source input: 0x%02x\n", input);
+ return AL_INVALID;
+}
+
+static ALenum getModSrcType(int type)
+{
+ if(type == 0x0000) return AL_UNORM_SOFT;
+ if(type == 0x0100) return AL_UNORM_REV_SOFT;
+ if(type == 0x0200) return AL_SNORM_SOFT;
+ if(type == 0x0300) return AL_SNORM_REV_SOFT;
+ ERR("Unhandled modulator source type: 0x%04x\n", type);
+ return AL_INVALID;
+}
+
+static ALenum getModSrcForm(int form)
+{
+ if(form == 0x0000) return AL_LINEAR_SOFT;
+ if(form == 0x0400) return AL_CONCAVE_SOFT;
+ if(form == 0x0800) return AL_CONVEX_SOFT;
+ if(form == 0x0C00) return AL_SWITCH_SOFT;
+ ERR("Unhandled modulator source form: 0x%04x\n", form);
+ return AL_INVALID;
+}
+
+static ALenum getModTransOp(int op)
+{
+ if(op == 0) return AL_LINEAR_SOFT;
+ if(op == 2) return AL_ABSOLUTE_SOFT;
+ ERR("Unhandled modulator transform op: 0x%04x\n", op);
+ return AL_INVALID;
+}
+
+static ALenum getLoopMode(int mode)
+{
+ if(mode == 0) return AL_NONE;
+ if(mode == 1) return AL_LOOP_CONTINUOUS_SOFT;
+ if(mode == 3) return AL_LOOP_UNTIL_RELEASE_SOFT;
+ ERR("Unhandled loop mode: %d\n", mode);
+ return AL_NONE;
+}
+
+static ALenum getSampleType(int type)
+{
+ if(type == 1) return AL_MONO_SOFT;
+ if(type == 2) return AL_RIGHT_SOFT;
+ if(type == 4) return AL_LEFT_SOFT;
+ if(type == 8)
+ {
+ WARN("Sample type \"linked\" ignored; pretending mono\n");
+ return AL_MONO_SOFT;
+ }
+ ERR("Unhandled sample type: 0x%04x\n", type);
+ return AL_MONO_SOFT;
+}
+
+static void fillZone(ALfontsound *sound, ALCcontext *context, const GenModList *zone)
+{
+ static const ALenum Gen2Param[60] = {
+ 0, /* 0 - startAddrOffset */
+ 0, /* 1 - endAddrOffset */
+ 0, /* 2 - startloopAddrOffset */
+ 0, /* 3 - endloopAddrOffset */
+ 0, /* 4 - startAddrCoarseOffset */
+ AL_MOD_LFO_TO_PITCH_SOFT, /* 5 - modLfoToPitch */
+ AL_VIBRATO_LFO_TO_PITCH_SOFT, /* 6 - vibLfoToPitch */
+ AL_MOD_ENV_TO_PITCH_SOFT, /* 7 - modEnvToPitch */
+ AL_FILTER_CUTOFF_SOFT, /* 8 - initialFilterFc */
+ AL_FILTER_RESONANCE_SOFT, /* 9 - initialFilterQ */
+ AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT, /* 10 - modLfoToFilterFc */
+ AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT, /* 11 - modEnvToFilterFc */
+ 0, /* 12 - endAddrCoarseOffset */
+ AL_MOD_LFO_TO_VOLUME_SOFT, /* 13 - modLfoToVolume */
+ 0, /* 14 - */
+ AL_CHORUS_SEND_SOFT, /* 15 - chorusEffectsSend */
+ AL_REVERB_SEND_SOFT, /* 16 - reverbEffectsSend */
+ AL_PAN_SOFT, /* 17 - pan */
+ 0, /* 18 - */
+ 0, /* 19 - */
+ 0, /* 20 - */
+ AL_MOD_LFO_DELAY_SOFT, /* 21 - delayModLFO */
+ AL_MOD_LFO_FREQUENCY_SOFT, /* 22 - freqModLFO */
+ AL_VIBRATO_LFO_DELAY_SOFT, /* 23 - delayVibLFO */
+ AL_VIBRATO_LFO_FREQUENCY_SOFT, /* 24 - freqVibLFO */
+ AL_MOD_ENV_DELAYTIME_SOFT, /* 25 - delayModEnv */
+ AL_MOD_ENV_ATTACKTIME_SOFT, /* 26 - attackModEnv */
+ AL_MOD_ENV_HOLDTIME_SOFT, /* 27 - holdModEnv */
+ AL_MOD_ENV_DECAYTIME_SOFT, /* 28 - decayModEnv */
+ AL_MOD_ENV_SUSTAINVOLUME_SOFT, /* 29 - sustainModEnv */
+ AL_MOD_ENV_RELEASETIME_SOFT, /* 30 - releaseModEnv */
+ AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT, /* 31 - keynumToModEnvHold */
+ AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT, /* 32 - keynumToModEnvDecay */
+ AL_VOLUME_ENV_DELAYTIME_SOFT, /* 33 - delayVolEnv */
+ AL_VOLUME_ENV_ATTACKTIME_SOFT, /* 34 - attackVolEnv */
+ AL_VOLUME_ENV_HOLDTIME_SOFT, /* 35 - holdVolEnv */
+ AL_VOLUME_ENV_DECAYTIME_SOFT, /* 36 - decayVolEnv */
+ AL_VOLUME_ENV_SUSTAINVOLUME_SOFT, /* 37 - sustainVolEnv */
+ AL_VOLUME_ENV_RELEASETIME_SOFT, /* 38 - releaseVolEnv */
+ AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT, /* 39 - keynumToVolEnvHold */
+ AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT, /* 40 - keynumToVolEnvDecay */
+ 0, /* 41 - */
+ 0, /* 42 - */
+ AL_KEY_RANGE_SOFT, /* 43 - keyRange */
+ AL_VELOCITY_RANGE_SOFT, /* 44 - velRange */
+ 0, /* 45 - startloopAddrCoarseOffset */
+ 0, /* 46 - keynum */
+ 0, /* 47 - velocity */
+ AL_ATTENUATION_SOFT, /* 48 - initialAttenuation */
+ 0, /* 49 - */
+ 0, /* 50 - endloopAddrCoarseOffset */
+ AL_TUNING_COARSE_SOFT, /* 51 - corseTune */
+ AL_TUNING_FINE_SOFT, /* 52 - fineTune */
+ 0, /* 53 - */
+ AL_LOOP_MODE_SOFT, /* 54 - sampleModes */
+ 0, /* 55 - */
+ AL_TUNING_SCALE_SOFT, /* 56 - scaleTuning */
+ AL_EXCLUSIVE_CLASS_SOFT, /* 57 - exclusiveClass */
+ AL_BASE_KEY_SOFT, /* 58 - overridingRootKey */
+ 0, /* 59 - */
+ };
+ const Generator *gen, *gen_end;
+
+ if(zone->mods)
+ {
+ ALsizei i;
+ for(i = 0;i < zone->mods_size;i++)
+ {
+ ALenum src0in = getModSrcInput(zone->mods[i].mSrcOp&0xFF);
+ ALenum src0type = getModSrcType(zone->mods[i].mSrcOp&0x0300);
+ ALenum src0form = getModSrcForm(zone->mods[i].mSrcOp&0xFC00);
+ ALenum src1in = getModSrcInput(zone->mods[i].mAmtSrcOp&0xFF);
+ ALenum src1type = getModSrcType(zone->mods[i].mAmtSrcOp&0x0300);
+ ALenum src1form = getModSrcForm(zone->mods[i].mAmtSrcOp&0xFC00);
+ ALenum trans = getModTransOp(zone->mods[i].mTransOp);
+ ALenum dst = (zone->mods[i].mDstOp < 60) ? Gen2Param[zone->mods[i].mDstOp] : 0;
+ if(!dst || dst == AL_KEY_RANGE_SOFT || dst == AL_VELOCITY_RANGE_SOFT ||
+ dst == AL_LOOP_MODE_SOFT || dst == AL_EXCLUSIVE_CLASS_SOFT ||
+ dst == AL_BASE_KEY_SOFT)
+ ERR("Unhandled modulator destination: %d\n", zone->mods[i].mDstOp);
+ else if(src0in != AL_INVALID && src0form != AL_INVALID && src0type != AL_INVALID &&
+ src1in != AL_INVALID && src1form != AL_INVALID && src0type != AL_INVALID &&
+ trans != AL_INVALID)
+ {
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_INPUT_SOFT, src0in);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_TYPE_SOFT, src0type);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE0_FORM_SOFT, src0form);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_INPUT_SOFT, src1in);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_TYPE_SOFT, src1type);
+ ALfontsound_setModStagei(sound, context, i, AL_SOURCE1_FORM_SOFT, src1form);
+ ALfontsound_setModStagei(sound, context, i, AL_AMOUNT_SOFT, zone->mods[i].mAmount);
+ ALfontsound_setModStagei(sound, context, i, AL_TRANSFORM_OP_SOFT, trans);
+ ALfontsound_setModStagei(sound, context, i, AL_DESTINATION_SOFT, dst);
+ }
+ }
+ }
+
+ gen = zone->gens;
+ gen_end = gen + zone->gens_size;
+ for(;gen != gen_end;gen++)
+ {
+ ALint value = (ALshort)gen->mAmount;
+ if(gen->mGenerator == 0)
+ sound->Start += value;
+ else if(gen->mGenerator == 1)
+ sound->End += value;
+ else if(gen->mGenerator == 2)
+ sound->LoopStart += value;
+ else if(gen->mGenerator == 3)
+ sound->LoopEnd += value;
+ else if(gen->mGenerator == 4)
+ sound->Start += value<<15;
+ else if(gen->mGenerator == 12)
+ sound->End += value<<15;
+ else if(gen->mGenerator == 45)
+ sound->LoopStart += value<<15;
+ else if(gen->mGenerator == 50)
+ sound->LoopEnd += value<<15;
+ else if(gen->mGenerator == 43)
+ {
+ sound->MinKey = mini((value&0xff), 127);
+ sound->MaxKey = mini(((value>>8)&0xff), 127);
+ }
+ else if(gen->mGenerator == 44)
+ {
+ sound->MinVelocity = mini((value&0xff), 127);
+ sound->MaxVelocity = mini(((value>>8)&0xff), 127);
+ }
+ else
+ {
+ ALenum param = 0;
+ if(gen->mGenerator < 60)
+ param = Gen2Param[gen->mGenerator];
+ if(param)
+ {
+ if(param == AL_BASE_KEY_SOFT)
+ {
+ if(!(value >= 0 && value <= 127))
+ {
+ if(value != -1)
+ WARN("Invalid overridingRootKey generator value %d\n", value);
+ continue;
+ }
+ }
+ if(param == AL_FILTER_RESONANCE_SOFT || param == AL_ATTENUATION_SOFT)
+ value = maxi(0, value);
+ else if(param == AL_CHORUS_SEND_SOFT || param == AL_REVERB_SEND_SOFT)
+ value = clampi(value, 0, 1000);
+ else if(param == AL_LOOP_MODE_SOFT)
+ value = getLoopMode(value);
+ ALfontsound_setPropi(sound, context, param, value);
+ }
+ else if(gen->mGenerator < 256)
+ {
+ static ALboolean warned[256];
+ if(!warned[gen->mGenerator])
+ {
+ warned[gen->mGenerator] = AL_TRUE;
+ ERR("Unhandled generator %d\n", gen->mGenerator);
+ }
+ }
+ }
+ }
+}
+
+static void processInstrument(ALfontsound ***sounds, ALsizei *sounds_size, ALCcontext *context, InstrumentHeader *inst, const PresetHeader *preset, const Soundfont *sfont, const GenModList *pzone)
+{
+ const Generator *gen, *gen_end;
+ const Modulator *mod, *mod_end;
+ const Zone *zone, *zone_end;
+ GenModList gzone;
+ ALvoid *temp;
+
+ if((inst+1)->mZoneIdx == inst->mZoneIdx)
+ ERR("Instrument with no zones!");
+
+ GenModList_Construct(&gzone);
+ zone = sfont->ibag + inst->mZoneIdx;
+ zone_end = sfont->ibag + (inst+1)->mZoneIdx;
+ if(zone_end-zone > 1)
+ {
+ gen = sfont->igen + zone->mGenIdx;
+ gen_end = sfont->igen + (zone+1)->mGenIdx;
+
+ // If no generators, or last generator is not a sample, this is a global zone
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 53)
+ break;
+ }
+
+ if(gen == gen_end)
+ {
+ gen = sfont->igen + zone->mGenIdx;
+ gen_end = sfont->igen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ GenModList_insertGen(&gzone, gen, AL_FALSE);
+
+ mod = sfont->imod + zone->mModIdx;
+ mod_end = sfont->imod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&gzone, mod);
+
+ zone++;
+ }
+ }
+
+ temp = realloc(*sounds, (zone_end-zone + *sounds_size)*sizeof((*sounds)[0]));
+ if(!temp)
+ {
+ ERR("Failed reallocating fontsound storage to %ld elements (from %d)\n",
+ (zone_end-zone + *sounds_size), *sounds_size);
+ return;
+ }
+ *sounds = temp;
+ for(;zone != zone_end;zone++)
+ {
+ GenModList lzone = GenModList_clone(&gzone);
+ mod = sfont->imod + zone->mModIdx;
+ mod_end = sfont->imod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&lzone, mod);
+
+ gen = sfont->igen + zone->mGenIdx;
+ gen_end = sfont->igen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 53)
+ {
+ const SampleHeader *samp;
+ ALfontsound *sound;
+
+ if(gen->mAmount >= sfont->shdr_size-1)
+ {
+ ERR("Generator %ld has invalid sample ID (%d of %d)\n",
+ (long)(gen-sfont->igen), gen->mAmount, sfont->shdr_size-1);
+ break;
+ }
+ samp = &sfont->shdr[gen->mAmount];
+
+ gen = pzone->gens;
+ gen_end = gen + pzone->gens_size;
+ for(;gen != gen_end;gen++)
+ GenModList_accumGen(&lzone, gen);
+
+ mod = pzone->mods;
+ mod_end = mod + pzone->mods_size;
+ for(;mod != mod_end;mod++)
+ GenModList_accumMod(&lzone, mod);
+
+ if(!checkZone(&lzone, preset, inst, samp))
+ break;
+ /* Ignore ROM samples for now. */
+ if((samp->mSampleType&0x8000))
+ break;
+
+ sound = NewFontsound(context);
+ (*sounds)[(*sounds_size)++] = sound;
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_START_SOFT, samp->mStart);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_END_SOFT, samp->mEnd);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_LOOP_START_SOFT, samp->mStartloop);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_LOOP_END_SOFT, samp->mEndloop);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_RATE_SOFT, samp->mSampleRate);
+ ALfontsound_setPropi(sound, context, AL_BASE_KEY_SOFT, (samp->mOriginalKey <= 127) ? samp->mOriginalKey : 60);
+ ALfontsound_setPropi(sound, context, AL_KEY_CORRECTION_SOFT, samp->mCorrection);
+ ALfontsound_setPropi(sound, context, AL_SAMPLE_TYPE_SOFT, getSampleType(samp->mSampleType&0x7fff));
+ fillZone(sound, context, &lzone);
+
+ break;
+ }
+ GenModList_insertGen(&lzone, gen, AL_FALSE);
+ }
+
+ GenModList_Destruct(&lzone);
+ }
+
+ GenModList_Destruct(&gzone);
+}
+
+ALboolean loadSf2(Reader *stream, ALsoundfont *soundfont, ALCcontext *context)
+{
+ ALsfpreset **presets = NULL;
+ ALsizei presets_size = 0;
+ ALuint ltype;
+ Soundfont sfont;
+ RiffHdr riff;
+ RiffHdr list;
+ ALsizei i;
+
+ Soundfont_Construct(&sfont);
+
+ RiffHdr_read(&riff, stream);
+ if(riff.mCode != FOURCC('R','I','F','F'))
+ ERROR_GOTO(error, "Invalid Format, expected RIFF got '%c%c%c%c'\n", FOURCCARGS(riff.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('s','f','b','k'))
+ ERROR_GOTO(error, "Invalid Format, expected sfbk got '%c%c%c%c'\n", FOURCCARGS(ltype));
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading file header\n");
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('L','I','S','T'))
+ ERROR_GOTO(error, "Invalid Format, expected LIST (INFO) got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('I','N','F','O'))
+ ERROR_GOTO(error, "Invalid Format, expected INFO got '%c%c%c%c'\n", FOURCCARGS(ltype));
+ list.mSize -= 4;
+ while(list.mSize > 0 && !READERR(stream))
+ {
+ RiffHdr info;
+
+ RiffHdr_read(&info, stream);
+ list.mSize -= 8;
+ if(info.mCode == FOURCC('i','f','i','l'))
+ {
+ if(info.mSize != 4)
+ ERR("Invalid ifil chunk size: %d\n", info.mSize);
+ else
+ {
+ ALushort major = read_le16(stream);
+ ALushort minor = read_le16(stream);
+
+ list.mSize -= 4;
+ info.mSize -= 4;
+
+ if(major != 2)
+ ERROR_GOTO(error, "Unsupported SF2 format version: %d.%02d\n", major, minor);
+ TRACE("SF2 format version: %d.%02d\n", major, minor);
+
+ sfont.ifil = (major<<16) | minor;
+ }
+ }
+ else if(info.mCode == FOURCC('i','r','o','m'))
+ {
+ if(info.mSize == 0 || (info.mSize&1))
+ ERR("Invalid irom size: %d\n", info.mSize);
+ else
+ {
+ free(sfont.irom);
+ sfont.irom = calloc(1, info.mSize+1);
+ READ(stream, sfont.irom, info.mSize);
+
+ list.mSize -= info.mSize;
+ info.mSize -= info.mSize;
+
+ TRACE("SF2 ROM ID: %s\n", sfont.irom);
+ }
+ }
+ list.mSize -= info.mSize;
+ skip(stream, info.mSize);
+ }
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading INFO chunk\n");
+ if(sfont.ifil == 0)
+ ERROR_GOTO(error, "Missing ifil sub-chunk\n");
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('L','I','S','T'))
+ ERROR_GOTO(error, "Invalid Format, expected LIST (sdta) got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('s','d','t','a'))
+ ERROR_GOTO(error, "Invalid Format, expected sdta got '%c%c%c%c'\n", FOURCCARGS(ltype));
+ list.mSize -= 4;
+ {
+ ALbyte *ptr;
+ RiffHdr smpl;
+
+ RiffHdr_read(&smpl, stream);
+ if(smpl.mCode != FOURCC('s','m','p','l'))
+ ERROR_GOTO(error, "Invalid Format, expected smpl got '%c%c%c%c'\n", FOURCCARGS(smpl.mCode));
+ list.mSize -= 8;
+
+ if(smpl.mSize > list.mSize)
+ ERROR_GOTO(error, "Invalid Format, sample chunk size mismatch\n");
+
+ if(!(ptr=realloc(soundfont->Samples, smpl.mSize)))
+ SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, error);
+ soundfont->Samples = (ALshort*)ptr;
+ soundfont->NumSamples = smpl.mSize/2;
+
+ if(IS_LITTLE_ENDIAN)
+ READ(stream, ptr, smpl.mSize);
+ else
+ {
+ while(smpl.mSize > 0)
+ {
+ ALbyte buf[4096];
+ ALuint todo = minu(smpl.mSize, sizeof(buf));
+ ALuint i;
+
+ READ(stream, buf, todo);
+ for(i = 0;i < todo;i++)
+ ptr[i] = buf[i^1];
+ }
+ }
+ list.mSize -= smpl.mSize;
+
+ skip(stream, list.mSize);
+ }
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading sdta chunk\n");
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('L','I','S','T'))
+ ERROR_GOTO(error, "Invalid Format, expected LIST (pdta) got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((ltype=read_le32(stream)) != FOURCC('p','d','t','a'))
+ ERROR_GOTO(error, "Invalid Format, expected pdta got '%c%c%c%c'\n", FOURCCARGS(ltype));
+
+ //
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','h','d','r'))
+ ERROR_GOTO(error, "Invalid Format, expected phdr got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%38) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad phdr size: %u\n", list.mSize);
+ sfont.phdr_size = list.mSize/38;
+ sfont.phdr = calloc(sfont.phdr_size, sizeof(sfont.phdr[0]));
+ for(i = 0;i < sfont.phdr_size;i++)
+ PresetHeader_read(&sfont.phdr[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','b','a','g'))
+ ERROR_GOTO(error, "Invalid Format, expected pbag got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad pbag size: %u\n", list.mSize);
+ sfont.pbag_size = list.mSize/4;
+ sfont.pbag = calloc(sfont.pbag_size, sizeof(sfont.pbag[0]));
+ for(i = 0;i < sfont.pbag_size;i++)
+ Zone_read(&sfont.pbag[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','m','o','d'))
+ ERROR_GOTO(error, "Invalid Format, expected pmod got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%10) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad pmod size: %u\n", list.mSize);
+ sfont.pmod_size = list.mSize/10;
+ sfont.pmod = calloc(sfont.pmod_size, sizeof(sfont.pmod[0]));
+ for(i = 0;i < sfont.pmod_size;i++)
+ Modulator_read(&sfont.pmod[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('p','g','e','n'))
+ ERROR_GOTO(error, "Invalid Format, expected pgen got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad pgen size: %u\n", list.mSize);
+ sfont.pgen_size = list.mSize/4;
+ sfont.pgen = calloc(sfont.pgen_size, sizeof(sfont.pgen[0]));
+ for(i = 0;i < sfont.pgen_size;i++)
+ Generator_read(&sfont.pgen[i], stream);
+
+ //
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','n','s','t'))
+ ERROR_GOTO(error, "Invalid Format, expected inst got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%22) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad inst size: %u\n", list.mSize);
+ sfont.inst_size = list.mSize/22;
+ sfont.inst = calloc(sfont.inst_size, sizeof(sfont.inst[0]));
+ for(i = 0;i < sfont.inst_size;i++)
+ InstrumentHeader_read(&sfont.inst[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','b','a','g'))
+ ERROR_GOTO(error, "Invalid Format, expected ibag got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad ibag size: %u\n", list.mSize);
+ sfont.ibag_size = list.mSize/4;
+ sfont.ibag = calloc(sfont.ibag_size, sizeof(sfont.ibag[0]));
+ for(i = 0;i < sfont.ibag_size;i++)
+ Zone_read(&sfont.ibag[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','m','o','d'))
+ ERROR_GOTO(error, "Invalid Format, expected imod got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%10) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad imod size: %u\n", list.mSize);
+ sfont.imod_size = list.mSize/10;
+ sfont.imod = calloc(sfont.imod_size, sizeof(sfont.imod[0]));
+ for(i = 0;i < sfont.imod_size;i++)
+ Modulator_read(&sfont.imod[i], stream);
+
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('i','g','e','n'))
+ ERROR_GOTO(error, "Invalid Format, expected igen got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%4) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad igen size: %u\n", list.mSize);
+ sfont.igen_size = list.mSize/4;
+ sfont.igen = calloc(sfont.igen_size, sizeof(sfont.igen[0]));
+ for(i = 0;i < sfont.igen_size;i++)
+ Generator_read(&sfont.igen[i], stream);
+
+ //
+ RiffHdr_read(&list, stream);
+ if(list.mCode != FOURCC('s','h','d','r'))
+ ERROR_GOTO(error, "Invalid Format, expected shdr got '%c%c%c%c'\n", FOURCCARGS(list.mCode));
+ if((list.mSize%46) != 0 || list.mSize == 0)
+ ERROR_GOTO(error, "Invalid Format, bad shdr size: %u\n", list.mSize);
+ sfont.shdr_size = list.mSize/46;
+ sfont.shdr = calloc(sfont.shdr_size, sizeof(sfont.shdr[0]));
+ for(i = 0;i < sfont.shdr_size;i++)
+ SampleHeader_read(&sfont.shdr[i], stream);
+
+ if(READERR(stream) != 0)
+ ERROR_GOTO(error, "Error reading pdta chunk\n");
+
+ if(!ensureFontSanity(&sfont))
+ goto error;
+
+ presets = calloc(1, (sfont.phdr_size-1)*sizeof(presets[0]));
+ if(!presets)
+ ERROR_GOTO(error, "Error allocating presets\n");
+
+ for(i = 0;i < sfont.phdr_size-1;i++)
+ {
+ const Generator *gen, *gen_end;
+ const Modulator *mod, *mod_end;
+ const Zone *zone, *zone_end;
+ ALfontsound **sounds = NULL;
+ ALsizei sounds_size = 0;
+ GenModList gzone;
+
+ if(sfont.phdr[i+1].mZoneIdx == sfont.phdr[i].mZoneIdx)
+ continue;
+
+ GenModList_Construct(&gzone);
+ zone = sfont.pbag + sfont.phdr[i].mZoneIdx;
+ zone_end = sfont.pbag + sfont.phdr[i+1].mZoneIdx;
+ if(zone_end-zone > 1)
+ {
+ gen = sfont.pgen + zone->mGenIdx;
+ gen_end = sfont.pgen + (zone+1)->mGenIdx;
+
+ // If no generators, or last generator is not an instrument, this is a global zone
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 41)
+ break;
+ }
+
+ if(gen == gen_end)
+ {
+ gen = sfont.pgen + zone->mGenIdx;
+ gen_end = sfont.pgen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ GenModList_insertGen(&gzone, gen, AL_TRUE);
+
+ mod = sfont.pmod + zone->mModIdx;
+ mod_end = sfont.pmod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&gzone, mod);
+
+ zone++;
+ }
+ }
+
+ for(;zone != zone_end;zone++)
+ {
+ GenModList lzone = GenModList_clone(&gzone);
+
+ mod = sfont.pmod + zone->mModIdx;
+ mod_end = sfont.pmod + (zone+1)->mModIdx;
+ for(;mod != mod_end;mod++)
+ GenModList_insertMod(&lzone, mod);
+
+ gen = sfont.pgen + zone->mGenIdx;
+ gen_end = sfont.pgen + (zone+1)->mGenIdx;
+ for(;gen != gen_end;gen++)
+ {
+ if(gen->mGenerator == 41)
+ {
+ if(gen->mAmount >= sfont.inst_size-1)
+ ERR("Generator %ld has invalid instrument ID (%d of %d)\n",
+ (long)(gen-sfont.pgen), gen->mAmount, sfont.inst_size-1);
+ else
+ processInstrument(&sounds, &sounds_size, context,
+ &sfont.inst[gen->mAmount], &sfont.phdr[i], &sfont, &lzone);
+ break;
+ }
+ GenModList_insertGen(&lzone, gen, AL_TRUE);
+ }
+ GenModList_Destruct(&lzone);
+ }
+
+ if(sounds_size > 0)
+ {
+ ALsizei j;
+
+ presets[presets_size] = NewPreset(context);
+ presets[presets_size]->Preset = sfont.phdr[i].mPreset;
+ presets[presets_size]->Bank = sfont.phdr[i].mBank;
+
+ for(j = 0;j < sounds_size;j++)
+ IncrementRef(&sounds[j]->ref);
+ sounds = ExchangePtr((XchgPtr*)&presets[presets_size]->Sounds, sounds);
+ ExchangeInt(&presets[presets_size]->NumSounds, sounds_size);
+ presets_size++;
+ }
+ free(sounds);
+
+ GenModList_Destruct(&gzone);
+ }
+
+ for(i = 0;i < presets_size;i++)
+ IncrementRef(&presets[i]->ref);
+ presets = ExchangePtr((XchgPtr*)&soundfont->Presets, presets);
+ ExchangeInt(&soundfont->NumPresets, presets_size);
+
+ free(presets);
+
+ Soundfont_Destruct(&sfont);
+
+ return AL_TRUE;
+
+error:
+ if(presets)
+ {
+ ALCdevice *device = context->Device;
+ for(i = 0;i < presets_size;i++)
+ DeletePreset(presets[i], device);
+ free(presets);
+ }
+
+ Soundfont_Destruct(&sfont);
+
+ return AL_FALSE;
+}