diff options
Diffstat (limited to 'Alc/midi')
-rw-r--r-- | Alc/midi/base.c | 278 | ||||
-rw-r--r-- | Alc/midi/base.h | 128 | ||||
-rw-r--r-- | Alc/midi/dummy.c | 95 | ||||
-rw-r--r-- | Alc/midi/fluidsynth.c | 843 | ||||
-rw-r--r-- | Alc/midi/sf2load.c | 1436 |
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; +} |