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