File 0025-alsaloop-Fixes-and-added-workaround-option.patch of Package alsa-utils

From bee994f509e6829647aa68fd276f5e8821d15445 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Mon, 11 Oct 2010 10:24:14 +0200
Subject: [PATCH 25/38] alsaloop: Fixes and added --workaround option

- added workaround for alsa-lib (pthread configuration parsing issue) -
  the workaround must be activated manually using ('--workaround serialopen')
- fixed avail_min initialization (caused high CPU usage or xruns)
- fixed shared buffer initialization (both capture and playback buffers
  must have equal number of samples in this config)

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
 alsaloop/Makefile.am |    1 +
 alsaloop/alsaloop.c  |    9 ++++-
 alsaloop/alsaloop.h  |    3 +
 alsaloop/pcmjob.c    |  111 ++++++++++++++++++++++++++++++++++++++------------
 alsaloop/test.sh     |   26 +++++++-----
 5 files changed, 112 insertions(+), 38 deletions(-)

diff --git a/alsaloop/Makefile.am b/alsaloop/Makefile.am
index 97d2e6f..f76eafd 100644
--- a/alsaloop/Makefile.am
+++ b/alsaloop/Makefile.am
@@ -1,5 +1,6 @@
 INCLUDES = -I$(top_srcdir)/include
 LDADD = -lm
+CFLAGS += -D_GNU_SOURCE
 if HAVE_SAMPLERATE
 LDADD += -lsamplerate
 endif
diff --git a/alsaloop/alsaloop.c b/alsaloop/alsaloop.c
index 97b00d5..3af3dd7 100644
--- a/alsaloop/alsaloop.c
+++ b/alsaloop/alsaloop.c
@@ -43,6 +43,7 @@ struct loopback_thread {
 };
 
 int verbose = 0;
+int workarounds = 0;
 int daemonize = 0;
 int use_syslog = 0;
 struct loopback **loopbacks = NULL;
@@ -171,6 +172,7 @@ void help(void)
 "		    ALSA_ID@OSS_ID  (for example: \"Master@VOLUME\")\n"
 "-e,--effect    apply an effect (bandpass filter sweep)\n"
 "-v,--verbose   verbose mode (more -v means more verbose)\n"
+"-w,--workaround use workaround (serialopen)\n"
 );
 	printf("\nRecognized sample formats are:");
 	for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
@@ -339,6 +341,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
 		{"thread", 1, NULL, 'T'},
 		{"mixer", 1, NULL, 'm'},
 		{"ossmixer", 1, NULL, 'O'},
+		{"workaround", 1, NULL, 'w'},
 		{NULL, 0, NULL, 0},
 	};
 	int err, morehelp;
@@ -370,7 +373,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
 	while (1) {
 		int c;
 		if ((c = getopt_long(argc, argv,
-				"hdg:P:C:l:t:F:f:c:r:s:benvA:S:a:m:T:O:",
+				"hdg:P:C:l:t:F:f:c:r:s:benvA:S:a:m:T:O:w:",
 				long_option, NULL)) < 0)
 			break;
 		switch (c) {
@@ -504,6 +507,10 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
 		case 'v':
 			verbose++;
 			break;
+		case 'w':
+			if (strcasecmp(optarg, "serialopen") == 0)
+				workarounds |= WORKAROUND_SERIALOPEN;
+			break;
 		}
 	}
 
