summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorStefan Hajnoczi2022-10-13 19:55:53 +0200
committerStefan Hajnoczi2022-10-13 19:55:53 +0200
commit2ba341b3694cf3cff7b8a1df4cc765900d5c4f60 (patch)
tree5c34ecc53cd32ea86d79c3e3e6a0db24f6e91c1c /audio
parentMerge tag 'win32-pull-request' of https://gitlab.com/marcandre.lureau/qemu in... (diff)
parentaudio: improve out.voices test (diff)
downloadqemu-2ba341b3694cf3cff7b8a1df4cc765900d5c4f60.tar.gz
qemu-2ba341b3694cf3cff7b8a1df4cc765900d5c4f60.tar.xz
qemu-2ba341b3694cf3cff7b8a1df4cc765900d5c4f60.zip
Merge tag 'kraxel-20221013-pull-request' of https://gitlab.com/kraxel/qemu into staging
pci: cleanup virtio ids. audio: bugfixes and latency improvements. misc fixes for hw/display and ui # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmNHtYsACgkQTLbY7tPo # cTjHhg//RDkHbqVSExe+Odw5ISuLu/EXZSHAVjo3KOCUvaj7O2cXi8N7DVfEy5a5 # T3+WSv0v4X6TYSV0PoMb36a11rCuOKzeLZrtEOQeYfG3D1WCVc9gIWMt6omzBC7A # YQ59P+u19qHD7xD2PP3WRtdcqmsceg1RG+47adX2EnsRZmmu/yJxD72w/Q1kXMuB # jIzuJU2ZVorYX9y11hnIU3M5pvoX/vjFA+Ib2UGZZdlE3KlUKtJeAtLiZkHfoyd1 # 5janU+PtSU6Z1yVirE7RVz3+IBbfqqEFTkDtMXJucJW/Eod0NHCyo4Q6D64HoiZe # +JZKkHmuvn8ZUgXMtIOZdH+aOHlaIJzA5SoA2IFxCBVuxn7p4NtPbCRoHHg7gkDh # BDsq+p/wsdOY06u1txFw9dYy+4tKvWS7+Dxhyme7GT2YUQHrEEG3pzGFmk3PE0Vi # tEAhmfNRxWzUgIcynQiN/3SnShAI8lANq0SEiiTvqcX7h1TK+cjEYjOTMsjK43nL # 2W/pgQxJpEPcSs3jgFLnBLk9rUHRNRC+GtMBlwN+Wdc1y17leZHiIinqhHjXuts3 # cJTdv4veeGuJENPIl2rk5JOdvpVtzduDkz+Rzx0mGb+LnAYdK2lBUV5LY9FfdwaK # 2Bgg02ZYNBz7K2zzFeeV+7b7K/LYOuWkGdzGvKbpqjbefopZmTM= # =6d/F # -----END PGP SIGNATURE----- # gpg: Signature made Thu 13 Oct 2022 02:51:55 EDT # gpg: using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full] # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * tag 'kraxel-20221013-pull-request' of https://gitlab.com/kraxel/qemu: (26 commits) audio: improve out.voices test audio: fix in.voices test gtk: Add show_menubar=on|off command line option. qemu-edid: Restrict input parameter -d to avoid division by zero ui/gtk: Fix the implicit mouse ungrabbing logic pci-ids: document modern virtio-pci ids in pci.h too pci-ids: drop list of modern virtio devices pci-ids: drop PCI_DEVICE_ID_VIRTIO_PMEM pci-ids: drop PCI_DEVICE_ID_VIRTIO_MEM pci-ids: drop PCI_DEVICE_ID_VIRTIO_IOMMU docs: add firmware feature flags cirrus_vga: fix potential memory overflow ui/gtk-egl: egl context needs to be unbound in the end of gd_egl_switch ui/vnc-clipboard: fix integer underflow in vnc_client_cut_text_ext audio: prevent an integer overflow in resampling code audio: fix sw->buf size for audio recording audio: refactor audio_get_avail() audio: rename audio_sw_bytes_free() audio: swap audio_rate_get_bytes() function parameters spiceaudio: update comment ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'audio')
-rw-r--r--audio/alsaaudio.c38
-rw-r--r--audio/audio.c111
-rw-r--r--audio/audio_int.h4
-rw-r--r--audio/audio_template.h4
-rw-r--r--audio/dbusaudio.c4
-rw-r--r--audio/noaudio.c4
-rw-r--r--audio/rate_template.h11
-rw-r--r--audio/spiceaudio.c19
-rw-r--r--audio/wavaudio.c2
9 files changed, 143 insertions, 54 deletions
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 4a61378cd7..7a2a94cd42 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -602,6 +602,42 @@ static int alsa_open(bool in, struct alsa_params_req *req,
return -1;
}
+static size_t alsa_buffer_get_free(HWVoiceOut *hw)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *)hw;
+ snd_pcm_sframes_t avail;
+ size_t alsa_free, generic_free, generic_in_use;
+
+ avail = snd_pcm_avail_update(alsa->handle);
+ if (avail < 0) {
+ if (avail == -EPIPE) {
+ if (!alsa_recover(alsa->handle)) {
+ avail = snd_pcm_avail_update(alsa->handle);
+ }
+ }
+ if (avail < 0) {
+ alsa_logerr(avail,
+ "Could not obtain number of available frames\n");
+ avail = 0;
+ }
+ }
+
+ alsa_free = avail * hw->info.bytes_per_frame;
+ generic_free = audio_generic_buffer_get_free(hw);
+ generic_in_use = hw->samples * hw->info.bytes_per_frame - generic_free;
+ if (generic_in_use) {
+ /*
+ * This code can only be reached in the unlikely case that
+ * snd_pcm_avail_update() returned a larger number of frames
+ * than snd_pcm_writei() could write. Make sure that all
+ * remaining bytes in the generic buffer can be written.
+ */
+ alsa_free = alsa_free > generic_in_use ? alsa_free - generic_in_use : 0;
+ }
+
+ return alsa_free;
+}
+
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
@@ -916,7 +952,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
.write = alsa_write,
- .buffer_get_free = audio_generic_buffer_get_free,
+ .buffer_get_free = alsa_buffer_get_free,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = alsa_enable_out,
diff --git a/audio/audio.c b/audio/audio.c
index df6818ed55..cc664271eb 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -986,6 +986,18 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
}
}
+/**
+ * audio_frontend_frames_in() - returns the number of frames the resampling
+ * code generates from frames_in frames
+ *
+ * @sw: audio recording frontend
+ * @frames_in: number of frames
+ */
+static size_t audio_frontend_frames_in(SWVoiceIn *sw, size_t frames_in)
+{
+ return (int64_t)frames_in * sw->ratio >> 32;
+}
+
static size_t audio_get_avail (SWVoiceIn *sw)
{
size_t live;
@@ -1002,17 +1014,24 @@ static size_t audio_get_avail (SWVoiceIn *sw)
}
ldebug (
- "%s: get_avail live %zu ret %" PRId64 "\n",
+ "%s: get_avail live %zu frontend frames %zu\n",
SW_NAME (sw),
- live, (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame
+ live, audio_frontend_frames_in(sw, live)
);
- return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame;
+ return live;
}
-static size_t audio_sw_bytes_free(SWVoiceOut *sw, size_t free)
+/**
+ * audio_frontend_frames_out() - returns the number of frames needed to
+ * get frames_out frames after resampling
+ *
+ * @sw: audio playback frontend
+ * @frames_out: number of frames
+ */
+static size_t audio_frontend_frames_out(SWVoiceOut *sw, size_t frames_out)
{
- return (((int64_t)free << 32) / sw->ratio) * sw->info.bytes_per_frame;
+ return ((int64_t)frames_out << 32) / sw->ratio;
}
static size_t audio_get_free(SWVoiceOut *sw)
@@ -1034,8 +1053,8 @@ static size_t audio_get_free(SWVoiceOut *sw)
dead = sw->hw->mix_buf->size - live;
#ifdef DEBUG_OUT
- dolog("%s: get_free live %zu dead %zu sw_bytes %zu\n",
- SW_NAME(sw), live, dead, audio_sw_bytes_free(sw, dead));
+ dolog("%s: get_free live %zu dead %zu frontend frames %zu\n",
+ SW_NAME(sw), live, dead, audio_frontend_frames_out(sw, dead));
#endif
return dead;
@@ -1121,8 +1140,12 @@ static void audio_run_out (AudioState *s)
HWVoiceOut *hw = NULL;
SWVoiceOut *sw;
- if (!audio_get_pdo_out(s->dev)->mixing_engine) {
- while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
+ while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
+ size_t played, live, prev_rpos;
+ size_t hw_free = audio_pcm_hw_get_free(hw);
+ int nb_live;
+
+ if (!audio_get_pdo_out(s->dev)->mixing_engine) {
/* there is exactly 1 sw for each hw with no mixeng */
sw = hw->sw_head.lh_first;
@@ -1135,16 +1158,16 @@ static void audio_run_out (AudioState *s)
}
if (sw->active) {
- sw->callback.fn(sw->callback.opaque, INT_MAX);
+ sw->callback.fn(sw->callback.opaque,
+ hw_free * sw->info.bytes_per_frame);
}
- }
- return;
- }
- while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
- size_t played, live, prev_rpos;
- size_t hw_free = audio_pcm_hw_get_free(hw);
- int nb_live;
+ if (hw->pcm_ops->run_buffer_out) {
+ hw->pcm_ops->run_buffer_out(hw);
+ }
+
+ continue;
+ }
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) {
@@ -1152,13 +1175,14 @@ static void audio_run_out (AudioState *s)
size_t free;
if (hw_free > sw->total_hw_samples_mixed) {
- free = audio_sw_bytes_free(sw,
+ free = audio_frontend_frames_out(sw,
MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
} else {
free = 0;
}
if (free > 0) {
- sw->callback.fn(sw->callback.opaque, free);
+ sw->callback.fn(sw->callback.opaque,
+ free * sw->info.bytes_per_frame);
}
}
}
@@ -1297,11 +1321,13 @@ static void audio_run_in (AudioState *s)
sw->total_hw_samples_acquired -= min;
if (sw->active) {
+ size_t sw_avail = audio_get_avail(sw);
size_t avail;
- avail = audio_get_avail (sw);
+ avail = audio_frontend_frames_in(sw, sw_avail);
if (avail > 0) {
- sw->callback.fn (sw->callback.opaque, avail);
+ sw->callback.fn(sw->callback.opaque,
+ avail * sw->info.bytes_per_frame);
}
}
}
@@ -1501,10 +1527,6 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
}
}
- if (hw->pcm_ops->run_buffer_out) {
- hw->pcm_ops->run_buffer_out(hw);
- }
-
return total;
}
@@ -1750,13 +1772,13 @@ static AudioState *audio_init(Audiodev *dev, const char *name)
s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices;
s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices;
- if (s->nb_hw_voices_out <= 0) {
+ if (s->nb_hw_voices_out < 1) {
dolog ("Bogus number of playback voices %d, setting to 1\n",
s->nb_hw_voices_out);
s->nb_hw_voices_out = 1;
}
- if (s->nb_hw_voices_in <= 0) {
+ if (s->nb_hw_voices_in < 0) {
dolog ("Bogus number of capture voices %d, setting to 0\n",
s->nb_hw_voices_in);
s->nb_hw_voices_in = 0;
@@ -2251,26 +2273,39 @@ void audio_rate_start(RateCtl *rate)
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
-size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
- size_t bytes_avail)
+size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)
{
int64_t now;
int64_t ticks;
int64_t bytes;
- int64_t samples;
- size_t ret;
+ int64_t frames;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - rate->start_ticks;
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
- samples = (bytes - rate->bytes_sent) / info->bytes_per_frame;
- if (samples < 0 || samples > 65536) {
- AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)\n", samples);
+ frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;
+ if (frames < 0 || frames > 65536) {
+ AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames);
audio_rate_start(rate);
- samples = 0;
+ frames = 0;
}
- ret = MIN(samples * info->bytes_per_frame, bytes_avail);
- rate->bytes_sent += ret;
- return ret;
+ return frames * info->bytes_per_frame;
+}
+
+void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)
+{
+ rate->bytes_sent += bytes_used;
+}
+
+size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
+ size_t bytes_avail)
+{
+ size_t bytes;
+
+ bytes = audio_rate_peek_bytes(rate, info);
+ bytes = MIN(bytes, bytes_avail);
+ audio_rate_add_bytes(rate, bytes);
+
+ return bytes;
}
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 2a6914d2aa..e87ce014a0 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -263,7 +263,9 @@ typedef struct RateCtl {
} RateCtl;
void audio_rate_start(RateCtl *rate);
-size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
+size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info);
+void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used);
+size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
size_t bytes_avail);
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 98ab557684..720a32e57e 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -110,7 +110,11 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
return 0;
}
+#ifdef DAC
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
+#else
+ samples = (int64_t)sw->HWBUF->size * sw->ratio >> 32;
+#endif
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) {
diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c
index a3d656d3b0..722df0355e 100644
--- a/audio/dbusaudio.c
+++ b/audio/dbusaudio.c
@@ -82,7 +82,7 @@ static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
}
*size = MIN(vo->buf_size - vo->buf_pos, *size);
- *size = audio_rate_get_bytes(&hw->info, &vo->rate, *size);
+ *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
return vo->buf + vo->buf_pos;
@@ -343,7 +343,7 @@ dbus_read(HWVoiceIn *hw, void *buf, size_t size)
trace_dbus_audio_read(size);
- /* size = audio_rate_get_bytes(&hw->info, &vo->rate, size); */
+ /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
g_hash_table_iter_init(&iter, da->in_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 84a6bfbb1c..4fdee5adec 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -44,7 +44,7 @@ typedef struct NoVoiceIn {
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
- return audio_rate_get_bytes(&hw->info, &no->rate, len);
+ return audio_rate_get_bytes(&no->rate, &hw->info, len);
}
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
@@ -89,7 +89,7 @@ static void no_fini_in (HWVoiceIn *hw)
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
- int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
+ int64_t bytes = audio_rate_get_bytes(&no->rate, &hw->info, size);
audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame);
return bytes;
diff --git a/audio/rate_template.h b/audio/rate_template.h
index f94c940c61..b432719ebb 100644
--- a/audio/rate_template.h
+++ b/audio/rate_template.h
@@ -72,11 +72,6 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
ilast = *ibuf++;
rate->ipos++;
- /* if ipos overflow, there is a infinite loop */
- if (rate->ipos == 0xffffffff) {
- rate->ipos = 1;
- rate->opos = rate->opos & 0xffffffff;
- }
/* See if we finished the input buffer yet */
if (ibuf >= iend) {
goto the_end;
@@ -85,6 +80,12 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
icur = *ibuf;
+ /* wrap ipos and opos around long before they overflow */
+ if (rate->ipos >= 0x10001) {
+ rate->ipos = 1;
+ rate->opos &= 0xffffffff;
+ }
+
/* interpolate */
#ifdef FLOAT_MIXENG
#ifdef RECIPROCAL
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index a8d370fe6f..d17ef1a25e 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -120,6 +120,13 @@ static void line_out_fini (HWVoiceOut *hw)
spice_server_remove_interface (&out->sin.base);
}
+static size_t line_out_get_free(HWVoiceOut *hw)
+{
+ SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+
+ return audio_rate_peek_bytes(&out->rate, &hw->info);
+}
+
static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
@@ -133,8 +140,6 @@ static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
*size = MIN((out->fsize - out->fpos) << 2, *size);
}
- *size = audio_rate_get_bytes(&hw->info, &out->rate, *size);
-
return out->frame + out->fpos;
}
@@ -142,6 +147,8 @@ static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+ audio_rate_add_bytes(&out->rate, size);
+
if (buf) {
assert(buf == out->frame + out->fpos && out->fpos <= out->fsize);
out->fpos += size >> 2;
@@ -232,10 +239,13 @@ static void line_in_fini (HWVoiceIn *hw)
static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
- uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2;
+ uint64_t to_read = audio_rate_get_bytes(&in->rate, &hw->info, len) >> 2;
size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
- /* XXX: do we need this? */
+ /*
+ * If the client didn't send new frames, it most likely disconnected.
+ * Generate silence in this case to avoid a stalled audio stream.
+ */
if (ready == 0) {
memset(buf, 0, to_read << 2);
ready = to_read;
@@ -282,6 +292,7 @@ static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init,
.fini_out = line_out_fini,
.write = audio_generic_write,
+ .buffer_get_free = line_out_get_free,
.get_buffer_out = line_out_get_buffer,
.put_buffer_out = line_out_put_buffer,
.enable_out = line_out_enable,
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index ac666335c7..3e1d84db83 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -42,7 +42,7 @@ typedef struct WAVVoiceOut {
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
- int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
+ int64_t bytes = audio_rate_get_bytes(&wav->rate, &hw->info, len);
assert(bytes % hw->info.bytes_per_frame == 0);
if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {