All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/15] reduce audio playback latency
@ 2022-01-22 12:52 Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
                   ` (14 more replies)
  0 siblings, 15 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:52 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Thomas Huth, Christian Schoenebeck, qemu-devel,
	Philippe Mathieu-Daudé

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.

For JACK users these patches don't reduce the playback latency
but improve drop-out safety. After patch "audio: restore mixing-
engine playback buffer size" the buffer size used is exactly
the same as before these patches.

v2:
[PATCH v2 01/15] audio: replace open-coded buffer arithmetic
Replaced the audio_ring_posb macro with an inline function. The
macro trick made the code quite unreadable. Compared to v1 the
generated code size stays the same.

[PATCH v2 06/15] jackaudio: use more jack audio buffers
Improved commit message.

[PATCH v2 09/15] Revert "audio: fix wavcapture segfault"
Used git revert cbaf25d1f5.

[PATCH v2 13/15] ossaudio: reduce effective playback buffer size
Improved commit message.

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
   Revert "audio: fix wavcapture segfault"
   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   |  13 ++-
  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, 203 insertions(+), 142 deletions(-)

-- 
2.31.1


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

* [PATCH v2 01/15] audio: replace open-coded buffer arithmetic
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  2022-01-23 17:40   ` Christian Schoenebeck
  2022-01-22 12:57 ` [PATCH v2 02/15] audio: move function audio_pcm_hw_clip_out() Volker Rümelin
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, Christian Schoenebeck, qemu-devel

Replace open-coded buffer arithmetic with the new function
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 |  6 ++++++
 audio/coreaudio.c | 10 ++++------
 audio/sdlaudio.c  | 11 +++++------
 4 files changed, 22 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..2fb459f34e 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -266,6 +266,12 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
     return (dst >= src) ? (dst - src) : (len - src + dst);
 }
 
+/* return new position in backward direction at given distance */
+static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len)
+{
+    return pos >= dist ? pos - dist : len - dist + pos;
+}
+
 #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] 22+ messages in thread

* [PATCH v2 02/15] audio: move function audio_pcm_hw_clip_out()
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 03/15] audio: add function audio_pcm_hw_conv_in() Volker Rümelin
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel, Philippe Mathieu-Daudé

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

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
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] 22+ messages in thread

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

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] 22+ messages in thread

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

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] 22+ messages in thread

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

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] 22+ messages in thread

* [PATCH v2 06/15] jackaudio: use more jack audio buffers
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
                   ` (4 preceding siblings ...)
  2022-01-22 12:57 ` [PATCH v2 05/15] paaudio: increase default latency to 46ms Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  2022-01-23 17:51   ` Christian Schoenebeck
  2022-01-22 12:57 ` [PATCH v2 07/15] audio: copy playback stream in sequential order Volker Rümelin
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Christian Schoenebeck, qemu-devel

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.

The larger jack audio buffer increases audio dropout safety,
because the high priority jack-audio worker threads can provide
audio data for a longer period of time as with a smaller buffer
and more audio data in the mixing engine buffer that they can't
access.

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 317009e936..26246c3a8b 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] 22+ messages in thread

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

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] 22+ messages in thread

* [PATCH v2 08/15] audio: add pcm_ops function table for capture backend
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
                   ` (6 preceding siblings ...)
  2022-01-22 12:57 ` [PATCH v2 07/15] audio: copy playback stream in sequential order Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 09/15] Revert "audio: fix wavcapture segfault" Volker Rümelin
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

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] 22+ messages in thread

* [PATCH v2 09/15] Revert "audio: fix wavcapture segfault"
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
                   ` (7 preceding siblings ...)
  2022-01-22 12:57 ` [PATCH v2 08/15] audio: add pcm_ops function table for capture backend Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 10/15] audio: restore mixing-engine playback buffer size Volker Rümelin
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

This reverts commit cbaf25d1f59ee13fc7542a06ea70784f2e000c04.

Since previous commit every audio backend has a pcm_ops function
table. It's no longer necessary to test if the table is available.

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] 22+ messages in thread

* [PATCH v2 10/15] audio: restore mixing-engine playback buffer size
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
                   ` (8 preceding siblings ...)
  2022-01-22 12:57 ` [PATCH v2 09/15] Revert "audio: fix wavcapture segfault" Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 11/15] paaudio: reduce effective " Volker Rümelin
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, Christian Schoenebeck, qemu-devel

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 2fb459f34e..8b042739ef 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 26246c3a8b..bf757250b5 100644
--- a/audio/jackaudio.c
+++ b/audio/jackaudio.c
@@ -652,6 +652,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] 22+ messages in thread

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

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] 22+ messages in thread

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

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 3dd2c4d4a6..231f3e65b3 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);
@@ -699,6 +702,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] 22+ messages in thread

* [PATCH v2 13/15] ossaudio: reduce effective playback buffer size
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
                   ` (11 preceding siblings ...)
  2022-01-22 12:57 ` [PATCH v2 12/15] dsoundaudio: " Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 14/15] paaudio: fix samples vs. frames mix-up Volker Rümelin
  2022-01-22 12:57 ` [PATCH v2 15/15] sdlaudio: " Volker Rümelin
  14 siblings, 0 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel, Philippe Mathieu-Daudé

