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