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