Return the free buffer size for the mmapped case in function
oss_buffer_get_free() 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] 22+ messages in thread

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

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] 22+ messages in thread

* [PATCH v2 15/15] sdlaudio: fix samples vs. frames mix-up
  2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
                   ` (13 preceding siblings ...)
  2022-01-22 12:57 ` [PATCH v2 14/15] paaudio: fix samples vs. frames mix-up Volker Rümelin
@ 2022-01-22 12:57 ` Volker Rümelin
  14 siblings, 0 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-22 12:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Thomas Huth, qemu-devel

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] 22+ messages in thread

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

On Samstag, 22. Januar 2022 13:57:31 CET Volker Rümelin wrote:
> Replace open-coded buffer arithmetic with the new function
> 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>
> ---

First of all, getting rid of all those redundant, open coded ringbuffer
traversal code places highly makes sense!

>  audio/audio.c     | 25 +++++++------------------
>  audio/audio_int.h |  6 ++++++
>  audio/coreaudio.c | 10 ++++------
>  audio/sdlaudio.c  | 11 +++++------
>  4 files changed, 22 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);

Just refactoring so far, which looks good so far.

> diff --git a/audio/audio_int.h b/audio/audio_int.h
> index 428a091d05..2fb459f34e 100644
> --- a/audio/audio_int.h
> +++ b/audio/audio_int.h
> @@ -266,6 +266,12 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
>      return (dst >= src) ? (dst - src) : (len - src + dst);
>  }

You haven't touched this function, but while I am looking at it, all function
arguments are unsigned. So probably modulo operator might be used to get rid
of a branch here.

> 
> +/* return new position in backward direction at given distance */
> +static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len)
> +{
> +    return pos >= dist ? pos - dist : len - dist + pos;
> +}
> +

Which is the exact same code as the already existing audio_ring_dist()
function above, and I see that you actually already used this in v1 before:

#define audio_ring_posb(pos, dist, len) audio_ring_dist(pos, dist, len)

I would definitely not copy-paste the body. Thomas just suggested in v1 to add
a comment, not to duplicate the actual math code:
https://lore.kernel.org/qemu-devel/20220106111718.0ec25383@tuxfamily.org/

Also for consistency, I would have called the function audio_ring_rpos()
and would have commented each argument.

/**
 * Returns new position in ringbuffer in backward direction at given distance.
 * @pos: current position in ringbuffer
 * @dist: distance in ringbuffer to walk in reverse direction
 * @len: size of ringbuffer
 */
static inline size_t audio_ring_rpos(pos, dist, len) {
    return audio_ring_dist(pos, dist, len);
}

At least IMO a bit more comments on math code barely hurts.

>  #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);

This rest looks fine to me.

Best regards,
Christian Schoenebeck




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

* Re: [PATCH v2 06/15] jackaudio: use more jack audio buffers
  2022-01-22 12:57 ` [PATCH v2 06/15] jackaudio: use more jack audio buffers Volker Rümelin
