All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/15] reduce audio playback latency
@ 2022-01-06  9:21 Volker Rümelin
  2022-01-06  9:23 ` [PATCH 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
                   ` (16 more replies)
  0 siblings, 17 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:21 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, Christian Schoenebeck, qemu-devel

This patch series reduces the playback latency for audio backends,
in some cases significantly. For PulseAudio, the audio buffer is
also moved from the QEMU side to the PulseAudio server side. This
improves the drop-out safety for PulseAudio.

I actually measured the latency reduction with the PulseAudio
backend. For the test I used my Linux host configured to play
audio with PulseAudio. The guest was a Linux guest, also
configured to use PulseAudio.

Measuring audio latencies is difficult. I played a sine tone in
the guest with Audacity and measured the time from releasing the
left mouse button until the tone can be heard. A few seconds
before the measurement I started playback of an audio file with
10 minutes of silence to fill the audio buffers. The over-all
latency can't be used to estimate the playback latency, but it
can be used to calculate the playback latency reduction.

The measured over-all latency with PulseAudio is around 200ms
without these patches and around 135ms with these patches. The
difference of 65ms agrees well with the expected value of
46.4ms * 2 + 15ms - 46.4ms = 61.4ms. 46.4ms * 2 is the size of
the mixing-engine buffer ("[PATCH 14/15] paaudio: fix samples vs.
frames mix-up" explains the factor 2), 15ms is the server side
PulseAudio buffer size used before these patches and 46.4ms is
the new server side PulseAudio buffer size.

Volker Rümelin (15):
   audio: replace open-coded buffer arithmetic
   audio: move function audio_pcm_hw_clip_out()
   audio: add function audio_pcm_hw_conv_in()
   audio: inline function audio_pcm_sw_get_rpos_in()
   paaudio: increase default latency to 46ms
   jackaudio: use more jack audio buffers
   audio: copy playback stream in sequential order
   audio: add pcm_ops function table for capture backend
   audio: revert tests for pcm_ops table
   audio: restore mixing-engine playback buffer size
   paaudio: reduce effective playback buffer size
   dsoundaudio: reduce effective playback buffer size
   ossaudio: reduce effective playback buffer size
   paaudio: fix samples vs. frames mix-up
   sdlaudio: fix samples vs. frames mix-up

  audio/alsaaudio.c   |   1 +
  audio/audio.c       | 194 ++++++++++++++++++++++++--------------------
  audio/audio_int.h   |   9 +-
  audio/coreaudio.c   |  13 +--
  audio/dsoundaudio.c |  30 ++++---
  audio/jackaudio.c   |   5 +-
  audio/noaudio.c     |   1 +
  audio/ossaudio.c    |  17 +++-
  audio/paaudio.c     |  49 ++++++-----
  audio/sdlaudio.c    |  21 +++--
  audio/wavaudio.c    |   1 +
  11 files changed, 199 insertions(+), 142 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 01/15] audio: replace open-coded buffer arithmetic
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06 10:18   ` Thomas Huth
  2022-01-06  9:23 ` [PATCH 02/15] audio: move function audio_pcm_hw_clip_out() Volker Rümelin
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, Christian Schoenebeck, qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Replace open-coded buffer arithmetic with the available function
audio_ring_dist(). Because the name audio_ring_dist implies it
calculates the distance between two points, define the alias
function name audio_ring_posb. That's the position in backward
direction of a given point at a given distance.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/audio.c     | 25 +++++++------------------
 audio/audio_int.h |  2 ++
 audio/coreaudio.c | 10 ++++------
 audio/sdlaudio.c  | 11 +++++------
 4 files changed, 18 insertions(+), 30 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index dc28685d22..e7a139e289 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -574,19 +574,13 @@ static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
 {
     HWVoiceIn *hw = sw->hw;
     ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
-    ssize_t rpos;
 
     if (audio_bug(__func__, live < 0 || live > hw->conv_buf->size)) {
         dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
         return 0;
     }
 
-    rpos = hw->conv_buf->pos - live;
-    if (rpos >= 0) {
-        return rpos;
-    } else {
-        return hw->conv_buf->size + rpos;
-    }
+    return audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
 }
 
 static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
