native_midi_macosx.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library
  3. Copyright (C) 2009 Ryan C. Gordon
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public
  13. License along with this library; if not, write to the Free
  14. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. Ryan C. Gordon
  16. icculus@icculus.org
  17. */
  18. /* This is Mac OS X only, using Core MIDI.
  19. Mac OS 9 support via QuickTime is in native_midi_mac.c */
  20. #include "SDL_config.h"
  21. #include <Carbon/Carbon.h>
  22. #include <AudioToolbox/AudioToolbox.h>
  23. #include <AvailabilityMacros.h>
  24. #include "SDL.h"
  25. #include "SDL_endian.h"
  26. #include "native_midi.h"
  27. /* Native Midi song */
  28. struct _NativeMidiSong
  29. {
  30. MusicPlayer player;
  31. MusicSequence sequence;
  32. MusicTimeStamp endTime;
  33. AudioUnit audiounit;
  34. };
  35. static NativeMidiSong *currentsong = NULL;
  36. static int latched_volume = 128;
  37. static OSStatus
  38. GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength)
  39. {
  40. // http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html
  41. // figure out sequence length
  42. UInt32 ntracks, i;
  43. MusicTimeStamp sequenceLength = 0;
  44. OSStatus err;
  45. err = MusicSequenceGetTrackCount(sequence, &ntracks);
  46. if (err != noErr)
  47. return err;
  48. for (i = 0; i < ntracks; ++i)
  49. {
  50. MusicTrack track;
  51. MusicTimeStamp tracklen = 0;
  52. UInt32 tracklenlen = sizeof (tracklen);
  53. err = MusicSequenceGetIndTrack(sequence, i, &track);
  54. if (err != noErr)
  55. return err;
  56. err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
  57. &tracklen, &tracklenlen);
  58. if (err != noErr)
  59. return err;
  60. if (sequenceLength < tracklen)
  61. sequenceLength = tracklen;
  62. }
  63. *_sequenceLength = sequenceLength;
  64. return noErr;
  65. }
  66. /* we're looking for the sequence output audiounit. */
  67. static OSStatus
  68. GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit)
  69. {
  70. AUGraph graph;
  71. UInt32 nodecount, i;
  72. OSStatus err;
  73. err = MusicSequenceGetAUGraph(sequence, &graph);
  74. if (err != noErr)
  75. return err;
  76. err = AUGraphGetNodeCount(graph, &nodecount);
  77. if (err != noErr)
  78. return err;
  79. for (i = 0; i < nodecount; i++) {
  80. AUNode node;
  81. if (AUGraphGetIndNode(graph, i, &node) != noErr)
  82. continue; /* better luck next time. */
  83. #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* this is deprecated, but works back to 10.0 */
  84. {
  85. struct ComponentDescription desc;
  86. UInt32 classdatasize = 0;
  87. void *classdata = NULL;
  88. err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize,
  89. &classdata, aunit);
  90. if (err != noErr)
  91. continue;
  92. else if (desc.componentType != kAudioUnitType_Output)
  93. continue;
  94. else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
  95. continue;
  96. }
  97. #else /* not deprecated, but requires 10.5 or later */
  98. {
  99. AudioComponentDescription desc;
  100. if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr)
  101. continue;
  102. else if (desc.componentType != kAudioUnitType_Output)
  103. continue;
  104. else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
  105. continue;
  106. }
  107. #endif
  108. return noErr; /* found it! */
  109. }
  110. return kAUGraphErr_NodeNotFound;
  111. }
  112. int native_midi_detect()
  113. {
  114. return 1; /* always available. */
  115. }
  116. NativeMidiSong *native_midi_loadsong(const char *midifile)
  117. {
  118. NativeMidiSong *retval = NULL;
  119. SDL_RWops *rw = SDL_RWFromFile(midifile, "rb");
  120. if (rw != NULL) {
  121. retval = native_midi_loadsong_RW(rw);
  122. SDL_RWclose(rw);
  123. }
  124. return retval;
  125. }
  126. NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
  127. {
  128. NativeMidiSong *retval = NULL;
  129. void *buf = NULL;
  130. int len = 0;
  131. CFDataRef data = NULL;
  132. if (SDL_RWseek(rw, 0, RW_SEEK_END) < 0)
  133. goto fail;
  134. len = SDL_RWtell(rw);
  135. if (len < 0)
  136. goto fail;
  137. if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0)
  138. goto fail;
  139. buf = malloc(len);
  140. if (buf == NULL)
  141. goto fail;
  142. if (SDL_RWread(rw, buf, len, 1) != 1)
  143. goto fail;
  144. retval = malloc(sizeof(NativeMidiSong));
  145. if (retval == NULL)
  146. goto fail;
  147. memset(retval, '\0', sizeof (*retval));
  148. if (NewMusicPlayer(&retval->player) != noErr)
  149. goto fail;
  150. if (NewMusicSequence(&retval->sequence) != noErr)
  151. goto fail;
  152. data = CFDataCreate(NULL, (const UInt8 *) buf, len);
  153. if (data == NULL)
  154. goto fail;
  155. free(buf);
  156. buf = NULL;
  157. #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 /* this is deprecated, but works back to 10.3 */
  158. if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr)
  159. goto fail;
  160. #else /* not deprecated, but requires 10.5 or later */
  161. if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr)
  162. goto fail;
  163. #endif
  164. CFRelease(data);
  165. data = NULL;
  166. if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr)
  167. goto fail;
  168. if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr)
  169. goto fail;
  170. return retval;
  171. fail:
  172. if (retval) {
  173. if (retval->sequence)
  174. DisposeMusicSequence(retval->sequence);
  175. if (retval->player)
  176. DisposeMusicPlayer(retval->player);
  177. free(retval);
  178. }
  179. if (data)
  180. CFRelease(data);
  181. if (buf)
  182. free(buf);
  183. return NULL;
  184. }
  185. void native_midi_freesong(NativeMidiSong *song)
  186. {
  187. if (song != NULL) {
  188. if (currentsong == song)
  189. currentsong = NULL;
  190. MusicPlayerStop(song->player);
  191. DisposeMusicSequence(song->sequence);
  192. DisposeMusicPlayer(song->player);
  193. free(song);
  194. }
  195. }
  196. void native_midi_start(NativeMidiSong *song)
  197. {
  198. int vol;
  199. if (song == NULL)
  200. return;
  201. SDL_PauseAudio(1);
  202. SDL_UnlockAudio();
  203. if (currentsong)
  204. MusicPlayerStop(currentsong->player);
  205. currentsong = song;
  206. MusicPlayerStart(song->player);
  207. GetSequenceAudioUnit(song->sequence, &song->audiounit);
  208. vol = latched_volume;
  209. latched_volume++; /* just make this not match. */
  210. native_midi_setvolume(vol);
  211. SDL_LockAudio();
  212. SDL_PauseAudio(0);
  213. }
  214. void native_midi_stop()
  215. {
  216. if (currentsong) {
  217. SDL_PauseAudio(1);
  218. SDL_UnlockAudio();
  219. MusicPlayerStop(currentsong->player);
  220. currentsong = NULL;
  221. SDL_LockAudio();
  222. SDL_PauseAudio(0);
  223. }
  224. }
  225. int native_midi_active()
  226. {
  227. MusicTimeStamp currentTime = 0;
  228. if (currentsong == NULL)
  229. return 0;
  230. MusicPlayerGetTime(currentsong->player, &currentTime);
  231. return ((currentTime < currentsong->endTime) ||
  232. (currentTime >= kMusicTimeStamp_EndOfTrack));
  233. }
  234. void native_midi_setvolume(int volume)
  235. {
  236. if (latched_volume == volume)
  237. return;
  238. latched_volume = volume;
  239. if ((currentsong) && (currentsong->audiounit)) {
  240. const float floatvol = ((float) volume) / ((float) 128);
  241. AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume,
  242. kAudioUnitScope_Global, 0, floatvol, 0);
  243. }
  244. }
  245. const char *native_midi_error(void)
  246. {
  247. return ""; /* !!! FIXME */
  248. }