2 * QEMU OS X CoreAudio audio driver
4 * Copyright (c) 2005 Mike Kronenberg
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
25 #include "qemu/osdep.h"
26 #include <CoreAudio/CoreAudio.h>
27 #include <pthread.h> /* pthread_X */
29 #include "qemu/module.h"
32 #define AUDIO_CAP "coreaudio"
33 #include "audio_int.h"
35 typedef struct coreaudioVoiceOut
{
37 pthread_mutex_t mutex
;
38 AudioDeviceID outputDeviceID
;
41 UInt32 audioDevicePropertyBufferFrameSize
;
42 AudioDeviceIOProcID ioprocid
;
46 static const AudioObjectPropertyAddress voice_addr
= {
47 kAudioHardwarePropertyDefaultOutputDevice
,
48 kAudioObjectPropertyScopeGlobal
,
49 kAudioObjectPropertyElementMaster
52 static OSStatus
coreaudio_get_voice(AudioDeviceID
*id
)
54 UInt32 size
= sizeof(*id
);
56 return AudioObjectGetPropertyData(kAudioObjectSystemObject
,
64 static OSStatus
coreaudio_get_framesizerange(AudioDeviceID id
,
65 AudioValueRange
*framerange
)
67 UInt32 size
= sizeof(*framerange
);
68 AudioObjectPropertyAddress addr
= {
69 kAudioDevicePropertyBufferFrameSizeRange
,
70 kAudioDevicePropertyScopeOutput
,
71 kAudioObjectPropertyElementMaster
74 return AudioObjectGetPropertyData(id
,
82 static OSStatus
coreaudio_get_framesize(AudioDeviceID id
, UInt32
*framesize
)
84 UInt32 size
= sizeof(*framesize
);
85 AudioObjectPropertyAddress addr
= {
86 kAudioDevicePropertyBufferFrameSize
,
87 kAudioDevicePropertyScopeOutput
,
88 kAudioObjectPropertyElementMaster
91 return AudioObjectGetPropertyData(id
,
99 static OSStatus
coreaudio_set_framesize(AudioDeviceID id
, UInt32
*framesize
)
101 UInt32 size
= sizeof(*framesize
);
102 AudioObjectPropertyAddress addr
= {
103 kAudioDevicePropertyBufferFrameSize
,
104 kAudioDevicePropertyScopeOutput
,
105 kAudioObjectPropertyElementMaster
108 return AudioObjectSetPropertyData(id
,
116 static OSStatus
coreaudio_set_streamformat(AudioDeviceID id
,
117 AudioStreamBasicDescription
*d
)
119 UInt32 size
= sizeof(*d
);
120 AudioObjectPropertyAddress addr
= {
121 kAudioDevicePropertyStreamFormat
,
122 kAudioDevicePropertyScopeOutput
,
123 kAudioObjectPropertyElementMaster
126 return AudioObjectSetPropertyData(id
,
134 static OSStatus
coreaudio_get_isrunning(AudioDeviceID id
, UInt32
*result
)
136 UInt32 size
= sizeof(*result
);
137 AudioObjectPropertyAddress addr
= {
138 kAudioDevicePropertyDeviceIsRunning
,
139 kAudioDevicePropertyScopeOutput
,
140 kAudioObjectPropertyElementMaster
143 return AudioObjectGetPropertyData(id
,
151 static void coreaudio_logstatus (OSStatus status
)
153 const char *str
= "BUG";
156 case kAudioHardwareNoError
:
157 str
= "kAudioHardwareNoError";
160 case kAudioHardwareNotRunningError
:
161 str
= "kAudioHardwareNotRunningError";
164 case kAudioHardwareUnspecifiedError
:
165 str
= "kAudioHardwareUnspecifiedError";
168 case kAudioHardwareUnknownPropertyError
:
169 str
= "kAudioHardwareUnknownPropertyError";
172 case kAudioHardwareBadPropertySizeError
:
173 str
= "kAudioHardwareBadPropertySizeError";
176 case kAudioHardwareIllegalOperationError
:
177 str
= "kAudioHardwareIllegalOperationError";
180 case kAudioHardwareBadDeviceError
:
181 str
= "kAudioHardwareBadDeviceError";
184 case kAudioHardwareBadStreamError
:
185 str
= "kAudioHardwareBadStreamError";
188 case kAudioHardwareUnsupportedOperationError
:
189 str
= "kAudioHardwareUnsupportedOperationError";
192 case kAudioDeviceUnsupportedFormatError
:
193 str
= "kAudioDeviceUnsupportedFormatError";
196 case kAudioDevicePermissionsError
:
197 str
= "kAudioDevicePermissionsError";
201 AUD_log (AUDIO_CAP
, "Reason: status code %" PRId32
"\n", (int32_t)status
);
205 AUD_log (AUDIO_CAP
, "Reason: %s\n", str
);
208 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
217 AUD_log (AUDIO_CAP
, fmt
, ap
);
220 coreaudio_logstatus (status
);
223 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
232 AUD_log (AUDIO_CAP
, "Could not initialize %s\n", typ
);
235 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
238 coreaudio_logstatus (status
);
241 #define coreaudio_playback_logerr(status, ...) \
242 coreaudio_logerr2(status, "playback", __VA_ARGS__)
244 static int coreaudio_lock (coreaudioVoiceOut
*core
, const char *fn_name
)
248 err
= pthread_mutex_lock (&core
->mutex
);
250 dolog ("Could not lock voice for %s\nReason: %s\n",
251 fn_name
, strerror (err
));
257 static int coreaudio_unlock (coreaudioVoiceOut
*core
, const char *fn_name
)
261 err
= pthread_mutex_unlock (&core
->mutex
);
263 dolog ("Could not unlock voice for %s\nReason: %s\n",
264 fn_name
, strerror (err
));
270 #define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
271 static ret_type glue(coreaudio_, name)args_decl \
273 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
276 if (coreaudio_lock(core, "coreaudio_" #name)) { \
280 ret = glue(audio_generic_, name)args; \
282 coreaudio_unlock(core, "coreaudio_" #name); \
285 COREAUDIO_WRAPPER_FUNC(get_buffer_out
, void *, (HWVoiceOut
*hw
, size_t *size
),
287 COREAUDIO_WRAPPER_FUNC(put_buffer_out
, size_t,
288 (HWVoiceOut
*hw
, void *buf
, size_t size
),
290 COREAUDIO_WRAPPER_FUNC(write
, size_t, (HWVoiceOut
*hw
, void *buf
, size_t size
),
292 #undef COREAUDIO_WRAPPER_FUNC
294 /* callback to feed audiooutput buffer */
295 static OSStatus
audioDeviceIOProc(
296 AudioDeviceID inDevice
,
297 const AudioTimeStamp
*inNow
,
298 const AudioBufferList
*inInputData
,
299 const AudioTimeStamp
*inInputTime
,
300 AudioBufferList
*outOutputData
,
301 const AudioTimeStamp
*inOutputTime
,
304 UInt32 frameCount
, pending_frames
;
305 void *out
= outOutputData
->mBuffers
[0].mData
;
306 HWVoiceOut
*hw
= hwptr
;
307 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hwptr
;
310 if (coreaudio_lock (core
, "audioDeviceIOProc")) {
315 if (inDevice
!= core
->outputDeviceID
) {
316 coreaudio_unlock (core
, "audioDeviceIOProc(old device)");
320 frameCount
= core
->audioDevicePropertyBufferFrameSize
;
321 pending_frames
= hw
->pending_emul
/ hw
->info
.bytes_per_frame
;
323 /* if there are not enough samples, set signal and return */
324 if (pending_frames
< frameCount
) {
326 coreaudio_unlock (core
, "audioDeviceIOProc(empty)");
330 len
= frameCount
* hw
->info
.bytes_per_frame
;
333 ssize_t start
= ((ssize_t
) hw
->pos_emul
) - hw
->pending_emul
;
335 start
+= hw
->size_emul
;
337 assert(start
>= 0 && start
< hw
->size_emul
);
339 write_len
= MIN(MIN(hw
->pending_emul
, len
),
340 hw
->size_emul
- start
);
342 memcpy(out
, hw
->buf_emul
+ start
, write_len
);
343 hw
->pending_emul
-= write_len
;
348 coreaudio_unlock (core
, "audioDeviceIOProc");
352 static OSStatus
init_out_device(coreaudioVoiceOut
*core
)
355 AudioValueRange frameRange
;
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
368 status
= coreaudio_get_voice(&core
->outputDeviceID
);
369 if (status
!= kAudioHardwareNoError
) {
370 coreaudio_playback_logerr (status
,
371 "Could not get default output Device\n");
374 if (core
->outputDeviceID
== kAudioDeviceUnknown
) {
375 dolog ("Could not initialize playback - Unknown Audiodevice\n");
379 /* get minimum and maximum buffer frame sizes */
380 status
= coreaudio_get_framesizerange(core
->outputDeviceID
,
382 if (status
== kAudioHardwareBadObjectError
) {
385 if (status
!= kAudioHardwareNoError
) {
386 coreaudio_playback_logerr (status
,
387 "Could not get device buffer frame range\n");
391 if (frameRange
.mMinimum
> core
->frameSizeSetting
) {
392 core
->audioDevicePropertyBufferFrameSize
= (UInt32
) frameRange
.mMinimum
;
393 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange
.mMinimum
);
394 } else if (frameRange
.mMaximum
< core
->frameSizeSetting
) {
395 core
->audioDevicePropertyBufferFrameSize
= (UInt32
) frameRange
.mMaximum
;
396 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange
.mMaximum
);
398 core
->audioDevicePropertyBufferFrameSize
= core
->frameSizeSetting
;
401 /* set Buffer Frame Size */
402 status
= coreaudio_set_framesize(core
->outputDeviceID
,
403 &core
->audioDevicePropertyBufferFrameSize
);
404 if (status
== kAudioHardwareBadObjectError
) {
407 if (status
!= kAudioHardwareNoError
) {
408 coreaudio_playback_logerr (status
,
409 "Could not set device buffer frame size %" PRIu32
"\n",
410 (uint32_t)core
->audioDevicePropertyBufferFrameSize
);
414 /* get Buffer Frame Size */
415 status
= coreaudio_get_framesize(core
->outputDeviceID
,
416 &core
->audioDevicePropertyBufferFrameSize
);
417 if (status
== kAudioHardwareBadObjectError
) {
420 if (status
!= kAudioHardwareNoError
) {
421 coreaudio_playback_logerr (status
,
422 "Could not get device buffer frame size\n");
425 core
->hw
.samples
= core
->bufferCount
* core
->audioDevicePropertyBufferFrameSize
;
428 status
= coreaudio_set_streamformat(core
->outputDeviceID
,
429 &streamBasicDescription
);
430 if (status
== kAudioHardwareBadObjectError
) {
433 if (status
!= kAudioHardwareNoError
) {
434 coreaudio_playback_logerr (status
,
435 "Could not set samplerate %lf\n",
436 streamBasicDescription
.mSampleRate
);
437 core
->outputDeviceID
= kAudioDeviceUnknown
;
442 core
->ioprocid
= NULL
;
443 status
= AudioDeviceCreateIOProcID(core
->outputDeviceID
,
447 if (status
== kAudioHardwareBadDeviceError
) {
450 if (status
!= kAudioHardwareNoError
|| core
->ioprocid
== NULL
) {
451 coreaudio_playback_logerr (status
, "Could not set IOProc\n");
452 core
->outputDeviceID
= kAudioDeviceUnknown
;
459 static void fini_out_device(coreaudioVoiceOut
*core
)
465 status
= coreaudio_get_isrunning(core
->outputDeviceID
, &isrunning
);
466 if (status
!= kAudioHardwareBadObjectError
) {
467 if (status
!= kAudioHardwareNoError
) {
468 coreaudio_logerr(status
,
469 "Could not determine whether Device is playing\n");
473 status
= AudioDeviceStop(core
->outputDeviceID
, core
->ioprocid
);
474 if (status
!= kAudioHardwareBadDeviceError
&& status
!= kAudioHardwareNoError
) {
475 coreaudio_logerr(status
, "Could not stop playback\n");
480 /* remove callback */
481 status
= AudioDeviceDestroyIOProcID(core
->outputDeviceID
,
483 if (status
!= kAudioHardwareBadDeviceError
&& status
!= kAudioHardwareNoError
) {
484 coreaudio_logerr(status
, "Could not remove IOProc\n");
486 core
->outputDeviceID
= kAudioDeviceUnknown
;
489 static void update_device_playback_state(coreaudioVoiceOut
*core
)
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");
507 status
= AudioDeviceStart(core
->outputDeviceID
, core
->ioprocid
);
508 if (status
!= kAudioHardwareBadDeviceError
&& status
!= kAudioHardwareNoError
) {
509 coreaudio_logerr (status
, "Could not resume playback\n");
515 status
= AudioDeviceStop(core
->outputDeviceID
,
517 if (status
!= kAudioHardwareBadDeviceError
&& status
!= kAudioHardwareNoError
) {
518 coreaudio_logerr(status
, "Could not pause playback\n");
524 static OSStatus
handle_voice_change(
525 AudioObjectID in_object_id
,
526 UInt32 in_number_addresses
,
527 const AudioObjectPropertyAddress
*in_addresses
,
528 void *in_client_data
)
531 coreaudioVoiceOut
*core
= in_client_data
;
533 if (coreaudio_lock(core
, __func__
)) {
537 if (core
->outputDeviceID
) {
538 fini_out_device(core
);
541 status
= init_out_device(core
);
543 update_device_playback_state(core
);
546 coreaudio_unlock (core
, __func__
);
550 static int coreaudio_init_out(HWVoiceOut
*hw
, struct audsettings
*as
,
554 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hw
;
556 Audiodev
*dev
= drv_opaque
;
557 AudiodevCoreaudioPerDirectionOptions
*cpdo
= dev
->u
.coreaudio
.out
;
558 struct audsettings obt_as
;
561 err
= pthread_mutex_init(&core
->mutex
, NULL
);
563 dolog("Could not create mutex\nReason: %s\n", strerror (err
));
567 if (coreaudio_lock(core
, __func__
)) {
573 as
->fmt
= AUDIO_FORMAT_F32
;
574 audio_pcm_init_info (&hw
->info
, as
);
576 core
->frameSizeSetting
= audio_buffer_frames(
577 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo
), as
, 11610);
579 core
->bufferCount
= cpdo
->has_buffer_count
? cpdo
->buffer_count
: 4;
581 status
= AudioObjectAddPropertyListener(kAudioObjectSystemObject
,
582 &voice_addr
, handle_voice_change
,
584 if (status
!= kAudioHardwareNoError
) {
585 coreaudio_playback_logerr (status
,
586 "Could not listen to voice property change\n");
590 if (init_out_device(core
)) {
594 coreaudio_unlock(core
, __func__
);
598 status
= AudioObjectRemovePropertyListener(kAudioObjectSystemObject
,
602 if (status
!= kAudioHardwareNoError
) {
603 coreaudio_playback_logerr(status
,
604 "Could not remove voice property change listener\n");
608 coreaudio_unlock(core
, __func__
);
611 err
= pthread_mutex_destroy(&core
->mutex
);
613 dolog("Could not destroy mutex\nReason: %s\n", strerror (err
));
620 static void coreaudio_fini_out (HWVoiceOut
*hw
)
624 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hw
;
626 if (coreaudio_lock(core
, __func__
)) {
630 status
= AudioObjectRemovePropertyListener(kAudioObjectSystemObject
,
634 if (status
!= kAudioHardwareNoError
) {
635 coreaudio_logerr(status
, "Could not remove voice property change listener\n");
638 fini_out_device(core
);
640 coreaudio_unlock(core
, __func__
);
643 err
= pthread_mutex_destroy(&core
->mutex
);
645 dolog("Could not destroy mutex\nReason: %s\n", strerror (err
));
649 static void coreaudio_enable_out(HWVoiceOut
*hw
, bool enable
)
651 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hw
;
653 if (coreaudio_lock(core
, __func__
)) {
657 core
->enabled
= enable
;
658 update_device_playback_state(core
);
660 coreaudio_unlock(core
, __func__
);
663 static void *coreaudio_audio_init(Audiodev
*dev
)
668 static void coreaudio_audio_fini (void *opaque
)
672 static struct audio_pcm_ops coreaudio_pcm_ops
= {
673 .init_out
= coreaudio_init_out
,
674 .fini_out
= coreaudio_fini_out
,
675 /* wrapper for audio_generic_write */
676 .write
= coreaudio_write
,
677 /* wrapper for audio_generic_get_buffer_out */
678 .get_buffer_out
= coreaudio_get_buffer_out
,
679 /* wrapper for audio_generic_put_buffer_out */
680 .put_buffer_out
= coreaudio_put_buffer_out
,
681 .enable_out
= coreaudio_enable_out
684 static struct audio_driver coreaudio_audio_driver
= {
686 .descr
= "CoreAudio http://developer.apple.com/audio/coreaudio.html",
687 .init
= coreaudio_audio_init
,
688 .fini
= coreaudio_audio_fini
,
689 .pcm_ops
= &coreaudio_pcm_ops
,
693 .voice_size_out
= sizeof (coreaudioVoiceOut
),
697 static void register_audio_coreaudio(void)
699 audio_driver_register(&coreaudio_audio_driver
);
701 type_init(register_audio_coreaudio
);