Statistics
| Branch: | Revision:

root / audio / coreaudio.c @ d796321b

History | View | Annotate | Download (15.7 kB)

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

    
34
struct {
35
    int buffer_frames;
36
    int nbuffers;
37
    int isAtexit;
38
} conf = {
39
    .buffer_frames = 512,
40
    .nbuffers = 4,
41
    .isAtexit = 0
42
};
43

    
44
typedef 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

    
56
static 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

    
113
static 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

    
128
static 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

    
146
static 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

    
161
static void coreaudio_atexit (void)
162
{
163
    conf.isAtexit = 1;
164
}
165

    
166
static 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

    
179
static 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

    
192
static 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 */
221
static 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
    /* cleanup */
279
    mixeng_clear (src, frameCount);
280
    rpos = (rpos + frameCount) % hw->samples;
281
    core->decr += frameCount;
282
    core->rpos = rpos;
283

    
284
    coreaudio_unlock (core, "audioDeviceIOProc");
285
    return 0;
286
}
287

    
288
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
289
{
290
    return audio_pcm_sw_write (sw, buf, len);
291
}
292

    
293
static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
294
{
295
    OSStatus status;
296
    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
297
    UInt32 propertySize;
298
    int err;
299
    int bits = 8;
300
    int endianess = 0;
301
    const char *typ = "playback";
302
    AudioValueRange frameRange;
303

    
304
    /* create mutex */
305
    err = pthread_mutex_init(&core->mutex, NULL);
306
    if (err) {
307
        dolog("Could not create mutex\nReason: %s\n", strerror (err));
308
        return -1;
309
    }
310

    
311
    if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
312
        bits = 16;
313
        endianess = 1;
314
    }
315

    
316
    audio_pcm_init_info (
317
        &hw->info,
318
        as,
319
        /* Following is irrelevant actually since we do not use
320
           mixengs clipping routines */
321
        audio_need_to_swap_endian (endianess)
322
        );
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,
332
                           "Could not get default output Device\n");
333
        return -1;
334
    }
335
    if (core->outputDeviceID == kAudioDeviceUnknown) {
336
        dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
337
        return -1;
338
    }
339

    
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);
369
    status = AudioDeviceSetProperty(
370
        core->outputDeviceID,
371
        NULL,
372
        0,
373
        false,
374
        kAudioDevicePropertyBufferFrameSize,
375
        propertySize,
376
        &core->audioDevicePropertyBufferFrameSize);
377
    if (status != kAudioHardwareNoError) {
378
        coreaudio_logerr2 (status, typ,
379
                           "Could not set device buffer frame size %ld\n",
380
                           core->audioDevicePropertyBufferFrameSize);
381
        return -1;
382
    }
383

    
384
    /* get Buffer Frame Size */
385
    propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
386
    status = AudioDeviceGetProperty(
387
        core->outputDeviceID,
388
        0,
389
        false,
390
        kAudioDevicePropertyBufferFrameSize,
391
        &propertySize,
392
        &core->audioDevicePropertyBufferFrameSize);
393
    if (status != kAudioHardwareNoError) {
394
        coreaudio_logerr2 (status, typ,
395
                           "Could not get device buffer frame size\n");
396
        return -1;
397
    }
398
    hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
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,
411
                           "Could not get Device Stream properties\n");
412
        core->outputDeviceID = kAudioDeviceUnknown;
413
        return -1;
414
    }
415

    
416
    /* set Samplerate */
417
    core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
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) {
428
        coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
429
                           as->freq);
430
        core->outputDeviceID = kAudioDeviceUnknown;
431
        return -1;
432
    }
433

    
434
    /* set Callback */
435
    status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
436
    if (status != kAudioHardwareNoError) {
437
        coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
438
        core->outputDeviceID = kAudioDeviceUnknown;
439
        return -1;
440
    }
441

    
442
    /* start Playback */
443
    if (!isPlaying(core->outputDeviceID)) {
444
        status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
445
        if (status != kAudioHardwareNoError) {
446
            coreaudio_logerr2 (status, typ, "Could not start playback\n");
447
            AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
448
            core->outputDeviceID = kAudioDeviceUnknown;
449
            return -1;
450
        }
451
    }
452

    
453
    return 0;
454
}
455

    
456
static void coreaudio_fini_out (HWVoiceOut *hw)
457
{
458
    OSStatus status;
459
    int err;
460
    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
461

    
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
            }
469
        }
470

    
471
        /* remove callback */
472
        status = AudioDeviceRemoveIOProc(core->outputDeviceID,
473
                                         audioDeviceIOProc);
474
        if (status != kAudioHardwareNoError) {
475
            coreaudio_logerr (status, "Could not remove IOProc\n");
476
        }
477
    }
478
    core->outputDeviceID = kAudioDeviceUnknown;
479

    
480
    /* destroy mutex */
481
    err = pthread_mutex_destroy(&core->mutex);
482
    if (err) {
483
        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
484
    }
485
}
486

    
487
static 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 */
495
        if (!isPlaying(core->outputDeviceID)) {
496
            status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
497
            if (status != kAudioHardwareNoError) {
498
                coreaudio_logerr (status, "Could not resume playback\n");
499
            }
500
        }
501
        break;
502

    
503
    case VOICE_DISABLE:
504
        /* stop playback */
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
                }
511
            }
512
        }
513
        break;
514
    }
515
    return 0;
516
}
517

    
518
static void *coreaudio_audio_init (void)
519
{
520
    atexit(coreaudio_atexit);
521
    return &coreaudio_audio_init;
522
}
523

    
524
static void coreaudio_audio_fini (void *opaque)
525
{
526
    (void) opaque;
527
}
528

    
529
static struct audio_option coreaudio_options[] = {
530
    {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
531
     "Size of the buffer in frames", NULL, 0},
532
    {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
533
     "Number of buffers", NULL, 0},
534
    {NULL, 0, NULL, NULL, NULL, 0}
535
};
536

    
537
static 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

    
551
struct 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
};