@ 2022-01-23 17:51   ` Christian Schoenebeck
  0 siblings, 0 replies; 22+ messages in thread
From: Christian Schoenebeck @ 2022-01-23 17:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: Volker Rümelin, Gerd Hoffmann

On Samstag, 22. Januar 2022 13:57:36 CET Volker Rümelin wrote:
> 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.
> 
> The larger jack audio buffer increases audio dropout safety,
> because the high priority jack-audio worker threads can provide
> audio data for a longer period of time as with a smaller buffer
> and more audio data in the mixing engine buffer that they can't
> access.
> 
> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>

Reviewed-by: Christian Schoenebeck <qemu_oss@crudebyte.com>

> ---
>  audio/jackaudio.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/audio/jackaudio.c b/audio/jackaudio.c
> index 317009e936..26246c3a8b 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;





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

* Re: [PATCH v2 01/15] audio: replace open-coded buffer arithmetic
  2022-01-23 17:40   ` Christian Schoenebeck
@ 2022-01-25 18:57     ` Volker Rümelin
  2022-01-26  8:48       ` Gerd Hoffmann
  2022-01-26 12:01       ` Christian Schoenebeck
  0 siblings, 2 replies; 22+ messages in thread
From: Volker Rümelin @ 2022-01-25 18:57 UTC (permalink / raw)
  To: Christian Schoenebeck, qemu-devel; +Cc: Thomas Huth, Gerd Hoffmann

> On Samstag, 22. Januar 2022 13:57:31 CET Volker Rümelin wrote:
>> Replace open-coded buffer arithmetic with the new function
>> 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>
>> ---
> First of all, getting rid of all those redundant, open coded ringbuffer
> traversal code places highly makes sense!
>
>>   audio/audio.c     | 25 +++++++------------------
>>   audio/audio_int.h |  6 ++++++
>>   audio/coreaudio.c | 10 ++++------
>>   audio/sdlaudio.c  | 11 +++++------
>>   4 files changed, 22 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);
> Just refactoring so far, which looks good so far.
>
>> diff --git a/audio/audio_int.h b/audio/audio_int.h
>> index 428a091d05..2fb459f34e 100644
>> --- a/audio/audio_int.h
>> +++ b/audio/audio_int.h
>> @@ -266,6 +266,12 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
>>       return (dst >= src) ? (dst - src) : (len - src + dst);
>>   }
> You haven't touched this function, but while I am looking at it, all function
> arguments are unsigned. So probably modulo operator might be used to get rid
> of a branch here.

That would be "return (len - dist + pos) % len;" but on my x86_64 system 
I always prefer a conditional move instruction to a 64 bit integer 
division instruction.

>> +/* return new position in backward direction at given distance */
>> +static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len)
>> +{
>> +    return pos >= dist ? pos - dist : len - dist + pos;
>> +}
>> +
> Which is the exact same code as the already existing audio_ring_dist()
> function above, and I see that you actually already used this in v1 before:
>
> #define audio_ring_posb(pos, dist, len) audio_ring_dist(pos, dist, len)
>
> I would definitely not copy-paste the body. Thomas just suggested in v1 to add
> a comment, not to duplicate the actual math code:
> https://lore.kernel.org/qemu-devel/20220106111718.0ec25383@tuxfamily.org/
>
> Also for consistency, I would have called the function audio_ring_rpos()
> and would have commented each argument.

In the audio subsystem rpos is typically the read position. I chose posb 
to distinguish it from read position.

> /**
>   * Returns new position in ringbuffer in backward direction at given distance.
>   * @pos: current position in ringbuffer
>   * @dist: distance in ringbuffer to walk in reverse direction
>   * @len: size of ringbuffer
>   */

This comment is better than my comment. I'll use it in my v3 series.

> static inline size_t audio_ring_rpos(pos, dist, len) {
>      return audio_ring_dist(pos, dist, len);
> }

I don't think this inline function improves readability compared to my 
macro from v1. To understand the code you still have to replace 
parameter names in your mind. My v2 inline function can be understood at 
first glance.

With best regards,
Volker

> At least IMO a bit more comments on math code barely hurts.
>
>>   #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);
> This rest looks fine to me.
>
> Best regards,
> Christian Schoenebeck
>
>



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

* Re: [PATCH v2 01/15] audio: replace open-coded buffer arithmetic
  2022-01-25 18:57     ` Volker Rümelin
