native_midi_common.c
上传用户:nini_0081
上传日期:2022-07-21
资源大小:2628k
文件大小:9k
源码类别:

多媒体编程

开发平台:

DOS

  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_mixer.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. Mix_SetError("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.         
  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.     
  265.     /* Allocate tracks */
  266.     mididata->track = (MIDITrack*) calloc(1, sizeof(MIDITrack) * mididata->nTracks);
  267.     if (NULL == mididata->track)
  268.     {
  269.         Mix_SetError("Out of memory");
  270.         goto bail;
  271.     }
  272.     
  273. /* Retrieve the PPQN value, needed for playback */
  274. SDL_RWread(rw, &division, 1, 2);
  275. mididata->division = BE_SHORT(division);
  276. for (i=0; i<tracks; i++)
  277. {
  278. SDL_RWread(rw, &ID, 1, 4); /* We might want to verify this is MTrk... */
  279. SDL_RWread(rw, &size, 1, 4);
  280. size = BE_LONG(size);
  281. mididata->track[i].len = size;
  282. mididata->track[i].data = malloc(size);
  283. if (NULL == mididata->track[i].data)
  284. {
  285. Mix_SetError("Out of memory");
  286. goto bail;
  287. }
  288. SDL_RWread(rw, mididata->track[i].data, 1, size);
  289. }
  290. return 1;
  291. bail:
  292. for(;i >= 0; i--)
  293. {
  294. if (mididata->track[i].data)
  295. free(mididata->track[i].data);
  296. }
  297. return 0;
  298. }
  299. MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division)
  300. {
  301. MIDIFile *mididata = NULL;
  302. MIDIEvent *eventList;
  303. int trackID;
  304. mididata = calloc(1, sizeof(MIDIFile));
  305. if (!mididata)
  306. return NULL;
  307. /* Open the file */
  308. if ( rw != NULL )
  309. {
  310. /* Read in the data */
  311. if ( ! ReadMIDIFile(mididata, rw))
  312. {
  313. free(mididata);
  314. return NULL;
  315. }
  316. }
  317. else
  318. {
  319. free(mididata);
  320. return NULL;
  321. }
  322. if (division)
  323. *division = mididata->division;
  324. eventList = MIDItoStream(mididata);
  325. for(trackID = 0; trackID < mididata->nTracks; trackID++)
  326. {
  327. if (mididata->track[trackID].data)
  328. free(mididata->track[trackID].data);
  329. }
  330. free(mididata->track);
  331.     free(mididata);
  332. return eventList;
  333. }
  334. void FreeMIDIEventList(MIDIEvent *head)
  335. {
  336. MIDIEvent *cur, *next;
  337. cur = head;
  338. while (cur)
  339. {
  340. next = cur->next;
  341. if (cur->extraData) 
  342. free (cur->extraData);
  343. free (cur);
  344. cur = next;
  345. }
  346. }