native_midi_common.c 9.2 KB

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