[alsa-devel] [Alsa-devel] Quality resampling code for libasound

Takashi Iwai tiwai at suse.de
Wed Mar 21 18:24:29 CET 2007


At Wed, 21 Mar 2007 17:57:38 +0100,
I wrote:
> 
> At Wed, 21 Mar 2007 17:38:21 +0100,
> Rene Herman wrote:
> > 
> > On 03/21/2007 04:34 PM, Jean-Marc Valin wrote:
> > 
> > >> Users of these distributions would then have to be fairly familiar with
> > >> alsa to know they could improve sound by recompiling alsa-lib against
> > >> the speex libraries, but given that it's (also) dirt cheap soundcards
> > >> that need the resampling, their users aren't too likely to _be_ fairly
> > >> familiar. They'd just observe (still) that their sound is "much better
> > >> on windows".
> > > 
> > > Oh, I meant using a copy of the pph code in the mean time, not the
> > > current linear interpolation resampler.
> > 
> > Mmm, I believe Takashi Iwai was though. If I interpreted him correctly 
> > he proposed to optionally link libasound against libspeex (libresample?) 
> > if so ./config-ured and found at alsa-lib compile time but to keep using 
> > the current resampler when not.
> > 
> > Given the idea that distributions probably don't want their alsa-lib 
> > package dependent on their speex package (alsa-lib is right above the 
> > kernel and mandatory on any Linux system wanting to do anything with 
> > sound while speex is significantly higher up on the chain) I worried 
> > this would mean your code wouldn't be used in practice.
> 
> Well, my (latest) prpoposal is:
> 
> - keep speex resampler in alsa-plugins tree
> - installing this plugin automatically enables it as the default rate
>   converter without touching any configuration
> 
> The second part is missing right now.

... and the patch below supports it.

With this patch, you can set the list of multiple default PCM rate
converters.  For example,

defaults.pcm.rate_converter [ "speexrate" "samplerate" "linear" ]

will look for speex plugin, then libsamplerate plugin, lastly it will
take the built-in linear converter as a fallback.  So, we can define
this safely in the default alsa.conf file.


Takashi

diff -r c40d95b55851 include/pcm_plugin.h
--- a/include/pcm_plugin.h	Wed Mar 21 12:21:38 2007 +0100
+++ b/include/pcm_plugin.h	Wed Mar 21 17:12:30 2007 +0100
@@ -156,7 +156,8 @@ int _snd_pcm_route_open(snd_pcm_t **pcmp
  *  Rate plugin for linear formats
  */
 int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
-		      snd_pcm_format_t sformat, unsigned int srate, const char *converter,
+		      snd_pcm_format_t sformat, unsigned int srate,
+		      const snd_config_t *converter,
 		      snd_pcm_t *slave, int close_slave);
 int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
 		       snd_config_t *root, snd_config_t *conf,
diff -r c40d95b55851 src/pcm/pcm_local.h
--- a/src/pcm/pcm_local.h	Wed Mar 21 12:21:38 2007 +0100
+++ b/src/pcm/pcm_local.h	Wed Mar 21 17:11:34 2007 +0100
@@ -756,7 +756,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp,
 
 int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout);
 
-const char *snd_pcm_rate_get_default_converter(snd_config_t *root);
+const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root);
 
 #define SND_PCM_HW_PARBIT_ACCESS	(1U << SND_PCM_HW_PARAM_ACCESS)
 #define SND_PCM_HW_PARBIT_FORMAT	(1U << SND_PCM_HW_PARAM_FORMAT)
