drop 'req' from gas macro, not available in binutils 2.16
[kugel-rb.git] / apps / plugins / midi / midifile.c
blobb5d9c5b6d172e2502af04e0079367d11fbc56a88
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Stepan Moskovchenko
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "midiutil.h"
24 struct Track * readTrack(int file);
25 int readID(int file);
27 struct MIDIfile midi_file IBSS_ATTR;
29 struct MIDIfile * loadFile(const char * filename)
31 struct MIDIfile * mfload;
32 int file = rb->open (filename, O_RDONLY);
34 if(file < 0)
36 printf("Could not open file");
37 return NULL;
40 mfload = &midi_file;
42 rb->memset(mfload, 0, sizeof(struct MIDIfile));
44 int fileID = readID(file);
45 if(fileID != ID_MTHD)
47 if(fileID == ID_RIFF)
49 printf("Detected RMID file");
50 printf("Looking for MThd header");
51 char dummy[17];
52 rb->read(file, &dummy, 16);
53 if(readID(file) != ID_MTHD)
55 rb->close(file);
56 printf("Invalid MIDI header within RIFF.");
57 return NULL;
60 } else
62 rb->close(file);
63 printf("Invalid file header chunk.");
64 return NULL;
68 if(readFourBytes(file)!=6)
70 rb->close(file);
71 printf("Header chunk size invalid.");
72 return NULL;
75 if(readTwoBytes(file)==2)
77 rb->close(file);
78 printf("MIDI file type 2 not supported");
79 return NULL;
82 mfload->numTracks = readTwoBytes(file);
83 mfload->div = readTwoBytes(file);
85 int track=0;
87 printf("File has %d tracks.", mfload->numTracks);
89 while(! eof(file) && track < mfload->numTracks)
91 unsigned char id = readID(file);
94 if(id == ID_EOF)
96 if(mfload->numTracks != track)
98 printf("Warning: file claims to have %d tracks. I only see %d here.", mfload->numTracks, track);
99 mfload->numTracks = track;
101 rb->close(file);
102 return mfload;
105 if(id == ID_MTRK)
107 mfload->tracks[track] = readTrack(file);
108 track++;
109 } else
111 printf("SKIPPING TRACK");
112 int len = readFourBytes(file);
113 while(--len)
114 readChar(file);
118 rb->close(file);
119 return mfload;
123 /* Global again. Not static. What if track 1 ends on a running status event
124 * and then track 2 starts loading */
126 int rStatus = 0;
127 /* Returns 0 if done, 1 if keep going */
128 int readEvent(int file, void * dest)
130 struct Event dummy;
131 struct Event * ev = (struct Event *) dest;
133 if(ev == NULL)
134 ev = &dummy; /* If we are just counting events instead of loading them */
136 ev->delta = readVarData(file);
139 int t=readChar(file);
141 if((t&0x80) == 0x80) /* if not a running status event */
143 ev->status = t;
144 if(t == 0xFF)
146 ev->d1 = readChar(file);
147 ev->len = readVarData(file);
149 /* Allocate and read in the data block */
150 if(dest != NULL)
152 /* Null-terminate for text events */
153 ev->evData = malloc(ev->len+1); /* Extra byte for the null termination */
155 rb->read(file, ev->evData, ev->len);
156 ev->evData[ev->len] = 0;
158 switch(ev->d1)
160 case 0x01: /* Generic text */
162 printf("Text: %s", ev->evData);
163 break;
166 case 0x02: /* A copyright string within the file */
168 printf("Copyright: %s", ev->evData);
169 break;
172 case 0x03: /* Sequence of track name */
174 printf("Name: %s", ev->evData);
175 break;
178 case 0x04: /* Instrument (synth) name */
180 printf("Instrument: %s", ev->evData);
181 break;
184 case 0x05: /* Lyrics. These appear on the tracks at the right times */
185 { /* Usually only a small 'piece' of the lyrics. */
186 /* Maybe the sequencer should print these at play time? */
187 printf("Lyric: %s", ev->evData);
188 break;
191 case 0x06: /* Text marker */
193 printf("Marker: %s", ev->evData);
194 break;
198 case 0x07: /* Cue point */
200 printf("Cue point: %s", ev->evData);
201 break;
204 case 0x08: /* Program name */
206 printf("Patch: %s", ev->evData);
207 break;
211 case 0x09: /* Device name. Very much irrelevant here, though. */
213 printf("Port: %s", ev->evData);
214 break;
218 else
221 * Don't allocate anything, just see how much it would take
222 * To make memory usage efficient
224 unsigned int a=0;
225 for(a=0; a<ev->len; a++)
226 readChar(file); //Skip skip
229 if(ev->d1 == 0x2F)
231 return 0; /* Termination meta-event */
233 } else /* If part of a running status event */
235 rStatus = t;
236 ev->status = t;
237 ev->d1 = readChar(file);
239 if ( ((t & 0xF0) != 0xD0) && ((t & 0xF0) != 0xC0) && ((t & 0xF0) > 0x40) )
241 ev->d2 = readChar(file);
242 } else
243 ev->d2 = 127;
245 } else /* Running Status */
247 ev->status = rStatus;
248 ev->d1 = t;
249 if ( ((rStatus & 0xF0) != 0xD0) && ((rStatus & 0xF0) != 0xC0) && ((rStatus & 0xF0) > 0x40) )
251 ev->d2 = readChar(file);
252 } else
253 ev->d2 = 127;
255 return 1;
258 int curr_track = 0;
259 struct Track tracks[48] IBSS_ATTR;
261 struct Track * readTrack(int file)
263 struct Track * trk = &tracks[curr_track++];
264 rb->memset(trk, 0, sizeof(struct Track));
266 trk->size = readFourBytes(file);
267 trk->pos = 0;
268 trk->delta = 0;
270 int numEvents=0;
272 int pos = rb->lseek(file, 0, SEEK_CUR);
274 while(readEvent(file, NULL)) /* Memory saving technique */
275 numEvents++; /* Attempt to read in events, count how many */
276 /* THEN allocate memory and read them in */
277 rb->lseek(file, pos, SEEK_SET);
279 int trackSize = (numEvents+1) * sizeof(struct Event);
280 void * dataPtr = malloc(trackSize);
281 trk->dataBlock = dataPtr;
283 numEvents=0;
285 while(readEvent(file, dataPtr))
287 if(trackSize < dataPtr-trk->dataBlock)
289 printf("Track parser memory out of bounds");
290 exit(1);
292 dataPtr+=sizeof(struct Event);
293 numEvents++;
295 trk->numEvents = numEvents;
297 return trk;
300 int readID(int file)
302 char id[5];
303 id[4]=0;
304 BYTE a;
306 for(a=0; a<4; a++)
307 id[a]=readChar(file);
308 if(eof(file))
310 printf("End of file reached.");
311 return ID_EOF;
313 if(rb->strcmp(id, "MThd")==0)
314 return ID_MTHD;
315 if(rb->strcmp(id, "MTrk")==0)
316 return ID_MTRK;
317 if(rb->strcmp(id, "RIFF")==0)
318 return ID_RIFF;
319 return ID_UNKNOWN;
323 int readFourBytes(int file)
325 int data=0;
326 BYTE a=0;
327 for(a=0; a<4; a++)
328 data=(data<<8)+readChar(file);
329 return data;
332 int readTwoBytes(int file)
334 int data=(readChar(file)<<8)+readChar(file);
335 return data;
338 /* This came from the MIDI file format guide */
339 int readVarData(int file)
341 unsigned int value;
342 char c;
343 if ( (value = readChar(file)) & 0x80 )
345 value &= 0x7F;
348 value = (value << 7) + ((c = readChar(file)) & 0x7F);
349 } while (c & 0x80);
351 return(value);
356 void unloadFile(struct MIDIfile * mf)
358 if(mf == NULL)
359 return;
360 int a=0;
361 //Unload each track
362 for(a=0; a<mf->numTracks; a++)
364 int b=0;
366 if(mf->tracks[a] != NULL)
367 for(b=0; b<mf->tracks[a]->numEvents; b++)
369 if(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData!=NULL)
370 free(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData);
373 if(mf->tracks[a]!=NULL && mf->tracks[a]->dataBlock != NULL)
374 free(mf->tracks[a]->dataBlock); //Unload the event block
376 if(mf->tracks[a]!=NULL)
377 free(mf->tracks[a]); //Unload the track structure itself
379 free(mf); //Unload the main struct