Skip to content

Latest commit

 

History

History
410 lines (343 loc) · 9.2 KB

native_midi_common.c

File metadata and controls

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"
Apr 30, 2006
Apr 30, 2006
26
#include "../SDL_mixer.h"
27
28
29
30
31
32
#include <stdlib.h>
#include <string.h>
#include <limits.h>
Feb 21, 2003
Feb 21, 2003
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) */
Feb 21, 2003
Feb 21, 2003
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;
Sep 6, 2001
Sep 6, 2001
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, &currentPos);
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, &currentPos);
/* 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;
}
}
}
Sep 6, 2001
Sep 6, 2001
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)
{
Feb 21, 2003
Feb 21, 2003
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;
Sep 6, 2001
Sep 6, 2001
229
Feb 21, 2003
Feb 21, 2003
230
if (NULL == head)
231
return NULL;
Feb 21, 2003
Feb 21, 2003
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 */
Feb 21, 2003
Feb 21, 2003
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)
{
Sep 6, 2001
Sep 6, 2001
245
Uint32 lowestTime = INT_MAX;
246
247
248
int currentTrackID = -1;
/* Find the next event */
Feb 21, 2003
Feb 21, 2003
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;
Feb 21, 2003
Feb 21, 2003
275
free(track);
276
277
278
279
free(head); /* release the dummy head event */
return currentEvent;
}
Jul 16, 2005
Jul 16, 2005
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;
Jul 16, 2005
Jul 16, 2005
291
if (!rw)
292
293
294
return 0;
/* Make sure this is really a MIDI file */
Jul 16, 2005
Jul 16, 2005
295
SDL_RWread(rw, &ID, 1, 4);
296
297
298
299
if (BE_LONG(ID) != 'MThd')
return 0;
/* Header size must be 6 */
Jul 16, 2005
Jul 16, 2005
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 */
Jul 16, 2005
Jul 16, 2005
306
SDL_RWread(rw, &format, 1, 2);
307
308
309
310
format = BE_SHORT(format);
if (format != 0 && format != 1)
return 0;
Jul 16, 2005
Jul 16, 2005
311
SDL_RWread(rw, &tracks, 1, 2);
312
tracks = BE_SHORT(tracks);
Feb 21, 2003
Feb 21, 2003
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 */
Jul 16, 2005
Jul 16, 2005
324
SDL_RWread(rw, &division, 1, 2);
325
326
327
328
329
mididata->division = BE_SHORT(division);
for (i=0; i<tracks; i++)
{
Jul 16, 2005
Jul 16, 2005
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;
}
Jul 16, 2005
Jul 16, 2005
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;
}
Jul 16, 2005
Jul 16, 2005
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 */
Jul 16, 2005
Jul 16, 2005
365
if ( rw != NULL )
366
367
{
/* Read in the data */
Jul 16, 2005
Jul 16, 2005
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);
Feb 21, 2003
Feb 21, 2003
385
for(trackID = 0; trackID < mididata->nTracks; trackID++)
386
387
388
389
{
if (mididata->track[trackID].data)
free(mididata->track[trackID].data);
}
Feb 21, 2003
Feb 21, 2003
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;
}
}