~ahs3/+junk/cq-qemu

« back to all changes in this revision

Viewing changes to audio/paaudio.c

  • Committer: Al Stone
  • Date: 2012-02-09 01:17:20 UTC
  • Revision ID: albert.stone@canonical.com-20120209011720-tztl7ik3qayz80p4
first commit to bzr for qemu

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* public domain */
 
2
#include "qemu-common.h"
 
3
#include "audio.h"
 
4
 
 
5
#include <pulse/simple.h>
 
6
#include <pulse/error.h>
 
7
 
 
8
#define AUDIO_CAP "pulseaudio"
 
9
#include "audio_int.h"
 
10
#include "audio_pt_int.h"
 
11
 
 
12
typedef struct {
 
13
    HWVoiceOut hw;
 
14
    int done;
 
15
    int live;
 
16
    int decr;
 
17
    int rpos;
 
18
    pa_simple *s;
 
19
    void *pcm_buf;
 
20
    struct audio_pt pt;
 
21
} PAVoiceOut;
 
22
 
 
23
typedef struct {
 
24
    HWVoiceIn hw;
 
25
    int done;
 
26
    int dead;
 
27
    int incr;
 
28
    int wpos;
 
29
    pa_simple *s;
 
30
    void *pcm_buf;
 
31
    struct audio_pt pt;
 
32
} PAVoiceIn;
 
33
 
 
34
static struct {
 
35
    int samples;
 
36
    char *server;
 
37
    char *sink;
 
38
    char *source;
 
39
} conf = {
 
40
    .samples = 4096,
 
41
};
 
42
 
 
43
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
 
44
{
 
45
    va_list ap;
 
46
 
 
47
    va_start (ap, fmt);
 
48
    AUD_vlog (AUDIO_CAP, fmt, ap);
 
49
    va_end (ap);
 
50
 
 
51
    AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
 
52
}
 
53
 
 
54
static void *qpa_thread_out (void *arg)
 
55
{
 
56
    PAVoiceOut *pa = arg;
 
57
    HWVoiceOut *hw = &pa->hw;
 
58
 
 
59
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 
60
        return NULL;
 
61
    }
 
62
 
 
63
    for (;;) {
 
64
        int decr, to_mix, rpos;
 
65
 
 
66
        for (;;) {
 
67
            if (pa->done) {
 
68
                goto exit;
 
69
            }
 
70
 
 
71
            if (pa->live > 0) {
 
72
                break;
 
73
            }
 
74
 
 
75
            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
 
76
                goto exit;
 
77
            }
 
78
        }
 
79
 
 
80
        decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
 
81
        rpos = pa->rpos;
 
82
 
 
83
        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
 
84
            return NULL;
 
85
        }
 
86
 
 
87
        while (to_mix) {
 
88
            int error;
 
89
            int chunk = audio_MIN (to_mix, hw->samples - rpos);
 
90
            struct st_sample *src = hw->mix_buf + rpos;
 
91
 
 
92
            hw->clip (pa->pcm_buf, src, chunk);
 
93
 
 
94
            if (pa_simple_write (pa->s, pa->pcm_buf,
 
95
                                 chunk << hw->info.shift, &error) < 0) {
 
96
                qpa_logerr (error, "pa_simple_write failed\n");
 
97
                return NULL;
 
98
            }
 
99
 
 
100
            rpos = (rpos + chunk) % hw->samples;
 
101
            to_mix -= chunk;
 
102
        }
 
103
 
 
104
        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 
105
            return NULL;
 
106
        }
 
107
 
 
108
        pa->rpos = rpos;
 
109
        pa->live -= decr;
 
110
        pa->decr += decr;
 
111
    }
 
112
 
 
113
 exit:
 
114
    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 
115
    return NULL;
 
116
}
 
117
 
 
118
static int qpa_run_out (HWVoiceOut *hw, int live)
 
119
{
 
120
    int decr;
 
121
    PAVoiceOut *pa = (PAVoiceOut *) hw;
 
122
 
 
123
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 
124
        return 0;
 
125
    }
 
126
 
 
127
    decr = audio_MIN (live, pa->decr);
 
128
    pa->decr -= decr;
 
129
    pa->live = live - decr;
 
130
    hw->rpos = pa->rpos;
 
131
    if (pa->live > 0) {
 
132
        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 
133
    }
 
134
    else {
 
135
        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 
136
    }
 
137
    return decr;
 
