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