Merge branch 'ucm'
diff --git a/.gitignore b/.gitignore
index 0319f44..d17c89a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,7 @@
seq/aseqdump/aseqdump
seq/aseqnet/aseqnet
speaker-test/speaker-test
+alsaloop/alsaloop
include/aconfig.h*
include/stamp-*
diff --git a/Makefile.am b/Makefile.am
index d92e204..21d69ab 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,6 +15,9 @@
endif
if HAVE_PCM
SUBDIRS += aplay iecset speaker-test
+if ALSALOOP
+SUBDIRS += alsaloop
+endif
endif
if HAVE_SEQ
SUBDIRS += seq
@@ -34,3 +37,9 @@
else \
$(TAR) --create --verbose --file=- $(distdir) | bzip2 -c -9 > $(distdir).tar.bz2 ; \
fi
+
+install-data-hook:
+ $(MKDIR_P) -m 0755 $(DESTDIR)$(ASOUND_STATE_DIR)
+
+DISTCHECK_CONFIGURE_FLAGS = \
+ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
diff --git a/alsaconf/alsaconf.in b/alsaconf/alsaconf.in
index ccc1b37..5c23787 100644
--- a/alsaconf/alsaconf.in
+++ b/alsaconf/alsaconf.in
@@ -956,8 +956,9 @@
aplay -N $TESTSOUND
fi
fi
- if [ ! -r /etc/asound.state ]; then
- xecho "Saving the mixer setup used for this in /etc/asound.state."
+ mkdir -p -m 0755 @ASOUND_STATE_DIR@
+ if [ ! -r @ASOUND_STATE_DIR@/asound.state ]; then
+ xecho "Saving the mixer setup used for this in @ASOUND_STATE_DIR@/asound.state."
$sbindir/alsactl store
fi
clear
diff --git a/alsactl/.gitignore b/alsactl/.gitignore
new file mode 100644
index 0000000..56ab3a2
--- /dev/null
+++ b/alsactl/.gitignore
@@ -0,0 +1,3 @@
+alsa-store.service
+alsa-restore.service
+90-alsa-restore.rules
diff --git a/alsactl/90-alsa-restore.rules.in b/alsactl/90-alsa-restore.rules.in
new file mode 100644
index 0000000..0bcee5b
--- /dev/null
+++ b/alsactl/90-alsa-restore.rules.in
@@ -0,0 +1,2 @@
+ACTION=="add", SUBSYSTEM=="sound", KERNEL=="controlC*", KERNELS=="card*", \
+ RUN+="@sbindir@/alsactl restore $attr{number}"
diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am
index 359f73a..5cfc415 100644
--- a/alsactl/Makefile.am
+++ b/alsactl/Makefile.am
@@ -8,7 +8,52 @@
EXTRA_DIST=alsactl.1 alsactl_init.xml
alsactl_SOURCES=alsactl.c state.c utils.c init_parse.c
+alsactl_CFLAGS=$(AM_CFLAGS) -DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\"
noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c init_utils_run.c init_sysfs.c
+dist_udevrules_DATA = \
+ 90-alsa-restore.rules
+
+if HAVE_SYSTEMD
+
+systemdsystemunit_DATA = \
+ alsa-store.service \
+ alsa-restore.service
+
+install-data-hook:
+ $(MKDIR_P) -m 0755 \
+ $(DESTDIR)$(systemdsystemunitdir)/basic.target.wants \
+ $(DESTDIR)$(systemdsystemunitdir)/shutdown.target.wants
+ ( cd $(DESTDIR)$(systemdsystemunitdir)/basic.target.wants && \
+ rm -f alsa-restore.service && \
+ $(LN_S) ../alsa-restore.service alsa-restore.service )
+ ( cd $(DESTDIR)$(systemdsystemunitdir)/shutdown.target.wants && \
+ rm -f alsa-store.service && \
+ $(LN_S) ../alsa-store.service alsa-store.service )
+
+endif
+
+edit = \
+ sed $(SED) -r 's,@sbindir\@,$(sbindir),g' < $< > $@ || rm $@
+
+alsa-store.service: alsa-store.service.in
+ $(edit)
+
+alsa-restore.service: alsa-restore.service.in
+ $(edit)
+
+90-alsa-restore.rules: 90-alsa-restore.rules.in
+ $(edit)
+
+EXTRA_DIST += \
+ alsa-store.service.in \
+ alsa-restore.service.in \
+ 90-alsa-restore.rules.in
+
+CLEANFILES = \
+ alsa-store.service \
+ alsa-restore.service \
+ 90-alsa-restore.rules
+
%.7: %.xml
xmlto man $?
diff --git a/alsactl/alsa-restore.service.in b/alsactl/alsa-restore.service.in
new file mode 100644
index 0000000..e97d196
--- /dev/null
+++ b/alsactl/alsa-restore.service.in
@@ -0,0 +1,11 @@
+[Unit]
+Description=Restore Sound Card State
+DefaultDependencies=no
+After=sysinit.target
+Before=shutdown.target
+Conflicts=shutdown.target
+
+[Service]
+Type=oneshot
+ExecStart=-@sbindir@/alsactl restore
+StandardOutput=syslog
diff --git a/alsactl/alsa-store.service.in b/alsactl/alsa-store.service.in
new file mode 100644
index 0000000..0e2823c
--- /dev/null
+++ b/alsactl/alsa-store.service.in
@@ -0,0 +1,9 @@
+[Unit]
+Description=Store Sound Card State
+DefaultDependencies=no
+Before=shutdown.target
+
+[Service]
+Type=oneshot
+ExecStart=@sbindir@/alsactl store
+StandardOutput=syslog
diff --git a/alsactl/alsactl.1 b/alsactl/alsactl.1
index eb3cbd1..eb5968c 100644
--- a/alsactl/alsactl.1
+++ b/alsactl/alsactl.1
@@ -43,7 +43,7 @@
.TP
\fI\-f, \-\-file\fP
-Select the configuration file to use. The default is /etc/asound.state.
+Select the configuration file to use. The default is /var/lib/alsa/asound.state.
.TP
\fI\-F, \-\-force\fP
@@ -90,7 +90,7 @@
is used.
.SH FILES
-\fI/etc/asound.state\fP (or whatever file you specify with the
+\fI/var/lib/alsa/asound.state\fP (or whatever file you specify with the
\fB\-f\fP flag) is used to store current settings for your
soundcards. The settings include all the usual soundcard mixer
settings. More importantly, alsactl is
diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c
index 3b5dfda..c2120bd 100644
--- a/alsactl/alsactl.c
+++ b/alsactl/alsactl.c
@@ -30,7 +30,9 @@
#include <alsa/asoundlib.h>
#include "alsactl.h"
-#define SYS_ASOUNDRC "/etc/asound.state"
+#ifndef SYS_ASOUNDRC
+#define SYS_ASOUNDRC "/var/lib/alsa/asound.state"
+#endif
int debugflag = 0;
int force_restore = 1;
diff --git a/alsaloop/Makefile.am b/alsaloop/Makefile.am
new file mode 100644
index 0000000..f76eafd
--- /dev/null
+++ b/alsaloop/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(top_srcdir)/include
+LDADD = -lm
+CFLAGS += -D_GNU_SOURCE
+if HAVE_SAMPLERATE
+LDADD += -lsamplerate
+endif
+# LDFLAGS = -static
+# CFLAGS += -g -Wall
+
+bin_PROGRAMS = alsaloop
+alsaloop_SOURCES = alsaloop.c pcmjob.c control.c
+noinst_HEADERS = alsaloop.h
+man_MANS = alsaloop.1
+EXTRA_DIST = alsaloop.1
diff --git a/alsaloop/alsaloop.1 b/alsaloop/alsaloop.1
new file mode 100644
index 0000000..048d1e0
--- /dev/null
+++ b/alsaloop/alsaloop.1
@@ -0,0 +1,206 @@
+.TH ALSALOOP 1 "5 Aug 2010"
+.SH NAME
+alsaloop \- command-line PCM loopback
+.SH SYNOPSIS
+\fBalsaloop\fP [\fI\-option\fP] [\fIcmd\fP]
+.SH DESCRIPTION
+
+\fBalsaloop\fP allows create a PCM loopback between a PCM capture device
+and a PCM playback device.
+
+\fBalsaloop\fP supports multiple soundcards, adaptive clock synchronization,
+adaptive rate resampling using the samplerate library (if available in
+the system). Also, mixer controls can be redirected from one card to
+another (for example Master and PCM).
+
+.SH OPTIONS
+
+.TP
+\fI\-h\fP | \fI\-\-help\fP
+
+Prints the help information.
+
+.TP
+\fI\-g <file>\fP | \fI\-\-config=<file>\fP
+
+Use given configuration file. The syntax of this file is simple: one line
+contains the command line options for one job. The '#' means comment and
+rest of line is ignored. Example:
+
+ # First line - comment, second line - first job
+ -C hw:1,0 -P hw:0,0 -t 50000 -T 1
+ # Third line - comment, fourth line - second job
+ -C hw:1,1 -P hw:0,1 -t 40000 -T 2
+
+.TP
+\fI\-d\fP | \fI\-\-daemonize\fP
+
+Daemonize the main process and use syslog for messages.
+
+.TP
+\fI\-P <device>\fP | \fI\-\-pdevice=<device>\fP
+
+Use given playback device.
+
+.TP
+\fI\-C <device>\fP | \fI\-\-cdevice=<device>\fP
+
+Use given capture device.
+
+.TP
+\fI\-X <device>\fP | \fI\-\-pctl=<device>\fP
+
+Use given CTL device for playback.
+
+.TP
+\fI\-Y <device>\fP | \fI\-\-cctl=<device>\fP
+
+Use given CTL device for capture.
+
+.TP
+\fI\-l <latency>\fP | \fI\-\-latency=<frames>\fP
+
+Requested latency in frames.
+
+.TP
+\fI\-t <usec>\fP | \fI\-\-tlatency=<usec>\fP
+
+Requested latency in usec (1/1000000sec).
+
+.TP
+\fI\-f <format>\fP | \fI\-\-format=<format>\fP
+
+Format specification (usually S16_LE S32_LE). Use -h to list all formats.
+Default format is S16_LE.
+
+.TP
+\fI\-c <channels>\fP | \fI\-\-channels=<channels>\fP
+
+Channel count specification. Default value is 2.
+
+.TP
+\fI\-c <rate>\fP | \fI\-\-rate=<rate>\fP
+
+Rate specification. Default value is 48000 (Hz).
+
+.TP
+\fI\-n\fP | \fI\-\-resample\fP
+
+Allow rate resampling using alsa-lib.
+
+.TP
+\fI\-A <converter>\fP | \fI\-\-samplerate=<converter>\fP
+
+Use libsamplerate and choose a converter:
+
+ 0 or sincbest - best quality
+ 1 or sincmedium - medium quality
+ 2 or sincfastest - lowest quality
+ 3 or zerohold - hold zero samples
+ 4 or linear - worst quality - linear resampling
+ 5 or auto - choose best method
+
+.TP
+\fI\-B <size>\fP | \fI\-\-buffer=<size>\fP
+
+Buffer size in frames.
+
+.TP
+\fI\-E <size>\fP | \fI\-\-period=<size>\fP
+
+Period size in frames.
+
+.TP
+\fI\-s <secs>\fP | \fI\-\-seconds=<secs>\fP
+
+Duration of loop in seconds.
+
+.TP
+\fI\-b\fP | \fI\-\-nblock\fP
+
+Non-block mode (very early process wakeup). Eats more CPU.
+
+.TP
+\fI\-S <mode>\fP | \fI\-\-sync=<mode>\fP
+
+Sync mode specification for capture to playback stream:
+ 0 or none - do not touch the stream
+ 1 or simple - add or remove samples to keep
+ both streams synchronized
+ 2 or captshift - use driver for the capture device
+ (if supported) to compensate
+ the rate shift
+ 3 or playshift - use driver for the playback device
+ (if supported) to compensate
+ the rate shift
+ 4 or samplerate - use samplerate library to do rate resampling
+ 5 or auto - automatically selects the best method
+ in this order: captshift, playshift,
+ samplerate, simple
+
+.TP
+\fI\-T <num>\fP | \fI\-\-thread=<num>\fP
+
+Thread number (-1 means create a unique thread). All jobs with same
+thread numbers are run within one thread.
+
+.TP
+\fI\-m <mixid>\fP | \fI\-\-mixer=<midid>\fP
+
+Redirect mixer control from the playback card to the capture card. Format of
+\fImixid\fP is SRCID(PLAYBACK)[@DSTID(PLAYBACK)]:
+
+ "name='Master Playback Switch'@name='Another Switch'"
+ "name='PCM Playback Volume'"
+
+Known attributes:
+
+ name - control ID name
+ index - control ID index
+ device - control ID device
+ subdevice - control ID subdevice
+ iface - control ID interface
+ numid - control ID numid
+
+.TP
+\fI\-O <ossmixid>\fP | \fI\-\-ossmixer=<midid>\fP
+
+Redirect mixer control from the OSS Mixer emulation layer (capture card)
+to the ALSA layer (capture card). Format of \fIossmixid\fP is
+ALSAID[,INDEX]@OSSID:
+
+ "Master@VOLUME"
+ "PCM,1@ALTPCM"
+
+Known OSS attributes:
+
+ VOLUME, BASS, TREBLE, SYNTH, PCM, SPEAKER, LINE, MIC, CD, IMIX, ALTPCM,
+ RECLEV, IGAIN, OGAIN, LINE1, LINE2, LINE3, DIGITAL1, DIGITAL2, DIGITAL3,
+ PHONEIN, PHONEOUT, VIDEO, RADIO, MONITOR
+
+.TP
+\fI\-v\fP | \fI\-\-verbose\fP
+
+Verbose mode. Use multiple times to increase verbosity.
+
+
+.TP
+\fI\-U\fP | \fI\-\-xrun\fP
+
+Verbose xrun profiling.
+
+.TP
+\fI\-W <timeout>\fP | \fI\-\-wake=<timeout>\fP
+
+Set process wake timeout.
+
+.SH EXAMPLES
+
+.TP
+\fBalsaloop \-C hw:0,0 \-P hw:1,0 \-t 50000\fR
+
+.SH BUGS
+None known.
+.SH AUTHOR
+\fBalsaloop\fP is by Jaroslav Kysela <perex@perex.cz>.
+This document is by Jaroslav Kysela <perex@perex.cz>.
diff --git a/alsaloop/alsaloop.c b/alsaloop/alsaloop.c
new file mode 100644
index 0000000..8710dd1
--- /dev/null
+++ b/alsaloop/alsaloop.c
@@ -0,0 +1,923 @@
+/*
+ * A simple PCM loopback utility with adaptive sample rate support
+ *
+ * Author: Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+#include <getopt.h>
+#include <alsa/asoundlib.h>
+#include <sys/time.h>
+#include <math.h>
+#include <pthread.h>
+#include <syslog.h>
+#include <sys/signal.h>
+#include "alsaloop.h"
+
+struct loopback_thread {
+ int threaded;
+ pthread_t thread;
+ int exitcode;
+ struct loopback **loopbacks;
+ int loopbacks_count;
+ snd_output_t *output;
+};
+
+int quit = 0;
+int verbose = 0;
+int workarounds = 0;
+int daemonize = 0;
+int use_syslog = 0;
+struct loopback **loopbacks = NULL;
+int loopbacks_count = 0;
+char **my_argv = NULL;
+int my_argc = 0;
+struct loopback_thread *threads;
+int threads_count = 0;
+pthread_t main_job;
+int arg_default_xrun = 0;
+int arg_default_wake = 0;
+
+static void my_exit(struct loopback_thread *thread, int exitcode)
+{
+ int i;
+
+ for (i = 0; i < thread->loopbacks_count; i++)
+ pcmjob_done(thread->loopbacks[i]);
+ if (thread->threaded) {
+ thread->exitcode = exitcode;
+ pthread_exit(0);
+ }
+ exit(exitcode);
+}
+
+static int create_loopback_handle(struct loopback_handle **_handle,
+ const char *device,
+ const char *ctldev,
+ const char *id)
+{
+ char idbuf[1024];
+ struct loopback_handle *handle;
+
+ handle = calloc(1, sizeof(*handle));
+ if (handle == NULL)
+ return -ENOMEM;
+ if (device == NULL)
+ device = "hw:0,0";
+ handle->device = strdup(device);
+ if (handle->device == NULL)
+ return -ENOMEM;
+ if (ctldev) {
+ handle->ctldev = strdup(ctldev);
+ if (handle->ctldev == NULL)
+ return -ENOMEM;
+ } else {
+ handle->ctldev = NULL;
+ }
+ snprintf(idbuf, sizeof(idbuf)-1, "%s %s", id, device);
+ idbuf[sizeof(idbuf)-1] = '\0';
+ handle->id = strdup(idbuf);
+ handle->access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ handle->format = SND_PCM_FORMAT_S16_LE;
+ handle->rate = handle->rate_req = 48000;
+ handle->channels = 2;
+ handle->resample = 0;
+ *_handle = handle;
+ return 0;
+}
+
+static int create_loopback(struct loopback **_handle,
+ struct loopback_handle *play,
+ struct loopback_handle *capt,
+ snd_output_t *output)
+{
+ struct loopback *handle;
+
+ handle = calloc(1, sizeof(*handle));
+ if (handle == NULL)
+ return -ENOMEM;
+ handle->play = play;
+ handle->capt = capt;
+ play->loopback = handle;
+ capt->loopback = handle;
+ handle->latency_req = 0;
+ handle->latency_reqtime = 10000;
+ handle->loop_time = ~0UL;
+ handle->loop_limit = ~0ULL;
+ handle->output = output;
+ handle->state = output;
+#ifdef USE_SAMPLERATE
+ handle->src_enable = 1;
+ handle->src_converter_type = SRC_SINC_BEST_QUALITY;
+#endif
+ *_handle = handle;
+ return 0;
+}
+
+static void set_loop_time(struct loopback *loop, unsigned long loop_time)
+{
+ loop->loop_time = loop_time;
+ loop->loop_limit = loop->capt->rate * loop_time;
+}
+
+static void setscheduler(void)
+{
+ struct sched_param sched_param;
+
+ if (sched_getparam(0, &sched_param) < 0) {
+ logit(LOG_WARNING, "Scheduler getparam failed.\n");
+ return;
+ }
+ sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
+ if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
+ if (verbose)
+ logit(LOG_WARNING, "Scheduler set to Round Robin with priority %i\n", sched_param.sched_priority);
+ return;
+ }
+ if (verbose)
+ logit(LOG_INFO, "!!!Scheduler set to Round Robin with priority %i FAILED!\n", sched_param.sched_priority);
+}
+
+void help(void)
+{
+ int k;
+ printf(
+"Usage: alsaloop [OPTION]...\n\n"
+"-h,--help help\n"
+"-g,--config configuration file (one line = one job specified)\n"
+"-d,--daemonize daemonize the main process and use syslog for errors\n"
+"-P,--pdevice playback device\n"
+"-C,--cdevice capture device\n"
+"-X,--pctl playback ctl device\n"
+"-Y,--cctl capture ctl device\n"
+"-l,--latency requested latency in frames\n"
+"-t,--tlatency requested latency in usec (1/1000000sec)\n"
+"-f,--format sample format\n"
+"-c,--channels channels\n"
+"-r,--rate rate\n"
+"-n,--resample resample in alsa-lib\n"
+"-A,--samplerate use converter (0=sincbest,1=sincmedium,2=sincfastest,\n"
+" 3=zerohold,4=linear)\n"
+"-B,--buffer buffer size in frames\n"
+"-E,--period period size in frames\n"
+"-s,--seconds duration of loop in seconds\n"
+"-b,--nblock non-block mode (very early process wakeup)\n"
+"-S,--sync sync mode(0=none,1=simple,2=captshift,3=playshift,4=samplerate,\n"
+" 5=auto)\n"
+"-a,--slave stream parameters slave mode (0=auto, 1=on, 2=off)\n"
+"-T,--thread thread number (-1 = create unique)\n"
+"-m,--mixer redirect mixer, argument is:\n"
+" SRC_SLAVE_ID(PLAYBACK)[@DST_SLAVE_ID(CAPTURE)]\n"
+"-O,--ossmixer rescan and redirect oss mixer, argument is:\n"
+" 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"
+"-U,--xrun xrun profiling\n"
+"-W,--wake process wake timeout in ms\n"
+);
+ printf("\nRecognized sample formats are:");
+ for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
+ const char *s = snd_pcm_format_name(k);
+ if (s)
+ printf(" %s", s);
+ }
+ printf("\n\n");
+ printf(
+"Tip #1 (usable 500ms latency, good CPU usage, superb xrun prevention):\n"
+" alsaloop -t 500000\n"
+"Tip #2 (superb 1ms latency, but heavy CPU usage):\n"
+" alsaloop -t 1000\n"
+);
+}
+
+static long timediff(struct timeval t1, struct timeval t2)
+{
+ signed long l;
+
+ t1.tv_sec -= t2.tv_sec;
+ l = (signed long) t1.tv_usec - (signed long) t2.tv_usec;
+ if (l < 0) {
+ t1.tv_sec--;
+ l = 1000000 + l;
+ l %= 1000000;
+ }
+ return (t1.tv_sec * 1000000) + l;
+}
+
+static void add_loop(struct loopback *loop)
+{
+ loopbacks = realloc(loopbacks, (loopbacks_count + 1) *
+ sizeof(struct loopback *));
+ if (loopbacks == NULL) {
+ logit(LOG_CRIT, "No enough memory\n");
+ exit(EXIT_FAILURE);
+ }
+ loopbacks[loopbacks_count++] = loop;
+}
+
+static int init_mixer_control(struct loopback_control *control,
+ char *id)
+{
+ int err;
+
+ err = snd_ctl_elem_id_malloc(&control->id);
+ if (err < 0)
+ return err;
+ err = snd_ctl_elem_info_malloc(&control->info);
+ if (err < 0)
+ return err;
+ err = snd_ctl_elem_value_malloc(&control->value);
+ if (err < 0)
+ return err;
+ err = control_parse_id(id, control->id);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int add_mixers(struct loopback *loop,
+ char **mixers,
+ int mixers_count)
+{
+ struct loopback_mixer *mixer, *last = NULL;
+ char *str1;
+ int err;
+
+ while (mixers_count > 0) {
+ mixer = calloc(1, sizeof(*mixer));
+ if (mixer == NULL)
+ return -ENOMEM;
+ if (last)
+ last->next = mixer;
+ else
+ loop->controls = mixer;
+ last = mixer;
+ str1 = strchr(*mixers, '@');
+ if (str1)
+ *str1 = '\0';
+ err = init_mixer_control(&mixer->src, *mixers);
+ if (err < 0) {
+ logit(LOG_CRIT, "Wrong mixer control ID syntax '%s'\n", *mixers);
+ return -EINVAL;
+ }
+ err = init_mixer_control(&mixer->dst, str1 ? str1 + 1 : *mixers);
+ if (err < 0) {
+ logit(LOG_CRIT, "Wrong mixer control ID syntax '%s'\n", str1 ? str1 + 1 : *mixers);
+ return -EINVAL;
+ }
+ if (str1)
+ *str1 = '@';
+ mixers++;
+ mixers_count--;
+ }
+ return 0;
+}
+
+static int add_oss_mixers(struct loopback *loop,
+ char **mixers,
+ int mixers_count)
+{
+ struct loopback_ossmixer *mixer, *last = NULL;
+ char *str1, *str2;
+
+ while (mixers_count > 0) {
+ mixer = calloc(1, sizeof(*mixer));
+ if (mixer == NULL)
+ return -ENOMEM;
+ if (last)
+ last->next = mixer;
+ else
+ loop->oss_controls = mixer;
+ last = mixer;
+ str1 = strchr(*mixers, ',');
+ if (str1)
+ *str1 = '\0';
+ str2 = strchr(str1 ? str1 + 1 : *mixers, '@');
+ if (str2)
+ *str2 = '\0';
+ mixer->alsa_id = strdup(*mixers);
+ if (str1)
+ mixer->alsa_index = atoi(str1);
+ mixer->oss_id = strdup(str2 ? str2 + 1 : *mixers);
+ if (mixer->alsa_id == NULL || mixer->oss_id == NULL) {
+ logit(LOG_CRIT, "Not enough memory");
+ return -ENOMEM;
+ }
+ if (str1)
+ *str1 = ',';
+ if (str2)
+ *str2 = ',';
+ mixers++;
+ mixers_count--;
+ }
+ return 0;
+}
+
+static int parse_config_file(const char *file, snd_output_t *output);
+
+static int parse_config(int argc, char *argv[], snd_output_t *output,
+ int cmdline)
+{
+ struct option long_option[] =
+ {
+ {"help", 0, NULL, 'h'},
+ {"config", 1, NULL, 'g'},
+ {"daemonize", 0, NULL, 'd'},
+ {"pdevice", 1, NULL, 'P'},
+ {"cdevice", 1, NULL, 'C'},
+ {"pctl", 1, NULL, 'X'},
+ {"cctl", 1, NULL, 'Y'},
+ {"latency", 1, NULL, 'l'},
+ {"tlatency", 1, NULL, 't'},
+ {"format", 1, NULL, 'f'},
+ {"channels", 1, NULL, 'c'},
+ {"rate", 1, NULL, 'r'},
+ {"buffer", 1, NULL, 'B'},
+ {"period", 1, NULL, 'E'},
+ {"seconds", 1, NULL, 's'},
+ {"nblock", 0, NULL, 'b'},
+ {"effect", 0, NULL, 'e'},
+ {"verbose", 0, NULL, 'v'},
+ {"resample", 0, NULL, 'n'},
+ {"samplerate", 1, NULL, 'A'},
+ {"sync", 1, NULL, 'S'},
+ {"slave", 1, NULL, 'a'},
+ {"thread", 1, NULL, 'T'},
+ {"mixer", 1, NULL, 'm'},
+ {"ossmixer", 1, NULL, 'O'},
+ {"workaround", 1, NULL, 'w'},
+ {"xrun", 0, NULL, 'U'},
+ {NULL, 0, NULL, 0},
+ };
+ int err, morehelp;
+ char *arg_config = NULL;
+ char *arg_pdevice = NULL;
+ char *arg_cdevice = NULL;
+ char *arg_pctl = NULL;
+ char *arg_cctl = NULL;
+ unsigned int arg_latency_req = 0;
+ unsigned int arg_latency_reqtime = 10000;
+ snd_pcm_format_t arg_format = SND_PCM_FORMAT_S16_LE;
+ unsigned int arg_channels = 2;
+ unsigned int arg_rate = 48000;
+ snd_pcm_uframes_t arg_buffer_size = 0;
+ snd_pcm_uframes_t arg_period_size = 0;
+ unsigned long arg_loop_time = ~0UL;
+ int arg_nblock = 0;
+ int arg_effect = 0;
+ int arg_resample = 0;
+ int arg_samplerate = SRC_SINC_FASTEST + 1;
+ int arg_sync = SYNC_TYPE_AUTO;
+ int arg_slave = SLAVE_TYPE_AUTO;
+ int arg_thread = 0;
+ struct loopback *loop = NULL;
+ char *arg_mixers[MAX_MIXERS];
+ int arg_mixers_count = 0;
+ char *arg_ossmixers[MAX_MIXERS];
+ int arg_ossmixers_count = 0;
+ int arg_xrun = arg_default_xrun;
+ int arg_wake = arg_default_wake;
+
+ morehelp = 0;
+ while (1) {
+ int c;
+ if ((c = getopt_long(argc, argv,
+ "hdg:P:C:X:Y:l:t:F:f:c:r:s:benvA:S:a:m:T:O:w:UW:",
+ long_option, NULL)) < 0)
+ break;
+ switch (c) {
+ case 'h':
+ morehelp++;
+ break;
+ case 'g':
+ arg_config = strdup(optarg);
+ break;
+ case 'd':
+ daemonize = 1;
+ use_syslog = 1;
+ openlog("alsaloop", LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ break;
+ case 'P':
+ arg_pdevice = strdup(optarg);
+ break;
+ case 'C':
+ arg_cdevice = strdup(optarg);
+ break;
+ case 'X':
+ arg_pctl = strdup(optarg);
+ break;
+ case 'Y':
+ arg_cctl = strdup(optarg);
+ break;
+ case 'l':
+ err = atoi(optarg);
+ arg_latency_req = err >= 4 ? err : 4;
+ break;
+ case 't':
+ err = atoi(optarg);
+ arg_latency_reqtime = err >= 500 ? err : 500;
+ break;
+ case 'f':
+ arg_format = snd_pcm_format_value(optarg);
+ if (arg_format == SND_PCM_FORMAT_UNKNOWN) {
+ logit(LOG_WARNING, "Unknown format, setting to default S16_LE\n");
+ arg_format = SND_PCM_FORMAT_S16_LE;
+ }
+ break;
+ case 'c':
+ err = atoi(optarg);
+ arg_channels = err >= 1 && err < 1024 ? err : 1;
+ break;
+ case 'r':
+ err = atoi(optarg);
+ arg_rate = err >= 4000 && err < 200000 ? err : 44100;
+ break;
+ case 'B':
+ err = atoi(optarg);
+ arg_buffer_size = err >= 32 && err < 200000 ? err : 0;
+ break;
+ case 'E':
+ err = atoi(optarg);
+ arg_period_size = err >= 32 && err < 200000 ? err : 0;
+ break;
+ case 's':
+ err = atoi(optarg);
+ arg_loop_time = err >= 1 && err <= 100000 ? err : 30;
+ break;
+ case 'b':
+ arg_nblock = 1;
+ break;
+ case 'e':
+ arg_effect = 1;
+ break;
+ case 'n':
+ arg_resample = 1;
+ break;
+ case 'A':
+ if (strcasecmp(optarg, "sincbest") == 0)
+ arg_samplerate = SRC_SINC_BEST_QUALITY;
+ else if (strcasecmp(optarg, "sincmedium") == 0)
+ arg_samplerate = SRC_SINC_MEDIUM_QUALITY;
+ else if (strcasecmp(optarg, "sincfastest") == 0)
+ arg_samplerate = SRC_SINC_FASTEST;
+ else if (strcasecmp(optarg, "zerohold") == 0)
+ arg_samplerate = SRC_ZERO_ORDER_HOLD;
+ else if (strcasecmp(optarg, "linear") == 0)
+ arg_samplerate = SRC_LINEAR;
+ else
+ arg_samplerate = atoi(optarg);
+ if (arg_samplerate < 0 || arg_samplerate > SRC_LINEAR)
+ arg_sync = SRC_SINC_FASTEST;
+ arg_samplerate += 1;
+ break;
+ case 'S':
+ if (strcasecmp(optarg, "samplerate") == 0)
+ arg_sync = SYNC_TYPE_SAMPLERATE;
+ else if (optarg[0] == 'n')
+ arg_sync = SYNC_TYPE_NONE;
+ else if (optarg[0] == 's')
+ arg_sync = SYNC_TYPE_SIMPLE;
+ else if (optarg[0] == 'c')
+ arg_sync = SYNC_TYPE_CAPTRATESHIFT;
+ else if (optarg[0] == 'p')
+ arg_sync = SYNC_TYPE_PLAYRATESHIFT;
+ else if (optarg[0] == 'r')
+ arg_sync = SYNC_TYPE_SAMPLERATE;
+ else
+ arg_sync = atoi(optarg);
+ if (arg_sync < 0 || arg_sync > SYNC_TYPE_LAST)
+ arg_sync = SYNC_TYPE_AUTO;
+ break;
+ case 'a':
+ if (optarg[0] == 'a')
+ arg_slave = SLAVE_TYPE_AUTO;
+ else if (strcasecmp(optarg, "on") == 0)
+ arg_slave = SLAVE_TYPE_ON;
+ else if (strcasecmp(optarg, "off") == 0)
+ arg_slave = SLAVE_TYPE_OFF;
+ else
+ arg_slave = atoi(optarg);
+ if (arg_slave < 0 || arg_slave > SLAVE_TYPE_LAST)
+ arg_slave = SLAVE_TYPE_AUTO;
+ break;
+ case 'T':
+ arg_thread = atoi(optarg);
+ if (arg_thread < 0)
+ arg_thread = 10000000 + loopbacks_count;
+ break;
+ case 'm':
+ if (arg_mixers_count >= MAX_MIXERS) {
+ logit(LOG_CRIT, "Maximum redirected mixer controls reached (max %i)\n", (int)MAX_MIXERS);
+ exit(EXIT_FAILURE);
+ }
+ arg_mixers[arg_mixers_count++] = optarg;
+ break;
+ case 'O':
+ if (arg_ossmixers_count >= MAX_MIXERS) {
+ logit(LOG_CRIT, "Maximum redirected mixer controls reached (max %i)\n", (int)MAX_MIXERS);
+ exit(EXIT_FAILURE);
+ }
+ arg_ossmixers[arg_ossmixers_count++] = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'w':
+ if (strcasecmp(optarg, "serialopen") == 0)
+ workarounds |= WORKAROUND_SERIALOPEN;
+ break;
+ case 'U':
+ arg_xrun = 1;
+ if (cmdline)
+ arg_default_xrun = 1;
+ break;
+ case 'W':
+ arg_wake = atoi(optarg);
+ if (cmdline)
+ arg_default_wake = arg_wake;
+ break;
+ }
+ }
+
+ if (morehelp) {
+ help();
+ exit(EXIT_SUCCESS);
+ }
+ if (arg_config == NULL) {
+ struct loopback_handle *play;
+ struct loopback_handle *capt;
+ err = create_loopback_handle(&play, arg_pdevice, arg_pctl, "playback");
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to create playback handle.\n");
+ exit(EXIT_FAILURE);
+ }
+ err = create_loopback_handle(&capt, arg_cdevice, arg_cctl, "capture");
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to create capture handle.\n");
+ exit(EXIT_FAILURE);
+ }
+ err = create_loopback(&loop, play, capt, output);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to create loopback handle.\n");
+ exit(EXIT_FAILURE);
+ }
+ play->format = capt->format = arg_format;
+ play->rate = play->rate_req = capt->rate = capt->rate_req = arg_rate;
+ play->channels = capt->channels = arg_channels;
+ play->buffer_size_req = capt->buffer_size_req = arg_buffer_size;
+ play->period_size_req = capt->period_size_req = arg_period_size;
+ play->resample = capt->resample = arg_resample;
+ play->nblock = capt->nblock = arg_nblock ? 1 : 0;
+ loop->latency_req = arg_latency_req;
+ loop->latency_reqtime = arg_latency_reqtime;
+ loop->sync = arg_sync;
+ loop->slave = arg_slave;
+ loop->thread = arg_thread;
+ loop->xrun = arg_xrun;
+ loop->wake = arg_wake;
+ err = add_mixers(loop, arg_mixers, arg_mixers_count);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to add mixer controls.\n");
+ exit(EXIT_FAILURE);
+ }
+ err = add_oss_mixers(loop, arg_ossmixers, arg_ossmixers_count);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to add ossmixer controls.\n");
+ exit(EXIT_FAILURE);
+ }
+#ifdef USE_SAMPLERATE
+ loop->src_enable = arg_samplerate > 0;
+ if (loop->src_enable)
+ loop->src_converter_type = arg_samplerate - 1;
+#else
+ if (arg_samplerate > 0) {
+ logit(LOG_CRIT, "No libsamplerate support.\n");
+ exit(EXIT_FAILURE);
+ }
+#endif
+ set_loop_time(loop, arg_loop_time);
+ add_loop(loop);
+ return 0;
+ }
+
+ return parse_config_file(arg_config, output);
+}
+
+static int parse_config_file(const char *file, snd_output_t *output)
+{
+ FILE *fp;
+ char line[2048], word[2048];
+ char *str, *ptr;
+ int argc, c, err = 0;
+ char **argv;
+
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ logit(LOG_CRIT, "Unable to open file '%s': %s\n", file, strerror(errno));
+ return -EIO;
+ }
+ while (!feof(fp)) {
+ if (fgets(line, sizeof(line)-1, fp) == NULL)
+ break;
+ line[sizeof(line)-1] = '\0';
+ my_argv = realloc(my_argv, my_argc + MAX_ARGS * sizeof(char *));
+ if (my_argv == NULL)
+ return -ENOMEM;
+ argv = my_argv + my_argc;
+ argc = 0;
+ argv[argc++] = strdup("<prog>");
+ my_argc++;
+ str = line;
+ while (*str) {
+ ptr = word;
+ while (*str && (*str == ' ' || *str < ' '))
+ str++;
+ if (*str == '#')
+ goto __next;
+ if (*str == '\'' || *str == '\"') {
+ c = *str++;
+ while (*str && *str != c)
+ *ptr++ = *str++;
+ if (*str == c)
+ str++;
+ } else {
+ while (*str && *str != ' ' && *str != '\t')
+ *ptr++ = *str++;
+ }
+ if (ptr != word) {
+ if (*(ptr-1) == '\n')
+ ptr--;
+ *ptr = '\0';
+ if (argc >= MAX_ARGS) {
+ logit(LOG_CRIT, "Too many arguments.");
+ goto __error;
+ }
+ argv[argc++] = strdup(word);
+ my_argc++;
+ }
+ }
+ /* erase runtime variables for getopt */
+ optarg = NULL;
+ optind = opterr = 1;
+ optopt = '?';
+
+ err = parse_config(argc, argv, output, 0);
+ __next:
+ if (err < 0)
+ break;
+ err = 0;
+ }
+ __error:
+ fclose(fp);
+
+ return err;
+}
+
+static void thread_job1(void *_data)
+{
+ struct loopback_thread *thread = _data;
+ snd_output_t *output = thread->output;
+ struct pollfd *pfds = NULL;
+ int pfds_count = 0;
+ int i, j, err, wake = 1000000;
+
+ setscheduler();
+
+ for (i = 0; i < thread->loopbacks_count; i++) {
+ err = pcmjob_init(thread->loopbacks[i]);
+ if (err < 0) {
+ logit(LOG_CRIT, "Loopback initialization failure.\n");
+ my_exit(thread, EXIT_FAILURE);
+ }
+ }
+ for (i = 0; i < thread->loopbacks_count; i++) {
+ err = pcmjob_start(thread->loopbacks[i]);
+ if (err < 0) {
+ logit(LOG_CRIT, "Loopback start failure.\n");
+ my_exit(thread, EXIT_FAILURE);
+ }
+ pfds_count += thread->loopbacks[i]->pollfd_count;
+ j = thread->loopbacks[i]->wake;
+ if (j > 0 && j < wake)
+ wake = j;
+ }
+ if (wake >= 1000000)
+ wake = -1;
+ pfds = calloc(pfds_count, sizeof(struct pollfd));
+ if (pfds == NULL || pfds_count <= 0) {
+ logit(LOG_CRIT, "Poll FDs allocation failed.\n");
+ my_exit(thread, EXIT_FAILURE);
+ }
+ while (!quit) {
+ struct timeval tv1, tv2;
+ for (i = j = 0; i < thread->loopbacks_count; i++) {
+ err = pcmjob_pollfds_init(thread->loopbacks[i], &pfds[j]);
+ if (err < 0) {
+ logit(LOG_CRIT, "Poll FD initialization failed.\n");
+ my_exit(thread, EXIT_FAILURE);
+ }
+ j += err;
+ }
+ if (verbose > 10)
+ gettimeofday(&tv1, NULL);
+ err = poll(pfds, j, wake);
+ if (err < 0)
+ err = -errno;
+ if (verbose > 10) {
+ gettimeofday(&tv2, NULL);
+ snd_output_printf(output, "pool took %lius\n", timediff(tv2, tv1));
+ }
+ if (err < 0) {
+ if (err == -EINTR || err == -ERESTART)
+ continue;
+ logit(LOG_CRIT, "Poll failed: %s\n", strerror(-err));
+ my_exit(thread, EXIT_FAILURE);
+ }
+ for (i = j = 0; i < thread->loopbacks_count; i++) {
+ struct loopback *loop = thread->loopbacks[i];
+ if (j < loop->active_pollfd_count) {
+ err = pcmjob_pollfds_handle(loop, &pfds[j]);
+ if (err < 0) {
+ logit(LOG_CRIT, "pcmjob failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ j += loop->active_pollfd_count;
+ }
+ }
+
+ my_exit(thread, EXIT_SUCCESS);
+}
+
+static void thread_job(struct loopback_thread *thread)
+{
+ if (!thread->threaded) {
+ thread_job1(thread);
+ return;
+ }
+ pthread_create(&thread->thread, NULL, (void *) &thread_job1,
+ (void *) thread);
+}
+
+static void send_to_all(int sig)
+{
+ struct loopback_thread *thread;
+ int i;
+
+ for (i = 0; i < threads_count; i++) {
+ thread = &threads[i];
+ if (thread->threaded)
+ pthread_kill(thread->thread, sig);
+ }
+}
+
+static void signal_handler(int sig)
+{
+ quit = 1;
+ send_to_all(SIGUSR2);
+}
+
+static void signal_handler_state(int sig)
+{
+ pthread_t self = pthread_self();
+ struct loopback_thread *thread;
+ int i, j;
+
+ if (pthread_equal(main_job, self))
+ send_to_all(SIGUSR1);
+ for (i = 0; i < threads_count; i++) {
+ thread = &threads[i];
+ if (thread->thread == self) {
+ for (j = 0; j < thread->loopbacks_count; j++)
+ pcmjob_state(thread->loopbacks[j]);
+ }
+ }
+ signal(sig, signal_handler_state);
+}
+
+static void signal_handler_ignore(int sig)
+{
+ signal(sig, signal_handler_ignore);
+}
+
+int main(int argc, char *argv[])
+{
+ snd_output_t *output;
+ int i, j, k, l, err;
+
+ err = snd_output_stdio_attach(&output, stdout, 0);
+ if (err < 0) {
+ logit(LOG_CRIT, "Output failed: %s\n", snd_strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ err = parse_config(argc, argv, output, 1);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to parse arguments or configuration...\n");
+ exit(EXIT_FAILURE);
+ }
+ while (my_argc > 0)
+ free(my_argv[--my_argc]);
+ free(my_argv);
+
+ if (loopbacks_count <= 0) {
+ logit(LOG_CRIT, "No loopback defined...\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (daemonize) {
+ if (daemon(0, 0) < 0) {
+ logit(LOG_CRIT, "daemon() failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ i = fork();
+ if (i < 0) {
+ logit(LOG_CRIT, "fork() failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (i > 0) {
+ /* wait(&i); */
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ /* we must sort thread IDs */
+ j = -1;
+ do {
+ k = 0x7fffffff;
+ for (i = 0; i < loopbacks_count; i++) {
+ if (loopbacks[i]->thread < k &&
+ loopbacks[i]->thread > j)
+ k = loopbacks[i]->thread;
+ }
+ j++;
+ for (i = 0; i < loopbacks_count; i++) {
+ if (loopbacks[i]->thread == k)
+ loopbacks[i]->thread = j;
+ }
+ } while (k != 0x7fffffff);
+ /* fix maximum thread id */
+ for (i = 0, j = -1; i < loopbacks_count; i++) {
+ if (loopbacks[i]->thread > j)
+ j = loopbacks[i]->thread;
+ }
+ j += 1;
+ threads = calloc(1, sizeof(struct loopback_thread) * j);
+ if (threads == NULL) {
+ logit(LOG_CRIT, "No enough memory\n");
+ exit(EXIT_FAILURE);
+ }
+ /* sort all threads */
+ for (k = 0; k < j; k++) {
+ for (i = l = 0; i < loopbacks_count; i++)
+ if (loopbacks[i]->thread == k)
+ l++;
+ threads[k].loopbacks = malloc(l * sizeof(struct loopback *));
+ threads[k].loopbacks_count = l;
+ threads[k].output = output;
+ threads[k].threaded = j > 1;
+ for (i = l = 0; i < loopbacks_count; i++)
+ if (loopbacks[i]->thread == k)
+ threads[k].loopbacks[l++] = loopbacks[i];
+ }
+ threads_count = j;
+ main_job = pthread_self();
+
+ signal(SIGINT, signal_handler);
+ signal(SIGTERM, signal_handler);
+ signal(SIGABRT, signal_handler);
+ signal(SIGUSR1, signal_handler_state);
+ signal(SIGUSR2, signal_handler_ignore);
+
+ for (k = 0; k < threads_count; k++)
+ thread_job(&threads[k]);
+
+ if (j > 1) {
+ for (k = 0; k < threads_count; k++)
+ pthread_join(threads[k].thread, NULL);
+ }
+
+ if (use_syslog)
+ closelog();
+ exit(EXIT_SUCCESS);
+}
diff --git a/alsaloop/alsaloop.h b/alsaloop/alsaloop.h
new file mode 100644
index 0000000..8dc445a
--- /dev/null
+++ b/alsaloop/alsaloop.h
@@ -0,0 +1,218 @@
+/*
+ * A simple PCM loopback utility
+ * Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#ifdef HAVE_SAMPLERATE_H
+#define USE_SAMPLERATE
+#include <samplerate.h>
+#else
+enum {
+ SRC_SINC_BEST_QUALITY = 0,
+ SRC_SINC_MEDIUM_QUALITY = 1,
+ SRC_SINC_FASTEST = 2,
+ SRC_ZERO_ORDER_HOLD = 3,
+ SRC_LINEAR = 4
+};
+#endif
+
+#define MAX_ARGS 128
+#define MAX_MIXERS 64
+
+#if 0
+#define FILE_PWRITE "/tmp/alsaloop.praw"
+#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 */
+ SYNC_TYPE_CAPTRATESHIFT,
+ SYNC_TYPE_PLAYRATESHIFT,
+ SYNC_TYPE_SAMPLERATE,
+ SYNC_TYPE_AUTO, /* order: CAPTRATESHIFT, PLAYRATESHIFT, */
+ /* SAMPLERATE, SIMPLE */
+ SYNC_TYPE_LAST = SYNC_TYPE_AUTO
+} sync_type_t;
+
+typedef enum _slave_type {
+ SLAVE_TYPE_AUTO = 0,
+ SLAVE_TYPE_ON = 1,
+ SLAVE_TYPE_OFF = 2,
+ SLAVE_TYPE_LAST = SLAVE_TYPE_OFF
+} slave_type_t;
+
+struct loopback_control {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_value_t *value;
+};
+
+struct loopback_mixer {
+ unsigned int skip:1;
+ struct loopback_control src;
+ struct loopback_control dst;
+ struct loopback_mixer *next;
+};
+
+struct loopback_ossmixer {
+ unsigned int skip:1;
+ const char *alsa_id;
+ int alsa_index;
+ const char *oss_id;
+ struct loopback_ossmixer *next;
+};
+
+struct loopback_handle {
+ struct loopback *loopback;
+ char *device;
+ char *ctldev;
+ char *id;
+ int card_number;
+ snd_pcm_t *handle;
+ snd_pcm_access_t access;
+ snd_pcm_format_t format;
+ unsigned int rate;
+ unsigned int rate_req;
+ unsigned int channels;
+ unsigned int buffer_size;
+ unsigned int period_size;
+ snd_pcm_uframes_t avail_min;
+ unsigned int buffer_size_req;
+ unsigned int period_size_req;
+ unsigned int frame_size;
+ unsigned int resample:1; /* do resample */
+ unsigned int nblock:1; /* do block (period size) transfers */
+ unsigned int xrun_pending:1;
+ unsigned int pollfd_count;
+ /* I/O job */
+ char *buf; /* I/O buffer */
+ snd_pcm_uframes_t buf_pos; /* I/O position */
+ snd_pcm_uframes_t buf_count; /* filled samples */
+ snd_pcm_uframes_t buf_size; /* buffer size in frames */
+ snd_pcm_uframes_t buf_over; /* capture buffer overflow */
+ /* statistics */
+ snd_pcm_uframes_t max;
+ unsigned long long counter;
+ unsigned long sync_point; /* in samples */
+ snd_pcm_sframes_t last_delay;
+ double pitch;
+ snd_pcm_uframes_t total_queued;
+ /* control */
+ snd_ctl_t *ctl;
+ unsigned int ctl_pollfd_count;
+ snd_ctl_elem_value_t *ctl_notify;
+ snd_ctl_elem_value_t *ctl_rate_shift;
+ snd_ctl_elem_value_t *ctl_active;
+ snd_ctl_elem_value_t *ctl_format;
+ snd_ctl_elem_value_t *ctl_rate;
+ snd_ctl_elem_value_t *ctl_channels;
+};
+
+struct loopback {
+ char *id;
+ struct loopback_handle *capt;
+ struct loopback_handle *play;
+ snd_pcm_uframes_t latency; /* final latency in frames */
+ unsigned int latency_req; /* in frames */
+ unsigned int latency_reqtime; /* in us */
+ unsigned long loop_time; /* ~0 = unlimited (in seconds) */
+ unsigned long long loop_limit; /* ~0 = unlimited (in frames) */
+ snd_output_t *output;
+ snd_output_t *state;
+ int pollfd_count;
+ int active_pollfd_count;
+ unsigned int linked:1; /* linked streams */
+ unsigned int reinit:1;
+ unsigned int running:1;
+ unsigned int stop_pending:1;
+ snd_pcm_uframes_t stop_count;
+ sync_type_t sync; /* type of sync */
+ slave_type_t slave;
+ int thread; /* thread number */
+ unsigned int wake;
+ /* statistics */
+ double pitch;
+ double pitch_delta;
+ snd_pcm_sframes_t pitch_diff;
+ snd_pcm_sframes_t pitch_diff_min;
+ snd_pcm_sframes_t pitch_diff_max;
+ unsigned int total_queued_count;
+ snd_timestamp_t tstamp_start;
+ snd_timestamp_t tstamp_end;
+ /* xrun profiling */
+ unsigned int xrun:1; /* xrun profiling */
+ snd_timestamp_t xrun_last_update;
+ snd_timestamp_t xrun_last_wake0;
+ snd_timestamp_t xrun_last_wake;
+ snd_timestamp_t xrun_last_check0;
+ snd_timestamp_t xrun_last_check;
+ snd_pcm_sframes_t xrun_last_pdelay;
+ snd_pcm_sframes_t xrun_last_cdelay;
+ snd_pcm_uframes_t xrun_buf_pcount;
+ snd_pcm_uframes_t xrun_buf_ccount;
+ unsigned int xrun_out_frames;
+ long xrun_max_proctime;
+ double xrun_max_missing;
+ /* control mixer */
+ struct loopback_mixer *controls;
+ struct loopback_ossmixer *oss_controls;
+ /* sample rate */
+ unsigned int use_samplerate:1;
+#ifdef USE_SAMPLERATE
+ unsigned int src_enable:1;
+ int src_converter_type;
+ SRC_STATE *src_state;
+ SRC_DATA src_data;
+ unsigned int src_out_frames;
+#endif
+#ifdef FILE_CWRITE
+ FILE *cfile;
+#endif
+#ifdef FILE_PWRITE
+ FILE *pfile;
+#endif
+};
+
+extern int verbose;
+extern int workarounds;
+extern int use_syslog;
+
+#define logit(priority, fmt, args...) do { \
+ if (use_syslog) \
+ syslog(priority, fmt, ##args); \
+ else \
+ fprintf(stderr, fmt, ##args); \
+} while (0)
+
+int pcmjob_init(struct loopback *loop);
+int pcmjob_done(struct loopback *loop);
+int pcmjob_start(struct loopback *loop);
+int pcmjob_stop(struct loopback *loop);
+int pcmjob_pollfds_init(struct loopback *loop, struct pollfd *fds);
+int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds);
+void pcmjob_state(struct loopback *loop);
+
+int control_parse_id(const char *str, snd_ctl_elem_id_t *id);
+int control_id_match(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2);
+int control_init(struct loopback *loop);
+int control_done(struct loopback *loop);
+int control_event(struct loopback_handle *lhandle, snd_ctl_event_t *ev);
diff --git a/alsaloop/control.c b/alsaloop/control.c
new file mode 100644
index 0000000..8383d79
--- /dev/null
+++ b/alsaloop/control.c
@@ -0,0 +1,424 @@
+/*
+ * A simple PCM loopback utility
+ * Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
+ *
+ * Author: Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <ctype.h>
+#include <syslog.h>
+#include <alsa/asoundlib.h>
+#include "alsaloop.h"
+
+static char *id_str(snd_ctl_elem_id_t *id)
+{
+ static char str[128];
+
+ sprintf(str, "%i,%s,%i,%i,%s,%i",
+ snd_ctl_elem_id_get_numid(id),
+ snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id)),
+ snd_ctl_elem_id_get_device(id),
+ snd_ctl_elem_id_get_subdevice(id),
+ snd_ctl_elem_id_get_name(id),
+ snd_ctl_elem_id_get_index(id));
+ return str;
+}
+
+int control_parse_id(const char *str, snd_ctl_elem_id_t *id)
+{
+ int c, size, numid;
+ char *ptr;
+
+ while (*str == ' ' || *str == '\t')
+ str++;
+ if (!(*str))
+ return -EINVAL;
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); /* default */
+ while (*str) {
+ if (!strncasecmp(str, "numid=", 6)) {
+ str += 6;
+ numid = atoi(str);
+ if (numid <= 0) {
+ logit(LOG_CRIT, "Invalid numid %d\n", numid);
+ return -EINVAL;
+ }
+ snd_ctl_elem_id_set_numid(id, atoi(str));
+ while (isdigit(*str))
+ str++;
+ } else if (!strncasecmp(str, "iface=", 6)) {
+ str += 6;
+ if (!strncasecmp(str, "card", 4)) {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD);
+ str += 4;
+ } else if (!strncasecmp(str, "mixer", 5)) {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ str += 5;
+ } else if (!strncasecmp(str, "pcm", 3)) {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
+ str += 3;
+ } else if (!strncasecmp(str, "rawmidi", 7)) {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_RAWMIDI);
+ str += 7;
+ } else if (!strncasecmp(str, "timer", 5)) {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_TIMER);
+ str += 5;
+ } else if (!strncasecmp(str, "sequencer", 9)) {
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_SEQUENCER);
+ str += 9;
+ } else {
+ return -EINVAL;
+ }
+ } else if (!strncasecmp(str, "name=", 5)) {
+ char buf[64];
+ str += 5;
+ ptr = buf;
+ size = 0;
+ if (*str == '\'' || *str == '\"') {
+ c = *str++;
+ while (*str && *str != c) {
+ if (size < (int)sizeof(buf)) {
+ *ptr++ = *str;
+ size++;
+ }
+ str++;
+ }
+ if (*str == c)
+ str++;
+ } else {
+ while (*str && *str != ',') {
+ if (size < (int)sizeof(buf)) {
+ *ptr++ = *str;
+ size++;
+ }
+ str++;
+ }
+ }
+ *ptr = '\0';
+ snd_ctl_elem_id_set_name(id, buf);
+ } else if (!strncasecmp(str, "index=", 6)) {
+ str += 6;
+ snd_ctl_elem_id_set_index(id, atoi(str));
+ while (isdigit(*str))
+ str++;
+ } else if (!strncasecmp(str, "device=", 7)) {
+ str += 7;
+ snd_ctl_elem_id_set_device(id, atoi(str));
+ while (isdigit(*str))
+ str++;
+ } else if (!strncasecmp(str, "subdevice=", 10)) {
+ str += 10;
+ snd_ctl_elem_id_set_subdevice(id, atoi(str));
+ while (isdigit(*str))
+ str++;
+ }
+ if (*str == ',') {
+ str++;
+ } else {
+ if (*str)
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+int control_id_match(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2)
+{
+ if (snd_ctl_elem_id_get_interface(id1) !=
+ snd_ctl_elem_id_get_interface(id2))
+ return 0;
+ if (snd_ctl_elem_id_get_device(id1) !=
+ snd_ctl_elem_id_get_device(id2))
+ return 0;
+ if (snd_ctl_elem_id_get_subdevice(id1) !=
+ snd_ctl_elem_id_get_subdevice(id2))
+ return 0;
+ if (strcmp(snd_ctl_elem_id_get_name(id1),
+ snd_ctl_elem_id_get_name(id2)) != 0)
+ return 0;
+ if (snd_ctl_elem_id_get_index(id1) !=
+ snd_ctl_elem_id_get_index(id2))
+ return 0;
+ return 1;
+}
+
+static int control_init1(struct loopback_handle *lhandle,
+ struct loopback_control *ctl)
+{
+ int err;
+
+ snd_ctl_elem_info_set_id(ctl->info, ctl->id);
+ snd_ctl_elem_value_set_id(ctl->value, ctl->id);
+ if (lhandle->ctl == NULL) {
+ logit(LOG_WARNING, "Unable to read control info for '%s'\n", id_str(ctl->id));
+ return -EIO;
+ }
+ err = snd_ctl_elem_info(lhandle->ctl, ctl->info);
+ if (err < 0) {
+ logit(LOG_WARNING, "Unable to read control info '%s': %s\n", id_str(ctl->id), snd_strerror(err));
+ return err;
+ }
+ err = snd_ctl_elem_read(lhandle->ctl, ctl->value);
+ if (err < 0) {
+ logit(LOG_WARNING, "Unable to read control value (init1) '%s': %s\n", id_str(ctl->id), snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int copy_value(struct loopback_control *dst,
+ struct loopback_control *src)
+{
+ snd_ctl_elem_type_t type;
+ unsigned int count;
+ int i;
+
+ type = snd_ctl_elem_info_get_type(dst->info);
+ count = snd_ctl_elem_info_get_count(dst->info);
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_boolean(dst->value,
+ i, snd_ctl_elem_value_get_boolean(src->value, i));
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (i = 0; i < count; i++) {
+ snd_ctl_elem_value_set_integer(dst->value,
+ i, snd_ctl_elem_value_get_integer(src->value, i));
+ }
+ break;
+ default:
+ logit(LOG_CRIT, "Unable to copy control value for type %s\n", snd_ctl_elem_type_name(type));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int oss_set(struct loopback *loop,
+ struct loopback_ossmixer *ossmix,
+ int enable)
+{
+ char buf[128], file[128];
+ int fd;
+
+ if (loop->capt->card_number < 0)
+ return 0;
+ if (!enable) {
+ sprintf(buf, "%s \"\" 0\n", ossmix->oss_id);
+ } else {
+ sprintf(buf, "%s \"%s\" %i\n", ossmix->oss_id, ossmix->alsa_id, ossmix->alsa_index);
+ }
+ sprintf(file, "/proc/asound/card%i/oss_mixer", loop->capt->card_number);
+ if (verbose)
+ snd_output_printf(loop->output, "%s: Initialize OSS volume %s: %s", loop->id, file, buf);
+ fd = open(file, O_WRONLY);
+ if (fd >= 0 && write(fd, buf, strlen(buf)) == strlen(buf)) {
+ close(fd);
+ return 0;
+ }
+ if (fd >= 0)
+ close(fd);
+ logit(LOG_INFO, "%s: Unable to initialize OSS Mixer ID '%s'\n", loop->id, ossmix->oss_id);
+ return -1;
+}
+
+static int control_init2(struct loopback *loop,
+ struct loopback_mixer *mix)
+{
+ snd_ctl_elem_type_t type;
+ unsigned int count;
+ int err;
+
+ snd_ctl_elem_info_copy(mix->dst.info, mix->src.info);
+ snd_ctl_elem_info_set_id(mix->dst.info, mix->dst.id);
+ snd_ctl_elem_value_clear(mix->dst.value);
+ snd_ctl_elem_value_set_id(mix->dst.value, mix->dst.id);
+ type = snd_ctl_elem_info_get_type(mix->dst.info);
+ count = snd_ctl_elem_info_get_count(mix->dst.info);
+ snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id);
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ err = snd_ctl_elem_add_boolean(loop->capt->ctl,
+ mix->dst.id, count);
+ copy_value(&mix->dst, &mix->src);
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ err = snd_ctl_elem_add_integer(loop->capt->ctl,
+ mix->dst.id, count,
+ snd_ctl_elem_info_get_min(mix->dst.info),
+ snd_ctl_elem_info_get_max(mix->dst.info),
+ snd_ctl_elem_info_get_step(mix->dst.info));
+ copy_value(&mix->dst, &mix->src);
+ break;
+ default:
+ logit(LOG_CRIT, "Unable to handle control type %s\n", snd_ctl_elem_type_name(type));
+ err = -EINVAL;
+ break;
+ }
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to create control '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
+ return err;
+ }
+ err = snd_ctl_elem_unlock(loop->capt->ctl, mix->dst.id);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to unlock control info '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
+ return err;
+ }
+ err = snd_ctl_elem_info(loop->capt->ctl, mix->dst.info);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to read control info '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
+ return err;
+ }
+ if (snd_ctl_elem_info_is_tlv_writable(mix->dst.info)) {
+ unsigned int tlv[64];
+ err = snd_ctl_elem_tlv_read(loop->play->ctl,
+ mix->src.id,
+ tlv, sizeof(tlv));
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to read TLV for '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
+ tlv[0] = tlv[1] = 0;
+ }
+ err = snd_ctl_elem_tlv_write(loop->capt->ctl,
+ mix->dst.id,
+ tlv);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to write TLV for '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
+ return err;
+ }
+ }
+ err = snd_ctl_elem_write(loop->capt->ctl, mix->dst.value);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to write control value '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+int control_init(struct loopback *loop)
+{
+ struct loopback_mixer *mix;
+ struct loopback_ossmixer *ossmix;
+ int err;
+
+ for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next)
+ oss_set(loop, ossmix, 0);
+ for (mix = loop->controls; mix; mix = mix->next) {
+ err = control_init1(loop->play, &mix->src);
+ if (err < 0) {
+ logit(LOG_WARNING, "%s: Disabling playback control '%s'\n", loop->id, id_str(mix->src.id));
+ mix->skip = 1;
+ continue;
+ }
+ err = control_init2(loop, mix);
+ if (err < 0)
+ return err;
+ }
+ for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) {
+ err = oss_set(loop, ossmix, 1);
+ if (err < 0) {
+ ossmix->skip = 1;
+ logit(LOG_WARNING, "%s: Disabling OSS mixer ID '%s'\n", loop->id, ossmix->oss_id);
+ }
+ }
+ return 0;
+}
+
+int control_done(struct loopback *loop)
+{
+ struct loopback_mixer *mix;
+ struct loopback_ossmixer *ossmix;
+ int err;
+
+ if (loop->capt->ctl == NULL)
+ return 0;
+ for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) {
+ err = oss_set(loop, ossmix, 0);
+ if (err < 0)
+ logit(LOG_WARNING, "%s: Unable to remove OSS control '%s'\n", loop->id, ossmix->oss_id);
+ }
+ for (mix = loop->controls; mix; mix = mix->next) {
+ if (mix->skip)
+ continue;
+ err = snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id);
+ if (err < 0)
+ logit(LOG_WARNING, "%s: Unable to remove control '%s': %s\n", loop->id, id_str(mix->dst.id), snd_strerror(err));
+ }
+ return 0;
+}
+
+static int control_event1(struct loopback *loop,
+ struct loopback_mixer *mix,
+ snd_ctl_event_t *ev,
+ int capture)
+{
+ unsigned int mask = snd_ctl_event_elem_get_mask(ev);
+ int err;
+
+ if (mask == SND_CTL_EVENT_MASK_REMOVE)
+ return 0;
+ if ((mask & SND_CTL_EVENT_MASK_VALUE) == 0)
+ return 0;
+ if (!capture) {
+ snd_ctl_elem_value_set_id(mix->src.value, mix->src.id);
+ err = snd_ctl_elem_read(loop->play->ctl, mix->src.value);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to read control value (event1) '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
+ return err;
+ }
+ copy_value(&mix->dst, &mix->src);
+ err = snd_ctl_elem_write(loop->capt->ctl, mix->dst.value);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to write control value (event1) '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
+ return err;
+ }
+ } else {
+ err = snd_ctl_elem_read(loop->capt->ctl, mix->dst.value);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to read control value (event2) '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
+ return err;
+ }
+ copy_value(&mix->src, &mix->dst);
+ err = snd_ctl_elem_write(loop->play->ctl, mix->src.value);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to write control value (event2) '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
+ return err;
+ }
+ }
+ return 0;
+}
+
+int control_event(struct loopback_handle *lhandle, snd_ctl_event_t *ev)
+{
+ snd_ctl_elem_id_t *id2;
+ struct loopback_mixer *mix;
+ int capt = lhandle == lhandle->loopback->capt;
+ int err;
+
+ snd_ctl_elem_id_alloca(&id2);
+ snd_ctl_event_elem_get_id(ev, id2);
+ for (mix = lhandle->loopback->controls; mix; mix = mix->next) {
+ if (mix->skip)
+ continue;
+ if (control_id_match(id2, capt ? mix->dst.id : mix->src.id)) {
+ err = control_event1(lhandle->loopback, mix, ev, capt);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
diff --git a/alsaloop/effect-sweep.c b/alsaloop/effect-sweep.c
new file mode 100644
index 0000000..4a0903d
--- /dev/null
+++ b/alsaloop/effect-sweep.c
@@ -0,0 +1,128 @@
+/*
+ * Bandpass filter sweep effect
+ * Copyright (c) Maarten de Boer <mdeboer@iua.upf.es>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <math.h>
+#include <alsa/asoundlib.h>
+
+struct effect_private {
+ /* filter the sweep variables */
+ float lfo,dlfo,fs,fc,BW,C,D,a0,a1,a2,b1,b2,*x[3],*y[3];
+ float lfo_depth, lfo_center;
+ unsigned int channels;
+};
+
+static int effect_init(struct lookback *loopback,
+ void *private_data,
+ snd_pcm_access_t access,
+ unsigned int channels,
+ unsigned int rate,
+ snd_pcm_format_t format)
+{
+ struct effect_private *priv = private_data;
+ int i;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (format != SND_PCM_FORMAT_S16_LE)
+ return -EIO;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (format != SND_PCM_FORMAT_S16_BE)
+ return -EIO;
+#else
+ return -EIO;
+#endif
+ priv->fs = (float) rate;
+ priv->channels = channels;
+ for (i = 0; i < 3; i++) {
+ priv->x[i] = calloc(channels * sizeof(float));
+ priv->y[i] = calloc(channels * sizeof(float));
+ }
+ return 0;
+}
+
+static int effect_done(struct loopback *loopback,
+ void *private_data)
+{
+ struct effect_private *priv = private_data;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ free(priv->x[i]);
+ free(priv->y[i]);
+ }
+ return 0;
+}
+
+static int effect_apply(struct loopback *loopback,
+ void *private_data,
+ const snd_pcm_channel_area_t *areas,
+ snd_uframes_t offset,
+ snd_uframes_t frames)
+{
+ struct effect_private *priv = private_data;
+ short *samples = (short*)areas[0].addr + offset*priv->channels;
+ snd_uframes_t i;
+
+ for (i=0; i < frames; i++) {
+ int chn;
+
+ fc = sin(priv->lfo)*priv->lfo_depth+priv->lfo_center;
+ priv->lfo += priv->dlfo;
+ if (priv->lfo>2.*M_PI) priv->lfo -= 2.*M_PI;
+ priv->C = 1./tan(M_PI*priv->BW/priv->fs);
+ priv->D = 2.*cos(2*M_PI*fc/fs);
+ priv->a0 = 1./(1.+priv->C);
+ priv->a1 = 0;
+ priv->a2 = -priv->a0;
+ priv->b1 = -priv->C*priv->D*a0;
+ priv->b2 = (priv->C-1)*priv->a0;
+
+ for (chn=0; chn < priv->channels; chn++)
+ {
+ priv->x[chn][2] = priv->x[chn][1];
+ priv->x[chn][1] = priv->x[chn][0];
+
+ priv->y[chn][2] = priv->y[chn][1];
+ priv->y[chn][1] = priv->y[chn][0];
+
+ priv->x[chn][0] = samples[i*channels+chn];
+ priv->y[chn][0] = priv->a0*priv->x[0][chn]
+ + priv->a1*priv->x[1][chn] + priv->a2*x[2][chn]
+ - priv->b1*priv->y[1][chn] - priv->b2*y[2][chn];
+ samples[i*channels+chn] = priv->y[chn][0];
+ }
+ }
+ return 0;
+}
+
+void effect_init_sweep(void)
+{
+ struct effect_private *priv;
+
+ priv = register_effect(effect_init,
+ effect_apply,
+ effect_done,
+ sizeof(struct effectprivate));
+ if (priv) {
+ priv->lfo_center = 2000.;
+ priv->lfo_depth = 1800.;
+ priv->lfo_freq = 0.2;
+ priv->BW = 50;
+ }
+}
diff --git a/alsaloop/pcmjob.c b/alsaloop/pcmjob.c
new file mode 100644
index 0000000..0b84803
--- /dev/null
+++ b/alsaloop/pcmjob.c
@@ -0,0 +1,1925 @@
+/*
+ * A simple PCM loopback utility
+ * Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
+ *
+ * Author: Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+#include <getopt.h>
+#include <alsa/asoundlib.h>
+#include <sys/time.h>
+#include <math.h>
+#include <syslog.h>
+#include <pthread.h>
+#include "alsaloop.h"
+
+#define XRUN_PROFILE_UNKNOWN (-10000000)
+
+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
+
+static const char *sync_types[] = {
+ SYNCTYPE(NONE),
+ SYNCTYPE(SIMPLE),
+ SYNCTYPE(CAPTRATESHIFT),
+ SYNCTYPE(PLAYRATESHIFT),
+ SYNCTYPE(SAMPLERATE),
+ SYNCTYPE(AUTO)
+};
+
+#define SRCTYPE(v) [SRC_##v] = "SRC_" #v
+
+#ifdef USE_SAMPLERATE
+static const char *src_types[] = {
+ SRCTYPE(SINC_BEST_QUALITY),
+ SRCTYPE(SINC_MEDIUM_QUALITY),
+ SRCTYPE(SINC_FASTEST),
+ SRCTYPE(ZERO_ORDER_HOLD),
+ SRCTYPE(LINEAR)
+};
+#endif
+
+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;
+}
+
+static inline unsigned long long
+ frames_to_time(unsigned int rate,
+ snd_pcm_uframes_t frames)
+{
+ return (frames * 1000000ULL) / rate;
+}
+
+static inline snd_pcm_uframes_t time_to_frames(unsigned int rate,
+ unsigned long long time)
+{
+ return (time * rate) / 1000000ULL;
+}
+
+static int setparams_stream(struct loopback_handle *lhandle,
+ snd_pcm_hw_params_t *params)
+{
+ snd_pcm_t *handle = lhandle->handle;
+ int err;
+ unsigned int rrate;
+
+ err = snd_pcm_hw_params_any(handle, params);
+ if (err < 0) {
+ 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);
+ if (err < 0) {
+ logit(LOG_CRIT, "Resample setup failed for %s (val %i): %s\n", lhandle->id, lhandle->resample, snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_hw_params_set_access(handle, params, lhandle->access);
+ if (err < 0) {
+ logit(LOG_CRIT, "Access type not available for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_hw_params_set_format(handle, params, lhandle->format);
+ if (err < 0) {
+ logit(LOG_CRIT, "Sample format not available for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_hw_params_set_channels(handle, params, lhandle->channels);
+ if (err < 0) {
+ logit(LOG_CRIT, "Channels count (%i) not available for %s: %s\n", lhandle->channels, lhandle->id, snd_strerror(err));
+ return err;
+ }
+ rrate = lhandle->rate_req;
+ err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
+ if (err < 0) {
+ logit(LOG_CRIT, "Rate %iHz not available for %s: %s\n", lhandle->rate_req, lhandle->id, snd_strerror(err));
+ return err;
+ }
+ rrate = 0;
+ snd_pcm_hw_params_get_rate(params, &rrate, 0);
+ lhandle->rate = rrate;
+ if (
+#ifdef USE_SAMPLERATE
+ !lhandle->loopback->src_enable &&
+#endif
+ (int)rrate != lhandle->rate) {
+ logit(LOG_CRIT, "Rate does not match (requested %iHz, got %iHz, resample %i)\n", lhandle->rate, rrate, lhandle->resample);
+ return -EINVAL;
+ }
+ lhandle->pitch = (double)lhandle->rate_req / (double)lhandle->rate;
+ return 0;
+}
+
+static int setparams_bufsize(struct loopback_handle *lhandle,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_hw_params_t *tparams,
+ snd_pcm_uframes_t bufsize)
+{
+ snd_pcm_t *handle = lhandle->handle;
+ int err;
+ snd_pcm_uframes_t periodsize;
+ snd_pcm_uframes_t buffersize;
+ snd_pcm_uframes_t last_bufsize = 0;
+
+ if (lhandle->buffer_size_req > 0) {
+ bufsize = lhandle->buffer_size_req;
+ last_bufsize = bufsize;
+ goto __set_it;
+ }
+ __again:
+ if (lhandle->buffer_size_req > 0) {
+ logit(LOG_CRIT, "Unable to set buffer size %li for %s\n", (long)lhandle->buffer_size, lhandle->id);
+ return -EIO;
+ }
+ if (last_bufsize == bufsize)
+ bufsize += 4;
+ last_bufsize = bufsize;
+ if (bufsize > 10*1024*1024) {
+ logit(LOG_CRIT, "Buffer size too big\n");
+ return -EIO;
+ }
+ __set_it:
+ snd_pcm_hw_params_copy(params, tparams);
+ periodsize = bufsize * 8;
+ err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &periodsize);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to set buffer size %li for %s: %s\n", periodsize, lhandle->id, snd_strerror(err));
+ 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
+ periodsize /= 8;
+ err = snd_pcm_hw_params_set_period_size_near(handle, params, &periodsize, 0);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to set period size %li for %s: %s\n", periodsize, lhandle->id, snd_strerror(err));
+ 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);
+ if (periodsize * 2 > buffersize)
+ goto __again;
+ lhandle->period_size = periodsize;
+ lhandle->buffer_size = buffersize;
+ return 0;
+}
+
+static int setparams_set(struct loopback_handle *lhandle,
+ snd_pcm_hw_params_t *params,
+ snd_pcm_sw_params_t *swparams,
+ snd_pcm_uframes_t bufsize)
+{
+ snd_pcm_t *handle = lhandle->handle;
+ int err;
+ snd_pcm_uframes_t val, period_size, buffer_size;
+
+ err = snd_pcm_hw_params(handle, params);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to set hw params for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_sw_params_current(handle, swparams);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to determine current swparams for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0x7fffffff);
+ if (err < 0) {
+ 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, &period_size, NULL);
+ snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
+ if (lhandle->nblock) {
+ if (lhandle == lhandle->loopback->play) {
+ 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) {
+ val = bufsize + bufsize / 2;
+ if (val < (period_size * 3) / 4)
+ val = (period_size * 3) / 4;
+ if (val > (buffer_size * 3) / 4)
+ val = (buffer_size * 3) / 4;
+ val = buffer_size - val;
+ } else {
+ val = bufsize / 2;
+ if (val < period_size / 2)
+ val = period_size / 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) {
+ logit(LOG_CRIT, "Unable to set avail min for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ snd_pcm_sw_params_get_avail_min(swparams, &lhandle->avail_min);
+ err = snd_pcm_sw_params(handle, swparams);
+ if (err < 0) {
+ logit(LOG_CRIT, "Unable to set sw params for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int setparams(struct loopback *loop, snd_pcm_uframes_t bufsize)
+{
+ int err;
+ snd_pcm_hw_params_t *pt_params, *ct_params; /* templates with rate, format and channels */
+ snd_pcm_hw_params_t *p_params, *c_params;
+ snd_pcm_sw_params_t *p_swparams, *c_swparams;
+
+ snd_pcm_hw_params_alloca(&p_params);
+ snd_pcm_hw_params_alloca(&c_params);
+ snd_pcm_hw_params_alloca(&pt_params);
+ snd_pcm_hw_params_alloca(&ct_params);
+ snd_pcm_sw_params_alloca(&p_swparams);
+ snd_pcm_sw_params_alloca(&c_swparams);
+ if ((err = setparams_stream(loop->play, pt_params)) < 0) {
+ logit(LOG_CRIT, "Unable to set parameters for %s stream: %s\n", loop->play->id, snd_strerror(err));
+ return err;
+ }
+ if ((err = setparams_stream(loop->capt, ct_params)) < 0) {
+ logit(LOG_CRIT, "Unable to set parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err));
+ return err;
+ }
+
+ if ((err = setparams_bufsize(loop->play, p_params, pt_params, bufsize / loop->play->pitch)) < 0) {
+ logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->play->id, snd_strerror(err));
+ return err;
+ }
+ if ((err = setparams_bufsize(loop->capt, c_params, ct_params, bufsize / loop->capt->pitch)) < 0) {
+ logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err));
+ return err;
+ }
+
+ 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, 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;
+ }
+
+#if 0
+ if (!loop->linked)
+ if (snd_pcm_link(loop->capt->handle, loop->play->handle) >= 0)
+ loop->linked = 1;
+#endif
+ if ((err = snd_pcm_prepare(loop->play->handle)) < 0) {
+ logit(LOG_CRIT, "Prepare %s error: %s\n", loop->play->id, snd_strerror(err));
+ return err;
+ }
+ if (!loop->linked && (err = snd_pcm_prepare(loop->capt->handle)) < 0) {
+ logit(LOG_CRIT, "Prepare %s error: %s\n", loop->capt->id, snd_strerror(err));
+ return err;
+ }
+
+ if (verbose) {
+ snd_pcm_dump(loop->play->handle, loop->output);
+ snd_pcm_dump(loop->capt->handle, loop->output);
+ }
+ return 0;
+}
+
+static void showlatency(snd_output_t *out, size_t latency, unsigned int rate,
+ char *prefix)
+{
+ double d;
+ d = (double)latency / (double)rate;
+ snd_output_printf(out, "%s %li frames, %.3fus, %.6fms (%.4fHz)\n", prefix, (long)latency, d * 1000000, d * 1000, (double)1 / d);
+}
+
+static long timediff(snd_timestamp_t t1, snd_timestamp_t t2)
+{
+ signed long l;
+
+ t1.tv_sec -= t2.tv_sec;
+ if (t1.tv_usec < t2.tv_usec) {
+ l = ((t1.tv_usec + 1000000) - t2.tv_usec) % 1000000;
+ t1.tv_sec--;
+ } else {
+ l = t1.tv_usec - t2.tv_usec;
+ }
+ return (t1.tv_sec * 1000000) + l;
+}
+
+static int getcurtimestamp(snd_timestamp_t *ts)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_usec = tv.tv_usec;
+ return 0;
+}
+
+static void xrun_profile0(struct loopback *loop)
+{
+ snd_pcm_sframes_t pdelay, cdelay;
+
+ if (snd_pcm_delay(loop->play->handle, &pdelay) >= 0 &&
+ snd_pcm_delay(loop->capt->handle, &cdelay) >= 0) {
+ getcurtimestamp(&loop->xrun_last_update);
+ loop->xrun_last_pdelay = pdelay;
+ loop->xrun_last_cdelay = cdelay;
+ loop->xrun_buf_pcount = loop->play->buf_count;
+ loop->xrun_buf_ccount = loop->capt->buf_count;
+#ifdef USE_SAMPLERATE
+ loop->xrun_out_frames = loop->src_out_frames;
+#endif
+ }
+}
+
+static inline void xrun_profile(struct loopback *loop)
+{
+ if (loop->xrun)
+ xrun_profile0(loop);
+}
+
+static void xrun_stats0(struct loopback *loop)
+{
+ snd_timestamp_t t;
+ double expected, last, wake, check, queued = -1, proc, missing = -1;
+ double maxbuf, pfilled, cfilled, cqueued = -1, avail_min;
+ double sincejob;
+
+ expected = ((double)loop->latency /
+ (double)loop->play->rate_req) * 1000;
+ getcurtimestamp(&t);
+ last = (double)timediff(t, loop->xrun_last_update) / 1000;
+ wake = (double)timediff(t, loop->xrun_last_wake) / 1000;
+ check = (double)timediff(t, loop->xrun_last_check) / 1000;
+ sincejob = (double)timediff(t, loop->tstamp_start) / 1000;
+ if (loop->xrun_last_pdelay != XRUN_PROFILE_UNKNOWN)
+ queued = ((double)loop->xrun_last_pdelay /
+ (double)loop->play->rate) * 1000;
+ if (loop->xrun_last_cdelay != XRUN_PROFILE_UNKNOWN)
+ cqueued = ((double)loop->xrun_last_cdelay /
+ (double)loop->capt->rate) * 1000;
+ maxbuf = ((double)loop->play->buffer_size /
+ (double)loop->play->rate) * 1000;
+ proc = (double)loop->xrun_max_proctime / 1000;
+ pfilled = ((double)(loop->xrun_buf_pcount + loop->xrun_out_frames) /
+ (double)loop->play->rate) * 1000;
+ cfilled = ((double)loop->xrun_buf_ccount /
+ (double)loop->capt->rate) * 1000;
+ avail_min = (((double)loop->play->buffer_size -
+ (double)loop->play->avail_min ) /
+ (double)loop->play->rate) * 1000;
+ avail_min = expected - avail_min;
+ if (queued >= 0)
+ missing = last - queued;
+ if (missing >= 0 && loop->xrun_max_missing < missing)
+ loop->xrun_max_missing = missing;
+ loop->xrun_max_proctime = 0;
+ getcurtimestamp(&t);
+ logit(LOG_INFO, " last write before %.4fms, queued %.4fms/%.4fms -> missing %.4fms\n", last, queued, cqueued, missing);
+ logit(LOG_INFO, " expected %.4fms, processing %.4fms, max missing %.4fms\n", expected, proc, loop->xrun_max_missing);
+ logit(LOG_INFO, " last wake %.4fms, last check %.4fms, avail_min %.4fms\n", wake, check, avail_min);
+ logit(LOG_INFO, " max buf %.4fms, pfilled %.4fms, cfilled %.4fms\n", maxbuf, pfilled, cfilled);
+ logit(LOG_INFO, " job started before %.4fms\n", sincejob);
+}
+
+static inline void xrun_stats(struct loopback *loop)
+{
+ if (loop->xrun)
+ xrun_stats0(loop);
+}
+
+static inline snd_pcm_uframes_t buf_avail(struct loopback_handle *lhandle)
+{
+ return lhandle->buf_size - lhandle->buf_count;
+}
+
+static void buf_remove(struct loopback *loop, snd_pcm_uframes_t count)
+{
+ /* remove samples from the capture buffer */
+ if (count <= 0)
+ return;
+ if (loop->play->buf == loop->capt->buf) {
+ if (count < loop->capt->buf_count)
+ loop->capt->buf_count -= count;
+ else
+ loop->capt->buf_count = 0;
+ }
+}
+
+#if 0
+static void buf_add_copy(struct loopback *loop)
+{
+ struct loopback_handle *capt = loop->capt;
+ struct loopback_handle *play = loop->play;
+ snd_pcm_uframes_t count, count1, cpos, ppos;
+
+ count = capt->buf_count;
+ cpos = capt->buf_pos - count;
+ if (cpos > capt->buf_size)
+ cpos += capt->buf_size;
+ ppos = (play->buf_pos + play->buf_count) % play->buf_size;
+ while (count > 0) {
+ count1 = count;
+ if (count1 + cpos > capt->buf_size)
+ count1 = capt->buf_size - cpos;
+ if (count1 > buf_avail(play))
+ count1 = buf_avail(play);
+ if (count1 + ppos > play->buf_size)
+ count1 = play->buf_size - ppos;
+ if (count1 == 0)
+ break;
+ memcpy(play->buf + ppos * play->frame_size,
+ capt->buf + cpos * capt->frame_size,
+ count1 * capt->frame_size);
+ play->buf_count += count1;
+ capt->buf_count -= count1;
+ ppos += count1;
+ ppos %= play->buf_size;
+ cpos += count1;
+ cpos %= capt->buf_size;
+ count -= count1;
+ }
+}
+#endif
+
+#ifdef USE_SAMPLERATE
+static void buf_add_src(struct loopback *loop)
+{
+ struct loopback_handle *capt = loop->capt;
+ struct loopback_handle *play = loop->play;
+ float *old_data_out;
+ snd_pcm_uframes_t count, pos, count1, pos1;
+ count = capt->buf_count;
+ pos = 0;
+ pos1 = capt->buf_pos - count;
+ if (pos1 > capt->buf_size)
+ pos1 += capt->buf_size;
+ while (count > 0) {
+ count1 = count;
+ if (count1 + pos1 > capt->buf_size)
+ count1 = capt->buf_size - pos1;
+ if (capt->format == SND_PCM_FORMAT_S32)
+ src_int_to_float_array((int *)(capt->buf +
+ pos1 * capt->frame_size),
+ loop->src_data.data_in +
+ pos * capt->channels,
+ count1 * capt->channels);
+ else
+ src_short_to_float_array((short *)(capt->buf +
+ pos1 * capt->frame_size),
+ loop->src_data.data_in +
+ pos * capt->channels,
+ count1 * capt->channels);
+ count -= count1;
+ pos += count1;
+ pos1 += count1;
+ pos1 %= capt->buf_size;
+ }
+ loop->src_data.input_frames = pos;
+ loop->src_data.output_frames = play->buf_size -
+ loop->src_out_frames;
+ loop->src_data.end_of_input = 0;
+ old_data_out = loop->src_data.data_out;
+ loop->src_data.data_out = old_data_out + loop->src_out_frames;
+ src_process(loop->src_state, &loop->src_data);
+ loop->src_data.data_out = old_data_out;
+ capt->buf_count -= loop->src_data.input_frames_used;
+ count = loop->src_data.output_frames_gen +
+ loop->src_out_frames;
+ pos = 0;
+ pos1 = (play->buf_pos + play->buf_count) % play->buf_size;
+ while (count > 0) {
+ count1 = count;
+ if (count1 + pos1 > play->buf_size)
+ count1 = play->buf_size - pos1;
+ if (count1 > buf_avail(play))
+ count1 = buf_avail(play);
+ if (count1 == 0)
+ break;
+ if (capt->format == SND_PCM_FORMAT_S32)
+ src_float_to_int_array(loop->src_data.data_out +
+ pos * play->channels,
+ (int *)(play->buf +
+ pos1 * play->frame_size),
+ count1 * play->channels);
+ else
+ src_float_to_short_array(loop->src_data.data_out +
+ pos * play->channels,
+ (short *)(play->buf +
+ pos1 * play->frame_size),
+ count1 * play->channels);
+ play->buf_count += count1;
+ count -= count1;
+ pos += count1;
+ pos1 += count1;
+ pos1 %= play->buf_size;
+ }
+#if 0
+ printf("src: pos = %li, gen = %li, out = %li, count = %li\n",
+ (long)pos, (long)loop->src_data.output_frames_gen,
+ (long)loop->src_out_frames, play->buf_count);
+#endif
+ loop->src_out_frames = (loop->src_data.output_frames_gen +
+ loop->src_out_frames) - pos;
+ if (loop->src_out_frames > 0) {
+ memmove(loop->src_data.data_out,
+ loop->src_data.data_out + pos * play->channels,
+ loop->src_out_frames * play->channels * sizeof(float));
+ }
+}
+#else
+static void buf_add_src(struct loopback *loop)
+{
+}
+#endif
+
+static void buf_add(struct loopback *loop, snd_pcm_uframes_t count)
+{
+ /* copy samples from capture to playback buffer */
+ if (count <= 0)
+ return;
+ if (loop->play->buf == loop->capt->buf) {
+ loop->play->buf_count += count;
+ } else {
+ buf_add_src(loop);
+ }
+}
+
+static int xrun(struct loopback_handle *lhandle)
+{
+ int err;
+
+ if (lhandle == lhandle->loopback->play) {
+ logit(LOG_DEBUG, "underrun for %s\n", lhandle->id);
+ xrun_stats(lhandle->loopback);
+ if ((err = snd_pcm_prepare(lhandle->handle)) < 0)
+ return err;
+ lhandle->xrun_pending = 1;
+ } else {
+ logit(LOG_DEBUG, "overrun for %s\n", lhandle->id);
+ xrun_stats(lhandle->loopback);
+ if ((err = snd_pcm_prepare(lhandle->handle)) < 0)
+ return err;
+ lhandle->xrun_pending = 1;
+ }
+ return 0;
+}
+
+static int suspend(struct loopback_handle *lhandle)
+{
+ int err;
+
+ while ((err = snd_pcm_resume(lhandle->handle)) == -EAGAIN)
+ usleep(1);
+ if (err < 0)
+ return xrun(lhandle);
+ return 0;
+}
+
+static int readit(struct loopback_handle *lhandle)
+{
+ snd_pcm_sframes_t r, res = 0;
+ snd_pcm_sframes_t avail;
+ 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);
+ } else if (avail == 0) {
+ if (snd_pcm_state(lhandle->handle) == SND_PCM_STATE_DRAINING) {
+ lhandle->loopback->reinit = 1;
+ return 0;
+ }
+ }
+ while (avail > 0) {
+ r = buf_avail(lhandle);
+ if (r + lhandle->buf_pos > lhandle->buf_size)
+ r = lhandle->buf_size - lhandle->buf_pos;
+ if (r > avail)
+ r = avail;
+ r = snd_pcm_readi(lhandle->handle,
+ lhandle->buf +
+ lhandle->buf_pos *
+ lhandle->frame_size, r);
+ if (r == 0)
+ return res;
+ if (r < 0) {
+ if (r == -EPIPE) {
+ err = xrun(lhandle);
+ return res > 0 ? res : err;
+ } else if (r == -ESTRPIPE) {
+ if ((err = suspend(lhandle)) < 0)
+ return res > 0 ? res : err;
+ r = 0;
+ } else {
+ return res > 0 ? res : r;
+ }
+ }
+#ifdef FILE_CWRITE
+ if (lhandle->loopback->cfile)
+ fwrite(lhandle->buf + lhandle->buf_pos * lhandle->frame_size,
+ r, lhandle->frame_size, lhandle->loopback->cfile);
+#endif
+ res += r;
+ if (lhandle->max < res)
+ lhandle->max = res;
+ lhandle->counter += r;
+ lhandle->buf_count += r;
+ lhandle->buf_pos += r;
+ lhandle->buf_pos %= lhandle->buf_size;
+ avail -= r;
+ }
+ return res;
+}
+
+static int writeit(struct loopback_handle *lhandle)
+{
+ snd_pcm_sframes_t avail;
+ snd_pcm_sframes_t r, res = 0;
+ int err;
+
+ __again:
+ avail = snd_pcm_avail_update(lhandle->handle);
+ if (avail == -EPIPE) {
+ if ((err = xrun(lhandle)) < 0)
+ return err;
+ return res;
+ } else if (avail == -ESTRPIPE) {
+ if ((err = suspend(lhandle)) < 0)
+ return err;
+ goto __again;
+ }
+ while (avail > 0 && lhandle->buf_count > 0) {
+ r = lhandle->buf_count;
+ if (r + lhandle->buf_pos > lhandle->buf_size)
+ r = lhandle->buf_size - lhandle->buf_pos;
+ if (r > avail)
+ r = avail;
+ r = snd_pcm_writei(lhandle->handle,
+ lhandle->buf +
+ lhandle->buf_pos *
+ lhandle->frame_size, r);
+ if (r <= 0) {
+ if (r == -EPIPE) {
+ if ((err = xrun(lhandle)) < 0)
+ return err;
+ return res;
+ } else if (r == -ESTRPIPE) {
+ }
+ return res > 0 ? res : r;
+ }
+#ifdef FILE_PWRITE
+ if (lhandle->loopback->pfile)
+ fwrite(lhandle->buf + lhandle->buf_pos * lhandle->frame_size,
+ r, lhandle->frame_size, lhandle->loopback->pfile);
+#endif
+ res += r;
+ lhandle->counter += r;
+ lhandle->buf_count -= r;
+ lhandle->buf_pos += r;
+ lhandle->buf_pos %= lhandle->buf_size;
+ xrun_profile(lhandle->loopback);
+ if (lhandle->loopback->stop_pending) {
+ lhandle->loopback->stop_count += r;
+ if (lhandle->loopback->stop_count * lhandle->pitch >
+ lhandle->loopback->latency * 3) {
+ lhandle->loopback->stop_pending = 0;
+ lhandle->loopback->reinit = 1;
+ break;
+ }
+ }
+ }
+ return res;
+}
+
+static snd_pcm_sframes_t remove_samples(struct loopback *loop,
+ int capture_preferred,
+ snd_pcm_sframes_t count)
+{
+ struct loopback_handle *play = loop->play;
+ struct loopback_handle *capt = loop->capt;
+
+ if (loop->play->buf == loop->capt->buf) {
+ if (count > loop->play->buf_count)
+ count = loop->play->buf_count;
+ if (count > loop->capt->buf_count)
+ count = loop->capt->buf_count;
+ capt->buf_count -= count;
+ play->buf_pos += count;
+ play->buf_pos %= play->buf_size;
+ play->buf_count -= count;
+ return count;
+ }
+ if (capture_preferred) {
+ if (count > capt->buf_count)
+ count = capt->buf_count;
+ capt->buf_count -= count;
+ } else {
+ if (count > play->buf_count)
+ count = play->buf_count;
+ play->buf_count -= count;
+ }
+ return count;
+}
+
+static int xrun_sync(struct loopback *loop)
+{
+ struct loopback_handle *play = loop->play;
+ struct loopback_handle *capt = loop->capt;
+ snd_pcm_uframes_t fill = get_whole_latency(loop);
+ snd_pcm_sframes_t pdelay, cdelay, delay1, pdelay1, cdelay1, diff;
+ int err;
+
+ __again:
+ if (verbose > 5)
+ snd_output_printf(loop->output, "%s: xrun sync %i %i\n", loop->id, capt->xrun_pending, play->xrun_pending);
+ if (capt->xrun_pending) {
+ __pagain:
+ capt->xrun_pending = 0;
+ if ((err = snd_pcm_prepare(capt->handle)) < 0) {
+ logit(LOG_CRIT, "%s prepare failed: %s\n", capt->id, snd_strerror(err));
+ return err;
+ }
+ if ((err = snd_pcm_start(capt->handle)) < 0) {
+ logit(LOG_CRIT, "%s start failed: %s\n", capt->id, snd_strerror(err));
+ return err;
+ }
+ } else {
+ diff = readit(capt);
+ buf_add(loop, diff);
+ if (capt->xrun_pending)
+ goto __pagain;
+ }
+ /* skip additional playback samples */
+ if ((err = snd_pcm_delay(capt->handle, &cdelay)) < 0) {
+ if (err == -EPIPE) {
+ capt->xrun_pending = 1;
+ goto __again;
+ }
+ if (err == -ESTRPIPE) {
+ err = suspend(capt);
+ if (err < 0)
+ return err;
+ goto __again;
+ }
+ logit(LOG_CRIT, "%s capture delay failed: %s\n", capt->id, snd_strerror(err));
+ return err;
+ }
+ if ((err = snd_pcm_delay(play->handle, &pdelay)) < 0) {
+ if (err == -EPIPE) {
+ pdelay = 0;
+ play->xrun_pending = 1;
+ } else if (err == -ESTRPIPE) {
+ err = suspend(play);
+ if (err < 0)
+ return err;
+ goto __again;
+ } else {
+ logit(LOG_CRIT, "%s playback delay failed: %s\n", play->id, snd_strerror(err));
+ return err;
+ }
+ }
+ capt->counter = cdelay;
+ play->counter = pdelay;
+ if (play->buf != capt->buf)
+ cdelay += capt->buf_count;
+ pdelay += play->buf_count;
+#ifdef USE_SAMPLERATE
+ pdelay += loop->src_out_frames;
+#endif
+ cdelay1 = cdelay * capt->pitch;
+ pdelay1 = pdelay * play->pitch;
+ delay1 = cdelay1 + pdelay1;
+ capt->total_queued = 0;
+ play->total_queued = 0;
+ loop->total_queued_count = 0;
+ loop->pitch_diff = loop->pitch_diff_min = loop->pitch_diff_max = 0;
+ if (verbose > 6) {
+ snd_output_printf(loop->output,
+ "sync: cdelay=%li(%li), pdelay=%li(%li), fill=%li (delay=%li)"
+#ifdef USE_SAMPLERATE
+ ", src_out=%li"
+#endif
+ "\n",
+ (long)cdelay, (long)cdelay1, (long)pdelay, (long)pdelay1,
+ (long)fill, (long)delay1
+#ifdef USE_SAMPLERATE
+ , (long)loop->src_out_frames
+#endif
+ );
+ snd_output_printf(loop->output,
+ "sync: cbufcount=%li, pbufcount=%li\n",
+ (long)capt->buf_count, (long)play->buf_count);
+ }
+ if (delay1 > fill && capt->counter > 0) {
+ if ((err = snd_pcm_drop(capt->handle)) < 0)
+ return err;
+ if ((err = snd_pcm_prepare(capt->handle)) < 0)
+ return err;
+ if ((err = snd_pcm_start(capt->handle)) < 0)
+ return err;
+ diff = remove_samples(loop, 1, (delay1 - fill) / capt->pitch);
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: capt stop removed %li samples\n", (long)diff);
+ goto __again;
+ }
+ if (delay1 > fill) {
+ diff = (delay1 - fill) / play->pitch;
+ if (diff > play->buf_count)
+ diff = play->buf_count;
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: removing %li playback samples, delay1=%li\n", (long)diff, (long)delay1);
+ diff = remove_samples(loop, 0, diff);
+ pdelay -= diff;
+ pdelay1 = pdelay * play->pitch;
+ delay1 = cdelay1 + pdelay1;
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: removed %li playback samples, delay1=%li\n", (long)diff, (long)delay1);
+ }
+ if (delay1 > fill) {
+ diff = (delay1 - fill) / capt->pitch;
+ if (diff > capt->buf_count)
+ diff = capt->buf_count;
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: removing %li captured samples, delay1=%li\n", (long)diff, (long)delay1);
+ diff -= remove_samples(loop, 1, diff);
+ cdelay -= diff;
+ cdelay1 = cdelay * capt->pitch;
+ delay1 = cdelay1 + pdelay1;
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: removed %li captured samples, delay1=%li\n", (long)diff, (long)delay1);
+ }
+ if (play->xrun_pending) {
+ play->xrun_pending = 0;
+ diff = (fill - delay1) / play->pitch;
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: xrun_pending, silence filling %li / buf_count=%li\n", (long)diff, play->buf_count);
+ if (fill > delay1 && play->buf_count < diff) {
+ diff = diff - play->buf_count;
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: playback silence added %li samples\n", (long)diff);
+ play->buf_pos -= diff;
+ play->buf_pos %= play->buf_size;
+ if ((err = snd_pcm_format_set_silence(play->format, play->buf + play->buf_pos * play->channels, diff)) < 0)
+ return err;
+ play->buf_count += diff;
+ }
+ if ((err = snd_pcm_prepare(play->handle)) < 0) {
+ logit(LOG_CRIT, "%s prepare failed: %s\n", play->id, snd_strerror(err));
+
+ return err;
+ }
+ delay1 = writeit(play);
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: playback wrote %li samples\n", (long)delay1);
+ if (delay1 > diff) {
+ buf_remove(loop, delay1 - diff);
+ if (verbose > 6)
+ snd_output_printf(loop->output,
+ "sync: playback buf_remove %li samples\n", (long)(delay1 - diff));
+ }
+ if ((err = snd_pcm_start(play->handle)) < 0) {
+ logit(LOG_CRIT, "%s start failed: %s\n", play->id, snd_strerror(err));
+ return err;
+ }
+ }
+ if (verbose > 5) {
+ snd_output_printf(loop->output, "%s: xrun sync ok\n", loop->id);
+ if (verbose > 6) {
+ if (snd_pcm_delay(capt->handle, &cdelay) < 0)
+ cdelay = -1;
+ if (snd_pcm_delay(play->handle, &pdelay) < 0)
+ pdelay = -1;
+ if (play->buf != capt->buf)
+ cdelay += capt->buf_count;
+ pdelay += play->buf_count;
+#ifdef USE_SAMPLERATE
+ pdelay += loop->src_out_frames;
+#endif
+ cdelay1 = cdelay * capt->pitch;
+ pdelay1 = pdelay * play->pitch;
+ delay1 = cdelay1 + pdelay1;
+ snd_output_printf(loop->output, "%s: sync verify: %li\n", loop->id, delay1);
+ }
+ }
+ loop->xrun_max_proctime = 0;
+ return 0;
+}
+
+static int set_notify(struct loopback_handle *lhandle, int enable)
+{
+ int err;
+
+ if (lhandle->ctl_notify == NULL)
+ return 0;
+ snd_ctl_elem_value_set_boolean(lhandle->ctl_notify, 0, enable);
+ err = snd_ctl_elem_write(lhandle->ctl, lhandle->ctl_notify);
+ if (err < 0) {
+ logit(LOG_CRIT, "Cannot set PCM Notify element for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_notify);
+ if (err < 0) {
+ logit(LOG_CRIT, "Cannot get PCM Notify element for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int set_rate_shift(struct loopback_handle *lhandle, double pitch)
+{
+ int err;
+
+ if (lhandle->ctl_rate_shift == NULL)
+ return 0;
+ snd_ctl_elem_value_set_integer(lhandle->ctl_rate_shift, 0, pitch * 100000);
+ err = snd_ctl_elem_write(lhandle->ctl, lhandle->ctl_rate_shift);
+ if (err < 0) {
+ logit(LOG_CRIT, "Cannot set PCM Rate Shift element for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+void update_pitch(struct loopback *loop)
+{
+ double pitch = loop->pitch;
+
+#ifdef USE_SAMPLERATE
+ if (loop->sync == SYNC_TYPE_SAMPLERATE) {
+ loop->src_data.src_ratio = (double)1.0 / (pitch *
+ loop->play->pitch * loop->capt->pitch);
+ if (verbose > 2)
+ snd_output_printf(loop->output, "%s: Samplerate src_ratio update1: %.8f\n", loop->id, loop->src_data.src_ratio);
+ } else
+#endif
+ if (loop->sync == SYNC_TYPE_CAPTRATESHIFT) {
+ set_rate_shift(loop->capt, pitch);
+#ifdef USE_SAMPLERATE
+ if (loop->use_samplerate) {
+ loop->src_data.src_ratio =
+ (double)1.0 /
+ (loop->play->pitch * loop->capt->pitch);
+ if (verbose > 2)
+ snd_output_printf(loop->output, "%s: Samplerate src_ratio update2: %.8f\n", loop->id, loop->src_data.src_ratio);
+ }
+#endif
+ }
+ else if (loop->sync == SYNC_TYPE_PLAYRATESHIFT) {
+ set_rate_shift(loop->play, pitch);
+#ifdef USE_SAMPLERATE
+ if (loop->use_samplerate) {
+ loop->src_data.src_ratio =
+ (double)1.0 /
+ (loop->play->pitch * loop->capt->pitch);
+ if (verbose > 2)
+ snd_output_printf(loop->output, "%s: Samplerate src_ratio update3: %.8f\n", loop->id, loop->src_data.src_ratio);
+ }
+#endif
+ }
+ if (verbose)
+ snd_output_printf(loop->output, "New pitch for %s: %.8f (min/max samples = %li/%li)\n", loop->id, pitch, loop->pitch_diff_min, loop->pitch_diff_max);
+}
+
+static int get_active(struct loopback_handle *lhandle)
+{
+ int err;
+
+ if (lhandle->ctl_active == NULL)
+ return 0;
+ err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_active);
+ if (err < 0) {
+ logit(LOG_CRIT, "Cannot get PCM Slave Active element for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ return snd_ctl_elem_value_get_boolean(lhandle->ctl_active, 0);
+}
+
+static int get_format(struct loopback_handle *lhandle)
+{
+ int err;
+
+ if (lhandle->ctl_format == NULL)
+ return 0;
+ err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_format);
+ if (err < 0) {
+ logit(LOG_CRIT, "Cannot get PCM Format element for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ return snd_ctl_elem_value_get_integer(lhandle->ctl_format, 0);
+}
+
+static int get_rate(struct loopback_handle *lhandle)
+{
+ int err;
+
+ if (lhandle->ctl_rate == NULL)
+ return 0;
+ err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_rate);
+ if (err < 0) {
+ logit(LOG_CRIT, "Cannot get PCM Rate element for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ return snd_ctl_elem_value_get_integer(lhandle->ctl_rate, 0);
+}
+
+static int get_channels(struct loopback_handle *lhandle)
+{
+ int err;
+
+ if (lhandle->ctl_channels == NULL)
+ return 0;
+ err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_channels);
+ if (err < 0) {
+ logit(LOG_CRIT, "Cannot get PCM Channels element for %s: %s\n", lhandle->id, snd_strerror(err));
+ return err;
+ }
+ return snd_ctl_elem_value_get_integer(lhandle->ctl_channels, 0);
+}
+
+static void openctl_elem(struct loopback_handle *lhandle,
+ int device, int subdevice,
+ const char *name,
+ snd_ctl_elem_value_t **elem)
+{
+ int err;
+
+ if (snd_ctl_elem_value_malloc(elem) < 0) {
+ *elem = NULL;
+ } else {
+ snd_ctl_elem_value_set_interface(*elem,
+ SND_CTL_ELEM_IFACE_PCM);
+ snd_ctl_elem_value_set_device(*elem, device);
+ snd_ctl_elem_value_set_subdevice(*elem, subdevice);
+ snd_ctl_elem_value_set_name(*elem, name);
+ err = snd_ctl_elem_read(lhandle->ctl, *elem);
+ if (err < 0) {
+ snd_ctl_elem_value_free(*elem);
+ *elem = NULL;
+ }
+ }
+}
+
+static int openctl(struct loopback_handle *lhandle, int device, int subdevice)
+{
+ int err;
+
+ lhandle->ctl_rate_shift = NULL;
+ if (lhandle->loopback->play == lhandle) {
+ if (lhandle->loopback->controls)
+ goto __events;
+ return 0;
+ }
+ openctl_elem(lhandle, device, subdevice, "PCM Notify",
+ &lhandle->ctl_notify);
+ openctl_elem(lhandle, device, subdevice, "PCM Rate Shift 100000",
+ &lhandle->ctl_rate_shift);
+ set_rate_shift(lhandle, 1);
+ openctl_elem(lhandle, device, subdevice, "PCM Slave Active",
+ &lhandle->ctl_active);
+ openctl_elem(lhandle, device, subdevice, "PCM Slave Format",
+ &lhandle->ctl_format);
+ openctl_elem(lhandle, device, subdevice, "PCM Slave Rate",
+ &lhandle->ctl_rate);
+ openctl_elem(lhandle, device, subdevice, "PCM Slave Channels",
+ &lhandle->ctl_channels);
+ if ((lhandle->ctl_active &&
+ lhandle->ctl_format &&
+ lhandle->ctl_rate &&
+ lhandle->ctl_channels) ||
+ lhandle->loopback->controls) {
+ __events:
+ if ((err = snd_ctl_poll_descriptors_count(lhandle->ctl)) < 0)
+ lhandle->ctl_pollfd_count = 0;
+ else
+ lhandle->ctl_pollfd_count = err;
+ if (snd_ctl_subscribe_events(lhandle->ctl, 1) < 0)
+ lhandle->ctl_pollfd_count = 0;
+ }
+ return 0;
+}
+
+static int openit(struct loopback_handle *lhandle)
+{
+ snd_pcm_info_t *info;
+ int stream = lhandle == lhandle->loopback->play ?
+ SND_PCM_STREAM_PLAYBACK :
+ SND_PCM_STREAM_CAPTURE;
+ int err, card, device, subdevice;
+ 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;
+ }
+ if ((err = snd_pcm_info_malloc(&info)) < 0)
+ return err;
+ if ((err = snd_pcm_info(lhandle->handle, info)) < 0) {
+ snd_pcm_info_free(info);
+ return err;
+ }
+ card = snd_pcm_info_get_card(info);
+ device = snd_pcm_info_get_device(info);
+ subdevice = snd_pcm_info_get_subdevice(info);
+ snd_pcm_info_free(info);
+ lhandle->card_number = card;
+ lhandle->ctl = NULL;
+ if (card >= 0 || lhandle->ctldev) {
+ char name[16], *dev = lhandle->ctldev;
+ if (dev == NULL) {
+ sprintf(name, "hw:%i", card);
+ dev = name;
+ }
+ pcm_open_lock();
+ err = snd_ctl_open(&lhandle->ctl, dev, SND_CTL_NONBLOCK);
+ pcm_open_unlock();
+ if (err < 0) {
+ logit(LOG_CRIT, "%s [%s] ctl open error: %s\n", lhandle->id, dev, snd_strerror(err));
+ lhandle->ctl = NULL;
+ }
+ if (lhandle->ctl)
+ openctl(lhandle, device, subdevice);
+ }
+ return 0;
+}
+
+static int freeit(struct loopback_handle *lhandle)
+{
+ free(lhandle->buf);
+ lhandle->buf = NULL;
+ return 0;
+}
+
+static int closeit(struct loopback_handle *lhandle)
+{
+ int err = 0;
+
+ set_rate_shift(lhandle, 1);
+ if (lhandle->ctl_rate_shift)
+ snd_ctl_elem_value_free(lhandle->ctl_rate_shift);
+ lhandle->ctl_rate_shift = NULL;
+ if (lhandle->ctl)
+ err = snd_ctl_close(lhandle->ctl);
+ lhandle->ctl = NULL;
+ if (lhandle->handle)
+ err = snd_pcm_close(lhandle->handle);
+ lhandle->handle = NULL;
+ return err;
+}
+
+static int init_handle(struct loopback_handle *lhandle, int alloc)
+{
+ snd_pcm_uframes_t lat;
+ lhandle->frame_size = (snd_pcm_format_width(lhandle->format) / 8) *
+ lhandle->channels;
+ lhandle->sync_point = lhandle->rate * 15; /* every 15 seconds */
+ lat = lhandle->loopback->latency;
+ if (lhandle->buffer_size > lat)
+ lat = lhandle->buffer_size;
+ lhandle->buf_size = lat * 2;
+ if (alloc) {
+ lhandle->buf = calloc(1, lhandle->buf_size * lhandle->frame_size);
+ if (lhandle->buf == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+int pcmjob_init(struct loopback *loop)
+{
+ int err;
+ char id[128];
+
+#ifdef FILE_CWRITE
+ loop->cfile = fopen(FILE_CWRITE, "w+");
+#endif
+#ifdef FILE_PWRITE
+ loop->pfile = fopen(FILE_PWRITE, "w+");
+#endif
+ if ((err = openit(loop->play)) < 0)
+ goto __error;
+ if ((err = openit(loop->capt)) < 0)
+ goto __error;
+ snprintf(id, sizeof(id), "%s/%s", loop->play->id, loop->capt->id);
+ id[sizeof(id)-1] = '\0';
+ loop->id = strdup(id);
+ if (loop->sync == SYNC_TYPE_AUTO && loop->capt->ctl_rate_shift)
+ loop->sync = SYNC_TYPE_CAPTRATESHIFT;
+ if (loop->sync == SYNC_TYPE_AUTO && loop->play->ctl_rate_shift)
+ loop->sync = SYNC_TYPE_PLAYRATESHIFT;
+#ifdef USE_SAMPLERATE
+ if (loop->sync == SYNC_TYPE_AUTO && loop->src_enable)
+ loop->sync = SYNC_TYPE_SAMPLERATE;
+#endif
+ if (loop->sync == SYNC_TYPE_AUTO)
+ loop->sync = SYNC_TYPE_SIMPLE;
+ if (loop->slave == SLAVE_TYPE_AUTO &&
+ loop->capt->ctl_notify &&
+ loop->capt->ctl_active &&
+ loop->capt->ctl_format &&
+ loop->capt->ctl_rate &&
+ loop->capt->ctl_channels)
+ loop->slave = SLAVE_TYPE_ON;
+ if (loop->slave == SLAVE_TYPE_ON) {
+ err = set_notify(loop->capt, 1);
+ if (err < 0)
+ goto __error;
+ if (loop->capt->ctl_notify == NULL ||
+ snd_ctl_elem_value_get_boolean(loop->capt->ctl_notify, 0) == 0) {
+ logit(LOG_CRIT, "unable to enable slave mode for %s\n", loop->id);
+ err = -EINVAL;
+ goto __error;
+ }
+ }
+ err = control_init(loop);
+ if (err < 0)
+ goto __error;
+ return 0;
+ __error:
+ pcmjob_done(loop);
+ return err;
+}
+
+static void freeloop(struct loopback *loop)
+{
+#ifdef USE_SAMPLERATE
+ if (loop->use_samplerate) {
+ if (loop->src_state)
+ src_delete(loop->src_state);
+ loop->src_state = NULL;
+ free(loop->src_data.data_in);
+ loop->src_data.data_in = NULL;
+ free(loop->src_data.data_out);
+ loop->src_data.data_out = NULL;
+ }
+#endif
+ if (loop->play->buf == loop->capt->buf)
+ loop->play->buf = NULL;
+ freeit(loop->play);
+ freeit(loop->capt);
+}
+
+int pcmjob_done(struct loopback *loop)
+{
+ control_done(loop);
+ closeit(loop->play);
+ closeit(loop->capt);
+ freeloop(loop);
+ free(loop->id);
+ loop->id = NULL;
+#ifdef FILE_PWRITE
+ if (loop->pfile) {
+ fclose(loop->pfile);
+ loop->pfile = NULL;
+ }
+#endif
+#ifdef FILE_CWRITE
+ if (loop->cfile) {
+ fclose(loop->cfile);
+ loop->cfile = NULL;
+ }
+#endif
+ return 0;
+}
+
+static void lhandle_start(struct loopback_handle *lhandle)
+{
+ lhandle->buf_pos = 0;
+ lhandle->buf_count = 0;
+ lhandle->counter = 0;
+ lhandle->total_queued = 0;
+}
+
+int pcmjob_start(struct loopback *loop)
+{
+ snd_pcm_uframes_t count;
+ int err;
+
+ loop->pollfd_count = loop->play->ctl_pollfd_count +
+ loop->capt->ctl_pollfd_count;
+ if ((err = snd_pcm_poll_descriptors_count(loop->play->handle)) < 0)
+ goto __error;
+ loop->play->pollfd_count = err;
+ loop->pollfd_count += err;
+ if ((err = snd_pcm_poll_descriptors_count(loop->capt->handle)) < 0)
+ goto __error;
+ loop->capt->pollfd_count = err;
+ loop->pollfd_count += err;
+ if (loop->slave == SLAVE_TYPE_ON) {
+ err = get_active(loop->capt);
+ if (err < 0)
+ goto __error;
+ if (err == 0) /* stream is not active */
+ return 0;
+ err = get_format(loop->capt);
+ if (err < 0)
+ goto __error;
+ loop->play->format = loop->capt->format = err;
+ err = get_rate(loop->capt);
+ if (err < 0)
+ goto __error;
+ loop->play->rate_req = loop->capt->rate_req = err;
+ err = get_channels(loop->capt);
+ if (err < 0)
+ goto __error;
+ loop->play->channels = loop->capt->channels = err;
+ }
+ loop->reinit = 0;
+ loop->use_samplerate = 0;
+ if (loop->latency_req) {
+ loop->latency_reqtime = frames_to_time(loop->play->rate_req,
+ loop->latency_req);
+ loop->latency_req = 0;
+ }
+ loop->latency = time_to_frames(loop->play->rate_req, loop->latency_reqtime);
+ if ((err = setparams(loop, loop->latency/2)) < 0)
+ goto __error;
+ if (verbose)
+ showlatency(loop->output, loop->latency, loop->play->rate_req, "Latency");
+ if (loop->play->access == loop->capt->access &&
+ loop->play->format == loop->capt->format &&
+ loop->play->rate == loop->capt->rate &&
+ loop->play->channels == loop->play->channels &&
+ loop->sync != SYNC_TYPE_SAMPLERATE) {
+ if (verbose > 1)
+ snd_output_printf(loop->output, "shared buffer!!!\n");
+ if ((err = init_handle(loop->play, 1)) < 0)
+ goto __error;
+ if ((err = init_handle(loop->capt, 0)) < 0)
+ goto __error;
+ if (loop->play->buf_size < loop->capt->buf_size) {
+ char *nbuf = realloc(loop->play->buf,
+ loop->capt->buf_size *
+ loop->capt->frame_size);
+ if (nbuf == NULL) {
+ err = -ENOMEM;
+ 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 {
+ if ((err = init_handle(loop->play, 1)) < 0)
+ goto __error;
+ if ((err = init_handle(loop->capt, 1)) < 0)
+ goto __error;
+ if (loop->play->rate_req != loop->play->rate)
+ loop->use_samplerate = 1;
+ if (loop->capt->rate_req != loop->capt->rate)
+ loop->use_samplerate = 1;
+ }
+#ifdef USE_SAMPLERATE
+ if (loop->sync == SYNC_TYPE_SAMPLERATE)
+ loop->use_samplerate = 1;
+ if (loop->use_samplerate && !loop->src_enable) {
+ logit(LOG_CRIT, "samplerate conversion required but disabled\n");
+ loop->use_samplerate = 0;
+ err = -EIO;
+ goto __error;
+ }
+ if (loop->use_samplerate) {
+ if ((loop->capt->format != SND_PCM_FORMAT_S16 ||
+ loop->play->format != SND_PCM_FORMAT_S16) &&
+ (loop->capt->format != SND_PCM_FORMAT_S32 ||
+ loop->play->format != SND_PCM_FORMAT_S32)) {
+ logit(LOG_CRIT, "samplerate conversion supports only %s or %s formats (play=%s, capt=%s)\n", snd_pcm_format_name(SND_PCM_FORMAT_S16), snd_pcm_format_name(SND_PCM_FORMAT_S32), snd_pcm_format_name(loop->play->format), snd_pcm_format_name(loop->capt->format));
+ loop->use_samplerate = 0;
+ err = -EIO;
+ goto __error;
+ }
+ loop->src_state = src_new(loop->src_converter_type,
+ loop->play->channels, &err);
+ loop->src_data.data_in = calloc(1, sizeof(float)*loop->capt->channels*loop->capt->buf_size);
+ if (loop->src_data.data_in == NULL) {
+ err = -ENOMEM;
+ goto __error;
+ }
+ loop->src_data.data_out = calloc(1, sizeof(float)*loop->play->channels*loop->play->buf_size);
+ if (loop->src_data.data_out == NULL) {
+ err = -ENOMEM;
+ goto __error;
+ }
+ loop->src_data.src_ratio = (double)loop->play->rate /
+ (double)loop->capt->rate;
+ loop->src_data.end_of_input = 0;
+ loop->src_out_frames = 0;
+ } else {
+ loop->src_state = NULL;
+ }
+#else
+ if (loop->sync == SYNC_TYPE_SAMPLERATE || loop->use_samplerate) {
+ logit(LOG_CRIT, "alsaloop is compiled without libsamplerate support\n");
+ err = -EIO;
+ goto __error;
+ }
+#endif
+ if (verbose) {
+ snd_output_printf(loop->output, "%s sync type: %s", loop->id, sync_types[loop->sync]);
+#ifdef USE_SAMPLERATE
+ if (loop->sync == SYNC_TYPE_SAMPLERATE)
+ snd_output_printf(loop->output, " (%s)", src_types[loop->src_converter_type]);
+#endif
+ snd_output_printf(loop->output, "\n");
+ }
+ lhandle_start(loop->play);
+ lhandle_start(loop->capt);
+ if ((err = snd_pcm_format_set_silence(loop->play->format,
+ loop->play->buf,
+ loop->play->buf_size * loop->play->channels)) < 0) {
+ 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);
+ loop->total_queued_count = 0;
+ loop->pitch_diff = 0;
+ count = get_whole_latency(loop) / loop->play->pitch;
+ loop->play->buf_count = count;
+ if (loop->play->buf == loop->capt->buf)
+ loop->capt->buf_pos = count;
+ 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;
+ }
+ loop->running = 1;
+ loop->stop_pending = 0;
+ if (loop->xrun) {
+ getcurtimestamp(&loop->xrun_last_update);
+ loop->xrun_last_pdelay = XRUN_PROFILE_UNKNOWN;
+ loop->xrun_last_cdelay = XRUN_PROFILE_UNKNOWN;
+ loop->xrun_max_proctime = 0;
+ }
+ if ((err = snd_pcm_start(loop->capt->handle)) < 0) {
+ logit(LOG_CRIT, "pcm start %s error: %s\n", loop->capt->id, snd_strerror(err));
+ goto __error;
+ }
+ if (!loop->linked) {
+ if ((err = snd_pcm_start(loop->play->handle)) < 0) {
+ logit(LOG_CRIT, "pcm start %s error: %s\n", loop->play->id, snd_strerror(err));
+ goto __error;
+ }
+ }
+ return 0;
+ __error:
+ pcmjob_stop(loop);
+ return err;
+}
+
+int pcmjob_stop(struct loopback *loop)
+{
+ int err;
+
+ if (loop->running) {
+ if ((err = snd_pcm_drop(loop->capt->handle)) < 0)
+ logit(LOG_WARNING, "pcm drop %s error: %s\n", loop->capt->id, snd_strerror(err));
+ if ((err = snd_pcm_drop(loop->play->handle)) < 0)
+ logit(LOG_WARNING, "pcm drop %s error: %s\n", loop->play->id, snd_strerror(err));
+ if ((err = snd_pcm_hw_free(loop->capt->handle)) < 0)
+ logit(LOG_WARNING, "pcm hw_free %s error: %s\n", loop->capt->id, snd_strerror(err));
+ if ((err = snd_pcm_hw_free(loop->play->handle)) < 0)
+ logit(LOG_WARNING, "pcm hw_free %s error: %s\n", loop->play->id, snd_strerror(err));
+ loop->running = 0;
+ }
+ freeloop(loop);
+ return 0;
+}
+
+int pcmjob_pollfds_init(struct loopback *loop, struct pollfd *fds)
+{
+ int err, idx = 0;
+
+ if (loop->running) {
+ err = snd_pcm_poll_descriptors(loop->play->handle, fds + idx, loop->play->pollfd_count);
+ if (err < 0)
+ return err;
+ idx += loop->play->pollfd_count;
+ err = snd_pcm_poll_descriptors(loop->capt->handle, fds + idx, loop->capt->pollfd_count);
+ if (err < 0)
+ return err;
+ idx += loop->capt->pollfd_count;
+ }
+ if (loop->play->ctl_pollfd_count > 0 &&
+ (loop->slave == SLAVE_TYPE_ON || loop->controls)) {
+ err = snd_ctl_poll_descriptors(loop->play->ctl, fds + idx, loop->play->ctl_pollfd_count);
+ if (err < 0)
+ return err;
+ idx += loop->play->ctl_pollfd_count;
+ }
+ if (loop->capt->ctl_pollfd_count > 0 &&
+ (loop->slave == SLAVE_TYPE_ON || loop->controls)) {
+ err = snd_ctl_poll_descriptors(loop->capt->ctl, fds + idx, loop->capt->ctl_pollfd_count);
+ if (err < 0)
+ return err;
+ idx += loop->capt->ctl_pollfd_count;
+ }
+ loop->active_pollfd_count = idx;
+ return idx;
+}
+
+static snd_pcm_sframes_t get_queued_playback_samples(struct loopback *loop)
+{
+ snd_pcm_sframes_t delay;
+ int err;
+
+ if ((err = snd_pcm_delay(loop->play->handle, &delay)) < 0)
+ return 0;
+ loop->play->last_delay = delay;
+ delay += loop->play->buf_count;
+#ifdef USE_SAMPLERATE
+ delay += loop->src_out_frames;
+#endif
+ return delay;
+}
+
+static snd_pcm_sframes_t get_queued_capture_samples(struct loopback *loop)
+{
+ snd_pcm_sframes_t delay;
+ int err;
+
+ if ((err = snd_pcm_delay(loop->capt->handle, &delay)) < 0)
+ return 0;
+ loop->capt->last_delay = delay;
+ delay += loop->capt->buf_count;
+ return delay;
+}
+
+static int ctl_event_check(snd_ctl_elem_value_t *val, snd_ctl_event_t *ev)
+{
+ snd_ctl_elem_id_t *id1, *id2;
+ snd_ctl_elem_id_alloca(&id1);
+ snd_ctl_elem_id_alloca(&id2);
+ snd_ctl_elem_value_get_id(val, id1);
+ snd_ctl_event_elem_get_id(ev, id2);
+ if (snd_ctl_event_elem_get_mask(ev) == SND_CTL_EVENT_MASK_REMOVE)
+ return 0;
+ if ((snd_ctl_event_elem_get_mask(ev) & SND_CTL_EVENT_MASK_VALUE) == 0)
+ return 0;
+ return control_id_match(id1, id2);
+}
+
+static int handle_ctl_events(struct loopback_handle *lhandle,
+ unsigned short events)
+{
+ struct loopback *loop = lhandle->loopback;
+ snd_ctl_event_t *ev;
+ int err, restart = 0;
+
+ snd_ctl_event_alloca(&ev);
+ while ((err = snd_ctl_read(lhandle->ctl, ev)) != 0 && err != -EAGAIN) {
+ if (err < 0)
+ break;
+ if (snd_ctl_event_get_type(ev) != SND_CTL_EVENT_ELEM)
+ continue;
+ if (lhandle == loop->play)
+ goto __ctl_check;
+ if (verbose > 6)
+ snd_output_printf(loop->output, "%s: ctl event!!!! %s\n", lhandle->id, snd_ctl_event_elem_get_name(ev));
+ if (ctl_event_check(lhandle->ctl_active, ev)) {
+ continue;
+ } else if (ctl_event_check(lhandle->ctl_format, ev)) {
+ err = get_format(lhandle);
+ if (lhandle->format != err)
+ restart = 1;
+ continue;
+ } else if (ctl_event_check(lhandle->ctl_rate, ev)) {
+ err = get_rate(lhandle);
+ if (lhandle->rate != err)
+ restart = 1;
+ continue;
+ } else if (ctl_event_check(lhandle->ctl_channels, ev)) {
+ err = get_channels(lhandle);
+ if (lhandle->channels != err)
+ restart = 1;
+ continue;
+ }
+ __ctl_check:
+ control_event(lhandle, ev);
+ }
+ err = get_active(lhandle);
+ if (verbose > 7)
+ snd_output_printf(loop->output, "%s: ctl event active %i\n", lhandle->id, err);
+ if (!err) {
+ if (lhandle->loopback->running) {
+ loop->stop_pending = 1;
+ loop->stop_count = 0;
+ }
+ } else {
+ loop->stop_pending = 0;
+ if (loop->running == 0)
+ restart = 1;
+ }
+ if (restart) {
+ pcmjob_stop(loop);
+ err = pcmjob_start(loop);
+ if (err < 0)
+ return err;
+ }
+ return 1;
+}
+
+int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
+{
+ struct loopback_handle *play = loop->play;
+ struct loopback_handle *capt = loop->capt;
+ unsigned short prevents, crevents, events;
+ snd_pcm_uframes_t ccount, pcount;
+ int err, loopcount = 10, idx;
+
+ if (verbose > 11)
+ snd_output_printf(loop->output, "%s: pollfds handle\n", loop->id);
+ if (verbose > 13 || loop->xrun)
+ getcurtimestamp(&loop->tstamp_start);
+ 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 / %li / %li\n", play->id, snd_strerror(err), play->buf_size, play->buf_count);
+ else
+ 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 / %li / %li\n", capt->id, snd_strerror(err), capt->buf_size, capt->buf_count);
+ else
+ 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) {
+ err = snd_pcm_poll_descriptors_revents(play->handle, fds,
+ play->pollfd_count,
+ &prevents);
+ if (err < 0)
+ return err;
+ idx += play->pollfd_count;
+ err = snd_pcm_poll_descriptors_revents(capt->handle, fds + idx,
+ capt->pollfd_count,
+ &crevents);
+ if (err < 0)
+ return err;
+ idx += capt->pollfd_count;
+ if (loop->xrun) {
+ if (prevents || crevents) {
+ loop->xrun_last_wake = loop->xrun_last_wake0;
+ loop->xrun_last_wake0 = loop->tstamp_start;
+ }
+ loop->xrun_last_check = loop->xrun_last_check0;
+ loop->xrun_last_check0 = loop->tstamp_start;
+ }
+ } else {
+ prevents = crevents = 0;
+ }
+ if (play->ctl_pollfd_count > 0 &&
+ (loop->slave == SLAVE_TYPE_ON || loop->controls)) {
+ err = snd_ctl_poll_descriptors_revents(play->ctl, fds + idx,
+ play->ctl_pollfd_count,
+ &events);
+ if (err < 0)
+ return err;
+ if (events) {
+ err = handle_ctl_events(play, events);
+ if (err == 1)
+ return 0;
+ if (err < 0)
+ return err;
+ }
+ idx += play->ctl_pollfd_count;
+ }
+ if (capt->ctl_pollfd_count > 0 &&
+ (loop->slave == SLAVE_TYPE_ON || loop->controls)) {
+ err = snd_ctl_poll_descriptors_revents(capt->ctl, fds + idx,
+ capt->ctl_pollfd_count,
+ &events);
+ if (err < 0)
+ return err;
+ if (events) {
+ err = handle_ctl_events(capt, events);
+ if (err == 1)
+ return 0;
+ if (err < 0)
+ return err;
+ }
+ idx += capt->ctl_pollfd_count;
+ }
+ if (verbose > 9)
+ snd_output_printf(loop->output, "%s: prevents = 0x%x, crevents = 0x%x\n", loop->id, prevents, crevents);
+ if (!loop->running)
+ goto __pcm_end;
+ do {
+ ccount = readit(capt);
+ buf_add(loop, ccount);
+ if (capt->xrun_pending || loop->reinit)
+ break;
+ /* we read new samples, if we have a room in the playback
+ buffer, feed them there */
+ pcount = writeit(play);
+ buf_remove(loop, pcount);
+ if (play->xrun_pending || loop->reinit)
+ break;
+ loopcount--;
+ } while ((ccount > 0 || pcount > 0) && loopcount > 0);
+ if (play->xrun_pending || capt->xrun_pending) {
+ if ((err = xrun_sync(loop)) < 0)
+ return err;
+ }
+ if (loop->reinit) {
+ err = pcmjob_stop(loop);
+ if (err < 0)
+ return err;
+ err = pcmjob_start(loop);
+ if (err < 0)
+ return err;
+ }
+ if (loop->sync != SYNC_TYPE_NONE &&
+ play->counter >= play->sync_point &&
+ capt->counter >= play->sync_point) {
+ snd_pcm_sframes_t diff, lat = get_whole_latency(loop);
+ diff = ((double)(((double)play->total_queued * play->pitch) +
+ ((double)capt->total_queued * capt->pitch)) /
+ (double)loop->total_queued_count) - lat;
+ /* FIXME: this algorithm may be slightly better */
+ if (verbose > 3)
+ snd_output_printf(loop->output, "%s: sync diff %li old diff %li\n", loop->id, diff, loop->pitch_diff);
+ if (diff > 0) {
+ if (diff == loop->pitch_diff)
+ loop->pitch += loop->pitch_delta;
+ else if (diff > loop->pitch_diff)
+ loop->pitch += loop->pitch_delta*2;
+ } else if (diff < 0) {
+ if (diff == loop->pitch_diff)
+ loop->pitch -= loop->pitch_delta;
+ else if (diff < loop->pitch_diff)
+ loop->pitch -= loop->pitch_delta*2;
+ }
+ loop->pitch_diff = diff;
+ if (loop->pitch_diff_min > diff)
+ loop->pitch_diff_min = diff;
+ if (loop->pitch_diff_max < diff)
+ loop->pitch_diff_max = diff;
+ update_pitch(loop);
+ play->counter -= play->sync_point;
+ capt->counter -= play->sync_point;
+ play->total_queued = 0;
+ capt->total_queued = 0;
+ loop->total_queued_count = 0;
+ }
+ if (loop->sync != SYNC_TYPE_NONE) {
+ snd_pcm_sframes_t pqueued, cqueued;
+ pqueued = get_queued_playback_samples(loop);
+ cqueued = get_queued_capture_samples(loop);
+ if (verbose > 4)
+ snd_output_printf(loop->output, "%s: queued %li/%li samples\n", loop->id, pqueued, cqueued);
+ if (pqueued > 0)
+ play->total_queued += pqueued;
+ if (cqueued > 0)
+ 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 / %li / %li\n", play->id, snd_strerror(err), play->buf_size, play->buf_count);
+ else
+ 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 / %li / %li\n", capt->id, snd_strerror(err), capt->buf_size, capt->buf_count);
+ else
+ 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 || loop->xrun) {
+ long diff;
+ getcurtimestamp(&loop->tstamp_end);
+ diff = timediff(loop->tstamp_end, loop->tstamp_start);
+ if (verbose > 13)
+ snd_output_printf(loop->output, "%s: processing time %lius\n", loop->id, diff);
+ if (loop->xrun && loop->xrun_max_proctime < diff)
+ loop->xrun_max_proctime = diff;
+ }
+ return 0;
+}
+
+#define OUT(args...) \
+ snd_output_printf(loop->state, ##args)
+
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void show_handle(struct loopback_handle *lhandle, const char *id)
+{
+ struct loopback *loop = lhandle->loopback;
+
+ OUT(" %s: %s:\n", id, lhandle->id);
+ OUT(" device = '%s', ctldev '%s'\n", lhandle->device, lhandle->ctldev);
+ OUT(" card_number = %i\n", lhandle->card_number);
+ if (!loop->running)
+ return;
+ OUT(" access = %s, format = %s, rate = %u, channels = %u\n", snd_pcm_access_name(lhandle->access), snd_pcm_format_name(lhandle->format), lhandle->rate, lhandle->channels);
+ OUT(" buffer_size = %u, period_size = %u, avail_min = %li\n", lhandle->buffer_size, lhandle->period_size, lhandle->avail_min);
+ OUT(" xrun_pending = %i\n", lhandle->xrun_pending);
+ OUT(" buf_size = %li, buf_pos = %li, buf_count = %li, buf_over = %li\n", lhandle->buf_size, lhandle->buf_pos, lhandle->buf_count, lhandle->buf_over);
+ OUT(" pitch = %.8f\n", lhandle->pitch);
+}
+
+void pcmjob_state(struct loopback *loop)
+{
+ pthread_t self = pthread_self();
+ pthread_mutex_lock(&state_mutex);
+ OUT("State dump for thread %p job %i: %s:\n", (void *)self, loop->thread, loop->id);
+ OUT(" running = %i\n", loop->running);
+ OUT(" sync = %i\n", loop->sync);
+ OUT(" slave = %i\n", loop->slave);
+ if (!loop->running)
+ goto __skip;
+ OUT(" pollfd_count = %i\n", loop->pollfd_count);
+ OUT(" pitch = %.8f, delta = %.8f, diff = %li, min = %li, max = %li\n", loop->pitch, loop->pitch_delta, loop->pitch_diff, loop->pitch_diff_min, loop->pitch_diff_max);
+ OUT(" use_samplerate = %i\n", loop->use_samplerate);
+ __skip:
+ show_handle(loop->play, "playback");
+ show_handle(loop->capt, "capture");
+ pthread_mutex_unlock(&state_mutex);
+}
diff --git a/alsaloop/test.sh b/alsaloop/test.sh
new file mode 100755
index 0000000..fac72b9
--- /dev/null
+++ b/alsaloop/test.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+#DBG="gdb --args "
+#DBG="strace"
+#DBG="valgrind --leak-check=full"
+ARGS=
+CFGFILE="/tmp/alsaloop.test.cfg"
+
+test1() {
+ echo "TEST1"
+ $DBG ./alsaloop -C hw:1,0 -P plughw:0,0 \
+ --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'" \
+ --ossmixer "Master@VOLUME" \
+ --ossmixer "PCM@PCM" \
+ $ARGS
+}
+
+test2() {
+ echo "TEST2"
+cat > $CFGFILE <<EOF
+# first job
+-C hw:1,0,0 -P hw:0,0,0 --tlatency 50000 --thread 1 \
+ --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
+ --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
+ --mixer "name='PCM Playback Volume'"
+# next line - second job
+-C hw:1,0,1 -P hw:0,1,0 --tlatency 50000 --thread 2
+EOF
+ $DBG ./alsaloop -d --config $CFGFILE $ARGS
+}
+
+test3() {
+ echo "TEST3"
+ LATENCY=180000
+cat > $CFGFILE <<EOF
+-C hw:1,0,0 -P plug:dmix:0 --tlatency $LATENCY --thread 0 \
+ --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
+ --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
+ --mixer "name='PCM Playback Volume'" \
+ --ossmixer "name=Master@VOLUME"
+-C hw:1,0,1 -P plug:dmix:0 --tlatency $LATENCY --thread 1
+-C hw:1,0,2 -P plug:dmix:0 --tlatency $LATENCY --thread 2
+-C hw:1,0,3 -P plug:dmix:0 --tlatency $LATENCY --thread 3
+-C hw:1,0,4 -P plug:dmix:0 --tlatency $LATENCY --thread 4
+-C hw:1,0,5 -P plug:dmix:0 --tlatency $LATENCY --thread 5
+-C hw:1,0,6 -P plug:dmix:0 --tlatency $LATENCY --thread 6
+-C hw:1,0,7 -P plug:dmix:0 --tlatency $LATENCY --thread 7
+EOF
+ $DBG ./alsaloop --config $CFGFILE $ARGS
+}
+
+test4() {
+ echo "TEST4"
+ $DBG ./alsaloop -C hw:1,0 -P plughw:0,0 -a off -r 11025 \
+ --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'" \
+ $ARGS
+}
+
+test5() {
+ echo "TEST5"
+cat > $CFGFILE <<EOF
+-C hw:1,0,0 -P plughw:0,0 --tlatency 50000 --thread 1 \
+ --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
+ --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
+ --mixer "name='PCM Playback Volume'" \
+ --ossmixer "name=Master@VOLUME"
+-C hw:1,0,1 -P plughw:0,1 --tlatency 50000 --thread 2
+EOF
+ $DBG ./alsaloop --config $CFGFILE $ARGS
+}
+
+sigusr1() {
+ pid=$(ps ax | grep alsaloop | grep -v grep | colrm 7 255)
+ if test -n "$pid"; then
+ echo "Killing alsaloop $pid..."
+ kill -SIGUSR1 $pid
+ fi
+}
+
+case "$1" in
+test1) shift; ARGS="$@"; test1 ;;
+test2) shift; ARGS="$@"; test2 ;;
+test3) shift; ARGS="$@"; test3 ;;
+test4) shift; ARGS="$@"; test4 ;;
+test5) shift; ARGS="$@"; test5 ;;
+usr|sig*) sigusr1 ;;
+*) ARGS="$@"; test1 ;;
+esac
diff --git a/alsamixer/Makefile.am b/alsamixer/Makefile.am
index 1de47c6..8a82323 100644
--- a/alsamixer/Makefile.am
+++ b/alsamixer/Makefile.am
@@ -15,6 +15,7 @@
proc_files.c proc_files.h \
textbox.c textbox.h \
utils.c utils.h \
+ volume_mapping.c volume_mapping.h \
widget.c widget.h
man_MANS = alsamixer.1
EXTRA_DIST = alsamixer.1
diff --git a/alsamixer/alsamixer.1 b/alsamixer/alsamixer.1
index ba05aca..cd88404 100644
--- a/alsamixer/alsamixer.1
+++ b/alsamixer/alsamixer.1
@@ -171,7 +171,7 @@
.SH AUTHOR
.B alsamixer
-has been written by Tim Janik <timj@gtk.org> and
+has been written by Tim Janik and
been further improved by Jaroslav Kysela <perex@perex.cz>
and Clemens Ladisch <clemens@ladisch.de>.
diff --git a/alsamixer/cli.c b/alsamixer/cli.c
index ab6255f..3898196 100644
--- a/alsamixer/cli.c
+++ b/alsamixer/cli.c
@@ -1,6 +1,6 @@
/*
* alsamixer - curses mixer for the ALSA project
- * Copyright (c) 1998,1999 Tim Janik <timj@gtk.org>
+ * Copyright (c) 1998,1999 Tim Janik
* Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
*
diff --git a/alsamixer/colors.c b/alsamixer/colors.c
index fcceb16..b4b98e5 100644
--- a/alsamixer/colors.c
+++ b/alsamixer/colors.c
@@ -1,6 +1,6 @@
/*
* colors.c - color and attribute definitions
- * Copyright (c) 1998,1999 Tim Janik <timj@gtk.org>
+ * Copyright (c) 1998,1999 Tim Janik
* Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
*
diff --git a/alsamixer/mixer_controls.c b/alsamixer/mixer_controls.c
index 796df7b..cc98b64 100644
--- a/alsamixer/mixer_controls.c
+++ b/alsamixer/mixer_controls.c
@@ -1,6 +1,6 @@
/*
* mixer_controls.c - handles mixer controls and mapping from selems
- * Copyright (c) 1998,1999 Tim Janik <timj@gtk.org>
+ * Copyright (c) 1998,1999 Tim Janik
* Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
*
diff --git a/alsamixer/mixer_display.c b/alsamixer/mixer_display.c
index 20d6d6a..51a1546 100644
--- a/alsamixer/mixer_display.c
+++ b/alsamixer/mixer_display.c
@@ -17,10 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define _C99_SOURCE /* lrint() */
#include "aconfig.h"
#include <stdlib.h>
#include <string.h>
#include <strings.h>
+#include <math.h>
#include CURSESINC
#include <alsa/asoundlib.h>
#include "gettext_curses.h"
@@ -28,6 +30,7 @@
#include "mem.h"
#include "colors.h"
#include "widget.h"
+#include "volume_mapping.h"
#include "mixer_widget.h"
#include "mixer_controls.h"
#include "mixer_display.h"
@@ -390,24 +393,14 @@
display_string_in_field(y, x, s, width, ALIGN_CENTER);
}
-static long clamp(long value, long min, long max)
-{
- if (value < min)
- return min;
- if (value > max)
- return max;
- return value;
-}
-
static void display_control(unsigned int control_index)
{
struct control *control;
int col;
int i, c;
int left, frame_left;
- int bar_height, value;
- long volumes[2];
- long min, max;
+ int bar_height;
+ double volumes[2];
int switches[2];
unsigned int index;
const char *s;
@@ -452,35 +445,22 @@
waddch(mixer_widget.window, ACS_LRCORNER);
}
if (control->flags & (TYPE_PVOLUME | TYPE_CVOLUME)) {
- int (*get_vol_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *);
+ double (*get_vol_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t);
if (control->flags & TYPE_PVOLUME)
- get_vol_func = snd_mixer_selem_get_playback_volume;
+ get_vol_func = get_normalized_playback_volume;
else
- get_vol_func = snd_mixer_selem_get_capture_volume;
- err = get_vol_func(control->elem, control->volume_channels[0], &volumes[0]);
- if (err >= 0 && (control->flags & HAS_VOLUME_1))
- err = get_vol_func(control->elem, control->volume_channels[1], &volumes[1]);
+ get_vol_func = get_normalized_capture_volume;
+ volumes[0] = get_vol_func(control->elem, control->volume_channels[0]);
+ if (control->flags & HAS_VOLUME_1)
+ volumes[1] = get_vol_func(control->elem, control->volume_channels[1]);
else
volumes[1] = volumes[0];
- if (err < 0)
- return;
- if (control->flags & TYPE_PVOLUME)
- err = snd_mixer_selem_get_playback_volume_range(control->elem, &min, &max);
- else
- err = snd_mixer_selem_get_capture_volume_range(control->elem, &min, &max);
- if (err < 0)
- return;
- if (min >= max)
- max = min + 1;
- volumes[0] = clamp(volumes[0], min, max);
- volumes[1] = clamp(volumes[1], min, max);
if (control->flags & IS_ACTIVE)
wattrset(mixer_widget.window, 0);
for (c = 0; c < 2; c++) {
- bar_height = ((volumes[c] - min) * volume_height +
- max - min - 1) / (max - min);
+ bar_height = lrint(volumes[c] * volume_height);
for (i = 0; i < volume_height; ++i) {
chtype ch;
if (i + 1 > bar_height)
@@ -505,19 +485,18 @@
}
if (control->flags & IS_ACTIVE)
wattrset(mixer_widget.window, attr_mixer_active);
- value = ((volumes[0] - min) * 100 + (max - min) / 2) / (max - min);
if (!(control->flags & HAS_VOLUME_1)) {
- sprintf(buf, "%d", value);
+ sprintf(buf, "%d", lrint(volumes[0] * 100));
display_string_in_field(values_y, frame_left - 2, buf, 8, ALIGN_CENTER);
} else {
- mvwprintw(mixer_widget.window, values_y, frame_left - 2, "%3d", value);
+ mvwprintw(mixer_widget.window, values_y, frame_left - 2,
+ "%3d", lrint(volumes[0] * 100));
if (control->flags & IS_ACTIVE)
wattrset(mixer_widget.window, attr_ctl_frame);
waddstr(mixer_widget.window, "<>");
if (control->flags & IS_ACTIVE)
wattrset(mixer_widget.window, attr_mixer_active);
- value = ((volumes[1] - min) * 100 + (max - min) / 2) / (max - min);
- wprintw(mixer_widget.window, "%-3d", value);
+ wprintw(mixer_widget.window, "%-3d", lrint(volumes[1] * 100));
}
}
diff --git a/alsamixer/mixer_widget.c b/alsamixer/mixer_widget.c
index 796ea1d..fb352d3 100644
--- a/alsamixer/mixer_widget.c
+++ b/alsamixer/mixer_widget.c
@@ -1,6 +1,6 @@
/*
* mixer_widget.c - mixer widget and keys handling
- * Copyright (c) 1998,1999 Tim Janik <timj@gtk.org>
+ * Copyright (c) 1998,1999 Tim Janik
* Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
*
@@ -33,6 +33,7 @@
#include "textbox.h"
#include "proc_files.h"
#include "card_select.h"
+#include "volume_mapping.h"
#include "mixer_controls.h"
#include "mixer_display.h"
#include "mixer_widget.h"
@@ -211,7 +212,7 @@
_("; ' Toggle left/right capture"),
"",
_("Authors:"),
- _(" Tim Janik <timj@gtk.org>"),
+ _(" Tim Janik"),
_(" Jaroslav Kysela <perex@perex.cz>"),
_(" Clemens Ladisch <clemens@ladisch.de>"),
};
@@ -295,80 +296,57 @@
static void change_volume_to_percent(struct control *control, int value, unsigned int channels)
{
- int (*get_range_func)(snd_mixer_elem_t *, long *, long *);
- int (*set_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long);
- long min, max;
- int err;
+ int (*set_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, double, int);
if (!(control->flags & HAS_VOLUME_1))
channels = LEFT;
- if (control->flags & TYPE_PVOLUME) {
- get_range_func = snd_mixer_selem_get_playback_volume_range;
- set_func = snd_mixer_selem_set_playback_volume;
- } else {
- get_range_func = snd_mixer_selem_get_capture_volume_range;
- set_func = snd_mixer_selem_set_capture_volume;
- }
- err = get_range_func(control->elem, &min, &max);
- if (err < 0)
- return;
+ if (control->flags & TYPE_PVOLUME)
+ set_func = set_normalized_playback_volume;
+ else
+ set_func = set_normalized_capture_volume;
if (channels & LEFT)
- set_func(control->elem, control->volume_channels[0], min + (max - min) * value / 100);
+ set_func(control->elem, control->volume_channels[0], value / 100.0, 0);
if (channels & RIGHT)
- set_func(control->elem, control->volume_channels[1], min + (max - min) * value / 100);
+ set_func(control->elem, control->volume_channels[1], value / 100.0, 0);
+}
+
+static double clamp_volume(double v)
+{
+ if (v < 0)
+ return 0;
+ if (v > 1)
+ return 1;
+ return v;
}
static void change_volume_relative(struct control *control, int delta, unsigned int channels)
{
- int (*get_range_func)(snd_mixer_elem_t *, long *, long *);
- int (*get_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *);
- int (*set_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long);
- long min, max;
- long left, right;
- long value;
- int err;
+ double (*get_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t);
+ int (*set_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, double, int);
+ double left, right;
+ int dir;
if (!(control->flags & HAS_VOLUME_1))
channels = LEFT;
if (control->flags & TYPE_PVOLUME) {
- get_range_func = snd_mixer_selem_get_playback_volume_range;
- get_func = snd_mixer_selem_get_playback_volume;
- set_func = snd_mixer_selem_set_playback_volume;
+ get_func = get_normalized_playback_volume;
+ set_func = set_normalized_playback_volume;
} else {
- get_range_func = snd_mixer_selem_get_capture_volume_range;
- get_func = snd_mixer_selem_get_capture_volume;
- set_func = snd_mixer_selem_set_capture_volume;
+ get_func = get_normalized_capture_volume;
+ set_func = set_normalized_capture_volume;
}
- err = get_range_func(control->elem, &min, &max);
- if (err < 0)
- return;
+ if (channels & LEFT)
+ left = get_func(control->elem, control->volume_channels[0]);
+ if (channels & RIGHT)
+ right = get_func(control->elem, control->volume_channels[1]);
+ dir = delta > 0 ? 1 : -1;
if (channels & LEFT) {
- err = get_func(control->elem, control->volume_channels[0], &left);
- if (err < 0)
- return;
+ left = clamp_volume(left + delta / 100.0);
+ set_func(control->elem, control->volume_channels[0], left, dir);
}
if (channels & RIGHT) {
- err = get_func(control->elem, control->volume_channels[1], &right);
- if (err < 0)
- return;
- }
- if (channels & LEFT) {
- value = left + delta;
- if (value < min)
- value = min;
- else if (value > max)
- value = max;
- if (value != left)
- set_func(control->elem, control->volume_channels[0], value);
- }
- if (channels & RIGHT) {
- value = right + delta;
- if (value < min)
- value = min;
- else if (value > max)
- value = max;
- if (value != right)
- set_func(control->elem, control->volume_channels[1], value);
+ right = clamp_volume(right + delta / 100.0);
+ set_func(control->elem, control->volume_channels[1], right, dir);
}
}
@@ -458,34 +436,26 @@
static void balance_volumes(void)
{
struct control *control;
- long left, right;
+ double left, right;
int err;
control = get_focus_control(TYPE_PVOLUME | TYPE_CVOLUME);
if (!control || !(control->flags & HAS_VOLUME_1))
return;
if (control->flags & TYPE_PVOLUME) {
- err = snd_mixer_selem_get_playback_volume(control->elem, control->volume_channels[0], &left);
- if (err < 0)
- return;
- err = snd_mixer_selem_get_playback_volume(control->elem, control->volume_channels[1], &right);
- if (err < 0)
- return;
+ left = get_normalized_playback_volume(control->elem, control->volume_channels[0]);
+ right = get_normalized_playback_volume(control->elem, control->volume_channels[1]);
} else {
- err = snd_mixer_selem_get_capture_volume(control->elem, control->volume_channels[0], &left);
- if (err < 0)
- return;
- err = snd_mixer_selem_get_capture_volume(control->elem, control->volume_channels[1], &right);
- if (err < 0)
- return;
+ left = get_normalized_capture_volume(control->elem, control->volume_channels[0]);
+ right = get_normalized_capture_volume(control->elem, control->volume_channels[1]);
}
left = (left + right) / 2;
if (control->flags & TYPE_PVOLUME) {
- snd_mixer_selem_set_playback_volume(control->elem, control->volume_channels[0], left);
- snd_mixer_selem_set_playback_volume(control->elem, control->volume_channels[1], left);
+ set_normalized_playback_volume(control->elem, control->volume_channels[0], left, 0);
+ set_normalized_playback_volume(control->elem, control->volume_channels[1], left, 0);
} else {
- snd_mixer_selem_set_capture_volume(control->elem, control->volume_channels[0], left);
- snd_mixer_selem_set_capture_volume(control->elem, control->volume_channels[1], left);
+ set_normalized_capture_volume(control->elem, control->volume_channels[0], left, 0);
+ set_normalized_capture_volume(control->elem, control->volume_channels[1], left, 0);
}
display_controls();
}
diff --git a/alsamixer/textbox.c b/alsamixer/textbox.c
index d743a14..a979d3c 100644
--- a/alsamixer/textbox.c
+++ b/alsamixer/textbox.c
@@ -1,6 +1,6 @@
/*
* textbox.c - show a text box for messages, files or help
- * Copyright (c) 1998,1999 Tim Janik <timj@gtk.org>
+ * Copyright (c) 1998,1999 Tim Janik
* Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
*
diff --git a/alsamixer/volume_mapping.c b/alsamixer/volume_mapping.c
new file mode 100644
index 0000000..9cacad8
--- /dev/null
+++ b/alsamixer/volume_mapping.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2010 Clemens Ladisch <clemens@ladisch.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * The functions in this file map the value ranges of ALSA mixer controls onto
+ * the interval 0..1.
+ *
+ * The mapping is designed so that the position in the interval is proportional
+ * to the volume as a human ear would perceive it (i.e., the position is the
+ * cubic root of the linear sample multiplication factor). For controls with
+ * a small range (24 dB or less), the mapping is linear in the dB values so
+ * that each step has the same size visually. Only for controls without dB
+ * information, a linear mapping of the hardware volume register values is used
+ * (this is the same algorithm as used in the old alsamixer).
+ *
+ * When setting the volume, 'dir' is the rounding direction:
+ * -1/0/1 = down/nearest/up.
+ */
+
+#define _ISOC99_SOURCE /* lrint() */
+#define _GNU_SOURCE /* exp10() */
+#include "aconfig.h"
+#include <math.h>
+#include <stdbool.h>
+#include "volume_mapping.h"
+
+#define MAX_LINEAR_DB_SCALE 24
+
+static inline bool use_linear_dB_scale(long dBmin, long dBmax)
+{
+ return dBmax - dBmin <= MAX_LINEAR_DB_SCALE * 100;
+}
+
+static long lrint_dir(double x, int dir)
+{
+ if (dir > 0)
+ return lrint(ceil(x));
+ else if (dir < 0)
+ return lrint(floor(x));
+ else
+ return lrint(x);
+}
+
+enum ctl_dir { PLAYBACK, CAPTURE };
+
+static int (* const get_dB_range[2])(snd_mixer_elem_t *, long *, long *) = {
+ snd_mixer_selem_get_playback_dB_range,
+ snd_mixer_selem_get_capture_dB_range,
+};
+static int (* const get_raw_range[2])(snd_mixer_elem_t *, long *, long *) = {
+ snd_mixer_selem_get_playback_volume_range,
+ snd_mixer_selem_get_capture_volume_range,
+};
+static int (* const get_dB[2])(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *) = {
+ snd_mixer_selem_get_playback_dB,
+ snd_mixer_selem_get_capture_dB,
+};
+static int (* const get_raw[2])(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *) = {
+ snd_mixer_selem_get_playback_volume,
+ snd_mixer_selem_get_capture_volume,
+};
+static int (* const set_dB[2])(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long, int) = {
+ snd_mixer_selem_set_playback_dB,
+ snd_mixer_selem_set_capture_dB,
+};
+static int (* const set_raw[2])(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long) = {
+ snd_mixer_selem_set_playback_volume,
+ snd_mixer_selem_set_capture_volume,
+};
+
+static double get_normalized_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ enum ctl_dir ctl_dir)
+{
+ long min, max, value;
+ double normalized, min_norm;
+ int err;
+
+ err = get_dB_range[ctl_dir](elem, &min, &max);
+ if (err < 0 || min >= max) {
+ err = get_raw_range[ctl_dir](elem, &min, &max);
+ if (err < 0 || min == max)
+ return 0;
+
+ err = get_raw[ctl_dir](elem, channel, &value);
+ if (err < 0)
+ return 0;
+
+ return (value - min) / (double)(max - min);
+ }
+
+ err = get_dB[ctl_dir](elem, channel, &value);
+ if (err < 0)
+ return 0;
+
+ if (use_linear_dB_scale(min, max))
+ return (value - min) / (double)(max - min);
+
+ normalized = exp10((value - max) / 6000.0);
+ if (min != SND_CTL_TLV_DB_GAIN_MUTE) {
+ min_norm = exp10((min - max) / 6000.0);
+ normalized = (normalized - min_norm) / (1 - min_norm);
+ }
+
+ return normalized;
+}
+
+static int set_normalized_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ double volume,
+ int dir,
+ enum ctl_dir ctl_dir)
+{
+ long min, max, value;
+ double min_norm;
+ int err;
+
+ err = get_dB_range[ctl_dir](elem, &min, &max);
+ if (err < 0 || min >= max) {
+ err = get_raw_range[ctl_dir](elem, &min, &max);
+ if (err < 0)
+ return err;
+
+ value = lrint_dir(volume * (max - min), dir) + min;
+ return set_raw[ctl_dir](elem, channel, value);
+ }
+
+ if (use_linear_dB_scale(min, max)) {
+ value = lrint_dir(volume * (max - min), dir) + min;
+ return set_dB[ctl_dir](elem, channel, value, dir);
+ }
+
+ if (min != SND_CTL_TLV_DB_GAIN_MUTE) {
+ min_norm = exp10((min - max) / 6000.0);
+ volume = volume * (1 - min_norm) + min_norm;
+ }
+ value = lrint_dir(6000.0 * log10(volume), dir) + max;
+ return set_dB[ctl_dir](elem, channel, value, dir);
+}
+
+double get_normalized_playback_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel)
+{
+ return get_normalized_volume(elem, channel, PLAYBACK);
+}
+
+double get_normalized_capture_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel)
+{
+ return get_normalized_volume(elem, channel, CAPTURE);
+}
+
+int set_normalized_playback_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ double volume,
+ int dir)
+{
+ return set_normalized_volume(elem, channel, volume, dir, PLAYBACK);
+}
+
+int set_normalized_capture_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ double volume,
+ int dir)
+{
+ return set_normalized_volume(elem, channel, volume, dir, CAPTURE);
+}
diff --git a/alsamixer/volume_mapping.h b/alsamixer/volume_mapping.h
new file mode 100644
index 0000000..d4251d6
--- /dev/null
+++ b/alsamixer/volume_mapping.h
@@ -0,0 +1,19 @@
+#ifndef VOLUME_MAPPING_H_INCLUDED
+#define VOLUME_MAPPING_H_INCLUDED
+
+#include <alsa/asoundlib.h>
+
+double get_normalized_playback_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel);
+double get_normalized_capture_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel);
+int set_normalized_playback_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ double volume,
+ int dir);
+int set_normalized_capture_volume(snd_mixer_elem_t *elem,
+ snd_mixer_selem_channel_id_t channel,
+ double volume,
+ int dir);
+
+#endif
diff --git a/amixer/amixer.c b/amixer/amixer.c
index c9ea572..a177288 100644
--- a/amixer/amixer.c
+++ b/amixer/amixer.c
@@ -1120,8 +1120,8 @@
}
str++;
}
- *ptr = '\0';
}
+ *ptr = '\0';
snd_ctl_elem_id_set_name(id, buf);
} else if (!strncasecmp(str, "index=", 6)) {
str += 6;
diff --git a/configure.in b/configure.in
index a6d6d3d..f784363 100644
--- a/configure.in
+++ b/configure.in
@@ -26,6 +26,7 @@
AC_PROG_CC
dnl AC_PROG_CXX
AC_PROG_INSTALL
+AC_PROG_MKDIR_P
AC_PROG_LN_S
AM_PATH_ALSA(1.0.16)
@@ -38,11 +39,14 @@
[#include <alsa/asoundlib.h>])
AC_CHECK_HEADERS([alsa/seq.h], [have_seq="yes"], [have_seq="no"],
[#include <alsa/asoundlib.h>])
+AC_CHECK_HEADERS([samplerate.h], [have_samplerate="yes"], [have_samplerate="no"],
+ [#include <samplerate.h>])
AM_CONDITIONAL(HAVE_PCM, test "$have_pcm" = "yes")
AM_CONDITIONAL(HAVE_MIXER, test "$have_mixer" = "yes")
AM_CONDITIONAL(HAVE_RAWMIDI, test "$have_rawmidi" = "yes")
AM_CONDITIONAL(HAVE_SEQ, test "$have_seq" = "yes")
+AM_CONDITIONAL(HAVE_SAMPLERATE, test "$have_samplerate" = "yes")
dnl Check for librt
LIBRT=""
@@ -87,17 +91,31 @@
esac],[alsaconf=true])
AM_CONDITIONAL(ALSACONF, test x$alsaconf = xtrue)
+dnl Disable alsaloop
+AC_ARG_ENABLE(alsaloop,
+ [ --disable-alsaloop Disable alsaloop packaging],
+ [case "${enableval}" in
+ yes) alsaloop=true ;;
+ no) alsaloop=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-alsaloop) ;;
+ esac],[alsaloop=true])
+AM_CONDITIONAL(ALSALOOP, test x$alsaloop = xtrue)
+
xmlto=""
-if test x"$alsaconf" = xtrue; then
- AC_ARG_ENABLE(xmlto,
- AS_HELP_STRING([--disable-xmlto], [Disable man page creation via xmlto]),
- xmlto="$enableval", xmlto="yes")
- if test "$xmlto" = "yes"; then
- AC_CHECK_PROG([xmlto], [xmlto], [yes])
- fi
+AC_ARG_ENABLE(xmlto,
+ AS_HELP_STRING([--disable-xmlto], [Disable man page creation via xmlto]),
+ xmlto="$enableval", xmlto="yes")
+if test "$xmlto" = "yes"; then
+ AC_CHECK_PROG([xmlto], [xmlto], [yes])
fi
AM_CONDITIONAL(USE_XMLTO, test x"$xmlto" = xyes)
+AC_ARG_WITH(
+ [udev-rules-dir],
+ AS_HELP_STRING([--with-udev-rules-dir],[Directory where to install udev rules to (defaults to /lib/udev/rules.d)]),
+ [udevrulesdir=$withval], [udevrulesdir="/lib/udev/rules.d"])
+AC_SUBST(udevrulesdir)
+
dnl Checks for header files.
AC_HEADER_STDC
if test x$alsamixer = xtrue; then
@@ -117,7 +135,7 @@
CURSES_CFLAGS=`ncursesw5-config --cflags`
curseslib="ncursesw"
else
- AC_CHECK_LIB(ncursesw, initscr,
+ AC_CHECK_LIB(ncursesw, initscr,
[ CURSESINC='<ncurses.h>'; CURSESLIB='-lncursesw'; curseslib="ncursesw"])
fi
if test -n "$CURSESINC"; then
@@ -134,12 +152,12 @@
CURSES_CFLAGS=`ncurses5-config --cflags`
curseslib="ncurses"
else
- AC_CHECK_LIB(ncurses, initscr,
+ AC_CHECK_LIB(ncurses, initscr,
[ CURSESINC='<ncurses.h>'; CURSESLIB='-lncurses'; curseslib="ncurses"])
fi
fi
if test "$curseslib" = "curses" -o "$curseslib" = "auto"; then
- AC_CHECK_LIB(curses, initscr,
+ AC_CHECK_LIB(curses, initscr,
[ CURSESINC='<curses.h>'; CURSESLIB='-lcurses'; curseslib="curses"])
fi
if test -z "$CURSESINC"; then
@@ -265,6 +283,22 @@
AC_SUBST(LIBRT)
+dnl Check for systemd
+PKG_PROG_PKG_CONFIG
+AC_ARG_WITH([systemdsystemunitdir],
+ AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
+ [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
+if test "x$with_systemdsystemunitdir" != xno; then
+ AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
+fi
+AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
+
+AC_ARG_WITH([asound-state-dir],
+ AS_HELP_STRING([--with-asound-state-dir=DIR], [Directory to place asound.state file in]),
+ [ASOUND_STATE_DIR="$withval"],
+ [ASOUND_STATE_DIR="/var/lib/alsa"])
+AC_SUBST(ASOUND_STATE_DIR)
+
AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \
alsamixer/Makefile amidi/Makefile amixer/Makefile \
m4/Makefile po/Makefile.in \
@@ -274,4 +308,5 @@
aplay/Makefile include/Makefile iecset/Makefile utils/Makefile \
utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \
seq/aplaymidi/Makefile seq/aseqdump/Makefile seq/aseqnet/Makefile \
- speaker-test/Makefile speaker-test/samples/Makefile)
+ speaker-test/Makefile speaker-test/samples/Makefile \
+ alsaloop/Makefile)
diff --git a/po/LINGUAS b/po/LINGUAS
index 081e105..2027386 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -1 +1 @@
-ja de
+de fr ja
diff --git a/po/de.po b/po/de.po
index 7cae06e..0c47c65 100644
--- a/po/de.po
+++ b/po/de.po
@@ -5,10 +5,10 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: alsa-utils 1.0.20\n"
+"Project-Id-Version: alsa-utils 1.0.23\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-05-24 19:56+0200\n"
-"PO-Revision-Date: 2009-05-24 12:34+0200\n"
+"POT-Creation-Date: 2010-11-08 21:32+0100\n"
+"PO-Revision-Date: 2010-11-09 21:12+0100\n"
"Last-Translator: Clemens Ladisch <clemens@ladisch.de>\n"
"Language-Team: German\n"
"MIME-Version: 1.0\n"
@@ -179,47 +179,47 @@
msgstr "Dieses Gerät hat keine Regler."
#. TRANSLATORS: playback on; one character
-#: alsamixer/mixer_display.c:512 alsamixer/mixer_display.c:517
+#: alsamixer/mixer_display.c:537 alsamixer/mixer_display.c:542
msgid "O"
msgstr "O"
#. TRANSLATORS: playback muted; one character
-#: alsamixer/mixer_display.c:514 alsamixer/mixer_display.c:518
+#: alsamixer/mixer_display.c:539 alsamixer/mixer_display.c:543
msgid "M"
msgstr "M"
#. TRANSLATORS: "left"; no more than two characters
-#: alsamixer/mixer_display.c:532
+#: alsamixer/mixer_display.c:557
msgid "L"
msgstr "L"
#. TRANSLATORS: "right"; no more than two characters
-#: alsamixer/mixer_display.c:536
+#: alsamixer/mixer_display.c:561
msgid "R"
msgstr "R"
#. TRANSLATORS: no more than eight characters
-#: alsamixer/mixer_display.c:538
+#: alsamixer/mixer_display.c:563
msgid "CAPTURE"
msgstr "AUFNAHME"
-#: alsamixer/mixer_display.c:588
+#: alsamixer/mixer_display.c:613
msgid "Front"
msgstr "Vorne"
-#: alsamixer/mixer_display.c:591
+#: alsamixer/mixer_display.c:616
msgid "Rear"
msgstr "Hinten"
-#: alsamixer/mixer_display.c:594 speaker-test/speaker-test.c:106
+#: alsamixer/mixer_display.c:619 speaker-test/speaker-test.c:108
msgid "Center"
msgstr "Mitte"
-#: alsamixer/mixer_display.c:597
+#: alsamixer/mixer_display.c:622
msgid "Woofer"
msgstr "Bass"
-#: alsamixer/mixer_display.c:600
+#: alsamixer/mixer_display.c:625
msgid "Side"
msgstr "Seiten"
@@ -336,8 +336,8 @@
msgstr "Autoren:"
#: alsamixer/mixer_widget.c:214
-msgid " Tim Janik <timj@gtk.org>"
-msgstr " Tim Janik <tim@gtk.org>"
+msgid " Tim Janik"
+msgstr " Tim Janik"
#: alsamixer/mixer_widget.c:215
msgid " Jaroslav Kysela <perex@perex.cz>"
@@ -364,23 +364,23 @@
msgid "Cannot open file \"%s\"."
msgstr "Fehler beim Öffnen der Datei \"%s\"."
-#: aplay/aplay.c:139
+#: aplay/aplay.c:152
msgid "raw data"
msgstr "Rohdaten"
-#: aplay/aplay.c:140
+#: aplay/aplay.c:153
msgid "VOC"
msgstr "VOC"
-#: aplay/aplay.c:142
+#: aplay/aplay.c:155
msgid "WAVE"
msgstr "WAVE"
-#: aplay/aplay.c:143
+#: aplay/aplay.c:156
msgid "Sparc Audio"
msgstr "Sparc-Audio"
-#: aplay/aplay.c:164
+#: aplay/aplay.c:177
#, c-format
msgid ""
"Usage: %s [OPTION]... [FILE]...\n"
@@ -419,6 +419,11 @@
" expression for validation is: coef * (buffer_size / "
"2)\n"
" --test-nowait do not wait for ring buffer - eats whole CPU\n"
+" --max-file-time=# start another output file when the old file has "
+"recorded\n"
+" for this many seconds\n"
+" --process-id-file write the process ID here\n"
+" --use-strftime apply the strftime facility to the output file name\n"
msgstr ""
"Verwendung: %s [Option]... [Datei]...\n"
"\n"
@@ -456,13 +461,16 @@
"2)\n"
" --test-nowait kein Warten auf Ringpuffer; beansprucht volle CPU-"
"Leistung\n"
+" --max-file-time=# erzeuge mehrere Dateien; Wechsel nach # Sekunden\n"
+" --process-id-file=# schreibe Prozess-ID in diese Datei\n"
+" --use-strftime formatiere Dateiname mit strftime; %%v=Dateinummer\n"
-#: aplay/aplay.c:199 speaker-test/speaker-test.c:740
+#: aplay/aplay.c:216 speaker-test/speaker-test.c:819
#, c-format
msgid "Recognized sample formats are:"
msgstr "Unterstützte Sample-Formate:"
-#: aplay/aplay.c:205
+#: aplay/aplay.c:222
#, c-format
msgid ""
"\n"
@@ -471,115 +479,120 @@
"\n"
"Nicht alle davon sind auf jeder Hardware verfügbar.\n"
-#: aplay/aplay.c:206
+#: aplay/aplay.c:223
#, c-format
msgid "The availabled format shortcuts are:\n"
msgstr "Unterstütze Format-Abkürzungen:\n"
-#: aplay/aplay.c:207
+#: aplay/aplay.c:224
#, c-format
msgid "-f cd (16 bit little endian, 44100, stereo)\n"
msgstr "-f cd (16 Bits, Little Endian, 44100 Hz, stereo)\n"
-#: aplay/aplay.c:208
+#: aplay/aplay.c:225
#, c-format
msgid "-f cdr (16 bit big endian, 44100, stereo)\n"
msgstr "-f cdr (16 Bits, Big Endian, 44100 Hz, stereo)\n"
-#: aplay/aplay.c:209
+#: aplay/aplay.c:226
#, c-format
msgid "-f dat (16 bit little endian, 48000, stereo)\n"
msgstr "-f dat (16 Bits, Little Endian, 48000 Hz, stereo)\n"
-#: aplay/aplay.c:223
+#: aplay/aplay.c:240
msgid "no soundcards found..."
msgstr "keine Soundkarten gefunden ..."
-#: aplay/aplay.c:226
+#: aplay/aplay.c:243
#, c-format
msgid "**** List of %s Hardware Devices ****\n"
msgstr "**** Liste der Hardware-Geräte (%s) ****\n"
-#: aplay/aplay.c:255
+#: aplay/aplay.c:272
#, c-format
msgid "card %i: %s [%s], device %i: %s [%s]\n"
msgstr "Karte %i: %s [%s], Gerät %i: %s [%s]\n"
-#: aplay/aplay.c:261
+#: aplay/aplay.c:278
#, c-format
msgid " Subdevices: %i/%i\n"
msgstr " Sub-Geräte: %i/%i\n"
-#: aplay/aplay.c:268
+#: aplay/aplay.c:285
#, c-format
msgid " Subdevice #%i: %s\n"
msgstr " Sub-Gerät #%i: %s\n"
-#: aplay/aplay.c:332
+#: aplay/aplay.c:362
#, c-format
msgid "Aborted by signal %s...\n"
msgstr "Abbruch durch Signal %s ...\n"
-#: aplay/aplay.c:430
+#: aplay/aplay.c:473
msgid "command should be named either arecord or aplay"
msgstr "Befehl sollte arecord oder aplay sein"
-#: aplay/aplay.c:469
+#: aplay/aplay.c:512
#, c-format
msgid "unrecognized file format %s"
msgstr "unbekanntes Dateiformat %s"
-#: aplay/aplay.c:476
+#: aplay/aplay.c:519
#, c-format
msgid "value %i for channels is invalid"
msgstr "Kanalanzahl %i ist ungültig"
-#: aplay/aplay.c:495
+#: aplay/aplay.c:538
#, c-format
msgid "wrong extended format '%s'"
msgstr "erweitertes Format '%s' ist ungültig"
-#: aplay/aplay.c:506
+#: aplay/aplay.c:549
#, c-format
msgid "bad speed value %i"
msgstr "ungültige Rate %i"
-#: aplay/aplay.c:592
+#: aplay/aplay.c:644
#, c-format
msgid "Try `%s --help' for more information.\n"
msgstr "Siehe `%s --help' für mehr Informationen.\n"
-#: aplay/aplay.c:608
+#: aplay/aplay.c:660
#, c-format
msgid "audio open error: %s"
msgstr "Fehler beim Öffnen des Gerätes: %s"
-#: aplay/aplay.c:613
+#: aplay/aplay.c:665
#, c-format
msgid "info error: %s"
msgstr "Fehler beim Lesen der Geräteinformationen: %s"
-#: aplay/aplay.c:620
+#: aplay/aplay.c:672
#, c-format
msgid "nonblock setting error: %s"
msgstr "Fehler beim Setzen des nonblock-Modus: %s"
-#: aplay/aplay.c:630 aplay/aplay.c:737 aplay/aplay.c:1092
+#: aplay/aplay.c:682 aplay/aplay.c:806 aplay/aplay.c:1174
msgid "not enough memory"
msgstr "nicht genug Speicher"
-#: aplay/aplay.c:727
+#: aplay/aplay.c:706
+#, c-format
+msgid "Cannot create process ID file %s: %s"
+msgstr "Fehler beim Schreiben der Prozess-ID-Datei %s: %s"
+
+#: aplay/aplay.c:796
#, c-format
msgid "read error (called from line %i)"
msgstr "Lesefehler (aufgerufen von Zeile %i)"
-#: aplay/aplay.c:785
+#: aplay/aplay.c:854
#, c-format
msgid "unknown length of 'fmt ' chunk (read %u, should be %u at least)"
msgstr ""
"unbekannte Länge des 'fmt '-Blocks (gelesen: %u, sollte mindestens %u sein)"
-#: aplay/aplay.c:795
+#: aplay/aplay.c:864
#, c-format
msgid ""
"unknown length of extensible 'fmt ' chunk (read %u, should be %u at least)"
@@ -587,198 +600,230 @@
"unbekannte Länge des erweiterten 'fmt '-Blocks (gelesen: %u, sollte "
"mindestens %u sein)"
-#: aplay/aplay.c:800
+#: aplay/aplay.c:869
msgid "wrong format tag in extensible 'fmt ' chunk"
msgstr "ungültiger Format-Wert im erweiterten 'fmt '-Block"
-#: aplay/aplay.c:807
+#: aplay/aplay.c:876
#, c-format
msgid "can't play WAVE-file format 0x%04x which is not PCM or FLOAT encoded"
msgstr ""
"kann WAVE-Datei-Format 0x%04x nicht abspielen; ist weder PCM noch FLOAT"
-#: aplay/aplay.c:811
+#: aplay/aplay.c:880
#, c-format
msgid "can't play WAVE-files with %d tracks"
msgstr "kann WAVE-Datei mit %d Kanälen nicht abspielen"
-#: aplay/aplay.c:819 aplay/aplay.c:919
+#: aplay/aplay.c:888 aplay/aplay.c:988
#, c-format
msgid "Warning: format is changed to U8\n"
msgstr "Warnung: benutztes Format ist U8\n"
-#: aplay/aplay.c:825
+#: aplay/aplay.c:894
#, c-format
msgid "Warning: format is changed to S16_LE\n"
msgstr "Warnung: benutztes Format ist S16_LE\n"
-#: aplay/aplay.c:833
+#: aplay/aplay.c:902
#, c-format
msgid "Warning: format is changed to S24_3LE\n"
msgstr "Warnung: benutztes Format ist S24_3LE\n"
-#: aplay/aplay.c:839
+#: aplay/aplay.c:908
#, c-format
msgid "Warning: format is changed to S24_LE\n"
msgstr "Warnung: benutztes Format ist S24_LE\n"
-#: aplay/aplay.c:843
+#: aplay/aplay.c:912
#, c-format
msgid ""
" can't play WAVE-files with sample %d bits in %d bytes wide (%d channels)"
msgstr ""
"kann WAVE-Datei mit %d-Bit-Samples in %d Bytes (%d Kanäle) nicht abspielen"
-#: aplay/aplay.c:855
+#: aplay/aplay.c:924
#, c-format
msgid " can't play WAVE-files with sample %d bits wide"
msgstr "kann WAVE-Datei mit %d-Bit-Samples nicht abspielen"
-#: aplay/aplay.c:913
+#: aplay/aplay.c:982
#, c-format
msgid "Warning: format is changed to MU_LAW\n"
msgstr "Warnung: benutztes Format ist MU_LAW\n"
-#: aplay/aplay.c:925
+#: aplay/aplay.c:994
#, c-format
msgid "Warning: format is changed to S16_BE\n"
msgstr "Warnung: benutztes Format ist S16_BE\n"
-#: aplay/aplay.c:938 aplay/aplay.c:1768 aplay/aplay.c:1775 aplay/aplay.c:2297
-#: aplay/aplay.c:2309
+#: aplay/aplay.c:1007 aplay/aplay.c:1925 aplay/aplay.c:1932 aplay/aplay.c:2455
+#: aplay/aplay.c:2467
msgid "read error"
msgstr "Lesefehler"
-#: aplay/aplay.c:957
+#: aplay/aplay.c:1037
msgid "Broken configuration for this PCM: no configurations available"
msgstr ""
"ungültige Konfiguration für dieses Gerät: keine unterstützte Konfiguration"
-#: aplay/aplay.c:974
+#: aplay/aplay.c:1054
msgid "Access type not available"
msgstr "Zugriffs-Modus nicht unterstützt"
-#: aplay/aplay.c:979
+#: aplay/aplay.c:1059
msgid "Sample format non available"
msgstr "Sample-Format nicht unterstützt"
-#: aplay/aplay.c:984
+#: aplay/aplay.c:1065
msgid "Channels count non available"
msgstr "Kanalanzahl nicht unterstützt"
-#: aplay/aplay.c:999
+#: aplay/aplay.c:1080
#, c-format
msgid "Warning: rate is not accurate (requested = %iHz, got = %iHz)\n"
msgstr ""
"Warnung: Rate ist nicht exakt (angefordert: %i Hz, unterstützt: %i Hz)\n"
-#: aplay/aplay.c:1005
+#: aplay/aplay.c:1086
#, c-format
msgid " please, try the plug plugin %s\n"
msgstr " probieren Sie bitte das plug-Plugin: %s\n"
-#: aplay/aplay.c:1041
+#: aplay/aplay.c:1123
msgid "Unable to install hw params:"
msgstr "Fehler beim Setzen der Hardware-Parameter:"
-#: aplay/aplay.c:1048
+#: aplay/aplay.c:1130
#, c-format
msgid "Can't use period equal to buffer size (%lu == %lu)"
msgstr "Periode gleich der Puffer-Größe wird nicht unterstützt (%lu == %lu)"
-#: aplay/aplay.c:1079
+#: aplay/aplay.c:1161
msgid "unable to install sw params:"
msgstr "Fehler beim Setzen der Software-Parameter:"
-#: aplay/aplay.c:1154
+#: aplay/aplay.c:1192
+#, c-format
+msgid "snd_pcm_mmap_begin problem: %s"
+msgstr "Fehler bei snd_pcm_mmap_begin: %s"
+
+#: aplay/aplay.c:1215
+#, c-format
+msgid "stdin O_NONBLOCK flag setup failed\n"
+msgstr "Fehler beim Setzen von O_NONBLOCK in stdin\n"
+
+#: aplay/aplay.c:1237
+#, c-format
+msgid "\rPAUSE command ignored (no hw support)\n"
+msgstr "\rPause-Kommando ignoriert (keine HW-Unterstützung)\n"
+
+#: aplay/aplay.c:1242
+#, c-format
+msgid "pause push error: %s"
+msgstr "Fehler beim Pausieren: %s"
+
+#: aplay/aplay.c:1251
+#, c-format
+msgid "pause release error: %s"
+msgstr "Fehler beim Beenden der Pause: %s"
+
+#: aplay/aplay.c:1265
+#, c-format
+msgid ""
+"\r=== PAUSE === "
+msgstr ""
+"\r=== PAUSE === "
+
+#: aplay/aplay.c:1307
#, c-format
msgid "status error: %s"
msgstr "Status-Fehler: %s"
-#: aplay/aplay.c:1164 aplay/aplay.c:1175
+#: aplay/aplay.c:1317 aplay/aplay.c:1328
#, c-format
msgid "%s!!! (at least %.3f ms long)\n"
msgstr "%s!!! (mindestens %.3f ms)\n"
-#: aplay/aplay.c:1165 aplay/aplay.c:1168 aplay/aplay.c:1176
+#: aplay/aplay.c:1318 aplay/aplay.c:1321 aplay/aplay.c:1329
msgid "underrun"
msgstr "Unterlauf"
-#: aplay/aplay.c:1165 aplay/aplay.c:1176
+#: aplay/aplay.c:1318 aplay/aplay.c:1329
msgid "overrun"
msgstr "Überlauf"
-#: aplay/aplay.c:1180
+#: aplay/aplay.c:1333
#, c-format
msgid "Status:\n"
msgstr "Status:\n"
-#: aplay/aplay.c:1184
+#: aplay/aplay.c:1337
#, c-format
msgid "xrun: prepare error: %s"
msgstr "Unter-/Überlauf: Fehler beim Re-Initialisieren des Gerätes: %s"
-#: aplay/aplay.c:1190
+#: aplay/aplay.c:1343
#, c-format
msgid "Status(DRAINING):\n"
msgstr "Status (DRAINING):\n"
-#: aplay/aplay.c:1194
+#: aplay/aplay.c:1347
#, c-format
msgid "capture stream format change? attempting recover...\n"
msgstr "Format-Wechsel der Aufnahme-Daten? Versuche Wiederherstellung ...\n"
-#: aplay/aplay.c:1196
+#: aplay/aplay.c:1349
#, c-format
msgid "xrun(DRAINING): prepare error: %s"
msgstr "XRUN (DRAINING): Fehler beim Re-Initialisieren des Gerätes: %s"
-#: aplay/aplay.c:1203
+#: aplay/aplay.c:1356
#, c-format
msgid "Status(R/W):\n"
msgstr "Status (R/W):\n"
-#: aplay/aplay.c:1206
+#: aplay/aplay.c:1359
#, c-format
msgid "read/write error, state = %s"
msgstr "Lese-/Schreibfehler, Status = %s"
-#: aplay/aplay.c:1216
+#: aplay/aplay.c:1369
#, c-format
msgid "Suspended. Trying resume. "
msgstr "Ruhezustand. Versuche, aufzuwecken. "
-#: aplay/aplay.c:1221
+#: aplay/aplay.c:1374
#, c-format
msgid "Failed. Restarting stream. "
msgstr "Fehlgeschlagen. Re-Initialisierung. "
-#: aplay/aplay.c:1223
+#: aplay/aplay.c:1376
#, c-format
msgid "suspend: prepare error: %s"
msgstr "Ruhezustand: Fehler beim Re-Initialisieren: %s"
-#: aplay/aplay.c:1228
+#: aplay/aplay.c:1381
#, c-format
msgid "Done.\n"
msgstr "Fertig.\n"
-#: aplay/aplay.c:1250
+#: aplay/aplay.c:1403
#, c-format
msgid " !clip "
msgstr " !clip "
-#: aplay/aplay.c:1397
+#: aplay/aplay.c:1550
#, c-format
msgid "Unsupported bit size %d.\n"
msgstr "%d-Bit-Samples werden nicht unterstützt.\n"
-#: aplay/aplay.c:1431
+#: aplay/aplay.c:1584
#, c-format
msgid "Max peak (%li samples): 0x%08x "
msgstr "Höchstwert (%li Samples): 0x%08x "
-#: aplay/aplay.c:1465
+#: aplay/aplay.c:1618
#, c-format
msgid ""
"Suspicious buffer position (%li total): avail = %li, delay = %li, buffer = %"
@@ -787,108 +832,108 @@
"verdächtige Puffer-Position (total %li): avail = %li, delay = %li, buffer = %"
"li\n"
-#: aplay/aplay.c:1528
+#: aplay/aplay.c:1682
#, c-format
msgid "write error: %s"
msgstr "Schreibfehler: %s"
-#: aplay/aplay.c:1574
+#: aplay/aplay.c:1729
#, c-format
msgid "writev error: %s"
msgstr "Vektor-Schreib-Fehler: %s"
-#: aplay/aplay.c:1617
+#: aplay/aplay.c:1773
#, c-format
msgid "read error: %s"
msgstr "Lesefehler: %s"
-#: aplay/aplay.c:1660
+#: aplay/aplay.c:1817
#, c-format
msgid "readv error: %s"
msgstr "Vektor-Lese-Fehler: %s"
-#: aplay/aplay.c:1708
+#: aplay/aplay.c:1865
msgid "can't allocate buffer for silence"
msgstr "nicht genug Speicher für Stille-Block"
-#: aplay/aplay.c:1717 aplay/aplay.c:1943 aplay/aplay.c:1948 aplay/aplay.c:1995
-#: aplay/aplay.c:2004 aplay/aplay.c:2011 aplay/aplay.c:2021 aplay/aplay.c:2027
-#: aplay/aplay.c:2099 aplay/aplay.c:2129 aplay/aplay.c:2143
+#: aplay/aplay.c:1874 aplay/aplay.c:2100 aplay/aplay.c:2105 aplay/aplay.c:2152
+#: aplay/aplay.c:2161 aplay/aplay.c:2168 aplay/aplay.c:2178 aplay/aplay.c:2184
+#: aplay/aplay.c:2256 aplay/aplay.c:2286 aplay/aplay.c:2300
msgid "write error"
msgstr "Schreibfehler"
-#: aplay/aplay.c:1730
+#: aplay/aplay.c:1887
#, c-format
msgid "voc_pcm_flush - silence error"
msgstr "voc_pcm_flush - Fehler in set_silence"
-#: aplay/aplay.c:1733
+#: aplay/aplay.c:1890
msgid "voc_pcm_flush error"
msgstr "Schreibfehler"
-#: aplay/aplay.c:1759
+#: aplay/aplay.c:1916
msgid "malloc error"
msgstr "nicht genug Speicher"
-#: aplay/aplay.c:1763
+#: aplay/aplay.c:1920
#, c-format
msgid "Playing Creative Labs Channel file '%s'...\n"
msgstr "Spiele Creative Labs Channel-Datei '%s'...\n"
-#: aplay/aplay.c:1831 aplay/aplay.c:1923
+#: aplay/aplay.c:1988 aplay/aplay.c:2080
msgid "can't play packed .voc files"
msgstr "kann komprimierte .voc-Dateien nicht abspielen"
-#: aplay/aplay.c:1883
+#: aplay/aplay.c:2040
#, c-format
msgid "can't play loops; %s isn't seekable\n"
msgstr ""
"kann Schleife nicht abspielen; Dateiposition in %s ist nicht änderbar\n"
-#: aplay/aplay.c:1932
+#: aplay/aplay.c:2089
#, c-format
msgid "unknown blocktype %d. terminate."
msgstr "Unbekannter Block-Typ %d. Abbruch."
-#: aplay/aplay.c:2063
+#: aplay/aplay.c:2220
#, c-format
msgid "Wave doesn't support %s format..."
msgstr "Format %s wird in WAVE nicht unterstützt ..."
-#: aplay/aplay.c:2123
+#: aplay/aplay.c:2280
#, c-format
msgid "Sparc Audio doesn't support %s format..."
msgstr "Format %s wird in Sparc-Audio nicht unterstützt ..."
-#: aplay/aplay.c:2204
+#: aplay/aplay.c:2361
msgid "Playing"
msgstr "Wiedergabe:"
-#: aplay/aplay.c:2204
+#: aplay/aplay.c:2361
msgid "Recording"
msgstr "Aufnahme:"
-#: aplay/aplay.c:2208
+#: aplay/aplay.c:2365
#, c-format
msgid "Rate %d Hz, "
msgstr "Rate: %d Hz, "
-#: aplay/aplay.c:2210
+#: aplay/aplay.c:2367
#, c-format
msgid "Mono"
msgstr "mono"
-#: aplay/aplay.c:2212
+#: aplay/aplay.c:2369
#, c-format
msgid "Stereo"
msgstr "stereo"
-#: aplay/aplay.c:2214
+#: aplay/aplay.c:2371
#, c-format
msgid "Channels %i"
msgstr "%i Kanäle"
-#: aplay/aplay.c:2573 aplay/aplay.c:2626
+#: aplay/aplay.c:2882 aplay/aplay.c:2935
#, c-format
msgid "You need to specify %d files"
msgstr "Es werden %d Dateien benötigt."
@@ -999,47 +1044,47 @@
msgid "kernel"
msgstr "Kernel"
-#: seq/aconnect/aconnect.c:326
+#: seq/aconnect/aconnect.c:307
#, c-format
msgid "can't open sequencer\n"
msgstr "Fehler beim Öffnen des Sequenzers\n"
-#: seq/aconnect/aconnect.c:354
+#: seq/aconnect/aconnect.c:335
#, c-format
msgid "can't get client id\n"
msgstr "Fehler beim Lesen der Client-ID\n"
-#: seq/aconnect/aconnect.c:361
+#: seq/aconnect/aconnect.c:342
#, c-format
msgid "can't set client info\n"
msgstr "Fehler beim Setzen des Client-Namens\n"
-#: seq/aconnect/aconnect.c:368
+#: seq/aconnect/aconnect.c:349
#, c-format
msgid "invalid sender address %s\n"
msgstr "ungültige Sender-Adresse %s\n"
-#: seq/aconnect/aconnect.c:373 seq/aseqnet/aseqnet.c:290
+#: seq/aconnect/aconnect.c:354 seq/aseqnet/aseqnet.c:290
#, c-format
msgid "invalid destination address %s\n"
msgstr "ungültige Ziel-Adresse %s\n"
-#: seq/aconnect/aconnect.c:387
+#: seq/aconnect/aconnect.c:368
#, c-format
msgid "No subscription is found\n"
msgstr "keine Verbindung gefunden\n"
-#: seq/aconnect/aconnect.c:392
+#: seq/aconnect/aconnect.c:373
#, c-format
msgid "Disconnection failed (%s)\n"
msgstr "Verbindungs-Trennung fehlgeschlagen (%s)\n"
-#: seq/aconnect/aconnect.c:398
+#: seq/aconnect/aconnect.c:379
#, c-format
msgid "Connection is already subscribed\n"
msgstr "Verbindung ist bereits vorhanden\n"
-#: seq/aconnect/aconnect.c:403
+#: seq/aconnect/aconnect.c:384
#, c-format
msgid "Connection failed (%s)\n"
msgstr "Verbindung fehlgeschlagen (%s)\n"
@@ -1169,262 +1214,262 @@
msgid "disconnected\n"
msgstr "Verbindung getrennt\n"
-#: speaker-test/speaker-test.c:102
+#: speaker-test/speaker-test.c:104
msgid "Front Left"
msgstr "Vorne links"
-#: speaker-test/speaker-test.c:103
+#: speaker-test/speaker-test.c:105
msgid "Front Right"
msgstr "Vorne rechts"
-#: speaker-test/speaker-test.c:104
+#: speaker-test/speaker-test.c:106
msgid "Rear Left"
msgstr "Hinten links"
-#: speaker-test/speaker-test.c:105
+#: speaker-test/speaker-test.c:107
msgid "Rear Right"
msgstr "Hinten rechts"
-#: speaker-test/speaker-test.c:107
+#: speaker-test/speaker-test.c:109
msgid "LFE"
msgstr "Bass"
-#: speaker-test/speaker-test.c:108
+#: speaker-test/speaker-test.c:110
msgid "Side Left"
msgstr "Seitlich links"
-#: speaker-test/speaker-test.c:109
+#: speaker-test/speaker-test.c:111
msgid "Side Right"
msgstr "Seitlich rechts"
-#: speaker-test/speaker-test.c:110
+#: speaker-test/speaker-test.c:112
msgid "Channel 9"
msgstr "Kanal 9"
-#: speaker-test/speaker-test.c:111
+#: speaker-test/speaker-test.c:113
msgid "Channel 10"
msgstr "Kanal 10"
-#: speaker-test/speaker-test.c:112
+#: speaker-test/speaker-test.c:114
msgid "Channel 11"
msgstr "Kanal 11"
-#: speaker-test/speaker-test.c:113
+#: speaker-test/speaker-test.c:115
msgid "Channel 12"
msgstr "Kanal 12"
-#: speaker-test/speaker-test.c:114
+#: speaker-test/speaker-test.c:116
msgid "Channel 13"
msgstr "Kanal 13"
-#: speaker-test/speaker-test.c:115
+#: speaker-test/speaker-test.c:117
msgid "Channel 14"
msgstr "Kanal 14"
-#: speaker-test/speaker-test.c:116
+#: speaker-test/speaker-test.c:118
msgid "Channel 15"
msgstr "Kanal 15"
-#: speaker-test/speaker-test.c:117
+#: speaker-test/speaker-test.c:119
msgid "Channel 16"
msgstr "Kanal 16"
-#: speaker-test/speaker-test.c:307
+#: speaker-test/speaker-test.c:383
#, c-format
msgid "Broken configuration for playback: no configurations available: %s\n"
msgstr "Ungültige Konfiguration: keine unterstützte Konfiguration: %s\n"
-#: speaker-test/speaker-test.c:314
+#: speaker-test/speaker-test.c:390
#, c-format
msgid "Access type not available for playback: %s\n"
msgstr "Zugriffsmodus nicht unterstützt: %s\n"
-#: speaker-test/speaker-test.c:321
+#: speaker-test/speaker-test.c:397
#, c-format
msgid "Sample format not available for playback: %s\n"
msgstr "Sample-Format nicht unterstützt: %s\n"
-#: speaker-test/speaker-test.c:328
+#: speaker-test/speaker-test.c:404
#, c-format
msgid "Channels count (%i) not available for playbacks: %s\n"
msgstr "Kanal-Anzahl %i nicht unterstützt: %s\n"
-#: speaker-test/speaker-test.c:336
+#: speaker-test/speaker-test.c:412
#, c-format
msgid "Rate %iHz not available for playback: %s\n"
msgstr "Rate %i Hz nicht unterstützt: %s\n"
-#: speaker-test/speaker-test.c:341
+#: speaker-test/speaker-test.c:417
#, c-format
msgid "Rate doesn't match (requested %iHz, get %iHz, err %d)\n"
msgstr ""
"Rate ist nicht exakt (angefordert: %i Hz, unterstützt: %i Hz, Fehlercode %"
"d)\n"
-#: speaker-test/speaker-test.c:345
+#: speaker-test/speaker-test.c:421
#, c-format
msgid "Rate set to %iHz (requested %iHz)\n"
msgstr "Rate ist %i Hz (angefordert: %i Hz)\n"
-#: speaker-test/speaker-test.c:351
+#: speaker-test/speaker-test.c:427
#, c-format
msgid "Buffer size range from %lu to %lu\n"
msgstr "Puffergröße von %lu bis %lu\n"
-#: speaker-test/speaker-test.c:352
+#: speaker-test/speaker-test.c:428
#, c-format
msgid "Period size range from %lu to %lu\n"
msgstr "Periodengröße von %lu bis %lu\n"
-#: speaker-test/speaker-test.c:354
+#: speaker-test/speaker-test.c:430
#, c-format
msgid "Requested period time %u us\n"
msgstr "Angeforderte Periodenzeit %u µs\n"
-#: speaker-test/speaker-test.c:357
+#: speaker-test/speaker-test.c:433
#, c-format
msgid "Unable to set period time %u us for playback: %s\n"
msgstr "Fehler beim Setzen der Periodenzeit %u µs: %s\n"
-#: speaker-test/speaker-test.c:363
+#: speaker-test/speaker-test.c:439
#, c-format
msgid "Requested buffer time %u us\n"
msgstr "Angeforderte Pufferlänge %u µs\n"
-#: speaker-test/speaker-test.c:366
+#: speaker-test/speaker-test.c:442
#, c-format
msgid "Unable to set buffer time %u us for playback: %s\n"
msgstr "Fehler beim Setzen der Pufferlänge %u µs: %s\n"
-#: speaker-test/speaker-test.c:375
+#: speaker-test/speaker-test.c:451
#, c-format
msgid "Using max buffer size %lu\n"
msgstr "Verwende maximale Puffergröße %lu\n"
-#: speaker-test/speaker-test.c:378
+#: speaker-test/speaker-test.c:454
#, c-format
msgid "Unable to set buffer size %lu for playback: %s\n"
msgstr "Fehler beim Setzen der Puffergröße %lu: %s\n"
-#: speaker-test/speaker-test.c:384
+#: speaker-test/speaker-test.c:460
#, c-format
msgid "Periods = %u\n"
msgstr "Perioden = %u\n"
-#: speaker-test/speaker-test.c:387
+#: speaker-test/speaker-test.c:463
#, c-format
msgid "Unable to set nperiods %u for playback: %s\n"
msgstr "Fehler beim Setzen der Periodenanzahl %u: %s\n"
-#: speaker-test/speaker-test.c:396
+#: speaker-test/speaker-test.c:472
#, c-format
msgid "Unable to set hw params for playback: %s\n"
msgstr "Fehler beim Setzen der Hardware-Parameter: %s\n"
-#: speaker-test/speaker-test.c:402
+#: speaker-test/speaker-test.c:478
#, c-format
msgid "was set period_size = %lu\n"
msgstr "gesetzt: period_size = %lu\n"
-#: speaker-test/speaker-test.c:403
+#: speaker-test/speaker-test.c:479
#, c-format
msgid "was set buffer_size = %lu\n"
msgstr "gesetzt: buffer_size = %lu\n"
-#: speaker-test/speaker-test.c:405
+#: speaker-test/speaker-test.c:481
#, c-format
msgid "buffer to small, could not use\n"
msgstr "Puffer zu klein, kann nicht benutzt werden\n"
-#: speaker-test/speaker-test.c:418
+#: speaker-test/speaker-test.c:494
#, c-format
msgid "Unable to determine current swparams for playback: %s\n"
msgstr "Fehler beim Lesen der Software-Parameter: %s\n"
-#: speaker-test/speaker-test.c:425
+#: speaker-test/speaker-test.c:501
#, c-format
msgid "Unable to set start threshold mode for playback: %s\n"
msgstr "Fehler beim Setzen des Start-Schwellenwertes: %s\n"
-#: speaker-test/speaker-test.c:432
+#: speaker-test/speaker-test.c:508
#, c-format
msgid "Unable to set avail min for playback: %s\n"
msgstr "Fehler beim Setzen des Mindest-verfügbar-Wertes: %s\n"
-#: speaker-test/speaker-test.c:439
+#: speaker-test/speaker-test.c:515
#, c-format
msgid "Unable to set sw params for playback: %s\n"
msgstr "Fehler beim Setzen der Software-Parameter: %s\n"
-#: speaker-test/speaker-test.c:454
+#: speaker-test/speaker-test.c:530
#, c-format
msgid "Can't recovery from underrun, prepare failed: %s\n"
msgstr ""
"Fehler bei Unterlauf-Behandlung, Re-Initialisierung fehlgeschlagen: %s\n"
-#: speaker-test/speaker-test.c:465
+#: speaker-test/speaker-test.c:541
#, c-format
msgid "Can't recovery from suspend, prepare failed: %s\n"
msgstr ""
"Fehler beim Aufwachen aus dem Ruhezustand, Re-Initialisierung "
"fehlgeschlagen: %s\n"
-#: speaker-test/speaker-test.c:529 speaker-test/speaker-test.c:926
+#: speaker-test/speaker-test.c:605 speaker-test/speaker-test.c:1025
#, c-format
msgid "No enough memory\n"
msgstr "Nicht genug Speicher\n"
-#: speaker-test/speaker-test.c:534
+#: speaker-test/speaker-test.c:610
#, c-format
msgid "Cannot open WAV file %s\n"
msgstr "Kann WAV-Datei %s nicht öffnen\n"
-#: speaker-test/speaker-test.c:538 speaker-test/speaker-test.c:567
+#: speaker-test/speaker-test.c:614 speaker-test/speaker-test.c:643
#, c-format
msgid "Invalid WAV file %s\n"
msgstr "Ungültige WAV-Datei %s\n"
-#: speaker-test/speaker-test.c:543
+#: speaker-test/speaker-test.c:619
#, c-format
msgid "Not a WAV file: %s\n"
msgstr "Keine WAV-Datei: %s\n"
-#: speaker-test/speaker-test.c:547
+#: speaker-test/speaker-test.c:623
#, c-format
msgid "Unsupported WAV format %d for %s\n"
msgstr "Nicht unterstütztes WAV-Format %d in %s\n"
-#: speaker-test/speaker-test.c:552
+#: speaker-test/speaker-test.c:628
#, c-format
msgid "%s is not a mono stream (%d channels)\n"
msgstr "%s ist keine Mono-Datei (%d Kanäle)\n"
-#: speaker-test/speaker-test.c:557
+#: speaker-test/speaker-test.c:633
#, c-format
msgid "Sample rate doesn't match (%d) for %s\n"
msgstr "Sample-Rate (%d) stimmt nicht überein in %s\n"
-#: speaker-test/speaker-test.c:562
+#: speaker-test/speaker-test.c:638
#, c-format
msgid "Unsupported sample format bits %d for %s\n"
msgstr "Nicht unterstütztes Sample-Format mit %d Bits in %s\n"
-#: speaker-test/speaker-test.c:612
+#: speaker-test/speaker-test.c:688
#, c-format
msgid "Undefined channel %d\n"
msgstr "Kanal %d nicht definiert\n"
-#: speaker-test/speaker-test.c:663
+#: speaker-test/speaker-test.c:739
#, c-format
msgid "Write error: %d,%s\n"
msgstr "Schreibfehler: %d, %s\n"
-#: speaker-test/speaker-test.c:665
+#: speaker-test/speaker-test.c:741
#, c-format
msgid "xrun_recovery failed: %d,%s\n"
msgstr "xrun_recovery fehlgeschlagen: %d, %s\n"
-#: speaker-test/speaker-test.c:723
+#: speaker-test/speaker-test.c:803
#, c-format
msgid ""
"Usage: speaker-test [OPTION]... \n"
@@ -1461,72 +1506,72 @@
"-W,--wavdir benutze Verzeichnis mit darin enthaltenen WAV-Dateien\n"
"\n"
-#: speaker-test/speaker-test.c:835
+#: speaker-test/speaker-test.c:921
#, c-format
msgid "Invalid number of periods %d\n"
msgstr "Ungültige Periodenanzahl %d\n"
-#: speaker-test/speaker-test.c:849 speaker-test/speaker-test.c:853
+#: speaker-test/speaker-test.c:937 speaker-test/speaker-test.c:941
#, c-format
msgid "Invalid test type %s\n"
msgstr "Ungültiger Test-Typ %s\n"
-#: speaker-test/speaker-test.c:865
+#: speaker-test/speaker-test.c:953
#, c-format
msgid "Invalid parameter for -s option.\n"
msgstr "Ungültiger Wert für Option -s\n"
-#: speaker-test/speaker-test.c:876
+#: speaker-test/speaker-test.c:967
#, c-format
msgid "Unknown option '%c'\n"
msgstr "Unbekannte Options '%c'\n"
-#: speaker-test/speaker-test.c:890
+#: speaker-test/speaker-test.c:981
#, c-format
msgid "Playback device is %s\n"
msgstr "Wiedergabe-Gerät ist %s\n"
-#: speaker-test/speaker-test.c:891
+#: speaker-test/speaker-test.c:982
#, c-format
msgid "Stream parameters are %iHz, %s, %i channels\n"
msgstr "Stream-Parameter sind %i Hz, %s, %i Kanäle\n"
-#: speaker-test/speaker-test.c:894
+#: speaker-test/speaker-test.c:985
#, c-format
msgid "Using 16 octaves of pink noise\n"
msgstr "Verwende 16 Oktaven rosa Rauschen\n"
-#: speaker-test/speaker-test.c:897
+#: speaker-test/speaker-test.c:988
#, c-format
msgid "Sine wave rate is %.4fHz\n"
msgstr "Sinuswelle mit Frequenz %.4f Hz\n"
-#: speaker-test/speaker-test.c:900
+#: speaker-test/speaker-test.c:991
#, c-format
msgid "WAV file(s)\n"
msgstr "WAV-Datei(en)\n"
-#: speaker-test/speaker-test.c:906
+#: speaker-test/speaker-test.c:997
#, c-format
msgid "Playback open error: %d,%s\n"
msgstr "Fehler beim Öffnen des Gerätes: %d, %s\n"
-#: speaker-test/speaker-test.c:911
+#: speaker-test/speaker-test.c:1002
#, c-format
msgid "Setting of hwparams failed: %s\n"
msgstr "Fehler beim Setzen der Hardware-Parameter: %s\n"
-#: speaker-test/speaker-test.c:916
+#: speaker-test/speaker-test.c:1007
#, c-format
msgid "Setting of swparams failed: %s\n"
msgstr "Fehler beim Setzen der Software-Parameter: %s\n"
-#: speaker-test/speaker-test.c:957 speaker-test/speaker-test.c:979
+#: speaker-test/speaker-test.c:1056 speaker-test/speaker-test.c:1078
#, c-format
msgid "Transfer failed: %s\n"
msgstr "Schreibfehler: %s\n"
-#: speaker-test/speaker-test.c:967
+#: speaker-test/speaker-test.c:1066
#, c-format
msgid "Time per period = %lf\n"
msgstr "Zeit pro Periode = %lf\n"
diff --git a/po/fr.po b/po/fr.po
new file mode 100644
index 0000000..a6028a4
--- /dev/null
+++ b/po/fr.po
@@ -0,0 +1,1566 @@
+# French translation for alsa-utils
+# Copyright (C) 2011 The ALSA Team
+# This file is distributed under the same license as the alsa-utils package.
+# Christoph J. Thompson <cjsthompson@gmail.com>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: alsa-utils 1.0.23\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-01-07 06:23+0100\n"
+"PO-Revision-Date: 2011-01-07 06:23+0100\n"
+"Last-Translator: Christoph J. Thompson <cjsthompson@gmail.com>\n"
+"Language-Team: French <fr@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../alsamixer/card_select.c:126 ../alsamixer/device_name.c:126
+msgid "Sound Card"
+msgstr "Carte Son"
+
+#: ../alsamixer/card_select.c:181
+msgid "(default)"
+msgstr "(par défaut)"
+
+#: ../alsamixer/card_select.c:191
+msgid "cannot enumerate sound cards"
+msgstr "les cartes son n'ont pas pu être énumérées"
+
+#: ../alsamixer/card_select.c:215
+msgid "enter device name..."
+msgstr "entrez le nom du périphérique..."
+
+#: ../alsamixer/cli.c:40
+msgid "Usage: alsamixer [options]"
+msgstr "Utilisation: alsamixer [options]"
+
+#: ../alsamixer/cli.c:41
+msgid ""
+"Useful options:\n"
+" -h, --help this help\n"
+" -c, --card=NUMBER sound card number or id\n"
+" -D, --device=NAME mixer device name\n"
+" -V, --view=MODE starting view mode: playback/capture/all"
+msgstr ""
+"Options utiles:\n"
+" -h, --help cette aide\n"
+" -c, --card=NUMÉRO numéro ou identifiant de la carte son\n"
+" -D, --device=NOM nom du périphérique de mixage\n"
+" -V, --view=MODE mode de visualisation par défaut: lecture/capture/tous"
+
+#: ../alsamixer/cli.c:46
+msgid ""
+"Debugging options:\n"
+" -g, --no-color toggle using of colors\n"
+" -a, --abstraction=NAME mixer abstraction level: none/basic"
+msgstr ""
+"Options de débogage:\n"
+" -g, --no-color supprimer la couleur\n"
+" -a, --abstraction=NOM niveau d'abstraction du mixeur: aucun/basique"
+
+#: ../alsamixer/cli.c:77
+#, c-format
+msgid "invalid card index: %s\n"
+msgstr "index de carte invalide: %s\n"
+
+#: ../alsamixer/cli.c:103
+#, c-format
+msgid "unknown abstraction level: %s\n"
+msgstr "niveau d'abstraction inconnu: %s\n"
+
+#: ../alsamixer/cli.c:108
+#, c-format
+msgid "unknown option: %c\n"
+msgstr "option inconnue: %c\n"
+
+#: ../alsamixer/cli.c:110
+msgid "try `alsamixer --help' for more information\n"
+msgstr "essayez `alsamixer --help' pour plus d'information\n"
+
+#: ../alsamixer/device_name.c:177
+msgid "Device name:"
+msgstr "Nom du périphérique:"
+
+#: ../alsamixer/die.c:37
+#, c-format
+msgid "%s: %s\n"
+msgstr "%s: %s\n"
+
+#: ../alsamixer/mixer_display.c:95
+msgid "Card:"
+msgstr "Carte:"
+
+#: ../alsamixer/mixer_display.c:96
+msgid "Chip:"
+msgstr "Puce:"
+
+#: ../alsamixer/mixer_display.c:97
+msgid "View:"
+msgstr "Vue:"
+
+#: ../alsamixer/mixer_display.c:98
+msgid "Item:"
+msgstr "Contrôle:"
+
+#: ../alsamixer/mixer_display.c:101
+msgid "F1: Help"
+msgstr "F1: Aide"
+
+#: ../alsamixer/mixer_display.c:102
+msgid "F2: System information"
+msgstr "F2: Informations Système"
+
+#: ../alsamixer/mixer_display.c:103
+msgid "F6: Select sound card"
+msgstr "F6: Choisir la carte son"
+
+#: ../alsamixer/mixer_display.c:104
+msgid "Esc: Exit"
+msgstr "Esc: Quitter"
+
+#: ../alsamixer/mixer_display.c:171
+msgid "(unplugged)"
+msgstr "(non branché)"
+
+#: ../alsamixer/mixer_display.c:189
+msgid "Playback"
+msgstr "Lecture"
+
+#: ../alsamixer/mixer_display.c:190
+msgid "Capture"
+msgstr "Capture"
+
+#: ../alsamixer/mixer_display.c:191
+msgid "All"
+msgstr "Tout"
+
+#: ../alsamixer/mixer_display.c:231
+msgid "mute"
+msgstr "muet"
+
+#: ../alsamixer/mixer_display.c:272 ../alsamixer/mixer_display.c:282
+msgid "dB gain:"
+msgstr "gain dB:"
+
+#: ../alsamixer/mixer_display.c:282
+#, c-format
+msgid " [%s %s, %s]"
+msgstr " [%s %s, %s]"
+
+#: ../alsamixer/mixer_display.c:291 ../alsamixer/mixer_display.c:297
+#: ../alsamixer/mixer_display.c:303 ../alsamixer/mixer_display.c:309
+msgid "Off"
+msgstr "Fermé"
+
+#: ../alsamixer/mixer_display.c:297 ../alsamixer/mixer_display.c:309
+msgid "On"
+msgstr "Ouvert"
+
+#: ../alsamixer/mixer_display.c:360
+msgid "The sound device was unplugged."
+msgstr "Le périphérique a été débranché"
+
+#: ../alsamixer/mixer_display.c:361
+msgid "Press F6 to select another sound card."
+msgstr "Appuyez sur F6 pour choisir une autre carte son."
+
+#: ../alsamixer/mixer_display.c:376
+msgid "This sound device does not have any playback controls."
+msgstr "Ce périphérique n'a pas de commandes de lecture."
+
+#: ../alsamixer/mixer_display.c:378
+msgid "This sound device does not have any capture controls."
+msgstr "Ce périphérique n'a pas de commandes de capture."
+
+#: ../alsamixer/mixer_display.c:380
+msgid "This sound device does not have any controls."
+msgstr "Ce périphérique n'a pas de commandes."
+
+#. TRANSLATORS: playback on; one character
+#: ../alsamixer/mixer_display.c:537 ../alsamixer/mixer_display.c:542
+msgid "O"
+msgstr "O"
+
+#. TRANSLATORS: playback muted; one character
+#: ../alsamixer/mixer_display.c:539 ../alsamixer/mixer_display.c:543
+msgid "M"
+msgstr "M"
+
+#. TRANSLATORS: "left"; no more than two characters
+#: ../alsamixer/mixer_display.c:557
+msgid "L"
+msgstr "G"
+
+#. TRANSLATORS: "right"; no more than two characters
+#: ../alsamixer/mixer_display.c:561
+msgid "R"
+msgstr "D"
+
+#. TRANSLATORS: no more than eight characters
+#: ../alsamixer/mixer_display.c:563
+msgid "CAPTURE"
+msgstr "CAPTURE"
+
+#: ../alsamixer/mixer_display.c:613
+msgid "Front"
+msgstr "Avant"
+
+#: ../alsamixer/mixer_display.c:616
+msgid "Rear"
+msgstr "Arrière"
+
+#. 4
+#: ../alsamixer/mixer_display.c:619 ../speaker-test/speaker-test.c:107
+msgid "Center"
+msgstr "Centre"
+
+#: ../alsamixer/mixer_display.c:622
+msgid "Woofer"
+msgstr "Basses"
+
+#: ../alsamixer/mixer_display.c:625
+msgid "Side"
+msgstr "Côté"
+
+#: ../alsamixer/mixer_widget.c:83 ../alsamixer/mixer_widget.c:88
+msgid "cannot open mixer"
+msgstr "le mixeur ne peut pas être ouvert"
+
+#: ../alsamixer/mixer_widget.c:94 ../alsamixer/mixer_widget.c:171
+msgid "cannot load mixer controls"
+msgstr "les commandes de mixage ne peuvent pas être chargés"
+
+#: ../alsamixer/mixer_widget.c:161
+#, c-format
+msgid "Cannot open mixer device '%s'."
+msgstr "Le périphérique de mixage '%s' ne peut pas être ouvert."
+
+#: ../alsamixer/mixer_widget.c:182
+msgid "Esc Exit"
+msgstr "Esc Quitter"
+
+#: ../alsamixer/mixer_widget.c:183
+msgid "F1 ? H Help"
+msgstr "F1 ? H Aide"
+
+#: ../alsamixer/mixer_widget.c:184
+msgid "F2 / System information"
+msgstr "F2 / Informations système"
+
+#: ../alsamixer/mixer_widget.c:185
+msgid "F3 Show playback controls"
+msgstr "F3 Afficher les commandes de lecture"
+
+#: ../alsamixer/mixer_widget.c:186
+msgid "F4 Show capture controls"
+msgstr "F4 Afficher les commandes de capture"
+
+#: ../alsamixer/mixer_widget.c:187
+msgid "F5 Show all controls"
+msgstr "F5 Afficher toutes les commandes"
+
+#: ../alsamixer/mixer_widget.c:188
+msgid "Tab Toggle view mode (F3/F4/F5)"
+msgstr "Tab Choisir le mode de visualisation (F3/F4/F5)"
+
+#: ../alsamixer/mixer_widget.c:189
+msgid "F6 S Select sound card"
+msgstr "F6 S Choisir la carte son"
+
+#: ../alsamixer/mixer_widget.c:190
+msgid "L Redraw screen"
+msgstr "L Actualiser l'écran"
+
+#: ../alsamixer/mixer_widget.c:192
+msgid "Left Move to the previous control"
+msgstr "Gauche Aller à la commande précédente"
+
+#: ../alsamixer/mixer_widget.c:193
+msgid "Right Move to the next control"
+msgstr "Droite Aller à la commande suivante"
+
+#: ../alsamixer/mixer_widget.c:195
+msgid "Up/Down Change volume"
+msgstr "Haut/Bas Ajuster le volume"
+
+#: ../alsamixer/mixer_widget.c:196
+msgid "+ - Change volume"
+msgstr "+ - Ajuster le volume"
+
+#: ../alsamixer/mixer_widget.c:197
+msgid "Page Up/Dn Change volume in big steps"
+msgstr "Page Préc./Suiv. Ajuster le volume en grandes intervalles"
+
+#: ../alsamixer/mixer_widget.c:198
+msgid "End Set volume to 0%"
+msgstr "Fin Couper le volume"
+
+#: ../alsamixer/mixer_widget.c:199
+msgid "0-9 Set volume to 0%-90%"
+msgstr "0-9 Ajuster le volume entre 0 et 90%"
+
+#: ../alsamixer/mixer_widget.c:200
+msgid "Q W E Increase left/both/right volumes"
+msgstr "Q W E Augmenter les volumes de gauche/centre/droite"
+
+#. TRANSLATORS: or Y instead of Z
+#: ../alsamixer/mixer_widget.c:202
+msgid "Z X C Decrease left/both/right volumes"
+msgstr "Z X C Baisser les volumes de gauche/centre/droite"
+
+#: ../alsamixer/mixer_widget.c:203
+msgid "B Balance left and right volumes"
+msgstr "B Égaliser les volumes de gauche et droite"
+
+#: ../alsamixer/mixer_widget.c:205
+msgid "M Toggle mute"
+msgstr "M (Dés)activer le mode muet"
+
+#. TRANSLATORS: or , .
+#: ../alsamixer/mixer_widget.c:207
+msgid "< > Toggle left/right mute"
+msgstr "< > (Dés)activer le mode muet à gauche et à droite"
+
+#: ../alsamixer/mixer_widget.c:209
+msgid "Space Toggle capture"
+msgstr "Espace (Dés)activer la capture"
+
+#. TRANSLATORS: or Insert Delete
+#: ../alsamixer/mixer_widget.c:211
+msgid "; ' Toggle left/right capture"
+msgstr "; (Dés)activer la capture à gauche et à droite"
+
+#: ../alsamixer/mixer_widget.c:213
+msgid "Authors:"
+msgstr "Auteurs:"
+
+#: ../alsamixer/mixer_widget.c:214
+msgid " Tim Janik <timj@gtk.org>"
+msgstr ""
+
+#: ../alsamixer/mixer_widget.c:215
+msgid " Jaroslav Kysela <perex@perex.cz>"
+msgstr ""
+
+#: ../alsamixer/mixer_widget.c:216
+msgid " Clemens Ladisch <clemens@ladisch.de>"
+msgstr ""
+
+#: ../alsamixer/mixer_widget.c:218
+msgid "Help"
+msgstr "Aide"
+
+#: ../alsamixer/proc_files.c:103
+msgid "Select File"
+msgstr "Choisir un Fichier"
+
+#: ../alsamixer/textbox.c:52 ../alsamixer/textbox.c:66
+msgid "Error"
+msgstr "Erreur"
+
+#: ../alsamixer/textbox.c:80
+#, c-format
+msgid "Cannot open file \"%s\"."
+msgstr "Le fichier \"%s\" n'a pas pu être ouvert."
+
+#: ../aplay/aplay.c:147
+msgid "raw data"
+msgstr "données brutes"
+
+#: ../aplay/aplay.c:148
+msgid "VOC"
+msgstr ""
+
+#. FIXME: can WAV handle exactly 2GB or less than it?
+#: ../aplay/aplay.c:150
+msgid "WAVE"
+msgstr ""
+
+#: ../aplay/aplay.c:151
+msgid "Sparc Audio"
+msgstr ""
+
+#: ../aplay/aplay.c:172
+#, c-format
+msgid ""
+"Usage: %s [OPTION]... [FILE]...\n"
+"\n"
+"-h, --help help\n"
+" --version print current version\n"
+"-l, --list-devices list all soundcards and digital audio devices\n"
+"-L, --list-pcms list device names\n"
+"-D, --device=NAME select PCM by name\n"
+"-q, --quiet quiet mode\n"
+"-t, --file-type TYPE file type (voc, wav, raw or au)\n"
+"-c, --channels=# channels\n"
+"-f, --format=FORMAT sample format (case insensitive)\n"
+"-r, --rate=# sample rate\n"
+"-d, --duration=# interrupt after # seconds\n"
+"-M, --mmap mmap stream\n"
+"-N, --nonblock nonblocking mode\n"
+"-F, --period-time=# distance between interrupts is # microseconds\n"
+"-B, --buffer-time=# buffer duration is # microseconds\n"
+" --period-size=# distance between interrupts is # frames\n"
+" --buffer-size=# buffer duration is # frames\n"
+"-A, --avail-min=# min available space for wakeup is # microseconds\n"
+"-R, --start-delay=# delay for automatic PCM start is # microseconds \n"
+" (relative to buffer size if <= 0)\n"
+"-T, --stop-delay=# delay for automatic PCM stop is # microseconds from "
+"xrun\n"
+"-v, --verbose show PCM structure and setup (accumulative)\n"
+"-V, --vumeter=TYPE enable VU meter (TYPE: mono or stereo)\n"
+"-I, --separate-channels one file for each channel\n"
+" --disable-resample disable automatic rate resample\n"
+" --disable-channels disable automatic channel conversions\n"
+" --disable-format disable automatic format conversions\n"
+" --disable-softvol disable software volume control (softvol)\n"
+" --test-position test ring buffer position\n"
+" --test-coef=#\t test coeficient for ring buffer position (default 8)\n"
+" expression for validation is: coef * (buffer_size / "
+"2)\n"
+" --test-nowait do not wait for ring buffer - eats whole CPU\n"
+" --max-file-time=# start another output file when the old file has "
+"recorded\n"
+" for this many seconds\n"
+" --process-id-file write the process ID here\n"
+" --use-strftime apply the strftime facility to the output file name\n"
+msgstr ""
+"Utilisation: %s [OPTION]... [FICHIER]...\n"
+"\n"
+"-h, --help aide\n"
+" --version afficher la version du programme\n"
+"-l, --list-devices afficher la listes des périphériques\n"
+"-L, --list-pcms afficher la liste des noms de périphériques\n"
+"-D, --device=NOM choisir le périphérique PCM par son nom\n"
+"-Q, --quiet mode silencieux\n"
+"-t, --file-type TYPE type de fichier (voc, wav, raw ou au)\n"
+"-c, --channels=# canaux\n"
+"-f, --format=FORMAT format d'échantillonage (insensible à la casse)\n"
+"-r, --rate=# fréquence d'échantillonage\n"
+"-d, --duration=# interrompre après # secondes\n"
+"-M, --mmap charger le flux en mémoire avec mmap\n"
+"-N, --nonblock mode non-bloquant\n"
+"-F, --period-time=# # microsecondes entre chaque interruption\n"
+"-B, --buffer-time=# la durée du tampon est de # microsecondes\n"
+" --period-size=# # trames entre chaque interruption\n"
+" --buffer-size=# la durée du tampon est de # trames\n"
+"-A, --avail-min=# # microsecondes min. disponibles entre chaque réveil\n"
+"-R, --start-delay=# # microsecondes de délai avant le démarrage\n"
+" automatique du périphérique PCM (relatif à la taille\n"
+" du tampon si <= 0)\n"
+"-T, --stop-delay=# # microsecondes de délai depuis xrun avant l'arrêt\n"
+" automatique du périphérique PCM \n"
+"-v, --verbose afficher la structure et la configuration du\n"
+" périphérique PCM (accumulatif)\n"
+"-V, --vumeter=TYPE activer le VU-mètre (TYPE: mono ou stereo)\n"
+"-I, --separate-channels un fichier par canal\n"
+" --disable-resample désactiver le rééchantillonage de fréquence\n"
+" automatique\n"
+" --disable-channels désactiver les conversions automatiques de canal\n"
+" --disable-format désactiver les conversions automatiques de format\n"
+" --disable-softvol désactiver la commande du volume logicielle\n"
+" --test-position tester la position du tampon circulaire\n"
+" --test-coef=# test coefficient pour la position du tampon\n"
+" circulaire (par défaut 8). L'expression pour la\n"
+" validation est : coef * (taille_tampon / 2)\n"
+" --test-nowait ne pas attendre le tampon circulaire - prend toutes\n"
+" les resources du processeur\n"
+" --max-file-time=# commencer un autre fichier de capture quand l'ancien\n"
+" fichier a enregistré pendant # secondes\n"
+" --process-id-file écrire l'identifiant du processus ici\n"
+" --use-strftime utiliser strftime pour le nom du fichier de capture\n"
+
+#: ../aplay/aplay.c:211 ../speaker-test/speaker-test.c:750
+#, c-format
+msgid "Recognized sample formats are:"
+msgstr "Les formats d'échantillonage connus sont:"
+
+#: ../aplay/aplay.c:217
+#, c-format
+msgid ""
+"\n"
+"Some of these may not be available on selected hardware\n"
+msgstr ""
+"\n"
+"Certains de ceux-ci ne sont peut être pas disponibles avec ce matériel\n"
+
+#: ../aplay/aplay.c:218
+#, c-format
+msgid "The availabled format shortcuts are:\n"
+msgstr "Les raccourcis format disponibles sont:\n"
+
+#: ../aplay/aplay.c:219
+#, c-format
+msgid "-f cd (16 bit little endian, 44100, stereo)\n"
+msgstr "-f cd (16 bit petit boutiste, 44100, stéréo)\n"
+
+#: ../aplay/aplay.c:220
+#, c-format
+msgid "-f cdr (16 bit big endian, 44100, stereo)\n"
+msgstr "-f cdr (16 bit gros boutiste, 44100, stéréo)\n"
+
+#: ../aplay/aplay.c:221
+#, c-format
+msgid "-f dat (16 bit little endian, 48000, stereo)\n"
+msgstr "-f dat (16 bit petit boutiste, 48000, stéréo)\n"
+
+#: ../aplay/aplay.c:235
+msgid "no soundcards found..."
+msgstr "aucune carte son n'a été trouvée..."
+
+#: ../aplay/aplay.c:238
+#, c-format
+msgid "**** List of %s Hardware Devices ****\n"
+msgstr "**** Liste des Périphériques Matériels %s ****\n"
+
+#: ../aplay/aplay.c:267
+#, c-format
+msgid "card %i: %s [%s], device %i: %s [%s]\n"
+msgstr "carte %i: %s [%s], périphérique %i: %s [%s]\n"
+
+#: ../aplay/aplay.c:273
+#, c-format
+msgid " Subdevices: %i/%i\n"
+msgstr " Sous-périphériques: %i/%i\n"
+
+#: ../aplay/aplay.c:280
+#, c-format
+msgid " Subdevice #%i: %s\n"
+msgstr " Sous-périphérique #%i: %s\n"
+
+#: ../aplay/aplay.c:356
+#, c-format
+msgid "Aborted by signal %s...\n"
+msgstr "Interrompu par le signal %s...\n"
+
+#: ../aplay/aplay.c:467
+msgid "command should be named either arecord or aplay"
+msgstr "la commande doit être nommée arecord ou aplay"
+
+#: ../aplay/aplay.c:506
+#, c-format
+msgid "unrecognized file format %s"
+msgstr "format de fichier inconnu %s"
+
+#: ../aplay/aplay.c:513
+#, c-format
+msgid "value %i for channels is invalid"
+msgstr "la valeur %i pour les canaux est incorrecte"
+
+#: ../aplay/aplay.c:532
+#, c-format
+msgid "wrong extended format '%s'"
+msgstr "format étendu '%s' erronné"
+
+#: ../aplay/aplay.c:543
+#, c-format
+msgid "bad speed value %i"
+msgstr "mauvaise valeur de vitesse %i"
+
+#: ../aplay/aplay.c:638
+#, c-format
+msgid "Try `%s --help' for more information.\n"
+msgstr "Essayez `%s --help' pour plus d'informations.\n"
+
+#: ../aplay/aplay.c:654
+#, c-format
+msgid "audio open error: %s"
+msgstr "erreur à l'ouverture audio: %s"
+
+#: ../aplay/aplay.c:659
+#, c-format
+msgid "info error: %s"
+msgstr "erreur info: %s"
+
+#: ../aplay/aplay.c:666
+#, c-format
+msgid "nonblock setting error: %s"
+msgstr "paramètre de non-blocage erronné: %s"
+
+#: ../aplay/aplay.c:676 ../aplay/aplay.c:800 ../aplay/aplay.c:1167
+msgid "not enough memory"
+msgstr "pas assez de mémoire"
+
+#: ../aplay/aplay.c:700
+#, c-format
+msgid "Cannot create process ID file %s: %s"
+msgstr "Le fichier de numéro de processus %s n'a pas pu être créé: %s"
+
+#: ../aplay/aplay.c:790
+#, c-format
+msgid "read error (called from line %i)"
+msgstr "erreur de lecture (appelé à la ligne %i)"
+
+#: ../aplay/aplay.c:848
+#, c-format
+msgid "unknown length of 'fmt ' chunk (read %u, should be %u at least)"
+msgstr "longueur du morceau 'fmt ' inconnue (lu %u, devrait être au moins %u)"
+
+#: ../aplay/aplay.c:858
+#, c-format
+msgid ""
+"unknown length of extensible 'fmt ' chunk (read %u, should be %u at least)"
+msgstr ""
+"longueur du morceau 'fmt ' extensible inconnue (lu %u, devrait être au moins %u)"
+
+#: ../aplay/aplay.c:863
+msgid "wrong format tag in extensible 'fmt ' chunk"
+msgstr "libellé de format erronné dans le morceau extensible 'fmt '"
+
+#: ../aplay/aplay.c:870
+#, c-format
+msgid "can't play WAVE-file format 0x%04x which is not PCM or FLOAT encoded"
+msgstr "Les formats de ficher WAVE 0x%04x qui ne sont pas encodés en PCM ou"
+ "FLOAT ne sont pas supportés"
+
+#: ../aplay/aplay.c:874
+#, c-format
+msgid "can't play WAVE-files with %d tracks"
+msgstr "Les fichiers WAVE avec %d pistes ne sont pas supportés"
+
+#: ../aplay/aplay.c:882 ../aplay/aplay.c:982
+#, c-format
+msgid "Warning: format is changed to U8\n"
+msgstr "Avertissement: le format est changé en U8\n"
+
+#: ../aplay/aplay.c:888
+#, c-format
+msgid "Warning: format is changed to S16_LE\n"
+msgstr "Avertissement: le format est changé en S16_LE\n"
+
+#: ../aplay/aplay.c:896
+#, c-format
+msgid "Warning: format is changed to S24_3LE\n"
+msgstr "Avertissement: le format est changé en S24_3LE\n"
+
+#: ../aplay/aplay.c:902
+#, c-format
+msgid "Warning: format is changed to S24_LE\n"
+msgstr "Avertissement: le format est changé en S24_LE\n"
+
+#: ../aplay/aplay.c:906
+#, c-format
+msgid ""
+" can't play WAVE-files with sample %d bits in %d bytes wide (%d channels)"
+msgstr ""
+" les fichiers WAVE échantillonés %d bits par %d octets en largeur (%d canaux)"
+"ne sont pas supportés"
+
+#: ../aplay/aplay.c:918
+#, c-format
+msgid " can't play WAVE-files with sample %d bits wide"
+msgstr " les fichiers WAVES échantillonés %d bits en largeur ne sont pas"
+"supportés"
+
+#: ../aplay/aplay.c:976
+#, c-format
+msgid "Warning: format is changed to MU_LAW\n"
+msgstr "Avertissement: le format est changé en MU_LAW\n"
+
+#: ../aplay/aplay.c:988
+#, c-format
+msgid "Warning: format is changed to S16_BE\n"
+msgstr "Avertissement: le format est changé en S16_BE\n"
+
+#: ../aplay/aplay.c:1001 ../aplay/aplay.c:1843 ../aplay/aplay.c:1850
+#: ../aplay/aplay.c:2372 ../aplay/aplay.c:2384
+msgid "read error"
+msgstr "erreur de lecture"
+
+#: ../aplay/aplay.c:1031
+msgid "Broken configuration for this PCM: no configurations available"
+msgstr "Configuration erronée pour ce périphérique PCM: pas de configuration"
+
+#: ../aplay/aplay.c:1048
+msgid "Access type not available"
+msgstr "Ce type d'accès n'est pas disponible"
+
+#: ../aplay/aplay.c:1053
+msgid "Sample format non available"
+msgstr "Ce format d'échantillonage n'est pas disponible"
+
+#: ../aplay/aplay.c:1059
+msgid "Channels count non available"
+msgstr "Nombre de canaux non disponible"
+
+#: ../aplay/aplay.c:1074
+#, c-format
+msgid "Warning: rate is not accurate (requested = %iHz, got = %iHz)\n"
+msgstr "Avertissement: la fréquence n'est pas précise (demandé = %iHz,\n"
+ "obtenu = %iHz)\n"
+
+#: ../aplay/aplay.c:1080
+#, c-format
+msgid " please, try the plug plugin %s\n"
+msgstr " veuillez essayez le greffon de branchement %s\n"
+
+#: ../aplay/aplay.c:1116
+msgid "Unable to install hw params:"
+msgstr "Les paramètres matériel n'ont pas pu être installés:"
+
+#: ../aplay/aplay.c:1123
+#, c-format
+msgid "Can't use period equal to buffer size (%lu == %lu)"
+msgstr "Une période égale à la taille du tampon (%lu == %lu) ne peut être"
+ "utilisée"
+
+#: ../aplay/aplay.c:1154
+msgid "unable to install sw params:"
+msgstr "Les paramètres logiciel n'ont pas pu être installés:"
+
+#: ../aplay/aplay.c:1229
+#, c-format
+msgid "status error: %s"
+msgstr "erreur d'état: %s"
+
+#: ../aplay/aplay.c:1239 ../aplay/aplay.c:1250
+#, c-format
+msgid "%s!!! (at least %.3f ms long)\n"
+msgstr "%s!!! (au moins longue de %.3f ms)\n"
+
+#: ../aplay/aplay.c:1240 ../aplay/aplay.c:1243 ../aplay/aplay.c:1251
+msgid "underrun"
+msgstr "perte"
+
+#: ../aplay/aplay.c:1240 ../aplay/aplay.c:1251
+msgid "overrun"
+msgstr "dépassement"
+
+#: ../aplay/aplay.c:1255
+#, c-format
+msgid "Status:\n"
+msgstr "État:\n"
+
+#: ../aplay/aplay.c:1259
+#, c-format
+msgid "xrun: prepare error: %s"
+msgstr ""
+
+#: ../aplay/aplay.c:1265
+#, c-format
+msgid "Status(DRAINING):\n"
+msgstr ""
+
+#: ../aplay/aplay.c:1269
+#, c-format
+msgid "capture stream format change? attempting recover...\n"
+msgstr "changement de format de flux de capture? tentative de récupération...\n"
+
+#: ../aplay/aplay.c:1271
+#, c-format
+msgid "xrun(DRAINING): prepare error: %s"
+msgstr ""
+
+#: ../aplay/aplay.c:1278
+#, c-format
+msgid "Status(R/W):\n"
+msgstr ""
+
+#: ../aplay/aplay.c:1281
+#, c-format
+msgid "read/write error, state = %s"
+msgstr "erreur de lecture/écriture, état = %s"
+
+#: ../aplay/aplay.c:1291
+#, c-format
+msgid "Suspended. Trying resume. "
+msgstr "Suspendu. Tentative de reprise. "
+
+#: ../aplay/aplay.c:1296
+#, c-format
+msgid "Failed. Restarting stream. "
+msgstr "Échec. Redémarrage du flux."
+
+#: ../aplay/aplay.c:1298
+#, c-format
+msgid "suspend: prepare error: %s"
+msgstr "suspension: prepare error: %s"
+
+#: ../aplay/aplay.c:1303
+#, c-format
+msgid "Done.\n"
+msgstr "Terminé.\n"
+
+#: ../aplay/aplay.c:1325
+#, c-format
+msgid " !clip "
+msgstr ""
+
+#: ../aplay/aplay.c:1472
+#, c-format
+msgid "Unsupported bit size %d.\n"
+msgstr "%d bit(s) non supporté(s).\n"
+
+#: ../aplay/aplay.c:1506
+#, c-format
+msgid "Max peak (%li samples): 0x%08x "
+msgstr "Pic max. (%li échantillons): 0x%08x "
+
+#: ../aplay/aplay.c:1540
+#, c-format
+msgid ""
+"Suspicious buffer position (%li total): avail = %li, delay = %li, buffer = %"
+"li\n"
+msgstr ""
+"Position de tampon suspicieuse (total %li): dispo = %li, délai = %li, tampon ="
+" %li\n"
+
+#: ../aplay/aplay.c:1603
+#, c-format
+msgid "write error: %s"
+msgstr "erreur en écriture: %s"
+
+#: ../aplay/aplay.c:1649
+#, c-format
+msgid "writev error: %s"
+msgstr "erreur de writev: %s"
+
+#: ../aplay/aplay.c:1692
+#, c-format
+msgid "read error: %s"
+msgstr "erreur en lecture: %s"
+
+#: ../aplay/aplay.c:1735
+#, c-format
+msgid "readv error: %s"
+msgstr "erreur de readv: %s"
+
+#: ../aplay/aplay.c:1783
+msgid "can't allocate buffer for silence"
+msgstr "Un tampon pour silence n'a pas pu être alloué"
+
+#. to stderr
+#: ../aplay/aplay.c:1792 ../aplay/aplay.c:2018 ../aplay/aplay.c:2023
+#: ../aplay/aplay.c:2070 ../aplay/aplay.c:2079 ../aplay/aplay.c:2086
+#: ../aplay/aplay.c:2096 ../aplay/aplay.c:2102 ../aplay/aplay.c:2174
+#: ../aplay/aplay.c:2204 ../aplay/aplay.c:2218
+msgid "write error"
+msgstr "erreur en écriture"
+
+#: ../aplay/aplay.c:1805
+#, c-format
+msgid "voc_pcm_flush - silence error"
+msgstr "voc_pcm_flush - erreur de silence"
+
+#: ../aplay/aplay.c:1808
+msgid "voc_pcm_flush error"
+msgstr "erreur voc_pcm_flush"
+
+#: ../aplay/aplay.c:1834
+msgid "malloc error"
+msgstr "erreur malloc"
+
+#: ../aplay/aplay.c:1838
+#, c-format
+msgid "Playing Creative Labs Channel file '%s'...\n"
+msgstr "Lecture du ficher de canaux Creative Labs '%s'...\n"
+
+#. /dev/dsp can't it
+#: ../aplay/aplay.c:1906 ../aplay/aplay.c:1998
+msgid "can't play packed .voc files"
+msgstr "les fichiers .voc de type 'packed' ne sont pas supportés"
+
+#: ../aplay/aplay.c:1958
+#, c-format
+msgid "can't play loops; %s isn't seekable\n"
+msgstr "%s n'est pas navigable; les motifs n'ont pas pu être lus\n"
+
+#: ../aplay/aplay.c:2007
+#, c-format
+msgid "unknown blocktype %d. terminate."
+msgstr "type de bloc %d inconnu. fin."
+
+#: ../aplay/aplay.c:2138
+#, c-format
+msgid "Wave doesn't support %s format..."
+msgstr "Le format wave %s n'est pas supporté..."
+
+#: ../aplay/aplay.c:2198
+#, c-format
+msgid "Sparc Audio doesn't support %s format..."
+msgstr "Le format %s Sparc Audio n'est pas supporté..."
+
+#: ../aplay/aplay.c:2279
+msgid "Playing"
+msgstr "Lecture"
+
+#: ../aplay/aplay.c:2279
+msgid "Recording"
+msgstr "Capture"
+
+#: ../aplay/aplay.c:2283
+#, c-format
+msgid "Rate %d Hz, "
+msgstr "Fréquence %d Hz, "
+
+#: ../aplay/aplay.c:2285
+#, c-format
+msgid "Mono"
+msgstr ""
+
+#: ../aplay/aplay.c:2287
+#, c-format
+msgid "Stereo"
+msgstr "Stéréo"
+
+#: ../aplay/aplay.c:2289
+#, c-format
+msgid "Channels %i"
+msgstr "%i Canaux"
+
+#: ../aplay/aplay.c:2798 ../aplay/aplay.c:2851
+#, c-format
+msgid "You need to specify %d files"
+msgstr "Vous devez spécifier %d fichiers"
+
+#: ../seq/aconnect/aconnect.c:49
+#, c-format
+msgid "aconnect - ALSA sequencer connection manager\n"
+msgstr "aconnect - gestionnaire de connection séquenceur ALSA\n"
+
+#: ../seq/aconnect/aconnect.c:50
+#, c-format
+msgid "Copyright (C) 1999-2000 Takashi Iwai\n"
+msgstr ""
+
+#: ../seq/aconnect/aconnect.c:51
+#, c-format
+msgid "Usage:\n"
+msgstr "Utilisation:\n"
+
+#: ../seq/aconnect/aconnect.c:52
+#, c-format
+msgid " * Connection/disconnection between two ports\n"
+msgstr " * Connexion/déconnexion entre deux ports\n"
+
+#: ../seq/aconnect/aconnect.c:53
+#, c-format
+msgid " aconnect [-options] sender receiver\n"
+msgstr " aconnect [-options] envoyeur destinataire\n"
+
+#: ../seq/aconnect/aconnect.c:54
+#, c-format
+msgid " sender, receiver = client:port pair\n"
+msgstr " envoyeur, destinataire = couple client:port\n"
+
+#: ../seq/aconnect/aconnect.c:55
+#, c-format
+msgid " -d,--disconnect disconnect\n"
+msgstr " -d,--disconnect déconnecter\n"
+
+#: ../seq/aconnect/aconnect.c:56
+#, c-format
+msgid " -e,--exclusive exclusive connection\n"
+msgstr " -e,--exclusive connexion exclusive\n"
+
+#: ../seq/aconnect/aconnect.c:57
+#, c-format
+msgid " -r,--real # convert real-time-stamp on queue\n"
+msgstr ""
+
+#: ../seq/aconnect/aconnect.c:58
+#, c-format
+msgid " -t,--tick # convert tick-time-stamp on queue\n"
+msgstr ""
+
+#: ../seq/aconnect/aconnect.c:59
+#, c-format
+msgid " * List connected ports (no subscription action)\n"
+msgstr " * Afficher la liste des ports connectés (pas de souscription)\n"
+
+#: ../seq/aconnect/aconnect.c:60
+#, c-format
+msgid " aconnect -i|-o [-options]\n"
+msgstr " aconnect -i|-o [-options]\n"
+
+#: ../seq/aconnect/aconnect.c:61
+#, c-format
+msgid " -i,--input list input (readable) ports\n"
+msgstr " -i,--input afficher la liste des ports d'entrée\n"
+
+#: ../seq/aconnect/aconnect.c:62
+#, c-format
+msgid " -o,--output list output (writable) ports\n"
+msgstr " -o,--output afficher la liste des ports de sortie\n"
+
+#: ../seq/aconnect/aconnect.c:63
+#, c-format
+msgid " -l,--list list current connections of each port\n"
+msgstr " -l,--list afficher la liste des connexions en cours\n"
+ " sur chaque port\n"
+
+#: ../seq/aconnect/aconnect.c:64
+#, c-format
+msgid " * Remove all exported connections\n"
+msgstr " * Retirer toutes les connexions exportées\n"
+
+#: ../seq/aconnect/aconnect.c:65
+#, c-format
+msgid " -x, --removeall\n"
+msgstr " -x, --removeall\n"
+
+#: ../seq/aconnect/aconnect.c:132
+msgid "Connecting To"
+msgstr "Connexion À"
+
+#: ../seq/aconnect/aconnect.c:133
+msgid "Connected From"
+msgstr "Connecté Depuis"
+
+#: ../seq/aconnect/aconnect.c:169
+#, c-format
+msgid "client %d: '%s' [type=%s]\n"
+msgstr "client %d: '%s' [type=%s]\n"
+
+#: ../seq/aconnect/aconnect.c:173
+msgid "user"
+msgstr "utilisateur"
+
+#: ../seq/aconnect/aconnect.c:173
+msgid "kernel"
+msgstr "noyau"
+
+#: ../seq/aconnect/aconnect.c:307
+#, c-format
+msgid "can't open sequencer\n"
+msgstr "le séquenceur n'a pas pu être ouvert\n"
+
+#: ../seq/aconnect/aconnect.c:335
+#, c-format
+msgid "can't get client id\n"
+msgstr "l'identifiant du client n'as pas pu être obtenu\n"
+
+#: ../seq/aconnect/aconnect.c:342
+#, c-format
+msgid "can't set client info\n"
+msgstr "les infos du client n'ont pas pu être définies\n"
+
+#: ../seq/aconnect/aconnect.c:349
+#, c-format
+msgid "invalid sender address %s\n"
+msgstr "l'addresse envoyeur %s est incorrecte\n"
+
+#: ../seq/aconnect/aconnect.c:354 ../seq/aseqnet/aseqnet.c:290
+#, c-format
+msgid "invalid destination address %s\n"
+msgstr "l'addresse destinataire %s est incorrecte\n"
+
+#: ../seq/aconnect/aconnect.c:368
+#, c-format
+msgid "No subscription is found\n"
+msgstr "Aucune souscription trouvée\n"
+
+#: ../seq/aconnect/aconnect.c:373
+#, c-format
+msgid "Disconnection failed (%s)\n"
+msgstr "La déconnexion a échoué (%s)\n"
+
+#: ../seq/aconnect/aconnect.c:379
+#, c-format
+msgid "Connection is already subscribed\n"
+msgstr "La connexion a déjà été souscrite\n"
+
+#: ../seq/aconnect/aconnect.c:384
+#, c-format
+msgid "Connection failed (%s)\n"
+msgstr "Échec de connexion (%s)\n"
+
+#: ../seq/aseqnet/aseqnet.c:164
+#, c-format
+msgid "aseqnet - network client/server on ALSA sequencer\n"
+msgstr "aseqnet - client/serveur réseau sur le séquenceur ALSA\n"
+
+#: ../seq/aseqnet/aseqnet.c:165
+#, c-format
+msgid " Copyright (C) 1999 Takashi Iwai\n"
+msgstr ""
+
+#: ../seq/aseqnet/aseqnet.c:166
+#, c-format
+msgid "usage:\n"
+msgstr "utilisation:\n"
+
+#: ../seq/aseqnet/aseqnet.c:167
+#, c-format
+msgid " server mode: aseqnet [-options]\n"
+msgstr " mode serveur: aseqnet [-options]\n"
+
+#: ../seq/aseqnet/aseqnet.c:168
+#, c-format
+msgid " client mode: aseqnet [-options] server_host\n"
+msgstr " mode client: aseqnet [-options] hôte_serveur\n"
+
+#: ../seq/aseqnet/aseqnet.c:169
+#, c-format
+msgid "options:\n"
+msgstr "options:\n"
+
+#: ../seq/aseqnet/aseqnet.c:170
+#, c-format
+msgid " -p,--port # : sepcify TCP port (digit or service name)\n"
+msgstr " -p,--port # : spécifier le port TCP (numéro ou nom de service)\n"
+
+#: ../seq/aseqnet/aseqnet.c:171
+#, c-format
+msgid " -s,--source addr : read from given addr (client:port)\n"
+msgstr " -s,--source addr : lire à partir de l'addr. donnée (client:port)\n"
+
+#: ../seq/aseqnet/aseqnet.c:172
+#, c-format
+msgid " -d,--dest addr : write to given addr (client:port)\n"
+msgstr " -d,--dest addr : écrire à l'addr. donnée (client:port)\n"
+
+#: ../seq/aseqnet/aseqnet.c:173
+#, c-format
+msgid " -v, --verbose : print verbose messages\n"
+msgstr " -v, --verbose : affichage locace de messages\n"
+
+#: ../seq/aseqnet/aseqnet.c:174
+#, c-format
+msgid " -i, --info : print certain received events\n"
+msgstr " -i, --info : afficher certains évènements reçus\n"
+
+#: ../seq/aseqnet/aseqnet.c:188
+#, c-format
+msgid "can't malloc\n"
+msgstr "échec allocation mémoire (malloc)\n"
+
+#: ../seq/aseqnet/aseqnet.c:213
+#, c-format
+msgid "closing files..\n"
+msgstr "fermeture des fichiers...\n"
+
+#: ../seq/aseqnet/aseqnet.c:272
+#, c-format
+msgid "sequencer opened: %d:%d\n"
+msgstr "séquenceur ouvert: %d:%d\n"
+
+#: ../seq/aseqnet/aseqnet.c:279
+#, c-format
+msgid "invalid source address %s\n"
+msgstr "addresse source %s incorrecte\n"
+
+#: ../seq/aseqnet/aseqnet.c:309
+#, c-format
+msgid "service '%s' is not found in /etc/services\n"
+msgstr "le service '%s' n'as pas été trouvé dans /etc/services\n"
+
+#: ../seq/aseqnet/aseqnet.c:377
+#, c-format
+msgid "too many connections!\n"
+msgstr "trop de connexions!\n"
+
+#: ../seq/aseqnet/aseqnet.c:388
+#, c-format
+msgid "accepted[%d]\n"
+msgstr "accepté[%d]\n"
+
+#: ../seq/aseqnet/aseqnet.c:411
+#, c-format
+msgid "can't get address %s\n"
+msgstr "l'addresse %s na pas pu être obtenue\n"
+
+#: ../seq/aseqnet/aseqnet.c:422
+#, c-format
+msgid "ok.. connected\n"
+msgstr "ok.. connecté\n"
+
+#: ../seq/aseqnet/aseqnet.c:518
+#, c-format
+msgid "Channel %2d: Control event : %5d\n"
+msgstr "Canal %2d: Évènement de contrôle: %5d\n"
+
+#: ../seq/aseqnet/aseqnet.c:522
+#, c-format
+msgid "Channel %2d: Pitchbender : %5d\n"
+msgstr "Canal %2d: Pitchbender : %5d\n"
+
+#: ../seq/aseqnet/aseqnet.c:526
+#, c-format
+msgid "Channel %2d: Note On event : %5d\n"
+msgstr "Canal %2d: évènement Note On : %5d\n"
+
+#: ../seq/aseqnet/aseqnet.c:530
+#, c-format
+msgid "Channel %2d: Note Off event: %5d\n"
+msgstr "Canal %2d: évènement Note Off : %5d\n"
+
+#: ../seq/aseqnet/aseqnet.c:585
+#, c-format
+msgid "disconnected\n"
+msgstr "déconnecté\n"
+
+#. 0
+#: ../speaker-test/speaker-test.c:103
+msgid "Front Left"
+msgstr "Avant Gauche"
+
+#. 1
+#: ../speaker-test/speaker-test.c:104
+msgid "Front Right"
+msgstr "Avant Droit"
+
+#. 2
+#: ../speaker-test/speaker-test.c:105
+msgid "Rear Left"
+msgstr "Arrière Gauche"
+
+#. 3
+#: ../speaker-test/speaker-test.c:106
+msgid "Rear Right"
+msgstr "Arrière Droit"
+
+#. 5
+#: ../speaker-test/speaker-test.c:108
+msgid "LFE"
+msgstr ""
+
+#. 6
+#: ../speaker-test/speaker-test.c:109
+msgid "Side Left"
+msgstr "Côté Gauche"
+
+#. 7
+#: ../speaker-test/speaker-test.c:110
+msgid "Side Right"
+msgstr "Côté Droit"
+
+#. 8
+#: ../speaker-test/speaker-test.c:111
+msgid "Channel 9"
+msgstr "Canal 9"
+
+#. 9
+#: ../speaker-test/speaker-test.c:112
+msgid "Channel 10"
+msgstr "Canal 10"
+
+#. 10
+#: ../speaker-test/speaker-test.c:113
+msgid "Channel 11"
+msgstr "Canal 11"
+
+#. 11
+#: ../speaker-test/speaker-test.c:114
+msgid "Channel 12"
+msgstr "Canal 12"
+
+#. 12
+#: ../speaker-test/speaker-test.c:115
+msgid "Channel 13"
+msgstr "Canal 13"
+
+#. 13
+#: ../speaker-test/speaker-test.c:116
+msgid "Channel 14"
+msgstr "Canal 14"
+
+#. 14
+#: ../speaker-test/speaker-test.c:117
+msgid "Channel 15"
+msgstr "Canal 15"
+
+#. 15
+#: ../speaker-test/speaker-test.c:118
+msgid "Channel 16"
+msgstr "Canal 16"
+
+#: ../speaker-test/speaker-test.c:317
+#, c-format
+msgid "Broken configuration for playback: no configurations available: %s\n"
+msgstr "Configuration erronée pour la lecture: pas de configuration: %s\n"
+
+#: ../speaker-test/speaker-test.c:324
+#, c-format
+msgid "Access type not available for playback: %s\n"
+msgstr "Le type d'accès est indisponible à la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:331
+#, c-format
+msgid "Sample format not available for playback: %s\n"
+msgstr "Le format d'échantillonage est indisponible à la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:338
+#, c-format
+msgid "Channels count (%i) not available for playbacks: %s\n"
+msgstr "Le nombre (%i) de canaux est indisponible à la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:346
+#, c-format
+msgid "Rate %iHz not available for playback: %s\n"
+msgstr "La fréquence %iHz est indisponible à la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:351
+#, c-format
+msgid "Rate doesn't match (requested %iHz, get %iHz, err %d)\n"
+msgstr "La fréquence ne correspond pas (demandée: %iHz, obtenue %iHz, err %d)\n"
+
+#: ../speaker-test/speaker-test.c:355
+#, c-format
+msgid "Rate set to %iHz (requested %iHz)\n"
+msgstr "La fréquence est %iHz (demandée %iHz)\n"
+
+#: ../speaker-test/speaker-test.c:361
+#, c-format
+msgid "Buffer size range from %lu to %lu\n"
+msgstr "L'intervalle de la taille du tampon est de %lu à %lu\n"
+
+#: ../speaker-test/speaker-test.c:362
+#, c-format
+msgid "Period size range from %lu to %lu\n"
+msgstr "L'intervalle de la taille de la période est de %lu à %lu\n"
+
+#: ../speaker-test/speaker-test.c:364
+#, c-format
+msgid "Requested period time %u us\n"
+msgstr "Temps de période demandé %u µs\n"
+
+#: ../speaker-test/speaker-test.c:367
+#, c-format
+msgid "Unable to set period time %u us for playback: %s\n"
+msgstr "Le temps de période %u µs n'a pas pu être défini pour la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:373
+#, c-format
+msgid "Requested buffer time %u us\n"
+msgstr "Temps tampon demandé %u µs\n"
+
+#: ../speaker-test/speaker-test.c:376
+#, c-format
+msgid "Unable to set buffer time %u us for playback: %s\n"
+msgstr "Le temps tampon de %u µs n'a pas pu être définie pour la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:385
+#, c-format
+msgid "Using max buffer size %lu\n"
+msgstr "Taille max. de tampon %lu utilisée\n"
+
+#: ../speaker-test/speaker-test.c:388
+#, c-format
+msgid "Unable to set buffer size %lu for playback: %s\n"
+msgstr "La taile de tampon %lu n'a pas pu être définie pour la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:394
+#, c-format
+msgid "Periods = %u\n"
+msgstr "Périodes = %u\n"
+
+#: ../speaker-test/speaker-test.c:397
+#, c-format
+msgid "Unable to set nperiods %u for playback: %s\n"
+msgstr "nperiods %u n'a pas pu être défini pour la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:406
+#, c-format
+msgid "Unable to set hw params for playback: %s\n"
+msgstr "Les paramètres matériel n'ont pas pu être définis pour la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:412
+#, c-format
+msgid "was set period_size = %lu\n"
+msgstr ""
+
+#: ../speaker-test/speaker-test.c:413
+#, c-format
+msgid "was set buffer_size = %lu\n"
+msgstr ""
+
+#: ../speaker-test/speaker-test.c:415
+#, c-format
+msgid "buffer to small, could not use\n"
+msgstr "tampon trop petit, il n'a pas pu être utilisé\n"
+
+#: ../speaker-test/speaker-test.c:428
+#, c-format
+msgid "Unable to determine current swparams for playback: %s\n"
+msgstr "Les paramètres logiciels actuels n'ont pas pu être déterminés pour la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:435
+#, c-format
+msgid "Unable to set start threshold mode for playback: %s\n"
+msgstr ""
+
+#: ../speaker-test/speaker-test.c:442
+#, c-format
+msgid "Unable to set avail min for playback: %s\n"
+msgstr ""
+
+#: ../speaker-test/speaker-test.c:449
+#, c-format
+msgid "Unable to set sw params for playback: %s\n"
+msgstr "Les paramètres logiciel n'ont pas pu être définis pour la lecture: %s\n"
+
+#: ../speaker-test/speaker-test.c:464
+#, c-format
+msgid "Can't recovery from underrun, prepare failed: %s\n"
+msgstr ""
+
+#: ../speaker-test/speaker-test.c:475
+#, c-format
+msgid "Can't recovery from suspend, prepare failed: %s\n"
+msgstr ""
+
+#: ../speaker-test/speaker-test.c:539 ../speaker-test/speaker-test.c:954
+#, c-format
+msgid "No enough memory\n"
+msgstr "Pas assez de mémoire\n"
+
+#: ../speaker-test/speaker-test.c:544
+#, c-format
+msgid "Cannot open WAV file %s\n"
+msgstr "Le fichier WAV %s n'a pas pu être ouvert\n"
+
+#: ../speaker-test/speaker-test.c:548 ../speaker-test/speaker-test.c:577
+#, c-format
+msgid "Invalid WAV file %s\n"
+msgstr "Fichier WAV incorrect %s\n"
+
+#: ../speaker-test/speaker-test.c:553
+#, c-format
+msgid "Not a WAV file: %s\n"
+msgstr "Le fichier %s n'est pas au format WAV\n"
+
+#: ../speaker-test/speaker-test.c:557
+#, c-format
+msgid "Unsupported WAV format %d for %s\n"
+msgstr "Format WAV %d non supporté pour %s\n"
+
+#: ../speaker-test/speaker-test.c:562
+#, c-format
+msgid "%s is not a mono stream (%d channels)\n"
+msgstr "%s n'est pas un flux en mono (%d canaux)\n"
+
+#: ../speaker-test/speaker-test.c:567
+#, c-format
+msgid "Sample rate doesn't match (%d) for %s\n"
+msgstr "La fréquence d'échantillonage (%d) ne correspond pas à %s\n"
+
+#: ../speaker-test/speaker-test.c:572
+#, c-format
+msgid "Unsupported sample format bits %d for %s\n"
+msgstr "Le format d'échantillonage %d bits n'est pas supporté pour %s\n"
+
+#: ../speaker-test/speaker-test.c:622
+#, c-format
+msgid "Undefined channel %d\n"
+msgstr "Canal non-défini %d\n"
+
+#: ../speaker-test/speaker-test.c:673
+#, c-format
+msgid "Write error: %d,%s\n"
+msgstr "Erreur en écriture: %d,%s\n"
+
+#: ../speaker-test/speaker-test.c:675
+#, c-format
+msgid "xrun_recovery failed: %d,%s\n"
+msgstr ""
+
+#: ../speaker-test/speaker-test.c:734
+#, c-format
+msgid ""
+"Usage: speaker-test [OPTION]... \n"
+"-h,--help\thelp\n"
+"-D,--device\tplayback device\n"
+"-r,--rate\tstream rate in Hz\n"
+"-c,--channels\tcount of channels in stream\n"
+"-f,--frequency\tsine wave frequency in Hz\n"
+"-F,--format\tsample format\n"
+"-b,--buffer\tring buffer size in us\n"
+"-p,--period\tperiod size in us\n"
+"-P,--nperiods\tnumber of periods\n"
+"-t,--test\tpink=use pink noise, sine=use sine wave, wav=WAV file\n"
+"-l,--nloops\tspecify number of loops to test, 0 = infinite\n"
+"-s,--speaker\tsingle speaker test. Values 1=Left, 2=right, etc\n"
+"-w,--wavfile\tUse the given WAV file as a test sound\n"
+"-W,--wavdir\tSpecify the directory containing WAV files\n"
+"\n"
+msgstr ""
+"Utilisation: speaker-test [OPTION]...\n"
+"-h,--help\taide\n"
+"-D,--device\tpériphérique de lecture\n"
+"-r,--rate\tfréquence du flux en Hz\n"
+"-c,--channels\tnombre de canaux du flux\n"
+"-f,--frequency\tfréquence courbe sinus en Hz\n"
+"-F,--format\tformat d'échantillonage\n"
+"-b,--buffer\ttaille en µs du tampon circulaire\n"
+"-p,--period\ttaille de la période en µs\n"
+"-P,--nperiods\tnombre de périodes\n"
+"-t,--test\tpink=pink noise, sine=courbe sinus, wav=fichier WAV\n"
+"-l,--nloops\tspécifier le nombre de motifs à tester, 0 = infini\n"
+"-s,--speaker\ttest sur une seule enceinte.\n"
+"\t\tValeurs 1=Gauche, 2=Droite, et c.\n"
+"-w,--wavfile\tUtiliser le fichier WAV spécifié pour le test\n"
+"-w,--wavdir\tSpécifier le répertoire contenant les fichiers WAV\n"
+"\n"
+
+#: ../speaker-test/speaker-test.c:852
+#, c-format
+msgid "Invalid number of periods %d\n"
+msgstr "Nombre de périodes %d incorrect\n"
+
+#: ../speaker-test/speaker-test.c:866 ../speaker-test/speaker-test.c:870
+#, c-format
+msgid "Invalid test type %s\n"
+msgstr "Type de test %s incorrect\n"
+
+#: ../speaker-test/speaker-test.c:882
+#, c-format
+msgid "Invalid parameter for -s option.\n"
+msgstr "Paramètre incorrect pour l'option -s.\n"
+
+#: ../speaker-test/speaker-test.c:896
+#, c-format
+msgid "Unknown option '%c'\n"
+msgstr "Option '%c' inconnue\n"
+
+#. fixed format
+#: ../speaker-test/speaker-test.c:910
+#, c-format
+msgid "Playback device is %s\n"
+msgstr "Le périphérique de lecture est %s\n"
+
+#: ../speaker-test/speaker-test.c:911
+#, c-format
+msgid "Stream parameters are %iHz, %s, %i channels\n"
+msgstr "Les paramètres du flux sont %iHz, %s, %i canaux\n"
+
+#: ../speaker-test/speaker-test.c:914
+#, c-format
+msgid "Using 16 octaves of pink noise\n"
+msgstr "Utilisation de 16 octaves de 'pink noise'\n"
+
+#: ../speaker-test/speaker-test.c:917
+#, c-format
+msgid "Sine wave rate is %.4fHz\n"
+msgstr "La fréquence de la courbe sinus est %.4fHz\n"
+
+#: ../speaker-test/speaker-test.c:920
+#, c-format
+msgid "WAV file(s)\n"
+msgstr "fichier(s) WAV\n"
+
+#: ../speaker-test/speaker-test.c:926
+#, c-format
+msgid "Playback open error: %d,%s\n"
+msgstr "Erreur d'ouverture à la lecture: %d,%s\n"
+
+#: ../speaker-test/speaker-test.c:931
+#, c-format
+msgid "Setting of hwparams failed: %s\n"
+msgstr "Échec de la configuration des paramètres matériel: %s\n"
+
+#: ../speaker-test/speaker-test.c:936
+#, c-format
+msgid "Setting of swparams failed: %s\n"
+msgstr "Échec de la configuration des paramètres logiciel: %s\n"
+
+#: ../speaker-test/speaker-test.c:985 ../speaker-test/speaker-test.c:1007
+#, c-format
+msgid "Transfer failed: %s\n"
+msgstr "Échec du transfer: %s\n"
+
+#: ../speaker-test/speaker-test.c:995
+#, c-format
+msgid "Time per period = %lf\n"
+msgstr "Temps par période = %lf\n"
diff --git a/speaker-test/speaker-test.c b/speaker-test/speaker-test.c
index 458a8d7..3029110 100644
--- a/speaker-test/speaker-test.c
+++ b/speaker-test/speaker-test.c
@@ -993,9 +993,9 @@
}
- while ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+ if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf(_("Playback open error: %d,%s\n"), err,snd_strerror(err));
- sleep(1);
+ exit(EXIT_FAILURE);
}
if ((err = set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {