native_midi_macosx.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. #ifdef __APPLE__
  19. /* This is Mac OS X only, using Core MIDI.
  20. Mac OS 9 support via QuickTime is in native_midi_mac.c */
  21. #include "SDL_config.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. int Loopinng;
  35. };
  36. static NativeMidiSong *currentsong = NULL;
  37. static int latched_volume = 128;
  38. static OSStatus
  39. GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength)
  40. {
  41. // http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html
  42. // figure out sequence length
  43. UInt32 ntracks, i;
  44. MusicTimeStamp sequenceLength = 0;
  45. OSStatus err;
  46. err = MusicSequenceGetTrackCount(sequence, &ntracks);
  47. if (err != noErr)
  48. return err;
  49. for (i = 0; i < ntracks; ++i)
  50. {
  51. MusicTrack track;
  52. MusicTimeStamp tracklen = 0;
  53. UInt32 tracklenlen = sizeof (tracklen);
  54. err = MusicSequenceGetIndTrack(sequence, i, &track);
  55. if (err != noErr)
  56. return err;
  57. err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
  58. &tracklen, &tracklenlen);
  59. if (err != noErr)
  60. return err;
  61. if (sequenceLength < tracklen)
  62. sequenceLength = tracklen;
  63. }
  64. *_sequenceLength = sequenceLength;
  65. return noErr;
  66. }
  67. /* we're looking for the sequence output audiounit. */
  68. static OSStatus
  69. GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit)
  70. {
  71. AUGraph graph;
  72. UInt32 nodecount, i;
  73. OSStatus err;
  74. err = MusicSequenceGetAUGraph(sequence, &graph);
  75. if (err != noErr)
  76. return err;
  77. err = AUGraphGetNodeCount(graph, &nodecount);
  78. if (err != noErr)
  79. return err;
  80. for (i = 0; i < nodecount; i++) {
  81. AUNode node;
  82. if (AUGraphGetIndNode(graph, i, &node) != noErr)
  83. continue; /* better luck next time. */
  84. #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* this is deprecated, but works back to 10.0 */
  85. {
  86. struct ComponentDescription desc;
  87. UInt32 classdatasize = 0;
  88. void *classdata = NULL;
  89. err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize,
  90. &classdata, aunit);
  91. if (err != noErr)
  92. continue;
  93. else if (desc.componentType != kAudioUnitType_Output)
  94. continue;
  95. else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
  96. continue;
  97. }
  98. #else /* not deprecated, but requires 10.5 or later */
  99. {
  100. AudioComponentDescription desc;
  101. if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr)
  102. continue;
  103. else if (desc.componentType != kAudioUnitType_Output)
  104. continue;
  105. else if (desc.componentSubType != kAudioUnitSubType_GenericOutput)
  106. continue;
  107. }
  108. #endif
  109. return noErr; /* found it! */
  110. }
  111. return kAUGraphErr_NodeNotFound;
  112. }
  113. int native_midi_detect()
  114. {
  115. return 1; /* always available. */
  116. }
  117. NativeMidiSong *native_midi_loadsong(const char *midifile)
  118. {
  119. NativeMidiSong *retval = NULL;
  120. SDL_RWops *rw = SDL_RWFromFile(midifile, "rb");
  121. if (rw != NULL) {
  122. retval = native_midi_loadsong_RW(rw);
  123. SDL_RWclose(rw);
  124. }
  125. return retval;
  126. }
  127. NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
  128. {
  129. NativeMidiSong *retval = NULL;
  130. void *buf = NULL;
  131. int len = 0;
  132. CFDataRef data = NULL;
  133. if (SDL_RWseek(rw, 0, RW_SEEK_END) < 0)
  134. goto fail;
  135. len = SDL_RWtell(rw);
  136. if (len < 0)
  137. goto fail;
  138. if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0)
  139. goto fail;
  140. buf = malloc(len);
  141. if (buf == NULL)
  142. goto fail;
  143. if (SDL_RWread(rw, buf, len, 1) != 1)
  144. goto fail;
  145. retval = malloc(sizeof(NativeMidiSong));
  146. if (retval == NULL)
  147. goto fail;
  148. memset(retval, '\0', sizeof (*retval));
  149. if (NewMusicPlayer(&retval->player) != noErr)
  150. goto fail;
  151. if (NewMusicSequence(&retval->sequence) != noErr)
  152. goto fail;
  153. data = CFDataCreate(NULL, (const UInt8 *) buf, len);
  154. if (data == NULL)
  155. goto fail;
  156. free(buf);
  157. buf = NULL;
  158. #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 /* this is deprecated, but works back to 10.3 */
  159. if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr)
  160. goto fail;
  161. #else /* not deprecated, but requires 10.5 or later */
  162. if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr)
  163. goto fail;
  164. #endif
  165. CFRelease(data);
  166. data = NULL;
  167. if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr)
  168. goto fail;
  169. if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr)
  170. goto fail;
  171. return retval;
  172. fail:
  173. if (retval) {
  174. if (retval->sequence)
  175. DisposeMusicSequence(retval->sequence);
  176. if (retval->player)
  177. DisposeMusicPlayer(retval->player);
  178. free(retval);
  179. }
  180. if (data)
  181. CFRelease(data);
  182. if (buf)
  183. free(buf);
  184. return NULL;
  185. }
  186. void native_midi_freesong(NativeMidiSong *song)
  187. {
  188. if (song != NULL) {
  189. if (currentsong == song)
  190. currentsong = NULL;
  191. MusicPlayerStop(song->player);
  192. DisposeMusicSequence(song->sequence);
  193. DisposeMusicPlayer(song->player);
  194. free(song);
  195. }
  196. }
  197. void native_midi_start(NativeMidiSong *song, int looping)
  198. {
  199. int vol;
  200. if (song == NULL)
  201. return;
  202. SDL_PauseAudio(1);
  203. SDL_UnlockAudio();
  204. if (currentsong)
  205. MusicPlayerStop(currentsong->player);
  206. currentsong = song;
  207. MusicPlayerStart(song->player);
  208. GetSequenceAudioUnit(song->sequence, &song->audiounit);
  209. vol = latched_volume;
  210. latched_volume++; /* just make this not match. */
  211. native_midi_setvolume(song,vol);
  212. SDL_LockAudio();
  213. SDL_PauseAudio(0);
  214. }
  215. void native_midi_stop()
  216. {
  217. if (currentsong) {
  218. SDL_PauseAudio(1);
  219. SDL_UnlockAudio();
  220. MusicPlayerStop(currentsong->player);
  221. currentsong = NULL;
  222. SDL_LockAudio();
  223. SDL_PauseAudio(0);
  224. }
  225. }
  226. int native_midi_active()
  227. {
  228. MusicTimeStamp currentTime = 0;
  229. if (currentsong == NULL)
  230. return 0;
  231. MusicPlayerGetTime(currentsong->player, &currentTime);
  232. return ((currentTime < currentsong->endTime) ||
  233. (currentTime >= kMusicTimeStamp_EndOfTrack));
  234. }
  235. void native_midi_setvolume(NativeMidiSong *song, int volume)
  236. {
  237. if (latched_volume == volume)
  238. return;
  239. latched_volume = volume;
  240. if ((song) && (song->audiounit)) {
  241. const float floatvol = ((float) volume) / ((float) 128);
  242. AudioUnitSetParameter(song->audiounit, kHALOutputParam_Volume,
  243. kAudioUnitScope_Global, 0, floatvol, 0);
  244. }
  245. }
  246. const char *native_midi_error(NativeMidiSong *song)
  247. {
  248. return ""; /* !!! FIXME */
  249. }
  250. #endif