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