]> git.proxmox.com Git - mirror_qemu.git/blame - audio/coreaudio.c
workaround for atexit - buffer size API change
[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
25#include <CoreAudio/CoreAudio.h>
26#include <string.h> /* strerror */
27#include <pthread.h> /* pthread_X */
28
29#include "vl.h"
30
31#define AUDIO_CAP "coreaudio"
32#include "audio_int.h"
33
1d14ffa9
FB
34struct {
35 int buffer_frames;
5e941d4b 36 int isAtexit;
1d14ffa9 37} conf = {
5e941d4b
FB
38 .buffer_frames = 512,
39 .isAtexit = 0
1d14ffa9
FB
40};
41
42typedef struct coreaudioVoiceOut {
43 HWVoiceOut hw;
44 pthread_mutex_t mutex;
5e941d4b 45 int isAtexit;
1d14ffa9 46 AudioDeviceID outputDeviceID;
5e941d4b 47 UInt32 audioDevicePropertyBufferFrameSize;
1d14ffa9 48 AudioStreamBasicDescription outputStreamBasicDescription;
1d14ffa9
FB
49 int live;
50 int decr;
51 int rpos;
52} coreaudioVoiceOut;
53
54static void coreaudio_logstatus (OSStatus status)
55{
56 char *str = "BUG";
57
58 switch(status) {
59 case kAudioHardwareNoError:
60 str = "kAudioHardwareNoError";
61 break;
62
63 case kAudioHardwareNotRunningError:
64 str = "kAudioHardwareNotRunningError";
65 break;
66
67 case kAudioHardwareUnspecifiedError:
68 str = "kAudioHardwareUnspecifiedError";
69 break;
70
71 case kAudioHardwareUnknownPropertyError:
72 str = "kAudioHardwareUnknownPropertyError";
73 break;
74
75 case kAudioHardwareBadPropertySizeError:
76 str = "kAudioHardwareBadPropertySizeError";
77 break;
78
79 case kAudioHardwareIllegalOperationError:
80 str = "kAudioHardwareIllegalOperationError";
81 break;
82
83 case kAudioHardwareBadDeviceError:
84 str = "kAudioHardwareBadDeviceError";
85 break;
86
87 case kAudioHardwareBadStreamError:
88 str = "kAudioHardwareBadStreamError";
89 break;
90
91 case kAudioHardwareUnsupportedOperationError:
92 str = "kAudioHardwareUnsupportedOperationError";
93 break;
94
95 case kAudioDeviceUnsupportedFormatError:
96 str = "kAudioDeviceUnsupportedFormatError";
97 break;
98
99 case kAudioDevicePermissionsError:
100 str = "kAudioDevicePermissionsError";
101 break;
102
103 default:
104 AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
105 return;
106 }
107
108 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
109}
110
111static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
112 OSStatus status,
113 const char *fmt,
114 ...
115 )
116{
117 va_list ap;
118
119 va_start (ap, fmt);
120 AUD_log (AUDIO_CAP, fmt, ap);
121 va_end (ap);
122
123 coreaudio_logstatus (status);
124}
125
126static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
127 OSStatus status,
128 const char *typ,
129 const char *fmt,
130 ...
131 )
132{
133 va_list ap;
134
c0fe3827 135 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
1d14ffa9
FB
136
137 va_start (ap, fmt);
138 AUD_vlog (AUDIO_CAP, fmt, ap);
139 va_end (ap);
140
141 coreaudio_logstatus (status);
142}
143
5e941d4b
FB
144static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
145{
146 OSStatus status;
147 UInt32 result = 0;
148 UInt32 propertySize = sizeof(outputDeviceID);
149 status = AudioDeviceGetProperty(
150 outputDeviceID, 0, 0,
151 kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
152 if (status != kAudioHardwareNoError) {
153 coreaudio_logerr(status,
154 "Could not determine whether Device is playing\n");
155 }
156 return result;
157}
158
159static void coreaudio_atexit (void)
160{
161 conf.isAtexit = 1;
162}
163
1d14ffa9
FB
164static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
165{
166 int err;
167
168 err = pthread_mutex_lock (&core->mutex);
169 if (err) {
c0fe3827 170 dolog ("Could not lock voice for %s\nReason: %s\n",
1d14ffa9
FB
171 fn_name, strerror (err));
172 return -1;
173 }
174 return 0;
175}
176
177static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
178{
179 int err;
180
181 err = pthread_mutex_unlock (&core->mutex);
182 if (err) {
c0fe3827 183 dolog ("Could not unlock voice for %s\nReason: %s\n",
1d14ffa9
FB
184 fn_name, strerror (err));
185 return -1;
186 }
187 return 0;
188}
189
190static int coreaudio_run_out (HWVoiceOut *hw)
191{
192 int live, decr;
193 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
194
195 if (coreaudio_lock (core, "coreaudio_run_out")) {
196 return 0;
197 }
198
199 live = audio_pcm_hw_get_live_out (hw);
200
201 if (core->decr > live) {
202 ldebug ("core->decr %d live %d core->live %d\n",
203 core->decr,
204 live,
205 core->live);
206 }
207
208 decr = audio_MIN (core->decr, live);
209 core->decr -= decr;
210
211 core->live = live - decr;
212 hw->rpos = core->rpos;
213
214 coreaudio_unlock (core, "coreaudio_run_out");
215 return decr;
216}
217
218/* callback to feed audiooutput buffer */
219static OSStatus audioDeviceIOProc(
220 AudioDeviceID inDevice,
221 const AudioTimeStamp* inNow,
222 const AudioBufferList* inInputData,
223 const AudioTimeStamp* inInputTime,
224 AudioBufferList* outOutputData,
225 const AudioTimeStamp* inOutputTime,
226 void* hwptr)
227{
5e941d4b 228 UInt32 frame, frameCount;
1d14ffa9
FB
229 float *out = outOutputData->mBuffers[0].mData;
230 HWVoiceOut *hw = hwptr;
231 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
232 int rpos, live;
233 st_sample_t *src;
234#ifndef FLOAT_MIXENG
235#ifdef RECIPROCAL
236 const float scale = 1.f / UINT_MAX;
237#else
238 const float scale = UINT_MAX;
239#endif
240#endif
241
242 if (coreaudio_lock (core, "audioDeviceIOProc")) {
243 inInputTime = 0;
244 return 0;
245 }
246
5e941d4b 247 frameCount = core->audioDevicePropertyBufferFrameSize;
1d14ffa9
FB
248 live = core->live;
249
250 /* if there are not enough samples, set signal and return */
251 if (live < frameCount) {
252 inInputTime = 0;
253 coreaudio_unlock (core, "audioDeviceIOProc(empty)");
254 return 0;
255 }
256
257 rpos = core->rpos;
258 src = hw->mix_buf + rpos;
259
260 /* fill buffer */
261 for (frame = 0; frame < frameCount; frame++) {
262#ifdef FLOAT_MIXENG
263 *out++ = src[frame].l; /* left channel */
264 *out++ = src[frame].r; /* right channel */
265#else
266#ifdef RECIPROCAL
267 *out++ = src[frame].l * scale; /* left channel */
268 *out++ = src[frame].r * scale; /* right channel */
269#else
270 *out++ = src[frame].l / scale; /* left channel */
271 *out++ = src[frame].r / scale; /* right channel */
272#endif
273#endif
274 }
275
276 /* cleanup */
277 mixeng_clear (src, frameCount);
278 rpos = (rpos + frameCount) % hw->samples;
5e941d4b 279 core->decr += frameCount;
1d14ffa9
FB
280 core->rpos = rpos;
281
282 coreaudio_unlock (core, "audioDeviceIOProc");
283 return 0;
284}
285
286static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
287{
288 return audio_pcm_sw_write (sw, buf, len);
289}
290
c0fe3827 291static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
1d14ffa9
FB
292{
293 OSStatus status;
294 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
295 UInt32 propertySize;
296 int err;
297 int bits = 8;
298 int endianess = 0;
5e941d4b
FB
299 const char *typ = "playback";
300 AudioValueRange frameRange;
1d14ffa9
FB
301
302 /* create mutex */
303 err = pthread_mutex_init(&core->mutex, NULL);
304 if (err) {
c0fe3827 305 dolog("Could not create mutex\nReason: %s\n", strerror (err));
1d14ffa9
FB
306 return -1;
307 }
308
c0fe3827 309 if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
1d14ffa9
FB
310 bits = 16;
311 endianess = 1;
312 }
313
314 audio_pcm_init_info (
315 &hw->info,
c0fe3827 316 as,
1d14ffa9
FB
317 /* Following is irrelevant actually since we do not use
318 mixengs clipping routines */
319 audio_need_to_swap_endian (endianess)
320 );
1d14ffa9
FB
321
322 /* open default output device */
323 propertySize = sizeof(core->outputDeviceID);
324 status = AudioHardwareGetProperty(
325 kAudioHardwarePropertyDefaultOutputDevice,
326 &propertySize,
327 &core->outputDeviceID);
328 if (status != kAudioHardwareNoError) {
329 coreaudio_logerr2 (status, typ,
c0fe3827 330 "Could not get default output Device\n");
1d14ffa9
FB
331 return -1;
332 }
333 if (core->outputDeviceID == kAudioDeviceUnknown) {
c0fe3827 334 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
1d14ffa9
FB
335 return -1;
336 }
337
5e941d4b
FB
338 /* get minimum and maximum buffer frame sizes */
339 propertySize = sizeof(frameRange);
340 status = AudioDeviceGetProperty(
341 core->outputDeviceID,
342 0,
343 0,
344 kAudioDevicePropertyBufferFrameSizeRange,
345 &propertySize,
346 &frameRange);
347 if (status != kAudioHardwareNoError) {
348 coreaudio_logerr2 (status, typ,
349 "Could not get device buffer frame range\n");
350 return -1;
351 }
352
353 if (frameRange.mMinimum > conf.buffer_frames) {
354 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
355 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
356 }
357 else if (frameRange.mMaximum < conf.buffer_frames) {
358 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
359 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
360 }
361 else {
362 core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
363 }
364
365 /* set Buffer Frame Size */
366 propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
1d14ffa9
FB
367 status = AudioDeviceSetProperty(
368 core->outputDeviceID,
369 NULL,
370 0,
371 false,
5e941d4b 372 kAudioDevicePropertyBufferFrameSize,
1d14ffa9 373 propertySize,
5e941d4b 374 &core->audioDevicePropertyBufferFrameSize);
1d14ffa9
FB
375 if (status != kAudioHardwareNoError) {
376 coreaudio_logerr2 (status, typ,
5e941d4b
FB
377 "Could not set device buffer frame size %ld\n",
378 core->audioDevicePropertyBufferFrameSize);
1d14ffa9
FB
379 return -1;
380 }
381
5e941d4b
FB
382 /* get Buffer Frame Size */
383 propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
1d14ffa9
FB
384 status = AudioDeviceGetProperty(
385 core->outputDeviceID,
386 0,
387 false,
5e941d4b 388 kAudioDevicePropertyBufferFrameSize,
1d14ffa9 389 &propertySize,
5e941d4b 390 &core->audioDevicePropertyBufferFrameSize);
1d14ffa9 391 if (status != kAudioHardwareNoError) {
5e941d4b
FB
392 coreaudio_logerr2 (status, typ,
393 "Could not get device buffer frame size\n");
1d14ffa9
FB
394 return -1;
395 }
5e941d4b 396 hw->samples = 4 * core->audioDevicePropertyBufferFrameSize;
1d14ffa9
FB
397
398 /* get StreamFormat */
399 propertySize = sizeof(core->outputStreamBasicDescription);
400 status = AudioDeviceGetProperty(
401 core->outputDeviceID,
402 0,
403 false,
404 kAudioDevicePropertyStreamFormat,
405 &propertySize,
406 &core->outputStreamBasicDescription);
407 if (status != kAudioHardwareNoError) {
408 coreaudio_logerr2 (status, typ,
c0fe3827 409 "Could not get Device Stream properties\n");
1d14ffa9
FB
410 core->outputDeviceID = kAudioDeviceUnknown;
411 return -1;
412 }
413
414 /* set Samplerate */
5e941d4b 415 core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
1d14ffa9
FB
416 propertySize = sizeof(core->outputStreamBasicDescription);
417 status = AudioDeviceSetProperty(
418 core->outputDeviceID,
419 0,
420 0,
421 0,
422 kAudioDevicePropertyStreamFormat,
423 propertySize,
424 &core->outputStreamBasicDescription);
425 if (status != kAudioHardwareNoError) {
575b5dc4
FB
426 coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
427 as->freq);
1d14ffa9
FB
428 core->outputDeviceID = kAudioDeviceUnknown;
429 return -1;
430 }
431
432 /* set Callback */
433 status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
434 if (status != kAudioHardwareNoError) {
c0fe3827 435 coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
1d14ffa9
FB
436 core->outputDeviceID = kAudioDeviceUnknown;
437 return -1;
438 }
439
440 /* start Playback */
5e941d4b 441 if (!isPlaying(core->outputDeviceID)) {
1d14ffa9
FB
442 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
443 if (status != kAudioHardwareNoError) {
c0fe3827 444 coreaudio_logerr2 (status, typ, "Could not start playback\n");
1d14ffa9
FB
445 AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
446 core->outputDeviceID = kAudioDeviceUnknown;
447 return -1;
448 }
1d14ffa9
FB
449 }
450
451 return 0;
452}
453
454static void coreaudio_fini_out (HWVoiceOut *hw)
455{
456 OSStatus status;
457 int err;
458 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
459
5e941d4b
FB
460 if (!conf.isAtexit) {
461 /* stop playback */
462 if (isPlaying(core->outputDeviceID)) {
463 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
464 if (status != kAudioHardwareNoError) {
465 coreaudio_logerr (status, "Could not stop playback\n");
466 }
1d14ffa9 467 }
1d14ffa9 468
5e941d4b
FB
469 /* remove callback */
470 status = AudioDeviceRemoveIOProc(core->outputDeviceID,
471 audioDeviceIOProc);
472 if (status != kAudioHardwareNoError) {
473 coreaudio_logerr (status, "Could not remove IOProc\n");
474 }
1d14ffa9
FB
475 }
476 core->outputDeviceID = kAudioDeviceUnknown;
477
478 /* destroy mutex */
479 err = pthread_mutex_destroy(&core->mutex);
480 if (err) {
c0fe3827 481 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
1d14ffa9
FB
482 }
483}
484
485static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
486{
487 OSStatus status;
488 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
489
490 switch (cmd) {
491 case VOICE_ENABLE:
492 /* start playback */
5e941d4b 493 if (!isPlaying(core->outputDeviceID)) {
1d14ffa9
FB
494 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
495 if (status != kAudioHardwareNoError) {
5e941d4b 496 coreaudio_logerr (status, "Could not resume playback\n");
1d14ffa9 497 }
1d14ffa9
FB
498 }
499 break;
500
501 case VOICE_DISABLE:
502 /* stop playback */
5e941d4b
FB
503 if (!conf.isAtexit) {
504 if (isPlaying(core->outputDeviceID)) {
505 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
506 if (status != kAudioHardwareNoError) {
507 coreaudio_logerr (status, "Could not pause playback\n");
508 }
1d14ffa9 509 }
1d14ffa9
FB
510 }
511 break;
512 }
513 return 0;
514}
515
516static void *coreaudio_audio_init (void)
517{
5e941d4b 518 atexit(coreaudio_atexit);
1d14ffa9
FB
519 return &coreaudio_audio_init;
520}
521
522static void coreaudio_audio_fini (void *opaque)
523{
524 (void) opaque;
525}
526
527static struct audio_option coreaudio_options[] = {
528 {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
529 "Size of the buffer in frames", NULL, 0},
530 {NULL, 0, NULL, NULL, NULL, 0}
531};
532
533static struct audio_pcm_ops coreaudio_pcm_ops = {
534 coreaudio_init_out,
535 coreaudio_fini_out,
536 coreaudio_run_out,
537 coreaudio_write,
538 coreaudio_ctl_out,
539
540 NULL,
541 NULL,
542 NULL,
543 NULL,
544 NULL
545};
546
547struct audio_driver coreaudio_audio_driver = {
548 INIT_FIELD (name = ) "coreaudio",
549 INIT_FIELD (descr = )
550 "CoreAudio http://developer.apple.com/audio/coreaudio.html",
551 INIT_FIELD (options = ) coreaudio_options,
552 INIT_FIELD (init = ) coreaudio_audio_init,
553 INIT_FIELD (fini = ) coreaudio_audio_fini,
554 INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
555 INIT_FIELD (can_be_default = ) 1,
556 INIT_FIELD (max_voices_out = ) 1,
557 INIT_FIELD (max_voices_in = ) 0,
558 INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
559 INIT_FIELD (voice_size_in = ) 0
560};