1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
#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 ALboolean DSynth_isSoundfont(DSynth *self, const char *filename);
static ALenum DSynth_loadSoundfont(DSynth *self, const char *filename);
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 ALboolean DSynth_isSoundfont(DSynth *self, const char *filename)
{
char buf[12];
FILE *f;
filename = MidiSynth_getFontName(STATIC_CAST(MidiSynth, self), filename);
if(!filename[0])
return AL_FALSE;
f = fopen(filename, "rb");
if(!f) return AL_FALSE;
if(fread(buf, 1, sizeof(buf), f) != sizeof(buf))
{
fclose(f);
return AL_FALSE;
}
if(memcmp(buf, "RIFF", 4) != 0 || memcmp(buf+8, "sfbk", 4) != 0)
{
fclose(f);
return AL_FALSE;
}
fclose(f);
return AL_TRUE;
}
static ALenum DSynth_loadSoundfont(DSynth *self, const char *filename)
{
if(!DSynth_isSoundfont(self, filename))
return AL_INVALID_VALUE;
return AL_NO_ERROR;
}
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);
}
|