native_midi/native_midi_mac.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 29 Jan 2016 12:44:13 -0800
changeset 718 fb0562cc1559
parent 625 1d489d8ec2e0
child 779 a2b494c054d5
permissions -rw-r--r--
Added Mix_OpenAudioDevice() so you can specify the audio device to open
     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 */
    86 int native_midi_detect()
    87 {
    88     /* TODO */
    89     return 1;
    90 }
    91 
    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 
   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 
   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 
   245 void native_midi_stop()
   246 {
   247     if (gTunePlayer == NULL)
   248         return;
   249 
   250     /* Stop music */
   251     TuneStop(gTunePlayer, 0);
   252 
   253     /* Deallocate all instruments */
   254     TuneUnroll(gTunePlayer);
   255 }
   256 
   257 int native_midi_active()
   258 {
   259     if (gTunePlayer != NULL)
   260     {
   261         TuneStatus  ts;
   262 
   263         TuneGetStatus(gTunePlayer,&ts);
   264         return ts.queueTime != 0;
   265     }
   266     else
   267         return 0;
   268 }
   269 
   270 void native_midi_setvolume(int volume)
   271 {
   272     if (gTunePlayer == NULL)
   273         return;
   274 
   275     /* QTMA olume may range from 0.0 to 1.0 (in 16.16 fixed point encoding) */
   276     TuneSetVolume(gTunePlayer, (0x00010000 * volume)/SDL_MIX_MAXVOLUME);
   277 }
   278 
   279 const char *native_midi_error(void)
   280 {
   281     return gErrorBuffer;
   282 }
   283 
   284 Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts)
   285 {
   286     int         part_poly[32];
   287     int         channel_to_part[16];
   288 
   289     int         channel_pan[16];
   290     int         channel_vol[16];
   291     int         channel_pitch_bend[16];
   292 
   293     int         lastEventTime = 0;
   294     int         tempo = 500000;
   295     double      Ippqn = 1.0 / (1000*ppqn);
   296     double      tick = tempo * Ippqn;
   297     MIDIEvent   *eventPos = evntlist;
   298     MIDIEvent   *noteOffPos;
   299     Uint32      *tunePos, *endPos;
   300     Uint32      *tuneSequence;
   301     size_t      tuneSize;
   302 
   303     /* allocate space for the tune header */
   304     tuneSize = 5000;
   305     tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32));
   306     if (tuneSequence == NULL)
   307         return NULL;
   308 
   309     /* Set starting position in our tune memory */
   310     tunePos = tuneSequence;
   311     endPos = tuneSequence + tuneSize;
   312 
   313     /* Initialise the arrays */
   314     memset(part_poly,0,sizeof(part_poly));
   315 
   316     memset(channel_to_part,-1,sizeof(channel_to_part));
   317     memset(channel_pan,-1,sizeof(channel_pan));
   318     memset(channel_vol,-1,sizeof(channel_vol));
   319     memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend));
   320 
   321     *numParts = 0;
   322 
   323     /*
   324      * Now the major work - iterate over all GM events,
   325      * and turn them into QuickTime Music format.
   326      * At the same time, calculate the max. polyphony for each part,
   327      * and also the part->instrument mapping.
   328      */
   329     while(eventPos)
   330     {
   331         int status = (eventPos->status&0xF0)>>4;
   332         int channel = eventPos->status&0x0F;
   333         int part = channel_to_part[channel];
   334         int velocity, pitch;
   335         int value, controller;
   336         int bend;
   337         int newInst;
   338 
   339         /* Check if we are running low on space... */
   340         if((tunePos+16) > endPos)
   341         {
   342             /* Resize our data storage. */
   343             Uint32      *oldTuneSequence = tuneSequence;
   344 
   345             tuneSize += BUFFER_INCREMENT;
   346             tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32));
   347             if(oldTuneSequence != tuneSequence)
   348                 tunePos += tuneSequence - oldTuneSequence;
   349             endPos = tuneSequence + tuneSize;
   350         }
   351 
   352         switch (status)
   353         {
   354         case MIDI_STATUS_NOTE_OFF:
   355             assert(part>=0 && part<=31);
   356 
   357             /* Keep track of the polyphony of the current part */
   358             part_poly[part]--;
   359             break;
   360         case MIDI_STATUS_NOTE_ON:
   361             if (part < 0)
   362             {
   363                 /* If no part is specified yet, we default to the first instrument, which
   364                    is piano (or the first drum kit if we are on the drum channel)
   365                 */
   366                 int newInst;
   367 
   368                 if (channel == 9)
   369                     newInst = kFirstDrumkit + 1;        /* the first drum kit is the "no drum" kit! */
   370                 else
   371                     newInst = kFirstGMInstrument;
   372                 part = channel_to_part[channel] = *numParts;
   373                 part_to_inst[(*numParts)++] = newInst;
   374             }
   375             /* TODO - add support for more than 32 parts using eXtended QTMA events */
   376             assert(part<=31);
   377 
   378             /* Decode pitch & velocity */
   379             pitch = eventPos->data[0];
   380             velocity = eventPos->data[1];
   381 
   382             if (velocity == 0)
   383             {
   384                 /* was a NOTE OFF in disguise, so we decrement the polyphony */
   385                 part_poly[part]--;
   386             }
   387             else
   388             {
   389                 /* Keep track of the polyphony of the current part */
   390                 int foo = ++part_poly[part];
   391                 if (part_poly_max[part] < foo)
   392                     part_poly_max[part] = foo;
   393 
   394                 /* Now scan forward to find the matching NOTE OFF event */
   395                 for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next)
   396                 {
   397                     if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF
   398                         && channel == (eventPos->status&0x0F)
   399                         && pitch == noteOffPos->data[0])
   400                         break;
   401                     /* NOTE ON with velocity == 0 is the same as a NOTE OFF */
   402                     if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON
   403                         && channel == (eventPos->status&0x0F)
   404                         && pitch == noteOffPos->data[0]
   405                         && 0 == noteOffPos->data[1])
   406                         break;
   407                 }
   408 
   409                 /* Did we find a note off? Should always be the case, but who knows... */
   410                 if (noteOffPos)
   411                 {
   412                     /* We found a NOTE OFF, now calculate the note duration */
   413                     int duration = (int)((noteOffPos->time - eventPos->time)*tick);
   414 
   415                     REST_IF_NECESSARY();
   416                     /* Now we need to check if we get along with a normal Note Event, or if we need an extended one... */
   417                     if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127)
   418                     {
   419                         qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration);
   420                         tunePos++;
   421                     }
   422                     else
   423                     {
   424                         qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration);
   425                         tunePos+=2;
   426                     }
   427                 }
   428             }
   429             break;
   430         case MIDI_STATUS_AFTERTOUCH:
   431             /* NYI - use kControllerAfterTouch. But how are the parameters to be mapped? */
   432             break;
   433         case MIDI_STATUS_CONTROLLER:
   434             controller = eventPos->data[0];
   435             value = eventPos->data[1];
   436 
   437             switch(controller)
   438             {
   439             case 0: /* bank change - igore for now */
   440                 break;
   441             case kControllerVolume:
   442                 if(channel_vol[channel] != value<<8)
   443                 {
   444                     channel_vol[channel] = value<<8;
   445                     if(part>=0 && part<=31)
   446                     {
   447                         REST_IF_NECESSARY();
   448                         qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
   449                         tunePos++;
   450                     }
   451                 }
   452                 break;
   453             case kControllerPan:
   454                 if(channel_pan[channel] != (value << 1) + 256)
   455                 {
   456                     channel_pan[channel] = (value << 1) + 256;
   457                     if(part>=0 && part<=31)
   458                     {
   459                         REST_IF_NECESSARY();
   460                         qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
   461                         tunePos++;
   462                     }
   463                 }
   464                 break;
   465             default:
   466                 /* No other controllers implemented yet */;
   467                 break;
   468             }
   469 
   470             break;
   471         case MIDI_STATUS_PROG_CHANGE:
   472             /* Instrument changed */
   473             newInst = eventPos->data[0];
   474 
   475             /* Channel 9 (the 10th channel) is different, it indicates a drum kit */
   476             if (channel == 9)
   477                 newInst += kFirstDrumkit;
   478             else
   479                 newInst += kFirstGMInstrument;
   480             /* Only if the instrument for this channel *really* changed, add a new part. */
   481             if(newInst != part_to_inst[part])
   482             {
   483                 /* TODO maybe make use of kGeneralEventPartChange here,
   484                    to help QT reuse note channels?
   485                 */
   486                 part = channel_to_part[channel] = *numParts;
   487                 part_to_inst[(*numParts)++] = newInst;
   488 
   489                 if(channel_vol[channel] >= 0)
   490                 {
   491                     REST_IF_NECESSARY();
   492                     qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);
   493                     tunePos++;
   494                 }
   495                 if(channel_pan[channel] >= 0)
   496                 {
   497                     REST_IF_NECESSARY();
   498                     qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);
   499                     tunePos++;
   500                 }
   501                 if(channel_pitch_bend[channel] >= 0)
   502                 {
   503                     REST_IF_NECESSARY();
   504                     qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]);
   505                     tunePos++;
   506                 }
   507             }
   508             break;
   509         case MIDI_STATUS_PRESSURE:
   510             /* NYI */
   511             break;
   512         case MIDI_STATUS_PITCH_WHEEL:
   513             /* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones
   514                but for QTMA, we specify it as a 8.8 fixed point of semitones
   515                TODO: detect "pitch bend range changes" & honor them!
   516             */
   517             bend = (eventPos->data[0] & 0x7f) | ((eventPos->data[1] & 0x7f) << 7);
   518 
   519             /* "Center" the bend */
   520             bend -= 0x2000;
   521 
   522             /* Move it to our format: */
   523             bend <<= 4;
   524 
   525             /* If it turns out the pitch bend didn't change, stop here */
   526             if(channel_pitch_bend[channel] == bend)
   527                 break;
   528 
   529             channel_pitch_bend[channel] = bend;
   530             if(part>=0 && part<=31)
   531             {
   532                 /* Stuff a control event */
   533                 REST_IF_NECESSARY();
   534                 qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend);
   535                 tunePos++;
   536             }
   537             break;
   538         case MIDI_STATUS_SYSEX:
   539             if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */
   540             {
   541                 tempo = (eventPos->extraData[0] << 16) +
   542                     (eventPos->extraData[1] << 8) +
   543                     eventPos->extraData[2];
   544 
   545                 tick = tempo * Ippqn;
   546             }
   547             break;
   548         }
   549 
   550         /* on to the next event */
   551         eventPos = eventPos->next;
   552     }
   553 
   554     /* Finally, place an end marker */
   555     *tunePos = kEndMarkerValue;
   556 
   557     return tuneSequence;
   558 }
   559 
   560 Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts)
   561 {
   562     Uint32          *myHeader;
   563     Uint32          *myPos1, *myPos2;       /* pointers to the head and tail long words of a music event */
   564     NoteRequest     *myNoteRequest;
   565     NoteAllocator   myNoteAllocator;        /* for the NAStuffToneDescription call */
   566     ComponentResult myErr = noErr;
   567     int             part;
   568 
   569     myHeader = NULL;
   570     myNoteAllocator = NULL;
   571 
   572     /*
   573      * Open up the Note Allocator
   574      */
   575     myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0);
   576     if (myNoteAllocator == NULL)
   577         goto bail;
   578 
   579     /*
   580      * Allocate space for the tune header
   581      */
   582     myHeader = (Uint32 *)
   583             NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32));
   584     if (myHeader == NULL)
   585         goto bail;
   586 
   587     myPos1 = myHeader;
   588 
   589     /*
   590      * Loop over all parts
   591      */
   592     for(part = 0; part < numParts; ++part)
   593     {
   594         /*
   595          * Stuff request for the instrument with the given polyphony
   596          */
   597         myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */
   598         qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength);
   599         myNoteRequest = (NoteRequest *)(myPos1 + 1);
   600         myNoteRequest->info.flags = 0;
   601         /* I'm told by the Apple people that the Quicktime types were poorly designed and it was
   602          * too late to change them. On little endian, the BigEndian(Short|Fixed) types are structs
   603          * while on big endian they are primitive types. Furthermore, Quicktime failed to
   604          * provide setter and getter functions. To get this to work, we need to case the
   605          * code for the two possible situations.
   606          * My assumption is that the right-side value was always expected to be BigEndian
   607          * as it was written way before the Universal Binary transition. So in the little endian
   608          * case, OSSwap is used.
   609          */
   610 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   611         myNoteRequest->info.polyphony.bigEndianValue = OSSwapHostToBigInt16(part_poly_max[part]);
   612         myNoteRequest->info.typicalPolyphony.bigEndianValue = OSSwapHostToBigInt32(0x00010000);
   613 #else
   614         myNoteRequest->info.polyphony = part_poly_max[part];
   615         myNoteRequest->info.typicalPolyphony = 0x00010000;
   616 #endif
   617         myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone);
   618         if (myErr != noErr)
   619             goto bail;
   620 
   621         /* move pointer to beginning of next event */
   622         myPos1 += kNoteRequestEventLength;
   623     }
   624 
   625     *myPos1 = kEndMarkerValue;      /* end of sequence marker */
   626 
   627 
   628 bail:
   629     if(myNoteAllocator)
   630         CloseComponent(myNoteAllocator);
   631 
   632     /* if we encountered an error, dispose of the storage we allocated and return NULL */
   633     if (myErr != noErr) {
   634         DisposePtr((Ptr)myHeader);
   635         myHeader = NULL;
   636     }
   637 
   638     return myHeader;
   639 }
   640 
   641 #endif /* MacOS native MIDI support */