qemu/audio/sndioaudio.c
<<
>>
Prefs
   1/*
   2 * SPDX-License-Identifier: ISC
   3 *
   4 * Copyright (c) 2019 Alexandre Ratchov <alex@caoua.org>
   5 */
   6
   7/*
   8 * TODO :
   9 *
  10 * Use a single device and open it in full-duplex rather than
  11 * opening it twice (once for playback once for recording).
  12 *
  13 * This is the only way to ensure that playback doesn't drift with respect
  14 * to recording, which is what guest systems expect.
  15 */
  16
  17#include "qemu/osdep.h"
  18#include <poll.h>
  19#include <sndio.h>
  20#include "qemu/main-loop.h"
  21#include "audio.h"
  22#include "trace.h"
  23
  24#define AUDIO_CAP "sndio"
  25#include "audio_int.h"
  26
  27/* default latency in microseconds if no option is set */
  28#define SNDIO_LATENCY_US   50000
  29
  30typedef struct SndioVoice {
  31    union {
  32        HWVoiceOut out;
  33        HWVoiceIn in;
  34    } hw;
  35    struct sio_par par;
  36    struct sio_hdl *hdl;
  37    struct pollfd *pfds;
  38    struct pollindex {
  39        struct SndioVoice *self;
  40        int index;
  41    } *pindexes;
  42    unsigned char *buf;
  43    size_t buf_size;
  44    size_t sndio_pos;
  45    size_t qemu_pos;
  46    unsigned int mode;
  47    unsigned int nfds;
  48    bool enabled;
  49} SndioVoice;
  50
  51typedef struct SndioConf {
  52    const char *devname;
  53    unsigned int latency;
  54} SndioConf;
  55
  56/* needed for forward reference */
  57static void sndio_poll_in(void *arg);
  58static void sndio_poll_out(void *arg);
  59
  60/*
  61 * stop polling descriptors
  62 */
  63static void sndio_poll_clear(SndioVoice *self)
  64{
  65    struct pollfd *pfd;
  66    int i;
  67
  68    for (i = 0; i < self->nfds; i++) {
  69        pfd = &self->pfds[i];
  70        qemu_set_fd_handler(pfd->fd, NULL, NULL, NULL);
  71    }
  72
  73    self->nfds = 0;
  74}
  75
  76/*
  77 * write data to the device until it blocks or
  78 * all of our buffered data is written
  79 */
  80static void sndio_write(SndioVoice *self)
  81{
  82    size_t todo, n;
  83
  84    todo = self->qemu_pos - self->sndio_pos;
  85
  86    /*
  87     * transfer data to device, until it blocks
  88     */
  89    while (todo > 0) {
  90        n = sio_write(self->hdl, self->buf + self->sndio_pos, todo);
  91        if (n == 0) {
  92            break;
  93        }
  94        self->sndio_pos += n;
  95        todo -= n;
  96    }
  97
  98    if (self->sndio_pos == self->buf_size) {
  99        /*
 100         * we complete the block
 101         */
 102        self->sndio_pos = 0;
 103        self->qemu_pos = 0;
 104    }
 105}
 106
 107/*
 108 * read data from the device until it blocks or
 109 * there no room any longer
 110 */
 111static void sndio_read(SndioVoice *self)
 112{
 113    size_t todo, n;
 114
 115    todo = self->buf_size - self->sndio_pos;
 116
 117    /*
 118     * transfer data from the device, until it blocks
 119     */
 120    while (todo > 0) {
 121        n = sio_read(self->hdl, self->buf + self->sndio_pos, todo);
 122        if (n == 0) {
 123            break;
 124        }
 125        self->sndio_pos += n;
 126        todo -= n;
 127    }
 128}
 129
 130/*
 131 * Set handlers for all descriptors libsndio needs to
 132 * poll
 133 */
 134static void sndio_poll_wait(SndioVoice *self)
 135{
 136    struct pollfd *pfd;
 137    int events, i;
 138
 139    events = 0;
 140    if (self->mode == SIO_PLAY) {
 141        if (self->sndio_pos < self->qemu_pos) {
 142            events |= POLLOUT;
 143        }
 144    } else {
 145        if (self->sndio_pos < self->buf_size) {
 146            events |= POLLIN;
 147        }
 148    }
 149
 150    /*
 151     * fill the given array of descriptors with the events sndio
 152     * wants, they are different from our 'event' variable because
 153     * sndio may use descriptors internally.
 154     */
 155    self->nfds = sio_pollfd(self->hdl, self->pfds, events);
 156
 157    for (i = 0; i < self->nfds; i++) {
 158        pfd = &self->pfds[i];
 159        if (pfd->fd < 0) {
 160            continue;
 161        }
 162        qemu_set_fd_handler(pfd->fd,
 163            (pfd->events & POLLIN) ? sndio_poll_in : NULL,
 164            (pfd->events & POLLOUT) ? sndio_poll_out : NULL,
 165            &self->pindexes[i]);
 166        pfd->revents = 0;
 167    }
 168}
 169
 170/*
 171 * call-back called when one of the descriptors
 172 * became readable or writable
 173 */
 174static void sndio_poll_event(SndioVoice *self, int index, int event)
 175{
 176    int revents;
 177
 178    /*
 179     * ensure we're not called twice this cycle
 180     */
 181    sndio_poll_clear(self);
 182
 183    /*
 184     * make self->pfds[] look as we're returning from poll syscal,
 185     * this is how sio_revents expects events to be.
 186     */
 187    self->pfds[index].revents = event;
 188
 189    /*
 190     * tell sndio to handle events and return whether we can read or
 191     * write without blocking.
 192     */
 193    revents = sio_revents(self->hdl, self->pfds);
 194    if (self->mode == SIO_PLAY) {
 195        if (revents & POLLOUT) {
 196            sndio_write(self);
 197        }
 198
 199        if (self->qemu_pos < self->buf_size) {
 200            audio_run(self->hw.out.s, "sndio_out");
 201        }
 202    } else {
 203        if (revents & POLLIN) {
 204            sndio_read(self);
 205        }
 206
 207        if (self->qemu_pos < self->sndio_pos) {
 208            audio_run(self->hw.in.s, "sndio_in");
 209        }
 210    }
 211
 212    /*
 213     * audio_run() may have changed state
 214     */
 215    if (self->enabled) {
 216        sndio_poll_wait(self);
 217    }
 218}
 219
 220/*
 221 * return the upper limit of the amount of free play buffer space
 222 */
 223static size_t sndio_buffer_get_free(HWVoiceOut *hw)
 224{
 225    SndioVoice *self = (SndioVoice *) hw;
 226
 227    return self->buf_size - self->qemu_pos;
 228}
 229
 230/*
 231 * return a buffer where data to play can be stored,
 232 * its size is stored in the location pointed by the size argument.
 233 */
 234static void *sndio_get_buffer_out(HWVoiceOut *hw, size_t *size)
 235{
 236    SndioVoice *self = (SndioVoice *) hw;
 237
 238    *size = self->buf_size - self->qemu_pos;
 239    return self->buf + self->qemu_pos;
 240}
 241
 242/*
 243 * put back to sndio back-end a buffer returned by sndio_get_buffer_out()
 244 */
 245static size_t sndio_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
 246{
 247    SndioVoice *self = (SndioVoice *) hw;
 248
 249    self->qemu_pos += size;
 250    sndio_poll_wait(self);
 251    return size;
 252}
 253
 254/*
 255 * return a buffer from where recorded data is available,
 256 * its size is stored in the location pointed by the size argument.
 257 * it may not exceed the initial value of "*size".
 258 */
 259static void *sndio_get_buffer_in(HWVoiceIn *hw, size_t *size)
 260{
 261    SndioVoice *self = (SndioVoice *) hw;
 262    size_t todo, max_todo;
 263
 264    /*
 265     * unlike the get_buffer_out() method, get_buffer_in()
 266     * must return a buffer of at most the given size, see audio.c
 267     */
 268    max_todo = *size;
 269
 270    todo = self->sndio_pos - self->qemu_pos;
 271    if (todo > max_todo) {
 272        todo = max_todo;
 273    }
 274
 275    *size = todo;
 276    return self->buf + self->qemu_pos;
 277}
 278
 279/*
 280 * discard the given amount of recorded data
 281 */
 282static void sndio_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
 283{
 284    SndioVoice *self = (SndioVoice *) hw;
 285
 286    self->qemu_pos += size;
 287    if (self->qemu_pos == self->buf_size) {
 288        self->qemu_pos = 0;
 289        self->sndio_pos = 0;
 290    }
 291    sndio_poll_wait(self);
 292}
 293
 294/*
 295 * call-back called when one of our descriptors becomes writable
 296 */
 297static void sndio_poll_out(void *arg)
 298{
 299    struct pollindex *pindex = (struct pollindex *) arg;
 300
 301    sndio_poll_event(pindex->self, pindex->index, POLLOUT);
 302}
 303
 304/*
 305 * call-back called when one of our descriptors becomes readable
 306 */
 307static void sndio_poll_in(void *arg)
 308{
 309    struct pollindex *pindex = (struct pollindex *) arg;
 310
 311    sndio_poll_event(pindex->self, pindex->index, POLLIN);
 312}
 313
 314static void sndio_fini(SndioVoice *self)
 315{
 316    if (self->hdl) {
 317        sio_close(self->hdl);
 318        self->hdl = NULL;
 319    }
 320
 321    g_free(self->pfds);
 322    g_free(self->pindexes);
 323    g_free(self->buf);
 324}
 325
 326static int sndio_init(SndioVoice *self,
 327                      struct audsettings *as, int mode, Audiodev *dev)
 328{
 329    AudiodevSndioOptions *opts = &dev->u.sndio;
 330    unsigned long long latency;
 331    const char *dev_name;
 332    struct sio_par req;
 333    unsigned int nch;
 334    int i, nfds;
 335
 336    dev_name = opts->dev ?: SIO_DEVANY;
 337    latency = opts->has_latency ? opts->latency : SNDIO_LATENCY_US;
 338
 339    /* open the device in non-blocking mode */
 340    self->hdl = sio_open(dev_name, mode, 1);
 341    if (self->hdl == NULL) {
 342        dolog("failed to open device\n");
 343        return -1;
 344    }
 345
 346    self->mode = mode;
 347
 348    sio_initpar(&req);
 349
 350    switch (as->fmt) {
 351    case AUDIO_FORMAT_S8:
 352        req.bits = 8;
 353        req.sig = 1;
 354        break;
 355    case AUDIO_FORMAT_U8:
 356        req.bits = 8;
 357        req.sig = 0;
 358        break;
 359    case AUDIO_FORMAT_S16:
 360        req.bits = 16;
 361        req.sig = 1;
 362        break;
 363    case AUDIO_FORMAT_U16:
 364        req.bits = 16;
 365        req.sig = 0;
 366        break;
 367    case AUDIO_FORMAT_S32:
 368        req.bits = 32;
 369        req.sig = 1;
 370        break;
 371    case AUDIO_FORMAT_U32:
 372        req.bits = 32;
 373        req.sig = 0;
 374        break;
 375    default:
 376        dolog("unknown audio sample format\n");
 377        return -1;
 378    }
 379
 380    if (req.bits > 8) {
 381        req.le = as->endianness ? 0 : 1;
 382    }
 383
 384    req.rate = as->freq;
 385    if (mode == SIO_PLAY) {
 386        req.pchan = as->nchannels;
 387    } else {
 388        req.rchan = as->nchannels;
 389    }
 390
 391    /* set on-device buffer size */
 392    req.appbufsz = req.rate * latency / 1000000;
 393
 394    if (!sio_setpar(self->hdl, &req)) {
 395        dolog("failed set audio params\n");
 396        goto fail;
 397    }
 398
 399    if (!sio_getpar(self->hdl, &self->par)) {
 400        dolog("failed get audio params\n");
 401        goto fail;
 402    }
 403
 404    nch = (mode == SIO_PLAY) ? self->par.pchan : self->par.rchan;
 405
 406    /*
 407     * With the default setup, sndio supports any combination of parameters
 408     * so these checks are mostly to catch configuration errors.
 409     */
 410    if (self->par.bits != req.bits || self->par.bps != req.bits / 8 ||
 411        self->par.sig != req.sig || (req.bits > 8 && self->par.le != req.le) ||
 412        self->par.rate != as->freq || nch != as->nchannels) {
 413        dolog("unsupported audio params\n");
 414        goto fail;
 415    }
 416
 417    /*
 418     * we use one block as buffer size; this is how
 419     * transfers get well aligned
 420     */
 421    self->buf_size = self->par.round * self->par.bps * nch;
 422
 423    self->buf = g_malloc(self->buf_size);
 424    if (self->buf == NULL) {
 425        dolog("failed to allocate audio buffer\n");
 426        goto fail;
 427    }
 428
 429    nfds = sio_nfds(self->hdl);
 430
 431    self->pfds = g_malloc_n(nfds, sizeof(struct pollfd));
 432    if (self->pfds == NULL) {
 433        dolog("failed to allocate pollfd structures\n");
 434        goto fail;
 435    }
 436
 437    self->pindexes = g_malloc_n(nfds, sizeof(struct pollindex));
 438    if (self->pindexes == NULL) {
 439        dolog("failed to allocate pollindex structures\n");
 440        goto fail;
 441    }
 442
 443    for (i = 0; i < nfds; i++) {
 444        self->pindexes[i].self = self;
 445        self->pindexes[i].index = i;
 446    }
 447
 448    return 0;
 449fail:
 450    sndio_fini(self);
 451    return -1;
 452}
 453
 454static void sndio_enable(SndioVoice *self, bool enable)
 455{
 456    if (enable) {
 457        sio_start(self->hdl);
 458        self->enabled = true;
 459        sndio_poll_wait(self);
 460    } else {
 461        self->enabled = false;
 462        sndio_poll_clear(self);
 463        sio_stop(self->hdl);
 464    }
 465}
 466
 467static void sndio_enable_out(HWVoiceOut *hw, bool enable)
 468{
 469    SndioVoice *self = (SndioVoice *) hw;
 470
 471    sndio_enable(self, enable);
 472}
 473
 474static void sndio_enable_in(HWVoiceIn *hw, bool enable)
 475{
 476    SndioVoice *self = (SndioVoice *) hw;
 477
 478    sndio_enable(self, enable);
 479}
 480
 481static int sndio_init_out(HWVoiceOut *hw, struct audsettings *as, void *opaque)
 482{
 483    SndioVoice *self = (SndioVoice *) hw;
 484
 485    if (sndio_init(self, as, SIO_PLAY, opaque) == -1) {
 486        return -1;
 487    }
 488
 489    audio_pcm_init_info(&hw->info, as);
 490    hw->samples = self->par.round;
 491    return 0;
 492}
 493
 494static int sndio_init_in(HWVoiceIn *hw, struct audsettings *as, void *opaque)
 495{
 496    SndioVoice *self = (SndioVoice *) hw;
 497
 498    if (sndio_init(self, as, SIO_REC, opaque) == -1) {
 499        return -1;
 500    }
 501
 502    audio_pcm_init_info(&hw->info, as);
 503    hw->samples = self->par.round;
 504    return 0;
 505}
 506
 507static void sndio_fini_out(HWVoiceOut *hw)
 508{
 509    SndioVoice *self = (SndioVoice *) hw;
 510
 511    sndio_fini(self);
 512}
 513
 514static void sndio_fini_in(HWVoiceIn *hw)
 515{
 516    SndioVoice *self = (SndioVoice *) hw;
 517
 518    sndio_fini(self);
 519}
 520
 521static void *sndio_audio_init(Audiodev *dev)
 522{
 523    assert(dev->driver == AUDIODEV_DRIVER_SNDIO);
 524    return dev;
 525}
 526
 527static void sndio_audio_fini(void *opaque)
 528{
 529}
 530
 531static struct audio_pcm_ops sndio_pcm_ops = {
 532    .init_out        = sndio_init_out,
 533    .fini_out        = sndio_fini_out,
 534    .enable_out      = sndio_enable_out,
 535    .write           = audio_generic_write,
 536    .buffer_get_free = sndio_buffer_get_free,
 537    .get_buffer_out  = sndio_get_buffer_out,
 538    .put_buffer_out  = sndio_put_buffer_out,
 539    .init_in         = sndio_init_in,
 540    .fini_in         = sndio_fini_in,
 541    .read            = audio_generic_read,
 542    .enable_in       = sndio_enable_in,
 543    .get_buffer_in   = sndio_get_buffer_in,
 544    .put_buffer_in   = sndio_put_buffer_in,
 545};
 546
 547static struct audio_driver sndio_audio_driver = {
 548    .name           = "sndio",
 549    .descr          = "sndio https://sndio.org",
 550    .init           = sndio_audio_init,
 551    .fini           = sndio_audio_fini,
 552    .pcm_ops        = &sndio_pcm_ops,
 553    .can_be_default = 1,
 554    .max_voices_out = INT_MAX,
 555    .max_voices_in  = INT_MAX,
 556    .voice_size_out = sizeof(SndioVoice),
 557    .voice_size_in  = sizeof(SndioVoice)
 558};
 559
 560static void register_audio_sndio(void)
 561{
 562    audio_driver_register(&sndio_audio_driver);
 563}
 564
 565type_init(register_audio_sndio);
 566