qemu/audio/paaudio.c
<<
>>
Prefs
   1/* public domain */
   2#include "qemu-common.h"
   3#include "audio.h"
   4
   5#include <pulse/pulseaudio.h>
   6
   7#define AUDIO_CAP "pulseaudio"
   8#include "audio_int.h"
   9#include "audio_pt_int.h"
  10
  11typedef struct {
  12    HWVoiceOut hw;
  13    int done;
  14    int live;
  15    int decr;
  16    int rpos;
  17    pa_stream *stream;
  18    void *pcm_buf;
  19    struct audio_pt pt;
  20} PAVoiceOut;
  21
  22typedef struct {
  23    HWVoiceIn hw;
  24    int done;
  25    int dead;
  26    int incr;
  27    int wpos;
  28    pa_stream *stream;
  29    void *pcm_buf;
  30    struct audio_pt pt;
  31    const void *read_data;
  32    size_t read_index, read_length;
  33} PAVoiceIn;
  34
  35typedef struct {
  36    int samples;
  37    char *server;
  38    char *sink;
  39    char *source;
  40    pa_threaded_mainloop *mainloop;
  41    pa_context *context;
  42} paaudio;
  43
  44static paaudio glob_paaudio = {
  45    .samples = 4096,
  46};
  47
  48static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
  49{
  50    va_list ap;
  51
  52    va_start (ap, fmt);
  53    AUD_vlog (AUDIO_CAP, fmt, ap);
  54    va_end (ap);
  55
  56    AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
  57}
  58
  59#ifndef PA_CONTEXT_IS_GOOD
  60static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
  61{
  62    return
  63        x == PA_CONTEXT_CONNECTING ||
  64        x == PA_CONTEXT_AUTHORIZING ||
  65        x == PA_CONTEXT_SETTING_NAME ||
  66        x == PA_CONTEXT_READY;
  67}
  68#endif
  69
  70#ifndef PA_STREAM_IS_GOOD
  71static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
  72{
  73    return
  74        x == PA_STREAM_CREATING ||
  75        x == PA_STREAM_READY;
  76}
  77#endif
  78
  79#define CHECK_SUCCESS_GOTO(c, rerror, expression, label)        \
  80    do {                                                        \
  81        if (!(expression)) {                                    \
  82            if (rerror) {                                       \
  83                *(rerror) = pa_context_errno ((c)->context);    \
  84            }                                                   \
  85            goto label;                                         \
  86        }                                                       \
  87    } while (0);
  88
  89#define CHECK_DEAD_GOTO(c, stream, rerror, label)                       \
  90    do {                                                                \
  91        if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
  92            !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
  93            if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
  94                ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
  95                if (rerror) {                                           \
  96                    *(rerror) = pa_context_errno ((c)->context);        \
  97                }                                                       \
  98            } else {                                                    \
  99                if (rerror) {                                           \
 100                    *(rerror) = PA_ERR_BADSTATE;                        \
 101                }                                                       \
 102            }                                                           \
 103            goto label;                                                 \
 104        }                                                               \
 105    } while (0);
 106
 107static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
 108{
 109    paaudio *g = &glob_paaudio;
 110
 111    pa_threaded_mainloop_lock (g->mainloop);
 112
 113    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
 114
 115    while (length > 0) {
 116        size_t l;
 117
 118        while (!p->read_data) {
 119            int r;
 120
 121            r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
 122            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
 123
 124            if (!p->read_data) {
 125                pa_threaded_mainloop_wait (g->mainloop);
 126                CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
 127            } else {
 128                p->read_index = 0;
 129            }
 130        }
 131
 132        l = p->read_length < length ? p->read_length : length;
 133        memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
 134
 135        data = (uint8_t *) data + l;
 136        length -= l;
 137
 138        p->read_index += l;
 139        p->read_length -= l;
 140
 141        if (!p->read_length) {
 142            int r;
 143
 144            r = pa_stream_drop (p->stream);
 145            p->read_data = NULL;
 146            p->read_length = 0;
 147            p->read_index = 0;
 148
 149            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
 150        }
 151    }
 152
 153    pa_threaded_mainloop_unlock (g->mainloop);
 154    return 0;
 155
 156unlock_and_fail:
 157    pa_threaded_mainloop_unlock (g->mainloop);
 158    return -1;
 159}
 160
 161static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
 162{
 163    paaudio *g = &glob_paaudio;
 164
 165    pa_threaded_mainloop_lock (g->mainloop);
 166
 167    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
 168
 169    while (length > 0) {
 170        size_t l;
 171        int r;
 172
 173        while (!(l = pa_stream_writable_size (p->stream))) {
 174            pa_threaded_mainloop_wait (g->mainloop);
 175            CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
 176        }
 177
 178        CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
 179
 180        if (l > length) {
 181            l = length;
 182        }
 183
 184        r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
 185        CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
 186
 187        data = (const uint8_t *) data + l;
 188        length -= l;
 189    }
 190
 191    pa_threaded_mainloop_unlock (g->mainloop);
 192    return 0;
 193
 194unlock_and_fail:
 195    pa_threaded_mainloop_unlock (g->mainloop);
 196    return -1;
 197}
 198
 199static void *qpa_thread_out (void *arg)
 200{
 201    PAVoiceOut *pa = arg;
 202    HWVoiceOut *hw = &pa->hw;
 203
 204    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 205        return NULL;
 206    }
 207
 208    for (;;) {
 209        int decr, to_mix, rpos;
 210
 211        for (;;) {
 212            if (pa->done) {
 213                goto exit;
 214            }
 215
 216            if (pa->live > 0) {
 217                break;
 218            }
 219
 220            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
 221                goto exit;
 222            }
 223        }
 224
 225        decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
 226        rpos = pa->rpos;
 227
 228        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
 229            return NULL;
 230        }
 231
 232        while (to_mix) {
 233            int error;
 234            int chunk = audio_MIN (to_mix, hw->samples - rpos);
 235            struct st_sample *src = hw->mix_buf + rpos;
 236
 237            hw->clip (pa->pcm_buf, src, chunk);
 238
 239            if (qpa_simple_write (pa, pa->pcm_buf,
 240                                  chunk << hw->info.shift, &error) < 0) {
 241                qpa_logerr (error, "pa_simple_write failed\n");
 242                return NULL;
 243            }
 244
 245            rpos = (rpos + chunk) % hw->samples;
 246            to_mix -= chunk;
 247        }
 248
 249        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 250            return NULL;
 251        }
 252
 253        pa->rpos = rpos;
 254        pa->live -= decr;
 255        pa->decr += decr;
 256    }
 257
 258 exit:
 259    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 260    return NULL;
 261}
 262
 263static int qpa_run_out (HWVoiceOut *hw, int live)
 264{
 265    int decr;
 266    PAVoiceOut *pa = (PAVoiceOut *) hw;
 267
 268    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 269        return 0;
 270    }
 271
 272    decr = audio_MIN (live, pa->decr);
 273    pa->decr -= decr;
 274    pa->live = live - decr;
 275    hw->rpos = pa->rpos;
 276    if (pa->live > 0) {
 277        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 278    }
 279    else {
 280        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 281    }
 282    return decr;
 283}
 284
 285static int qpa_write (SWVoiceOut *sw, void *buf, int len)
 286{
 287    return audio_pcm_sw_write (sw, buf, len);
 288}
 289
 290/* capture */
 291static void *qpa_thread_in (void *arg)
 292{
 293    PAVoiceIn *pa = arg;
 294    HWVoiceIn *hw = &pa->hw;
 295
 296    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 297        return NULL;
 298    }
 299
 300    for (;;) {
 301        int incr, to_grab, wpos;
 302
 303        for (;;) {
 304            if (pa->done) {
 305                goto exit;
 306            }
 307
 308            if (pa->dead > 0) {
 309                break;
 310            }
 311
 312            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
 313                goto exit;
 314            }
 315        }
 316
 317        incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
 318        wpos = pa->wpos;
 319
 320        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
 321            return NULL;
 322        }
 323
 324        while (to_grab) {
 325            int error;
 326            int chunk = audio_MIN (to_grab, hw->samples - wpos);
 327            void *buf = advance (pa->pcm_buf, wpos);
 328
 329            if (qpa_simple_read (pa, buf,
 330                                 chunk << hw->info.shift, &error) < 0) {
 331                qpa_logerr (error, "pa_simple_read failed\n");
 332                return NULL;
 333            }
 334
 335            hw->conv (hw->conv_buf + wpos, buf, chunk);
 336            wpos = (wpos + chunk) % hw->samples;
 337            to_grab -= chunk;
 338        }
 339
 340        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 341            return NULL;
 342        }
 343
 344        pa->wpos = wpos;
 345        pa->dead -= incr;
 346        pa->incr += incr;
 347    }
 348
 349 exit:
 350    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 351    return NULL;
 352}
 353
 354static int qpa_run_in (HWVoiceIn *hw)
 355{
 356    int live, incr, dead;
 357    PAVoiceIn *pa = (PAVoiceIn *) hw;
 358
 359    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
 360        return 0;
 361    }
 362
 363    live = audio_pcm_hw_get_live_in (hw);
 364    dead = hw->samples - live;
 365    incr = audio_MIN (dead, pa->incr);
 366    pa->incr -= incr;
 367    pa->dead = dead - incr;
 368    hw->wpos = pa->wpos;
 369    if (pa->dead > 0) {
 370        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 371    }
 372    else {
 373        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
 374    }
 375    return incr;
 376}
 377
 378static int qpa_read (SWVoiceIn *sw, void *buf, int len)
 379{
 380    return audio_pcm_sw_read (sw, buf, len);
 381}
 382
 383static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
 384{
 385    int format;
 386
 387    switch (afmt) {
 388    case AUD_FMT_S8:
 389    case AUD_FMT_U8:
 390        format = PA_SAMPLE_U8;
 391        break;
 392    case AUD_FMT_S16:
 393    case AUD_FMT_U16:
 394        format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
 395        break;
 396    case AUD_FMT_S32:
 397    case AUD_FMT_U32:
 398        format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
 399        break;
 400    default:
 401        dolog ("Internal logic error: Bad audio format %d\n", afmt);
 402        format = PA_SAMPLE_U8;
 403        break;
 404    }
 405    return format;
 406}
 407
 408static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 409{
 410    switch (fmt) {
 411    case PA_SAMPLE_U8:
 412        return AUD_FMT_U8;
 413    case PA_SAMPLE_S16BE:
 414        *endianness = 1;
 415        return AUD_FMT_S16;
 416    case PA_SAMPLE_S16LE:
 417        *endianness = 0;
 418        return AUD_FMT_S16;
 419    case PA_SAMPLE_S32BE:
 420        *endianness = 1;
 421        return AUD_FMT_S32;
 422    case PA_SAMPLE_S32LE:
 423        *endianness = 0;
 424        return AUD_FMT_S32;
 425    default:
 426        dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
 427        return AUD_FMT_U8;
 428    }
 429}
 430
 431static void context_state_cb (pa_context *c, void *userdata)
 432{
 433    paaudio *g = &glob_paaudio;
 434
 435    switch (pa_context_get_state(c)) {
 436    case PA_CONTEXT_READY:
 437    case PA_CONTEXT_TERMINATED:
 438    case PA_CONTEXT_FAILED:
 439        pa_threaded_mainloop_signal (g->mainloop, 0);
 440        break;
 441
 442    case PA_CONTEXT_UNCONNECTED:
 443    case PA_CONTEXT_CONNECTING:
 444    case PA_CONTEXT_AUTHORIZING:
 445    case PA_CONTEXT_SETTING_NAME:
 446        break;
 447    }
 448}
 449
 450static void stream_state_cb (pa_stream *s, void * userdata)
 451{
 452    paaudio *g = &glob_paaudio;
 453
 454    switch (pa_stream_get_state (s)) {
 455
 456    case PA_STREAM_READY:
 457    case PA_STREAM_FAILED:
 458    case PA_STREAM_TERMINATED:
 459        pa_threaded_mainloop_signal (g->mainloop, 0);
 460        break;
 461
 462    case PA_STREAM_UNCONNECTED:
 463    case PA_STREAM_CREATING:
 464        break;
 465    }
 466}
 467
 468static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
 469{
 470    paaudio *g = &glob_paaudio;
 471
 472    pa_threaded_mainloop_signal (g->mainloop, 0);
 473}
 474
 475static pa_stream *qpa_simple_new (
 476        const char *server,
 477        const char *name,
 478        pa_stream_direction_t dir,
 479        const char *dev,
 480        const char *stream_name,
 481        const pa_sample_spec *ss,
 482        const pa_channel_map *map,
 483        const pa_buffer_attr *attr,
 484        int *rerror)
 485{
 486    paaudio *g = &glob_paaudio;
 487    int r;
 488    pa_stream *stream;
 489
 490    pa_threaded_mainloop_lock (g->mainloop);
 491
 492    stream = pa_stream_new (g->context, name, ss, map);
 493    if (!stream) {
 494        goto fail;
 495    }
 496
 497    pa_stream_set_state_callback (stream, stream_state_cb, g);
 498    pa_stream_set_read_callback (stream, stream_request_cb, g);
 499    pa_stream_set_write_callback (stream, stream_request_cb, g);
 500
 501    if (dir == PA_STREAM_PLAYBACK) {
 502        r = pa_stream_connect_playback (stream, dev, attr,
 503                                        PA_STREAM_INTERPOLATE_TIMING
 504#ifdef PA_STREAM_ADJUST_LATENCY
 505                                        |PA_STREAM_ADJUST_LATENCY
 506#endif
 507                                        |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
 508    } else {
 509        r = pa_stream_connect_record (stream, dev, attr,
 510                                      PA_STREAM_INTERPOLATE_TIMING
 511#ifdef PA_STREAM_ADJUST_LATENCY
 512                                      |PA_STREAM_ADJUST_LATENCY
 513#endif
 514                                      |PA_STREAM_AUTO_TIMING_UPDATE);
 515    }
 516
 517    if (r < 0) {
 518      goto fail;
 519    }
 520
 521    pa_threaded_mainloop_unlock (g->mainloop);
 522
 523    return stream;
 524
 525fail:
 526    pa_threaded_mainloop_unlock (g->mainloop);
 527
 528    if (stream) {
 529        pa_stream_unref (stream);
 530    }
 531
 532    *rerror = pa_context_errno (g->context);
 533
 534    return NULL;
 535}
 536
 537static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
 538{
 539    int error;
 540    static pa_sample_spec ss;
 541    static pa_buffer_attr ba;
 542    struct audsettings obt_as = *as;
 543    PAVoiceOut *pa = (PAVoiceOut *) hw;
 544
 545    ss.format = audfmt_to_pa (as->fmt, as->endianness);
 546    ss.channels = as->nchannels;
 547    ss.rate = as->freq;
 548
 549    /*
 550     * qemu audio tick runs at 250 Hz (by default), so processing
 551     * data chunks worth 4 ms of sound should be a good fit.
 552     */
 553    ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
 554    ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
 555    ba.maxlength = -1;
 556    ba.prebuf = -1;
 557
 558    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 559
 560    pa->stream = qpa_simple_new (
 561        glob_paaudio.server,
 562        "qemu",
 563        PA_STREAM_PLAYBACK,
 564        glob_paaudio.sink,
 565        "pcm.playback",
 566        &ss,
 567        NULL,                   /* channel map */
 568        &ba,                    /* buffering attributes */
 569        &error
 570        );
 571    if (!pa->stream) {
 572        qpa_logerr (error, "pa_simple_new for playback failed\n");
 573        goto fail1;
 574    }
 575
 576    audio_pcm_init_info (&hw->info, &obt_as);
 577    hw->samples = glob_paaudio.samples;
 578    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 579    pa->rpos = hw->rpos;
 580    if (!pa->pcm_buf) {
 581        dolog ("Could not allocate buffer (%d bytes)\n",
 582               hw->samples << hw->info.shift);
 583        goto fail2;
 584    }
 585
 586    if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
 587        goto fail3;
 588    }
 589
 590    return 0;
 591
 592 fail3:
 593    g_free (pa->pcm_buf);
 594    pa->pcm_buf = NULL;
 595 fail2:
 596    if (pa->stream) {
 597        pa_stream_unref (pa->stream);
 598        pa->stream = NULL;
 599    }
 600 fail1:
 601    return -1;
 602}
 603
 604static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
 605{
 606    int error;
 607    static pa_sample_spec ss;
 608    struct audsettings obt_as = *as;
 609    PAVoiceIn *pa = (PAVoiceIn *) hw;
 610
 611    ss.format = audfmt_to_pa (as->fmt, as->endianness);
 612    ss.channels = as->nchannels;
 613    ss.rate = as->freq;
 614
 615    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 616
 617    pa->stream = qpa_simple_new (
 618        glob_paaudio.server,
 619        "qemu",
 620        PA_STREAM_RECORD,
 621        glob_paaudio.source,
 622        "pcm.capture",
 623        &ss,
 624        NULL,                   /* channel map */
 625        NULL,                   /* buffering attributes */
 626        &error
 627        );
 628    if (!pa->stream) {
 629        qpa_logerr (error, "pa_simple_new for capture failed\n");
 630        goto fail1;
 631    }
 632
 633    audio_pcm_init_info (&hw->info, &obt_as);
 634    hw->samples = glob_paaudio.samples;
 635    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 636    pa->wpos = hw->wpos;
 637    if (!pa->pcm_buf) {
 638        dolog ("Could not allocate buffer (%d bytes)\n",
 639               hw->samples << hw->info.shift);
 640        goto fail2;
 641    }
 642
 643    if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
 644        goto fail3;
 645    }
 646
 647    return 0;
 648
 649 fail3:
 650    g_free (pa->pcm_buf);
 651    pa->pcm_buf = NULL;
 652 fail2:
 653    if (pa->stream) {
 654        pa_stream_unref (pa->stream);
 655        pa->stream = NULL;
 656    }
 657 fail1:
 658    return -1;
 659}
 660
 661static void qpa_fini_out (HWVoiceOut *hw)
 662{
 663    void *ret;
 664    PAVoiceOut *pa = (PAVoiceOut *) hw;
 665
 666    audio_pt_lock (&pa->pt, AUDIO_FUNC);
 667    pa->done = 1;
 668    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 669    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 670
 671    if (pa->stream) {
 672        pa_stream_unref (pa->stream);
 673        pa->stream = NULL;
 674    }
 675
 676    audio_pt_fini (&pa->pt, AUDIO_FUNC);
 677    g_free (pa->pcm_buf);
 678    pa->pcm_buf = NULL;
 679}
 680
 681static void qpa_fini_in (HWVoiceIn *hw)
 682{
 683    void *ret;
 684    PAVoiceIn *pa = (PAVoiceIn *) hw;
 685
 686    audio_pt_lock (&pa->pt, AUDIO_FUNC);
 687    pa->done = 1;
 688    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
 689    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 690
 691    if (pa->stream) {
 692        pa_stream_unref (pa->stream);
 693        pa->stream = NULL;
 694    }
 695
 696    audio_pt_fini (&pa->pt, AUDIO_FUNC);
 697    g_free (pa->pcm_buf);
 698    pa->pcm_buf = NULL;
 699}
 700
 701static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
 702{
 703    PAVoiceOut *pa = (PAVoiceOut *) hw;
 704    pa_operation *op;
 705    pa_cvolume v;
 706    paaudio *g = &glob_paaudio;
 707
 708#ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */
 709    pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
 710#endif
 711
 712    switch (cmd) {
 713    case VOICE_VOLUME:
 714        {
 715            SWVoiceOut *sw;
 716            va_list ap;
 717
 718            va_start (ap, cmd);
 719            sw = va_arg (ap, SWVoiceOut *);
 720            va_end (ap);
 721
 722            v.channels = 2;
 723            v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
 724            v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
 725
 726            pa_threaded_mainloop_lock (g->mainloop);
 727
 728            op = pa_context_set_sink_input_volume (g->context,
 729                pa_stream_get_index (pa->stream),
 730                &v, NULL, NULL);
 731            if (!op)
 732                qpa_logerr (pa_context_errno (g->context),
 733                            "set_sink_input_volume() failed\n");
 734            else
 735                pa_operation_unref (op);
 736
 737            op = pa_context_set_sink_input_mute (g->context,
 738                pa_stream_get_index (pa->stream),
 739               sw->vol.mute, NULL, NULL);
 740            if (!op) {
 741                qpa_logerr (pa_context_errno (g->context),
 742                            "set_sink_input_mute() failed\n");
 743            } else {
 744                pa_operation_unref (op);
 745            }
 746
 747            pa_threaded_mainloop_unlock (g->mainloop);
 748        }
 749    }
 750    return 0;
 751}
 752
 753static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 754{
 755    PAVoiceIn *pa = (PAVoiceIn *) hw;
 756    pa_operation *op;
 757    pa_cvolume v;
 758    paaudio *g = &glob_paaudio;
 759
 760#ifdef PA_CHECK_VERSION
 761    pa_cvolume_init (&v);
 762#endif
 763
 764    switch (cmd) {
 765    case VOICE_VOLUME:
 766        {
 767            SWVoiceIn *sw;
 768            va_list ap;
 769
 770            va_start (ap, cmd);
 771            sw = va_arg (ap, SWVoiceIn *);
 772            va_end (ap);
 773
 774            v.channels = 2;
 775            v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
 776            v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
 777
 778            pa_threaded_mainloop_lock (g->mainloop);
 779
 780            /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
 781            op = pa_context_set_source_volume_by_index (g->context,
 782                pa_stream_get_device_index (pa->stream),
 783                &v, NULL, NULL);
 784            if (!op) {
 785                qpa_logerr (pa_context_errno (g->context),
 786                            "set_source_volume() failed\n");
 787            } else {
 788                pa_operation_unref(op);
 789            }
 790
 791            op = pa_context_set_source_mute_by_index (g->context,
 792                pa_stream_get_index (pa->stream),
 793                sw->vol.mute, NULL, NULL);
 794            if (!op) {
 795                qpa_logerr (pa_context_errno (g->context),
 796                            "set_source_mute() failed\n");
 797            } else {
 798                pa_operation_unref (op);
 799            }
 800
 801            pa_threaded_mainloop_unlock (g->mainloop);
 802        }
 803    }
 804    return 0;
 805}
 806
 807/* common */
 808static void *qpa_audio_init (void)
 809{
 810    paaudio *g = &glob_paaudio;
 811
 812    g->mainloop = pa_threaded_mainloop_new ();
 813    if (!g->mainloop) {
 814        goto fail;
 815    }
 816
 817    g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
 818    if (!g->context) {
 819        goto fail;
 820    }
 821
 822    pa_context_set_state_callback (g->context, context_state_cb, g);
 823
 824    if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
 825        qpa_logerr (pa_context_errno (g->context),
 826                    "pa_context_connect() failed\n");
 827        goto fail;
 828    }
 829
 830    pa_threaded_mainloop_lock (g->mainloop);
 831
 832    if (pa_threaded_mainloop_start (g->mainloop) < 0) {
 833        goto unlock_and_fail;
 834    }
 835
 836    for (;;) {
 837        pa_context_state_t state;
 838
 839        state = pa_context_get_state (g->context);
 840
 841        if (state == PA_CONTEXT_READY) {
 842            break;
 843        }
 844
 845        if (!PA_CONTEXT_IS_GOOD (state)) {
 846            qpa_logerr (pa_context_errno (g->context),
 847                        "Wrong context state\n");
 848            goto unlock_and_fail;
 849        }
 850
 851        /* Wait until the context is ready */
 852        pa_threaded_mainloop_wait (g->mainloop);
 853    }
 854
 855    pa_threaded_mainloop_unlock (g->mainloop);
 856
 857    return &glob_paaudio;
 858
 859unlock_and_fail:
 860    pa_threaded_mainloop_unlock (g->mainloop);
 861fail:
 862    AUD_log (AUDIO_CAP, "Failed to initialize PA context");
 863    return NULL;
 864}
 865
 866static void qpa_audio_fini (void *opaque)
 867{
 868    paaudio *g = opaque;
 869
 870    if (g->mainloop) {
 871        pa_threaded_mainloop_stop (g->mainloop);
 872    }
 873
 874    if (g->context) {
 875        pa_context_disconnect (g->context);
 876        pa_context_unref (g->context);
 877        g->context = NULL;
 878    }
 879
 880    if (g->mainloop) {
 881        pa_threaded_mainloop_free (g->mainloop);
 882    }
 883
 884    g->mainloop = NULL;
 885}
 886
 887struct audio_option qpa_options[] = {
 888    {
 889        .name  = "SAMPLES",
 890        .tag   = AUD_OPT_INT,
 891        .valp  = &glob_paaudio.samples,
 892        .descr = "buffer size in samples"
 893    },
 894    {
 895        .name  = "SERVER",
 896        .tag   = AUD_OPT_STR,
 897        .valp  = &glob_paaudio.server,
 898        .descr = "server address"
 899    },
 900    {
 901        .name  = "SINK",
 902        .tag   = AUD_OPT_STR,
 903        .valp  = &glob_paaudio.sink,
 904        .descr = "sink device name"
 905    },
 906    {
 907        .name  = "SOURCE",
 908        .tag   = AUD_OPT_STR,
 909        .valp  = &glob_paaudio.source,
 910        .descr = "source device name"
 911    },
 912    { /* End of list */ }
 913};
 914
 915static struct audio_pcm_ops qpa_pcm_ops = {
 916    .init_out = qpa_init_out,
 917    .fini_out = qpa_fini_out,
 918    .run_out  = qpa_run_out,
 919    .write    = qpa_write,
 920    .ctl_out  = qpa_ctl_out,
 921
 922    .init_in  = qpa_init_in,
 923    .fini_in  = qpa_fini_in,
 924    .run_in   = qpa_run_in,
 925    .read     = qpa_read,
 926    .ctl_in   = qpa_ctl_in
 927};
 928
 929struct audio_driver pa_audio_driver = {
 930    .name           = "pa",
 931    .descr          = "http://www.pulseaudio.org/",
 932    .options        = qpa_options,
 933    .init           = qpa_audio_init,
 934    .fini           = qpa_audio_fini,
 935    .pcm_ops        = &qpa_pcm_ops,
 936    .can_be_default = 1,
 937    .max_voices_out = INT_MAX,
 938    .max_voices_in  = INT_MAX,
 939    .voice_size_out = sizeof (PAVoiceOut),
 940    .voice_size_in  = sizeof (PAVoiceIn),
 941    .ctl_caps       = VOICE_VOLUME_CAP
 942};
 943