aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2014-02-02 02:39:56 -0800
committerChris Robinson <[email protected]>2014-02-02 02:39:56 -0800
commit7c4339c195e29b17c8f76e6fbe9c8ee1b4f7b9b8 (patch)
tree728e3298bff7d7612ee6d7fcd88a2049d343d763 /Alc
parent755f161fc58aec0b541c7783ed716bd5c861a615 (diff)
Rework MIDI clock timing
It's best to avoid using doubles in the mixer since the FPU's set to single- precision mode. The new clock timing is similar to the device clock timing, and should hopefully be less prone to drift caused by fp rounding errors.
Diffstat (limited to 'Alc')
-rw-r--r--Alc/midi/base.c69
-rw-r--r--Alc/midi/base.h17
-rw-r--r--Alc/midi/dummy.c27
-rw-r--r--Alc/midi/fluidsynth.c72
4 files changed, 62 insertions, 123 deletions
diff --git a/Alc/midi/base.c b/Alc/midi/base.c
index 249bc2a8..1850a6c6 100644
--- a/Alc/midi/base.c
+++ b/Alc/midi/base.c
@@ -16,9 +16,6 @@
#include "alu.h"
-/* Nanosecond resolution */
-#define TICKS_PER_SECOND (1000000000)
-
/* MIDI events */
#define SYSEX_EVENT (0xF0)
@@ -129,12 +126,9 @@ void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
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;
+ self->ClockBase = 0;
+ self->SamplesDone = 0;
+ self->SampleRate = device->Frequency;
}
void MidiSynth_Destruct(MidiSynth *self)
@@ -195,29 +189,22 @@ void MidiSynth_stop(MidiSynth *self)
{
ResetEvtQueue(&self->EventQueue);
- self->LastEvtTime = 0;
- self->NextEvtTime = UINT64_MAX;
- self->SamplesSinceLast = 0.0;
- self->SamplesToNext = 0.0;
+ self->ClockBase = 0;
+ self->SamplesDone = 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_getTime(const MidiSynth *self);
extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
-void MidiSynth_setSampleRate(MidiSynth *self, ALdouble srate)
+void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate)
{
- ALdouble sampletickrate = srate / TICKS_PER_SECOND;
-
- self->SamplesSinceLast = self->SamplesSinceLast * sampletickrate / self->SamplesPerTick;
- self->SamplesToNext = self->SamplesToNext * sampletickrate / self->SamplesPerTick;
- self->SamplesPerTick = sampletickrate;
+ if(self->SampleRate != srate)
+ {
+ self->ClockBase += self->SamplesDone * MIDI_CLOCK_RES / self->SampleRate;
+ self->SamplesDone = 0;
+ self->SampleRate = srate;
+ }
}
extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
@@ -225,25 +212,11 @@ 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;
+ return InsertEvtQueue(&self->EventQueue, &entry);
}
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
@@ -261,18 +234,6 @@ ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *
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;
+ return err;
}
diff --git a/Alc/midi/base.h b/Alc/midi/base.h
index f900c941..4d13a054 100644
--- a/Alc/midi/base.h
+++ b/Alc/midi/base.h
@@ -23,17 +23,17 @@ typedef struct Reader {
ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
+#define MIDI_CLOCK_RES U64(1000000000)
+
+
struct MidiSynthVtable;
typedef struct MidiSynth {
EvtQueue EventQueue;
- ALuint64 LastEvtTime;
- ALuint64 NextEvtTime;
- ALdouble SamplesSinceLast;
- ALdouble SamplesToNext;
-
- ALdouble SamplesPerTick;
+ ALuint64 ClockBase;
+ ALuint SamplesDone;
+ ALuint SampleRate;
/* 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
@@ -59,14 +59,15 @@ inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&sel
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_getTime(const MidiSynth *self)
+{ return self->ClockBase + (self->SamplesDone*MIDI_CLOCK_RES/self->SampleRate); }
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);
+void MidiSynth_setSampleRate(MidiSynth *self, ALuint 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);
diff --git a/Alc/midi/dummy.c b/Alc/midi/dummy.c
index 71c03efb..79f82b87 100644
--- a/Alc/midi/dummy.c
+++ b/Alc/midi/dummy.c
@@ -49,30 +49,17 @@ static void DSynth_processQueue(DSynth *self, ALuint64 time)
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALuint64 curtime;
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;
- }
+ synth->SamplesDone += SamplesToDo;
+ synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
+ synth->SamplesDone %= synth->SampleRate;
+
+ curtime = MidiSynth_getTime(synth);
+ DSynth_processQueue(self, maxi64(curtime-1, 0));
}
diff --git a/Alc/midi/fluidsynth.c b/Alc/midi/fluidsynth.c
index 9d58f87b..d2db71e3 100644
--- a/Alc/midi/fluidsynth.c
+++ b/Alc/midi/fluidsynth.c
@@ -640,24 +640,12 @@ static void FSynth_setState(FSynth *self, ALenum state)
static void FSynth_stop(FSynth *self)
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
+ ALuint64 curtime;
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;
- }
+ curtime = MidiSynth_getTime(synth);
+ FSynth_processQueue(self, curtime);
/* All notes off */
for(chan = 0;chan < 16;chan++)
@@ -759,6 +747,7 @@ static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALenum state = synth->State;
+ ALuint64 curtime;
ALuint total = 0;
if(state == AL_INITIAL)
@@ -770,41 +759,42 @@ static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict
return;
}
+ curtime = MidiSynth_getTime(synth);
while(total < SamplesToDo)
{
- if(synth->SamplesToNext >= 1.0)
- {
- ALuint todo = minu(SamplesToDo - total, fastf2u(synth->SamplesToNext));
+ ALuint64 time, diff;
+ ALint tonext;
- 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;
+ 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
{
- 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);
+ /* Figure out how many samples until the next event. */
+ tonext = (ALint)(((diff * synth->SampleRate)+(MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
+ tonext -= total;
+ }
- synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
- if(synth->NextEvtTime != UINT64_MAX)
- synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
+ if(tonext > 0)
+ {
+ ALuint todo = mini(tonext, SamplesToDo-total);
+ fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1,
+ DryBuffer[FrontRight], total, 1);
+ total += todo;
+ tonext -= todo;
}
+ if(total < SamplesToDo && tonext == 0)
+ FSynth_processQueue(self, time);
}
+
+ synth->SamplesDone += SamplesToDo;
+ synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
+ synth->SamplesDone %= synth->SampleRate;
}