native_midi/native_midi_macosx.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 21 May 2013 21:21:23 -0700
changeset 617 87116a42526e
parent 590 180ef9af767b
child 625 1d489d8ec2e0
permissions -rw-r--r--
Cleaned up whitespace for the 2.0.0 release
     1 /*
     2   native_midi_macosx:  Native Midi support on Mac OS X for the SDL_mixer library
     3   Copyright (C) 2009  Ryan C. Gordon <icculus@icculus.org>
     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 
    22 /* This is Mac OS X only, using Core MIDI.
    23    Mac OS 9 support via QuickTime is in native_midi_mac.c */
    24 
    25 #include "SDL_config.h"
    26 
    27 #if __MACOSX__
    28 
    29 #include <CoreServices/CoreServices.h>      /* ComponentDescription */
    30 #include <AudioUnit/AudioUnit.h>
    31 #include <AudioToolbox/AudioToolbox.h>
    32 #include <AvailabilityMacros.h>
    33 
    34 #include "../SDL_mixer.h"
    35 #include "SDL_endian.h"
    36 #include "native_midi.h"
    37 
    38 /* Native Midi song */
    39 struct _NativeMidiSong
    40 {
    41     MusicPlayer player;
    42     MusicSequence sequence;
    43     MusicTimeStamp endTime;
    44     AudioUnit audiounit;
    45     int loops;
    46 };
    47 
    48 static NativeMidiSong *currentsong = NULL;
    49 static int latched_volume = MIX_MAX_VOLUME;
    50 
    51 static OSStatus
    52 GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength)
    53 {
    54     // http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html
    55     // figure out sequence length
    56     UInt32 ntracks, i;
    57     MusicTimeStamp sequenceLength = 0;
    58     OSStatus err;
    59 
    60     err = MusicSequenceGetTrackCount(sequence, &ntracks);
    61     if (err != noErr)
    62         return err;
    63 
    64     for (i = 0; i < ntracks; ++i)
    65     {
    66         MusicTrack track;
    67         MusicTimeStamp tracklen = 0;
    68         UInt32 tracklenlen = sizeof (tracklen);
    69 
    70         err = MusicSequenceGetIndTrack(sequence, i, &track);
    71         if (err != noErr)
    72             return err;
    73 
    74         err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
    75                                     &tracklen, &tracklenlen);
    76         if (err != noErr)
    77             return err;
    78 
    79         if (sequenceLength < tracklen)
    80             sequenceLength = tracklen;
    81     }
    82 
    83     *_sequenceLength = sequenceLength;
    84 
    85     return noErr;
    86 }
    87 
    88 
    89 /* we're looking for the sequence output audiounit. */
    90 static OSStatus
    91 GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit)
    92 {
    93     AUGraph graph;
    94     UInt32 nodecount, i;
    95     OSStatus err;
    96 
    97     err = MusicSequenceGetAUGraph(sequence, &graph);
    98     if (err != noErr)
    99         return err;
   100 
   101     err = AUGraphGetNodeCount(graph, &nodecount);
   102     if (err != noErr)
   103         return err;
   104 
   105     for (i = 0; i < nodecount; i++) {
   106         AUNode node;
   107 
   108         if (AUGraphGetIndNode(graph, i, &node) != noErr)
   109             continue;  /* better luck next time. */
   110 
   111 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 /* this is deprecated, but works back to 10.0 */
   112         {
   113             struct ComponentDescription desc;
   114             UInt32 classdatasize = 0;
   115             void *classdata = NULL;
   116             err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize,
   117                                      &classdata, aunit);
   118             if (err != noErr)
   119                 continue;
   120             else if (desc.componentType != kAudioUnitType_Output)
   121                 continue;
   122             else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
   123                 continue;
   124         }
   125         #else  /* not deprecated, but requires 10.5 or later */
   126         {
   127         # if !defined(AUDIO_UNIT_VERSION) || ((AUDIO_UNIT_VERSION + 0) < 1060)
   128          /* AUGraphAddNode () is changed to take an AudioComponentDescription*
   129           * desc parameter instead of a ComponentDescription* in the 10.6 SDK.
   130           * AudioComponentDescription is in 10.6 or newer, but it is actually
   131           * the same as struct ComponentDescription with 20 bytes of size and
   132           * the same offsets of all members, therefore, is binary compatible. */
   133         #   define AudioComponentDescription ComponentDescription
   134         # endif
   135             AudioComponentDescription desc;
   136             if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr)
   137                 continue;
   138             else if (desc.componentType != kAudioUnitType_Output)
   139                 continue;
   140             else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
   141                 continue;
   142         }
   143         #endif
   144 
   145         return noErr;  /* found it! */
   146     }
   147 
   148     return kAUGraphErr_NodeNotFound;
   149 }
   150 
   151 
   152 int native_midi_detect()
   153 {
   154     return 1;  /* always available. */
   155 }
   156 
   157 NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw)
   158 {
   159     NativeMidiSong *retval = NULL;
   160     void *buf = NULL;
   161     int len = 0;
   162     CFDataRef data = NULL;
   163 
   164     if (SDL_RWseek(rw, 0, RW_SEEK_END) < 0)
   165         goto fail;
   166     len = SDL_RWtell(rw);
   167     if (len < 0)
   168         goto fail;
   169     if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0)
   170         goto fail;
   171 
   172     buf = malloc(len);
   173     if (buf == NULL)
   174         goto fail;
   175 
   176     if (SDL_RWread(rw, buf, len, 1) != 1)
   177         goto fail;
   178 
   179     retval = malloc(sizeof(NativeMidiSong));
   180     if (retval == NULL)
   181         goto fail;
   182 
   183     memset(retval, '\0', sizeof (*retval));
   184 
   185     if (NewMusicPlayer(&retval->player) != noErr)
   186         goto fail;
   187     if (NewMusicSequence(&retval->sequence) != noErr)
   188         goto fail;
   189 
   190     data = CFDataCreate(NULL, (const UInt8 *) buf, len);
   191     if (data == NULL)
   192         goto fail;
   193 
   194     free(buf);
   195     buf = NULL;
   196 
   197     #if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
   198     /* MusicSequenceLoadSMFData() (avail. in 10.2, no 64 bit) is
   199      * equivalent to calling MusicSequenceLoadSMFDataWithFlags()
   200      * with a flags value of 0 (avail. in 10.3, avail. 64 bit).
   201      * So, we use MusicSequenceLoadSMFData() for powerpc versions
   202      * but the *WithFlags() on intel which require 10.4 anyway. */
   203     # if defined(__ppc__) || defined(__POWERPC__)
   204     if (MusicSequenceLoadSMFData(song->sequence, data) != noErr)
   205         goto fail;
   206     # else
   207     if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr)
   208         goto fail;
   209     # endif
   210     #else  /* MusicSequenceFileLoadData() requires 10.5 or later.  */
   211     if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr)
   212         goto fail;
   213     #endif
   214 
   215     CFRelease(data);
   216     data = NULL;
   217 
   218     if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr)
   219         goto fail;
   220 
   221     if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr)
   222         goto fail;
   223 
   224     if (freerw)
   225         SDL_RWclose(rw);
   226 
   227     return retval;
   228 
   229 fail:
   230     if (retval) {
   231         if (retval->sequence)
   232             DisposeMusicSequence(retval->sequence);
   233         if (retval->player)
   234             DisposeMusicPlayer(retval->player);
   235         free(retval);
   236     }
   237 
   238     if (data)
   239         CFRelease(data);
   240 
   241     if (buf)
   242         free(buf);
   243 
   244     if (freerw)
   245         SDL_RWclose(rw);
   246 
   247     return NULL;
   248 }
   249 
   250 void native_midi_freesong(NativeMidiSong *song)
   251 {
   252     if (song != NULL) {
   253         if (currentsong == song)
   254             currentsong = NULL;
   255         MusicPlayerStop(song->player);
   256         DisposeMusicSequence(song->sequence);
   257         DisposeMusicPlayer(song->player);
   258         free(song);
   259     }
   260 }
   261 
   262 void native_midi_start(NativeMidiSong *song, int loops)
   263 {
   264     int vol;
   265 
   266     if (song == NULL)
   267         return;
   268 
   269     SDL_PauseAudio(1);
   270     SDL_UnlockAudio();
   271 
   272     if (currentsong)
   273         MusicPlayerStop(currentsong->player);
   274 
   275     currentsong = song;
   276     currentsong->loops = loops;
   277 
   278     MusicPlayerPreroll(song->player);
   279     MusicPlayerSetTime(song->player, 0);
   280     MusicPlayerStart(song->player);
   281 
   282     GetSequenceAudioUnit(song->sequence, &song->audiounit);
   283 
   284     vol = latched_volume;
   285     latched_volume++;  /* just make this not match. */
   286     native_midi_setvolume(vol);
   287 
   288     SDL_LockAudio();
   289     SDL_PauseAudio(0);
   290 }
   291 
   292 void native_midi_stop()
   293 {
   294     if (currentsong) {
   295         SDL_PauseAudio(1);
   296         SDL_UnlockAudio();
   297         MusicPlayerStop(currentsong->player);
   298         currentsong = NULL;
   299         SDL_LockAudio();
   300         SDL_PauseAudio(0);
   301     }
   302 }
   303 
   304 int native_midi_active()
   305 {
   306     MusicTimeStamp currentTime = 0;
   307     if (currentsong == NULL)
   308         return 0;
   309 
   310     MusicPlayerGetTime(currentsong->player, &currentTime);
   311     if ((currentTime < currentsong->endTime) ||
   312         (currentTime >= kMusicTimeStamp_EndOfTrack)) {
   313         return 1;
   314     } else if (currentsong->loops) {
   315         --currentsong->loops;
   316         MusicPlayerSetTime(currentsong->player, 0);
   317         return 1;
   318     }
   319     return 0;
   320 }
   321 
   322 void native_midi_setvolume(int volume)
   323 {
   324     if (latched_volume == volume)
   325         return;
   326 
   327     latched_volume = volume;
   328     if ((currentsong) && (currentsong->audiounit)) {
   329         const float floatvol = ((float) volume) / ((float) MIX_MAX_VOLUME);
   330         AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume,
   331                               kAudioUnitScope_Global, 0, floatvol, 0);
   332     }
   333 }
   334 
   335 const char *native_midi_error(void)
   336 {
   337     return "";  /* !!! FIXME */
   338 }
   339 
   340 #endif /* Mac OS X native MIDI support */
   341