@@ -1394,12 +1388,10 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw)
 
 void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
 {
-    ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
+    size_t start;
 
-    if (start < 0) {
-        start += hw->size_emul;
-    }
-    assert(start >= 0 && start < hw->size_emul);
+    start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
+    assert(start < hw->size_emul);
 
     *size = MIN(*size, hw->pending_emul);
     *size = MIN(*size, hw->size_emul - start);
@@ -1415,13 +1407,10 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
 void audio_generic_run_buffer_out(HWVoiceOut *hw)
 {
     while (hw->pending_emul) {
-        size_t write_len, written;
-        ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
+        size_t write_len, written, start;
 
-        if (start < 0) {
-            start += hw->size_emul;
-        }
-        assert(start >= 0 && start < hw->size_emul);
+        start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
+        assert(start < hw->size_emul);
 
         write_len = MIN(hw->pending_emul, hw->size_emul - start);
 
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 428a091d05..928d8e107e 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -266,6 +266,8 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
     return (dst >= src) ? (dst - src) : (len - src + dst);
 }
 
+#define audio_ring_posb(pos, dist, len) audio_ring_dist(pos, dist, len)
+
 #define dolog(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__)
 
 #ifdef DEBUG
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index d8a21d3e50..1fdd1d4b14 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -333,12 +333,10 @@ static OSStatus audioDeviceIOProc(
 
     len = frameCount * hw->info.bytes_per_frame;
     while (len) {
-        size_t write_len;
-        ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
-        if (start < 0) {
-            start += hw->size_emul;
-        }
-        assert(start >= 0 && start < hw->size_emul);
+        size_t write_len, start;
+
+        start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
+        assert(start < hw->size_emul);
 
         write_len = MIN(MIN(hw->pending_emul, len),
                         hw->size_emul - start);
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index c68c62a3e4..d6f3aa1a9a 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -224,12 +224,11 @@ static void sdl_callback_out(void *opaque, Uint8 *buf, int len)
         /* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */
 
         while (hw->pending_emul && len) {
-            size_t write_len;
-            ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
-            if (start < 0) {
-                start += hw->size_emul;
-            }
-            assert(start >= 0 && start < hw->size_emul);
+            size_t write_len, start;
+
+            start = audio_ring_posb(hw->pos_emul, hw->pending_emul,
+                                    hw->size_emul);
+            assert(start < hw->size_emul);
 
             write_len = MIN(MIN(hw->pending_emul, len),
                             hw->size_emul - start);
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 02/15] audio: move function audio_pcm_hw_clip_out()
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
  2022-01-06  9:23 ` [PATCH 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-07 13:30   ` Philippe Mathieu-Daudé
  2022-01-06  9:23 ` [PATCH 03/15] audio: add function audio_pcm_hw_conv_in() Volker Rümelin
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Move the function audio_pcm_hw_clip_out() into the correct
section 'Hard voice (playback)'.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/audio.c | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index e7a139e289..dfd32912da 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -548,25 +548,6 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
     return live;
 }
 
-static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
-{
-    size_t clipped = 0;
-    size_t pos = hw->mix_buf->pos;
-
-    while (len) {
-        st_sample *src = hw->mix_buf->samples + pos;
-        uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
-        size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
-        size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
-
-        hw->clip(dst, src, samples_to_clip);
-
-        pos = (pos + samples_to_clip) % hw->mix_buf->size;
-        len -= samples_to_clip;
-        clipped += samples_to_clip;
-    }
-}
-
 /*
  * Soft voice (capture)
  */
@@ -677,6 +658,25 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
     return 0;
 }
 
+static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
+{
+    size_t clipped = 0;
+    size_t pos = hw->mix_buf->pos;
+
+    while (len) {
+        st_sample *src = hw->mix_buf->samples + pos;
+        uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
+        size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
+        size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
+
+        hw->clip(dst, src, samples_to_clip);
+
+        pos = (pos + samples_to_clip) % hw->mix_buf->size;
+        len -= samples_to_clip;
+        clipped += samples_to_clip;
+    }
+}
+
 /*
  * Soft voice (playback)
  */
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 03/15] audio: add function audio_pcm_hw_conv_in()
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
  2022-01-06  9:23 ` [PATCH 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
  2022-01-06  9:23 ` [PATCH 02/15] audio: move function audio_pcm_hw_clip_out() Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 04/15] audio: inline function audio_pcm_sw_get_rpos_in() Volker Rümelin
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Add a function audio_pcm_hw_conv_in() similar to the existing
counterpart function audio_pcm_hw_clip_out(). This function reduces
the number of calls to the pcm_ops functions get_buffer_in() and
put_buffer_in(). That's one less call to get_buffer_in() and
put_buffer_in() every time the conv_buffer wraps around.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/audio.c | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index dfd32912da..f28e91853f 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -548,6 +548,24 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
     return live;
 }
 
+static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
+{
+    size_t conv = 0;
+    STSampleBuffer *conv_buf = hw->conv_buf;
+
+    while (samples) {
+        uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);
+        size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);
+
+        hw->conv(conv_buf->samples + conv_buf->pos, src, proc);
+        conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
+        samples -= proc;
+        conv += proc;
+    }
+
+    return conv;
+}
+
 /*
  * Soft voice (capture)
  */
@@ -1219,7 +1237,6 @@ static void audio_run_out (AudioState *s)
 static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
 {
     size_t conv = 0;
-    STSampleBuffer *conv_buf = hw->conv_buf;
 
     if (hw->pcm_ops->run_buffer_in) {
         hw->pcm_ops->run_buffer_in(hw);
@@ -1235,11 +1252,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
             break;
         }
 
-        proc = MIN(size / hw->info.bytes_per_frame,
-                   conv_buf->size - conv_buf->pos);
-
-        hw->conv(conv_buf->samples + conv_buf->pos, buf, proc);
-        conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
+        proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame);
 
         samples -= proc;
         conv += proc;
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 04/15] audio: inline function audio_pcm_sw_get_rpos_in()
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (2 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 03/15] audio: add function audio_pcm_hw_conv_in() Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 05/15] paaudio: increase default latency to 46ms Volker Rümelin
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Simplify code by inlining function audio_pcm_sw_get_rpos_in()
at the only call site and remove the duplicated audio_bug()
test.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/audio.c | 23 +++++------------------
 1 file changed, 5 insertions(+), 18 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index f28e91853f..35437986d9 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -569,37 +569,24 @@ static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
 /*
  * Soft voice (capture)
  */
-static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
-{
-    HWVoiceIn *hw = sw->hw;
-    ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
-
-    if (audio_bug(__func__, live < 0 || live > hw->conv_buf->size)) {
-        dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
-        return 0;
-    }
-
-    return audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
-}
-
 static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
 {
     HWVoiceIn *hw = sw->hw;
     size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
     struct st_sample *src, *dst = sw->buf;
 
-    rpos = audio_pcm_sw_get_rpos_in(sw) % hw->conv_buf->size;
-
     live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+    if (!live) {
+        return 0;
+    }
     if (audio_bug(__func__, live > hw->conv_buf->size)) {
         dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
         return 0;
     }
 
+    rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
+
     samples = size / sw->info.bytes_per_frame;
-    if (!live) {
-        return 0;
-    }
 
     swlim = (live * sw->ratio) >> 32;
     swlim = MIN (swlim, samples);
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 05/15] paaudio: increase default latency to 46ms
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (3 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 04/15] audio: inline function audio_pcm_sw_get_rpos_in() Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 06/15] jackaudio: use more jack audio buffers Volker Rümelin
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

This is a patch to improve the pulseaudio playback experience.
Asking pulseaudio for a playback latency of 15ms is quite
demanding. Increase this to 46ms. The total playback latency
now is 31ms larger. One of the next patches will reduce the
total playback latency again by more than 46ms.

Here is a quote from the PulseAudio Latency Control
documentation: 'For the sake of (...) drop-out safety always
make sure to pick the highest latency possible that fulfills
your needs.'

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/paaudio.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index 75401d5391..9df1e69c08 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -744,7 +744,7 @@ static int qpa_validate_per_direction_opts(Audiodev *dev,
 {
     if (!pdo->has_latency) {
         pdo->has_latency = true;
-        pdo->latency = 15000;
+        pdo->latency = 46440;
     }
     return 1;
 }
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 06/15] jackaudio: use more jack audio buffers
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (4 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 05/15] paaudio: increase default latency to 46ms Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 07/15] audio: copy playback stream in sequential order Volker Rümelin
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Christian Schoenebeck, qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

The next patch reduces the effective qemu playback buffer size
by timer-period. Increase the number of jack audio buffers by
one to preserve the total effective buffer size. The size of one
jack audio buffer is 512 samples. With audio defaults that's
512 samples / 44100 samples/s = 11.6 ms and only slightly larger
than the timer-period of 10 ms.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/jackaudio.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/audio/jackaudio.c b/audio/jackaudio.c
index e7de6d5433..fe4d9d54c6 100644
--- a/audio/jackaudio.c
+++ b/audio/jackaudio.c
@@ -483,8 +483,8 @@ static int qjack_client_init(QJackClient *c)
         c->buffersize = 512;
     }
 
-    /* create a 2 period buffer */
-    qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2);
+    /* create a 3 period buffer */
+    qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 3);
 
     qjack_client_connect_ports(c);
     c->state = QJACK_STATE_RUNNING;
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 07/15] audio: copy playback stream in sequential order
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (5 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 06/15] jackaudio: use more jack audio buffers Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 08/15] audio: add pcm_ops function table for capture backend Volker Rümelin
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Christian Schoenebeck, qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Change the code to copy the playback stream in sequential order.
The advantage can be seen in the next patches where the stream
copy operation effectively becomes a write through operation.

The following diagram shows the average buffer fill level and
the stream copy sequence. ### represents a timer_period sized
chunk. The rest of the buffer sizes are not to scale.

With current code:
         |--------| |#####111| |---#####|
          sw->buf    mix_buf    backend buffer
  1. clip
         |--------| |---#####| |111##222|
          sw->buf    mix_buf    backend buffer
  2. write to audio device
  333 -> |--------| |---#####| |---111##| -> 222
          sw->buf    mix_buf    backend buffer
  3a. sw device write
         |-----333| |---#####| |---111##|
          sw->buf    mix_buf    backend buffer
  3b. resample and mix
         |--------| |333#####| |---111##|
          sw->buf    mix_buf    backend buffer

With this patch:
  111 -> |--------| |---#####| |---#####|
          sw->buf    mix_buf    backend buffer
  1a: sw device write
         |-----111| |---#####| |---#####|
          sw->buf    mix_buf    backend buffer
  1b. resample and mix
         |--------| |111##222| |---#####|
          sw->buf    mix_buf    backend buffer
  2. clip
         |--------| |---111##| |222##333|
          sw->buf    mix_buf    backend buffer
  3. write to audio device
         |--------| |---111##| |---222##| -> 333
          sw->buf    mix_buf    backend buffer

The effective total playback buffer size is reduced by
timer_period.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/audio.c | 24 +++++++++---------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 35437986d9..9e2d7fb209 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1134,6 +1134,15 @@ static void audio_run_out (AudioState *s)
         size_t played, live, prev_rpos, free;
         int nb_live;
 
+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+            if (sw->active) {
+                free = audio_get_free(sw);
+                if (free > 0) {
+                    sw->callback.fn(sw->callback.opaque, free);
+                }
+            }
+        }
+
         live = audio_pcm_hw_get_live_out (hw, &nb_live);
         if (!nb_live) {
             live = 0;
@@ -1162,14 +1171,6 @@ static void audio_run_out (AudioState *s)
         }
 
         if (!live) {
-            for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
-                if (sw->active) {
-                    free = audio_get_free (sw);
-                    if (free > 0) {
-                        sw->callback.fn (sw->callback.opaque, free);
-                    }
-                }
-            }
             if (hw->pcm_ops->run_buffer_out) {
                 hw->pcm_ops->run_buffer_out(hw);
             }
@@ -1210,13 +1211,6 @@ static void audio_run_out (AudioState *s)
             if (!sw->total_hw_samples_mixed) {
                 sw->empty = 1;
             }
-
-            if (sw->active) {
-                free = audio_get_free (sw);
-                if (free > 0) {
-                    sw->callback.fn (sw->callback.opaque, free);
-                }
-            }
         }
     }
 }
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 08/15] audio: add pcm_ops function table for capture backend
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (6 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 07/15] audio: copy playback stream in sequential order Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 09/15] audio: revert tests for pcm_ops table Volker Rümelin
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Add a pcm_ops function table for the capture backend. This avoids
additional code in the next patches to test if the pcm_ops table
is available.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/audio.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/audio/audio.c b/audio/audio.c
index 9e2d7fb209..55f885f8e9 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1804,6 +1804,7 @@ void AUD_remove_card (QEMUSoundCard *card)
     g_free (card->name);
 }
 
+static struct audio_pcm_ops capture_pcm_ops;
 
 CaptureVoiceOut *AUD_add_capture(
     AudioState *s,
@@ -1849,6 +1850,7 @@ CaptureVoiceOut *AUD_add_capture(
 
         hw = &cap->hw;
         hw->s = s;
+        hw->pcm_ops = &capture_pcm_ops;
         QLIST_INIT (&hw->sw_head);
         QLIST_INIT (&cap->cb_head);
 
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 09/15] audio: revert tests for pcm_ops table
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (7 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 08/15] audio: add pcm_ops function table for capture backend Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-13  9:43   ` Gerd Hoffmann
  2022-01-06  9:23 ` [PATCH 10/15] audio: restore mixing-engine playback buffer size Volker Rümelin
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

With previous commit every audio backend has a pcm_ops function
table. It's no longer necessary to test if the table is
available. This reverts commit cbaf25d1f5: "audio: fix
wavcapture segfault"

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/audio.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 55f885f8e9..c420a8bd1c 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -612,7 +612,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
         total += isamp;
     }
 
-    if (hw->pcm_ops && !hw->pcm_ops->volume_in) {
+    if (!hw->pcm_ops->volume_in) {
         mixeng_volume (sw->buf, ret, &sw->vol);
     }
 
@@ -718,7 +718,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
     if (swlim) {
         sw->conv (sw->buf, buf, swlim);
 
-        if (sw->hw->pcm_ops && !sw->hw->pcm_ops->volume_out) {
+        if (!sw->hw->pcm_ops->volume_out) {
             mixeng_volume (sw->buf, swlim, &sw->vol);
         }
     }
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 10/15] audio: restore mixing-engine playback buffer size
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (8 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 09/15] audio: revert tests for pcm_ops table Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 11/15] paaudio: reduce effective " Volker Rümelin
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, Christian Schoenebeck, qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Commit ff095e5231 "audio: api for mixeng code free backends"
introduced another FIFO for the audio subsystem with exactly the
same size as the mixing-engine FIFO. Most audio backends use
this generic FIFO. The generic FIFO used together with the
mixing-engine FIFO doubles the audio FIFO size, because that's
just two independent FIFOs connected together in series.

For audio playback this nearly doubles the playback latency.

This patch restores the effective mixing-engine playback buffer
size to a pre v4.2.0 size by only accepting the amount of
samples for the mixing-engine queue which the downstream queue
accepts.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/alsaaudio.c |  1 +
 audio/audio.c     | 69 +++++++++++++++++++++++++++++++++++------------
 audio/audio_int.h |  7 ++++-
 audio/coreaudio.c |  3 +++
 audio/jackaudio.c |  1 +
 audio/noaudio.c   |  1 +
 audio/ossaudio.c  | 12 +++++++++
 audio/sdlaudio.c  |  3 +++
 audio/wavaudio.c  |  1 +
 9 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 2b9789e647..b04716a6cc 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -916,6 +916,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,
     .run_buffer_out = audio_generic_run_buffer_out,
     .enable_out = alsa_enable_out,
 
diff --git a/audio/audio.c b/audio/audio.c
index c420a8bd1c..a88572e713 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -663,6 +663,12 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
     return 0;
 }
 
+static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)
+{
+    return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) :
+            INT_MAX) / hw->info.bytes_per_frame;
+}
+
 static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
 {
     size_t clipped = 0;
@@ -687,7 +693,8 @@ static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
  */
 static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
 {
-    size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
+    size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, blck;
+    size_t hw_free;
     size_t ret = 0, pos = 0, total = 0;
 
     if (!sw) {
@@ -710,27 +717,28 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
     }
 
     wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
-    samples = size / sw->info.bytes_per_frame;
 
     dead = hwsamples - live;
-    swlim = ((int64_t) dead << 32) / sw->ratio;
-    swlim = MIN (swlim, samples);
-    if (swlim) {
-        sw->conv (sw->buf, buf, swlim);
+    hw_free = audio_pcm_hw_get_free(sw->hw);
+    hw_free = hw_free > live ? hw_free - live : 0;
+    samples = ((int64_t)MIN(dead, hw_free) << 32) / sw->ratio;
+    samples = MIN(samples, size / sw->info.bytes_per_frame);
+    if (samples) {
+        sw->conv(sw->buf, buf, samples);
 
         if (!sw->hw->pcm_ops->volume_out) {
-            mixeng_volume (sw->buf, swlim, &sw->vol);
+            mixeng_volume(sw->buf, samples, &sw->vol);
         }
     }
 
-    while (swlim) {
+    while (samples) {
         dead = hwsamples - live;
         left = hwsamples - wpos;
         blck = MIN (dead, left);
         if (!blck) {
             break;
         }
-        isamp = swlim;
+        isamp = samples;
         osamp = blck;
         st_rate_flow_mix (
             sw->rate,
@@ -740,7 +748,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
             &osamp
             );
         ret += isamp;
-        swlim -= isamp;
+        samples -= isamp;
         pos += isamp;
         live += osamp;
         wpos = (wpos + osamp) % hwsamples;
@@ -1002,6 +1010,11 @@ static size_t audio_get_avail (SWVoiceIn *sw)
     return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame;
 }
 
+static size_t audio_sw_bytes_free(SWVoiceOut *sw, size_t free)
+{
+    return (((int64_t)free << 32) / sw->ratio) * sw->info.bytes_per_frame;
+}
+
 static size_t audio_get_free(SWVoiceOut *sw)
 {
     size_t live, dead;
@@ -1021,13 +1034,11 @@ 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 ret %" PRId64 "\n",
-           SW_NAME (sw),
-           live, dead, (((int64_t) dead << 32) / sw->ratio) *
-           sw->info.bytes_per_frame);
+    dolog("%s: get_free live %zu dead %zu sw_bytes %zu\n",
+          SW_NAME(sw), live, dead, audio_sw_bytes_free(sw, dead));
 #endif
 
-    return (((int64_t) dead << 32) / sw->ratio) * sw->info.bytes_per_frame;
+    return dead;
 }
 
 static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
@@ -1131,12 +1142,21 @@ static void audio_run_out (AudioState *s)
     }
 
     while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
-        size_t played, live, prev_rpos, free;
+        size_t played, live, prev_rpos;
+        size_t hw_free = audio_pcm_hw_get_free(hw);
         int nb_live;
 
         for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
             if (sw->active) {
-                free = audio_get_free(sw);
+                size_t sw_free = audio_get_free(sw);
+                size_t free;
+
+                if (hw_free > sw->total_hw_samples_mixed) {
+                    free = audio_sw_bytes_free(sw,
+                        MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
+                } else {
+                    free = 0;
+                }
                 if (free > 0) {
                     sw->callback.fn(sw->callback.opaque, free);
                 }
@@ -1398,6 +1418,15 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
     hw->pending_emul -= size;
 }
 
+size_t audio_generic_buffer_get_free(HWVoiceOut *hw)
+{
+    if (hw->buf_emul) {
+        return hw->size_emul - hw->pending_emul;
+    } else {
+        return hw->samples * hw->info.bytes_per_frame;
+    }
+}
+
 void audio_generic_run_buffer_out(HWVoiceOut *hw)
 {
     while (hw->pending_emul) {
@@ -1445,6 +1474,12 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
 {
     size_t total = 0;
 
+    if (hw->pcm_ops->buffer_get_free) {
+        size_t free = hw->pcm_ops->buffer_get_free(hw);
+
+        size = MIN(size, free);
+    }
+
     while (total < size) {
         size_t dst_size = size - total;
         size_t copy_size, proc;
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 928d8e107e..3c6a5eaa86 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -161,10 +161,14 @@ struct audio_pcm_ops {
     void   (*fini_out)(HWVoiceOut *hw);
     size_t (*write)   (HWVoiceOut *hw, void *buf, size_t size);
     void   (*run_buffer_out)(HWVoiceOut *hw);
+    /*
+     * Get the free output buffer size. This is an upper limit. The size
+     * returned by function get_buffer_out may be smaller.
+     */
+    size_t (*buffer_get_free)(HWVoiceOut *hw);
     /*
      * get a buffer that after later can be passed to put_buffer_out; optional
      * returns the buffer, and writes it's size to size (in bytes)
-     * this is unrelated to the above buffer_size_out function
      */
     void  *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
     /*
@@ -190,6 +194,7 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw);
 void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
 void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
 void audio_generic_run_buffer_out(HWVoiceOut *hw);
+size_t audio_generic_buffer_get_free(HWVoiceOut *hw);
 void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
 size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
 size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 1fdd1d4b14..91ea6ae975 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -283,6 +283,7 @@ static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
         coreaudio_buf_unlock(core, "coreaudio_" #name);             \
         return ret;                                             \
     }
+COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
 COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
                        (hw, size))
 COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
@@ -652,6 +653,8 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
     .fini_out = coreaudio_fini_out,
   /* wrapper for audio_generic_write */
     .write    = coreaudio_write,
+  /* wrapper for audio_generic_buffer_get_free */
+    .buffer_get_free = coreaudio_buffer_get_free,
   /* wrapper for audio_generic_get_buffer_out */
     .get_buffer_out = coreaudio_get_buffer_out,
   /* wrapper for audio_generic_put_buffer_out */
diff --git a/audio/jackaudio.c b/audio/jackaudio.c
index fe4d9d54c6..18c9fc666e 100644
--- a/audio/jackaudio.c
+++ b/audio/jackaudio.c
@@ -650,6 +650,7 @@ static struct audio_pcm_ops jack_pcm_ops = {
     .init_out       = qjack_init_out,
     .fini_out       = qjack_fini_out,
     .write          = qjack_write,
+    .buffer_get_free = audio_generic_buffer_get_free,
     .run_buffer_out = audio_generic_run_buffer_out,
     .enable_out     = qjack_enable_out,
 
diff --git a/audio/noaudio.c b/audio/noaudio.c
index aac87dbc93..84a6bfbb1c 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = {
     .init_out = no_init_out,
     .fini_out = no_fini_out,
     .write    = no_write,
+    .buffer_get_free = audio_generic_buffer_get_free,
     .run_buffer_out = audio_generic_run_buffer_out,
     .enable_out = no_enable_out,
 
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 60eff66424..1bd6800840 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -389,6 +389,17 @@ static void oss_run_buffer_out(HWVoiceOut *hw)
     }
 }
 
+static size_t oss_buffer_get_free(HWVoiceOut *hw)
+{
+    OSSVoiceOut *oss = (OSSVoiceOut *)hw;
+
+    if (oss->mmapped) {
+        return INT_MAX;
+    } else {
+        return audio_generic_buffer_get_free(hw);
+    }
+}
+
 static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
 {
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
@@ -750,6 +761,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
     .init_out = oss_init_out,
     .fini_out = oss_fini_out,
     .write    = oss_write,
+    .buffer_get_free = oss_buffer_get_free,
     .run_buffer_out = oss_run_buffer_out,
     .get_buffer_out = oss_get_buffer_out,
     .put_buffer_out = oss_put_buffer_out,
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index d6f3aa1a9a..e605c787ba 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -309,6 +309,7 @@ static void sdl_callback_in(void *opaque, Uint8 *buf, int len)
         SDL_UnlockAudioDevice(sdl->devid);                     \
     }
 
+SDL_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw), Out)
 SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
                  (hw, size), Out)
 SDL_WRAPPER_FUNC(put_buffer_out, size_t,
@@ -471,6 +472,8 @@ static struct audio_pcm_ops sdl_pcm_ops = {
     .fini_out = sdl_fini_out,
   /* wrapper for audio_generic_write */
     .write    = sdl_write,
+  /* wrapper for audio_generic_buffer_get_free */
+    .buffer_get_free = sdl_buffer_get_free,
   /* wrapper for audio_generic_get_buffer_out */
     .get_buffer_out = sdl_get_buffer_out,
   /* wrapper for audio_generic_put_buffer_out */
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 20e6853f85..ac666335c7 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -197,6 +197,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
     .init_out = wav_init_out,
     .fini_out = wav_fini_out,
     .write    = wav_write_out,
+    .buffer_get_free = audio_generic_buffer_get_free,
     .run_buffer_out = audio_generic_run_buffer_out,
     .enable_out = wav_enable_out,
 };
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 11/15] paaudio: reduce effective playback buffer size
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (9 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 10/15] audio: restore mixing-engine playback buffer size Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 12/15] dsoundaudio: " Volker Rümelin
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Add the buffer_get_free pcm_ops function to reduce the effective
playback buffer size. All intermediate audio playback buffers
become temporary buffers.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/paaudio.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index 9df1e69c08..d94f858ec7 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -201,13 +201,11 @@ unlock_and_fail:
     return 0;
 }
 
-static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
+static size_t qpa_buffer_get_free(HWVoiceOut *hw)
 {
-    PAVoiceOut *p = (PAVoiceOut *) hw;
+    PAVoiceOut *p = (PAVoiceOut *)hw;
     PAConnection *c = p->g->conn;
-    void *ret;
     size_t l;
-    int r;
 
     pa_threaded_mainloop_lock(c->mainloop);
 
@@ -216,7 +214,6 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
     if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
         /* wait for stream to become ready */
         l = 0;
-        ret = NULL;
         goto unlock;
     }
 
@@ -224,16 +221,33 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
     CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
                        "pa_stream_writable_size failed\n");
 
+unlock:
+    pa_threaded_mainloop_unlock(c->mainloop);
+    return l;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(c->mainloop);
+    return 0;
+}
+
+static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
+{
+    PAVoiceOut *p = (PAVoiceOut *)hw;
+    PAConnection *c = p->g->conn;
+    void *ret;
+    int r;
+
+    pa_threaded_mainloop_lock(c->mainloop);
+
+    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
+                    "pa_threaded_mainloop_lock failed\n");
+
     *size = -1;
     r = pa_stream_begin_write(p->stream, &ret, size);
     CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
                        "pa_stream_begin_write failed\n");
 
-unlock:
     pa_threaded_mainloop_unlock(c->mainloop);
-    if (*size > l) {
-        *size = l;
-    }
     return ret;
 
 unlock_and_fail:
@@ -901,6 +915,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
     .init_out = qpa_init_out,
     .fini_out = qpa_fini_out,
     .write    = qpa_write,
+    .buffer_get_free = qpa_buffer_get_free,
     .get_buffer_out = qpa_get_buffer_out,
     .put_buffer_out = qpa_put_buffer_out,
     .volume_out = qpa_volume_out,
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 12/15] dsoundaudio: reduce effective playback buffer size
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (10 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 11/15] paaudio: reduce effective " Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 13/15] ossaudio: " Volker Rümelin
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Add the buffer_get_free pcm_ops function to reduce the effective
playback buffer size. All intermediate audio playback buffers
become temporary buffers.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/dsoundaudio.c | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index cfc79c129e..1f743866e8 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -427,22 +427,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
     }
 }
 
-static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
+static size_t dsound_buffer_get_free(HWVoiceOut *hw)
 {
     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
     HRESULT hr;
-    DWORD ppos, wpos, act_size;
-    size_t req_size;
-    int err;
-    void *ret;
+    DWORD ppos, wpos;
 
     hr = IDirectSoundBuffer_GetCurrentPosition(
         dsb, &ppos, ds->first_time ? &wpos : NULL);
     if (FAILED(hr)) {
         dsound_logerr(hr, "Could not get playback buffer position\n");
-        *size = 0;
-        return NULL;
+        return 0;
     }
 
     if (ds->first_time) {
@@ -450,13 +446,20 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
         ds->first_time = false;
     }
 
-    req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
-    req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
+    return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
+}
 
-    if (req_size == 0) {
-        *size = 0;
-        return NULL;
-    }
+static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
+{
+    DSoundVoiceOut *ds = (DSoundVoiceOut *)hw;
+    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+    DWORD act_size;
+    size_t req_size;
+    int err;
+    void *ret;
+
+    req_size = MIN(*size, hw->size_emul - hw->pos_emul);
+    assert(req_size > 0);
 
     err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
                           &act_size, NULL, false, ds->s);
@@ -700,6 +703,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
     .init_out = dsound_init_out,
     .fini_out = dsound_fini_out,
     .write    = audio_generic_write,
+    .buffer_get_free = dsound_buffer_get_free,
     .get_buffer_out = dsound_get_buffer_out,
     .put_buffer_out = dsound_put_buffer_out,
     .enable_out = dsound_enable_out,
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 13/15] ossaudio: reduce effective playback buffer size
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (11 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 12/15] dsoundaudio: " Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-07 13:42   ` Philippe Mathieu-Daudé
  2022-01-06  9:23 ` [PATCH 14/15] paaudio: fix samples vs. frames mix-up Volker Rümelin
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Add the buffer_get_free pcm_ops function for the mmapped case
to reduce the effective playback buffer size. All intermediate
audio playback buffers become temporary buffers.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/ossaudio.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 1bd6800840..da9c232222 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -394,7 +394,7 @@ static size_t oss_buffer_get_free(HWVoiceOut *hw)
     OSSVoiceOut *oss = (OSSVoiceOut *)hw;
 
     if (oss->mmapped) {
-        return INT_MAX;
+        return oss_get_available_bytes(oss);
     } else {
         return audio_generic_buffer_get_free(hw);
     }
@@ -402,9 +402,10 @@ static size_t oss_buffer_get_free(HWVoiceOut *hw)
 
 static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
 {
-    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    OSSVoiceOut *oss = (OSSVoiceOut *)hw;
+
     if (oss->mmapped) {
-        *size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
+        *size = hw->size_emul - hw->pos_emul;
         return hw->buf_emul + hw->pos_emul;
     } else {
         return audio_generic_get_buffer_out(hw, size);
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 14/15] paaudio: fix samples vs. frames mix-up
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (12 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 13/15] ossaudio: " Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:23 ` [PATCH 15/15] sdlaudio: " Volker Rümelin
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Now that the mixing buffer size no longer adds to playback
latency, fix the samples vs. frames mix-up in the mixing buffer
size calculation. This change will go largely unnoticed as long
as the user doesn't use a buffer-size smaller than timer-period.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/paaudio.c | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index d94f858ec7..a53ed85e0b 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -549,11 +549,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    /*
-     * This is wrong. hw->samples counts in frames. hw->samples will be
-     * number of channels times larger than expected.
-     */
-    hw->samples = audio_buffer_samples(
+    /* hw->samples counts in frames */
+    hw->samples = audio_buffer_frames(
         qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
 
     return 0;
@@ -601,11 +598,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    /*
-     * This is wrong. hw->samples counts in frames. hw->samples will be
-     * number of channels times larger than expected.
-     */
-    hw->samples = audio_buffer_samples(
+    /* hw->samples counts in frames */
+    hw->samples = audio_buffer_frames(
         qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
 
     return 0;
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 15/15] sdlaudio: fix samples vs. frames mix-up
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (13 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 14/15] paaudio: fix samples vs. frames mix-up Volker Rümelin
@ 2022-01-06  9:23 ` Volker Rümelin
  2022-01-06  9:48 ` [PATCH 00/15] reduce audio playback latency Volker Rümelin
  2022-01-09 14:17 ` Christian Schoenebeck
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:23 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, qemu-devel

From: Volker Rümelin <vr_qemu@t-online.de>

Fix the same samples vs. frames mix-up that the previous commit
fixed for the PulseAudio backend.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
---
 audio/sdlaudio.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index e605c787ba..797b47bbdd 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -347,11 +347,8 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
     req.freq = as->freq;
     req.format = aud_to_sdlfmt (as->fmt);
     req.channels = as->nchannels;
-    /*
-     * This is wrong. SDL samples are QEMU frames. The buffer size will be
-     * the requested buffer size multiplied by the number of channels.
-     */
-    req.samples = audio_buffer_samples(
+    /* SDL samples are QEMU frames */
+    req.samples = audio_buffer_frames(
         qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
     req.callback = sdl_callback_out;
     req.userdata = sdl;
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/15] reduce audio playback latency
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (14 preceding siblings ...)
  2022-01-06  9:23 ` [PATCH 15/15] sdlaudio: " Volker Rümelin
@ 2022-01-06  9:48 ` Volker Rümelin
  2022-01-09 14:17 ` Christian Schoenebeck
  16 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-06  9:48 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, Christian Schoenebeck, qemu-devel

> This patch series reduces the playback latency for audio backends,
> in some cases significantly. For PulseAudio, the audio buffer is
> also moved from the QEMU side to the PulseAudio server side. This
> improves the drop-out safety for PulseAudio.
>

>
> Volker Rümelin (15):
>   audio: replace open-coded buffer arithmetic
>   audio: move function audio_pcm_hw_clip_out()
>   audio: add function audio_pcm_hw_conv_in()
>   audio: inline function audio_pcm_sw_get_rpos_in()
>   paaudio: increase default latency to 46ms
>   jackaudio: use more jack audio buffers
>   audio: copy playback stream in sequential order
>   audio: add pcm_ops function table for capture backend
>   audio: revert tests for pcm_ops table
>   audio: restore mixing-engine playback buffer size
>   paaudio: reduce effective playback buffer size
>   dsoundaudio: reduce effective playback buffer size
>   ossaudio: reduce effective playback buffer size
>   paaudio: fix samples vs. frames mix-up
>   sdlaudio: fix samples vs. frames mix-up
>
>  audio/alsaaudio.c   |   1 +
>  audio/audio.c       | 194 ++++++++++++++++++++++++--------------------
>  audio/audio_int.h   |   9 +-
>  audio/coreaudio.c   |  13 +--
>  audio/dsoundaudio.c |  30 ++++---
>  audio/jackaudio.c   |   5 +-
>  audio/noaudio.c     |   1 +
>  audio/ossaudio.c    |  17 +++-
>  audio/paaudio.c     |  49 ++++++-----
>  audio/sdlaudio.c    |  21 +++--
>  audio/wavaudio.c    |   1 +
>  11 files changed, 199 insertions(+), 142 deletions(-)
>

I used the wrong mail address to send these patches. I'll wait a few 
days for reviews before I send a version 2 series with the correct address.

With best regards,
Volker


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 01/15] audio: replace open-coded buffer arithmetic
  2022-01-06  9:23 ` [PATCH 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
@ 2022-01-06 10:18   ` Thomas Huth
  2022-01-07 12:10     ` Volker Rümelin
  0 siblings, 1 reply; 26+ messages in thread
From: Thomas Huth @ 2022-01-06 10:18 UTC (permalink / raw)
  To: Volker Rümelin, qemu-devel; +Cc: Christian Schoenebeck, Gerd Hoffmann

Am Thu,  6 Jan 2022 10:23:18 +0100
schrieb Volker Rümelin <volker.ruemelin@t-online.de>:

> From: Volker Rümelin <vr_qemu@t-online.de>
> 
> Replace open-coded buffer arithmetic with the available function
> audio_ring_dist(). Because the name audio_ring_dist implies it
> calculates the distance between two points, define the alias
> function name audio_ring_posb. That's the position in backward
> direction of a given point at a given distance.
> 
> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
> ---
>  audio/audio.c     | 25 +++++++------------------
>  audio/audio_int.h |  2 ++
>  audio/coreaudio.c | 10 ++++------
>  audio/sdlaudio.c  | 11 +++++------
>  4 files changed, 18 insertions(+), 30 deletions(-)
[...]
> diff --git a/audio/audio_int.h b/audio/audio_int.h
> index 428a091d05..928d8e107e 100644
> --- a/audio/audio_int.h
> +++ b/audio/audio_int.h
> @@ -266,6 +266,8 @@ static inline size_t audio_ring_dist(size_t dst,
> size_t src, size_t len) return (dst >= src) ? (dst - src) : (len -
> src + dst); }
>  
> +#define audio_ring_posb(pos, dist, len) audio_ring_dist(pos, dist,
> len)

I think this will be quite incomprehensible for people reading the
source code later (without seeing your commit message above). Thus I'd
prefer to directly use audio_ring_dist() everywhere instead. But in
case you insist on this macro, please add at least a comment in front
of it for explanation.

 Thomas


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 01/15] audio: replace open-coded buffer arithmetic
  2022-01-06 10:18   ` Thomas Huth
@ 2022-01-07 12:10     ` Volker Rümelin
  0 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-07 12:10 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel; +Cc: Christian Schoenebeck, Gerd Hoffmann

> Am Thu,  6 Jan 2022 10:23:18 +0100
> schrieb Volker Rümelin <volker.ruemelin@t-online.de>:
>
>> From: Volker Rümelin <vr_qemu@t-online.de>
>>
>> Replace open-coded buffer arithmetic with the available function
>> audio_ring_dist(). Because the name audio_ring_dist implies it
>> calculates the distance between two points, define the alias
>> function name audio_ring_posb. That's the position in backward
>> direction of a given point at a given distance.
>>
>> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
>> ---
>>   audio/audio.c     | 25 +++++++------------------
>>   audio/audio_int.h |  2 ++
>>   audio/coreaudio.c | 10 ++++------
>>   audio/sdlaudio.c  | 11 +++++------
>>   4 files changed, 18 insertions(+), 30 deletions(-)
> [...]
>> diff --git a/audio/audio_int.h b/audio/audio_int.h
>> index 428a091d05..928d8e107e 100644
>> --- a/audio/audio_int.h
>> +++ b/audio/audio_int.h
>> @@ -266,6 +266,8 @@ static inline size_t audio_ring_dist(size_t dst,
>> size_t src, size_t len) return (dst >= src) ? (dst - src) : (len -
>> src + dst); }
>>   
>> +#define audio_ring_posb(pos, dist, len) audio_ring_dist(pos, dist,
>> len)
> I think this will be quite incomprehensible for people reading the
> source code later (without seeing your commit message above). Thus I'd
> prefer to directly use audio_ring_dist() everywhere instead. But in
> case you insist on this macro, please add at least a comment in front
> of it for explanation.
>
>   Thomas

Yes you are right. Renaming variables with a macro doesn't improve 
readability.

With best regards,
Volker


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 02/15] audio: move function audio_pcm_hw_clip_out()
  2022-01-06  9:23 ` [PATCH 02/15] audio: move function audio_pcm_hw_clip_out() Volker Rümelin
@ 2022-01-07 13:30   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 26+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-01-07 13:30 UTC (permalink / raw)
  To: Volker Rümelin, Gerd Hoffmann; +Cc: qemu-devel

On 6/1/22 10:23, Volker Rümelin wrote:
> From: Volker Rümelin <vr_qemu@t-online.de>
> 
> Move the function audio_pcm_hw_clip_out() into the correct
> section 'Hard voice (playback)'.
> 
> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
> ---
>   audio/audio.c | 38 +++++++++++++++++++-------------------
>   1 file changed, 19 insertions(+), 19 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 13/15] ossaudio: reduce effective playback buffer size
  2022-01-06  9:23 ` [PATCH 13/15] ossaudio: " Volker Rümelin
@ 2022-01-07 13:42   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 26+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-01-07 13:42 UTC (permalink / raw)
  To: Volker Rümelin, Gerd Hoffmann; +Cc: qemu-devel

On 6/1/22 10:23, Volker Rümelin wrote:
> From: Volker Rümelin <vr_qemu@t-online.de>
> 
> Add the buffer_get_free pcm_ops function for the mmapped case
> to reduce the effective playback buffer size. All intermediate
> audio playback buffers become temporary buffers.

Description doesn't seem to match.

> 
> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
> ---
>   audio/ossaudio.c | 7 ++++---
>   1 file changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/audio/ossaudio.c b/audio/ossaudio.c
> index 1bd6800840..da9c232222 100644
> --- a/audio/ossaudio.c
> +++ b/audio/ossaudio.c
> @@ -394,7 +394,7 @@ static size_t oss_buffer_get_free(HWVoiceOut *hw)
>       OSSVoiceOut *oss = (OSSVoiceOut *)hw;
>   
>       if (oss->mmapped) {
> -        return INT_MAX;
> +        return oss_get_available_bytes(oss);
>       } else {
>           return audio_generic_buffer_get_free(hw);
>       }
> @@ -402,9 +402,10 @@ static size_t oss_buffer_get_free(HWVoiceOut *hw)
>   
>   static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
>   {
> -    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
> +    OSSVoiceOut *oss = (OSSVoiceOut *)hw;
> +
>       if (oss->mmapped) {
> -        *size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
> +        *size = hw->size_emul - hw->pos_emul;
>           return hw->buf_emul + hw->pos_emul;
>       } else {
>           return audio_generic_get_buffer_out(hw, size);



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/15] reduce audio playback latency
  2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
                   ` (15 preceding siblings ...)
  2022-01-06  9:48 ` [PATCH 00/15] reduce audio playback latency Volker Rümelin
@ 2022-01-09 14:17 ` Christian Schoenebeck
  2022-01-09 17:06   ` Volker Rümelin
  16 siblings, 1 reply; 26+ messages in thread
From: Christian Schoenebeck @ 2022-01-09 14:17 UTC (permalink / raw)
  To: Volker Rümelin; +Cc: Gerd Hoffmann, qemu-devel, Thomas Huth

On Donnerstag, 6. Januar 2022 10:21:47 CET Volker Rümelin wrote:
> This patch series reduces the playback latency for audio backends,
> in some cases significantly. For PulseAudio, the audio buffer is
> also moved from the QEMU side to the PulseAudio server side. This
> improves the drop-out safety for PulseAudio.
> 
> I actually measured the latency reduction with the PulseAudio
> backend. For the test I used my Linux host configured to play
> audio with PulseAudio. The guest was a Linux guest, also
> configured to use PulseAudio.

I haven't reviewed all the patches yet, but from what I read so far, does that 
mean the additional 3rd buffer is solely for PulseAudio, so for JACK and other 
backends these changes would overall be a degradation, wouldn't they?

> Measuring audio latencies is difficult. I played a sine tone in
> the guest with Audacity and measured the time from releasing the
> left mouse button until the tone can be heard. A few seconds
> before the measurement I started playback of an audio file with
> 10 minutes of silence to fill the audio buffers. The over-all
> latency can't be used to estimate the playback latency, but it
> can be used to calculate the playback latency reduction.
> 
> The measured over-all latency with PulseAudio is around 200ms
> without these patches and around 135ms with these patches. The
> difference of 65ms agrees well with the expected value of
> 46.4ms * 2 + 15ms - 46.4ms = 61.4ms. 46.4ms * 2 is the size of
> the mixing-engine buffer ("[PATCH 14/15] paaudio: fix samples vs.
> frames mix-up" explains the factor 2), 15ms is the server side
> PulseAudio buffer size used before these patches and 46.4ms is
> the new server side PulseAudio buffer size.
> 
> Volker Rümelin (15):
>    audio: replace open-coded buffer arithmetic
>    audio: move function audio_pcm_hw_clip_out()
>    audio: add function audio_pcm_hw_conv_in()
>    audio: inline function audio_pcm_sw_get_rpos_in()
>    paaudio: increase default latency to 46ms
>    jackaudio: use more jack audio buffers
>    audio: copy playback stream in sequential order
>    audio: add pcm_ops function table for capture backend
>    audio: revert tests for pcm_ops table
>    audio: restore mixing-engine playback buffer size
>    paaudio: reduce effective playback buffer size
>    dsoundaudio: reduce effective playback buffer size
>    ossaudio: reduce effective playback buffer size
>    paaudio: fix samples vs. frames mix-up
>    sdlaudio: fix samples vs. frames mix-up
> 
>   audio/alsaaudio.c   |   1 +
>   audio/audio.c       | 194 ++++++++++++++++++++++++--------------------
>   audio/audio_int.h   |   9 +-
>   audio/coreaudio.c   |  13 +--
>   audio/dsoundaudio.c |  30 ++++---
>   audio/jackaudio.c   |   5 +-
>   audio/noaudio.c     |   1 +
>   audio/ossaudio.c    |  17 +++-
>   audio/paaudio.c     |  49 ++++++-----
>   audio/sdlaudio.c    |  21 +++--
>   audio/wavaudio.c    |   1 +
>   11 files changed, 199 insertions(+), 142 deletions(-)




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/15] reduce audio playback latency
  2022-01-09 14:17 ` Christian Schoenebeck
@ 2022-01-09 17:06   ` Volker Rümelin
  2022-01-10 13:11     ` Christian Schoenebeck
  0 siblings, 1 reply; 26+ messages in thread
From: Volker Rümelin @ 2022-01-09 17:06 UTC (permalink / raw)
  To: Christian Schoenebeck; +Cc: Thomas Huth, Gerd Hoffmann, qemu-devel

> On Donnerstag, 6. Januar 2022 10:21:47 CET Volker Rümelin wrote:
>> This patch series reduces the playback latency for audio backends,
>> in some cases significantly. For PulseAudio, the audio buffer is
>> also moved from the QEMU side to the PulseAudio server side. This
>> improves the drop-out safety for PulseAudio.
>>
>> I actually measured the latency reduction with the PulseAudio
>> backend. For the test I used my Linux host configured to play
>> audio with PulseAudio. The guest was a Linux guest, also
>> configured to use PulseAudio.
> I haven't reviewed all the patches yet, but from what I read so far, does that
> mean the additional 3rd buffer is solely for PulseAudio, so for JACK and other
> backends these changes would overall be a degradation, wouldn't they?

No, nothing changes for JACK and it's an improvement for all the other 
backends where I added a buffer_get_free function. The important changes 
are in [PATCH 10/15] audio: restore mixing-engine playback buffer size. 
That patch tries to keep the mixing-engine buffer empty at the end of 
audio_run_out().

I couldn't reduce the playback latency for JACK, because the JACK audio 
buffers are already very small and any further reduction introduces 
playback glitches on my system.

For PulseAudio there is no additional buffer. I only increased the size 
of the server side buffer from 15ms to 46,4ms and added a 
buffer_get_free function. Before this patch series a few ten ms after 
playback started the mixing-engine buffer was full which added 2 * 
46,4ms to the playback latency. With these patches the mixing-engine 
buffer is empty. This looks like the buffer in use was moved from the 
mixing-engine to the PulseAudio server side.

>> Measuring audio latencies is difficult. I played a sine tone in
>> the guest with Audacity and measured the time from releasing the
>> left mouse button until the tone can be heard. A few seconds
>> before the measurement I started playback of an audio file with
>> 10 minutes of silence to fill the audio buffers. The over-all
>> latency can't be used to estimate the playback latency, but it
>> can be used to calculate the playback latency reduction.
>>
>> The measured over-all latency with PulseAudio is around 200ms
>> without these patches and around 135ms with these patches. The
>> difference of 65ms agrees well with the expected value of
>> 46.4ms * 2 + 15ms - 46.4ms = 61.4ms. 46.4ms * 2 is the size of
>> the mixing-engine buffer ("[PATCH 14/15] paaudio: fix samples vs.
>> frames mix-up" explains the factor 2), 15ms is the server side
>> PulseAudio buffer size used before these patches and 46.4ms is
>> the new server side PulseAudio buffer size.
>>
>> Volker Rümelin (15):
>>     audio: replace open-coded buffer arithmetic
>>     audio: move function audio_pcm_hw_clip_out()
>>     audio: add function audio_pcm_hw_conv_in()
>>     audio: inline function audio_pcm_sw_get_rpos_in()
>>     paaudio: increase default latency to 46ms
>>     jackaudio: use more jack audio buffers
>>     audio: copy playback stream in sequential order
>>     audio: add pcm_ops function table for capture backend
>>     audio: revert tests for pcm_ops table
>>     audio: restore mixing-engine playback buffer size
>>     paaudio: reduce effective playback buffer size
>>     dsoundaudio: reduce effective playback buffer size
>>     ossaudio: reduce effective playback buffer size
>>     paaudio: fix samples vs. frames mix-up
>>     sdlaudio: fix samples vs. frames mix-up
>>
>>    audio/alsaaudio.c   |   1 +
>>    audio/audio.c       | 194 ++++++++++++++++++++++++--------------------
>>    audio/audio_int.h   |   9 +-
>>    audio/coreaudio.c   |  13 +--
>>    audio/dsoundaudio.c |  30 ++++---
>>    audio/jackaudio.c   |   5 +-
>>    audio/noaudio.c     |   1 +
>>    audio/ossaudio.c    |  17 +++-
>>    audio/paaudio.c     |  49 ++++++-----
>>    audio/sdlaudio.c    |  21 +++--
>>    audio/wavaudio.c    |   1 +
>>    11 files changed, 199 insertions(+), 142 deletions(-)



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/15] reduce audio playback latency
  2022-01-09 17:06   ` Volker Rümelin
@ 2022-01-10 13:11     ` Christian Schoenebeck
  2022-01-10 21:50       ` Volker Rümelin
  0 siblings, 1 reply; 26+ messages in thread
From: Christian Schoenebeck @ 2022-01-10 13:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Volker Rümelin, Thomas Huth, Gerd Hoffmann

On Sonntag, 9. Januar 2022 18:06:44 CET Volker Rümelin wrote:
> > On Donnerstag, 6. Januar 2022 10:21:47 CET Volker Rümelin wrote:
> >> This patch series reduces the playback latency for audio backends,
> >> in some cases significantly. For PulseAudio, the audio buffer is
> >> also moved from the QEMU side to the PulseAudio server side. This
> >> improves the drop-out safety for PulseAudio.
> >> 
> >> I actually measured the latency reduction with the PulseAudio
> >> backend. For the test I used my Linux host configured to play
> >> audio with PulseAudio. The guest was a Linux guest, also
> >> configured to use PulseAudio.
> > 
> > I haven't reviewed all the patches yet, but from what I read so far, does
> > that mean the additional 3rd buffer is solely for PulseAudio, so for JACK
> > and other backends these changes would overall be a degradation, wouldn't
> > they?
> No, nothing changes for JACK and it's an improvement for all the other
> backends where I added a buffer_get_free function. The important changes
> are in [PATCH 10/15] audio: restore mixing-engine playback buffer size.
> That patch tries to keep the mixing-engine buffer empty at the end of
> audio_run_out().
> 
> I couldn't reduce the playback latency for JACK, because the JACK audio
> buffers are already very small and any further reduction introduces
> playback glitches on my system.

And that's actually my concern. A split 2 buffers -> 3 buffers while 
(approximately) retaining overall latency increases the chance of dropouts.

> For PulseAudio there is no additional buffer. I only increased the size
> of the server side buffer from 15ms to 46,4ms and added a
> buffer_get_free function. Before this patch series a few ten ms after
> playback started the mixing-engine buffer was full which added 2 *
> 46,4ms to the playback latency. With these patches the mixing-engine
> buffer is empty. This looks like the buffer in use was moved from the
> mixing-engine to the PulseAudio server side.




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/15] reduce audio playback latency
  2022-01-10 13:11     ` Christian Schoenebeck
@ 2022-01-10 21:50       ` Volker Rümelin
  0 siblings, 0 replies; 26+ messages in thread
From: Volker Rümelin @ 2022-01-10 21:50 UTC (permalink / raw)
  To: Christian Schoenebeck, qemu-devel; +Cc: Thomas Huth, Gerd Hoffmann

Am 10.01.22 um 14:11 schrieb Christian Schoenebeck:
> On Sonntag, 9. Januar 2022 18:06:44 CET Volker Rümelin wrote:
>>> On Donnerstag, 6. Januar 2022 10:21:47 CET Volker Rümelin wrote:
>>>> This patch series reduces the playback latency for audio backends,
>>>> in some cases significantly. For PulseAudio, the audio buffer is
>>>> also moved from the QEMU side to the PulseAudio server side. This
>>>> improves the drop-out safety for PulseAudio.
>>>>
>>>> I actually measured the latency reduction with the PulseAudio
>>>> backend. For the test I used my Linux host configured to play
>>>> audio with PulseAudio. The guest was a Linux guest, also
>>>> configured to use PulseAudio.
>>> I haven't reviewed all the patches yet, but from what I read so far, does
>>> that mean the additional 3rd buffer is solely for PulseAudio, so for JACK
>>> and other backends these changes would overall be a degradation, wouldn't
>>> they?
>> No, nothing changes for JACK and it's an improvement for all the other
>> backends where I added a buffer_get_free function. The important changes
>> are in [PATCH 10/15] audio: restore mixing-engine playback buffer size.
>> That patch tries to keep the mixing-engine buffer empty at the end of
>> audio_run_out().
>>
>> I couldn't reduce the playback latency for JACK, because the JACK audio
>> buffers are already very small and any further reduction introduces
>> playback glitches on my system.
> And that's actually my concern. A split 2 buffers -> 3 buffers while
> (approximately) retaining overall latency increases the chance of dropouts.

No, the 3 * 512 frames JACK buffer improves dropout safety compared to a 
2 * 512 frames buffer. Before my patches I could sometimes hear glitches 
when I switched from and to the QEMU GTK window. With the change to a 3 
* 512 frames JACK buffer, audio playback is glitch free and it comes 
without additional playback latency.

>
>> For PulseAudio there is no additional buffer. I only increased the size
>> of the server side buffer from 15ms to 46,4ms and added a
>> buffer_get_free function. Before this patch series a few ten ms after
>> playback started the mixing-engine buffer was full which added 2 *
>> 46,4ms to the playback latency. With these patches the mixing-engine
>> buffer is empty. This looks like the buffer in use was moved from the
>> mixing-engine to the PulseAudio server side.
>



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 09/15] audio: revert tests for pcm_ops table
  2022-01-06  9:23 ` [PATCH 09/15] audio: revert tests for pcm_ops table Volker Rümelin
@ 2022-01-13  9:43   ` Gerd Hoffmann
  0 siblings, 0 replies; 26+ messages in thread
From: Gerd Hoffmann @ 2022-01-13  9:43 UTC (permalink / raw)
  To: Volker Rümelin; +Cc: qemu-devel

On Thu, Jan 06, 2022 at 10:23:26AM +0100, Volker Rümelin wrote:
> From: Volker Rümelin <vr_qemu@t-online.de>
> 
> With previous commit every audio backend has a pcm_ops function
> table. It's no longer necessary to test if the table is
> available. This reverts commit cbaf25d1f5: "audio: fix
> wavcapture segfault"

You can just "git revert cbaf25d1f5" then, and append the reason
why you are reverting the commit to the commit message.

> 
> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
> ---
>  audio/audio.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/audio/audio.c b/audio/audio.c
> index 55f885f8e9..c420a8bd1c 100644
> --- a/audio/audio.c
> +++ b/audio/audio.c
> @@ -612,7 +612,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
>          total += isamp;
>      }
>  
> -    if (hw->pcm_ops && !hw->pcm_ops->volume_in) {
> +    if (!hw->pcm_ops->volume_in) {
>          mixeng_volume (sw->buf, ret, &sw->vol);
>      }
>  
> @@ -718,7 +718,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
>      if (swlim) {
>          sw->conv (sw->buf, buf, swlim);
>  
> -        if (sw->hw->pcm_ops && !sw->hw->pcm_ops->volume_out) {
> +        if (!sw->hw->pcm_ops->volume_out) {
>              mixeng_volume (sw->buf, swlim, &sw->vol);
>          }
>      }
> -- 
> 2.31.1
> 

-- 



^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2022-01-13  9:45 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-06  9:21 [PATCH 00/15] reduce audio playback latency Volker Rümelin
2022-01-06  9:23 ` [PATCH 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
2022-01-06 10:18   ` Thomas Huth
2022-01-07 12:10     ` Volker Rümelin
2022-01-06  9:23 ` [PATCH 02/15] audio: move function audio_pcm_hw_clip_out() Volker Rümelin
2022-01-07 13:30   ` Philippe Mathieu-Daudé
2022-01-06  9:23 ` [PATCH 03/15] audio: add function audio_pcm_hw_conv_in() Volker Rümelin
2022-01-06  9:23 ` [PATCH 04/15] audio: inline function audio_pcm_sw_get_rpos_in() Volker Rümelin
2022-01-06  9:23 ` [PATCH 05/15] paaudio: increase default latency to 46ms Volker Rümelin
2022-01-06  9:23 ` [PATCH 06/15] jackaudio: use more jack audio buffers Volker Rümelin
2022-01-06  9:23 ` [PATCH 07/15] audio: copy playback stream in sequential order Volker Rümelin
2022-01-06  9:23 ` [PATCH 08/15] audio: add pcm_ops function table for capture backend Volker Rümelin
2022-01-06  9:23 ` [PATCH 09/15] audio: revert tests for pcm_ops table Volker Rümelin
2022-01-13  9:43   ` Gerd Hoffmann
2022-01-06  9:23 ` [PATCH 10/15] audio: restore mixing-engine playback buffer size Volker Rümelin
2022-01-06  9:23 ` [PATCH 11/15] paaudio: reduce effective " Volker Rümelin
2022-01-06  9:23 ` [PATCH 12/15] dsoundaudio: " Volker Rümelin
2022-01-06  9:23 ` [PATCH 13/15] ossaudio: " Volker Rümelin
2022-01-07 13:42   ` Philippe Mathieu-Daudé
2022-01-06  9:23 ` [PATCH 14/15] paaudio: fix samples vs. frames mix-up Volker Rümelin
2022-01-06  9:23 ` [PATCH 15/15] sdlaudio: " Volker Rümelin
2022-01-06  9:48 ` [PATCH 00/15] reduce audio playback latency Volker Rümelin
2022-01-09 14:17 ` Christian Schoenebeck
2022-01-09 17:06   ` Volker Rümelin
2022-01-10 13:11     ` Christian Schoenebeck
2022-01-10 21:50       ` Volker Rümelin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.