This should be interesting for home theater installations, as I was personnally missing a "per channel delay configuration". Feel free to tell me what should be modified, I am not really used to proposing a patch. Thx
diff -x libtool -x stamp-h1 -x .vimrc -x rate -x Makefile.in -x Makefile -x .deps -x configure -x config.status -x config.h -x config.log -x a52 -x '*cache*' -x '*aclocal*' -Naur alsa-plugins-1.0.17/configure.in alsa-plugins-1.0.17+delay/configure.in --- alsa-plugins-1.0.17/configure.in 2008-07-14 10:57:59.000000000 +0200 +++ alsa-plugins-1.0.17+delay/configure.in 2008-09-24 10:52:23.000000000 +0200 @@ -89,6 +89,9 @@ fi AM_CONDITIONAL(HAVE_PPH, test "$PPH" = "builtin" -o "$PPH" = "lib") + +AM_CONDITIONAL(HAVE_DELAY, test x$HAVE_DELAY = xyes) + AM_CONDITIONAL(USE_LIBSPEEX, test "$PPH" = "lib") dnl ALSA plugin directory @@ -124,6 +127,7 @@ a52/Makefile rate-lavc/Makefile maemo/Makefile + delay/Makefile doc/Makefile ]) diff -x libtool -x stamp-h1 -x .vimrc -x rate -x Makefile.in -x Makefile -x .deps -x configure -x config.status -x config.h -x config.log -x a52 -x '*cache*' -x '*aclocal*' -Naur alsa-plugins-1.0.17/delay/Makefile.am alsa-plugins-1.0.17+delay/delay/Makefile.am --- alsa-plugins-1.0.17/delay/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ alsa-plugins-1.0.17+delay/delay/Makefile.am 2008-09-27 00:26:05.000000000 +0200 @@ -0,0 +1,9 @@ +asound_module_pcm_delay_LTLIBRARIES = libasound_module_pcm_delay.la + +asound_module_pcm_delaydir = @ALSA_PLUGIN_DIR@ + +AM_CFLAGS = -Wall -g @ALSA_CFLAGS@ +AM_LDFLAGS = -module -avoid-version -export-dynamic -no-undefined + +libasound_module_pcm_delay_la_SOURCES = pcm_delay.c +libasound_module_pcm_delay_la_LIBADD = @ALSA_LIBS@ diff -x libtool -x stamp-h1 -x .vimrc -x rate -x Makefile.in -x Makefile -x .deps -x configure -x config.status -x config.h -x config.log -x a52 -x '*cache*' -x '*aclocal*' -Naur alsa-plugins-1.0.17/delay/pcm_delay.c alsa-plugins-1.0.17+delay/delay/pcm_delay.c --- alsa-plugins-1.0.17/delay/pcm_delay.c 1970-01-01 01:00:00.000000000 +0100 +++ alsa-plugins-1.0.17+delay/delay/pcm_delay.c 2008-09-30 21:57:56.000000000 +0200 @@ -0,0 +1,335 @@ +/* + * Delay Output Plugin + * + * Copyright (c) 2008 by Alexandre BUISINE <alex@xxxxxxxxxxxxx> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <assert.h> +#include <alsa/asoundlib.h> +#include <alsa/pcm_external.h> + +#define SND_PCM_DELAY_PARSE_MS 1 +#define SND_PCM_DELAY_PARSE_METER 2 +#define SND_PCM_DELAY_PARSE_FEET 3 + +/* Ambient temperature is 20° celsius */ +#define SND_PCM_DELAY_CELERITY ( 331.5 + 0.6 * 20 ) +#define SND_PCM_DELAY_FEET 3.28083989501 + +#define round(x) ((int)((x)+0.5)) + + +typedef struct { + snd_pcm_extplug_t ext; + + double *delay_ms; + unsigned int *delay_sample; + + snd_pcm_channel_area_t *dly_areas; + snd_pcm_uframes_t *dly_offset; + + unsigned int channels; + unsigned int mode; +} snd_pcm_delay_t; + +static int snd_pcm_delay_parse_delay(snd_pcm_delay_t *pcm_delay, + snd_config_t *conf_delay, + unsigned char mode) +{ + snd_config_iterator_t i, next; + double *delays; + double delay = 0.0; + unsigned int cnt = 0; + unsigned int channel; + double val; + + pcm_delay->mode = mode; + if (conf_delay) { + snd_config_for_each(i, next, conf_delay) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + cnt++; + } + pcm_delay->delay_ms = delays = calloc(cnt, sizeof(double)); + if (delays == NULL) + return -ENOMEM; + pcm_delay->channels = cnt; + snd_config_for_each(i, next, conf_delay) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + + if (snd_config_get_id(n, &id) < 0) + continue; + channel = atoi(id); + if (snd_config_get_ireal(n, &val) < 0) + continue; + if (mode == SND_PCM_DELAY_PARSE_MS) + delay = val; + if (mode == SND_PCM_DELAY_PARSE_METER) + delay = ( val / SND_PCM_DELAY_CELERITY ) * 1000; + if (mode == SND_PCM_DELAY_PARSE_FEET) + delay = ( val / SND_PCM_DELAY_FEET / SND_PCM_DELAY_CELERITY ) * 1000; + delays[channel] = delay; + } + if (pcm_delay->mode == SND_PCM_DELAY_PARSE_METER + || pcm_delay->mode == SND_PCM_DELAY_PARSE_FEET) { + double max = 0.0; + + unsigned int j; + for (j = 0; j < cnt; j++) + max = delays[j] > max ? delays[j] : max; + for (j = 0; j < pcm_delay->channels; j++) + delays[j] = max - delays[j]; + } + } + return 0; +} + +static void snd_pcm_delay_free(snd_pcm_delay_t *pcm_delay) { + if (pcm_delay) { + if (pcm_delay->delay_ms) + free(pcm_delay->delay_ms); + if (pcm_delay->delay_sample) + free(pcm_delay->delay_sample); + if (pcm_delay->dly_offset) + free(pcm_delay->dly_offset); + if (pcm_delay->dly_areas) { + unsigned int i; + for (i = 0; i < pcm_delay->channels; i++) + if (pcm_delay->dly_areas[i].addr) + free(pcm_delay->dly_areas[i].addr); + free(pcm_delay->dly_areas); + } + } + free(pcm_delay); + return; +} + +/* Converting delay in ms to delay in sample */ +static int snd_pcm_delay_init(snd_pcm_extplug_t *ext) +{ + snd_pcm_delay_t *pcm_delay = (snd_pcm_delay_t *)ext; + unsigned int i; + + pcm_delay->delay_sample = calloc(pcm_delay->channels, sizeof(unsigned int)); + if (pcm_delay->delay_sample == NULL) + return -ENOMEM; + for (i = 0; i < pcm_delay->channels; i++) + pcm_delay->delay_sample[i] = round(pcm_delay->ext.rate + * pcm_delay->delay_ms[i] + / 1000); + pcm_delay->dly_offset = calloc(pcm_delay->channels, sizeof(snd_pcm_uframes_t)); + if (pcm_delay->dly_offset == NULL) + return -ENOMEM; + pcm_delay->dly_areas = calloc(pcm_delay->channels, sizeof(snd_pcm_channel_area_t)); + if (pcm_delay->dly_areas == NULL) + return -ENOMEM; + for (i = 0; i < pcm_delay->channels; i++) { + pcm_delay->dly_areas[i].addr = + malloc(pcm_delay->delay_sample[i] + * snd_pcm_format_physical_width(pcm_delay->ext.format) / 8); + pcm_delay->dly_areas[i].first = 0; + pcm_delay->dly_areas[i].step = snd_pcm_format_physical_width(pcm_delay->ext.format); + snd_pcm_area_silence(pcm_delay->dly_areas + i, 0, + pcm_delay->delay_sample[i], pcm_delay->ext.format); + if (pcm_delay->dly_areas[i].addr == NULL) + return -ENOMEM; + } + return 0; +} + +static int snd_pcm_delay_close(snd_pcm_extplug_t *ext) +{ + snd_pcm_delay_t *pcm_delay = (snd_pcm_delay_t *)ext; + + snd_pcm_delay_free(pcm_delay); + return 0; +} + +static snd_pcm_sframes_t snd_pcm_delay_transfer(snd_pcm_extplug_t *ext, + const snd_pcm_channel_area_t *dst_areas, + snd_pcm_uframes_t dst_offset, + const snd_pcm_channel_area_t *src_areas, + snd_pcm_uframes_t src_offset, + snd_pcm_uframes_t size) +{ + snd_pcm_delay_t *pcm_delay = (snd_pcm_delay_t *)ext; + unsigned int i; + snd_pcm_uframes_t left; + + for (i = 0; i < pcm_delay->ext.channels; i++) { + if (! pcm_delay->delay_sample[i]) { + snd_pcm_area_copy(dst_areas + i, dst_offset, + src_areas + i, src_offset, + size, pcm_delay->ext.format); + continue; + } + if (pcm_delay->delay_sample[i] <= size) { + assert(! pcm_delay->dly_offset[i]); + left = size - pcm_delay->delay_sample[i]; + /* delay to dst */ + snd_pcm_area_copy(dst_areas + i, dst_offset, + pcm_delay->dly_areas + i, 0, + pcm_delay->delay_sample[i], pcm_delay->ext.format); + if (left) { + /* src to dst left */ + snd_pcm_area_copy(dst_areas + i, dst_offset + pcm_delay->delay_sample[i], + src_areas + i, src_offset, + left, pcm_delay->ext.format); + } + /* src left to delay */ + snd_pcm_area_copy(pcm_delay->dly_areas + i, 0, + src_areas + i, src_offset + left, + pcm_delay->delay_sample[i], pcm_delay->ext.format); + continue; + } + /* usual (small) delays wont reach this part of the code */ + left = pcm_delay->dly_offset[i] + size > pcm_delay->delay_sample[i] + ? (pcm_delay->dly_offset[i] + size) % pcm_delay->delay_sample[i] : 0; + /* copy dly to dst */ + snd_pcm_area_copy(dst_areas + i, dst_offset, + pcm_delay->dly_areas + i, pcm_delay->dly_offset[i], + size - left, pcm_delay->ext.format); + /* copy src to dly */ + snd_pcm_area_copy(pcm_delay->dly_areas + i, pcm_delay->dly_offset[i], + src_areas + i, src_offset, + size - left, pcm_delay->ext.format); + if (left) { + /* copy dly to dst */ + snd_pcm_area_copy(dst_areas + i, dst_offset + size - left, + pcm_delay->dly_areas + i, 0, + left, pcm_delay->ext.format); + /* copy src to dly */ + snd_pcm_area_copy(pcm_delay->dly_areas + i, 0, + src_areas + i, src_offset + size - left, + left, pcm_delay->ext.format); + pcm_delay->dly_offset[i] = left; + continue; + } + pcm_delay->dly_offset[i] += size; + } + return size; +} + +static snd_pcm_extplug_callback_t snd_pcm_delay_callback = { + .transfer = snd_pcm_delay_transfer, + .init = snd_pcm_delay_init, + .close = snd_pcm_delay_close, +}; + +/* + * Main entry point + */ +SND_PCM_PLUGIN_DEFINE_FUNC(delay) +{ + snd_config_iterator_t i, next; + snd_config_t *slave_conf = NULL; + snd_config_t *delays = NULL; + snd_pcm_delay_t *pcm_delay; + static unsigned int chlist[6] = {1, 2, 3, 4, 5, 6}; + unsigned int parser_mode = 0; + int err; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) + continue; + if (strcmp(id, "slave") == 0) { + slave_conf = n; + continue; + } + if (strcmp(id, "delay_ms") == 0) { + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + delays = n; + parser_mode = SND_PCM_DELAY_PARSE_MS; + continue; + } + if (strcmp(id, "delay_meter") == 0) { + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + delays = n; + parser_mode = SND_PCM_DELAY_PARSE_METER; + continue; + } + if (strcmp(id, "delay_feet") == 0) { + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + delays = n; + parser_mode = SND_PCM_DELAY_PARSE_FEET; + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + + if (! slave_conf) { + SNDERR("No slave defined for delay"); + return -EINVAL; + } + + pcm_delay = calloc(1, sizeof(*pcm_delay)); + if (pcm_delay == NULL) + return -ENOMEM; + + pcm_delay->ext.version = SND_PCM_EXTPLUG_VERSION; + pcm_delay->ext.name = "Delay plugin"; + pcm_delay->ext.callback = &snd_pcm_delay_callback; + pcm_delay->ext.private_data = pcm_delay; + + err = snd_pcm_extplug_create(&pcm_delay->ext, name, root, slave_conf, stream, mode); + if (err < 0) { + snd_pcm_delay_free(pcm_delay); + return err; + } + + if (delays) { + err = snd_pcm_delay_parse_delay(pcm_delay, delays, parser_mode); + if (err < 0) { + snd_pcm_delay_free(pcm_delay); + return err; + } + } + + snd_pcm_extplug_set_param_minmax(&pcm_delay->ext, + SND_PCM_EXTPLUG_HW_CHANNELS, + 1, 6); + snd_pcm_extplug_set_param_list(&pcm_delay->ext, + SND_PCM_EXTPLUG_HW_CHANNELS, + 6, chlist); + snd_pcm_extplug_set_param(&pcm_delay->ext, SND_PCM_EXTPLUG_HW_FORMAT, + SND_PCM_FORMAT_S16); + snd_pcm_extplug_set_slave_param(&pcm_delay->ext, SND_PCM_EXTPLUG_HW_FORMAT, + SND_PCM_FORMAT_S16); + + *pcmp = pcm_delay->ext.pcm; + return 0; +} + +SND_PCM_PLUGIN_SYMBOL(delay); diff -x libtool -x stamp-h1 -x .vimrc -x rate -x Makefile.in -x Makefile -x .deps -x configure -x config.status -x config.h -x config.log -x a52 -x '*cache*' -x '*aclocal*' -Naur alsa-plugins-1.0.17/doc/delay.txt alsa-plugins-1.0.17+delay/doc/delay.txt --- alsa-plugins-1.0.17/doc/delay.txt 1970-01-01 01:00:00.000000000 +0100 +++ alsa-plugins-1.0.17+delay/doc/delay.txt 2008-09-30 22:36:41.000000000 +0200 @@ -0,0 +1,22 @@ +DELAY PLUGIN +============ + +The delay plugin is an easy-to-use plugin for delaying 1 to 6 channels +stream. The number of channels to be delayed is determined by the +slave PCM. For example, the following PCM defines 3 delays but only 2 +will be applied: + + pcm.delay3 { + type delay + delay_ms.0 10 + delay_ms.1 10 + delay_ms.2 20 + slave.pcm "default" + } + +You can give the delay to be applied by using one of the following +option: + +delay_ms +delay_meter +delay_feet diff -x libtool -x stamp-h1 -x .vimrc -x rate -x Makefile.in -x Makefile -x .deps -x configure -x config.status -x config.h -x config.log -x a52 -x '*cache*' -x '*aclocal*' -Naur alsa-plugins-1.0.17/doc/Makefile.am alsa-plugins-1.0.17+delay/doc/Makefile.am --- alsa-plugins-1.0.17/doc/Makefile.am 2008-07-14 10:57:59.000000000 +0200 +++ alsa-plugins-1.0.17+delay/doc/Makefile.am 2008-09-30 22:29:52.000000000 +0200 @@ -1,3 +1,3 @@ -EXTRA_DIST = README-pcm-oss README-jack README-pulse README-maemo \ +EXTRA_DIST = README-pcm-oss README-jack README-pulse README-maemo README-delay\ upmix.txt vdownmix.txt samplerate.txt a52.txt lavcrate.txt \ - speexrate.txt + speexrate.txt delay.txt diff -x libtool -x stamp-h1 -x .vimrc -x rate -x Makefile.in -x Makefile -x .deps -x configure -x config.status -x config.h -x config.log -x a52 -x '*cache*' -x '*aclocal*' -Naur alsa-plugins-1.0.17/Makefile.am alsa-plugins-1.0.17+delay/Makefile.am --- alsa-plugins-1.0.17/Makefile.am 2008-07-14 10:57:59.000000000 +0200 +++ alsa-plugins-1.0.17+delay/Makefile.am 2008-09-24 16:25:28.000000000 +0200 @@ -18,7 +18,7 @@ PPHDIR = pph endif -SUBDIRS = oss mix $(PPHDIR) $(JACKDIR) $(PULSEDIR) $(SAMPLERATEDIR) $(A52DIR) $(LAVCRATEDIR) $(MAEMODIR) doc +SUBDIRS = oss mix delay $(PPHDIR) $(JACKDIR) $(PULSEDIR) $(SAMPLERATEDIR) $(A52DIR) $(LAVCRATEDIR) $(MAEMODIR) doc EXTRA_DIST = gitcompile version COPYING.GPL AUTOMAKE_OPTIONS = foreign
_______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel