]> git.proxmox.com Git - mirror_qemu.git/blame - audio/coreaudio.c
Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-6.1-pull-request...
[mirror_qemu.git] / audio / coreaudio.c
CommitLineData
1d14ffa9
FB
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
6086a565 25#include "qemu/osdep.h"
1d14ffa9 26#include <CoreAudio/CoreAudio.h>
1d14ffa9
FB
27#include <pthread.h> /* pthread_X */
28
0b8fa32f 29#include "qemu/module.h"
749bc4bf 30#include "audio.h"
1d14ffa9
FB
31
32#define AUDIO_CAP "coreaudio"
33#include "audio_int.h"
34
1d14ffa9
FB
35typedef struct coreaudioVoiceOut {
36 HWVoiceOut hw;
37 pthread_mutex_t mutex;
38 AudioDeviceID outputDeviceID;
7d6948cd
AO
39 int frameSizeSetting;
40 uint32_t bufferCount;
5e941d4b 41 UInt32 audioDevicePropertyBufferFrameSize;
2f79a18f 42 AudioDeviceIOProcID ioprocid;
3ba6e3f6 43 bool enabled;
1d14ffa9
FB
44} coreaudioVoiceOut;
45
3ba6e3f6
AO
46static const AudioObjectPropertyAddress voice_addr = {
47 kAudioHardwarePropertyDefaultOutputDevice,
48 kAudioObjectPropertyScopeGlobal,
49 kAudioObjectPropertyElementMaster
50};
51
624d1fc3
PM
52static OSStatus coreaudio_get_voice(AudioDeviceID *id)
53{
54 UInt32 size = sizeof(*id);
624d1fc3
PM
55
56 return AudioObjectGetPropertyData(kAudioObjectSystemObject,
3ba6e3f6 57 &voice_addr,
624d1fc3
PM
58 0,
59 NULL,
60 &size,
61 id);
62}
2d99f629
PM
63
64static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
65 AudioValueRange *framerange)
66{
67 UInt32 size = sizeof(*framerange);
68 AudioObjectPropertyAddress addr = {
69 kAudioDevicePropertyBufferFrameSizeRange,
70 kAudioDevicePropertyScopeOutput,
71 kAudioObjectPropertyElementMaster
72 };
73
74 return AudioObjectGetPropertyData(id,
75 &addr,
76 0,
77 NULL,
78 &size,
79 framerange);
80}
81
82static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
83{
84 UInt32 size = sizeof(*framesize);
85 AudioObjectPropertyAddress addr = {
86 kAudioDevicePropertyBufferFrameSize,
87 kAudioDevicePropertyScopeOutput,
88 kAudioObjectPropertyElementMaster
89 };
90
91 return AudioObjectGetPropertyData(id,
92 &addr,
93 0,
94 NULL,
95 &size,
96 framesize);
97}
98
99static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
100{
101 UInt32 size = sizeof(*framesize);
102 AudioObjectPropertyAddress addr = {
103 kAudioDevicePropertyBufferFrameSize,
104 kAudioDevicePropertyScopeOutput,
105 kAudioObjectPropertyElementMaster
106 };
107
108 return AudioObjectSetPropertyData(id,
109 &addr,
110 0,
111 NULL,
112 size,
113 framesize);
114}
115
2d99f629
PM
116static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
117 AudioStreamBasicDescription *d)
118{
119 UInt32 size = sizeof(*d);
120 AudioObjectPropertyAddress addr = {
121 kAudioDevicePropertyStreamFormat,
122 kAudioDevicePropertyScopeOutput,
123 kAudioObjectPropertyElementMaster
124 };
125
126 return AudioObjectSetPropertyData(id,
127 &addr,
128 0,
129 NULL,
130 size,
131 d);
132}
133
134static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
135{
136 UInt32 size = sizeof(*result);
137 AudioObjectPropertyAddress addr = {
138 kAudioDevicePropertyDeviceIsRunning,
139 kAudioDevicePropertyScopeOutput,
140 kAudioObjectPropertyElementMaster
141 };
142
143 return AudioObjectGetPropertyData(id,
144 &addr,
145 0,
146 NULL,
147 &size,
148 result);
149}
95a860f6 150
1d14ffa9
FB
151static void coreaudio_logstatus (OSStatus status)
152{
d9cbb0f3 153 const char *str = "BUG";
1d14ffa9 154
3c8de96c 155 switch (status) {
1d14ffa9
FB
156 case kAudioHardwareNoError:
157 str = "kAudioHardwareNoError";
158 break;
159
160 case kAudioHardwareNotRunningError:
161 str = "kAudioHardwareNotRunningError";
162 break;
163
164 case kAudioHardwareUnspecifiedError:
165 str = "kAudioHardwareUnspecifiedError";
166 break;
167
168 case kAudioHardwareUnknownPropertyError:
169 str = "kAudioHardwareUnknownPropertyError";
170 break;
171
172 case kAudioHardwareBadPropertySizeError:
173 str = "kAudioHardwareBadPropertySizeError";
174 break;
175
176 case kAudioHardwareIllegalOperationError:
177 str = "kAudioHardwareIllegalOperationError";
178 break;
179
180 case kAudioHardwareBadDeviceError:
181 str = "kAudioHardwareBadDeviceError";
182 break;
183
184 case kAudioHardwareBadStreamError:
185 str = "kAudioHardwareBadStreamError";
186 break;
187
188 case kAudioHardwareUnsupportedOperationError:
189 str = "kAudioHardwareUnsupportedOperationError";
190 break;
191
192 case kAudioDeviceUnsupportedFormatError:
193 str = "kAudioDeviceUnsupportedFormatError";
194 break;
195
196 case kAudioDevicePermissionsError:
197 str = "kAudioDevicePermissionsError";
198 break;
199
200 default:
744d3644 201 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
1d14ffa9
FB
202 return;
203 }
204
205 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
206}
207
208static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
209 OSStatus status,
210 const char *fmt,
211 ...
212 )
213{
214 va_list ap;
215
216 va_start (ap, fmt);
217 AUD_log (AUDIO_CAP, fmt, ap);
218 va_end (ap);
219
220 coreaudio_logstatus (status);
221}
222
223static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
224 OSStatus status,
225 const char *typ,
226 const char *fmt,
227 ...
228 )
229{
230 va_list ap;
231
c0fe3827 232 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
1d14ffa9
FB
233
234 va_start (ap, fmt);
235 AUD_vlog (AUDIO_CAP, fmt, ap);
236 va_end (ap);
237
238 coreaudio_logstatus (status);
239}
240
7d6948cd
AO
241#define coreaudio_playback_logerr(status, ...) \
242 coreaudio_logerr2(status, "playback", __VA_ARGS__)
243
1d14ffa9
FB
244static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
245{
246 int err;
247
248 err = pthread_mutex_lock (&core->mutex);
249 if (err) {
c0fe3827 250 dolog ("Could not lock voice for %s\nReason: %s\n",
1d14ffa9
FB
251 fn_name, strerror (err));
252 return -1;
253 }
254 return 0;
255}
256
257static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
258{
259 int err;
260
261 err = pthread_mutex_unlock (&core->mutex);
262 if (err) {
c0fe3827 263 dolog ("Could not unlock voice for %s\nReason: %s\n",
1d14ffa9
FB
264 fn_name, strerror (err));
265 return -1;
266 }
267 return 0;
268}
269
2ceb8240
KZ
270#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
271 static ret_type glue(coreaudio_, name)args_decl \
272 { \
273 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
274 ret_type ret; \
275 \
276 if (coreaudio_lock(core, "coreaudio_" #name)) { \
277 return 0; \
278 } \
279 \
280 ret = glue(audio_generic_, name)args; \
281 \
282 coreaudio_unlock(core, "coreaudio_" #name); \
283 return ret; \
1d14ffa9 284 }
2ceb8240
KZ
285COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
286 (hw, size))
fdc8c5f4 287COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
2ceb8240
KZ
288 (HWVoiceOut *hw, void *buf, size_t size),
289 (hw, buf, size))
290COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
291 (hw, buf, size))
292#undef COREAUDIO_WRAPPER_FUNC
1d14ffa9
FB
293
294/* callback to feed audiooutput buffer */
295static OSStatus audioDeviceIOProc(
296 AudioDeviceID inDevice,
dcf10e40
ZH
297 const AudioTimeStamp *inNow,
298 const AudioBufferList *inInputData,
299 const AudioTimeStamp *inInputTime,
300 AudioBufferList *outOutputData,
301 const AudioTimeStamp *inOutputTime,
302 void *hwptr)
1d14ffa9 303{
2ceb8240
KZ
304 UInt32 frameCount, pending_frames;
305 void *out = outOutputData->mBuffers[0].mData;
1d14ffa9
FB
306 HWVoiceOut *hw = hwptr;
307 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
2ceb8240 308 size_t len;
1d14ffa9
FB
309
310 if (coreaudio_lock (core, "audioDeviceIOProc")) {
311 inInputTime = 0;
312 return 0;
313 }
314
3ba6e3f6
AO
315 if (inDevice != core->outputDeviceID) {
316 coreaudio_unlock (core, "audioDeviceIOProc(old device)");
317 return 0;
318 }
319
5e941d4b 320 frameCount = core->audioDevicePropertyBufferFrameSize;
2b9cce8c 321 pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
1d14ffa9
FB
322
323 /* if there are not enough samples, set signal and return */
2ceb8240 324 if (pending_frames < frameCount) {
1d14ffa9
FB
325 inInputTime = 0;
326 coreaudio_unlock (core, "audioDeviceIOProc(empty)");
327 return 0;
328 }
329
2b9cce8c 330 len = frameCount * hw->info.bytes_per_frame;
2ceb8240
KZ
331 while (len) {
332 size_t write_len;
333 ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
334 if (start < 0) {
335 start += hw->size_emul;
336 }
337 assert(start >= 0 && start < hw->size_emul);
1d14ffa9 338
2ceb8240
KZ
339 write_len = MIN(MIN(hw->pending_emul, len),
340 hw->size_emul - start);
1d14ffa9 341
2ceb8240
KZ
342 memcpy(out, hw->buf_emul + start, write_len);
343 hw->pending_emul -= write_len;
344 len -= write_len;
345 out += write_len;
346 }
1d14ffa9
FB
347
348 coreaudio_unlock (core, "audioDeviceIOProc");
349 return 0;
350}
351
7d6948cd 352static OSStatus init_out_device(coreaudioVoiceOut *core)
1d14ffa9
FB
353{
354 OSStatus status;
5e941d4b 355 AudioValueRange frameRange;
1d14ffa9 356
986bdbc6
AO
357 AudioStreamBasicDescription streamBasicDescription = {
358 .mBitsPerChannel = core->hw.info.bits,
359 .mBytesPerFrame = core->hw.info.bytes_per_frame,
360 .mBytesPerPacket = core->hw.info.bytes_per_frame,
361 .mChannelsPerFrame = core->hw.info.nchannels,
362 .mFormatFlags = kLinearPCMFormatFlagIsFloat,
363 .mFormatID = kAudioFormatLinearPCM,
364 .mFramesPerPacket = 1,
365 .mSampleRate = core->hw.info.freq
366 };
367
88a0f830 368 status = coreaudio_get_voice(&core->outputDeviceID);
1d14ffa9 369 if (status != kAudioHardwareNoError) {
7d6948cd
AO
370 coreaudio_playback_logerr (status,
371 "Could not get default output Device\n");
372 return status;
1d14ffa9
FB
373 }
374 if (core->outputDeviceID == kAudioDeviceUnknown) {
7d6948cd
AO
375 dolog ("Could not initialize playback - Unknown Audiodevice\n");
376 return status;
1d14ffa9
FB
377 }
378
5e941d4b 379 /* get minimum and maximum buffer frame sizes */
95a860f6
PM
380 status = coreaudio_get_framesizerange(core->outputDeviceID,
381 &frameRange);
3ba6e3f6
AO
382 if (status == kAudioHardwareBadObjectError) {
383 return 0;
384 }
5e941d4b 385 if (status != kAudioHardwareNoError) {
7d6948cd
AO
386 coreaudio_playback_logerr (status,
387 "Could not get device buffer frame range\n");
388 return status;
5e941d4b
FB
389 }
390
7d6948cd 391 if (frameRange.mMinimum > core->frameSizeSetting) {
5e941d4b
FB
392 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
393 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
7d6948cd 394 } else if (frameRange.mMaximum < core->frameSizeSetting) {
5e941d4b
FB
395 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
396 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
6c6886bd 397 } else {
7d6948cd 398 core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
5e941d4b
FB
399 }
400
401 /* set Buffer Frame Size */
95a860f6
PM
402 status = coreaudio_set_framesize(core->outputDeviceID,
403 &core->audioDevicePropertyBufferFrameSize);
3ba6e3f6
AO
404 if (status == kAudioHardwareBadObjectError) {
405 return 0;
406 }
1d14ffa9 407 if (status != kAudioHardwareNoError) {
7d6948cd
AO
408 coreaudio_playback_logerr (status,
409 "Could not set device buffer frame size %" PRIu32 "\n",
410 (uint32_t)core->audioDevicePropertyBufferFrameSize);
411 return status;
1d14ffa9
FB
412 }
413
5e941d4b 414 /* get Buffer Frame Size */
95a860f6
PM
415 status = coreaudio_get_framesize(core->outputDeviceID,
416 &core->audioDevicePropertyBufferFrameSize);
3ba6e3f6
AO
417 if (status == kAudioHardwareBadObjectError) {
418 return 0;
419 }
1d14ffa9 420 if (status != kAudioHardwareNoError) {
7d6948cd
AO
421 coreaudio_playback_logerr (status,
422 "Could not get device buffer frame size\n");
423 return status;
1d14ffa9 424 }
7d6948cd 425 core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
1d14ffa9 426
1d14ffa9 427 /* set Samplerate */
95a860f6 428 status = coreaudio_set_streamformat(core->outputDeviceID,
986bdbc6 429 &streamBasicDescription);
3ba6e3f6
AO
430 if (status == kAudioHardwareBadObjectError) {
431 return 0;
432 }
1d14ffa9 433 if (status != kAudioHardwareNoError) {
7d6948cd
AO
434 coreaudio_playback_logerr (status,
435 "Could not set samplerate %lf\n",
986bdbc6 436 streamBasicDescription.mSampleRate);
1d14ffa9 437 core->outputDeviceID = kAudioDeviceUnknown;
7d6948cd 438 return status;
1d14ffa9
FB
439 }
440
441 /* set Callback */
2f79a18f
PM
442 core->ioprocid = NULL;
443 status = AudioDeviceCreateIOProcID(core->outputDeviceID,
444 audioDeviceIOProc,
7d6948cd 445 &core->hw,
2f79a18f 446 &core->ioprocid);
3ba6e3f6
AO
447 if (status == kAudioHardwareBadDeviceError) {
448 return 0;
449 }
2f79a18f 450 if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
7d6948cd 451 coreaudio_playback_logerr (status, "Could not set IOProc\n");
1d14ffa9 452 core->outputDeviceID = kAudioDeviceUnknown;
7d6948cd 453 return status;
1d14ffa9
FB
454 }
455
1d14ffa9
FB
456 return 0;
457}
458
7d6948cd 459static void fini_out_device(coreaudioVoiceOut *core)
1d14ffa9
FB
460{
461 OSStatus status;
3ba6e3f6 462 UInt32 isrunning;
1d14ffa9 463
ceb1165e 464 /* stop playback */
3ba6e3f6
AO
465 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
466 if (status != kAudioHardwareBadObjectError) {
5e941d4b 467 if (status != kAudioHardwareNoError) {
3ba6e3f6
AO
468 coreaudio_logerr(status,
469 "Could not determine whether Device is playing\n");
470 }
471
472 if (isrunning) {
473 status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
474 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
475 coreaudio_logerr(status, "Could not stop playback\n");
476 }
5e941d4b 477 }
1d14ffa9 478 }
ceb1165e
VR
479
480 /* remove callback */
481 status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
482 core->ioprocid);
3ba6e3f6 483 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
ceb1165e
VR
484 coreaudio_logerr(status, "Could not remove IOProc\n");
485 }
1d14ffa9 486 core->outputDeviceID = kAudioDeviceUnknown;
7d6948cd
AO
487}
488
3ba6e3f6
AO
489static void update_device_playback_state(coreaudioVoiceOut *core)
490{
491 OSStatus status;
492 UInt32 isrunning;
493
494 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
495 if (status != kAudioHardwareNoError) {
496 if (status != kAudioHardwareBadObjectError) {
497 coreaudio_logerr(status,
498 "Could not determine whether Device is playing\n");
499 }
500
501 return;
502 }
503
504 if (core->enabled) {
505 /* start playback */
506 if (!isrunning) {
507 status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
508 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
509 coreaudio_logerr (status, "Could not resume playback\n");
510 }
511 }
512 } else {
513 /* stop playback */
514 if (isrunning) {
515 status = AudioDeviceStop(core->outputDeviceID,
516 core->ioprocid);
517 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
518 coreaudio_logerr(status, "Could not pause playback\n");
519 }
520 }
521 }
522}
523
524static OSStatus handle_voice_change(
525 AudioObjectID in_object_id,
526 UInt32 in_number_addresses,
527 const AudioObjectPropertyAddress *in_addresses,
528 void *in_client_data)
529{
530 OSStatus status;
531 coreaudioVoiceOut *core = in_client_data;
532
533 if (coreaudio_lock(core, __func__)) {
534 abort();
535 }
536
537 if (core->outputDeviceID) {
538 fini_out_device(core);
539 }
540
541 status = init_out_device(core);
542 if (!status) {
543 update_device_playback_state(core);
544 }
545
546 coreaudio_unlock (core, __func__);
547 return status;
548}
549
7d6948cd
AO
550static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
551 void *drv_opaque)
552{
553 OSStatus status;
554 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
555 int err;
556 Audiodev *dev = drv_opaque;
557 AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
558 struct audsettings obt_as;
559
560 /* create mutex */
561 err = pthread_mutex_init(&core->mutex, NULL);
562 if (err) {
563 dolog("Could not create mutex\nReason: %s\n", strerror (err));
3ba6e3f6
AO
564 goto mutex_error;
565 }
566
567 if (coreaudio_lock(core, __func__)) {
568 goto lock_error;
7d6948cd
AO
569 }
570
571 obt_as = *as;
572 as = &obt_as;
573 as->fmt = AUDIO_FORMAT_F32;
574 audio_pcm_init_info (&hw->info, as);
575
576 core->frameSizeSetting = audio_buffer_frames(
577 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
578
579 core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
7d6948cd 580
3ba6e3f6
AO
581 status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
582 &voice_addr, handle_voice_change,
583 core);
584 if (status != kAudioHardwareNoError) {
585 coreaudio_playback_logerr (status,
586 "Could not listen to voice property change\n");
587 goto listener_error;
588 }
589
7d6948cd 590 if (init_out_device(core)) {
3ba6e3f6 591 goto device_error;
7d6948cd
AO
592 }
593
3ba6e3f6 594 coreaudio_unlock(core, __func__);
7d6948cd 595 return 0;
3ba6e3f6
AO
596
597device_error:
598 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
599 &voice_addr,
600 handle_voice_change,
601 core);
602 if (status != kAudioHardwareNoError) {
603 coreaudio_playback_logerr(status,
604 "Could not remove voice property change listener\n");
605 }
606
607listener_error:
608 coreaudio_unlock(core, __func__);
609
610lock_error:
611 err = pthread_mutex_destroy(&core->mutex);
612 if (err) {
613 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
614 }
615
616mutex_error:
617 return -1;
7d6948cd
AO
618}
619
620static void coreaudio_fini_out (HWVoiceOut *hw)
621{
622 OSStatus status;
623 int err;
624 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
625
3ba6e3f6
AO
626 if (coreaudio_lock(core, __func__)) {
627 abort();
628 }
629
630 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
631 &voice_addr,
632 handle_voice_change,
633 core);
634 if (status != kAudioHardwareNoError) {
635 coreaudio_logerr(status, "Could not remove voice property change listener\n");
636 }
637
7d6948cd 638 fini_out_device(core);
1d14ffa9 639
3ba6e3f6
AO
640 coreaudio_unlock(core, __func__);
641
1d14ffa9
FB
642 /* destroy mutex */
643 err = pthread_mutex_destroy(&core->mutex);
644 if (err) {
c0fe3827 645 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
1d14ffa9
FB
646 }
647}
648
571a8c52 649static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
1d14ffa9 650{
1d14ffa9
FB
651 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
652
3ba6e3f6
AO
653 if (coreaudio_lock(core, __func__)) {
654 abort();
1d14ffa9 655 }
3ba6e3f6
AO
656
657 core->enabled = enable;
658 update_device_playback_state(core);
659
660 coreaudio_unlock(core, __func__);
1d14ffa9
FB
661}
662
71830221 663static void *coreaudio_audio_init(Audiodev *dev)
1d14ffa9 664{
17c56dc1 665 return dev;
1d14ffa9
FB
666}
667
668static void coreaudio_audio_fini (void *opaque)
669{
1d14ffa9
FB
670}
671
35f4b58c 672static struct audio_pcm_ops coreaudio_pcm_ops = {
1dd3e4d1
JQ
673 .init_out = coreaudio_init_out,
674 .fini_out = coreaudio_fini_out,
fdc8c5f4 675 /* wrapper for audio_generic_write */
2ceb8240 676 .write = coreaudio_write,
fdc8c5f4 677 /* wrapper for audio_generic_get_buffer_out */
2ceb8240 678 .get_buffer_out = coreaudio_get_buffer_out,
fdc8c5f4
VR
679 /* wrapper for audio_generic_put_buffer_out */
680 .put_buffer_out = coreaudio_put_buffer_out,
571a8c52 681 .enable_out = coreaudio_enable_out
1d14ffa9
FB
682};
683
d3893a39 684static struct audio_driver coreaudio_audio_driver = {
bee37f32
JQ
685 .name = "coreaudio",
686 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
bee37f32
JQ
687 .init = coreaudio_audio_init,
688 .fini = coreaudio_audio_fini,
689 .pcm_ops = &coreaudio_pcm_ops,
690 .can_be_default = 1,
691 .max_voices_out = 1,
692 .max_voices_in = 0,
693 .voice_size_out = sizeof (coreaudioVoiceOut),
694 .voice_size_in = 0
1d14ffa9 695};
d3893a39
GH
696
697static void register_audio_coreaudio(void)
698{
699 audio_driver_register(&coreaudio_audio_driver);
700}
701type_init(register_audio_coreaudio);