native_midi_common.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /*
  2. native_midi: Hardware Midi support for the SDL_mixer library
  3. Copyright (C) 2000,2001 Florian 'Proff' Schulze
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public
  13. License along with this library; if not, write to the Free
  14. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. Florian 'Proff' Schulze
  16. florian.proff.schulze@gmx.net
  17. */
  18. #include "native_midi_common.h"
  19. #include "SDL.h"
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <limits.h>
  23. /* The maximum number of midi tracks that we can handle
  24. #define MIDI_TRACKS 32 */
  25. /* A single midi track as read from the midi file */
  26. typedef struct
  27. {
  28. Uint8 *data; /* MIDI message stream */
  29. int len; /* length of the track data */
  30. } MIDITrack;
  31. /* A midi file, stripped down to the absolute minimum - divison & track data */
  32. typedef struct
  33. {
  34. int division; /* number of pulses per quarter note (ppqn) */
  35. int nTracks; /* number of tracks */
  36. MIDITrack *track; /* tracks */
  37. } MIDIFile;
  38. /* Some macros that help us stay endianess-independant */
  39. #if SDL_BYTEORDER == SDL_BIG_ENDIAN
  40. #define BE_SHORT(x) (x)
  41. #define BE_LONG(x) (x)
  42. #else
  43. #define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
  44. #define BE_LONG(x) ((((x)&0x0000FF)<<24) | \
  45. (((x)&0x00FF00)<<8) | \
  46. (((x)&0xFF0000)>>8) | \
  47. (((x)>>24)&0xFF))
  48. #endif
  49. /* Get Variable Length Quantity */
  50. static int GetVLQ(MIDITrack *track, int *currentPos)
  51. {
  52. int l = 0;
  53. Uint8 c;
  54. while(1)
  55. {
  56. c = track->data[*currentPos];
  57. (*currentPos)++;
  58. l += (c & 0x7f);
  59. if (!(c & 0x80))
  60. return l;
  61. l <<= 7;
  62. }
  63. }
  64. /* Create a single MIDIEvent */
  65. static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b)
  66. {
  67. MIDIEvent *newEvent;
  68. newEvent = calloc(1, sizeof(MIDIEvent));
  69. if (newEvent)
  70. {
  71. newEvent->time = time;
  72. newEvent->status = event;
  73. newEvent->data[0] = a;
  74. newEvent->data[1] = b;
  75. }
  76. else
  77. printf("Out of memory");
  78. return newEvent;
  79. }
  80. /* Convert a single midi track to a list of MIDIEvents */
  81. static MIDIEvent *MIDITracktoStream(MIDITrack *track)
  82. {
  83. Uint32 atime = 0;
  84. Uint32 len = 0;
  85. Uint8 event,type,a,b;
  86. Uint8 laststatus = 0;
  87. Uint8 lastchan = 0;
  88. int currentPos = 0;
  89. int end = 0;
  90. MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
  91. MIDIEvent *currentEvent = head;
  92. while (!end)
  93. {
  94. if (currentPos >= track->len)
  95. break; /* End of data stream reached */
  96. atime += GetVLQ(track, &currentPos);
  97. event = track->data[currentPos++];
  98. /* Handle SysEx seperatly */
  99. if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX)
  100. {
  101. if (event == 0xFF)
  102. {
  103. type = track->data[currentPos];
  104. currentPos++;
  105. switch(type)
  106. {
  107. case 0x2f: /* End of data marker */
  108. end = 1;
  109. case 0x51: /* Tempo change */
  110. /*
  111. a=track->data[currentPos];
  112. b=track->data[currentPos+1];
  113. c=track->data[currentPos+2];
  114. AddEvent(song, atime, MEVT_TEMPO, c, b, a);
  115. */
  116. break;
  117. }
  118. }
  119. else
  120. type = 0;
  121. len = GetVLQ(track, &currentPos);
  122. /* Create an event and attach the extra data, if any */
  123. currentEvent->next = CreateEvent(atime, event, type, 0);
  124. currentEvent = currentEvent->next;
  125. if (NULL == currentEvent)
  126. {
  127. FreeMIDIEventList(head);
  128. return NULL;
  129. }
  130. if (len)
  131. {
  132. currentEvent->extraLen = len;
  133. currentEvent->extraData = malloc(len);
  134. memcpy(currentEvent->extraData, &(track->data[currentPos]), len);
  135. currentPos += len;
  136. }
  137. }
  138. else
  139. {
  140. a = event;
  141. if (a & 0x80) /* It's a status byte */
  142. {
  143. /* Extract channel and status information */
  144. lastchan = a & 0x0F;
  145. laststatus = (a>>4) & 0x0F;
  146. /* Read the next byte which should always be a data byte */
  147. a = track->data[currentPos++] & 0x7F;
  148. }
  149. switch(laststatus)
  150. {
  151. case MIDI_STATUS_NOTE_OFF:
  152. case MIDI_STATUS_NOTE_ON: /* Note on */
  153. case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */
  154. case MIDI_STATUS_CONTROLLER: /* Control change */
  155. case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */
  156. b = track->data[currentPos++] & 0x7F;
  157. currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b);
  158. currentEvent = currentEvent->next;
  159. if (NULL == currentEvent)
  160. {
  161. FreeMIDIEventList(head);
  162. return NULL;
  163. }
  164. break;
  165. case MIDI_STATUS_PROG_CHANGE: /* Program change */
  166. case MIDI_STATUS_PRESSURE: /* Channel pressure */
  167. a &= 0x7f;
  168. currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0);
  169. currentEvent = currentEvent->next;
  170. if (NULL == currentEvent)
  171. {
  172. FreeMIDIEventList(head);
  173. return NULL;
  174. }
  175. break;
  176. default: /* Sysex already handled above */
  177. break;
  178. }
  179. }
  180. }
  181. currentEvent = head->next;
  182. free(head); /* release the dummy head event */
  183. return currentEvent;
  184. }
  185. /*
  186. * Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents.
  187. * To do so, first convert the tracks seperatly, then interweave the resulting
  188. * MIDIEvent-Lists to one big list.
  189. */
  190. static MIDIEvent *MIDItoStream(MIDIFile *mididata)
  191. {
  192. MIDIEvent **track;
  193. MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */
  194. MIDIEvent *currentEvent = head;
  195. int trackID;
  196. if (NULL == head)
  197. return NULL;
  198. track = (MIDIEvent**) calloc(1, sizeof(MIDIEvent*) * mididata->nTracks);
  199. if (NULL == head)
  200. return NULL;
  201. /* First, convert all tracks to MIDIEvent lists */
  202. for (trackID = 0; trackID < mididata->nTracks; trackID++)
  203. track[trackID] = MIDITracktoStream(&mididata->track[trackID]);
  204. /* Now, merge the lists. */
  205. /* TODO */
  206. while(1)
  207. {
  208. Uint32 lowestTime = INT_MAX;
  209. int currentTrackID = -1;
  210. /* Find the next event */
  211. for (trackID = 0; trackID < mididata->nTracks; trackID++)
  212. {
  213. if (track[trackID] && (track[trackID]->time < lowestTime))
  214. {
  215. currentTrackID = trackID;
  216. lowestTime = track[currentTrackID]->time;
  217. }
  218. }
  219. /* Check if we processes all events */
  220. if (currentTrackID == -1)
  221. break;
  222. currentEvent->next = track[currentTrackID];
  223. track[currentTrackID] = track[currentTrackID]->next;
  224. currentEvent = currentEvent->next;
  225. lowestTime = 0;
  226. }
  227. /* Make sure the list is properly terminated */
  228. currentEvent->next = 0;
  229. currentEvent = head->next;
  230. free(track);
  231. free(head); /* release the dummy head event */
  232. return currentEvent;
  233. }
  234. static int ReadMIDIFile(MIDIFile *mididata, SDL_RWops *rw)
  235. {
  236. int i = 0;
  237. Uint32 ID;
  238. Uint32 size;
  239. Uint16 format;
  240. Uint16 tracks;
  241. Uint16 division;
  242. if (!mididata)
  243. return 0;
  244. if (!rw)
  245. return 0;
  246. /* Make sure this is really a MIDI file */
  247. SDL_RWread(rw, &ID, 1, 4);
  248. if (BE_LONG(ID) != 'MThd')
  249. return 0;
  250. /* Header size must be 6 */
  251. SDL_RWread(rw, &size, 1, 4);
  252. size = BE_LONG(size);
  253. if (size != 6)
  254. return 0;
  255. /* We only support format 0 and 1, but not 2 */
  256. SDL_RWread(rw, &format, 1, 2);
  257. format = BE_SHORT(format);
  258. if (format != 0 && format != 1)
  259. return 0;
  260. SDL_RWread(rw, &tracks, 1, 2);
  261. tracks = BE_SHORT(tracks);
  262. mididata->nTracks = tracks;
  263. /* Allocate tracks */
  264. mididata->track = (MIDITrack*) calloc(1, sizeof(MIDITrack) * mididata->nTracks);
  265. if (NULL == mididata->track)
  266. {
  267. printf("Out of memory");
  268. goto bail;
  269. }
  270. /* Retrieve the PPQN value, needed for playback */
  271. SDL_RWread(rw, &division, 1, 2);
  272. mididata->division = BE_SHORT(division);
  273. for (i=0; i<tracks; i++)
  274. {
  275. SDL_RWread(rw, &ID, 1, 4); /* We might want to verify this is MTrk... */
  276. SDL_RWread(rw, &size, 1, 4);
  277. size = BE_LONG(size);
  278. mididata->track[i].len = size;
  279. mididata->track[i].data = malloc(size);
  280. if (NULL == mididata->track[i].data)
  281. {
  282. printf("Out of memory");
  283. goto bail;
  284. }
  285. SDL_RWread(rw, mididata->track[i].data, 1, size);
  286. }
  287. return 1;
  288. bail:
  289. for(;i >= 0; i--)
  290. {
  291. if (mididata->track[i].data)
  292. free(mididata->track[i].data);
  293. }
  294. return 0;
  295. }
  296. MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division)
  297. {
  298. MIDIFile *mididata = NULL;
  299. MIDIEvent *eventList;
  300. int trackID;
  301. mididata = calloc(1, sizeof(MIDIFile));
  302. if (!mididata)
  303. return NULL;
  304. /* Open the file */
  305. if ( rw != NULL )
  306. {
  307. /* Read in the data */
  308. if ( ! ReadMIDIFile(mididata, rw))
  309. {
  310. free(mididata);
  311. return NULL;
  312. }
  313. }
  314. else
  315. {
  316. free(mididata);
  317. return NULL;
  318. }
  319. if (division)
  320. *division = mididata->division;
  321. eventList = MIDItoStream(mididata);
  322. for(trackID = 0; trackID < mididata->nTracks; trackID++)
  323. {
  324. if (mididata->track[trackID].data)
  325. free(mididata->track[trackID].data);
  326. }
  327. free(mididata->track);
  328. free(mididata);
  329. return eventList;
  330. }
  331. void FreeMIDIEventList(MIDIEvent *head)
  332. {
  333. MIDIEvent *cur, *next;
  334. cur = head;
  335. while (cur)
  336. {
  337. next = cur->next;
  338. if (cur->extraData)
  339. free (cur->extraData);
  340. free (cur);
  341. cur = next;
  342. }
  343. }