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