PageRenderTime 38ms CodeModel.GetById 14ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/project/jni/sdl_mixer/native_midi/native_midi_common.c

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