aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OpenAL32/Include/alMidi.h64
-rw-r--r--OpenAL32/alMidi.c97
2 files changed, 161 insertions, 0 deletions
diff --git a/OpenAL32/Include/alMidi.h b/OpenAL32/Include/alMidi.h
new file mode 100644
index 00000000..b6504706
--- /dev/null
+++ b/OpenAL32/Include/alMidi.h
@@ -0,0 +1,64 @@
+#ifndef ALMIDI_H
+#define ALMIDI_H
+
+#include "alMain.h"
+#include "evtqueue.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct MidiSynthVtable;
+
+typedef struct MidiSynth {
+ EvtQueue EventQueue;
+
+ ALuint64 LastEvtTime;
+ ALuint64 NextEvtTime;
+ ALdouble SamplesSinceLast;
+ ALdouble SamplesToNext;
+
+ ALuint SampleRate;
+ ALdouble SamplesPerTick;
+
+ volatile ALenum State;
+
+ char *FontName;
+
+ const struct MidiSynthVtable *vtbl;
+} MidiSynth;
+
+
+struct MidiSynthVtable {
+ void (*const Destruct)(MidiSynth *self);
+
+ void (*const setState)(MidiSynth *self, ALenum state);
+ 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_THUNK1(T, MidiSynth, void, setState, ALenum) \
+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_setState, \
+ T##_MidiSynth_update, \
+ T##_MidiSynth_process, \
+ \
+ T##_MidiSynth_Delete, \
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ALMIDI_H */
diff --git a/OpenAL32/alMidi.c b/OpenAL32/alMidi.c
index 773f4cac..a2f2eb12 100644
--- a/OpenAL32/alMidi.c
+++ b/OpenAL32/alMidi.c
@@ -5,7 +5,104 @@
#include <string.h>
#include <limits.h>
+#include "alMidi.h"
+#include "alMain.h"
+#include "alError.h"
#include "evtqueue.h"
+#include "rwlock.h"
+#include "alu.h"
+
+
+/* Microsecond resolution */
+#define TICKS_PER_SECOND (1000000)
+
+static void MidiSynth_Construct(MidiSynth *self, ALCdevice *device);
+static void MidiSynth_Destruct(MidiSynth *self);
+static inline void MidiSynth_setState(MidiSynth *self, ALenum state);
+static inline ALuint MidiSynth_getTime(const MidiSynth *self);
+static inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
+static void MidiSynth_update(MidiSynth *self, ALCdevice *device);
+static void MidiSynth_updateSpeed(MidiSynth *self);
+static ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
+
+
+static void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
+{
+ InitEvtQueue(&self->EventQueue);
+
+ self->LastEvtTime = 0;
+ self->NextEvtTime = UINT64_MAX;
+ self->SamplesSinceLast = 0.0;
+ self->SamplesToNext = 0.0;
+
+ self->State = AL_INITIAL;
+
+ self->FontName = NULL;
+
+ self->SampleRate = device->Frequency;
+ self->SamplesPerTick = (ALdouble)self->SampleRate / TICKS_PER_SECOND;
+ MidiSynth_updateSpeed(self);
+}
+
+static void MidiSynth_Destruct(MidiSynth *self)
+{
+ free(self->FontName);
+ self->FontName = NULL;
+
+ ResetEvtQueue(&self->EventQueue);
+}
+
+static inline void MidiSynth_setState(MidiSynth *self, ALenum state)
+{
+ ExchangeInt(&self->State, state);
+}
+
+static inline ALuint MidiSynth_getTime(const MidiSynth *self)
+{
+ ALuint time = self->LastEvtTime + (self->SamplesSinceLast/self->SamplesPerTick);
+ return clampu(time, self->LastEvtTime, self->NextEvtTime);
+}
+
+static 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;
+}
+
+static void MidiSynth_update(MidiSynth *self, ALCdevice *device)
+{
+ self->SampleRate = device->Frequency;
+ MidiSynth_updateSpeed(self);
+}
+
+static void MidiSynth_updateSpeed(MidiSynth *self)
+{
+ ALdouble sampletickrate = (ALdouble)self->SampleRate / TICKS_PER_SECOND;
+
+ self->SamplesSinceLast = self->SamplesSinceLast * sampletickrate / self->SamplesPerTick;
+ self->SamplesToNext = self->SamplesToNext * sampletickrate / self->SamplesPerTick;
+ self->SamplesPerTick = sampletickrate;
+}
+
+static ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
+{
+ MidiEvent entry = { time, event, { param1, param2 } };
+ ALenum err;
+
+ 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;
+}
void InitEvtQueue(EvtQueue *queue)