138
}
 
139
 
 
140
static int qpa_write (SWVoiceOut *sw, void *buf, int len)
 
141
{
 
142
    return audio_pcm_sw_write (sw, buf, len);
 
143
}
 
144
 
 
145
/* capture */
 
146
static void *qpa_thread_in (void *arg)
 
147
{
 
148
    PAVoiceIn *pa = arg;
 
149
    HWVoiceIn *hw = &pa->hw;
 
150
 
 
151
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 
152
        return NULL;
 
153
    }
 
154
 
 
155
    for (;;) {
 
156
        int incr, to_grab, wpos;
 
157
 
 
158
        for (;;) {
 
159
            if (pa->done) {
 
160
                goto exit;
 
161
            }
 
162
 
 
163
            if (pa->dead > 0) {
 
164
                break;
 
165
            }
 
166
 
 
167
            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
 
168
                goto exit;
 
169
            }
 
170
        }
 
171
 
 
172
        incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
 
173
        wpos = pa->wpos;
 
174
 
 
175
        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
 
176
            return NULL;
 
177
        }
 
178
 
 
179
        while (to_grab) {
 
180
            int error;
 
181
            int chunk = audio_MIN (to_grab, hw->samples - wpos);
 
182
            void *buf = advance (pa->pcm_buf, wpos);
 
183
 
 
184
            if (pa_simple_read (pa->s, buf,
 
185
                                chunk << hw->info.shift, &error) < 0) {
 
186
                qpa_logerr (error, "pa_simple_read failed\n");
 
187
                return NULL;
 
188
            }
 
189
 
 
190
            hw->conv (hw->conv_buf + wpos, buf, chunk);
 
191
            wpos = (wpos + chunk) % hw->samples;
 
192
            to_grab -= chunk;
 
193
        }
 
194
 
 
195
        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 
196
            return NULL;
 
197
        }
 
198
 
 
199
        pa->wpos = wpos;
 
200
        pa->dead -= incr;
 
201
        pa->incr += incr;
 
202
    }
 
203
 
 
204
 exit:
 
205
    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 
206
    return NULL;
 
207
}
 
208
 
 
209
static int qpa_run_in (HWVoiceIn *hw)
 
210
{
 
211
    int live, incr, dead;
 
212
    PAVoiceIn *pa = (PAVoiceIn *) hw;
 
213
 
 
214
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 
215
        return 0;
 
216
    }
 
217
 
 
218
    live = audio_pcm_hw_get_live_in (hw);
 
219
    dead = hw->samples - live;
 
220
    incr = audio_MIN (dead, pa->incr);
 
221
    pa->incr -= incr;
 
222
    pa->dead = dead - incr;
 
223
    hw->wpos = pa->wpos;
 
224
    if (pa->dead > 0) {
 
225
        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 
226
    }
 
227
    else {
 
228
        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 
229
    }
 
230
    return incr;
 
231
}
 
232
 
 
233
static int qpa_read (SWVoiceIn *sw, void *buf, int len)
 
234
{
 
235
    return audio_pcm_sw_read (sw, buf, len);
 
236
}
 
237
 
 
238
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
 
239
{
 
240
    int format;
 
241
 
 
242
    switch (afmt) {
 
243
    case AUD_FMT_S8:
 
244
    case AUD_FMT_U8:
 
245
        format = PA_SAMPLE_U8;
 
246
        break;
 
247
    case AUD_FMT_S16:
 
248
    case AUD_FMT_U16:
 
249
        format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
 
250
        break;
 
251
    case AUD_FMT_S32:
 
252
    case AUD_FMT_U32:
 
253
        format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
 
254
        break;
 
255
    default:
 
256
        dolog ("Internal logic error: Bad audio format %d\n", afmt);
 
257
        format = PA_SAMPLE_U8;
 
258
        break;
 
259
    }
 
260
    return format;
 
261
}
 
262
 
 
263
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 
264
{
 
265
    switch (fmt) {
 
266
    case PA_SAMPLE_U8:
 
267
        return AUD_FMT_U8;
 
268
    case PA_SAMPLE_S16BE:
 
269
        *endianness = 1;
 
270
        return AUD_FMT_S16;
 
271
    case PA_SAMPLE_S16LE:
 
272
        *endianness = 0;
 
273
        return AUD_FMT_S16;
 
274
    case PA_SAMPLE_S32BE:
 
275
        *endianness = 1;
 
276
        return AUD_FMT_S32;
 
277
    case PA_SAMPLE_S32LE:
 
278
        *endianness = 0;
 
279
        return AUD_FMT_S32;
 
280
    default:
 
281
        dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
 
282
        return AUD_FMT_U8;
 
283
    }
 
284
}
 
285
 
 
286
static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
 
287
{
 
288
    int error;
 
289
    static pa_sample_spec ss;
 
290
    static pa_buffer_attr ba;
 
291
    struct audsettings obt_as = *as;
 
292
    PAVoiceOut *pa = (PAVoiceOut *) hw;
 
293
 
 
294
    ss.format = audfmt_to_pa (as->fmt, as->endianness);
 
295
    ss.channels = as->nchannels;
 
296
    ss.rate = as->freq;
 
297
 
 
298
    /*
 
299
     * qemu audio tick runs at 250 Hz (by default), so processing
 
300
     * data chunks worth 4 ms of sound should be a good fit.
 
301
     */
 
302
    ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
 
303
    ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
 
304
    ba.maxlength = -1;
 
305
    ba.prebuf = -1;
 
306
 
 
307
    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 
308
 
 
309
    pa->s = pa_simple_new (
 
310
        conf.server,
 
311
        "qemu",
 
312
        PA_STREAM_PLAYBACK,
 
313
        conf.sink,
 
314
        "pcm.playback",
 
315
        &ss,
 
316
        NULL,                   /* channel map */
 
317
        &ba,                    /* buffering attributes */
 
318
        &error
 
319
        );
 
320
    if (!pa->s) {
 
321
        qpa_logerr (error, "pa_simple_new for playback failed\n");
 
322
        goto fail1;
 
323
    }
 
324
 
 
325
    audio_pcm_init_info (&hw->info, &obt_as);
 
326
    hw->samples = conf.samples;
 
327
    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 
328
    pa->rpos = hw->rpos;
 
329
    if (!pa->pcm_buf) {
 
330
        dolog ("Could not allocate buffer (%d bytes)\n",
 
331
               hw->samples << hw->info.shift);
 
332
        goto fail2;
 
333
    }
 
334
 
 
335
    if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
 
336
        goto fail3;
 
337
    }
 
338
 
 
339
    return 0;
 
340
 
 
341
 fail3:
 
342
    g_free (pa->pcm_buf);
 
343
    pa->pcm_buf = NULL;
 
344
 fail2:
 
345
    pa_simple_free (pa->s);
 
346
    pa->s = NULL;
 
347
 fail1:
 
348
    return -1;
 
349
}
 
350
 
 
351
static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
 
352
{
 
353
    int error;
 
354
    static pa_sample_spec ss;
 
355
    struct audsettings obt_as = *as;
 
356
    PAVoiceIn *pa = (PAVoiceIn *) hw;
 
357
 
 
358
    ss.format = audfmt_to_pa (as->fmt, as->endianness);
 
359
    ss.channels = as->nchannels;
 
360
    ss.rate = as->freq;
 
361
 
 
362
    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 
363
 
 
364
    pa->s = pa_simple_new (
 
365
        conf.server,
 
366
        "qemu",
 
367
        PA_STREAM_RECORD,
 
368
        conf.source,
 
369
        "pcm.capture",
 
370
        &ss,
 
371
        NULL,                   /* channel map */
 
372
        NULL,                   /* buffering attributes */
 
373
        &error
 
374
        );
 
375
    if (!pa->s) {
 
376
        qpa_logerr (error, "pa_simple_new for capture failed\n");
 
377
        goto fail1;
 
378
    }
 
379
 
 
380
    audio_pcm_init_info (&hw->info, &obt_as);
 
381
    hw->samples = conf.samples;
 
382
    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 
383
    pa->wpos = hw->wpos;
 
384
    if (!pa->pcm_buf) {
 
385
        dolog ("Could not allocate buffer (%d bytes)\n",
 
386
               hw->samples << hw->info.shift);
 
387
        goto fail2;
 
388
    }
 
389
 
 
390
    if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
 
391
        goto fail3;
 
392
    }
 
393
 
 
394
    return 0;
 
395
 
 
396
 fail3:
 
397
    g_free (pa->pcm_buf);
 
398
    pa->pcm_buf = NULL;
 
399
 fail2:
 
400
    pa_simple_free (pa->s);
 
401
    pa->s = NULL;
 
