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