]> git.proxmox.com Git - mirror_qemu.git/blame - audio/coreaudio.m
Replace GCC_FMT_ATTR with G_GNUC_PRINTF
[mirror_qemu.git] / audio / coreaudio.m
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
9f56bd6d
PMD
47#if !defined(MAC_OS_VERSION_12_0) \
48 || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0)
49#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
50#endif
51
3ba6e3f6
AO
52static const AudioObjectPropertyAddress voice_addr = {
53 kAudioHardwarePropertyDefaultOutputDevice,
54 kAudioObjectPropertyScopeGlobal,
9f56bd6d 55 kAudioObjectPropertyElementMain
3ba6e3f6
AO
56};
57
624d1fc3
PM
58static OSStatus coreaudio_get_voice(AudioDeviceID *id)
59{
60 UInt32 size = sizeof(*id);
624d1fc3
PM
61
62 return AudioObjectGetPropertyData(kAudioObjectSystemObject,
3ba6e3f6 63 &voice_addr,
624d1fc3
PM
64 0,
65 NULL,
66 &size,
67 id);
68}
2d99f629
PM
69
70static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
71 AudioValueRange *framerange)
72{
73 UInt32 size = sizeof(*framerange);
74 AudioObjectPropertyAddress addr = {
75 kAudioDevicePropertyBufferFrameSizeRange,
76 kAudioDevicePropertyScopeOutput,
9f56bd6d 77 kAudioObjectPropertyElementMain
2d99f629
PM
78 };
79
80 return AudioObjectGetPropertyData(id,
81 &addr,
82 0,
83 NULL,
84 &size,
85 framerange);
86}
87
88static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
89{
90 UInt32 size = sizeof(*framesize);
91 AudioObjectPropertyAddress addr = {
92 kAudioDevicePropertyBufferFrameSize,
93 kAudioDevicePropertyScopeOutput,
9f56bd6d 94 kAudioObjectPropertyElementMain
2d99f629
PM
95 };
96
97 return AudioObjectGetPropertyData(id,
98 &addr,
99 0,
100 NULL,
101 &size,
102 framesize);
103}
104
105static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
106{
107 UInt32 size = sizeof(*framesize);
108 AudioObjectPropertyAddress addr = {
109 kAudioDevicePropertyBufferFrameSize,
110 kAudioDevicePropertyScopeOutput,
9f56bd6d 111 kAudioObjectPropertyElementMain
2d99f629
PM
112 };
113
114 return AudioObjectSetPropertyData(id,
115 &addr,
116 0,
117 NULL,
118 size,
119 framesize);
120}
121
2d99f629
PM
122static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
123 AudioStreamBasicDescription *d)
124{
125 UInt32 size = sizeof(*d);
126 AudioObjectPropertyAddress addr = {
127 kAudioDevicePropertyStreamFormat,
128 kAudioDevicePropertyScopeOutput,
9f56bd6d 129 kAudioObjectPropertyElementMain
2d99f629
PM
130 };
131
132 return AudioObjectSetPropertyData(id,
133 &addr,
134 0,
135 NULL,
136 size,
137 d);
138}
139
140static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
141{
142 UInt32 size = sizeof(*result);
143 AudioObjectPropertyAddress addr = {
144 kAudioDevicePropertyDeviceIsRunning,
145 kAudioDevicePropertyScopeOutput,
9f56bd6d 146 kAudioObjectPropertyElementMain
2d99f629
PM
147 };
148
149 return AudioObjectGetPropertyData(id,
150 &addr,
151 0,
152 NULL,
153 &size,
154 result);
155}
95a860f6 156
1d14ffa9
FB
157static void coreaudio_logstatus (OSStatus status)
158{
d9cbb0f3 159 const char *str = "BUG";
1d14ffa9 160
3c8de96c 161 switch (status) {
1d14ffa9
FB
162 case kAudioHardwareNoError:
163 str = "kAudioHardwareNoError";
164 break;
165
166 case kAudioHardwareNotRunningError:
167 str = "kAudioHardwareNotRunningError";
168 break;
169
170 case kAudioHardwareUnspecifiedError:
171 str = "kAudioHardwareUnspecifiedError";
172 break;
173
174 case kAudioHardwareUnknownPropertyError:
175 str = "kAudioHardwareUnknownPropertyError";
176 break;
177
178 case kAudioHardwareBadPropertySizeError:
179 str = "kAudioHardwareBadPropertySizeError";
180 break;
181
182 case kAudioHardwareIllegalOperationError:
183 str = "kAudioHardwareIllegalOperationError";
184 break;
185
186 case kAudioHardwareBadDeviceError:
187 str = "kAudioHardwareBadDeviceError";
188 break;
189
190 case kAudioHardwareBadStreamError:
191 str = "kAudioHardwareBadStreamError";
192 break;
193
194 case kAudioHardwareUnsupportedOperationError:
195 str = "kAudioHardwareUnsupportedOperationError";
196 break;
197
198 case kAudioDeviceUnsupportedFormatError:
199 str = "kAudioDeviceUnsupportedFormatError";
200 break;
201
202 case kAudioDevicePermissionsError:
203 str = "kAudioDevicePermissionsError";
204 break;
205
206 default:
744d3644 207 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
1d14ffa9
FB
208 return;
209 }
210
211 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
212}
213
9edc6313 214static void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
1d14ffa9
FB
215 OSStatus status,
216 const char *fmt,
217 ...
218 )
219{
220 va_list ap;
221
222 va_start (ap, fmt);
223 AUD_log (AUDIO_CAP, fmt, ap);
224 va_end (ap);
225
226 coreaudio_logstatus (status);
227}
228
9edc6313 229static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
1d14ffa9
FB
230 OSStatus status,
231 const char *typ,
232 const char *fmt,
233 ...
234 )
235{
236 va_list ap;
237
c0fe3827 238 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
1d14ffa9
FB
239
240 va_start (ap, fmt);
241 AUD_vlog (AUDIO_CAP, fmt, ap);
242 va_end (ap);
243
244 coreaudio_logstatus (status);
245}
246
7d6948cd
AO
247#define coreaudio_playback_logerr(status, ...) \
248 coreaudio_logerr2(status, "playback", __VA_ARGS__)
249
eb1a35e4 250static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
1d14ffa9
FB
251{
252 int err;
253
eb1a35e4 254 err = pthread_mutex_lock (&core->buf_mutex);
1d14ffa9 255 if (err) {
c0fe3827 256 dolog ("Could not lock voice for %s\nReason: %s\n",
1d14ffa9
FB
257 fn_name, strerror (err));
258 return -1;
259 }
260 return 0;
261}
262
eb1a35e4 263static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
1d14ffa9
FB
264{
265 int err;
266
eb1a35e4 267 err = pthread_mutex_unlock (&core->buf_mutex);
1d14ffa9 268 if (err) {
c0fe3827 269 dolog ("Could not unlock voice for %s\nReason: %s\n",
1d14ffa9
FB
270 fn_name, strerror (err));
271 return -1;
272 }
273 return 0;
274}
275
2ceb8240
KZ
276#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
277 static ret_type glue(coreaudio_, name)args_decl \
278 { \
279 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
280 ret_type ret; \
281 \
eb1a35e4 282 if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
2ceb8240
KZ
283 return 0; \
284 } \
285 \
286 ret = glue(audio_generic_, name)args; \
287 \
eb1a35e4 288 coreaudio_buf_unlock(core, "coreaudio_" #name); \
2ceb8240 289 return ret; \
1d14ffa9 290 }
9833438e 291COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
2ceb8240
KZ
292COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
293 (hw, size))
fdc8c5f4 294COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
2ceb8240
KZ
295 (HWVoiceOut *hw, void *buf, size_t size),
296 (hw, buf, size))
297COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
298 (hw, buf, size))
299#undef COREAUDIO_WRAPPER_FUNC
1d14ffa9 300
eb1a35e4
AO
301/*
302 * callback to feed audiooutput buffer. called without iothread lock.
303 * allowed to lock "buf_mutex", but disallowed to have any other locks.
304 */
1d14ffa9
FB
305static OSStatus audioDeviceIOProc(
306 AudioDeviceID inDevice,
dcf10e40
ZH
307 const AudioTimeStamp *inNow,
308 const AudioBufferList *inInputData,
309 const AudioTimeStamp *inInputTime,
310 AudioBufferList *outOutputData,
311 const AudioTimeStamp *inOutputTime,
312 void *hwptr)
1d14ffa9 313{
2ceb8240
KZ
314 UInt32 frameCount, pending_frames;
315 void *out = outOutputData->mBuffers[0].mData;
1d14ffa9
FB
316 HWVoiceOut *hw = hwptr;
317 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
2ceb8240 318 size_t len;
1d14ffa9 319
eb1a35e4 320 if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
1d14ffa9
FB
321 inInputTime = 0;
322 return 0;
323 }
324
3ba6e3f6 325 if (inDevice != core->outputDeviceID) {
eb1a35e4 326 coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
3ba6e3f6
AO
327 return 0;
328 }
329
5e941d4b 330 frameCount = core->audioDevicePropertyBufferFrameSize;
2b9cce8c 331 pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
1d14ffa9
FB
332
333 /* if there are not enough samples, set signal and return */
2ceb8240 334 if (pending_frames < frameCount) {
1d14ffa9 335 inInputTime = 0;
eb1a35e4 336 coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
1d14ffa9
FB
337 return 0;
338 }
339
2b9cce8c 340 len = frameCount * hw->info.bytes_per_frame;
2ceb8240 341 while (len) {
18404ff1
VR
342 size_t write_len, start;
343
344 start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
345 assert(start < hw->size_emul);
1d14ffa9 346
2ceb8240
KZ
347 write_len = MIN(MIN(hw->pending_emul, len),
348 hw->size_emul - start);
1d14ffa9 349
2ceb8240
KZ
350 memcpy(out, hw->buf_emul + start, write_len);
351 hw->pending_emul -= write_len;
352 len -= write_len;
353 out += write_len;
354 }
1d14ffa9 355
eb1a35e4 356 coreaudio_buf_unlock (core, "audioDeviceIOProc");
1d14ffa9
FB
357 return 0;
358}
359
7d6948cd 360static OSStatus init_out_device(coreaudioVoiceOut *core)
1d14ffa9
FB
361{
362 OSStatus status;
5e941d4b 363 AudioValueRange frameRange;
1d14ffa9 364
986bdbc6
AO
365 AudioStreamBasicDescription streamBasicDescription = {
366 .mBitsPerChannel = core->hw.info.bits,
367 .mBytesPerFrame = core->hw.info.bytes_per_frame,
368 .mBytesPerPacket = core->hw.info.bytes_per_frame,
369 .mChannelsPerFrame = core->hw.info.nchannels,
370 .mFormatFlags = kLinearPCMFormatFlagIsFloat,
371 .mFormatID = kAudioFormatLinearPCM,
372 .mFramesPerPacket = 1,
373 .mSampleRate = core->hw.info.freq
374 };
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 434
1d14ffa9 435 /* set Samplerate */
95a860f6 436 status = coreaudio_set_streamformat(core->outputDeviceID,
986bdbc6 437 &streamBasicDescription);
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 set samplerate %lf\n",
986bdbc6 444 streamBasicDescription.mSampleRate);
1d14ffa9 445 core->outputDeviceID = kAudioDeviceUnknown;
7d6948cd 446 return status;
1d14ffa9
FB
447 }
448
eb1a35e4
AO
449 /*
450 * set Callback.
451 *
452 * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
453 * internal function named HALB_Mutex::Lock(), which locks a mutex in
454 * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
455 * AudioObjectGetPropertyData, which is called by coreaudio driver.
456 * Therefore, the specified callback must be designed to avoid a deadlock
457 * with the callers of AudioObjectGetPropertyData.
458 */
2f79a18f
PM
459 core->ioprocid = NULL;
460 status = AudioDeviceCreateIOProcID(core->outputDeviceID,
461 audioDeviceIOProc,
7d6948cd 462 &core->hw,
2f79a18f 463 &core->ioprocid);
3ba6e3f6
AO
464 if (status == kAudioHardwareBadDeviceError) {
465 return 0;
466 }
2f79a18f 467 if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
7d6948cd 468 coreaudio_playback_logerr (status, "Could not set IOProc\n");
1d14ffa9 469 core->outputDeviceID = kAudioDeviceUnknown;
7d6948cd 470 return status;
1d14ffa9
FB
471 }
472
1d14ffa9
FB
473 return 0;
474}
475
7d6948cd 476static void fini_out_device(coreaudioVoiceOut *core)
1d14ffa9
FB
477{
478 OSStatus status;
3ba6e3f6 479 UInt32 isrunning;
1d14ffa9 480
ceb1165e 481 /* stop playback */
3ba6e3f6
AO
482 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
483 if (status != kAudioHardwareBadObjectError) {
5e941d4b 484 if (status != kAudioHardwareNoError) {
3ba6e3f6
AO
485 coreaudio_logerr(status,
486 "Could not determine whether Device is playing\n");
487 }
488
489 if (isrunning) {
490 status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
491 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
492 coreaudio_logerr(status, "Could not stop playback\n");
493 }
5e941d4b 494 }
1d14ffa9 495 }
ceb1165e
VR
496
497 /* remove callback */
498 status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
499 core->ioprocid);
3ba6e3f6 500 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
ceb1165e
VR
501 coreaudio_logerr(status, "Could not remove IOProc\n");
502 }
1d14ffa9 503 core->outputDeviceID = kAudioDeviceUnknown;
7d6948cd
AO
504}
505
3ba6e3f6
AO
506static void update_device_playback_state(coreaudioVoiceOut *core)
507{
508 OSStatus status;
509 UInt32 isrunning;
510
511 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
512 if (status != kAudioHardwareNoError) {
513 if (status != kAudioHardwareBadObjectError) {
514 coreaudio_logerr(status,
515 "Could not determine whether Device is playing\n");
516 }
517
518 return;
519 }
520
521 if (core->enabled) {
522 /* start playback */
523 if (!isrunning) {
524 status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
525 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
526 coreaudio_logerr (status, "Could not resume playback\n");
527 }
528 }
529 } else {
530 /* stop playback */
531 if (isrunning) {
532 status = AudioDeviceStop(core->outputDeviceID,
533 core->ioprocid);
534 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
535 coreaudio_logerr(status, "Could not pause playback\n");
536 }
537 }
538 }
539}
540
eb1a35e4 541/* called without iothread lock. */
3ba6e3f6
AO
542static OSStatus handle_voice_change(
543 AudioObjectID in_object_id,
544 UInt32 in_number_addresses,
545 const AudioObjectPropertyAddress *in_addresses,
546 void *in_client_data)
547{
3ba6e3f6
AO
548 coreaudioVoiceOut *core = in_client_data;
549
eb1a35e4 550 qemu_mutex_lock_iothread();
3ba6e3f6
AO
551
552 if (core->outputDeviceID) {
553 fini_out_device(core);
554 }
555
44ccb2db 556 if (!init_out_device(core)) {
3ba6e3f6
AO
557 update_device_playback_state(core);
558 }
559
eb1a35e4 560 qemu_mutex_unlock_iothread();
44ccb2db 561 return 0;
3ba6e3f6
AO
562}
563
7d6948cd
AO
564static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
565 void *drv_opaque)
566{
567 OSStatus status;
568 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
569 int err;
570 Audiodev *dev = drv_opaque;
571 AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
572 struct audsettings obt_as;
573
574 /* create mutex */
eb1a35e4 575 err = pthread_mutex_init(&core->buf_mutex, NULL);
7d6948cd
AO
576 if (err) {
577 dolog("Could not create mutex\nReason: %s\n", strerror (err));
eb1a35e4 578 return -1;
7d6948cd
AO
579 }
580
581 obt_as = *as;
582 as = &obt_as;
583 as->fmt = AUDIO_FORMAT_F32;
584 audio_pcm_init_info (&hw->info, as);
585
586 core->frameSizeSetting = audio_buffer_frames(
587 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
588
589 core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
7d6948cd 590
3ba6e3f6
AO
591 status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
592 &voice_addr, handle_voice_change,
593 core);
594 if (status != kAudioHardwareNoError) {
595 coreaudio_playback_logerr (status,
596 "Could not listen to voice property change\n");
eb1a35e4 597 return -1;
3ba6e3f6
AO
598 }
599
7d6948cd 600 if (init_out_device(core)) {
eb1a35e4
AO
601 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
602 &voice_addr,
603 handle_voice_change,
604 core);
605 if (status != kAudioHardwareNoError) {
606 coreaudio_playback_logerr(status,
607 "Could not remove voice property change listener\n");
608 }
bd7819de
AO
609
610 return -1;
7d6948cd
AO
611 }
612
613 return 0;
614}
615
616static void coreaudio_fini_out (HWVoiceOut *hw)
617{
618 OSStatus status;
619 int err;
620 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
621
3ba6e3f6
AO
622 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
623 &voice_addr,
624 handle_voice_change,
625 core);
626 if (status != kAudioHardwareNoError) {
627 coreaudio_logerr(status, "Could not remove voice property change listener\n");
628 }
629
7d6948cd 630 fini_out_device(core);
1d14ffa9
FB
631
632 /* destroy mutex */
eb1a35e4 633 err = pthread_mutex_destroy(&core->buf_mutex);
1d14ffa9 634 if (err) {
c0fe3827 635 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
1d14ffa9
FB
636 }
637}
638
571a8c52 639static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
1d14ffa9 640{
1d14ffa9
FB
641 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
642
3ba6e3f6
AO
643 core->enabled = enable;
644 update_device_playback_state(core);
1d14ffa9
FB
645}
646
71830221 647static void *coreaudio_audio_init(Audiodev *dev)
1d14ffa9 648{
17c56dc1 649 return dev;
1d14ffa9
FB
650}
651
652static void coreaudio_audio_fini (void *opaque)
653{
1d14ffa9
FB
654}
655
35f4b58c 656static struct audio_pcm_ops coreaudio_pcm_ops = {
1dd3e4d1
JQ
657 .init_out = coreaudio_init_out,
658 .fini_out = coreaudio_fini_out,
fdc8c5f4 659 /* wrapper for audio_generic_write */
2ceb8240 660 .write = coreaudio_write,
9833438e
VR
661 /* wrapper for audio_generic_buffer_get_free */
662 .buffer_get_free = coreaudio_buffer_get_free,
fdc8c5f4 663 /* wrapper for audio_generic_get_buffer_out */
2ceb8240 664 .get_buffer_out = coreaudio_get_buffer_out,
fdc8c5f4
VR
665 /* wrapper for audio_generic_put_buffer_out */
666 .put_buffer_out = coreaudio_put_buffer_out,
571a8c52 667 .enable_out = coreaudio_enable_out
1d14ffa9
FB
668};
669
d3893a39 670static struct audio_driver coreaudio_audio_driver = {
bee37f32
JQ
671 .name = "coreaudio",
672 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
bee37f32
JQ
673 .init = coreaudio_audio_init,
674 .fini = coreaudio_audio_fini,
675 .pcm_ops = &coreaudio_pcm_ops,
676 .can_be_default = 1,
677 .max_voices_out = 1,
678 .max_voices_in = 0,
679 .voice_size_out = sizeof (coreaudioVoiceOut),
680 .voice_size_in = 0
1d14ffa9 681};
d3893a39
GH
682
683static void register_audio_coreaudio(void)
684{
685 audio_driver_register(&coreaudio_audio_driver);
686}
687type_init(register_audio_coreaudio);