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