From nobody Mon Feb 9 20:46:15 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1664269216; cv=none; d=zohomail.com; s=zohoarc; b=LnmYSt7BzXzNrd94hAJNACZT8TWCjQuTIFsK8mpa6PMTElZJG1xMPpgJLvA4tPLYs05hlxB7+P+nbA+KCnXk6PXj7eg9zgwHNLg83Fc5As7t+miMnRznDpoxGvY7RNPDUItYEDxgYK1z/ksFJ7EhZcxidg01Y2+mXsQikDC599c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1664269216; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=IaAIzLnv6j0oElP8sw877kHAdtnTXggNHIKJP/nbolc=; b=l9uFnWzRgiQAW6pp+tHzrRLgX88Buc6us6d5sL6gOlK42/Su0rzVIk9qlodTC/h2zZ+Ifg/JFzku+5sRgYywV6P120iGxnAdzY9iOzmyJX+T2Smmn1M8KvEYumLVddBEJN76JY8Ipboudf+vXlgBx5qZi9m1byb1qMKh/WIbISQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1664269216139930.0355015900274; Tue, 27 Sep 2022 02:00:16 -0700 (PDT) Received: from localhost ([::1]:33858 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1od6Ro-0004zP-63 for importer@patchew.org; Tue, 27 Sep 2022 05:00:13 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35656) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1od5pK-0006Rv-GR for qemu-devel@nongnu.org; Tue, 27 Sep 2022 04:20:28 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:43871) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1od5pE-0000aM-R5 for qemu-devel@nongnu.org; Tue, 27 Sep 2022 04:20:24 -0400 Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-418-22YGV06oOUWJmmAORQd38w-1; Tue, 27 Sep 2022 04:20:17 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A8DE13C10234; Tue, 27 Sep 2022 08:20:16 +0000 (UTC) Received: from sirius.home.kraxel.org (unknown [10.39.192.24]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D220918EB3; Tue, 27 Sep 2022 08:19:32 +0000 (UTC) Received: by sirius.home.kraxel.org (Postfix, from userid 1000) id 1A6BD18009AB; Tue, 27 Sep 2022 10:19:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1664266820; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IaAIzLnv6j0oElP8sw877kHAdtnTXggNHIKJP/nbolc=; b=YLH/tg7khT2FZtqn1W5y1Go+Z4Ynzmd1S0oDmO6p8Y/nDCGZJH5swfeOEQE/5eKurZkv4h zttw15HrVSFuQ5KFWzL9abXMD0nwh76s7Gqn9dtXqwUqTq3fcx6jPqwuTc3xIGqSn+hrj/ xN0G2B9RMtoXSYOSbhZjthbWUeROhmw= X-MC-Unique: 22YGV06oOUWJmmAORQd38w-1 From: Gerd Hoffmann To: qemu-devel@nongnu.org Cc: Qiuhao Li , "Michael S. Tsirkin" , Stefan Hajnoczi , Eric Blake , Darren Kenny , Bandan Das , Alexander Bulekov , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Markus Armbruster , Akihiko Odaki , Alexandre Ratchov , Laurent Vivier , Paolo Bonzini , Thomas Huth , Gerd Hoffmann , Peter Maydell , Brad Smith , =?UTF-8?q?Volker=20R=C3=BCmelin?= Subject: [PULL 20/24] audio: Add sndio backend Date: Tue, 27 Sep 2022 10:19:08 +0200 Message-Id: <20220927081912.180983-21-kraxel@redhat.com> In-Reply-To: <20220927081912.180983-1-kraxel@redhat.com> References: <20220927081912.180983-1-kraxel@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.1 on 10.11.54.5 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; Received-SPF: pass client-ip=170.10.133.124; envelope-from=kraxel@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.082, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1664269217699100001 From: Alexandre Ratchov 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.). Signed-off-by: Brad Smith Signed-off-by: Alexandre Ratchov Reviewed-by: Volker R=C3=BCmelin Tested-by: Volker R=C3=BCmelin Message-Id: Signed-off-by: Gerd Hoffmann --- meson_options.txt | 4 +- audio/audio_template.h | 2 + audio/audio.c | 1 + audio/sndioaudio.c | 565 ++++++++++++++++++++++++++++++++++ MAINTAINERS | 7 + audio/meson.build | 1 + meson.build | 9 +- qapi/audio.json | 25 +- qemu-options.hx | 16 + scripts/meson-buildoptions.sh | 7 +- 10 files changed, 632 insertions(+), 5 deletions(-) create mode 100644 audio/sndioaudio.c diff --git a/meson_options.txt b/meson_options.txt index 63f072517427..9df9e86d7d35 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -21,7 +21,7 @@ option('tls_priority', type : 'string', value : 'NORMAL', option('default_devices', type : 'boolean', value : true, description: 'Include a default selection of devices in emulators') option('audio_drv_list', type: 'array', value: ['default'], - choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', = 'pa', 'sdl'], + choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', = 'pa', 'sdl', 'sndio'], description: 'Set audio driver list') option('block_drv_rw_whitelist', type : 'string', value : '', description: 'set block driver read-write whitelist (by default aff= ects only QEMU, not tools like qemu-img)') @@ -240,6 +240,8 @@ option('oss', type: 'feature', value: 'auto', description: 'OSS sound support') option('pa', type: 'feature', value: 'auto', description: 'PulseAudio sound support') +option('sndio', type: 'feature', value: 'auto', + description: 'sndio sound support') =20 option('vhost_kernel', type: 'feature', value: 'auto', description: 'vhost kernel backend support') diff --git a/audio/audio_template.h b/audio/audio_template.h index 7192b19e7390..81860cea6202 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 qapi_AudiodevSdlPerDirectionOptions_base(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/audio.c b/audio/audio.c index cfa4119c0598..5600593da043 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -2030,6 +2030,7 @@ void audio_create_pdos(Audiodev *dev) CASE(OSS, oss, Oss); CASE(PA, pa, Pa); CASE(SDL, sdl, Sdl); + CASE(SNDIO, sndio, ); CASE(SPICE, spice, ); CASE(WAV, wav, ); =20 diff --git a/audio/sndioaudio.c b/audio/sndioaudio.c new file mode 100644 index 000000000000..7c45276d36ce --- /dev/null +++ b/audio/sndioaudio.c @@ -0,0 +1,565 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2019 Alexandre Ratchov + */ + +/* + * 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/main-loop.h" +#include "audio.h" +#include "trace.h" + +#define AUDIO_CAP "sndio" +#include "audio_int.h" + +/* default latency in microseconds 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; + bool enabled; +} 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"); + } + } + + /* + * audio_run() may have changed state + */ + if (self->enabled) { + sndio_poll_wait(self); + } +} + +/* + * return the upper limit of the amount of free play buffer space + */ +static size_t sndio_buffer_get_free(HWVoiceOut *hw) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + return self->buf_size - self->qemu_pos; +} + +/* + * 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 =3D self->buf_size - self->qemu_pos; + return self->buf + self->qemu_pos; +} + +/* + * put back to sndio back-end a buffer returned by sndio_get_buffer_out() + */ +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; + } + + g_free(self->pfds); + g_free(self->pindexes); + g_free(self->buf); +} + +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; + break; + 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); + self->enabled =3D true; + sndio_poll_wait(self); + } else { + self->enabled =3D false; + sndio_poll_clear(self); + sio_stop(self->hdl); + } +} + +static void sndio_enable_out(HWVoiceOut *hw, bool enable) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + sndio_enable(self, enable); +} + +static void sndio_enable_in(HWVoiceIn *hw, bool enable) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + 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; + + sndio_fini(self); +} + +static void sndio_fini_in(HWVoiceIn *hw) +{ + SndioVoice *self =3D (SndioVoice *) hw; + + 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, + .write =3D audio_generic_write, + .buffer_get_free =3D sndio_buffer_get_free, + .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, + .read =3D audio_generic_read, + .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 "sndio https://sndio.org", + .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/MAINTAINERS b/MAINTAINERS index 738c4eb647c8..269e07cf4777 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2438,6 +2438,7 @@ X: audio/jackaudio.c X: audio/ossaudio.c X: audio/paaudio.c X: audio/sdlaudio.c +X: audio/sndioaudio.c X: audio/spiceaudio.c F: qapi/audio.json =20 @@ -2482,6 +2483,12 @@ R: Thomas Huth S: Odd Fixes F: audio/sdlaudio.c =20 +Sndio Audio backend +M: Gerd Hoffmann +R: Alexandre Ratchov +S: Odd Fixes +F: audio/sndioaudio.c + Block layer core M: Kevin Wolf M: Hanna Reitz diff --git a/audio/meson.build b/audio/meson.build index 3abee908602a..34aed7834223 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -17,6 +17,7 @@ foreach m : [ ['pa', pulse, files('paaudio.c')], ['sdl', sdl, files('sdlaudio.c')], ['jack', jack, files('jackaudio.c')], + ['sndio', sndio, files('sndioaudio.c')], ['spice', spice, files('spiceaudio.c')] ] if m[1].found() diff --git a/meson.build b/meson.build index d9ac91ff3659..13db89c65af1 100644 --- a/meson.build +++ b/meson.build @@ -675,6 +675,11 @@ if not get_option('jack').auto() or have_system jack =3D dependency('jack', required: get_option('jack'), method: 'pkg-config', kwargs: static_kwargs) endif +sndio =3D not_found +if not get_option('sndio').auto() or have_system + sndio =3D dependency('sndio', required: get_option('sndio'), + method: 'pkg-config', kwargs: static_kwargs) +endif =20 spice_protocol =3D not_found if not get_option('spice_protocol').auto() or have_system @@ -1591,6 +1596,7 @@ if have_system 'oss': oss.found(), 'pa': pulse.found(), 'sdl': sdl.found(), + 'sndio': sndio.found(), } foreach k, v: audio_drivers_available config_host_data.set('CONFIG_AUDIO_' + k.to_upper(), v) @@ -1598,7 +1604,7 @@ if have_system =20 # Default to native drivers first, OSS second, SDL third audio_drivers_priority =3D \ - [ 'pa', 'coreaudio', 'dsound', 'oss' ] + \ + [ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss' ] + \ (targetos =3D=3D 'linux' ? [] : [ 'sdl' ]) audio_drivers_default =3D [] foreach k: audio_drivers_priority @@ -3922,6 +3928,7 @@ if vnc.found() endif if targetos not in ['darwin', 'haiku', 'windows'] summary_info +=3D {'OSS support': oss} + summary_info +=3D {'sndio support': sndio} elif targetos =3D=3D 'darwin' summary_info +=3D {'CoreAudio support': coreaudio} elif targetos =3D=3D 'windows' diff --git a/qapi/audio.json b/qapi/audio.json index 8099e3d7f13b..1e0a24bdfc40 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -106,6 +106,28 @@ '*out': 'AudiodevAlsaPerDirectionOptions', '*threshold': 'uint32' } } =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: 7.2 +## +{ 'struct': 'AudiodevSndioOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*dev': 'str', + '*latency': 'uint32'} } + ## # @AudiodevCoreaudioPerDirectionOptions: # @@ -387,7 +409,7 @@ ## { 'enum': 'AudiodevDriver', 'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', = 'pa', - 'sdl', 'spice', 'wav' ] } + 'sdl', 'sndio', 'spice', 'wav' ] } =20 ## # @Audiodev: @@ -418,5 +440,6 @@ 'oss': 'AudiodevOssOptions', 'pa': 'AudiodevPaOptions', 'sdl': 'AudiodevSdlOptions', + 'sndio': 'AudiodevSndioOptions', 'spice': 'AudiodevGenericOptions', 'wav': 'AudiodevWavOptions' } } diff --git a/qemu-options.hx b/qemu-options.hx index d8b5ce5b4354..2ff06884f42d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -769,6 +769,9 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, "-audiodev sdl,id=3Did[,prop[=3Dvalue][,...]]\n" " in|out.buffer-count=3D number of buffers\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 @@ -935,6 +938,19 @@ SRST ``in|out.buffer-count=3Dcount`` Sets the count of the buffers. =20 +``-audiodev sndio,id=3Did[,prop[=3Dvalue][,...]]`` + Creates a backend using SNDIO. This backend is available on + OpenBSD and most other Unix-like systems. + + Sndio specific options are: + + ``in|out.dev=3Ddevice`` + Specify the sndio device to use for input and/or output. Default + is ``default``. + + ``in|out.latency=3Dusecs`` + Sets the desired period length in microseconds. + ``-audiodev spice,id=3Did[,prop[=3Dvalue][,...]]`` Creates a backend that sends audio through SPICE. This backend requires ``-spice`` and automatically selected in that case, so diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 359b04e0e6d3..f08e3a8a7e02 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -1,7 +1,7 @@ # This file is generated by meson-buildoptions.py, do not edit! meson_options_help() { - printf "%s\n" ' --audio-drv-list=3DCHOICES Set audio driver list [defau= lt] (choices:' - printf "%s\n" ' alsa/coreaudio/default/dsound/= jack/oss/pa/sdl)' + printf "%s\n" ' --audio-drv-list=3DCHOICES Set audio driver list [defau= lt] (choices: alsa/co' + printf "%s\n" ' reaudio/default/dsound/jack/os= s/pa/sdl/sndio)' printf "%s\n" ' --block-drv-ro-whitelist=3DVALUE' printf "%s\n" ' set block driver read-only whi= telist (by default' printf "%s\n" ' affects only QEMU, not tools l= ike qemu-img)' @@ -144,6 +144,7 @@ meson_options_help() { printf "%s\n" ' slirp-smbd use smbd (at path --smbd=3D*) in slirp = networking' printf "%s\n" ' smartcard CA smartcard emulation support' printf "%s\n" ' snappy snappy compression support' + printf "%s\n" ' sndio sndio sound support' printf "%s\n" ' sparse sparse checker' printf "%s\n" ' spice Spice server support' printf "%s\n" ' spice-protocol Spice protocol support' @@ -393,6 +394,8 @@ _meson_option_parse() { --disable-smartcard) printf "%s" -Dsmartcard=3Ddisabled ;; --enable-snappy) printf "%s" -Dsnappy=3Denabled ;; --disable-snappy) printf "%s" -Dsnappy=3Ddisabled ;; + --enable-sndio) printf "%s" -Dsndio=3Denabled ;; + --disable-sndio) printf "%s" -Dsndio=3Ddisabled ;; --enable-sparse) printf "%s" -Dsparse=3Denabled ;; --disable-sparse) printf "%s" -Dsparse=3Ddisabled ;; --sphinx-build=3D*) quote_sh "-Dsphinx_build=3D$2" ;; --=20 2.37.3