diff -r c40d95b55851 src/pcm/pcm_plug.c
--- a/src/pcm/pcm_plug.c	Wed Mar 21 12:21:38 2007 +0100
+++ b/src/pcm/pcm_plug.c	Wed Mar 21 18:11:36 2007 +0100
@@ -50,7 +50,7 @@ typedef struct {
 	snd_pcm_format_t sformat;
 	int schannels;
 	int srate;
-	const char *rate_converter;
+	const snd_config_t *rate_converter;
 	enum snd_pcm_plug_route_policy route_policy;
 	snd_pcm_route_ttable_entry_t *ttable;
 	int ttable_ok, ttable_last;
@@ -1015,7 +1015,7 @@ int snd_pcm_plug_open(snd_pcm_t **pcmp,
 int snd_pcm_plug_open(snd_pcm_t **pcmp,
 		      const char *name,
 		      snd_pcm_format_t sformat, int schannels, int srate,
-		      const char *rate_converter,
+		      const snd_config_t *rate_converter,
 		      enum snd_pcm_plug_route_policy route_policy,
 		      snd_pcm_route_ttable_entry_t *ttable,
 		      unsigned int tt_ssize,
@@ -1133,7 +1133,7 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp,
 	unsigned int cused, sused;
 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
 	int schannels = -1, srate = -1;
-	const char *rate_converter = NULL;
+	const snd_config_t *rate_converter = NULL;
 
 	snd_config_for_each(i, next, conf) {
 		snd_config_t *n = snd_config_iterator_entry(i);
@@ -1177,12 +1177,7 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp,
 #endif
 #ifdef BUILD_PCM_PLUGIN_RATE
 		if (strcmp(id, "rate_converter") == 0) {
-			const char *str;
-			if ((err = snd_config_get_string(n, &str)) < 0) {
-				SNDERR("Invalid type for %s", id);
-				return -EINVAL;
-			}
-			rate_converter = str;
+			rate_converter = n;
 			continue;
 		}
 #endif
diff -r c40d95b55851 src/pcm/pcm_rate.c
--- a/src/pcm/pcm_rate.c	Wed Mar 21 12:21:38 2007 +0100
+++ b/src/pcm/pcm_rate.c	Wed Mar 21 18:18:35 2007 +0100
@@ -1254,17 +1254,44 @@ static snd_pcm_ops_t snd_pcm_rate_ops = 
  * \param root Root configuration node
  * \retval A const string if found, or NULL
  */
-const char *snd_pcm_rate_get_default_converter(snd_config_t *root)
+const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
 {
 	snd_config_t *n;
 	/* look for default definition */
-	if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0) {
-		const char *str;
-		if (snd_config_get_string(n, &str) >= 0)
-			return str;
-	}
+	if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
+		return n;
 	return NULL;
 }
+
+#ifdef PIC
+static int rate_open_func(snd_pcm_rate_t *rate, const char *type)
+{
+	char open_name[64];
+	snd_pcm_rate_open_func_t open_func;
+
+	snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
+	open_func = snd_dlobj_cache_lookup(open_name);
+	if (!open_func) {
+		void *h;
+		char lib_name[128], *lib = NULL;
+		if (strcmp(type, "linear")) {
+			snprintf(lib_name, sizeof(lib_name),
+				 "%s/libasound_module_rate_%s.so", PKGLIBDIR, type);
+			lib = lib_name;
+		}
+		h = snd_dlopen(lib, RTLD_NOW);
+		if (!h)
+			return -ENOENT;
+		open_func = dlsym(h, open_name);
+		if (!open_func) {
+			snd_dlclose(h);
+			return -ENOENT;
+		}
+		snd_dlobj_cache_add(open_name, h, open_func);
+	}
+	return open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
+}
+#endif
 
 /**
  * \brief Creates a new rate PCM
@@ -1280,15 +1307,17 @@ const char *snd_pcm_rate_get_default_con
  *          of compatibility reasons. The prototype might be freely
  *          changed in future.
  */
-int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
-		      unsigned int srate, const char *type, snd_pcm_t *slave, int close_slave)
+int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
+		      snd_pcm_format_t sformat, unsigned int srate,
+		      const snd_config_t *converter,
+		      snd_pcm_t *slave, int close_slave)
 {
 	snd_pcm_t *pcm;
 	snd_pcm_rate_t *rate;
-	snd_pcm_rate_open_func_t open_func;
-	char open_name[64];
+	const char *type;
 	int err;
 #ifndef PIC
+	snd_pcm_rate_open_func_t open_func;
 	extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops);
 #endif
 
@@ -1306,49 +1335,47 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, 
 	rate->sformat = sformat;
 	snd_atomic_write_init(&rate->watom);
 
-	if (! type || ! *type)
-		type = "linear";
-
-#ifdef PIC
-	snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
-	open_func = snd_dlobj_cache_lookup(open_name);
-	if (! open_func) {
-		void *h;
-		char lib_name[128], *lib = NULL;
-		if (strcmp(type, "linear")) {
-			snprintf(lib_name, sizeof(lib_name),
-				 "%s/libasound_module_rate_%s.so", PKGLIBDIR, type);
-			lib = lib_name;
-		}
-		h = snd_dlopen(lib, RTLD_NOW);
-		if (! h) {
-			SNDERR("Cannot open library for type %s", type);
-			free(rate);
-			return -ENOENT;
-		}
-		open_func = dlsym(h, open_name);
-		if (! open_func) {
-			SNDERR("Cannot find function %s", open_name);
-			snd_dlclose(h);
-			free(rate);
-			return -ENOENT;
-		}
-		snd_dlobj_cache_add(open_name, h, open_func);
-	}
-#else
-	open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
-#endif
-
 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
 	if (err < 0) {
 		free(rate);
 		return err;
 	}
+
+#ifdef PIC
+	err = -ENOENT;
+	if (!converter)
+		err = rate_open_func(rate, "linear");
+	else if (!snd_config_get_string(converter, &type))
+		err = rate_open_func(rate, type);
+	else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) {
+		snd_config_iterator_t i, next;
+		snd_config_for_each(i, next, converter) {
+			snd_config_t *n = snd_config_iterator_entry(i);
+			if (snd_config_get_string(n, &type) < 0)
+				break;
+			err = rate_open_func(rate, type);
+			if (!err)
+				break;
+		}
+	} else {
+		SNDERR("Invalid type for rate converter");
+		snd_pcm_close(pcm);
+		return -EINVAL;
+	}
+	if (err < 0) {
+		SNDERR("Cannot find rate converter");
+		snd_pcm_close(pcm);
+		return -ENOENT;
+	}
+#else
+	open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
 	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
 	if (err < 0) {
 		snd_pcm_close(pcm);
 		return err;
 	}
+#endif
+
 	if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) ||
 	    ! rate->ops.input_frames || ! rate->ops.output_frames) {
 		SNDERR("Inproper rate plugin %s initialization", type);
@@ -1424,7 +1451,7 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp,
 	snd_config_t *slave = NULL, *sconf;
 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
 	int srate = -1;
-	const char *type = NULL;
+	const snd_config_t *converter = NULL;
 
 	snd_config_for_each(i, next, conf) {
 		snd_config_t *n = snd_config_iterator_entry(i);
@@ -1438,12 +1465,7 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp,
 			continue;
 		}
 		if (strcmp(id, "converter") == 0) {
-			const char *str;
-			if ((err = snd_config_get_string(n, &str)) < 0) {
-				SNDERR("invalid converter string");
-				return -EINVAL;
-			}
-			type = str;
+			converter = n;
 			continue;
 		}
 		SNDERR("Unknown field %s", id);
@@ -1470,7 +1492,7 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp,
 	if (err < 0)
 		return err;
 	err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
-				type, spcm, 1);
+				converter, spcm, 1);
 	if (err < 0)
 		snd_pcm_close(spcm);
 	return err;


More information about the Alsa-devel mailing list