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