diff options
| author | Stefan Hajnoczi | 2022-10-13 19:55:53 +0200 |
|---|---|---|
| committer | Stefan Hajnoczi | 2022-10-13 19:55:53 +0200 |
| commit | 2ba341b3694cf3cff7b8a1df4cc765900d5c4f60 (patch) | |
| tree | 5c34ecc53cd32ea86d79c3e3e6a0db24f6e91c1c /audio | |
| parent | Merge tag 'win32-pull-request' of https://gitlab.com/marcandre.lureau/qemu in... (diff) | |
| parent | audio: improve out.voices test (diff) | |
| download | qemu-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.c | 38 | ||||
| -rw-r--r-- | audio/audio.c | 111 | ||||
| -rw-r--r-- | audio/audio_int.h | 4 | ||||
| -rw-r--r-- | audio/audio_template.h | 4 | ||||
| -rw-r--r-- | audio/dbusaudio.c | 4 | ||||
| -rw-r--r-- | audio/noaudio.c | 4 | ||||
| -rw-r--r-- | audio/rate_template.h | 11 | ||||
| -rw-r--r-- | audio/spiceaudio.c | 19 | ||||
| -rw-r--r-- | audio/wavaudio.c | 2 |
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) { |
