Added native MIDI music support on MacOS and MacOS X
authorSam Lantinga <slouken@libsdl.org>
Wed, 05 Sep 2001 03:38:51 +0000
changeset 106ba6a6039e158
parent 105 4fc38aabb44c
child 107 2a03845b2d28
Added native MIDI music support on MacOS and MacOS X
CHANGES
configure.in
music.c
native_midi/native_midi_common.c
native_midi/native_midi_common.h
native_midi/native_midi_mac.c
     1.1 --- a/CHANGES	Mon Aug 20 03:57:29 2001 +0000
     1.2 +++ b/CHANGES	Wed Sep 05 03:38:51 2001 +0000
     1.3 @@ -1,5 +1,7 @@
     1.4  
     1.5  1.2.1:
     1.6 +Max Horn - Tue Sep  4 20:38:11 PDT 2001
     1.7 + * Added native MIDI music support on MacOS and MacOS X
     1.8  Florian Schulze - Sun Aug 19 14:55:37 PDT 2001
     1.9   * Added native MIDI music support on Windows
    1.10  Sam Lantinga - Sun Aug 19 02:20:55 PDT 2001
     2.1 --- a/configure.in	Mon Aug 20 03:57:29 2001 +0000
     2.2 +++ b/configure.in	Wed Sep 05 03:38:51 2001 +0000
     2.3 @@ -118,8 +118,14 @@
     2.4  [  --enable-music-midi     enable MIDI music via timidity [default=yes]],
     2.5                , enable_music_midi=yes)
     2.6  if test x$enable_music_midi = xyes; then
     2.7 -    CFLAGS="$CFLAGS -DMID_MUSIC -I\$(top_srcdir)/timidity"
     2.8 -    MUSIC_SUBDIRS="$MUSIC_SUBDIRS timidity"
     2.9 +    CFLAGS="$CFLAGS -DMID_MUSIC"
    2.10 +    AC_ARG_ENABLE(music-timidity-midi,
    2.11 +[  --enable-music-timidity-midi  enable timidity MIDI output [default=yes]],
    2.12 +                  , enable_music_timidity_midi=yes)
    2.13 +    if test x$enable_music_timidity_midi = xyes; then
    2.14 +        CFLAGS="$CFLAGS -I\$(top_srcdir)/timidity"
    2.15 +        MUSIC_SUBDIRS="$MUSIC_SUBDIRS timidity"
    2.16 +    fi
    2.17      AC_ARG_ENABLE(music-native-midi,
    2.18  [  --enable-music-native-midi  enable native MIDI music output [default=yes]],
    2.19                    , enable_music_native_midi=yes)
    2.20 @@ -170,7 +176,7 @@
    2.21  dnl Add Makefile conditionals
    2.22  AC_SUBST(MUSIC_SUBDIRS)
    2.23  AM_CONDITIONAL(USE_MIKMOD, test x$enable_music_mod = xyes)
    2.24 -AM_CONDITIONAL(USE_TIMIDITY, test x$enable_music_midi = xyes)
    2.25 +AM_CONDITIONAL(USE_TIMIDITY, test x$enable_music_timidity_midi = xyes)
    2.26  AM_CONDITIONAL(USE_NATIVE_MIDI, test x$use_music_native_midi = xyes)
    2.27  
    2.28  # Finally create all the generated files
     3.1 --- a/music.c	Mon Aug 20 03:57:29 2001 +0000
     3.2 +++ b/music.c	Wed Sep 05 03:38:51 2001 +0000
     3.3 @@ -55,10 +55,17 @@
     3.4  #  endif
     3.5  #endif
     3.6  #ifdef MID_MUSIC
     3.7 -#include "timidity.h"
     3.8 -#ifdef USE_NATIVE_MIDI
     3.9 -#include "native_midi.h"
    3.10 -#endif
    3.11 +#  ifdef USE_TIMIDITY_MIDI
    3.12 +#    include "timidity.h"
    3.13 +#  endif
    3.14 +#  ifdef USE_NATIVE_MIDI
    3.15 +#    include "native_midi.h"
    3.16 +#  endif
    3.17 +#  if defined(USE_TIMIDITY_MIDI) && defined(USE_NATIVE_MIDI)
    3.18 +#    define MIDI_ELSE	else
    3.19 +#  else
    3.20 +#    define MIDI_ELSE
    3.21 +#  endif
    3.22  #endif
    3.23  #ifdef OGG_MUSIC
    3.24  #include "music_ogg.h"
    3.25 @@ -99,7 +106,9 @@
    3.26  		UNIMOD *module;
    3.27  #endif
    3.28  #ifdef MID_MUSIC
    3.29 +#ifdef USE_TIMIDITY_MIDI
    3.30  		MidiSong *midi;
    3.31 +#endif
    3.32  #ifdef USE_NATIVE_MIDI
    3.33  		NativeMidiSong *nativemidi;
    3.34  #endif
    3.35 @@ -118,7 +127,9 @@
    3.36  	int error;
    3.37  };
    3.38  #ifdef MID_MUSIC
    3.39 +#ifdef USE_TIMIDITY_MIDI
    3.40  static int timidity_ok;
    3.41 +#endif
    3.42  #ifdef USE_NATIVE_MIDI
    3.43  static int native_midi_ok;
    3.44  #endif
    3.45 @@ -232,6 +243,7 @@
    3.46  				break;
    3.47  #endif
    3.48  #ifdef MID_MUSIC
    3.49 +#ifdef USE_TIMIDITY_MIDI
    3.50  			case MUS_MID:
    3.51  				if ( timidity_ok ) {
    3.52  					int samples = len / samplesize;
    3.53 @@ -239,6 +251,7 @@
    3.54  				}
    3.55  				break;
    3.56  #endif
    3.57 +#endif
    3.58  #ifdef OGG_MUSIC
    3.59  			case MUS_OGG:
    3.60  				OGG_playAudio(music_playing->data.ogg, stream, len);
    3.61 @@ -323,6 +336,7 @@
    3.62  	}
    3.63  #endif
    3.64  #ifdef MID_MUSIC
    3.65 +#ifdef USE_TIMIDITY_MIDI
    3.66  	samplesize = mixer->size/mixer->samples;
    3.67  	if ( Timidity_Init(mixer->freq, mixer->format,
    3.68  	                    mixer->channels, mixer->samples) == 0 ) {
    3.69 @@ -330,11 +344,13 @@
    3.70  	} else {
    3.71  		timidity_ok = 0;
    3.72  	}
    3.73 +#endif
    3.74  #ifdef USE_NATIVE_MIDI
    3.75 +#ifdef USE_TIMIDITY_MIDI
    3.76  	native_midi_ok = !timidity_ok;
    3.77 -	if ( native_midi_ok ) {
    3.78 +	if ( native_midi_ok )
    3.79 +#endif
    3.80  		native_midi_ok = native_midi_detect();
    3.81 -	}
    3.82  #endif
    3.83  #endif
    3.84  #ifdef OGG_MUSIC
    3.85 @@ -419,8 +435,9 @@
    3.86  		  		Mix_SetError("%s", native_midi_error());
    3.87  			  	music->error = 1;
    3.88  			}
    3.89 -      		} else
    3.90 +	  	} MIDI_ELSE
    3.91  #endif
    3.92 +#ifdef USE_TIMIDITY_MIDI
    3.93  		if ( timidity_ok ) {
    3.94  			music->data.midi = Timidity_LoadSong((char *)file);
    3.95  			if ( music->data.midi == NULL ) {
    3.96 @@ -431,6 +448,7 @@
    3.97  			Mix_SetError("%s", Timidity_Error());
    3.98  			music->error = 1;
    3.99  		}
   3.100 +#endif
   3.101  	} else
   3.102  #endif
   3.103  #ifdef OGG_MUSIC
   3.104 @@ -512,11 +530,13 @@
   3.105  #ifdef USE_NATIVE_MIDI
   3.106    				if ( native_midi_ok ) {
   3.107  					native_midi_freesong(music->data.nativemidi);
   3.108 -				} else
   3.109 +				} MIDI_ELSE
   3.110  #endif
   3.111 +#ifdef USE_TIMIDITY_MIDI
   3.112  				if ( timidity_ok ) {
   3.113  					Timidity_FreeSong(music->data.midi);
   3.114  				}
   3.115 +#endif
   3.116  				break;
   3.117  #endif
   3.118  #ifdef OGG_MUSIC
   3.119 @@ -568,12 +588,14 @@
   3.120    			if ( native_midi_ok ) {
   3.121  				native_midi_setvolume(music_volume);
   3.122  				native_midi_start(music->data.nativemidi);
   3.123 -			} else
   3.124 +			} MIDI_ELSE
   3.125  #endif
   3.126 +#ifdef USE_TIMIDITY_MIDI
   3.127  			if ( timidity_ok ) {
   3.128  				Timidity_SetVolume(music_volume);
   3.129  				Timidity_Start(music->data.midi);
   3.130  			}
   3.131 +#endif
   3.132  			break;
   3.133  #endif
   3.134  #ifdef OGG_MUSIC
   3.135 @@ -672,11 +694,13 @@
   3.136  #ifdef USE_NATIVE_MIDI
   3.137  			if ( native_midi_ok ) {
   3.138  				native_midi_setvolume(music_volume);
   3.139 -			} else
   3.140 +			} MIDI_ELSE
   3.141  #endif
   3.142 +#ifdef USE_TIMIDITY_MIDI
   3.143  			if ( timidity_ok ) {
   3.144  				Timidity_SetVolume(music_volume);
   3.145  			}
   3.146 +#endif
   3.147  			break;
   3.148  #endif
   3.149  #ifdef OGG_MUSIC
   3.150 @@ -720,11 +744,13 @@
   3.151  #ifdef USE_NATIVE_MIDI
   3.152  		if ( native_midi_ok ) {
   3.153  			native_midi_stop();
   3.154 -		} else
   3.155 +		} MIDI_ELSE
   3.156  #endif
   3.157 +#ifdef USE_TIMIDITY_MIDI
   3.158  		if ( timidity_ok ) {
   3.159  			Timidity_Stop();
   3.160  		}
   3.161 +#endif
   3.162  		break;
   3.163  #endif
   3.164  #ifdef OGG_MUSIC
   3.165 @@ -869,12 +895,14 @@
   3.166  				if ( native_midi_ok ) {
   3.167  					if ( ! native_midi_active() )
   3.168  						return(0);
   3.169 -				} else
   3.170 +				} MIDI_ELSE
   3.171  #endif
   3.172 +#ifdef USE_TIMIDITY_MIDI
   3.173  				if ( timidity_ok ) {
   3.174  					if ( ! Timidity_Active() )
   3.175  						return(0);
   3.176  				}
   3.177 +#endif
   3.178  				break;
   3.179  #endif
   3.180  #ifdef OGG_MUSIC
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/native_midi/native_midi_common.c	Wed Sep 05 03:38:51 2001 +0000
     4.3 @@ -0,0 +1,400 @@
     4.4 +/*
     4.5 +    native_midi:  Hardware Midi support for the SDL_mixer library
     4.6 +    Copyright (C) 2000,2001  Florian 'Proff' Schulze
     4.7 +
     4.8 +    This library is free software; you can redistribute it and/or
     4.9 +    modify it under the terms of the GNU Library General Public
    4.10 +    License as published by the Free Software Foundation; either
    4.11 +    version 2 of the License, or (at your option) any later version.
    4.12 +
    4.13 +    This library is distributed in the hope that it will be useful,
    4.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    4.16 +    Library General Public License for more details.
    4.17 +
    4.18 +    You should have received a copy of the GNU Library General Public
    4.19 +    License along with this library; if not, write to the Free
    4.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    4.21 +
    4.22 +    Florian 'Proff' Schulze
    4.23 +    florian.proff.schulze@gmx.net
    4.24 +*/
    4.25 +
    4.26 +
    4.27 +#include "native_midi_common.h"
    4.28 +
    4.29 +#include "SDL_mixer.h"
    4.30 +
    4.31 +#include <stdlib.h>
    4.32 +#include <string.h>
    4.33 +#include <limits.h>
    4.34 +
    4.35 +
    4.36 +/* The maximum number of midi tracks that we can handle */
    4.37 +#define MIDI_TRACKS 32
    4.38 +
    4.39 +
    4.40 +/* A single midi track as read from the midi file */
    4.41 +typedef struct
    4.42 +{
    4.43 +	Uint8 *data;					/* MIDI message stream */
    4.44 +	int len;						/* length of the track data */
    4.45 +} MIDITrack;
    4.46 +
    4.47 +/* A midi file, stripped down to the absolute minimum - divison & track data */
    4.48 +typedef struct
    4.49 +{
    4.50 +	int division;					/* number of pulses per quarter note (ppqn) */
    4.51 +	MIDITrack track[MIDI_TRACKS];
    4.52 +} MIDIFile;
    4.53 +
    4.54 +
    4.55 +/* Some macros that help us stay endianess-independant */
    4.56 +#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    4.57 +#define BE_SHORT(x) (x)
    4.58 +#define BE_LONG(x) (x)
    4.59 +#else
    4.60 +#define BE_SHORT(x)	((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
    4.61 +#define BE_LONG(x)	((((x)&0x0000FF)<<24) | \
    4.62 +			 (((x)&0x00FF00)<<8) | \
    4.63 +			 (((x)&0xFF0000)>>8) | \
    4.64 +			 (((x)>>24)&0xFF))
    4.65 +#endif
    4.66 +
    4.67 +
    4.68 +
    4.69 +/* Get Variable Length Quantity */
    4.70 +static int GetVLQ(MIDITrack *track, int *currentPos)
    4.71 +{
    4.72 +	int l = 0;
    4.73 +	Uint8 c;
    4.74 +	while(1)
    4.75 +	{
    4.76 +		c = track->data[*currentPos];
    4.77 +		(*currentPos)++;
    4.78 +		l += (c & 0x7f);
    4.79 +		if (!(c & 0x80)) 
    4.80 +			return l;
    4.81 +		l <<= 7;
    4.82 +	}
    4.83 +}
    4.84 +
    4.85 +/* Create a single MIDIEvent */
    4.86 +static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b)
    4.87 +{
    4.88 +	MIDIEvent *newEvent;
    4.89 +	
    4.90 +	newEvent = calloc(1, sizeof(MIDIEvent));
    4.91 +
    4.92 +	if (newEvent)
    4.93 +	{
    4.94 +		newEvent->time = time;
    4.95 +		newEvent->status = event;
    4.96 +		newEvent->data[0] = a;
    4.97 +		newEvent->data[1] = b;
    4.98 +	}
    4.99 +	else
   4.100 +		Mix_SetError("Out of memory");
   4.101 +	
   4.102 +	return newEvent;
   4.103 +}
   4.104 +
   4.105 +/* Convert a single midi track to a list of MIDIEvents */
   4.106 +static MIDIEvent *MIDITracktoStream(MIDITrack *track)
   4.107 +{
   4.108 +	Uint32 atime = 0;
   4.109 +	Uint32 len = 0;
   4.110 +	Uint8 event,type,a,b,c;
   4.111 +	Uint8 laststatus = 0;
   4.112 +	Uint8 lastchan = 0;
   4.113 +	int currentPos = 0;
   4.114 +	int end = 0;
   4.115 +	MIDIEvent *head = CreateEvent(0,0,0,0);	/* dummy event to make handling the list easier */
   4.116 +	MIDIEvent *currentEvent = head;
   4.117 +
   4.118 +	while (!end)
   4.119 +	{
   4.120 +		if (currentPos >= track->len)
   4.121 +			break; /* End of data stream reached */
   4.122 +
   4.123 +		atime += GetVLQ(track, &currentPos);
   4.124 +		event = track->data[currentPos++];
   4.125 +		
   4.126 +		/* Handle SysEx seperatly */
   4.127 +		if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX)
   4.128 +		{
   4.129 +			if (event == 0xFF)
   4.130 +			{
   4.131 +				type = track->data[currentPos];
   4.132 +				currentPos++;
   4.133 +				switch(type)
   4.134 +				{
   4.135 +					case 0x2f: /* End of data marker */
   4.136 +						end = 1;
   4.137 +					case 0x51: /* Tempo change */
   4.138 +						/*
   4.139 +						a=track->data[currentPos];
   4.140 +						b=track->data[currentPos+1];
   4.141 +						c=track->data[currentPos+2];
   4.142 +						AddEvent(song, atime, MEVT_TEMPO, c, b, a);
   4.143 +						*/
   4.144 +						break;
   4.145 +				}
   4.146 +			}
   4.147 +			else
   4.148 +				type = 0;
   4.149 +
   4.150 +			len = GetVLQ(track, &currentPos);
   4.151 +			
   4.152 +			/* Create an event and attach the extra data, if any */
   4.153 +			currentEvent->next = CreateEvent(atime, event, type, 0);
   4.154 +			currentEvent = currentEvent->next;
   4.155 +			if (NULL == currentEvent)
   4.156 +			{
   4.157 +				FreeMIDIEventList(head);
   4.158 +				return NULL;
   4.159 +			}
   4.160 +			if (len)
   4.161 +			{
   4.162 +				currentEvent->extraLen = len;
   4.163 +				currentEvent->extraData = malloc(len);
   4.164 +				memcpy(currentEvent->extraData, &(track->data[currentPos]), len);
   4.165 +				currentPos += len;
   4.166 +			}
   4.167 +		}
   4.168 +		else
   4.169 +		{
   4.170 +			a = event;
   4.171 +			if (a & 0x80) /* It's a status byte */
   4.172 +			{
   4.173 +				/* Extract channel and status information */
   4.174 +				lastchan = a & 0x0F;
   4.175 +				laststatus = (a>>4) & 0x0F;
   4.176 +				
   4.177 +				/* Read the next byte which should always be a data byte */
   4.178 +				a = track->data[currentPos++] & 0x7F;
   4.179 +			}
   4.180 +			switch(laststatus)
   4.181 +			{
   4.182 +				case MIDI_STATUS_NOTE_OFF:
   4.183 +				case MIDI_STATUS_NOTE_ON: /* Note on */
   4.184 +				case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */
   4.185 +				case MIDI_STATUS_CONTROLLER: /* Control change */
   4.186 +				case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */
   4.187 +					b = track->data[currentPos++] & 0x7F;
   4.188 +					currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b);
   4.189 +					currentEvent = currentEvent->next;
   4.190 +					if (NULL == currentEvent)
   4.191 +					{
   4.192 +						FreeMIDIEventList(head);
   4.193 +						return NULL;
   4.194 +					}
   4.195 +					break;
   4.196 +
   4.197 +				case MIDI_STATUS_PROG_CHANGE: /* Program change */
   4.198 +				case MIDI_STATUS_PRESSURE: /* Channel pressure */
   4.199 +					a &= 0x7f;
   4.200 +					currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0);
   4.201 +					currentEvent = currentEvent->next;
   4.202 +					if (NULL == currentEvent)
   4.203 +					{
   4.204 +						FreeMIDIEventList(head);
   4.205 +						return NULL;
   4.206 +					}
   4.207 +					break;
   4.208 +
   4.209 +				default: /* Sysex already handled above */
   4.210 +					break;
   4.211 +			}
   4.212 +		}
   4.213 +	}
   4.214 +
   4.215 +end:	
   4.216 +	currentEvent = head->next;
   4.217 +	free(head);	/* release the dummy head event */
   4.218 +	return currentEvent;
   4.219 +}
   4.220 +
   4.221 +/*
   4.222 + *  Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents.
   4.223 + *  To do so, first convert the tracks seperatly, then interweave the resulting
   4.224 + *  MIDIEvent-Lists to one big list.
   4.225 + */
   4.226 +static MIDIEvent *MIDItoStream(MIDIFile *mididata)
   4.227 +{
   4.228 +	MIDIEvent *track[MIDI_TRACKS];
   4.229 +	MIDIEvent *head = CreateEvent(0,0,0,0);	/* dummy event to make handling the list easier */
   4.230 +	MIDIEvent *currentEvent = head;
   4.231 +	int trackID;
   4.232 +	int time = 0;
   4.233 +	
   4.234 +	if (NULL == head)
   4.235 +		return NULL;
   4.236 +	
   4.237 +	/* First, convert all tracks to MIDIEvent lists */
   4.238 +	for (trackID = 0; trackID < MIDI_TRACKS; trackID++)
   4.239 +		track[trackID] = MIDITracktoStream(&mididata->track[trackID]);
   4.240 +
   4.241 +	/* Now, merge the lists. */
   4.242 +	/* TODO */
   4.243 +	while(1)
   4.244 +	{
   4.245 +		int lowestTime = INT_MAX;
   4.246 +		int currentTrackID = -1;
   4.247 +		
   4.248 +		/* Find the next event */
   4.249 +		for (trackID = 0; trackID < MIDI_TRACKS; trackID++)
   4.250 +		{
   4.251 +			if (track[trackID] && (track[trackID]->time < lowestTime))
   4.252 +			{
   4.253 +				currentTrackID = trackID;
   4.254 +				lowestTime = track[currentTrackID]->time;
   4.255 +			}
   4.256 +		}
   4.257 +		
   4.258 +		/* Check if we processes all events */
   4.259 +		if (currentTrackID == -1)
   4.260 +			break;
   4.261 +		
   4.262 +		currentEvent->next = track[currentTrackID];
   4.263 +		track[currentTrackID] = track[currentTrackID]->next;
   4.264 +
   4.265 +		currentEvent = currentEvent->next;
   4.266 +		
   4.267 +		
   4.268 +		lowestTime = 0;
   4.269 +	}
   4.270 +
   4.271 +	/* Make sure the list is properly terminated */
   4.272 +	currentEvent->next = 0;
   4.273 +
   4.274 +	currentEvent = head->next;
   4.275 +	free(head);	/* release the dummy head event */
   4.276 +	return currentEvent;
   4.277 +}
   4.278 +
   4.279 +static int ReadMIDIFile(MIDIFile *mididata, FILE *fp)
   4.280 +{
   4.281 +	int i = 0;
   4.282 +	Uint32	ID;
   4.283 +	Uint32	size;
   4.284 +	Uint16	format;
   4.285 +	Uint16	tracks;
   4.286 +	Uint16	division;
   4.287 +
   4.288 +	if (!mididata)
   4.289 +		return 0;
   4.290 +	if (!fp)
   4.291 +		return 0;
   4.292 +
   4.293 +	/* Make sure this is really a MIDI file */
   4.294 +	fread(&ID, 1, 4, fp);
   4.295 +	if (BE_LONG(ID) != 'MThd')
   4.296 +		return 0;
   4.297 +	
   4.298 +	/* Header size must be 6 */
   4.299 +	fread(&size, 1, 4, fp);
   4.300 +	size = BE_LONG(size);
   4.301 +	if (size != 6)
   4.302 +		return 0;
   4.303 +	
   4.304 +	/* We only support format 0 and 1, but not 2 */
   4.305 +	fread(&format, 1, 2, fp);
   4.306 +	format = BE_SHORT(format);
   4.307 +	if (format != 0 && format != 1)
   4.308 +		return 0;
   4.309 +	
   4.310 +	fread(&tracks, 1, 2, fp);
   4.311 +	tracks = BE_SHORT(tracks);
   4.312 +	
   4.313 +	/* Retrieve the PPQN value, needed for playback */
   4.314 +	fread(&division, 1, 2, fp);
   4.315 +	mididata->division = BE_SHORT(division);
   4.316 +	
   4.317 +	
   4.318 +	for (i=0; i<tracks; i++)
   4.319 +	{
   4.320 +		fread(&ID, 1, 4, fp);	/* We might want to verify this is MTrk... */
   4.321 +		fread(&size, 1, 4, fp);
   4.322 +		size = BE_LONG(size);
   4.323 +		mididata->track[i].len = size;
   4.324 +		mididata->track[i].data = malloc(size);
   4.325 +		if (NULL == mididata->track[i].data)
   4.326 +		{
   4.327 +			Mix_SetError("Out of memory");
   4.328 +			goto bail;
   4.329 +		}
   4.330 +		fread(mididata->track[i].data, 1, size, fp);
   4.331 +	}
   4.332 +	return 1;
   4.333 +
   4.334 +bail:
   4.335 +	for(;i >= 0; i--)
   4.336 +	{
   4.337 +		if (mididata->track[i].data)
   4.338 +			free(mididata->track[i].data);
   4.339 +	}
   4.340 +
   4.341 +	return 0;
   4.342 +}
   4.343 +
   4.344 +MIDIEvent *CreateMIDIEventList(char *midifile, Uint16 *division)
   4.345 +{
   4.346 +	FILE *fp = NULL;
   4.347 +	MIDIFile *mididata = NULL;
   4.348 +	MIDIEvent *eventList;
   4.349 +	int trackID;
   4.350 +	
   4.351 +	mididata = calloc(1, sizeof(MIDIFile));
   4.352 +	if (!mididata)
   4.353 +		return NULL;
   4.354 +
   4.355 +	/* Open the file */
   4.356 +	fp = fopen(midifile, "rb");
   4.357 +	if ( fp != NULL )
   4.358 +	{
   4.359 +		/* Read in the data */
   4.360 +		if ( ! ReadMIDIFile(mididata, fp))
   4.361 +		{
   4.362 +			free(mididata);
   4.363 +			fclose(fp);
   4.364 +			return NULL;
   4.365 +		}
   4.366 +		fclose(fp);
   4.367 +	}
   4.368 +	else
   4.369 +	{
   4.370 +		free(mididata);
   4.371 +		return NULL;
   4.372 +	}
   4.373 +	
   4.374 +	if (division)
   4.375 +		*division = mididata->division;
   4.376 +	
   4.377 +	eventList = MIDItoStream(mididata);
   4.378 +	
   4.379 +	for(trackID = 0; trackID < MIDI_TRACKS; trackID++)
   4.380 +	{
   4.381 +		if (mididata->track[trackID].data)
   4.382 +			free(mididata->track[trackID].data);
   4.383 +	}
   4.384 +	free(mididata);
   4.385 +	
   4.386 +	return eventList;
   4.387 +}
   4.388 +
   4.389 +void FreeMIDIEventList(MIDIEvent *head)
   4.390 +{
   4.391 +	MIDIEvent *cur, *next;
   4.392 +	
   4.393 +	cur = head;
   4.394 +
   4.395 +	while (cur)
   4.396 +	{
   4.397 +		next = cur->next;
   4.398 +		if (cur->extraData) 
   4.399 +			free (cur->extraData);
   4.400 +		free (cur);
   4.401 +		cur = next;
   4.402 +	}
   4.403 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/native_midi/native_midi_common.h	Wed Sep 05 03:38:51 2001 +0000
     5.3 @@ -0,0 +1,67 @@
     5.4 +/*
     5.5 +    native_midi:  Hardware Midi support for the SDL_mixer library
     5.6 +    Copyright (C) 2000,2001  Florian 'Proff' Schulze & Max Horn
     5.7 +
     5.8 +    This library is free software; you can redistribute it and/or
     5.9 +    modify it under the terms of the GNU Library General Public
    5.10 +    License as published by the Free Software Foundation; either
    5.11 +    version 2 of the License, or (at your option) any later version.
    5.12 +
    5.13 +    This library is distributed in the hope that it will be useful,
    5.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    5.16 +    Library General Public License for more details.
    5.17 +
    5.18 +    You should have received a copy of the GNU Library General Public
    5.19 +    License along with this library; if not, write to the Free
    5.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    5.21 +
    5.22 +    Florian 'Proff' Schulze
    5.23 +    florian.proff.schulze@gmx.net
    5.24 +
    5.25 +    Max Horn
    5.26 +    max@quendi.de
    5.27 +*/
    5.28 +
    5.29 +#ifndef _NATIVE_MIDI_COMMON_H_
    5.30 +#define _NATIVE_MIDI_COMMON_H_
    5.31 +
    5.32 +#include "SDL.h"
    5.33 +
    5.34 +/* Midi Status Bytes */
    5.35 +#define MIDI_STATUS_NOTE_OFF	0x8
    5.36 +#define MIDI_STATUS_NOTE_ON	0x9
    5.37 +#define MIDI_STATUS_AFTERTOUCH	0xA
    5.38 +#define MIDI_STATUS_CONTROLLER	0xB
    5.39 +#define MIDI_STATUS_PROG_CHANGE	0xC
    5.40 +#define MIDI_STATUS_PRESSURE	0xD
    5.41 +#define MIDI_STATUS_PITCH_WHEEL	0xE
    5.42 +#define MIDI_STATUS_SYSEX	0xF
    5.43 +
    5.44 +/* We store the midi events in a linked list; this way it is
    5.45 +   easy to shuffle the tracks together later on; and we are
    5.46 +   flexible in the size of each elemnt.
    5.47 + */
    5.48 +typedef struct MIDIEvent
    5.49 +{
    5.50 +	Uint32	time;		/* Time at which this midi events occurs */
    5.51 +	Uint8	status;		/* Status byte */
    5.52 +	Uint8	data[2];	/* 1 or 2 bytes additional data for most events */
    5.53 +
    5.54 +	Uint32	extraLen;	/* For some SysEx events, we need additional storage */
    5.55 +	Uint8	*extraData;
    5.56 +	
    5.57 +	struct MIDIEvent *next;
    5.58 +} MIDIEvent;
    5.59 +
    5.60 +
    5.61 +/* Load a midifile to memory, converting it to a list of MIDIEvents.
    5.62 +   This function returns a linked lists of MIDIEvents, 0 if an error occured.
    5.63 + */ 
    5.64 +MIDIEvent *CreateMIDIEventList(char *midifile, Uint16 *division);
    5.65 +
    5.66 +/* Release a MIDIEvent list after usage. */
    5.67 +void FreeMIDIEventList(MIDIEvent *head);
    5.68 +
    5.69 +
    5.70 +#endif /* _NATIVE_MIDI_COMMON_H_ */
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/native_midi/native_midi_mac.c	Wed Sep 05 03:38:51 2001 +0000
     6.3 @@ -0,0 +1,608 @@
     6.4 +/*
     6.5 +    native_midi_mac:  Native Midi support on MacOS for the SDL_mixer library
     6.6 +    Copyright (C) 2001  Max Horn
     6.7 +
     6.8 +    This library is free software; you can redistribute it and/or
     6.9 +    modify it under the terms of the GNU Library General Public
    6.10 +    License as published by the Free Software Foundation; either
    6.11 +    version 2 of the License, or (at your option) any later version.
    6.12 +
    6.13 +    This library is distributed in the hope that it will be useful,
    6.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    6.16 +    Library General Public License for more details.
    6.17 +
    6.18 +    You should have received a copy of the GNU Library General Public
    6.19 +    License along with this library; if not, write to the Free
    6.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    6.21 +
    6.22 +    Max Horn
    6.23 +    max@quendi.de
    6.24 +*/
    6.25 +
    6.26 +#if 1 || defined(MACOS) || defined(MACOSX)
    6.27 +
    6.28 +#include "native_midi.h"
    6.29 +#include "native_midi_common.h"
    6.30 +
    6.31 +#include <QuickTimeMusic.h>
    6.32 +
    6.33 +#include <assert.h>
    6.34 +#include <stdlib.h>
    6.35 +#include <string.h>
    6.36 +
    6.37 +
    6.38 +/* Native Midi song */
    6.39 +struct _NativeMidiSong
    6.40 +{
    6.41 +	Uint32		*tuneSequence;
    6.42 +	Uint32		*tuneHeader;
    6.43 +};
    6.44 +
    6.45 +enum
    6.46 +{
    6.47 +	/* number of (32-bit) long words in a note request event */
    6.48 +	kNoteRequestEventLength = ((sizeof(NoteRequest)/sizeof(long)) + 2),
    6.49 +
    6.50 +	/* number of (32-bit) long words in a marker event */
    6.51 +	kMarkerEventLength	= 1,
    6.52 +
    6.53 +	/* number of (32-bit) long words in a general event, minus its data */
    6.54 +	kGeneralEventLength	= 2
    6.55 +};
    6.56 +
    6.57 +#define ERROR_BUF_SIZE			256
    6.58 +#define	BUFFER_INCREMENT		5000
    6.59 +
    6.60 +#define REST_IF_NECESSARY()	do {\
    6.61 +			int timeDiff = eventPos->time - lastEventTime;	\
    6.62 +			if(timeDiff)	\
    6.63 +			{	\
    6.64 +				timeDiff = (int)(timeDiff*tick);	\
    6.65 +				qtma_StuffRestEvent(*tunePos, timeDiff);	\
    6.66 +				tunePos++;	\
    6.67 +				lastEventTime = eventPos->time;	\
    6.68 +			}	\
    6.69 +		} while(0)
    6.70 +
    6.71 +
    6.72 +static Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts);
    6.73 +static Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts);
    6.74 +
    6.75 +/* The global TunePlayer instance */
    6.76 +static TunePlayer	gTunePlayer = NULL;
    6.77 +static int			gInstaceCount = 0;
    6.78 +static Uint32		*gCurrentTuneSequence = NULL;
    6.79 +static char			gErrorBuffer[ERROR_BUF_SIZE] = "";
    6.80 +
    6.81 +
    6.82 +/* Check whether QuickTime is available */
    6.83 +int native_midi_detect()
    6.84 +{
    6.85 +	/* TODO */
    6.86 +	return 1;
    6.87 +}
    6.88 +
    6.89 +NativeMidiSong *native_midi_loadsong(char *midifile)
    6.90 +{
    6.91 +	NativeMidiSong	*song = NULL;
    6.92 +	MIDIEvent		*evntlist = NULL;
    6.93 +	int				part_to_inst[32];
    6.94 +	int				part_poly_max[32];
    6.95 +	int				numParts = 0;
    6.96 +	Uint16			ppqn;
    6.97 +
    6.98 +	/* Init the arrays */
    6.99 +	memset(part_poly_max,0,sizeof(part_poly_max));
   6.100 +	memset(part_to_inst,-1,sizeof(part_to_inst));
   6.101 +	
   6.102 +	/* Attempt to load the midi file */
   6.103 +	evntlist = CreateMIDIEventList(midifile, &ppqn);
   6.104 +	if (!evntlist)
   6.105 +		goto bail;
   6.106 +
   6.107 +	/* Allocate memory for the song struct */
   6.108 +	song = malloc(sizeof(NativeMidiSong));
   6.109 +	if (!song)
   6.110 +		goto bail;
   6.111 +
   6.112 +	/* Build a tune sequence from the event list */
   6.113 +	song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts);
   6.114 +	if(!song->tuneSequence)
   6.115 +		goto bail;
   6.116 +
   6.117 +	/* Now build a tune header from the data we collect above, create
   6.118 +	   all parts as needed and assign them the correct instrument.
   6.119 +	*/
   6.120 +	song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts);
   6.121 +	if(!song->tuneHeader)
   6.122 +		goto bail;
   6.123 +	
   6.124 +	/* Increment the instance count */
   6.125 +	gInstaceCount++;
   6.126 +	if (NULL == gTunePlayer)
   6.127 +		gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0);
   6.128 +
   6.129 +	/* Finally, free the event list */
   6.130 +	FreeMIDIEventList(evntlist);
   6.131 +	
   6.132 +	return song;
   6.133 +	
   6.134 +bail:
   6.135 +	if (evntlist)
   6.136 +		FreeMIDIEventList(evntlist);
   6.137 +	
   6.138 +	if (song)
   6.139 +	{
   6.140 +		if(song->tuneSequence)
   6.141 +			free(song->tuneSequence);
   6.142 +		
   6.143 +		if(song->tuneHeader)
   6.144 +			DisposePtr((Ptr)song->tuneHeader);
   6.145 +
   6.146 +		free(song);
   6.147 +	}
   6.148 +	
   6.149 +	return NULL;
   6.150 +}
   6.151 +
   6.152 +void native_midi_freesong(NativeMidiSong *song)
   6.153 +{
   6.154 +	if(!song || !song->tuneSequence)
   6.155 +		return;
   6.156 +
   6.157 +	/* If this is the currently playing song, stop it now */	
   6.158 +	if (song->tuneSequence == gCurrentTuneSequence)
   6.159 +		native_midi_stop();
   6.160 +	
   6.161 +	/* Finally, free the data storage */
   6.162 +	free(song->tuneSequence);
   6.163 +	DisposePtr((Ptr)song->tuneHeader);
   6.164 +	free(song);
   6.165 +
   6.166 +	/* Increment the instance count */
   6.167 +	gInstaceCount--;
   6.168 +	if (gTunePlayer && (gInstaceCount == 0))
   6.169 +	{
   6.170 +		CloseComponent(gTunePlayer);
   6.171 +		gTunePlayer = NULL;
   6.172 +	}
   6.173 +}
   6.174 +
   6.175 +void native_midi_start(NativeMidiSong *song)
   6.176 +{
   6.177 +	UInt32		queueFlags = 0;
   6.178 +	ComponentResult tpError;
   6.179 +	
   6.180 +	/* First, stop the currently playing music */
   6.181 +	native_midi_stop();
   6.182 +	
   6.183 +	/* Set up the queue flags */
   6.184 +	queueFlags = kTuneStartNow;
   6.185 +
   6.186 +	/* Set the time scale (units per second), we want milliseconds */
   6.187 +	tpError = TuneSetTimeScale(gTunePlayer, 1000);
   6.188 +	if (tpError != noErr)
   6.189 +	{
   6.190 +		strncpy (gErrorBuffer, "MIDI error during TuneSetTimeScale", ERROR_BUF_SIZE);
   6.191 +		return;
   6.192 +	}
   6.193 +
   6.194 +	/* Set the header, to tell what instruments are used */
   6.195 +	tpError = TuneSetHeader(gTunePlayer, (UInt32 *)song->tuneHeader);
   6.196 +	if (tpError != noErr)
   6.197 +	{
   6.198 +		strncpy (gErrorBuffer, "MIDI error during TuneSetHeader", ERROR_BUF_SIZE);
   6.199 +		return;
   6.200 +	}
   6.201 +	
   6.202 +	/* Have it allocate whatever resources are needed */
   6.203 +	tpError = TunePreroll(gTunePlayer);
   6.204 +	if (tpError != noErr)
   6.205 +	{
   6.206 +		strncpy (gErrorBuffer, "MIDI error during TunePreroll", ERROR_BUF_SIZE);
   6.207 +		return;
   6.208 +	}
   6.209 +
   6.210 +	/* We want to play at normal volume */
   6.211 +	tpError = TuneSetVolume(gTunePlayer, 0x00010000);
   6.212 +	if (tpError != noErr)
   6.213 +	{
   6.214 +		strncpy (gErrorBuffer, "MIDI error during TuneSetVolume", ERROR_BUF_SIZE);
   6.215 +		return;
   6.216 +	}
   6.217 +	
   6.218 +	/* Finally, start playing the full song */
   6.219 +	gCurrentTuneSequence = song->tuneSequence;
   6.220 +	tpError = TuneQueue(gTunePlayer, (UInt32 *)song->tuneSequence, 0x00010000, 0, 0xFFFFFFFF, queueFlags, NULL, 0);
   6.221 +	if (tpError != noErr)
   6.222 +	{
   6.223 +		strncpy (gErrorBuffer, "MIDI error during TuneQueue", ERROR_BUF_SIZE);
   6.224 +		return;
   6.225 +	}
   6.226 +}
   6.227 +
   6.228 +void native_midi_stop()
   6.229 +{
   6.230 +	if (gTunePlayer)
   6.231 +		return;
   6.232 +
   6.233 +	/* Stop music */
   6.234 +	TuneStop(gTunePlayer, 0);
   6.235 +	
   6.236 +	/* Deallocate all instruments */
   6.237 +	TuneUnroll(gTunePlayer);
   6.238 +}
   6.239 +
   6.240 +int native_midi_active()
   6.241 +{
   6.242 +	if (gTunePlayer)
   6.243 +	{
   6.244 +		TuneStatus	ts;
   6.245 +
   6.246 +		TuneGetStatus(gTunePlayer,&ts);
   6.247 +		return ts.queueTime != 0;
   6.248 +	}
   6.249 +	else
   6.250 +		return 0;
   6.251 +}
   6.252 +
   6.253 +void native_midi_setvolume(int volume)
   6.254 +{
   6.255 +	if (gTunePlayer)
   6.256 +		return;
   6.257 +
   6.258 +	/* QTMA olume may range from 0.0 to 1.0 (in 16.16 fixed point encoding) */
   6.259 +	TuneSetVolume(gTunePlayer, (0x00010000 * volume)/SDL_MIX_MAXVOLUME);
   6.260 +}
   6.261 +
   6.262 +char *native_midi_error()
   6.263 +{
   6.264 +	return gErrorBuffer;
   6.265 +}
   6.266 +
   6.267 +Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts)
   6.268 +{
   6.269 +	int			part_poly[32];
   6.270 +	int			channel_to_part[16];
   6.271 +	
   6.272 +	int			channel_pan[16];
   6.273 +	int			channel_vol[16];
   6.274 +	int			channel_pitch_bend[16];
   6.275 +	
   6.276 +	int			lastEventTime = 0;
   6.277 +	int			tempo = 500000;
   6.278 +	double		Ippqn = 1.0 / (1000*ppqn);
   6.279 +	double		tick = tempo * Ippqn;
   6.280 +	MIDIEvent	*eventPos = evntlist;
   6.281 +	MIDIEvent	*noteOffPos;
   6.282 +	Uint32 		*tunePos, *endPos;
   6.283 +	Uint32		*tuneSequence;
   6.284 +	size_t		tuneSize;
   6.285 +	
   6.286 +	/* allocate space for the tune header */
   6.287 +	tuneSize = 5000;
   6.288 +	tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32));
   6.289 +	if (tuneSequence == NULL)
   6.290 +		return NULL;
   6.291 +	
   6.292 +	/* Set starting position in our tune memory */
   6.293 +	tunePos = tuneSequence;
   6.294 +	endPos = tuneSequence + tuneSize;
   6.295 +
   6.296 +	/* Initialise the arrays */
   6.297 +	memset(part_poly,0,sizeof(part_poly));
   6.298 +	
   6.299 +	memset(channel_to_part,-1,sizeof(channel_to_part));
   6.300 +	memset(channel_pan,-1,sizeof(channel_pan));
   6.301 +	memset(channel_vol,-1,sizeof(channel_vol));
   6.302 +	memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend));
   6.303 +	
   6.304 +	*numParts = 0;
   6.305 +	
   6.306 +	/*
   6.307 +	 * Now the major work - iterate over all GM events,
   6.308 +	 * and turn them into QuickTime Music format.
   6.309 +	 * At the same time, calculate the max. polyphony for each part,
   6.310 +	 * and also the part->instrument mapping.
   6.311 +	 */
   6.312 +	while(eventPos)
   6.313 +	{
   6.314 +		int status = (eventPos->status&0xF0)>>4;
   6.315 +		int channel = eventPos->status&0x0F;
   6.316 +		int part = channel_to_part[channel];
   6.317 +        int velocity, pitch;
   6.318 +        int value, controller;
   6.319 +        int bend;
   6.320 +        int newInst;
   6.321 +		
   6.322 +		/* Check if we are running low on space... */
   6.323 +		if((tunePos+16) > endPos)
   6.324 +		{
   6.325 +			/* Resize our data storage. */
   6.326 +			Uint32 		*oldTuneSequence = tuneSequence;
   6.327 +
   6.328 +			tuneSize += BUFFER_INCREMENT;
   6.329 +			tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32));
   6.330 +			if(oldTuneSequence != tuneSequence)
   6.331 +				tunePos += tuneSequence - oldTuneSequence;
   6.332 +			endPos = tuneSequence + tuneSize;
   6.333 +		}
   6.334 +		
   6.335 +		switch (status)
   6.336 +		{
   6.337 +		case MIDI_STATUS_NOTE_OFF:
   6.338 +			assert(part>=0 && part<=31);
   6.339 +
   6.340 +			/* Keep track of the polyphony of the current part */
   6.341 +			part_poly[part]--;
   6.342 +			break;
   6.343 +		case MIDI_STATUS_NOTE_ON:
   6.344 +			if (part < 0)
   6.345 +			{
   6.346 +				/* If no part is specified yet, we default to the first instrument, which
   6.347 +				   is piano (or the first drum kit if we are on the drum channel)
   6.348 +				*/
   6.349 +				int newInst;
   6.350 +				
   6.351 +				if (channel == 9)
   6.352 +					newInst = kFirstDrumkit + 1;		/* the first drum kit is the "no drum" kit! */
   6.353 +				else
   6.354 +					newInst = kFirstGMInstrument;
   6.355 +				part = channel_to_part[channel] = *numParts;
   6.356 +				part_to_inst[(*numParts)++] = newInst;
   6.357 +			}
   6.358 +			/* TODO - add support for more than 32 parts using eXtended QTMA events */
   6.359 +			assert(part<=31);
   6.360 +			
   6.361 +			/* Decode pitch & velocity */
   6.362 +			pitch = eventPos->data[0];
   6.363 +			velocity = eventPos->data[1];
   6.364 +			
   6.365 +			if (velocity == 0)
   6.366 +			{
   6.367 +				/* was a NOTE OFF in disguise, so we decrement the polyphony */
   6.368 +				part_poly[part]--;
   6.369 +			}
   6.370 +			else
   6.371 +			{
   6.372 +				/* Keep track of the polyphony of the current part */
   6.373 +				int foo = ++part_poly[part];
   6.374 +				if (part_poly_max[part] < foo)
   6.375 +					part_poly_max[part] = foo;
   6.376 +
   6.377 +				/* Now scan forward to find the matching NOTE OFF event */
   6.378 +				for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next)
   6.379 +				{
   6.380 +					if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF
   6.381 +						&& channel == (eventPos->status&0x0F)
   6.382 +						&& pitch == noteOffPos->data[0])
   6.383 +						break;
   6.384 +					/* NOTE ON with velocity == 0 is the same as a NOTE OFF */
   6.385 +					if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON
   6.386 +						&& channel == (eventPos->status&0x0F)
   6.387 +						&& pitch == noteOffPos->data[0]
   6.388 +						&& 0 == noteOffPos->data[1])
   6.389 +						break;
   6.390 +				}
   6.391 +				
   6.392 +				/* Did we find a note off? Should always be the case, but who knows... */
   6.393 +				if (noteOffPos)
   6.394 +				{
   6.395 +					/* We found a NOTE OFF, now calculate the note duration */
   6.396 +					int duration = (int)((noteOffPos->time - eventPos->time)*tick);
   6.397 +					
   6.398 +					REST_IF_NECESSARY();
   6.399 +					/* Now we need to check if we get along with a normal Note Even, or if we need an extended one... */
   6.400 +					if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127)
   6.401 +					{
   6.402 +						qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration);
   6.403 +						tunePos++;
   6.404 +					}
   6.405 +					else
   6.406 +					{
   6.407 +						qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration);
   6.408 +						tunePos+=2;
   6.409 +					}
   6.410 +				}
   6.411 +			}
   6.412 +			break;
   6.413 +		case MIDI_STATUS_AFTERTOUCH:
   6.414 +			/* NYI */
   6.415 +			break;
   6.416 +		case MIDI_STATUS_CONTROLLER:
   6.417 +			controller = eventPos->data[0];
   6.418 +			value = eventPos->data[1];
   6.419 +
   6.420 +			switch(controller)
   6.421 +			{
   6.422 +			case 0:	/* bank change - igore for now */
   6.423 +				break;
   6.424 +			case kControllerVolume:
   6.425 +				if(channel_vol[channel] != value<<8)
   6.426 +				{
   6.427 +					channel_vol[channel] = value<<8;
   6.428 +					if(part>=0 && part<=31)
   6.429 +					{
   6.430 +						REST_IF_NECESSARY();
   6.431 +						qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
   6.432 +						tunePos++;
   6.433 +					}
   6.434 +				}
   6.435 +				break;
   6.436 +			case kControllerPan:
   6.437 +				if(channel_pan[channel] != ((value-64)<<8))
   6.438 +				{
   6.439 +					channel_pan[channel] = (value-64)<<8;
   6.440 +					if(part>=0 && part<=31)
   6.441 +					{
   6.442 +						REST_IF_NECESSARY();
   6.443 +						qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
   6.444 +						tunePos++;
   6.445 +					}
   6.446 +				}
   6.447 +				break;
   6.448 +			default:
   6.449 +				/* No other controllers implemented yet */;
   6.450 +				break;
   6.451 +			}
   6.452 +			
   6.453 +			break;
   6.454 +		case MIDI_STATUS_PROG_CHANGE:
   6.455 +			/* Instrument changed */
   6.456 +			newInst = eventPos->data[0];
   6.457 +			
   6.458 +			/* Channel 9 (the 10th channel) is different, it indicates a drum kit */
   6.459 +			if (channel == 9)
   6.460 +				newInst += kFirstDrumkit;
   6.461 +			else
   6.462 +				newInst += kFirstGMInstrument;
   6.463 +			/* Only if the instrument for this channel *really* changed, add a new part. */
   6.464 +			if(newInst != part_to_inst[part])
   6.465 +			{
   6.466 +				/* TODO maybe make use of kGeneralEventPartChange here,
   6.467 +				   to help QT reuse note channels?
   6.468 +				*/
   6.469 +				part = channel_to_part[channel] = *numParts;
   6.470 +				part_to_inst[(*numParts)++] = newInst;
   6.471 +
   6.472 +				if(channel_vol[channel] >= 0)
   6.473 +				{
   6.474 +					REST_IF_NECESSARY();
   6.475 +					qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
   6.476 +					tunePos++;
   6.477 +				}
   6.478 +				if(channel_pan[channel] >= 0)
   6.479 +				{
   6.480 +					REST_IF_NECESSARY();
   6.481 +					qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
   6.482 +					tunePos++;
   6.483 +				}
   6.484 +				if(channel_pitch_bend[channel] >= 0)
   6.485 +				{
   6.486 +					REST_IF_NECESSARY();
   6.487 +					qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]);
   6.488 +					tunePos++;
   6.489 +				}			
   6.490 +			}
   6.491 +			break;
   6.492 +		case MIDI_STATUS_PRESSURE:
   6.493 +			/* NYI */
   6.494 +			break;
   6.495 +		case MIDI_STATUS_PITCH_WHEEL:
   6.496 +			/* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones
   6.497 +			   but for QTMA, we specify it as a 8.8 fixed point of semitones
   6.498 +			   TODO: detect "pitch bend range changes" & honor them!
   6.499 +			*/
   6.500 +			bend = eventPos->data[0] & 0x7f | (eventPos->data[1] & 0x7f) << 7;
   6.501 +			
   6.502 +			/* "Center" the bend */
   6.503 +			bend -= 0x2000;
   6.504 +			
   6.505 +			/* Move it to our format: */
   6.506 +			bend <<= 4;
   6.507 +			
   6.508 +			/* If it turns out the pitch bend didn't change, stop here */
   6.509 +			if(channel_pitch_bend[channel] == bend)
   6.510 +				break;
   6.511 +			
   6.512 +			channel_pitch_bend[channel] = bend;
   6.513 +			if(part>=0 && part<=31)
   6.514 +			{
   6.515 +				/* Stuff a control event */
   6.516 +				REST_IF_NECESSARY();
   6.517 +				qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend);
   6.518 +				tunePos++;
   6.519 +			}			
   6.520 +			break;
   6.521 +		case MIDI_STATUS_SYSEX:
   6.522 +			if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */
   6.523 +			{
   6.524 +				tempo = (eventPos->extraData[0] << 16) +
   6.525 +					(eventPos->extraData[1] << 8) +
   6.526 +					eventPos->extraData[2];
   6.527 +				
   6.528 +				tick = tempo * Ippqn;
   6.529 +			}
   6.530 +			break;
   6.531 +		}
   6.532 +		
   6.533 +		/* on to the next event */
   6.534 +		eventPos = eventPos->next;
   6.535 +	} 
   6.536 +	
   6.537 +	/* Finally, place an end marker */
   6.538 +	*tunePos = kEndMarkerValue;
   6.539 +	
   6.540 +	return tuneSequence;
   6.541 +}
   6.542 +
   6.543 +Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts)
   6.544 +{
   6.545 +	Uint32			*myHeader;
   6.546 +	Uint32			*myPos1, *myPos2;		/* pointers to the head and tail long words of a music event */
   6.547 +	NoteRequest		*myNoteRequest;
   6.548 +	NoteAllocator	myNoteAllocator;		/* for the NAStuffToneDescription call */
   6.549 +	ComponentResult	myErr = noErr;
   6.550 +	int				part;
   6.551 +
   6.552 +	myHeader = NULL;
   6.553 +	myNoteAllocator = NULL;
   6.554 +
   6.555 +	/*
   6.556 +	 * Open up the Note Allocator
   6.557 +	 */
   6.558 +	myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0);
   6.559 +	if (myNoteAllocator == NULL)
   6.560 +		goto bail;
   6.561 +	
   6.562 +	/*
   6.563 +	 * Allocate space for the tune header
   6.564 +	 */
   6.565 +	myHeader = (Uint32 *)
   6.566 +			NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32));
   6.567 +	if (myHeader == NULL)
   6.568 +		goto bail;
   6.569 +	
   6.570 +	myPos1 = myHeader;
   6.571 +	
   6.572 +	/*
   6.573 +	 * Loop over all parts
   6.574 +	 */
   6.575 +	for(part = 0; part < numParts; ++part)
   6.576 +	{
   6.577 +		/*
   6.578 +		 * Stuff request for the instrument with the given polyphony
   6.579 +		 */
   6.580 +		myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */
   6.581 +		qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength);
   6.582 +		myNoteRequest = (NoteRequest *)(myPos1 + 1);
   6.583 +		myNoteRequest->info.flags = 0;
   6.584 +		myNoteRequest->info.reserved = 0;
   6.585 +		myNoteRequest->info.polyphony = part_poly_max[part];
   6.586 +		myNoteRequest->info.typicalPolyphony = 0x00010000;
   6.587 +		myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone);
   6.588 +		if (myErr != noErr)
   6.589 +			goto bail;
   6.590 +		
   6.591 +		/* move pointer to beginning of next event */
   6.592 +		myPos1 += kNoteRequestEventLength;
   6.593 +	}
   6.594 +
   6.595 +	*myPos1 = kEndMarkerValue;		/* end of sequence marker */
   6.596 +
   6.597 +
   6.598 +bail:
   6.599 +	if(myNoteAllocator)
   6.600 +		CloseComponent(myNoteAllocator);
   6.601 +
   6.602 +	/* if we encountered an error, dispose of the storage we allocated and return NULL */
   6.603 +	if (myErr != noErr) {
   6.604 +		DisposePtr((Ptr)myHeader);
   6.605 +		myHeader = NULL;
   6.606 +	}
   6.607 +
   6.608 +	return myHeader;
   6.609 +}
   6.610 +
   6.611 +#endif /* MacOS native MIDI support */