native_midi/native_midi_mac.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 03 Oct 2009 20:43:33 +0000
changeset 419 e27fe0bfe470
parent 401 ee71829d80e7
child 458 c42a228f3d90
permissions -rw-r--r--
Sam Lantinga - Sat Oct 3 13:33:36 PDT 2009
* MOD support uses libmikmod and is dynamically loaded by default
     1 /*
     2     native_midi_mac:  Native Midi support on MacOS for the SDL_mixer library
     3     Copyright (C) 2001  Max Horn
     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     Max Horn
    20     max@quendi.de
    21 */
    22 #include "SDL_config.h"
    23 #include "SDL_endian.h"
    24 
    25 #if __MACOS__ || __MACOSX__
    26 
    27 #include "native_midi.h"
    28 #include "native_midi_common.h"
    29 
    30 #if __MACOSX__
    31 #include <QuickTime/QuickTimeMusic.h>
    32 #else
    33 #include <QuickTimeMusic.h>
    34 #endif
    35 
    36 #include <assert.h>
    37 #include <stdlib.h>
    38 #include <string.h>
    39 
    40 
    41 /* Native Midi song */
    42 struct _NativeMidiSong
    43 {
    44 	Uint32		*tuneSequence;
    45 	Uint32		*tuneHeader;
    46 };
    47 
    48 enum
    49 {
    50 	/* number of (32-bit) long words in a note request event */
    51 	kNoteRequestEventLength = ((sizeof(NoteRequest)/sizeof(long)) + 2),
    52 
    53 	/* number of (32-bit) long words in a marker event */
    54 	kMarkerEventLength	= 1,
    55 
    56 	/* number of (32-bit) long words in a general event, minus its data */
    57 	kGeneralEventLength	= 2
    58 };
    59 
    60 #define ERROR_BUF_SIZE			256
    61 #define	BUFFER_INCREMENT		5000
    62 
    63 #define REST_IF_NECESSARY()	do {\
    64 			int timeDiff = eventPos->time - lastEventTime;	\
    65 			if(timeDiff)	\
    66 			{	\
    67 				timeDiff = (int)(timeDiff*tick);	\
    68 				qtma_StuffRestEvent(*tunePos, timeDiff);	\
    69 				tunePos++;	\
    70 				lastEventTime = eventPos->time;	\
    71 			}	\
    72 		} while(0)
    73 
    74 
    75 static Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts);
    76 static Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts);
    77 
    78 /* The global TunePlayer instance */
    79 static TunePlayer	gTunePlayer = NULL;
    80 static int			gInstaceCount = 0;
    81 static Uint32		*gCurrentTuneSequence = NULL;
    82 static char			gErrorBuffer[ERROR_BUF_SIZE] = "";
    83 
    84 
    85 /* Check whether QuickTime is available */
    86 int native_midi_detect()
    87 {
    88 	/* TODO */
    89 	return 1;
    90 }
    91 
    92 NativeMidiSong *native_midi_loadsong(const char *midifile)
    93 {
    94 	NativeMidiSong	*song = NULL;
    95 	MIDIEvent		*evntlist = NULL;
    96 	int				part_to_inst[32];
    97 	int				part_poly_max[32];
    98 	int				numParts = 0;
    99 	Uint16			ppqn;
   100 	SDL_RWops		*rw;
   101 
   102 	/* Init the arrays */
   103 	memset(part_poly_max,0,sizeof(part_poly_max));
   104 	memset(part_to_inst,-1,sizeof(part_to_inst));
   105 	
   106 	/* Attempt to load the midi file */
   107 	rw = SDL_RWFromFile(midifile, "rb");
   108 	if (rw) {
   109 		evntlist = CreateMIDIEventList(rw, &ppqn);
   110 		SDL_RWclose(rw);
   111 		if (!evntlist)
   112 			goto bail;
   113 	}
   114 
   115 	/* Allocate memory for the song struct */
   116 	song = malloc(sizeof(NativeMidiSong));
   117 	if (!song)
   118 		goto bail;
   119 
   120 	/* Build a tune sequence from the event list */
   121 	song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts);
   122 	if(!song->tuneSequence)
   123 		goto bail;
   124 
   125 	/* Now build a tune header from the data we collect above, create
   126 	   all parts as needed and assign them the correct instrument.
   127 	*/
   128 	song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts);
   129 	if(!song->tuneHeader)
   130 		goto bail;
   131 	
   132 	/* Increment the instance count */
   133 	gInstaceCount++;
   134 	if (gTunePlayer == NULL)
   135 		gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0);
   136 
   137 	/* Finally, free the event list */
   138 	FreeMIDIEventList(evntlist);
   139 	
   140 	return song;
   141 	
   142 bail:
   143 	if (evntlist)
   144 		FreeMIDIEventList(evntlist);
   145 	
   146 	if (song)
   147 	{
   148 		if(song->tuneSequence)
   149 			free(song->tuneSequence);
   150 		
   151 		if(song->tuneHeader)
   152 			DisposePtr((Ptr)song->tuneHeader);
   153 
   154 		free(song);
   155 	}
   156 	
   157 	return NULL;
   158 }
   159 
   160 NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
   161 {
   162 	NativeMidiSong	*song = NULL;
   163 	MIDIEvent		*evntlist = NULL;
   164 	int				part_to_inst[32];
   165 	int				part_poly_max[32];
   166 	int				numParts = 0;
   167 	Uint16			ppqn;
   168 
   169 	/* Init the arrays */
   170 	memset(part_poly_max,0,sizeof(part_poly_max));
   171 	memset(part_to_inst,-1,sizeof(part_to_inst));
   172 	
   173 	/* Attempt to load the midi file */
   174 	evntlist = CreateMIDIEventList(rw, &ppqn);
   175 	if (!evntlist)
   176 		goto bail;
   177 
   178 	/* Allocate memory for the song struct */
   179 	song = malloc(sizeof(NativeMidiSong));
   180 	if (!song)
   181 		goto bail;
   182 
   183 	/* Build a tune sequence from the event list */
   184 	song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts);
   185 	if(!song->tuneSequence)
   186 		goto bail;
   187 
   188 	/* Now build a tune header from the data we collect above, create
   189 	   all parts as needed and assign them the correct instrument.
   190 	*/
   191 	song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts);
   192 	if(!song->tuneHeader)
   193 		goto bail;
   194 	
   195 	/* Increment the instance count */
   196 	gInstaceCount++;
   197 	if (gTunePlayer == NULL)
   198 		gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0);
   199 
   200 	/* Finally, free the event list */
   201 	FreeMIDIEventList(evntlist);
   202 	
   203 	return song;
   204 	
   205 bail:
   206 	if (evntlist)
   207 		FreeMIDIEventList(evntlist);
   208 	
   209 	if (song)
   210 	{
   211 		if(song->tuneSequence)
   212 			free(song->tuneSequence);
   213 		
   214 		if(song->tuneHeader)
   215 			DisposePtr((Ptr)song->tuneHeader);
   216 
   217 		free(song);
   218 	}
   219 	
   220 	return NULL;
   221 }
   222 
   223 void native_midi_freesong(NativeMidiSong *song)
   224 {
   225 	if(!song || !song->tuneSequence)
   226 		return;
   227 
   228 	/* If this is the currently playing song, stop it now */	
   229 	if (song->tuneSequence == gCurrentTuneSequence)
   230 		native_midi_stop();
   231 	
   232 	/* Finally, free the data storage */
   233 	free(song->tuneSequence);
   234 	DisposePtr((Ptr)song->tuneHeader);
   235 	free(song);
   236 
   237 	/* Increment the instance count */
   238 	gInstaceCount--;
   239 	if ((gTunePlayer != NULL) && (gInstaceCount == 0))
   240 	{
   241 		CloseComponent(gTunePlayer);
   242 		gTunePlayer = NULL;
   243 	}
   244 }
   245 
   246 void native_midi_start(NativeMidiSong *song)
   247 {
   248 	UInt32		queueFlags = 0;
   249 	ComponentResult tpError;
   250 	
   251 	assert (gTunePlayer != NULL);
   252 	
   253 	SDL_PauseAudio(1);
   254 	SDL_UnlockAudio();
   255     
   256 	/* First, stop the currently playing music */
   257 	native_midi_stop();
   258 	
   259 	/* Set up the queue flags */
   260 	queueFlags = kTuneStartNow;
   261 
   262 	/* Set the time scale (units per second), we want milliseconds */
   263 	tpError = TuneSetTimeScale(gTunePlayer, 1000);
   264 	if (tpError != noErr)
   265 	{
   266 		strncpy (gErrorBuffer, "MIDI error during TuneSetTimeScale", ERROR_BUF_SIZE);
   267 		goto done;
   268 	}
   269 
   270 	/* Set the header, to tell what instruments are used */
   271 	tpError = TuneSetHeader(gTunePlayer, (UInt32 *)song->tuneHeader);
   272 	if (tpError != noErr)
   273 	{
   274 		strncpy (gErrorBuffer, "MIDI error during TuneSetHeader", ERROR_BUF_SIZE);
   275 		goto done;
   276 	}
   277 	
   278 	/* Have it allocate whatever resources are needed */
   279 	tpError = TunePreroll(gTunePlayer);
   280 	if (tpError != noErr)
   281 	{
   282 		strncpy (gErrorBuffer, "MIDI error during TunePreroll", ERROR_BUF_SIZE);
   283 		goto done;
   284 	}
   285 
   286 	/* We want to play at normal volume */
   287 	tpError = TuneSetVolume(gTunePlayer, 0x00010000);
   288 	if (tpError != noErr)
   289 	{
   290 		strncpy (gErrorBuffer, "MIDI error during TuneSetVolume", ERROR_BUF_SIZE);
   291 		goto done;
   292 	}
   293 	
   294 	/* Finally, start playing the full song */
   295 	gCurrentTuneSequence = song->tuneSequence;
   296 	tpError = TuneQueue(gTunePlayer, (UInt32 *)song->tuneSequence, 0x00010000, 0, 0xFFFFFFFF, queueFlags, NULL, 0);
   297 	if (tpError != noErr)
   298 	{
   299 		strncpy (gErrorBuffer, "MIDI error during TuneQueue", ERROR_BUF_SIZE);
   300 		goto done;
   301 	}
   302     
   303 done:
   304 	SDL_LockAudio();
   305 	SDL_PauseAudio(0);
   306 }
   307 
   308 void native_midi_stop()
   309 {
   310 	if (gTunePlayer == NULL)
   311 		return;
   312 
   313 	/* Stop music */
   314 	TuneStop(gTunePlayer, 0);
   315 	
   316 	/* Deallocate all instruments */
   317 	TuneUnroll(gTunePlayer);
   318 }
   319 
   320 int native_midi_active()
   321 {
   322 	if (gTunePlayer != NULL)
   323 	{
   324 		TuneStatus	ts;
   325 
   326 		TuneGetStatus(gTunePlayer,&ts);
   327 		return ts.queueTime != 0;
   328 	}
   329 	else
   330 		return 0;
   331 }
   332 
   333 void native_midi_setvolume(int volume)
   334 {
   335 	if (gTunePlayer == NULL)
   336 		return;
   337 
   338 	/* QTMA olume may range from 0.0 to 1.0 (in 16.16 fixed point encoding) */
   339 	TuneSetVolume(gTunePlayer, (0x00010000 * volume)/SDL_MIX_MAXVOLUME);
   340 }
   341 
   342 const char *native_midi_error(void)
   343 {
   344 	return gErrorBuffer;
   345 }
   346 
   347 Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts)
   348 {
   349 	int			part_poly[32];
   350 	int			channel_to_part[16];
   351 	
   352 	int			channel_pan[16];
   353 	int			channel_vol[16];
   354 	int			channel_pitch_bend[16];
   355 	
   356 	int			lastEventTime = 0;
   357 	int			tempo = 500000;
   358 	double		Ippqn = 1.0 / (1000*ppqn);
   359 	double		tick = tempo * Ippqn;
   360 	MIDIEvent	*eventPos = evntlist;
   361 	MIDIEvent	*noteOffPos;
   362 	Uint32 		*tunePos, *endPos;
   363 	Uint32		*tuneSequence;
   364 	size_t		tuneSize;
   365 	
   366 	/* allocate space for the tune header */
   367 	tuneSize = 5000;
   368 	tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32));
   369 	if (tuneSequence == NULL)
   370 		return NULL;
   371 	
   372 	/* Set starting position in our tune memory */
   373 	tunePos = tuneSequence;
   374 	endPos = tuneSequence + tuneSize;
   375 
   376 	/* Initialise the arrays */
   377 	memset(part_poly,0,sizeof(part_poly));
   378 	
   379 	memset(channel_to_part,-1,sizeof(channel_to_part));
   380 	memset(channel_pan,-1,sizeof(channel_pan));
   381 	memset(channel_vol,-1,sizeof(channel_vol));
   382 	memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend));
   383 	
   384 	*numParts = 0;
   385 	
   386 	/*
   387 	 * Now the major work - iterate over all GM events,
   388 	 * and turn them into QuickTime Music format.
   389 	 * At the same time, calculate the max. polyphony for each part,
   390 	 * and also the part->instrument mapping.
   391 	 */
   392 	while(eventPos)
   393 	{
   394 		int status = (eventPos->status&0xF0)>>4;
   395 		int channel = eventPos->status&0x0F;
   396 		int part = channel_to_part[channel];
   397         int velocity, pitch;
   398         int value, controller;
   399         int bend;
   400         int newInst;
   401 		
   402 		/* Check if we are running low on space... */
   403 		if((tunePos+16) > endPos)
   404 		{
   405 			/* Resize our data storage. */
   406 			Uint32 		*oldTuneSequence = tuneSequence;
   407 
   408 			tuneSize += BUFFER_INCREMENT;
   409 			tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32));
   410 			if(oldTuneSequence != tuneSequence)
   411 				tunePos += tuneSequence - oldTuneSequence;
   412 			endPos = tuneSequence + tuneSize;
   413 		}
   414 		
   415 		switch (status)
   416 		{
   417 		case MIDI_STATUS_NOTE_OFF:
   418 			assert(part>=0 && part<=31);
   419 
   420 			/* Keep track of the polyphony of the current part */
   421 			part_poly[part]--;
   422 			break;
   423 		case MIDI_STATUS_NOTE_ON:
   424 			if (part < 0)
   425 			{
   426 				/* If no part is specified yet, we default to the first instrument, which
   427 				   is piano (or the first drum kit if we are on the drum channel)
   428 				*/
   429 				int newInst;
   430 				
   431 				if (channel == 9)
   432 					newInst = kFirstDrumkit + 1;		/* the first drum kit is the "no drum" kit! */
   433 				else
   434 					newInst = kFirstGMInstrument;
   435 				part = channel_to_part[channel] = *numParts;
   436 				part_to_inst[(*numParts)++] = newInst;
   437 			}
   438 			/* TODO - add support for more than 32 parts using eXtended QTMA events */
   439 			assert(part<=31);
   440 			
   441 			/* Decode pitch & velocity */
   442 			pitch = eventPos->data[0];
   443 			velocity = eventPos->data[1];
   444 			
   445 			if (velocity == 0)
   446 			{
   447 				/* was a NOTE OFF in disguise, so we decrement the polyphony */
   448 				part_poly[part]--;
   449 			}
   450 			else
   451 			{
   452 				/* Keep track of the polyphony of the current part */
   453 				int foo = ++part_poly[part];
   454 				if (part_poly_max[part] < foo)
   455 					part_poly_max[part] = foo;
   456 
   457 				/* Now scan forward to find the matching NOTE OFF event */
   458 				for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next)
   459 				{
   460 					if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF
   461 						&& channel == (eventPos->status&0x0F)
   462 						&& pitch == noteOffPos->data[0])
   463 						break;
   464 					/* NOTE ON with velocity == 0 is the same as a NOTE OFF */
   465 					if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON
   466 						&& channel == (eventPos->status&0x0F)
   467 						&& pitch == noteOffPos->data[0]
   468 						&& 0 == noteOffPos->data[1])
   469 						break;
   470 				}
   471 				
   472 				/* Did we find a note off? Should always be the case, but who knows... */
   473 				if (noteOffPos)
   474 				{
   475 					/* We found a NOTE OFF, now calculate the note duration */
   476 					int duration = (int)((noteOffPos->time - eventPos->time)*tick);
   477 					
   478 					REST_IF_NECESSARY();
   479 					/* Now we need to check if we get along with a normal Note Event, or if we need an extended one... */
   480 					if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127)
   481 					{
   482 						qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration);
   483 						tunePos++;
   484 					}
   485 					else
   486 					{
   487 						qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration);
   488 						tunePos+=2;
   489 					}
   490 				}
   491 			}
   492 			break;
   493 		case MIDI_STATUS_AFTERTOUCH:
   494 			/* NYI - use kControllerAfterTouch. But how are the parameters to be mapped? */
   495 			break;
   496 		case MIDI_STATUS_CONTROLLER:
   497 			controller = eventPos->data[0];
   498 			value = eventPos->data[1];
   499 
   500 			switch(controller)
   501 			{
   502 			case 0:	/* bank change - igore for now */
   503 				break;
   504 			case kControllerVolume:
   505 				if(channel_vol[channel] != value<<8)
   506 				{
   507 					channel_vol[channel] = value<<8;
   508 					if(part>=0 && part<=31)
   509 					{
   510 						REST_IF_NECESSARY();
   511 						qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
   512 						tunePos++;
   513 					}
   514 				}
   515 				break;
   516 			case kControllerPan:
   517 				if(channel_pan[channel] != (value << 1) + 256)
   518 				{
   519 					channel_pan[channel] = (value << 1) + 256;
   520 					if(part>=0 && part<=31)
   521 					{
   522 						REST_IF_NECESSARY();
   523 						qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
   524 						tunePos++;
   525 					}
   526 				}
   527 				break;
   528 			default:
   529 				/* No other controllers implemented yet */;
   530 				break;
   531 			}
   532 			
   533 			break;
   534 		case MIDI_STATUS_PROG_CHANGE:
   535 			/* Instrument changed */
   536 			newInst = eventPos->data[0];
   537 			
   538 			/* Channel 9 (the 10th channel) is different, it indicates a drum kit */
   539 			if (channel == 9)
   540 				newInst += kFirstDrumkit;
   541 			else
   542 				newInst += kFirstGMInstrument;
   543 			/* Only if the instrument for this channel *really* changed, add a new part. */
   544 			if(newInst != part_to_inst[part])
   545 			{
   546 				/* TODO maybe make use of kGeneralEventPartChange here,
   547 				   to help QT reuse note channels?
   548 				*/
   549 				part = channel_to_part[channel] = *numParts;
   550 				part_to_inst[(*numParts)++] = newInst;
   551 
   552 				if(channel_vol[channel] >= 0)
   553 				{
   554 					REST_IF_NECESSARY();
   555 					qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
   556 					tunePos++;
   557 				}
   558 				if(channel_pan[channel] >= 0)
   559 				{
   560 					REST_IF_NECESSARY();
   561 					qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
   562 					tunePos++;
   563 				}
   564 				if(channel_pitch_bend[channel] >= 0)
   565 				{
   566 					REST_IF_NECESSARY();
   567 					qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]);
   568 					tunePos++;
   569 				}			
   570 			}
   571 			break;
   572 		case MIDI_STATUS_PRESSURE:
   573 			/* NYI */
   574 			break;
   575 		case MIDI_STATUS_PITCH_WHEEL:
   576 			/* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones
   577 			   but for QTMA, we specify it as a 8.8 fixed point of semitones
   578 			   TODO: detect "pitch bend range changes" & honor them!
   579 			*/
   580 			bend = (eventPos->data[0] & 0x7f) | ((eventPos->data[1] & 0x7f) << 7);
   581 			
   582 			/* "Center" the bend */
   583 			bend -= 0x2000;
   584 			
   585 			/* Move it to our format: */
   586 			bend <<= 4;
   587 			
   588 			/* If it turns out the pitch bend didn't change, stop here */
   589 			if(channel_pitch_bend[channel] == bend)
   590 				break;
   591 			
   592 			channel_pitch_bend[channel] = bend;
   593 			if(part>=0 && part<=31)
   594 			{
   595 				/* Stuff a control event */
   596 				REST_IF_NECESSARY();
   597 				qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend);
   598 				tunePos++;
   599 			}			
   600 			break;
   601 		case MIDI_STATUS_SYSEX:
   602 			if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */
   603 			{
   604 				tempo = (eventPos->extraData[0] << 16) +
   605 					(eventPos->extraData[1] << 8) +
   606 					eventPos->extraData[2];
   607 				
   608 				tick = tempo * Ippqn;
   609 			}
   610 			break;
   611 		}
   612 		
   613 		/* on to the next event */
   614 		eventPos = eventPos->next;
   615 	} 
   616 	
   617 	/* Finally, place an end marker */
   618 	*tunePos = kEndMarkerValue;
   619 	
   620 	return tuneSequence;
   621 }
   622 
   623 Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts)
   624 {
   625 	Uint32			*myHeader;
   626 	Uint32			*myPos1, *myPos2;		/* pointers to the head and tail long words of a music event */
   627 	NoteRequest		*myNoteRequest;
   628 	NoteAllocator	myNoteAllocator;		/* for the NAStuffToneDescription call */
   629 	ComponentResult	myErr = noErr;
   630 	int				part;
   631 
   632 	myHeader = NULL;
   633 	myNoteAllocator = NULL;
   634 
   635 	/*
   636 	 * Open up the Note Allocator
   637 	 */
   638 	myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0);
   639 	if (myNoteAllocator == NULL)
   640 		goto bail;
   641 	
   642 	/*
   643 	 * Allocate space for the tune header
   644 	 */
   645 	myHeader = (Uint32 *)
   646 			NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32));
   647 	if (myHeader == NULL)
   648 		goto bail;
   649 	
   650 	myPos1 = myHeader;
   651 	
   652 	/*
   653 	 * Loop over all parts
   654 	 */
   655 	for(part = 0; part < numParts; ++part)
   656 	{
   657 		/*
   658 		 * Stuff request for the instrument with the given polyphony
   659 		 */
   660 		myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */
   661 		qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength);
   662 		myNoteRequest = (NoteRequest *)(myPos1 + 1);
   663 		myNoteRequest->info.flags = 0;
   664 		/* I'm told by the Apple people that the Quicktime types were poorly designed and it was 
   665 		 * too late to change them. On little endian, the BigEndian(Short|Fixed) types are structs
   666 		 * while on big endian they are primitive types. Furthermore, Quicktime failed to 
   667 		 * provide setter and getter functions. To get this to work, we need to case the 
   668 		 * code for the two possible situations.
   669 		 * My assumption is that the right-side value was always expected to be BigEndian
   670 		 * as it was written way before the Universal Binary transition. So in the little endian
   671 		 * case, OSSwap is used.
   672 		 */
   673 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   674 		myNoteRequest->info.polyphony.bigEndianValue = OSSwapHostToBigInt16(part_poly_max[part]);
   675 		myNoteRequest->info.typicalPolyphony.bigEndianValue = OSSwapHostToBigInt32(0x00010000);
   676 #else
   677 		myNoteRequest->info.polyphony = part_poly_max[part];
   678 		myNoteRequest->info.typicalPolyphony = 0x00010000;
   679 #endif
   680 		myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone);
   681 		if (myErr != noErr)
   682 			goto bail;
   683 		
   684 		/* move pointer to beginning of next event */
   685 		myPos1 += kNoteRequestEventLength;
   686 	}
   687 
   688 	*myPos1 = kEndMarkerValue;		/* end of sequence marker */
   689 
   690 
   691 bail:
   692 	if(myNoteAllocator)
   693 		CloseComponent(myNoteAllocator);
   694 
   695 	/* if we encountered an error, dispose of the storage we allocated and return NULL */
   696 	if (myErr != noErr) {
   697 		DisposePtr((Ptr)myHeader);
   698 		myHeader = NULL;
   699 	}
   700 
   701 	return myHeader;
   702 }
   703 
   704 #endif /* MacOS native MIDI support */