diff mbox series

alsaloop: reduce cumulative error caused by non-atomic samples calculation

Message ID 1583785794-5173-1-git-send-email-ruslan.bilovol@gmail.com (mailing list archive)
State New, archived
Headers show
Series alsaloop: reduce cumulative error caused by non-atomic samples calculation | expand

Commit Message

Ruslan Bilovol March 9, 2020, 8:29 p.m. UTC
When doing loopback between two audio card with
same sampling frequency, I noticed slow increase
of pitch_diff.

When I changed order of get_queued_playback_samples()
vs get_queued_capture_samples(), I noticed same drift
of pitch_diff but if was decreasing this time.

This seems to be caused by non-atomic consecutive
snd_pcm_delay() invocation for playback then for
capture. snd_pcm_delay() measures delay between
read/write call and actual ADC/DAC operation.

So while we get this value for playback path in
get_queued_playback_samples(), next call to
get_queued_capture_samples() will happen a little
bit later so snd_pcm_delay() may return incorrect
value.

Be interleaving get_queued_{playback,capture}_samples()
order, we divide this small error between playback
and capture paths. I do not see any issues anymore
with one-way drift of pitch_diff.

Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
---
 alsaloop/pcmjob.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

Comments

Pavel Hofman March 11, 2020, 8:24 a.m. UTC | #1
Dne 09. 03. 20 v 21:29 Ruslan Bilovol napsal(a):
> 
> Be interleaving get_queued_{playback,capture}_samples()
> order, we divide this small error between playback
> and capture paths. I do not see any issues anymore
> with one-way drift of pitch_diff.

Nice simple and effective solution. Thanks a lot!
Jaroslav Kysela March 11, 2020, 8:45 a.m. UTC | #2
Dne 09. 03. 20 v 21:29 Ruslan Bilovol napsal(a):
> When doing loopback between two audio card with
> same sampling frequency, I noticed slow increase
> of pitch_diff.
> 
> When I changed order of get_queued_playback_samples()
> vs get_queued_capture_samples(), I noticed same drift
> of pitch_diff but if was decreasing this time.
> 
> This seems to be caused by non-atomic consecutive
> snd_pcm_delay() invocation for playback then for
> capture. snd_pcm_delay() measures delay between
> read/write call and actual ADC/DAC operation.
> 
> So while we get this value for playback path in
> get_queued_playback_samples(), next call to
> get_queued_capture_samples() will happen a little
> bit later so snd_pcm_delay() may return incorrect
> value.
> 
> Be interleaving get_queued_{playback,capture}_samples()
> order, we divide this small error between playback
> and capture paths. I do not see any issues anymore
> with one-way drift of pitch_diff.
> 
> Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>

Nice catch and implementation. Applied.

				Thank you,
					Jaroslav

> ---
>   alsaloop/pcmjob.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/alsaloop/pcmjob.c b/alsaloop/pcmjob.c
> index b252486..1b7925a 100644
> --- a/alsaloop/pcmjob.c
> +++ b/alsaloop/pcmjob.c
> @@ -1951,8 +1951,16 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
>   	}
>   	if (loop->sync != SYNC_TYPE_NONE) {
>   		snd_pcm_sframes_t pqueued, cqueued;
> -		pqueued = get_queued_playback_samples(loop);
> -		cqueued = get_queued_capture_samples(loop);
> +
> +		/* Reduce cumulative error by interleaving playback vs capture reading order */
> +		if (loop->total_queued_count & 1) {
> +			pqueued = get_queued_playback_samples(loop);
> +			cqueued = get_queued_capture_samples(loop);
> +		} else {
> +			cqueued = get_queued_capture_samples(loop);
> +			pqueued = get_queued_playback_samples(loop);
> +		}
> +
>   		if (verbose > 4)
>   			snd_output_printf(loop->output, "%s: queued %li/%li samples\n", loop->id, pqueued, cqueued);
>   		if (pqueued > 0)
>
diff mbox series

Patch

diff --git a/alsaloop/pcmjob.c b/alsaloop/pcmjob.c
index b252486..1b7925a 100644
--- a/alsaloop/pcmjob.c
+++ b/alsaloop/pcmjob.c
@@ -1951,8 +1951,16 @@  int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
 	}
 	if (loop->sync != SYNC_TYPE_NONE) {
 		snd_pcm_sframes_t pqueued, cqueued;
-		pqueued = get_queued_playback_samples(loop);
-		cqueued = get_queued_capture_samples(loop);
+
+		/* Reduce cumulative error by interleaving playback vs capture reading order */
+		if (loop->total_queued_count & 1) {
+			pqueued = get_queued_playback_samples(loop);
+			cqueued = get_queued_capture_samples(loop);
+		} else {
+			cqueued = get_queued_capture_samples(loop);
+			pqueued = get_queued_playback_samples(loop);
+		}
+
 		if (verbose > 4)
 			snd_output_printf(loop->output, "%s: queued %li/%li samples\n", loop->id, pqueued, cqueued);
 		if (pqueued > 0)