qemu/audio/paaudio.c
<<
>>
Prefs
   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
  12typedef 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
  23typedef 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
  34static struct {
  35    int samples;
  36    char *server;
  37    char *sink;
  38    char *source;
  39} conf = {
  40    .samples = 4096,
  41};
  42
  43static 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
  54static 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
 118static 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
 140static int qpa_write (SWVoiceOut *sw, void *buf, int len)
 141{
 142    return audio_pcm_sw_write (sw, buf, len);
 143}
 144
 145/* capture */
 146static 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
 209static 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
 233static int qpa_read (SWVoiceIn *sw, void *buf, int len)
 234{
 235    return audio_pcm_sw_read (sw, buf, len);
 236}
 237
 238static 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
 263static 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
 286static 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    qemu_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
 351static 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    qemu_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
 406static 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    qemu_free (pa->pcm_buf);
 423    pa->pcm_buf = NULL;
 424}
 425
 426static 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    qemu_free (pa->pcm_buf);
 443    pa->pcm_buf = NULL;
 444}
 445
 446static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
 447{
 448    (void) hw;
 449    (void) cmd;
 450    return 0;
 451}
 452
 453static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 454{
 455    (void) hw;
 456    (void) cmd;
 457    return 0;
 458}
 459
 460/* common */
 461static void *qpa_audio_init (void)
 462{
 463    return &conf;
 464}
 465
 466static void qpa_audio_fini (void *opaque)
 467{
 468    (void) opaque;
 469}
 470
 471struct 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
 499static 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
 513struct 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};
 526