VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/coreaudio.c@ 9380

Last change on this file since 9380 was 1584, checked in by vboxsync, 18 years ago

export

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette