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