Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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