source: trunk/packages/xen-3.1/xen-3.1/tools/ioemu/audio/coreaudio.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

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#include "vl.h"
30
31#define AUDIO_CAP "coreaudio"
32#include "audio_int.h"
33
34struct {
35    int buffer_frames;
36    int nbuffers;
37    int isAtexit;
38} conf = {
39    .buffer_frames = 512,
40    .nbuffers = 4,
41    .isAtexit = 0
42};
43
44typedef struct coreaudioVoiceOut {
45    HWVoiceOut hw;
46    pthread_mutex_t mutex;
47    int isAtexit;
48    AudioDeviceID outputDeviceID;
49    UInt32 audioDevicePropertyBufferFrameSize;
50    AudioStreamBasicDescription outputStreamBasicDescription;
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
137    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
138
139    va_start (ap, fmt);
140    AUD_vlog (AUDIO_CAP, fmt, ap);
141    va_end (ap);
142
143    coreaudio_logstatus (status);
144}
145
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
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) {
172        dolog ("Could not lock voice for %s\nReason: %s\n",
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) {
185        dolog ("Could not unlock voice for %s\nReason: %s\n",
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{
230    UInt32 frame, frameCount;
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
249    frameCount = core->audioDevicePropertyBufferFrameSize;
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    rpos = (rpos + frameCount) % hw->samples;
279    core->decr += frameCount;
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
291static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
292{
293    OSStatus status;
294    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
295    UInt32 propertySize;
296    int err;
297    int bits = 8;
298    const char *typ = "playback";
299    AudioValueRange frameRange;
300
301    /* create mutex */
302    err = pthread_mutex_init(&core->mutex, NULL);
303    if (err) {
304        dolog("Could not create mutex\nReason: %s\n", strerror (err));
305        return -1;
306    }
307
308    if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
309        bits = 16;
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.