402
 fail1:
 
403
    return -1;
 
404
}
 
405
 
 
406
static void qpa_fini_out (HWVoiceOut *hw)
 
407
{
 
408
    void *ret;
 
409
    PAVoiceOut *pa = (PAVoiceOut *) hw;
 
410
 
 
411
    audio_pt_lock (&pa->pt, AUDIO_FUNC);
 
412
    pa->done = 1;
 
413
    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 
414
    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 
415
 
 
416
    if (pa->s) {
 
417
        pa_simple_free (pa->s);
 
418
        pa->s = NULL;
 
419
    }
 
420
 
 
421
    audio_pt_fini (&pa->pt, AUDIO_FUNC);
 
422
    g_free (pa->pcm_buf);
 
423
    pa->pcm_buf = NULL;
 
424
}
 
425
 
 
426
static void qpa_fini_in (HWVoiceIn *hw)
 
427
{
 
428
    void *ret;
 
429
    PAVoiceIn *pa = (PAVoiceIn *) hw;
 
430
 
 
431
    audio_pt_lock (&pa->pt, AUDIO_FUNC);
 
432
    pa->done = 1;
 
433
    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 
434
    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 
435
 
 
436
    if (pa->s) {
 
437
        pa_simple_free (pa->s);
 
438
        pa->s = NULL;
 
439
    }
 
440
 
 
441
    audio_pt_fini (&pa->pt, AUDIO_FUNC);
 
442
    g_free (pa->pcm_buf);
 
443
    pa->pcm_buf = NULL;
 
444
}
 
445
 
 
446
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
 
447
{
 
448
    (void) hw;
 
449
    (void) cmd;
 
450
    return 0;
 
451
}
 
452
 
 
453
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 
454
{
 
455
    (void) hw;
 
456
    (void) cmd;
 
457
    return 0;
 
458
}
 
459
 
 
460
/* common */
 
461
static void *qpa_audio_init (void)
 
462
{
 
463
    return &conf;
 
464
}
 
465
 
 
466
static void qpa_audio_fini (void *opaque)
 
467
{
 
468
    (void) opaque;
 
469
}
 
470
 
 
471
struct audio_option qpa_options[] = {
 
472
    {
 
473
        .name  = "SAMPLES",
 
474
        .tag   = AUD_OPT_INT,
 
475
        .valp  = &conf.samples,
 
476
        .descr = "buffer size in samples"
 
477
    },
 
478
    {
 
479
        .name  = "SERVER",
 
480
        .tag   = AUD_OPT_STR,
 
481
        .valp  = &conf.server,
 
482
        .descr = "server address"
 
483
    },
 
484
    {
 
485
        .name  = "SINK",
 
486
        .tag   = AUD_OPT_STR,
 
487
        .valp  = &conf.sink,
 
488
        .descr = "sink device name"
 
489
    },
 
490
    {
 
491
        .name  = "SOURCE",
 
492
        .tag   = AUD_OPT_STR,
 
493
        .valp  = &conf.source,
 
494
        .descr = "source device name"
 
495
    },
 
496
    { /* End of list */ }
 
497
};
 
498
 
 
499
static struct audio_pcm_ops qpa_pcm_ops = {
 
500
    .init_out = qpa_init_out,
 
501
    .fini_out = qpa_fini_out,
 
502
    .run_out  = qpa_run_out,
 
503
    .write    = qpa_write,
 
504
    .ctl_out  = qpa_ctl_out,
 
505
 
 
506
    .init_in  = qpa_init_in,
 
507
    .fini_in  = qpa_fini_in,
 
508
    .run_in   = qpa_run_in,
 
509
    .read     = qpa_read,
 
510
    .ctl_in   = qpa_ctl_in
 
511
};
 
512
 
 
513
struct audio_driver pa_audio_driver = {
 
514
    .name           = "pa",
 
515
    .descr          = "http://www.pulseaudio.org/",
 
516
    .options        = qpa_options,
 
517
    .init           = qpa_audio_init,
 
518
    .fini           = qpa_audio_fini,
 
519
    .pcm_ops        = &qpa_pcm_ops,
 
520
    .can_be_default = 1,
 
521
    .max_voices_out = INT_MAX,
 
522
    .max_voices_in  = INT_MAX,
 
523
    .voice_size_out = sizeof (PAVoiceOut),
 
524
    .voice_size_in  = sizeof (PAVoiceIn)
 
525
};