From nobody Wed Jun 26 05:45:10 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1583333965; cv=none; d=zohomail.com; s=zohoarc; b=Z130jWVWbaGAlU153KfE5B+Nb7BoUlD7Sc+e9c6L4iwR0sz8mRfuTbhKZlDdOd9PHGBMUC9+cXKSigjYp1w0RJswNYos0nTOQJcQkW/ID4dZ3+f1nA547hMx9wbH5TOve6Pih+wTKFI9hi/33bRNomM+4kTU0OExZKHqvzEZQzI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1583333965; h=Content-Type:Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:To; bh=mo65BPI/EcbSxuS5vPc/9590sFacD00aS4w9uQnJiUg=; b=N7CAnGzKH/uu8ew4hfPUd9MOzCxNtkkoRB/voz63D5th8YpwjTzPgsFfqCQa/hRMBg1jH2IItBpX61pe8lX73BU/51S0BG5ffJ9iCW1f692dznPGBi34zOVLxPqGTXMlA6LB6V0S4Adn1g8EOcGdPlUpriCMSkYT1uT6S/+mGiE= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1583333965630983.7231603822032; Wed, 4 Mar 2020 06:59:25 -0800 (PST) Received: from localhost ([::1]:35320 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j9VUa-0002LE-8b for importer@patchew.org; Wed, 04 Mar 2020 09:59:24 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:35358) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j9VSR-0008K1-DE for qemu-devel@nongnu.org; Wed, 04 Mar 2020 09:57:14 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1j9VSN-00072J-NJ for qemu-devel@nongnu.org; Wed, 04 Mar 2020 09:57:10 -0500 Received: from speedy.comstyle.com ([2607:f938:3000:8::2]:3504 helo=mail.comstyle.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1j9VSN-0006vG-Dr for qemu-devel@nongnu.org; Wed, 04 Mar 2020 09:57:07 -0500 Received: from mail.comstyle.com (localhost [127.0.0.1]) by mail.comstyle.com (Postfix) with ESMTP id 48XcHt13Wsz8Pb1; Wed, 4 Mar 2020 09:50:34 -0500 (EST) Received: from humpty.home.comstyle.com (unknown [IPv6:2001:470:b0db:100:245e:2103:c7c7:5edc]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: brad) by mail.comstyle.com (Postfix) with ESMTPSA id 48XcHs6ldSz8Pb0; Wed, 4 Mar 2020 09:50:33 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=comstyle.com; h=date:from :to:cc:subject:message-id:mime-version:content-type; s= selector1; bh=UAA6XKsj0Oao+7M4eHeaqCntJi0=; b=o2lLm/wWzIud44hSbL T8ImrC9OBT1e5e1g92ELQH5FtXT0f/xgLB9lNA2uLSrcuQserjMg/B9vypywrKU7 4xcKoZZy6YSn+Oyb+EKu+LhZsbSJWPBiZd/U4YwKGZnr4RAu/wIBo7stM1Dn3sFQ VJN1D4UGcfM+RLrQ+wD2vIJpI= DomainKey-Signature: a=rsa-sha1; c=nofws; d=comstyle.com; h=date:from:to :cc:subject:message-id:mime-version:content-type; q=dns; s= selector1; b=HNAj9iXBIzDjvumXfn29cQeZPcBJ94GrKXlhJ0aytJDHQ/s4XhD oyCgzFxPX4ZR5tJKIe2Lho2qPlAEA9pQKXqg4XA1QCJov0Kho2y+us47WzNAB8PJ tt/RL08mr3TV/WTyT7i6uiYglNC4JVvwBIj/YssQJx7yAQSnpZ/D8JL0= Date: Wed, 4 Mar 2020 09:50:03 -0500 From: Brad Smith To: qemu-devel@nongnu.org Subject: [PATCH] audio: Add sndio backend Message-ID: <20200304145003.GB15649@humpty.home.comstyle.com> MIME-Version: 1.0 Content-Disposition: inline X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f938:3000:8::2 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Gerd Hoffmann Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a sndio backend. sndio is the native API used by OpenBSD, although it has been ported to other *BSD's and Linux (packages for Ubuntu, Debian, Void, Arch, etc.). The C code is from Alexandre Ratchov and the rest of the bits are from me. Signed-off-by: Alexandre Ratchov Signed-off-by: Brad Smith diff --git a/audio/Makefile.objs b/audio/Makefile.objs index d7490a379f..e6b86aacc7 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -28,3 +28,8 @@ common-obj-$(CONFIG_AUDIO_SDL) +=3D sdl.mo sdl.mo-objs =3D sdlaudio.o sdl.mo-cflags :=3D $(SDL_CFLAGS) sdl.mo-libs :=3D $(SDL_LIBS) + +# sndio module +common-obj-$(CONFIG_AUDIO_SNDIO) +=3D sndio.mo +sndio.mo-objs =3D sndioaudio.o +sndio.mo-libs :=3D $(SNDIO_LIBS) diff --git a/audio/audio.c b/audio/audio.c index 9ac9a20c41..6eeaaece5a 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1974,6 +1974,7 @@ void audio_create_pdos(Audiodev *dev) CASE(OSS, oss, Oss); CASE(PA, pa, Pa); CASE(SDL, sdl, ); + CASE(SNDIO, sndio, ); CASE(SPICE, spice, ); CASE(WAV, wav, ); =20 diff --git a/audio/audio_template.h b/audio/audio_template.h index 7013d3041f..b516bae41b 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -336,6 +336,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)= (Audiodev *dev) return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); case AUDIODEV_DRIVER_SDL: return dev->u.sdl.TYPE; + case AUDIODEV_DRIVER_SNDIO: + return dev->u.sndio.TYPE; case AUDIODEV_DRIVER_SPICE: return dev->u.spice.TYPE; case AUDIODEV_DRIVER_WAV: diff --git a/audio/sndioaudio.c b/audio/sndioaudio.c new file mode 100644 index 0000000000..2dbc0ddaee --- /dev/null +++ b/audio/sndioaudio.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2019 Alexandre Ratchov + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * TODO : + * + * Use a single device and open it in full-duplex rather than + * opening it twice (once for playback once for recording). + * + * This is the only way to ensure that playback doesn't drift with respect + * to recording, which is what guest systems expect. + */ + +#include +#include +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/main-loop.h" +#include "audio.h" +#include "trace.h" + +#define AUDIO_CAP "sndio" +#include "audio_int.h" + +/* default latency in ms if no option is set */ +#define SNDIO_LATENCY_US 50000 + +typedef struct SndioVoice { + union { + HWVoiceOut out; + HWVoiceIn in; + } hw; + struct sio_par par; + struct sio_hdl *hdl; + struct pollfd *pfds; + struct pollindex { + struct SndioVoice *self; + int index; + } *pindexes; + unsigned char *buf; + size_t buf_size; + size_t sndio_pos; + size_t qemu_pos; + unsigned int mode; + unsigned int nfds; +} SndioVoice; + +typedef struct SndioConf { + const char *devname; + unsigned int latency; +} SndioConf; + +/* needed for forward reference */ +static void sndio_poll_in(void *arg); +static void sndio_poll_out(void *arg); + +/* + * stop polling descriptors + */ +static void sndio_poll_clear(SndioVoice *self) +{ + struct pollfd *pfd; + int i; + + for (i =3D 0; i < self->nfds; i++) { + pfd =3D &self->pfds[i]; + qemu_set_fd_handler (pfd->fd, NULL, NULL, NULL); + } + + self->nfds =3D 0; +} + +/* + * write data to the device until it blocks or + * all of our buffered data is written + */ +static void sndio_write(SndioVoice *self) +{ + size_t todo, n; + + todo =3D self->qemu_pos - self->sndio_pos; + + /* + * transfer data to device, until it blocks + */ + while (todo > 0) { + n =3D sio_write(self->hdl, self->buf + self->sndio_pos, todo); + if (n =3D=3D 0) { + break; + } + self->sndio_pos +=3D n; + todo -=3D n; + } + + if (self->sndio_pos =3D=3D self->buf_size) { + /* + * we complete the block + */ + self->sndio_pos =3D 0; + self->qemu_pos =3D 0; + } +} + +/* + * read data from the device until it blocks or + * there no room any longer + */ +static void sndio_read(SndioVoice *self) +{ + size_t todo, n; + + todo =3D self->buf_size - self->sndio_pos; + + /* + * transfer data from the device, until it blocks + */ + while (todo > 0) { + n =3D sio_read(self->hdl, self->buf + self->sndio_pos, todo); + if (n =3D=3D 0) { + break; + } + self->sndio_pos +=3D n; + todo -=3D n; + } +} + +/* + * Set handlers for all descriptors libsndio needs to + * poll + */ +static void sndio_poll_wait(SndioVoice *self) +{ + struct pollfd *pfd; + int events, i; + + events =3D 0; + if (self->mode =3D=3D SIO_PLAY) { + if (self->sndio_pos < self->qemu_pos) { + events |=3D POLLOUT; + } + } else { + if (self->sndio_pos < self->buf_size) { + events |=3D POLLIN; + } + } + + /* + * fill the given array of descriptors with the events sndio + * wants, they are different from our 'event' variable because + * sndio may use descriptors internally. + */ + self->nfds =3D sio_pollfd(self->hdl, self->pfds, events); + + for (i =3D 0; i < self->nfds; i++) { + pfd =3D &self->pfds[i]; + if (pfd->fd < 0) { + continue; + } + qemu_set_fd_handler(pfd->fd, + (pfd->events & POLLIN) ? sndio_poll_in : NULL, + (pfd->events & POLLOUT) ? sndio_poll_out : NULL, + &self->pindexes[i]); + pfd->revents =3D 0; + } +} + +/* + * call-back called when one of the descriptors + * became readable or writable + */ +static void sndio_poll_event(SndioVoice *self, int index, int event) +{ + int revents; + + /* + * ensure we're not called twice this cycle + */ + sndio_poll_clear(self); + + /* + * make self->pfds[] look as we're returning from poll syscal, + * this is how sio_revents expects events to be. + */ + self->pfds[index].revents =3D event; + + /* + * tell sndio to handle events and return whether we can read or + * write without blocking. + */ + revents =3D sio_revents(self->hdl, self->pfds); + if (self->mode =3D=3D SIO_PLAY) { + if (revents & POLLOUT) { + sndio_write(self); + } + + if (self->qemu_pos < self->buf_size) { + audio_run(self->hw.out.s, "sndio_out"); + } + } else { + if (revents & POLLIN) { + sndio_read(self); + } + + if (self->qemu_pos < self->sndio_pos) { + audio_run(self->hw.in.s, "sndio_in"); + } + } + + sndio_poll_wait(self); +} + +/* + * return a buffer where data to play can be stored, + * its size is stored in the location pointed by the size argument. + */ +static void *sndio_get_buffer_out(HWVoiceOut *hw, size_t *size) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + /* size is not set by the caller */ + *size =3D self->buf_size - self->qemu_pos; + return self->buf + self->qemu_pos; +} + +/* + * return a buffer where data to play can be stored + */ +static size_t sndio_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + self->qemu_pos +=3D size; + sndio_poll_wait(self); + return size; +} + +/* + * return a buffer from where recorded data is available, + * its size is stored in the location pointed by the size argument. + * it may not exceed the initial value of "*size". + */ +static void *sndio_get_buffer_in(HWVoiceIn *hw, size_t *size) +{ + SndioVoice *self =3D (SndioVoice *) hw; + size_t todo, max_todo; + + /* + * unlike the get_buffer_out() method, get_buffer_in() + * must return a buffer of at most the given size, see audio.c + */ + max_todo =3D *size; + + todo =3D self->sndio_pos - self->qemu_pos; + if (todo > max_todo) { + todo =3D max_todo; + } + + *size =3D todo; + return self->buf + self->qemu_pos; +} + +/* + * discard the given amount of recorded data + */ +static void sndio_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + self->qemu_pos +=3D size; + if (self->qemu_pos =3D=3D self->buf_size) { + self->qemu_pos =3D 0; + self->sndio_pos =3D 0; + } + sndio_poll_wait(self); +} + +/* + * call-back called when one of our descriptors becomes writable + */ +static void sndio_poll_out(void *arg) +{ + struct pollindex *pindex =3D (struct pollindex *) arg; + + sndio_poll_event(pindex->self, pindex->index, POLLOUT); +} + +/* + * call-back called when one of our descriptors becomes readable + */ +static void sndio_poll_in(void *arg) +{ + struct pollindex *pindex =3D (struct pollindex *) arg; + + sndio_poll_event(pindex->self, pindex->index, POLLIN); +} + +static void sndio_fini(SndioVoice *self) +{ + if (self->hdl) { + sio_close(self->hdl); + self->hdl =3D NULL; + } + + if (self->pfds) { + g_free(self->pfds); + self->pfds =3D NULL; + } + + if (self->pindexes) { + g_free(self->pindexes); + self->pindexes =3D NULL; + } + + if (self->buf) { + g_free(self->buf); + self->buf =3D NULL; + } +} + +static int sndio_init(SndioVoice *self, + struct audsettings *as, int mode, Audiodev *dev) +{ + AudiodevSndioOptions *opts =3D &dev->u.sndio; + unsigned long long latency; + const char *dev_name; + struct sio_par req; + unsigned int nch; + int i, nfds; + + dev_name =3D opts->has_dev ? opts->dev : SIO_DEVANY; + latency =3D opts->has_latency ? opts->latency : SNDIO_LATENCY_US; + + /* open the device in non-blocking mode */ + self->hdl =3D sio_open(dev_name, mode, 1); + if (self->hdl =3D=3D NULL) { + dolog("failed to open device\n"); + return -1; + } + + self->mode =3D mode; + + sio_initpar(&req); + + switch (as->fmt) { + case AUDIO_FORMAT_S8: + req.bits =3D 8; + req.sig =3D 1; + break; + case AUDIO_FORMAT_U8: + req.bits =3D 8; + req.sig =3D 0; + break; + case AUDIO_FORMAT_S16: + req.bits =3D 16; + req.sig =3D 1; + break; + case AUDIO_FORMAT_U16: + req.bits =3D 16; + req.sig =3D 0; + break; + case AUDIO_FORMAT_S32: + req.bits =3D 32; + req.sig =3D 1; + break; + case AUDIO_FORMAT_U32: + req.bits =3D 32; + req.sig =3D 0; + default: + dolog("unknown audio sample format\n"); + return -1; + } + + if (req.bits > 8) { + req.le =3D as->endianness ? 0 : 1; + } + + req.rate =3D as->freq; + if (mode =3D=3D SIO_PLAY) { + req.pchan =3D as->nchannels; + } else { + req.rchan =3D as->nchannels; + } + + /* set on-device buffer size */ + req.appbufsz =3D req.rate * latency / 1000000; + + if (!sio_setpar(self->hdl, &req)) { + dolog("failed set audio params\n"); + goto fail; + } + + if (!sio_getpar(self->hdl, &self->par)) { + dolog("failed get audio params\n"); + goto fail; + } + + nch =3D (mode =3D=3D SIO_PLAY) ? self->par.pchan : self->par.rchan; + + /* + * With the default setup, sndio supports any combination of parameters + * so these checks are mostly to catch configuration errors. + */ + if (self->par.bits !=3D req.bits || self->par.bps !=3D req.bits / 8 || + self->par.sig !=3D req.sig || (req.bits > 8 && self->par.le !=3D r= eq.le) || + self->par.rate !=3D as->freq || nch !=3D as->nchannels) { + dolog("unsupported audio params\n"); + goto fail; + } + + /* + * we use one block as buffer size; this is how + * transfers get well aligned + */ + self->buf_size =3D self->par.round * self->par.bps * nch; + + self->buf =3D g_malloc(self->buf_size); + if (self->buf =3D=3D NULL) { + dolog("failed to allocate audio buffer\n"); + goto fail; + } + + nfds =3D sio_nfds(self->hdl); + + self->pfds =3D g_malloc_n(nfds, sizeof(struct pollfd)); + if (self->pfds =3D=3D NULL) { + dolog("failed to allocate pollfd structures\n"); + goto fail; + } + + self->pindexes =3D g_malloc_n(nfds, sizeof(struct pollindex)); + if (self->pindexes =3D=3D NULL) { + dolog("failed to allocate pollindex structures\n"); + goto fail; + } + + for (i =3D 0; i < nfds; i++) { + self->pindexes[i].self =3D self; + self->pindexes[i].index =3D i; + } + + return 0; +fail: + sndio_fini(self); + return -1; +} + +static void sndio_enable(SndioVoice *self, bool enable) +{ + if (enable) { + sio_start(self->hdl); + sndio_poll_wait(self); + } else { + sndio_poll_clear(self); + sio_stop(self->hdl); + } +} + +static void sndio_enable_out(HWVoiceOut *hw, bool enable) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + return sndio_enable(self, enable); +} + +static void sndio_enable_in(HWVoiceIn *hw, bool enable) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + return sndio_enable(self, enable); +} + +static int sndio_init_out(HWVoiceOut *hw, struct audsettings *as, void *op= aque) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + if (sndio_init(self, as, SIO_PLAY, opaque) =3D=3D -1) { + return -1; + } + + audio_pcm_init_info(&hw->info, as); + hw->samples =3D self->par.round; + return 0; +} + +static int sndio_init_in(HWVoiceIn *hw, struct audsettings *as, void *opaq= ue) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + if (sndio_init(self, as, SIO_REC, opaque) =3D=3D -1) { + return -1; + } + + audio_pcm_init_info(&hw->info, as); + hw->samples =3D self->par.round; + return 0; +} + +static void sndio_fini_out(HWVoiceOut *hw) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + return sndio_fini(self); +} + +static void sndio_fini_in(HWVoiceIn *hw) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + return sndio_fini(self); +} + +static void *sndio_audio_init(Audiodev *dev) +{ + assert(dev->driver =3D=3D AUDIODEV_DRIVER_SNDIO); + return dev; +} + +static void sndio_audio_fini(void *opaque) +{ +} + +static struct audio_pcm_ops sndio_pcm_ops =3D { + .init_out =3D sndio_init_out, + .fini_out =3D sndio_fini_out, + .enable_out =3D sndio_enable_out, + .get_buffer_out =3D sndio_get_buffer_out, + .put_buffer_out =3D sndio_put_buffer_out, + .init_in =3D sndio_init_in, + .fini_in =3D sndio_fini_in, + .enable_in =3D sndio_enable_in, + .get_buffer_in =3D sndio_get_buffer_in, + .put_buffer_in =3D sndio_put_buffer_in, +}; + +static struct audio_driver sndio_audio_driver =3D { + .name =3D "sndio", + .descr =3D "https://man.openbsd.org/sndio", + .init =3D sndio_audio_init, + .fini =3D sndio_audio_fini, + .pcm_ops =3D &sndio_pcm_ops, + .can_be_default =3D 1, + .max_voices_out =3D INT_MAX, + .max_voices_in =3D INT_MAX, + .voice_size_out =3D sizeof(SndioVoice), + .voice_size_in =3D sizeof(SndioVoice) +}; + +static void register_audio_sndio(void) +{ + audio_driver_register(&sndio_audio_driver); +} + +type_init(register_audio_sndio); diff --git a/configure b/configure index 7b373bc0bb..90b295db97 100755 --- a/configure +++ b/configure @@ -814,8 +814,8 @@ GNU/kFreeBSD) FreeBSD) bsd=3D"yes" make=3D"${MAKE-gmake}" - audio_drv_list=3D"oss try-sdl" - audio_possible_drivers=3D"oss sdl pa" + audio_drv_list=3D"oss try-sndio try-sdl" + audio_possible_drivers=3D"oss sndio sdl pa" # needed for kinfo_getvmmap(3) in libutil.h LIBS=3D"-lutil $LIBS" # needed for kinfo_getproc @@ -827,16 +827,16 @@ FreeBSD) DragonFly) bsd=3D"yes" make=3D"${MAKE-gmake}" - audio_drv_list=3D"oss try-sdl" - audio_possible_drivers=3D"oss sdl pa" + audio_drv_list=3D"oss try-sndio try-sdl" + audio_possible_drivers=3D"oss sndio sdl pa" HOST_VARIANT_DIR=3D"dragonfly" ;; NetBSD) bsd=3D"yes" hax=3D"yes" make=3D"${MAKE-gmake}" - audio_drv_list=3D"oss try-sdl" - audio_possible_drivers=3D"oss sdl" + audio_drv_list=3D"oss try-sndio try-sdl" + audio_possible_drivers=3D"oss sndio sdl" oss_lib=3D"-lossaudio" HOST_VARIANT_DIR=3D"netbsd" supported_os=3D"yes" @@ -844,8 +844,8 @@ NetBSD) OpenBSD) bsd=3D"yes" make=3D"${MAKE-gmake}" - audio_drv_list=3D"try-sdl" - audio_possible_drivers=3D"sdl" + audio_drv_list=3D"sndio" + audio_possible_drivers=3D"sndio" HOST_VARIANT_DIR=3D"openbsd" supported_os=3D"yes" ;; @@ -894,8 +894,8 @@ Haiku) LIBS=3D"-lposix_error_mapper -lnetwork $LIBS" ;; Linux) - audio_drv_list=3D"try-pa oss" - audio_possible_drivers=3D"oss alsa sdl pa" + audio_drv_list=3D"try-pa try-sndio oss" + audio_possible_drivers=3D"oss alsa sndio sdl pa" linux=3D"yes" linux_user=3D"yes" kvm=3D"yes" @@ -3522,6 +3522,24 @@ fi ########################################## # Sound support libraries probe =20 +audio_drv_probe() +{ + drv=3D$1 + hdr=3D$2 + lib=3D$3 + exp=3D$4 + cfl=3D$5 + cat > $TMPC << EOF +#include <$hdr> +int main(void) { $exp } +EOF + if compile_prog "$cfl" "$lib" ; then + : + else + return 1 + fi +} + audio_drv_list=3D$(echo "$audio_drv_list" | sed -e 's/,/ /g') for drv in $audio_drv_list; do case $drv in @@ -3571,6 +3589,23 @@ for drv in $audio_drv_list; do fi ;; =20 + sndio | try-sndio) + if audio_drv_probe $drv sndio.h "-lsndio" \ + "sio_open(0, SIO_PLAY, 0); return 0;"; then + sndio_libs=3D"-lsndio" + if test "$drv" =3D "try-sndio"; then + audio_drv_list=3D$(echo "$audio_drv_list" | sed -e 's/try-sndi= o/sndio/') + fi + else + if test "$drv" =3D "try-sndio"; then + audio_drv_list=3D$(echo "$audio_drv_list" | sed -e 's/try-sndi= o//') + else + error_exit "$drv check failed" \ + "Make sure to have the $drv libs and headers installed." + fi + fi + ;; + coreaudio) coreaudio_libs=3D"-framework CoreAudio" ;; @@ -6876,7 +6911,7 @@ echo "CONFIG_AUDIO_DRIVERS=3D$audio_drv_list" >> $con= fig_host_mak for drv in $audio_drv_list; do def=3DCONFIG_AUDIO_$(echo $drv | LC_ALL=3DC tr '[a-z]' '[A-Z]') case "$drv" in - alsa | oss | pa | sdl) + alsa | oss | pa | sdl | sndio) echo "$def=3Dm" >> $config_host_mak ;; *) echo "$def=3Dy" >> $config_host_mak ;; @@ -6887,6 +6922,7 @@ echo "PULSE_LIBS=3D$pulse_libs" >> $config_host_mak echo "COREAUDIO_LIBS=3D$coreaudio_libs" >> $config_host_mak echo "DSOUND_LIBS=3D$dsound_libs" >> $config_host_mak echo "OSS_LIBS=3D$oss_libs" >> $config_host_mak +echo "SNDIO_LIBS=3D$sndio_libs" >> $config_host_mak if test "$audio_win_int" =3D "yes" ; then echo "CONFIG_AUDIO_WIN_INT=3Dy" >> $config_host_mak fi diff --git a/qapi/audio.json b/qapi/audio.json index d8c507cced..e31adeb687 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -248,6 +248,28 @@ '*out': 'AudiodevPaPerDirectionOptions', '*server': 'str' } } =20 +## +# @AudiodevSndioOptions: +# +# Options of the sndio audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @dev: the name of the sndio device to use (default 'default') +# +# @latency: play buffer size (in microseconds) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevSndioOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*dev': 'str', + '*latency': 'uint32'} } + ## # @AudiodevWavOptions: # @@ -287,7 +309,7 @@ ## { 'enum': 'AudiodevDriver', 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', - 'spice', 'wav' ] } + 'sndio', 'spice', 'wav' ] } =20 ## # @Audiodev: @@ -316,5 +338,6 @@ 'oss': 'AudiodevOssOptions', 'pa': 'AudiodevPaOptions', 'sdl': 'AudiodevGenericOptions', + 'sndio': 'AudiodevSndioOptions', 'spice': 'AudiodevGenericOptions', 'wav': 'AudiodevWavOptions' } } diff --git a/qemu-options.hx b/qemu-options.hx index ac315c1ac4..100a3c9f59 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -566,6 +566,9 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, #ifdef CONFIG_AUDIO_SDL "-audiodev sdl,id=3Did[,prop[=3Dvalue][,...]]\n" #endif +#ifdef CONFIG_AUDIO_SNDIO + "-audiodev sndio,id=3Did[,prop[=3Dvalue][,...]]\n" +#endif #ifdef CONFIG_SPICE "-audiodev spice,id=3Did[,prop[=3Dvalue][,...]]\n" #endif @@ -747,6 +750,10 @@ Creates a backend using SDL. This backend is availabl= e on most systems, but you should use your platform's native backend if possible. This backend has no backend specific properties. =20 +@item -audiodev sndio,id=3D@var{id}[,@var{prop}[=3D@var{value}][,...]] +Creates a backend using SNDIO. This backend is available on OpenBSD and m= ost +other Unix-like systems. This backend has no backend specific properties. + @item -audiodev spice,id=3D@var{id}[,@var{prop}[=3D@var{value}][,...]] Creates a backend that sends audio through SPICE. This backend requires @code{-spice} and automatically selected in that case, so usually you diff --git a/tests/qtest/modules-test.c b/tests/qtest/modules-test.c index 88217686e1..25e1089f2d 100644 --- a/tests/qtest/modules-test.c +++ b/tests/qtest/modules-test.c @@ -46,6 +46,9 @@ int main(int argc, char *argv[]) #ifdef CONFIG_AUDIO_SDL "audio-", "sdl", #endif +#ifdef CONFIG_AUDIO_SNDIO + "audio-", "sndio", +#endif #ifdef CONFIG_CURSES "ui-", "curses", #endif diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 86770878b6..d9c23c0137 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -58,6 +58,9 @@ class FreeBSDVM(basevm.BaseVM): =20 # libs: migration "zstd", + + # libs: sndio + "sndio", ] =20 BUILD_SCRIPT =3D """