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 <CoreAudio/CoreAudio.h>
26 #include <string.h> /* strerror */
27 #include <pthread.h> /* pthread_X */
29 #include "qemu-common.h"
32 #define AUDIO_CAP "coreaudio"
33 #include "audio_int.h"
42 typedef struct coreaudioVoiceOut
{
44 pthread_mutex_t mutex
;
45 AudioDeviceID outputDeviceID
;
46 UInt32 audioDevicePropertyBufferFrameSize
;
47 AudioStreamBasicDescription outputStreamBasicDescription
;
53 static void coreaudio_logstatus (OSStatus status
)
55 const char *str
= "BUG";
58 case kAudioHardwareNoError
:
59 str
= "kAudioHardwareNoError";
62 case kAudioHardwareNotRunningError
:
63 str
= "kAudioHardwareNotRunningError";
66 case kAudioHardwareUnspecifiedError
:
67 str
= "kAudioHardwareUnspecifiedError";
70 case kAudioHardwareUnknownPropertyError
:
71 str
= "kAudioHardwareUnknownPropertyError";
74 case kAudioHardwareBadPropertySizeError
:
75 str
= "kAudioHardwareBadPropertySizeError";
78 case kAudioHardwareIllegalOperationError
:
79 str
= "kAudioHardwareIllegalOperationError";
82 case kAudioHardwareBadDeviceError
:
83 str
= "kAudioHardwareBadDeviceError";
86 case kAudioHardwareBadStreamError
:
87 str
= "kAudioHardwareBadStreamError";
90 case kAudioHardwareUnsupportedOperationError
:
91 str
= "kAudioHardwareUnsupportedOperationError";
94 case kAudioDeviceUnsupportedFormatError
:
95 str
= "kAudioDeviceUnsupportedFormatError";
98 case kAudioDevicePermissionsError
:
99 str
= "kAudioDevicePermissionsError";
103 AUD_log (AUDIO_CAP
, "Reason: status code %" PRId32
"\n", (int32_t)status
);
107 AUD_log (AUDIO_CAP
, "Reason: %s\n", str
);
110 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
119 AUD_log (AUDIO_CAP
, fmt
, ap
);
122 coreaudio_logstatus (status
);
125 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
134 AUD_log (AUDIO_CAP
, "Could not initialize %s\n", typ
);
137 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
140 coreaudio_logstatus (status
);
143 static inline UInt32
isPlaying (AudioDeviceID outputDeviceID
)
147 UInt32 propertySize
= sizeof(outputDeviceID
);
148 status
= AudioDeviceGetProperty(
149 outputDeviceID
, 0, 0,
150 kAudioDevicePropertyDeviceIsRunning
, &propertySize
, &result
);
151 if (status
!= kAudioHardwareNoError
) {
152 coreaudio_logerr(status
,
153 "Could not determine whether Device is playing\n");
158 static void coreaudio_atexit (void)
163 static int coreaudio_lock (coreaudioVoiceOut
*core
, const char *fn_name
)
167 err
= pthread_mutex_lock (&core
->mutex
);
169 dolog ("Could not lock voice for %s\nReason: %s\n",
170 fn_name
, strerror (err
));
176 static int coreaudio_unlock (coreaudioVoiceOut
*core
, const char *fn_name
)
180 err
= pthread_mutex_unlock (&core
->mutex
);
182 dolog ("Could not unlock voice for %s\nReason: %s\n",
183 fn_name
, strerror (err
));
189 static int coreaudio_run_out (HWVoiceOut
*hw
, int live
)
192 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hw
;
194 if (coreaudio_lock (core
, "coreaudio_run_out")) {
198 if (core
->decr
> live
) {
199 ldebug ("core->decr %d live %d core->live %d\n",
205 decr
= audio_MIN (core
->decr
, live
);
208 core
->live
= live
- decr
;
209 hw
->rpos
= core
->rpos
;
211 coreaudio_unlock (core
, "coreaudio_run_out");
215 /* callback to feed audiooutput buffer */
216 static OSStatus
audioDeviceIOProc(
217 AudioDeviceID inDevice
,
218 const AudioTimeStamp
* inNow
,
219 const AudioBufferList
* inInputData
,
220 const AudioTimeStamp
* inInputTime
,
221 AudioBufferList
* outOutputData
,
222 const AudioTimeStamp
* inOutputTime
,
225 UInt32 frame
, frameCount
;
226 float *out
= outOutputData
->mBuffers
[0].mData
;
227 HWVoiceOut
*hw
= hwptr
;
228 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hwptr
;
230 struct st_sample
*src
;
233 const float scale
= 1.f
/ UINT_MAX
;
235 const float scale
= UINT_MAX
;
239 if (coreaudio_lock (core
, "audioDeviceIOProc")) {
244 frameCount
= core
->audioDevicePropertyBufferFrameSize
;
247 /* if there are not enough samples, set signal and return */
248 if (live
< frameCount
) {
250 coreaudio_unlock (core
, "audioDeviceIOProc(empty)");
255 src
= hw
->mix_buf
+ rpos
;
258 for (frame
= 0; frame
< frameCount
; frame
++) {
260 *out
++ = src
[frame
].l
; /* left channel */
261 *out
++ = src
[frame
].r
; /* right channel */
264 *out
++ = src
[frame
].l
* scale
; /* left channel */
265 *out
++ = src
[frame
].r
* scale
; /* right channel */
267 *out
++ = src
[frame
].l
/ scale
; /* left channel */
268 *out
++ = src
[frame
].r
/ scale
; /* right channel */
273 rpos
= (rpos
+ frameCount
) % hw
->samples
;
274 core
->decr
+= frameCount
;
277 coreaudio_unlock (core
, "audioDeviceIOProc");
281 static int coreaudio_write (SWVoiceOut
*sw
, void *buf
, int len
)
283 return audio_pcm_sw_write (sw
, buf
, len
);
286 static int coreaudio_init_out(HWVoiceOut
*hw
, struct audsettings
*as
,
290 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hw
;
293 const char *typ
= "playback";
294 AudioValueRange frameRange
;
295 CoreaudioConf
*conf
= drv_opaque
;
298 err
= pthread_mutex_init(&core
->mutex
, NULL
);
300 dolog("Could not create mutex\nReason: %s\n", strerror (err
));
304 audio_pcm_init_info (&hw
->info
, as
);
306 /* open default output device */
307 propertySize
= sizeof(core
->outputDeviceID
);
308 status
= AudioHardwareGetProperty(
309 kAudioHardwarePropertyDefaultOutputDevice
,
311 &core
->outputDeviceID
);
312 if (status
!= kAudioHardwareNoError
) {
313 coreaudio_logerr2 (status
, typ
,
314 "Could not get default output Device\n");
317 if (core
->outputDeviceID
== kAudioDeviceUnknown
) {
318 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ
);
322 /* get minimum and maximum buffer frame sizes */
323 propertySize
= sizeof(frameRange
);
324 status
= AudioDeviceGetProperty(
325 core
->outputDeviceID
,
328 kAudioDevicePropertyBufferFrameSizeRange
,
331 if (status
!= kAudioHardwareNoError
) {
332 coreaudio_logerr2 (status
, typ
,
333 "Could not get device buffer frame range\n");
337 if (frameRange
.mMinimum
> conf
->buffer_frames
) {
338 core
->audioDevicePropertyBufferFrameSize
= (UInt32
) frameRange
.mMinimum
;
339 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange
.mMinimum
);
341 else if (frameRange
.mMaximum
< conf
->buffer_frames
) {
342 core
->audioDevicePropertyBufferFrameSize
= (UInt32
) frameRange
.mMaximum
;
343 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange
.mMaximum
);
346 core
->audioDevicePropertyBufferFrameSize
= conf
->buffer_frames
;
349 /* set Buffer Frame Size */
350 propertySize
= sizeof(core
->audioDevicePropertyBufferFrameSize
);
351 status
= AudioDeviceSetProperty(
352 core
->outputDeviceID
,
356 kAudioDevicePropertyBufferFrameSize
,
358 &core
->audioDevicePropertyBufferFrameSize
);
359 if (status
!= kAudioHardwareNoError
) {
360 coreaudio_logerr2 (status
, typ
,
361 "Could not set device buffer frame size %" PRIu32
"\n",
362 (uint32_t)core
->audioDevicePropertyBufferFrameSize
);
366 /* get Buffer Frame Size */
367 propertySize
= sizeof(core
->audioDevicePropertyBufferFrameSize
);
368 status
= AudioDeviceGetProperty(
369 core
->outputDeviceID
,
372 kAudioDevicePropertyBufferFrameSize
,
374 &core
->audioDevicePropertyBufferFrameSize
);
375 if (status
!= kAudioHardwareNoError
) {
376 coreaudio_logerr2 (status
, typ
,
377 "Could not get device buffer frame size\n");
380 hw
->samples
= conf
->nbuffers
* core
->audioDevicePropertyBufferFrameSize
;
382 /* get StreamFormat */
383 propertySize
= sizeof(core
->outputStreamBasicDescription
);
384 status
= AudioDeviceGetProperty(
385 core
->outputDeviceID
,
388 kAudioDevicePropertyStreamFormat
,
390 &core
->outputStreamBasicDescription
);
391 if (status
!= kAudioHardwareNoError
) {
392 coreaudio_logerr2 (status
, typ
,
393 "Could not get Device Stream properties\n");
394 core
->outputDeviceID
= kAudioDeviceUnknown
;
399 core
->outputStreamBasicDescription
.mSampleRate
= (Float64
) as
->freq
;
400 propertySize
= sizeof(core
->outputStreamBasicDescription
);
401 status
= AudioDeviceSetProperty(
402 core
->outputDeviceID
,
406 kAudioDevicePropertyStreamFormat
,
408 &core
->outputStreamBasicDescription
);
409 if (status
!= kAudioHardwareNoError
) {
410 coreaudio_logerr2 (status
, typ
, "Could not set samplerate %d\n",
412 core
->outputDeviceID
= kAudioDeviceUnknown
;
417 status
= AudioDeviceAddIOProc(core
->outputDeviceID
, audioDeviceIOProc
, hw
);
418 if (status
!= kAudioHardwareNoError
) {
419 coreaudio_logerr2 (status
, typ
, "Could not set IOProc\n");
420 core
->outputDeviceID
= kAudioDeviceUnknown
;
425 if (!isPlaying(core
->outputDeviceID
)) {
426 status
= AudioDeviceStart(core
->outputDeviceID
, audioDeviceIOProc
);
427 if (status
!= kAudioHardwareNoError
) {
428 coreaudio_logerr2 (status
, typ
, "Could not start playback\n");
429 AudioDeviceRemoveIOProc(core
->outputDeviceID
, audioDeviceIOProc
);
430 core
->outputDeviceID
= kAudioDeviceUnknown
;
438 static void coreaudio_fini_out (HWVoiceOut
*hw
)
442 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hw
;
446 if (isPlaying(core
->outputDeviceID
)) {
447 status
= AudioDeviceStop(core
->outputDeviceID
, audioDeviceIOProc
);
448 if (status
!= kAudioHardwareNoError
) {
449 coreaudio_logerr (status
, "Could not stop playback\n");
453 /* remove callback */
454 status
= AudioDeviceRemoveIOProc(core
->outputDeviceID
,
456 if (status
!= kAudioHardwareNoError
) {
457 coreaudio_logerr (status
, "Could not remove IOProc\n");
460 core
->outputDeviceID
= kAudioDeviceUnknown
;
463 err
= pthread_mutex_destroy(&core
->mutex
);
465 dolog("Could not destroy mutex\nReason: %s\n", strerror (err
));
469 static int coreaudio_ctl_out (HWVoiceOut
*hw
, int cmd
, ...)
472 coreaudioVoiceOut
*core
= (coreaudioVoiceOut
*) hw
;
477 if (!isPlaying(core
->outputDeviceID
)) {
478 status
= AudioDeviceStart(core
->outputDeviceID
, audioDeviceIOProc
);
479 if (status
!= kAudioHardwareNoError
) {
480 coreaudio_logerr (status
, "Could not resume playback\n");
488 if (isPlaying(core
->outputDeviceID
)) {
489 status
= AudioDeviceStop(core
->outputDeviceID
, audioDeviceIOProc
);
490 if (status
!= kAudioHardwareNoError
) {
491 coreaudio_logerr (status
, "Could not pause playback\n");
500 static CoreaudioConf glob_conf
= {
501 .buffer_frames
= 512,
505 static void *coreaudio_audio_init (void)
507 CoreaudioConf
*conf
= g_malloc(sizeof(CoreaudioConf
));
510 atexit(coreaudio_atexit
);
514 static void coreaudio_audio_fini (void *opaque
)
519 static struct audio_option coreaudio_options
[] = {
521 .name
= "BUFFER_SIZE",
523 .valp
= &glob_conf
.buffer_frames
,
524 .descr
= "Size of the buffer in frames"
527 .name
= "BUFFER_COUNT",
529 .valp
= &glob_conf
.nbuffers
,
530 .descr
= "Number of buffers"
532 { /* End of list */ }
535 static struct audio_pcm_ops coreaudio_pcm_ops
= {
536 .init_out
= coreaudio_init_out
,
537 .fini_out
= coreaudio_fini_out
,
538 .run_out
= coreaudio_run_out
,
539 .write
= coreaudio_write
,
540 .ctl_out
= coreaudio_ctl_out
543 struct audio_driver coreaudio_audio_driver
= {
545 .descr
= "CoreAudio http://developer.apple.com/audio/coreaudio.html",
546 .options
= coreaudio_options
,
547 .init
= coreaudio_audio_init
,
548 .fini
= coreaudio_audio_fini
,
549 .pcm_ops
= &coreaudio_pcm_ops
,
553 .voice_size_out
= sizeof (coreaudioVoiceOut
),