/
native_midi_common.c
410 lines (343 loc) · 9.2 KB
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
/*
native_midi: Hardware Midi support for the SDL_mixer library
Copyright (C) 2000,2001 Florian 'Proff' Schulze
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Florian 'Proff' Schulze
florian.proff.schulze@gmx.net
*/
#include "native_midi_common.h"
26
#include "../SDL_mixer.h"
27
28
29
30
31
32
#include <stdlib.h>
#include <string.h>
#include <limits.h>
33
34
/* The maximum number of midi tracks that we can handle
#define MIDI_TRACKS 32 */
35
36
37
38
39
40
41
42
43
44
45
46
47
/* A single midi track as read from the midi file */
typedef struct
{
Uint8 *data; /* MIDI message stream */
int len; /* length of the track data */
} MIDITrack;
/* A midi file, stripped down to the absolute minimum - divison & track data */
typedef struct
{
int division; /* number of pulses per quarter note (ppqn) */
48
49
int nTracks; /* number of tracks */
MIDITrack *track; /* tracks */
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
} MIDIFile;
/* Some macros that help us stay endianess-independant */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define BE_SHORT(x) (x)
#define BE_LONG(x) (x)
#else
#define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
#define BE_LONG(x) ((((x)&0x0000FF)<<24) | \
(((x)&0x00FF00)<<8) | \
(((x)&0xFF0000)>>8) | \
(((x)>>24)&0xFF))
#endif
/* Get Variable Length Quantity */
static int GetVLQ(MIDITrack *track, int *currentPos)
{
int l = 0;
Uint8 c;
while(1)
{
c = track->data[*currentPos];
(*currentPos)++;
l += (c & 0x7f);
if (!(c & 0x80))
return l;
l <<= 7;
}
}
/* Create a single MIDIEvent */
static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b)
{
MIDIEvent *newEvent;
newEvent = calloc(1, sizeof(MIDIEvent));
if (newEvent)
{
newEvent->time = time;
newEvent->status = event;
newEvent->data[0] = a;
newEvent->data[1] = b;
}
else
Mix_SetError("Out of memory");
return newEvent;
}
/* Convert a single midi track to a list of MIDIEvents */
static MIDIEvent *MIDITracktoStream(MIDITrack *track)
{
Uint32 atime = 0;
Uint32 len = 0;
108
Uint8 event,type,a,b;
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
Uint8 laststatus = 0;
Uint8 lastchan = 0;
int currentPos = 0;
int end = 0;
MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
MIDIEvent *currentEvent = head;
while (!end)
{
if (currentPos >= track->len)
break; /* End of data stream reached */
atime += GetVLQ(track, ¤tPos);
event = track->data[currentPos++];
/* Handle SysEx seperatly */
if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX)
{
if (event == 0xFF)
{
type = track->data[currentPos];
currentPos++;
switch(type)
{
case 0x2f: /* End of data marker */
end = 1;
case 0x51: /* Tempo change */
/*
a=track->data[currentPos];
b=track->data[currentPos+1];
c=track->data[currentPos+2];
AddEvent(song, atime, MEVT_TEMPO, c, b, a);
*/
break;
}
}
else
type = 0;
len = GetVLQ(track, ¤tPos);
/* Create an event and attach the extra data, if any */
currentEvent->next = CreateEvent(atime, event, type, 0);
currentEvent = currentEvent->next;
if (NULL == currentEvent)
{
FreeMIDIEventList(head);
return NULL;
}
if (len)
{
currentEvent->extraLen = len;
currentEvent->extraData = malloc(len);
memcpy(currentEvent->extraData, &(track->data[currentPos]), len);
currentPos += len;
}
}
else
{
a = event;
if (a & 0x80) /* It's a status byte */
{
/* Extract channel and status information */
lastchan = a & 0x0F;
laststatus = (a>>4) & 0x0F;
/* Read the next byte which should always be a data byte */
a = track->data[currentPos++] & 0x7F;
}
switch(laststatus)
{
case MIDI_STATUS_NOTE_OFF:
case MIDI_STATUS_NOTE_ON: /* Note on */
case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */
case MIDI_STATUS_CONTROLLER: /* Control change */
case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */
b = track->data[currentPos++] & 0x7F;
currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b);
currentEvent = currentEvent->next;
if (NULL == currentEvent)
{
FreeMIDIEventList(head);
return NULL;
}
break;
case MIDI_STATUS_PROG_CHANGE: /* Program change */
case MIDI_STATUS_PRESSURE: /* Channel pressure */
a &= 0x7f;
currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0);
currentEvent = currentEvent->next;
if (NULL == currentEvent)
{
FreeMIDIEventList(head);
return NULL;
}
break;
default: /* Sysex already handled above */
break;
}
}
}
212
213
214
215
216
217
218
219
220
221
222
223
224
currentEvent = head->next;
free(head); /* release the dummy head event */
return currentEvent;
}
/*
* Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents.
* To do so, first convert the tracks seperatly, then interweave the resulting
* MIDIEvent-Lists to one big list.
*/
static MIDIEvent *MIDItoStream(MIDIFile *mididata)
{
225
MIDIEvent **track;
226
227
228
MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
MIDIEvent *currentEvent = head;
int trackID;
229
230
if (NULL == head)
231
return NULL;
232
233
234
235
track = (MIDIEvent**) calloc(1, sizeof(MIDIEvent*) * mididata->nTracks);
if (NULL == head)
return NULL;
236
237
/* First, convert all tracks to MIDIEvent lists */
238
for (trackID = 0; trackID < mididata->nTracks; trackID++)
239
240
241
242
243
244
track[trackID] = MIDITracktoStream(&mididata->track[trackID]);
/* Now, merge the lists. */
/* TODO */
while(1)
{
245
Uint32 lowestTime = INT_MAX;
246
247
248
int currentTrackID = -1;
/* Find the next event */
249
for (trackID = 0; trackID < mididata->nTracks; trackID++)
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
{
if (track[trackID] && (track[trackID]->time < lowestTime))
{
currentTrackID = trackID;
lowestTime = track[currentTrackID]->time;
}
}
/* Check if we processes all events */
if (currentTrackID == -1)
break;
currentEvent->next = track[currentTrackID];
track[currentTrackID] = track[currentTrackID]->next;
currentEvent = currentEvent->next;
lowestTime = 0;
}
/* Make sure the list is properly terminated */
currentEvent->next = 0;
currentEvent = head->next;
275
free(track);
276
277
278
279
free(head); /* release the dummy head event */
return currentEvent;
}
280
static int ReadMIDIFile(MIDIFile *mididata, SDL_RWops *rw)
281
282
283
284
285
286
287
288
289
290
{
int i = 0;
Uint32 ID;
Uint32 size;
Uint16 format;
Uint16 tracks;
Uint16 division;
if (!mididata)
return 0;
291
if (!rw)
292
293
294
return 0;
/* Make sure this is really a MIDI file */
295
SDL_RWread(rw, &ID, 1, 4);
296
297
298
299
if (BE_LONG(ID) != 'MThd')
return 0;
/* Header size must be 6 */
300
SDL_RWread(rw, &size, 1, 4);
301
302
303
304
305
size = BE_LONG(size);
if (size != 6)
return 0;
/* We only support format 0 and 1, but not 2 */
306
SDL_RWread(rw, &format, 1, 2);
307
308
309
310
format = BE_SHORT(format);
if (format != 0 && format != 1)
return 0;
311
SDL_RWread(rw, &tracks, 1, 2);
312
tracks = BE_SHORT(tracks);
313
314
315
316
317
318
319
320
321
322
mididata->nTracks = tracks;
/* Allocate tracks */
mididata->track = (MIDITrack*) calloc(1, sizeof(MIDITrack) * mididata->nTracks);
if (NULL == mididata->track)
{
Mix_SetError("Out of memory");
goto bail;
}
323
/* Retrieve the PPQN value, needed for playback */
324
SDL_RWread(rw, &division, 1, 2);
325
326
327
328
329
mididata->division = BE_SHORT(division);
for (i=0; i<tracks; i++)
{
330
331
SDL_RWread(rw, &ID, 1, 4); /* We might want to verify this is MTrk... */
SDL_RWread(rw, &size, 1, 4);
332
333
334
335
336
337
338
339
size = BE_LONG(size);
mididata->track[i].len = size;
mididata->track[i].data = malloc(size);
if (NULL == mididata->track[i].data)
{
Mix_SetError("Out of memory");
goto bail;
}
340
SDL_RWread(rw, mididata->track[i].data, 1, size);
341
342
343
344
345
346
347
348
349
350
351
352
353
}
return 1;
bail:
for(;i >= 0; i--)
{
if (mididata->track[i].data)
free(mididata->track[i].data);
}
return 0;
}
354
MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division)
355
356
357
358
359
360
361
362
363
364
{
MIDIFile *mididata = NULL;
MIDIEvent *eventList;
int trackID;
mididata = calloc(1, sizeof(MIDIFile));
if (!mididata)
return NULL;
/* Open the file */
365
if ( rw != NULL )
366
367
{
/* Read in the data */
368
if ( ! ReadMIDIFile(mididata, rw))
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
{
free(mididata);
return NULL;
}
}
else
{
free(mididata);
return NULL;
}
if (division)
*division = mididata->division;
eventList = MIDItoStream(mididata);
385
for(trackID = 0; trackID < mididata->nTracks; trackID++)
386
387
388
389
{
if (mididata->track[trackID].data)
free(mididata->track[trackID].data);
}
390
391
free(mididata->track);
free(mididata);
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
return eventList;
}
void FreeMIDIEventList(MIDIEvent *head)
{
MIDIEvent *cur, *next;
cur = head;
while (cur)
{
next = cur->next;
if (cur->extraData)
free (cur->extraData);
free (cur);
cur = next;
}
}