@ 2022-01-26  8:48       ` Gerd Hoffmann
  2022-03-01 18:59         ` Volker Rümelin
  2022-01-26 12:01       ` Christian Schoenebeck
  1 sibling, 1 reply; 22+ messages in thread
From: Gerd Hoffmann @ 2022-01-26  8:48 UTC (permalink / raw)
  To: Volker Rümelin; +Cc: Thomas Huth, Christian Schoenebeck, qemu-devel

  Hi,

> > > --- a/audio/audio_int.h
> > > +++ b/audio/audio_int.h
> > > @@ -266,6 +266,12 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
> > >       return (dst >= src) ? (dst - src) : (len - src + dst);
> > >   }
> > You haven't touched this function, but while I am looking at it, all function
> > arguments are unsigned. So probably modulo operator might be used to get rid
> > of a branch here.
> 
> That would be "return (len - dist + pos) % len;" but on my x86_64 system I
> always prefer a conditional move instruction to a 64 bit integer division
> instruction.

Why?  Performance?

Don't underestimate the optimizer of a modern compiler.  In many cases
it simply isn't worth the effort.  Better optimize the code for humans,
i.e. make it easy to read and understand.

take care,
  Gerd



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

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

On Dienstag, 25. Januar 2022 19:57:59 CET Volker Rümelin wrote:
> > On Samstag, 22. Januar 2022 13:57:31 CET Volker Rümelin wrote:
> >> Replace open-coded buffer arithmetic with the new function
> >> 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>
> >> ---
> > 
> > First of all, getting rid of all those redundant, open coded ringbuffer
> > traversal code places highly makes sense!
> > 
> >>   audio/audio.c     | 25 +++++++------------------
> >>   audio/audio_int.h |  6 ++++++
> >>   audio/coreaudio.c | 10 ++++------
> >>   audio/sdlaudio.c  | 11 +++++------
> >>   4 files changed, 22 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);
> > 
> > Just refactoring so far, which looks good so far.
> > 
> >> diff --git a/audio/audio_int.h b/audio/audio_int.h
> >> index 428a091d05..2fb459f34e 100644
> >> --- a/audio/audio_int.h
> >> +++ b/audio/audio_int.h
> >> @@ -266,6 +266,12 @@ static inline size_t audio_ring_dist(size_t dst,
> >> size_t src, size_t len)>> 
> >>       return (dst >= src) ? (dst - src) : (len - src + dst);
> >>   
> >>   }
> > 
> > You haven't touched this function, but while I am looking at it, all
> > function arguments are unsigned. So probably modulo operator might be
> > used to get rid of a branch here.
> 
> That would be "return (len - dist + pos) % len;" but on my x86_64 system
> I always prefer a conditional move instruction to a 64 bit integer
> division instruction.

Nevermind, I just mentioned it as a side note, here in QEMU it probably 
doesn't matter at all. In other apps this is sometimes used in tight loops 
where it can make a measurable difference to get rid of the branch(es).

> 
> >> +/* return new position in backward direction at given distance */
> >> +static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t
> >> len)
> >> +{
> >> +    return pos >= dist ? pos - dist : len - dist + pos;
> >> +}
> >> +
> > 
> > Which is the exact same code as the already existing audio_ring_dist()
> > function above, and I see that you actually already used this in v1
> > before:
> > 
> > #define audio_ring_posb(pos, dist, len) audio_ring_dist(pos, dist, len)
> > 
> > I would definitely not copy-paste the body. Thomas just suggested in v1 to
> > add a comment, not to duplicate the actual math code:
> > https://lore.kernel.org/qemu-devel/20220106111718.0ec25383@tuxfamily.org/
> > 
> > Also for consistency, I would have called the function audio_ring_rpos()
> > and would have commented each argument.
> 
> In the audio subsystem rpos is typically the read position. I chose posb
> to distinguish it from read position.
> 
> > /**
> > 
> >   * Returns new position in ringbuffer in backward direction at given
> >   distance. * @pos: current position in ringbuffer
> >   * @dist: distance in ringbuffer to walk in reverse direction
> >   * @len: size of ringbuffer
> >   */
> 
> This comment is better than my comment. I'll use it in my v3 series.
> 
> > static inline size_t audio_ring_rpos(pos, dist, len) {
> > 
> >      return audio_ring_dist(pos, dist, len);
> > 
> > }
> 
> I don't think this inline function improves readability compared to my
> macro from v1. To understand the code you still have to replace
> parameter names in your mind. My v2 inline function can be understood at
> first glance.

I didn't mean readability. Believe it or not, it took me a bit to realize that 
it was the exact same code as above.

Best regards,
Christian Schoenebeck




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

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

Am 26.01.22 um 09:48 schrieb Gerd Hoffmann:
>    Hi,
>
>>>> --- a/audio/audio_int.h
>>>> +++ b/audio/audio_int.h
>>>> @@ -266,6 +266,12 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
>>>>        return (dst >= src) ? (dst - src) : (len - src + dst);
>>>>    }
>>> You haven't touched this function, but while I am looking at it, all function
>>> arguments are unsigned. So probably modulo operator might be used to get rid
>>> of a branch here.
>> That would be "return (len - dist + pos) % len;" but on my x86_64 system I
>> always prefer a conditional move instruction to a 64 bit integer division
>> instruction.
> Why?  Performance?
>
> Don't underestimate the optimizer of a modern compiler.  In many cases
> it simply isn't worth the effort.  Better optimize the code for humans,
> i.e. make it easy to read and understand.

I know this micro optimization is useless. I will not try to defend it 
because there are no good reasons for it. But I can't see that "return 
(len - dist + pos) % len;" is more readable than "return pos >= dist ? 
pos - dist : len - dist + pos;".

With best regards
Volker

> take care,
>    Gerd
>



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

end of thread, other threads:[~2022-03-01 19:01 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-22 12:52 [PATCH v2 00/15] reduce audio playback latency Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 01/15] audio: replace open-coded buffer arithmetic Volker Rümelin
2022-01-23 17:40   ` Christian Schoenebeck
2022-01-25 18:57     ` Volker Rümelin
2022-01-26  8:48       ` Gerd Hoffmann
2022-03-01 18:59         ` Volker Rümelin
2022-01-26 12:01       ` Christian Schoenebeck
2022-01-22 12:57 ` [PATCH v2 02/15] audio: move function audio_pcm_hw_clip_out() Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 03/15] audio: add function audio_pcm_hw_conv_in() Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 04/15] audio: inline function audio_pcm_sw_get_rpos_in() Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 05/15] paaudio: increase default latency to 46ms Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 06/15] jackaudio: use more jack audio buffers Volker Rümelin
2022-01-23 17:51   ` Christian Schoenebeck
2022-01-22 12:57 ` [PATCH v2 07/15] audio: copy playback stream in sequential order Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 08/15] audio: add pcm_ops function table for capture backend Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 09/15] Revert "audio: fix wavcapture segfault" Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 10/15] audio: restore mixing-engine playback buffer size Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 11/15] paaudio: reduce effective " Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 12/15] dsoundaudio: " Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 13/15] ossaudio: " Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 14/15] paaudio: fix samples vs. frames mix-up Volker Rümelin
2022-01-22 12:57 ` [PATCH v2 15/15] sdlaudio: " 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.