coreaudio.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. /*
  2. * QEMU OS X CoreAudio audio driver
  3. *
  4. * Copyright (c) 2005 Mike Kronenberg
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #include "qemu/osdep.h"
  25. #include <CoreAudio/CoreAudio.h>
  26. #include <pthread.h> /* pthread_X */
  27. #include "qemu/main-loop.h"
  28. #include "qemu/module.h"
  29. #include "audio.h"
  30. #define AUDIO_CAP "coreaudio"
  31. #include "audio_int.h"
  32. typedef struct coreaudioVoiceOut {
  33. HWVoiceOut hw;
  34. pthread_mutex_t buf_mutex;
  35. AudioDeviceID outputDeviceID;
  36. int frameSizeSetting;
  37. uint32_t bufferCount;
  38. UInt32 audioDevicePropertyBufferFrameSize;
  39. AudioDeviceIOProcID ioprocid;
  40. bool enabled;
  41. } coreaudioVoiceOut;
  42. static const AudioObjectPropertyAddress voice_addr = {
  43. kAudioHardwarePropertyDefaultOutputDevice,
  44. kAudioObjectPropertyScopeGlobal,
  45. kAudioObjectPropertyElementMain
  46. };
  47. static OSStatus coreaudio_get_voice(AudioDeviceID *id)
  48. {
  49. UInt32 size = sizeof(*id);
  50. return AudioObjectGetPropertyData(kAudioObjectSystemObject,
  51. &voice_addr,
  52. 0,
  53. NULL,
  54. &size,
  55. id);
  56. }
  57. static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
  58. AudioValueRange *framerange)
  59. {
  60. UInt32 size = sizeof(*framerange);
  61. AudioObjectPropertyAddress addr = {
  62. kAudioDevicePropertyBufferFrameSizeRange,
  63. kAudioDevicePropertyScopeOutput,
  64. kAudioObjectPropertyElementMain
  65. };
  66. return AudioObjectGetPropertyData(id,
  67. &addr,
  68. 0,
  69. NULL,
  70. &size,
  71. framerange);
  72. }
  73. static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
  74. {
  75. UInt32 size = sizeof(*framesize);
  76. AudioObjectPropertyAddress addr = {
  77. kAudioDevicePropertyBufferFrameSize,
  78. kAudioDevicePropertyScopeOutput,
  79. kAudioObjectPropertyElementMain
  80. };
  81. return AudioObjectGetPropertyData(id,
  82. &addr,
  83. 0,
  84. NULL,
  85. &size,
  86. framesize);
  87. }
  88. static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
  89. {
  90. UInt32 size = sizeof(*framesize);
  91. AudioObjectPropertyAddress addr = {
  92. kAudioDevicePropertyBufferFrameSize,
  93. kAudioDevicePropertyScopeOutput,
  94. kAudioObjectPropertyElementMain
  95. };
  96. return AudioObjectSetPropertyData(id,
  97. &addr,
  98. 0,
  99. NULL,
  100. size,
  101. framesize);
  102. }
  103. static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
  104. AudioStreamBasicDescription *d)
  105. {
  106. UInt32 size = sizeof(*d);
  107. AudioObjectPropertyAddress addr = {
  108. kAudioDevicePropertyStreamFormat,
  109. kAudioDevicePropertyScopeOutput,
  110. kAudioObjectPropertyElementMain
  111. };
  112. return AudioObjectSetPropertyData(id,
  113. &addr,
  114. 0,
  115. NULL,
  116. size,
  117. d);
  118. }
  119. static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
  120. {
  121. UInt32 size = sizeof(*result);
  122. AudioObjectPropertyAddress addr = {
  123. kAudioDevicePropertyDeviceIsRunning,
  124. kAudioDevicePropertyScopeOutput,
  125. kAudioObjectPropertyElementMain
  126. };
  127. return AudioObjectGetPropertyData(id,
  128. &addr,
  129. 0,
  130. NULL,
  131. &size,
  132. result);
  133. }
  134. static void coreaudio_logstatus (OSStatus status)
  135. {
  136. const char *str = "BUG";
  137. switch (status) {
  138. case kAudioHardwareNoError:
  139. str = "kAudioHardwareNoError";
  140. break;
  141. case kAudioHardwareNotRunningError:
  142. str = "kAudioHardwareNotRunningError";
  143. break;
  144. case kAudioHardwareUnspecifiedError:
  145. str = "kAudioHardwareUnspecifiedError";
  146. break;
  147. case kAudioHardwareUnknownPropertyError:
  148. str = "kAudioHardwareUnknownPropertyError";
  149. break;
  150. case kAudioHardwareBadPropertySizeError:
  151. str = "kAudioHardwareBadPropertySizeError";
  152. break;
  153. case kAudioHardwareIllegalOperationError:
  154. str = "kAudioHardwareIllegalOperationError";
  155. break;
  156. case kAudioHardwareBadDeviceError:
  157. str = "kAudioHardwareBadDeviceError";
  158. break;
  159. case kAudioHardwareBadStreamError:
  160. str = "kAudioHardwareBadStreamError";
  161. break;
  162. case kAudioHardwareUnsupportedOperationError:
  163. str = "kAudioHardwareUnsupportedOperationError";
  164. break;
  165. case kAudioDeviceUnsupportedFormatError:
  166. str = "kAudioDeviceUnsupportedFormatError";
  167. break;
  168. case kAudioDevicePermissionsError:
  169. str = "kAudioDevicePermissionsError";
  170. break;
  171. default:
  172. AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
  173. return;
  174. }
  175. AUD_log (AUDIO_CAP, "Reason: %s\n", str);
  176. }
  177. static void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
  178. OSStatus status,
  179. const char *fmt,
  180. ...
  181. )
  182. {
  183. va_list ap;
  184. va_start (ap, fmt);
  185. AUD_log (AUDIO_CAP, fmt, ap);
  186. va_end (ap);
  187. coreaudio_logstatus (status);
  188. }
  189. static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
  190. OSStatus status,
  191. const char *typ,
  192. const char *fmt,
  193. ...
  194. )
  195. {
  196. va_list ap;
  197. AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
  198. va_start (ap, fmt);
  199. AUD_vlog (AUDIO_CAP, fmt, ap);
  200. va_end (ap);
  201. coreaudio_logstatus (status);
  202. }
  203. #define coreaudio_playback_logerr(status, ...) \
  204. coreaudio_logerr2(status, "playback", __VA_ARGS__)
  205. static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
  206. {
  207. int err;
  208. err = pthread_mutex_lock (&core->buf_mutex);
  209. if (err) {
  210. dolog ("Could not lock voice for %s\nReason: %s\n",
  211. fn_name, strerror (err));
  212. return -1;
  213. }
  214. return 0;
  215. }
  216. static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
  217. {
  218. int err;
  219. err = pthread_mutex_unlock (&core->buf_mutex);
  220. if (err) {
  221. dolog ("Could not unlock voice for %s\nReason: %s\n",
  222. fn_name, strerror (err));
  223. return -1;
  224. }
  225. return 0;
  226. }
  227. #define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
  228. static ret_type glue(coreaudio_, name)args_decl \
  229. { \
  230. coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
  231. ret_type ret; \
  232. \
  233. if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
  234. return 0; \
  235. } \
  236. \
  237. ret = glue(audio_generic_, name)args; \
  238. \
  239. coreaudio_buf_unlock(core, "coreaudio_" #name); \
  240. return ret; \
  241. }
  242. COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
  243. COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
  244. (hw, size))
  245. COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
  246. (HWVoiceOut *hw, void *buf, size_t size),
  247. (hw, buf, size))
  248. COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
  249. (hw, buf, size))
  250. #undef COREAUDIO_WRAPPER_FUNC
  251. /*
  252. * callback to feed audiooutput buffer. called without BQL.
  253. * allowed to lock "buf_mutex", but disallowed to have any other locks.
  254. */
  255. static OSStatus audioDeviceIOProc(
  256. AudioDeviceID inDevice,
  257. const AudioTimeStamp *inNow,
  258. const AudioBufferList *inInputData,
  259. const AudioTimeStamp *inInputTime,
  260. AudioBufferList *outOutputData,
  261. const AudioTimeStamp *inOutputTime,
  262. void *hwptr)
  263. {
  264. UInt32 frameCount, pending_frames;
  265. void *out = outOutputData->mBuffers[0].mData;
  266. HWVoiceOut *hw = hwptr;
  267. coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
  268. size_t len;
  269. if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
  270. inInputTime = 0;
  271. return 0;
  272. }
  273. if (inDevice != core->outputDeviceID) {
  274. coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
  275. return 0;
  276. }
  277. frameCount = core->audioDevicePropertyBufferFrameSize;
  278. pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
  279. /* if there are not enough samples, set signal and return */
  280. if (pending_frames < frameCount) {
  281. inInputTime = 0;
  282. coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
  283. return 0;
  284. }
  285. len = frameCount * hw->info.bytes_per_frame;
  286. while (len) {
  287. size_t write_len, start;
  288. start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
  289. assert(start < hw->size_emul);
  290. write_len = MIN(MIN(hw->pending_emul, len),
  291. hw->size_emul - start);
  292. memcpy(out, hw->buf_emul + start, write_len);
  293. hw->pending_emul -= write_len;
  294. len -= write_len;
  295. out += write_len;
  296. }
  297. coreaudio_buf_unlock (core, "audioDeviceIOProc");
  298. return 0;
  299. }
  300. static OSStatus init_out_device(coreaudioVoiceOut *core)
  301. {
  302. OSStatus status;
  303. AudioDeviceID deviceID;
  304. AudioValueRange frameRange;
  305. UInt32 audioDevicePropertyBufferFrameSize;
  306. AudioDeviceIOProcID ioprocid;
  307. AudioStreamBasicDescription streamBasicDescription = {
  308. .mBitsPerChannel = core->hw.info.bits,
  309. .mBytesPerFrame = core->hw.info.bytes_per_frame,
  310. .mBytesPerPacket = core->hw.info.bytes_per_frame,
  311. .mChannelsPerFrame = core->hw.info.nchannels,
  312. .mFormatFlags = kLinearPCMFormatFlagIsFloat,
  313. .mFormatID = kAudioFormatLinearPCM,
  314. .mFramesPerPacket = 1,
  315. .mSampleRate = core->hw.info.freq
  316. };
  317. status = coreaudio_get_voice(&deviceID);
  318. if (status != kAudioHardwareNoError) {
  319. coreaudio_playback_logerr (status,
  320. "Could not get default output Device\n");
  321. return status;
  322. }
  323. if (deviceID == kAudioDeviceUnknown) {
  324. dolog ("Could not initialize playback - Unknown Audiodevice\n");
  325. return status;
  326. }
  327. /* get minimum and maximum buffer frame sizes */
  328. status = coreaudio_get_framesizerange(deviceID, &frameRange);
  329. if (status == kAudioHardwareBadObjectError) {
  330. return 0;
  331. }
  332. if (status != kAudioHardwareNoError) {
  333. coreaudio_playback_logerr (status,
  334. "Could not get device buffer frame range\n");
  335. return status;
  336. }
  337. if (frameRange.mMinimum > core->frameSizeSetting) {
  338. audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
  339. dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
  340. } else if (frameRange.mMaximum < core->frameSizeSetting) {
  341. audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
  342. dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
  343. } else {
  344. audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
  345. }
  346. /* set Buffer Frame Size */
  347. status = coreaudio_set_framesize(deviceID,
  348. &audioDevicePropertyBufferFrameSize);
  349. if (status == kAudioHardwareBadObjectError) {
  350. return 0;
  351. }
  352. if (status != kAudioHardwareNoError) {
  353. coreaudio_playback_logerr (status,
  354. "Could not set device buffer frame size %" PRIu32 "\n",
  355. (uint32_t)audioDevicePropertyBufferFrameSize);
  356. return status;
  357. }
  358. /* get Buffer Frame Size */
  359. status = coreaudio_get_framesize(deviceID,
  360. &audioDevicePropertyBufferFrameSize);
  361. if (status == kAudioHardwareBadObjectError) {
  362. return 0;
  363. }
  364. if (status != kAudioHardwareNoError) {
  365. coreaudio_playback_logerr (status,
  366. "Could not get device buffer frame size\n");
  367. return status;
  368. }
  369. /* set Samplerate */
  370. status = coreaudio_set_streamformat(deviceID, &streamBasicDescription);
  371. if (status == kAudioHardwareBadObjectError) {
  372. return 0;
  373. }
  374. if (status != kAudioHardwareNoError) {
  375. coreaudio_playback_logerr (status,
  376. "Could not set samplerate %lf\n",
  377. streamBasicDescription.mSampleRate);
  378. return status;
  379. }
  380. /*
  381. * set Callback.
  382. *
  383. * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
  384. * internal function named HALB_Mutex::Lock(), which locks a mutex in
  385. * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
  386. * AudioObjectGetPropertyData, which is called by coreaudio driver.
  387. * Therefore, the specified callback must be designed to avoid a deadlock
  388. * with the callers of AudioObjectGetPropertyData.
  389. */
  390. ioprocid = NULL;
  391. status = AudioDeviceCreateIOProcID(deviceID,
  392. audioDeviceIOProc,
  393. &core->hw,
  394. &ioprocid);
  395. if (status == kAudioHardwareBadDeviceError) {
  396. return 0;
  397. }
  398. if (status != kAudioHardwareNoError || ioprocid == NULL) {
  399. coreaudio_playback_logerr (status, "Could not set IOProc\n");
  400. return status;
  401. }
  402. core->outputDeviceID = deviceID;
  403. core->audioDevicePropertyBufferFrameSize = audioDevicePropertyBufferFrameSize;
  404. core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
  405. audio_generic_initialize_buffer_out(&core->hw);
  406. core->ioprocid = ioprocid;
  407. return 0;
  408. }
  409. static void fini_out_device(coreaudioVoiceOut *core)
  410. {
  411. OSStatus status;
  412. UInt32 isrunning;
  413. /* stop playback */
  414. status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
  415. if (status != kAudioHardwareBadObjectError) {
  416. if (status != kAudioHardwareNoError) {
  417. coreaudio_logerr(status,
  418. "Could not determine whether Device is playing\n");
  419. }
  420. if (isrunning) {
  421. status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
  422. if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
  423. coreaudio_logerr(status, "Could not stop playback\n");
  424. }
  425. }
  426. }
  427. /* remove callback */
  428. status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
  429. core->ioprocid);
  430. if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
  431. coreaudio_logerr(status, "Could not remove IOProc\n");
  432. }
  433. core->outputDeviceID = kAudioDeviceUnknown;
  434. }
  435. static void update_device_playback_state(coreaudioVoiceOut *core)
  436. {
  437. OSStatus status;
  438. UInt32 isrunning;
  439. status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
  440. if (status != kAudioHardwareNoError) {
  441. if (status != kAudioHardwareBadObjectError) {
  442. coreaudio_logerr(status,
  443. "Could not determine whether Device is playing\n");
  444. }
  445. return;
  446. }
  447. if (core->enabled) {
  448. /* start playback */
  449. if (!isrunning) {
  450. status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
  451. if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
  452. coreaudio_logerr (status, "Could not resume playback\n");
  453. }
  454. }
  455. } else {
  456. /* stop playback */
  457. if (isrunning) {
  458. status = AudioDeviceStop(core->outputDeviceID,
  459. core->ioprocid);
  460. if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
  461. coreaudio_logerr(status, "Could not pause playback\n");
  462. }
  463. }
  464. }
  465. }
  466. /* called without BQL. */
  467. static OSStatus handle_voice_change(
  468. AudioObjectID in_object_id,
  469. UInt32 in_number_addresses,
  470. const AudioObjectPropertyAddress *in_addresses,
  471. void *in_client_data)
  472. {
  473. coreaudioVoiceOut *core = in_client_data;
  474. bql_lock();
  475. if (core->outputDeviceID) {
  476. fini_out_device(core);
  477. }
  478. init_out_device(core);
  479. if (core->outputDeviceID) {
  480. update_device_playback_state(core);
  481. }
  482. bql_unlock();
  483. return 0;
  484. }
  485. static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
  486. void *drv_opaque)
  487. {
  488. OSStatus status;
  489. coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
  490. int err;
  491. Audiodev *dev = drv_opaque;
  492. AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
  493. struct audsettings obt_as;
  494. /* create mutex */
  495. err = pthread_mutex_init(&core->buf_mutex, NULL);
  496. if (err) {
  497. dolog("Could not create mutex\nReason: %s\n", strerror (err));
  498. return -1;
  499. }
  500. obt_as = *as;
  501. as = &obt_as;
  502. as->fmt = AUDIO_FORMAT_F32;
  503. audio_pcm_init_info (&hw->info, as);
  504. core->frameSizeSetting = audio_buffer_frames(
  505. qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
  506. core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
  507. status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
  508. &voice_addr, handle_voice_change,
  509. core);
  510. if (status != kAudioHardwareNoError) {
  511. coreaudio_playback_logerr (status,
  512. "Could not listen to voice property change\n");
  513. return -1;
  514. }
  515. if (init_out_device(core)) {
  516. status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
  517. &voice_addr,
  518. handle_voice_change,
  519. core);
  520. if (status != kAudioHardwareNoError) {
  521. coreaudio_playback_logerr(status,
  522. "Could not remove voice property change listener\n");
  523. }
  524. return -1;
  525. }
  526. return 0;
  527. }
  528. static void coreaudio_fini_out (HWVoiceOut *hw)
  529. {
  530. OSStatus status;
  531. int err;
  532. coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
  533. status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
  534. &voice_addr,
  535. handle_voice_change,
  536. core);
  537. if (status != kAudioHardwareNoError) {
  538. coreaudio_logerr(status, "Could not remove voice property change listener\n");
  539. }
  540. fini_out_device(core);
  541. /* destroy mutex */
  542. err = pthread_mutex_destroy(&core->buf_mutex);
  543. if (err) {
  544. dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
  545. }
  546. }
  547. static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
  548. {
  549. coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
  550. core->enabled = enable;
  551. update_device_playback_state(core);
  552. }
  553. static void *coreaudio_audio_init(Audiodev *dev, Error **errp)
  554. {
  555. return dev;
  556. }
  557. static void coreaudio_audio_fini (void *opaque)
  558. {
  559. }
  560. static struct audio_pcm_ops coreaudio_pcm_ops = {
  561. .init_out = coreaudio_init_out,
  562. .fini_out = coreaudio_fini_out,
  563. /* wrapper for audio_generic_write */
  564. .write = coreaudio_write,
  565. /* wrapper for audio_generic_buffer_get_free */
  566. .buffer_get_free = coreaudio_buffer_get_free,
  567. /* wrapper for audio_generic_get_buffer_out */
  568. .get_buffer_out = coreaudio_get_buffer_out,
  569. /* wrapper for audio_generic_put_buffer_out */
  570. .put_buffer_out = coreaudio_put_buffer_out,
  571. .enable_out = coreaudio_enable_out
  572. };
  573. static struct audio_driver coreaudio_audio_driver = {
  574. .name = "coreaudio",
  575. .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
  576. .init = coreaudio_audio_init,
  577. .fini = coreaudio_audio_fini,
  578. .pcm_ops = &coreaudio_pcm_ops,
  579. .max_voices_out = 1,
  580. .max_voices_in = 0,
  581. .voice_size_out = sizeof (coreaudioVoiceOut),
  582. .voice_size_in = 0
  583. };
  584. static void register_audio_coreaudio(void)
  585. {
  586. audio_driver_register(&coreaudio_audio_driver);
  587. }
  588. type_init(register_audio_coreaudio);