diff options
Diffstat (limited to 'Alc/midi/soft.c')
-rw-r--r-- | Alc/midi/soft.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/Alc/midi/soft.c b/Alc/midi/soft.c new file mode 100644 index 00000000..cef3211b --- /dev/null +++ b/Alc/midi/soft.c @@ -0,0 +1,142 @@ + +#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 "alu.h" + +#include "midi/base.h" + + +typedef struct SSynth { + DERIVE_FROM_TYPE(MidiSynth); +} SSynth; + +static void SSynth_mixSamples(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); + +static void SSynth_Construct(SSynth *self, ALCdevice *device); +static void SSynth_Destruct(SSynth *self); +static DECLARE_FORWARD3(SSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) +static DECLARE_FORWARD1(SSynth, MidiSynth, void, setGain, ALfloat) +static DECLARE_FORWARD1(SSynth, MidiSynth, void, setState, ALenum) +static DECLARE_FORWARD(SSynth, MidiSynth, void, stop) +static DECLARE_FORWARD(SSynth, MidiSynth, void, reset) +static void SSynth_update(SSynth *self, ALCdevice *device); +static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]); +DECLARE_DEFAULT_ALLOCATORS(SSynth) +DEFINE_MIDISYNTH_VTABLE(SSynth); + + +static void SSynth_Construct(SSynth *self, ALCdevice *device) +{ + MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device); + SET_VTABLE2(SSynth, MidiSynth, self); +} + +static void SSynth_Destruct(SSynth* UNUSED(self)) +{ +} + + +static void SSynth_update(SSynth* UNUSED(self), ALCdevice* UNUSED(device)) +{ +} + + +static void SSynth_mixSamples(SSynth* UNUSED(self), ALuint UNUSED(SamplesToDo), ALfloat (*restrict DryBuffer)[BUFFERSIZE]) +{ + (void)DryBuffer; +} + + +static void SSynth_processQueue(SSynth *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 SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]) +{ + MidiSynth *synth = STATIC_CAST(MidiSynth, self); + ALenum state = synth->State; + ALuint64 curtime; + ALuint total = 0; + + if(state == AL_INITIAL) + return; + if(state != AL_PLAYING) + { + SSynth_mixSamples(self, SamplesToDo, DryBuffer); + return; + } + + curtime = MidiSynth_getTime(synth); + while(total < SamplesToDo) + { + ALuint64 time, diff; + ALint tonext; + + time = MidiSynth_getNextEvtTime(synth); + diff = maxu64(time, curtime) - curtime; + if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX) + { + /* If there's no pending event, or if it's more than 1 second + * away, do as many samples as we can. */ + tonext = INT_MAX; + } + else + { + /* Figure out how many samples until the next event. */ + tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES); + tonext -= total; + /* For efficiency reasons, try to mix a multiple of 64 samples + * (~1ms @ 44.1khz) before processing the next event. */ + tonext = (tonext+63) & ~63; + } + + if(tonext > 0) + { + ALuint todo = mini(tonext, SamplesToDo-total); + SSynth_mixSamples(self, todo, DryBuffer); + total += todo; + tonext -= todo; + } + if(total < SamplesToDo && tonext <= 0) + SSynth_processQueue(self, time); + } + + synth->SamplesDone += SamplesToDo; + synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES; + synth->SamplesDone %= synth->SampleRate; +} + + +MidiSynth *SSynth_create(ALCdevice *device) +{ + SSynth *synth; + + /* This option is temporary. Once this synth is in a more usable state, a + * more generic selector should be used. */ + if(!GetConfigValueBool("midi", "internal-synth", 0)) + { + TRACE("Not using internal MIDI synth\n"); + return NULL; + } + + synth = SSynth_New(sizeof(*synth)); + if(!synth) + { + ERR("Failed to allocate SSynth\n"); + return NULL; + } + SSynth_Construct(synth, device); + return STATIC_CAST(MidiSynth, synth); +} |