SDL_coreaudio.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "../../SDL_internal.h"
  19. #if SDL_AUDIO_DRIVER_COREAUDIO
  20. #include "SDL_audio.h"
  21. #include "../SDL_audio_c.h"
  22. #include "../SDL_sysaudio.h"
  23. #include "SDL_coreaudio.h"
  24. #include "SDL_assert.h"
  25. #define DEBUG_COREAUDIO 0
  26. static void COREAUDIO_CloseDevice(_THIS);
  27. #define CHECK_RESULT(msg) \
  28. if (result != noErr) { \
  29. COREAUDIO_CloseDevice(this); \
  30. SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
  31. return 0; \
  32. }
  33. #if MACOSX_COREAUDIO
  34. static const AudioObjectPropertyAddress devlist_address = {
  35. kAudioHardwarePropertyDevices,
  36. kAudioObjectPropertyScopeGlobal,
  37. kAudioObjectPropertyElementMaster
  38. };
  39. typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
  40. typedef struct AudioDeviceList
  41. {
  42. AudioDeviceID devid;
  43. SDL_bool alive;
  44. struct AudioDeviceList *next;
  45. } AudioDeviceList;
  46. static AudioDeviceList *output_devs = NULL;
  47. static AudioDeviceList *capture_devs = NULL;
  48. static SDL_bool
  49. add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
  50. {
  51. AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
  52. if (item == NULL) {
  53. return SDL_FALSE;
  54. }
  55. item->devid = devId;
  56. item->alive = SDL_TRUE;
  57. item->next = iscapture ? capture_devs : output_devs;
  58. if (iscapture) {
  59. capture_devs = item;
  60. } else {
  61. output_devs = item;
  62. }
  63. return SDL_TRUE;
  64. }
  65. static void
  66. addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
  67. {
  68. if (add_to_internal_dev_list(iscapture, devId)) {
  69. SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
  70. }
  71. }
  72. static void
  73. build_device_list(int iscapture, addDevFn addfn, void *addfndata)
  74. {
  75. OSStatus result = noErr;
  76. UInt32 size = 0;
  77. AudioDeviceID *devs = NULL;
  78. UInt32 i = 0;
  79. UInt32 max = 0;
  80. result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
  81. &devlist_address, 0, NULL, &size);
  82. if (result != kAudioHardwareNoError)
  83. return;
  84. devs = (AudioDeviceID *) alloca(size);
  85. if (devs == NULL)
  86. return;
  87. result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
  88. &devlist_address, 0, NULL, &size, devs);
  89. if (result != kAudioHardwareNoError)
  90. return;
  91. max = size / sizeof (AudioDeviceID);
  92. for (i = 0; i < max; i++) {
  93. CFStringRef cfstr = NULL;
  94. char *ptr = NULL;
  95. AudioDeviceID dev = devs[i];
  96. AudioBufferList *buflist = NULL;
  97. int usable = 0;
  98. CFIndex len = 0;
  99. const AudioObjectPropertyAddress addr = {
  100. kAudioDevicePropertyStreamConfiguration,
  101. iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
  102. kAudioObjectPropertyElementMaster
  103. };
  104. const AudioObjectPropertyAddress nameaddr = {
  105. kAudioObjectPropertyName,
  106. iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
  107. kAudioObjectPropertyElementMaster
  108. };
  109. result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
  110. if (result != noErr)
  111. continue;
  112. buflist = (AudioBufferList *) SDL_malloc(size);
  113. if (buflist == NULL)
  114. continue;
  115. result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
  116. &size, buflist);
  117. if (result == noErr) {
  118. UInt32 j;
  119. for (j = 0; j < buflist->mNumberBuffers; j++) {
  120. if (buflist->mBuffers[j].mNumberChannels > 0) {
  121. usable = 1;
  122. break;
  123. }
  124. }
  125. }
  126. SDL_free(buflist);
  127. if (!usable)
  128. continue;
  129. size = sizeof (CFStringRef);
  130. result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
  131. if (result != kAudioHardwareNoError)
  132. continue;
  133. len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
  134. kCFStringEncodingUTF8);
  135. ptr = (char *) SDL_malloc(len + 1);
  136. usable = ((ptr != NULL) &&
  137. (CFStringGetCString
  138. (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
  139. CFRelease(cfstr);
  140. if (usable) {
  141. len = strlen(ptr);
  142. /* Some devices have whitespace at the end...trim it. */
  143. while ((len > 0) && (ptr[len - 1] == ' ')) {
  144. len--;
  145. }
  146. usable = (len > 0);
  147. }
  148. if (usable) {
  149. ptr[len] = '\0';
  150. #if DEBUG_COREAUDIO
  151. printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
  152. ((iscapture) ? "capture" : "output"),
  153. (int) i, ptr, (int) dev);
  154. #endif
  155. addfn(ptr, iscapture, dev, addfndata);
  156. }
  157. SDL_free(ptr); /* addfn() would have copied the string. */
  158. }
  159. }
  160. static void
  161. free_audio_device_list(AudioDeviceList **list)
  162. {
  163. AudioDeviceList *item = *list;
  164. while (item) {
  165. AudioDeviceList *next = item->next;
  166. SDL_free(item);
  167. item = next;
  168. }
  169. *list = NULL;
  170. }
  171. static void
  172. COREAUDIO_DetectDevices(void)
  173. {
  174. build_device_list(SDL_TRUE, addToDevList, NULL);
  175. build_device_list(SDL_FALSE, addToDevList, NULL);
  176. }
  177. static void
  178. build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
  179. {
  180. AudioDeviceList **list = (AudioDeviceList **) data;
  181. AudioDeviceList *item;
  182. for (item = *list; item != NULL; item = item->next) {
  183. if (item->devid == devId) {
  184. item->alive = SDL_TRUE;
  185. return;
  186. }
  187. }
  188. add_to_internal_dev_list(iscapture, devId); /* new device, add it. */
  189. SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
  190. }
  191. static void
  192. reprocess_device_list(const int iscapture, AudioDeviceList **list)
  193. {
  194. AudioDeviceList *item;
  195. AudioDeviceList *prev = NULL;
  196. for (item = *list; item != NULL; item = item->next) {
  197. item->alive = SDL_FALSE;
  198. }
  199. build_device_list(iscapture, build_device_change_list, list);
  200. /* free items in the list that aren't still alive. */
  201. item = *list;
  202. while (item != NULL) {
  203. AudioDeviceList *next = item->next;
  204. if (item->alive) {
  205. prev = item;
  206. } else {
  207. SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
  208. if (prev) {
  209. prev->next = item->next;
  210. } else {
  211. *list = item->next;
  212. }
  213. SDL_free(item);
  214. }
  215. item = next;
  216. }
  217. }
  218. /* this is called when the system's list of available audio devices changes. */
  219. static OSStatus
  220. device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
  221. {
  222. reprocess_device_list(SDL_TRUE, &capture_devs);
  223. reprocess_device_list(SDL_FALSE, &output_devs);
  224. return 0;
  225. }
  226. #endif
  227. /* The CoreAudio callback */
  228. static OSStatus
  229. outputCallback(void *inRefCon,
  230. AudioUnitRenderActionFlags * ioActionFlags,
  231. const AudioTimeStamp * inTimeStamp,
  232. UInt32 inBusNumber, UInt32 inNumberFrames,
  233. AudioBufferList * ioData)
  234. {
  235. SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
  236. AudioBuffer *abuf;
  237. UInt32 remaining, len;
  238. void *ptr;
  239. UInt32 i;
  240. /* Only do anything if audio is enabled and not paused */
  241. if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
  242. for (i = 0; i < ioData->mNumberBuffers; i++) {
  243. abuf = &ioData->mBuffers[i];
  244. SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize);
  245. }
  246. return 0;
  247. }
  248. /* No SDL conversion should be needed here, ever, since we accept
  249. any input format in OpenAudio, and leave the conversion to CoreAudio.
  250. */
  251. /*
  252. SDL_assert(!this->convert.needed);
  253. SDL_assert(this->spec.channels == ioData->mNumberChannels);
  254. */
  255. for (i = 0; i < ioData->mNumberBuffers; i++) {
  256. abuf = &ioData->mBuffers[i];
  257. remaining = abuf->mDataByteSize;
  258. ptr = abuf->mData;
  259. while (remaining > 0) {
  260. if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
  261. /* Generate the data */
  262. SDL_LockMutex(this->mixer_lock);
  263. (*this->spec.callback)(this->spec.userdata,
  264. this->hidden->buffer, this->hidden->bufferSize);
  265. SDL_UnlockMutex(this->mixer_lock);
  266. this->hidden->bufferOffset = 0;
  267. }
  268. len = this->hidden->bufferSize - this->hidden->bufferOffset;
  269. if (len > remaining)
  270. len = remaining;
  271. SDL_memcpy(ptr, (char *)this->hidden->buffer +
  272. this->hidden->bufferOffset, len);
  273. ptr = (char *)ptr + len;
  274. remaining -= len;
  275. this->hidden->bufferOffset += len;
  276. }
  277. }
  278. return noErr;
  279. }
  280. static OSStatus
  281. inputCallback(void *inRefCon,
  282. AudioUnitRenderActionFlags *ioActionFlags,
  283. const AudioTimeStamp *inTimeStamp,
  284. UInt32 inBusNumber, UInt32 inNumberFrames,
  285. AudioBufferList *ioData)
  286. {
  287. SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
  288. if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
  289. return noErr; /* just drop this if we're not accepting input. */
  290. }
  291. const OSStatus err = AudioUnitRender(this->hidden->audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &this->hidden->captureBufferList);
  292. SDL_assert(this->hidden->captureBufferList.mNumberBuffers == 1);
  293. if (err == noErr) {
  294. const AudioBuffer *abuf = &this->hidden->captureBufferList.mBuffers[0];
  295. UInt32 remaining = abuf->mDataByteSize;
  296. const Uint8 *ptr = (const Uint8 *) abuf->mData;
  297. /* No SDL conversion should be needed here, ever, since we accept
  298. any input format in OpenAudio, and leave the conversion to CoreAudio.
  299. */
  300. while (remaining > 0) {
  301. UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
  302. if (len > remaining)
  303. len = remaining;
  304. SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
  305. ptr += len;
  306. remaining -= len;
  307. this->hidden->bufferOffset += len;
  308. if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
  309. SDL_LockMutex(this->mixer_lock);
  310. (*this->spec.callback)(this->spec.userdata,
  311. this->hidden->buffer, this->hidden->bufferSize);
  312. SDL_UnlockMutex(this->mixer_lock);
  313. this->hidden->bufferOffset = 0;
  314. }
  315. }
  316. }
  317. return noErr;
  318. }
  319. #if MACOSX_COREAUDIO
  320. static const AudioObjectPropertyAddress alive_address =
  321. {
  322. kAudioDevicePropertyDeviceIsAlive,
  323. kAudioObjectPropertyScopeGlobal,
  324. kAudioObjectPropertyElementMaster
  325. };
  326. static OSStatus
  327. device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
  328. {
  329. SDL_AudioDevice *this = (SDL_AudioDevice *) data;
  330. SDL_bool dead = SDL_FALSE;
  331. UInt32 isAlive = 1;
  332. UInt32 size = sizeof (isAlive);
  333. OSStatus error;
  334. if (!SDL_AtomicGet(&this->enabled)) {
  335. return 0; /* already known to be dead. */
  336. }
  337. error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
  338. 0, NULL, &size, &isAlive);
  339. if (error == kAudioHardwareBadDeviceError) {
  340. dead = SDL_TRUE; /* device was unplugged. */
  341. } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
  342. dead = SDL_TRUE; /* device died in some other way. */
  343. }
  344. if (dead) {
  345. SDL_OpenedAudioDeviceDisconnected(this);
  346. }
  347. return 0;
  348. }
  349. #endif
  350. static void
  351. COREAUDIO_CloseDevice(_THIS)
  352. {
  353. if (this->hidden != NULL) {
  354. if (this->hidden->audioUnitOpened) {
  355. #if MACOSX_COREAUDIO
  356. /* Unregister our disconnect callback. */
  357. AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
  358. #endif
  359. AURenderCallbackStruct callback;
  360. const AudioUnitElement output_bus = 0;
  361. const AudioUnitElement input_bus = 1;
  362. const int iscapture = this->iscapture;
  363. const AudioUnitElement bus =
  364. ((iscapture) ? input_bus : output_bus);
  365. /* stop processing the audio unit */
  366. AudioOutputUnitStop(this->hidden->audioUnit);
  367. /* Remove the input callback */
  368. SDL_zero(callback);
  369. AudioUnitSetProperty(this->hidden->audioUnit,
  370. iscapture ? kAudioOutputUnitProperty_SetInputCallback : kAudioUnitProperty_SetRenderCallback,
  371. kAudioUnitScope_Global, bus, &callback, sizeof(callback));
  372. AudioComponentInstanceDispose(this->hidden->audioUnit);
  373. this->hidden->audioUnitOpened = 0;
  374. #if MACOSX_COREAUDIO
  375. SDL_free(this->hidden->captureBufferList.mBuffers[0].mData);
  376. #endif
  377. }
  378. SDL_free(this->hidden->buffer);
  379. SDL_free(this->hidden);
  380. this->hidden = NULL;
  381. }
  382. }
  383. #if MACOSX_COREAUDIO
  384. static int
  385. prepare_device(_THIS, void *handle, int iscapture)
  386. {
  387. AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
  388. OSStatus result = noErr;
  389. UInt32 size = 0;
  390. UInt32 alive = 0;
  391. pid_t pid = 0;
  392. AudioObjectPropertyAddress addr = {
  393. 0,
  394. kAudioObjectPropertyScopeGlobal,
  395. kAudioObjectPropertyElementMaster
  396. };
  397. if (handle == NULL) {
  398. size = sizeof (AudioDeviceID);
  399. addr.mSelector =
  400. ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
  401. kAudioHardwarePropertyDefaultOutputDevice);
  402. result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
  403. 0, NULL, &size, &devid);
  404. CHECK_RESULT("AudioHardwareGetProperty (default device)");
  405. }
  406. addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
  407. addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
  408. kAudioDevicePropertyScopeOutput;
  409. size = sizeof (alive);
  410. result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
  411. CHECK_RESULT
  412. ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
  413. if (!alive) {
  414. SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
  415. return 0;
  416. }
  417. addr.mSelector = kAudioDevicePropertyHogMode;
  418. size = sizeof (pid);
  419. result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
  420. /* some devices don't support this property, so errors are fine here. */
  421. if ((result == noErr) && (pid != -1)) {
  422. SDL_SetError("CoreAudio: requested device is being hogged.");
  423. return 0;
  424. }
  425. this->hidden->deviceID = devid;
  426. return 1;
  427. }
  428. #endif
  429. static int
  430. prepare_audiounit(_THIS, void *handle, int iscapture,
  431. const AudioStreamBasicDescription * strdesc)
  432. {
  433. OSStatus result = noErr;
  434. AURenderCallbackStruct callback;
  435. AudioComponentDescription desc;
  436. AudioComponent comp = NULL;
  437. const AudioUnitElement output_bus = 0;
  438. const AudioUnitElement input_bus = 1;
  439. #if MACOSX_COREAUDIO
  440. if (!prepare_device(this, handle, iscapture)) {
  441. return 0;
  442. }
  443. #endif
  444. SDL_zero(desc);
  445. desc.componentType = kAudioUnitType_Output;
  446. desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  447. #if MACOSX_COREAUDIO
  448. desc.componentSubType = iscapture ? kAudioUnitSubType_HALOutput : kAudioUnitSubType_DefaultOutput;
  449. #else
  450. desc.componentSubType = kAudioUnitSubType_RemoteIO;
  451. #endif
  452. comp = AudioComponentFindNext(NULL, &desc);
  453. if (comp == NULL) {
  454. SDL_SetError("Couldn't find requested CoreAudio component");
  455. return 0;
  456. }
  457. /* Open & initialize the audio unit */
  458. result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit);
  459. CHECK_RESULT("AudioComponentInstanceNew");
  460. this->hidden->audioUnitOpened = 1;
  461. #if MACOSX_COREAUDIO
  462. if (iscapture) { /* have to do EnableIO only for capture devices. */
  463. UInt32 enable = 1;
  464. result = AudioUnitSetProperty(this->hidden->audioUnit,
  465. kAudioOutputUnitProperty_EnableIO,
  466. kAudioUnitScope_Input, input_bus,
  467. &enable, sizeof (enable));
  468. CHECK_RESULT
  469. ("AudioUnitSetProperty (kAudioOutputUnitProperty_EnableIO input bus)");
  470. enable = 0;
  471. result = AudioUnitSetProperty(this->hidden->audioUnit,
  472. kAudioOutputUnitProperty_EnableIO,
  473. kAudioUnitScope_Output, output_bus,
  474. &enable, sizeof (enable));
  475. CHECK_RESULT
  476. ("AudioUnitSetProperty (kAudioOutputUnitProperty_EnableIO output bus)");
  477. }
  478. /* this is always on the output_bus, even for capture devices. */
  479. result = AudioUnitSetProperty(this->hidden->audioUnit,
  480. kAudioOutputUnitProperty_CurrentDevice,
  481. kAudioUnitScope_Global, output_bus,
  482. &this->hidden->deviceID,
  483. sizeof(AudioDeviceID));
  484. CHECK_RESULT
  485. ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
  486. #endif
  487. /* Set the data format of the audio unit. */
  488. result = AudioUnitSetProperty(this->hidden->audioUnit,
  489. kAudioUnitProperty_StreamFormat,
  490. iscapture ? kAudioUnitScope_Output : kAudioUnitScope_Input,
  491. iscapture ? input_bus : output_bus,
  492. strdesc, sizeof (*strdesc));
  493. CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
  494. #if MACOSX_COREAUDIO
  495. if (iscapture) { /* only need to do this for capture devices. */
  496. void *ptr;
  497. UInt32 framesize = 0;
  498. UInt32 propsize = sizeof (UInt32);
  499. result = AudioUnitGetProperty(this->hidden->audioUnit,
  500. kAudioDevicePropertyBufferFrameSize,
  501. kAudioUnitScope_Global, output_bus,
  502. &framesize, &propsize);
  503. CHECK_RESULT
  504. ("AudioUnitGetProperty (kAudioDevicePropertyBufferFrameSize)");
  505. framesize *= SDL_AUDIO_BITSIZE(this->spec.format) / 8;
  506. ptr = SDL_calloc(1, framesize);
  507. if (ptr == NULL) {
  508. COREAUDIO_CloseDevice(this);
  509. SDL_OutOfMemory();
  510. return 0;
  511. }
  512. this->hidden->captureBufferList.mNumberBuffers = 1;
  513. this->hidden->captureBufferList.mBuffers[0].mNumberChannels = this->spec.channels;
  514. this->hidden->captureBufferList.mBuffers[0].mDataByteSize = framesize;
  515. this->hidden->captureBufferList.mBuffers[0].mData = ptr;
  516. }
  517. #endif
  518. /* Set the audio callback */
  519. SDL_zero(callback);
  520. callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
  521. callback.inputProcRefCon = this;
  522. result = AudioUnitSetProperty(this->hidden->audioUnit,
  523. iscapture ? kAudioOutputUnitProperty_SetInputCallback : kAudioUnitProperty_SetRenderCallback,
  524. kAudioUnitScope_Global, output_bus, &callback, sizeof(callback));
  525. CHECK_RESULT
  526. ("AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)");
  527. /* Calculate the final parameters for this audio specification */
  528. SDL_CalculateAudioSpec(&this->spec);
  529. /* Allocate a sample buffer */
  530. this->hidden->bufferSize = this->spec.size;
  531. this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
  532. this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
  533. if (this->hidden->buffer == NULL) {
  534. COREAUDIO_CloseDevice(this);
  535. SDL_OutOfMemory();
  536. return 0;
  537. }
  538. result = AudioUnitInitialize(this->hidden->audioUnit);
  539. CHECK_RESULT("AudioUnitInitialize");
  540. /* Finally, start processing of the audio unit */
  541. result = AudioOutputUnitStart(this->hidden->audioUnit);
  542. CHECK_RESULT("AudioOutputUnitStart");
  543. /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
  544. /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
  545. #if MACOSX_COREAUDIO
  546. /* Fire a callback if the device stops being "alive" (disconnected, etc). */
  547. AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
  548. #endif
  549. /* We're running! */
  550. return 1;
  551. }
  552. static int
  553. COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
  554. {
  555. AudioStreamBasicDescription strdesc;
  556. SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
  557. int valid_datatype = 0;
  558. /* Initialize all variables that we clean on shutdown */
  559. this->hidden = (struct SDL_PrivateAudioData *)
  560. SDL_malloc((sizeof *this->hidden));
  561. if (this->hidden == NULL) {
  562. return SDL_OutOfMemory();
  563. }
  564. SDL_zerop(this->hidden);
  565. /* Setup a AudioStreamBasicDescription with the requested format */
  566. SDL_zero(strdesc);
  567. strdesc.mFormatID = kAudioFormatLinearPCM;
  568. strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
  569. strdesc.mChannelsPerFrame = this->spec.channels;
  570. strdesc.mSampleRate = this->spec.freq;
  571. strdesc.mFramesPerPacket = 1;
  572. while ((!valid_datatype) && (test_format)) {
  573. this->spec.format = test_format;
  574. /* Just a list of valid SDL formats, so people don't pass junk here. */
  575. switch (test_format) {
  576. case AUDIO_U8:
  577. case AUDIO_S8:
  578. case AUDIO_U16LSB:
  579. case AUDIO_S16LSB:
  580. case AUDIO_U16MSB:
  581. case AUDIO_S16MSB:
  582. case AUDIO_S32LSB:
  583. case AUDIO_S32MSB:
  584. case AUDIO_F32LSB:
  585. case AUDIO_F32MSB:
  586. valid_datatype = 1;
  587. strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
  588. if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
  589. strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
  590. if (SDL_AUDIO_ISFLOAT(this->spec.format))
  591. strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
  592. else if (SDL_AUDIO_ISSIGNED(this->spec.format))
  593. strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
  594. break;
  595. }
  596. }
  597. if (!valid_datatype) { /* shouldn't happen, but just in case... */
  598. COREAUDIO_CloseDevice(this);
  599. return SDL_SetError("Unsupported audio format");
  600. }
  601. strdesc.mBytesPerFrame =
  602. strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
  603. strdesc.mBytesPerPacket =
  604. strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
  605. if (!prepare_audiounit(this, handle, iscapture, &strdesc)) {
  606. COREAUDIO_CloseDevice(this);
  607. return -1; /* prepare_audiounit() will call SDL_SetError()... */
  608. }
  609. return 0; /* good to go. */
  610. }
  611. static void
  612. COREAUDIO_Deinitialize(void)
  613. {
  614. #if MACOSX_COREAUDIO
  615. AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
  616. free_audio_device_list(&capture_devs);
  617. free_audio_device_list(&output_devs);
  618. #endif
  619. }
  620. static int
  621. COREAUDIO_Init(SDL_AudioDriverImpl * impl)
  622. {
  623. /* Set the function pointers */
  624. impl->OpenDevice = COREAUDIO_OpenDevice;
  625. impl->CloseDevice = COREAUDIO_CloseDevice;
  626. impl->Deinitialize = COREAUDIO_Deinitialize;
  627. #if MACOSX_COREAUDIO
  628. impl->DetectDevices = COREAUDIO_DetectDevices;
  629. AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
  630. impl->HasCaptureSupport = 1;
  631. #else
  632. impl->OnlyHasDefaultOutputDevice = 1;
  633. /* Set category to ambient sound so that other music continues playing.
  634. You can change this at runtime in your own code if you need different
  635. behavior. If this is common, we can add an SDL hint for this.
  636. */
  637. AudioSessionInitialize(NULL, NULL, NULL, nil);
  638. UInt32 category = kAudioSessionCategory_AmbientSound;
  639. AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
  640. #endif
  641. impl->ProvidesOwnCallbackThread = 1;
  642. return 1; /* this audio target is available. */
  643. }
  644. AudioBootStrap COREAUDIO_bootstrap = {
  645. "coreaudio", "CoreAudio", COREAUDIO_Init, 0
  646. };
  647. #endif /* SDL_AUDIO_DRIVER_COREAUDIO */
  648. /* vi: set ts=4 sw=4 expandtab: */