diff --git a/alsaloop/alsaloop.h b/alsaloop/alsaloop.h
index 366a296..6c4f40a 100644
--- a/alsaloop/alsaloop.h
+++ b/alsaloop/alsaloop.h
@@ -40,6 +40,8 @@ enum {
 #define FILE_CWRITE "/tmp/alsaloop.craw"
 #endif
 
+#define WORKAROUND_SERIALOPEN	(1<<0)
+
 typedef enum _sync_type {
 	SYNC_TYPE_NONE = 0,
 	SYNC_TYPE_SIMPLE,	/* add or remove samples */
@@ -171,6 +173,7 @@ struct loopback {
 };
 
 extern int verbose;
+extern int workarounds;
 extern int use_syslog;
 
 #define logit(priority, fmt, args...) do {		\
diff --git a/alsaloop/pcmjob.c b/alsaloop/pcmjob.c
index df835f0..8b3e568 100644
--- a/alsaloop/pcmjob.c
+++ b/alsaloop/pcmjob.c
@@ -31,9 +31,11 @@
 #include <sys/time.h>
 #include <math.h>
 #include <syslog.h>
+#include <pthread.h>
 #include "alsaloop.h"
 
 static int set_rate_shift(struct loopback_handle *lhandle, double pitch);
+static int get_rate(struct loopback_handle *lhandle);
 
 #define SYNCTYPE(v) [SYNC_TYPE_##v] = #v
 
@@ -56,6 +58,21 @@ static const char *src_types[] = {
 	SRCTYPE(LINEAR)
 };
 
+static pthread_mutex_t pcm_open_mutex =
+                                PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+static inline void pcm_open_lock(void)
+{
+	if (workarounds & WORKAROUND_SERIALOPEN)
+	        pthread_mutex_lock(&pcm_open_mutex);
+}
+ 
+static inline void pcm_open_unlock(void)
+{
+	if (workarounds & WORKAROUND_SERIALOPEN)
+	        pthread_mutex_unlock(&pcm_open_mutex);
+}
+
 static inline snd_pcm_uframes_t get_whole_latency(struct loopback *loop)
 {
 	return loop->latency;
@@ -76,7 +93,7 @@ static int setparams_stream(struct loopback_handle *lhandle,
 
 	err = snd_pcm_hw_params_any(handle, params);
 	if (err < 0) {
-		logit(LOG_CRIT, "Broken configuration for %s PCM: no configurations available: %s\n", snd_strerror(err), lhandle->id);
+		logit(LOG_CRIT, "Broken configuration for %s PCM: no configurations available: %s\n", lhandle->id, snd_strerror(err));
 		return err;
 	}
 	err = snd_pcm_hw_params_set_rate_resample(handle, params, lhandle->resample);
@@ -153,6 +170,8 @@ static int setparams_bufsize(struct loopback_handle *lhandle,
 		goto __again;
 	}
 	snd_pcm_hw_params_get_buffer_size(params, &periodsize);
+	if (verbose > 6)
+		snd_output_printf(lhandle->loopback->output, "%s: buffer_size=%li\n", lhandle->id, periodsize);
 	if (lhandle->period_size_req > 0)
 		periodsize = lhandle->period_size_req;
 	else
@@ -163,6 +182,8 @@ static int setparams_bufsize(struct loopback_handle *lhandle,
 		goto __again;
 	}
 	snd_pcm_hw_params_get_period_size(params, &periodsize, NULL);
+	if (verbose > 6)
+		snd_output_printf(lhandle->loopback->output, "%s: period_size=%li\n", lhandle->id, periodsize);
 	if (periodsize != bufsize)
 		bufsize = periodsize;
 	snd_pcm_hw_params_get_buffer_size(params, &buffersize);
@@ -175,11 +196,12 @@ static int setparams_bufsize(struct loopback_handle *lhandle,
 
 static int setparams_set(struct loopback_handle *lhandle,
 			 snd_pcm_hw_params_t *params,
-			 snd_pcm_sw_params_t *swparams)
+			 snd_pcm_sw_params_t *swparams,
+			 snd_pcm_uframes_t bufsize)
 {
 	snd_pcm_t *handle = lhandle->handle;
 	int err;
-	snd_pcm_uframes_t val, val1;
+	snd_pcm_uframes_t val, period_size, buffer_size;
 
 	err = snd_pcm_hw_params(handle, params);
 	if (err < 0) {
@@ -196,21 +218,29 @@ static int setparams_set(struct loopback_handle *lhandle,
 		logit(LOG_CRIT, "Unable to set start threshold mode for %s: %s\n", lhandle->id, snd_strerror(err));
 		return err;
 	}
-	snd_pcm_hw_params_get_period_size(params, &val, NULL);
-	snd_pcm_hw_params_get_buffer_size(params, &val1);
+	snd_pcm_hw_params_get_period_size(params, &period_size, NULL);
+	snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
 	if (lhandle->nblock) {
 		if (lhandle == lhandle->loopback->play) {
-			val = val1 - (2 * val - 4);
+			val = buffer_size - (2 * period_size - 4);
 		} else {
 			val = 4;
 		}
+		if (verbose > 6)
+			snd_output_printf(lhandle->loopback->output, "%s: avail_min1=%li\n", lhandle->id, val);
 	} else {
 		if (lhandle == lhandle->loopback->play) {
-			snd_pcm_hw_params_get_buffer_size(params, &val1);
-			val = val1 - val - val / 2;
+			val = bufsize / 2 + bufsize / 4 + bufsize / 8;
+			if (val > buffer_size / 4)
+				val = buffer_size / 4;
+			val = buffer_size - val;
 		} else {
-			val /= 2;
+			val = bufsize / 2;
+			if (val > buffer_size / 4)
+				val = buffer_size / 4;
 		}
+		if (verbose > 6)
+			snd_output_printf(lhandle->loopback->output, "%s: avail_min2=%li\n", lhandle->id, val);
 	}
 	err = snd_pcm_sw_params_set_avail_min(handle, swparams, val);
 	if (err < 0) {
@@ -256,11 +286,11 @@ static int setparams(struct loopback *loop, snd_pcm_uframes_t bufsize)
 		return err;
 	}
 
-	if ((err = setparams_set(loop->play, p_params, p_swparams)) < 0) {
+	if ((err = setparams_set(loop->play, p_params, p_swparams, bufsize / loop->play->pitch)) < 0) {
 		logit(LOG_CRIT, "Unable to set sw parameters for %s stream: %s\n", loop->play->id, snd_strerror(err));
 		return err;
 	}
-	if ((err = setparams_set(loop->capt, c_params, c_swparams)) < 0) {
+	if ((err = setparams_set(loop->capt, c_params, c_swparams, bufsize / loop->capt->pitch)) < 0) {
 		logit(LOG_CRIT, "Unable to set sw parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err));
 		return err;
 	}
@@ -495,6 +525,12 @@ static int readit(struct loopback_handle *lhandle)
 	int err;
 
 	avail = snd_pcm_avail_update(lhandle->handle);
+	if (avail == -EPIPE) {
+		return xrun(lhandle);
+	} else if (avail == -ESTRPIPE) {
+		if ((err = suspend(lhandle)) < 0)
+			return err;
+	}
 	if (avail > buf_avail(lhandle)) {
 		lhandle->buf_over += avail - buf_avail(lhandle);
 		avail = buf_avail(lhandle);
@@ -1008,7 +1044,10 @@ static int openit(struct loopback_handle *lhandle)
 				SND_PCM_STREAM_PLAYBACK :
 				SND_PCM_STREAM_CAPTURE;
 	int err, card, device, subdevice;
-	if ((err = snd_pcm_open(&lhandle->handle, lhandle->device, stream, SND_PCM_NONBLOCK)) < 0) {
+	pcm_open_lock();
+	err = snd_pcm_open(&lhandle->handle, lhandle->device, stream, SND_PCM_NONBLOCK);
+	pcm_open_unlock();
+	if (err < 0) {
 		logit(LOG_CRIT, "%s open error: %s\n", lhandle->id, snd_strerror(err));
 		return err;
 	}
@@ -1027,7 +1066,9 @@ static int openit(struct loopback_handle *lhandle)
 	if (card >= 0) {
 		char name[16];
 		sprintf(name, "hw:%i", card);
+		pcm_open_lock();
 		err = snd_ctl_open(&lhandle->ctl, name, SND_CTL_NONBLOCK);
+		pcm_open_unlock();
 		if (err < 0) {
 			logit(LOG_CRIT, "%s [%s] ctl open error: %s\n", lhandle->id, name, snd_strerror(err));
 			lhandle->ctl = NULL;
@@ -1254,6 +1295,17 @@ int pcmjob_start(struct loopback *loop)
 				goto __error;
 			}
 			loop->play->buf = nbuf;
+			loop->play->buf_size = loop->capt->buf_size;
+		} else if (loop->capt->buf_size < loop->play->buf_size) {
+			char *nbuf = realloc(loop->capt->buf,
+					     loop->play->buf_size *
+					       loop->play->frame_size);
+			if (nbuf == NULL) {
+				err = -ENOMEM;
+				goto __error;
+			}
+			loop->capt->buf = nbuf;
+			loop->capt->buf_size = loop->play->buf_size;
 		}
 		loop->capt->buf = loop->play->buf;
 	} else {
@@ -1278,7 +1330,7 @@ int pcmjob_start(struct loopback *loop)
 	if (loop->use_samplerate) {
 		if (loop->capt->format != SND_PCM_FORMAT_S16 ||
 		    loop->play->format != SND_PCM_FORMAT_S16) {
-			logit(LOG_CRIT, "samplerate conversion supports only S16_LE format (%i, %i)\n", loop->play->format, loop->capt->format);
+			logit(LOG_CRIT, "samplerate conversion supports only %s format (play=%s, capt=%s)\n", snd_pcm_format_name(SND_PCM_FORMAT_S16), snd_pcm_format_name(loop->play->format), snd_pcm_format_name(loop->capt->format));
 			loop->use_samplerate = 0;
 			err = -EIO;
 			goto __error;		
@@ -1325,6 +1377,8 @@ int pcmjob_start(struct loopback *loop)
 		logit(LOG_CRIT, "%s: silence error\n", loop->id);
 		goto __error;
 	}
+	if (verbose > 4)
+		snd_output_printf(loop->output, "%s: capt->buffer_size = %li, play->buffer_size = %li\n", loop->id, loop->capt->buf_size, loop->play->buf_size);
 	loop->pitch = 1.0;
 	update_pitch(loop);
 	loop->pitch_delta = 1.0 / ((double)loop->capt->rate * 4);
@@ -1334,8 +1388,13 @@ int pcmjob_start(struct loopback *loop)
 	loop->play->buf_count = count;
 	if (loop->play->buf == loop->capt->buf)
 		loop->capt->buf_pos = count;
-	if (writeit(loop->play) != count) {
-		logit(LOG_CRIT, "%s: initial playback fill error\n", loop->id);
+	err = writeit(loop->play);
+	if (verbose > 4)
+		snd_output_printf(loop->output, "%s: silence queued %i samples\n", loop->id, err);
+	if (count > loop->play->buffer_size)
+		count = loop->play->buffer_size;
+	if (err != count) {
+		logit(LOG_CRIT, "%s: initial playback fill error (%i/%i/%i)\n", loop->id, err, (int)count, loop->play->buffer_size);
 		err = -EIO;
 		goto __error;
 	}
@@ -1511,13 +1570,13 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
 	if (verbose > 12) {
 		snd_pcm_sframes_t pdelay, cdelay;
 		if ((err = snd_pcm_delay(play->handle, &pdelay)) < 0)
-			snd_output_printf(loop->output, "%s: delay error: %s\n", play->id, snd_strerror(err));
+			snd_output_printf(loop->output, "%s: delay error: %s / %li / %li\n", play->id, snd_strerror(err), play->buf_size, play->buf_count);
 		else
-			snd_output_printf(loop->output, "%s: delay %li\n", play->id, pdelay);
+			snd_output_printf(loop->output, "%s: delay %li / %li / %li\n", play->id, pdelay, play->buf_size, play->buf_count);
 		if ((err = snd_pcm_delay(capt->handle, &cdelay)) < 0)
-			snd_output_printf(loop->output, "%s: delay error: %s\n", capt->id, snd_strerror(err));
+			snd_output_printf(loop->output, "%s: delay error: %s / %li / %li\n", capt->id, snd_strerror(err), capt->buf_size, capt->buf_count);
 		else
-			snd_output_printf(loop->output, "%s: delay %li\n", capt->id, cdelay);
+			snd_output_printf(loop->output, "%s: delay %li / %li / %li\n", capt->id, cdelay, capt->buf_size, capt->buf_count);
 	}
 	idx = 0;
 	if (loop->running) {
@@ -1570,7 +1629,7 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
 	}
 	if (verbose > 9)
 		snd_output_printf(loop->output, "%s: prevents = 0x%x, crevents = 0x%x\n", loop->id, prevents, crevents);
-	if (prevents == 0 && crevents == 0)
+	if (!loop->running)
 		goto __pcm_end;
 	do {
 		ccount = readit(capt);
@@ -1637,22 +1696,22 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
 		if (verbose > 4)
 			snd_output_printf(loop->output, "%s: queued %li/%li samples\n", loop->id, pqueued, cqueued);
 		if (pqueued > 0)
-			loop->play->total_queued += pqueued;
+			play->total_queued += pqueued;
 		if (cqueued > 0)
-			loop->capt->total_queued += cqueued;
+			capt->total_queued += cqueued;
 		if (pqueued > 0 || cqueued > 0)
 			loop->total_queued_count += 1;
 	}
 	if (verbose > 12) {
 		snd_pcm_sframes_t pdelay, cdelay;
 		if ((err = snd_pcm_delay(play->handle, &pdelay)) < 0)
-			snd_output_printf(loop->output, "%s: end delay error: %s\n", play->id, snd_strerror(err));
+			snd_output_printf(loop->output, "%s: end delay error: %s / %li / %li\n", play->id, snd_strerror(err), play->buf_size, play->buf_count);
 		else
-			snd_output_printf(loop->output, "%s: end delay %li\n", play->id, pdelay);
+			snd_output_printf(loop->output, "%s: end delay %li / %li / %li\n", play->id, pdelay, play->buf_size, play->buf_count);
 		if ((err = snd_pcm_delay(capt->handle, &cdelay)) < 0)
-			snd_output_printf(loop->output, "%s: end delay error: %s\n", capt->id, snd_strerror(err));
+			snd_output_printf(loop->output, "%s: end delay error: %s / %li / %li\n", capt->id, snd_strerror(err), capt->buf_size, capt->buf_count);
 		else
-			snd_output_printf(loop->output, "%s: end delay %li\n", capt->id, cdelay);
+			snd_output_printf(loop->output, "%s: end delay %li / %li / %li\n", capt->id, cdelay, capt->buf_size, capt->buf_count);
 	}
       __pcm_end:
 	if (verbose > 13) {
diff --git a/alsaloop/test.sh b/alsaloop/test.sh
index 91f4cbc..e3d81b1 100755
--- a/alsaloop/test.sh
+++ b/alsaloop/test.sh
@@ -3,6 +3,7 @@
 #DBG="gdb --args "
 #DBG="strace"
 #DBG="valgrind --leak-check=full"
+ARGS=
 CFGFILE="/tmp/alsaloop.test.cfg"
 
 test1() {
@@ -13,7 +14,8 @@ test1() {
     --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
     --mixer "name='PCM Playback Volume'" \
     --ossmixer "Master@VOLUME" \
-    --ossmixer "PCM@PCM"
+    --ossmixer "PCM@PCM" \
+    $ARGS
 }
 
 test2() {
@@ -27,7 +29,7 @@ cat > $CFGFILE <<EOF
 # next line - second job
 -C hw:1,0,1 -P hw:0,1,0 --tlatency 50000 --thread 2
 EOF
-  $DBG ./alsaloop -d --config $CFGFILE
+  $DBG ./alsaloop -d --config $CFGFILE $ARGS
 }
 
 test3() {
@@ -46,7 +48,8 @@ cat > $CFGFILE <<EOF
 -C hw:1,0,6 -P plug:dmix:0 --tlatency 50000 --thread 6
 -C hw:1,0,7 -P plug:dmix:0 --tlatency 50000 --thread 7
 EOF
-  $DBG ./alsaloop --config $CFGFILE
+  LD_PRELOAD="/home/perex/alsa/alsa-lib/src/.libs/libasound.so" \
+  $DBG ./alsaloop --config $CFGFILE $ARGS
 }
 
 test4() {
@@ -55,7 +58,8 @@ test4() {
     --tlatency 50000 \
     --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
     --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
-    --mixer "name='PCM Playback Volume'"
+    --mixer "name='PCM Playback Volume'" \
+    $ARGS
 }
 
 test5() {
@@ -68,14 +72,14 @@ cat > $CFGFILE <<EOF
     --ossmixer "name=Master@VOLUME"
 -C hw:1,0,1 -P plughw:0,1 --tlatency 50000 --thread 2
 EOF
-  $DBG ./alsaloop --config $CFGFILE
+  $DBG ./alsaloop --config $CFGFILE $ARGS
 }
 
 case "$1" in
-test1) test1 ;;
-test2) test2 ;;
-test3) test3 ;;
-test4) test4 ;;
-test5) test5 ;;
-*) test1 ;;
+test1) shift; ARGS="$@"; test1 ;;
+test2) shift; ARGS="$@"; test2 ;;
+test3) shift; ARGS="$@"; test3 ;;
+test4) shift; ARGS="$@"; test4 ;;
+test5) shift; ARGS="$@"; test5 ;;
+*) ARGS="$@"; test1 ;;
 esac
-- 
1.7.3.1

openSUSE Build Service is sponsored by