mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-12-22 13:16:31 +01:00
alsaloop: added option prateshift for PLAYSHIFT ctl elem used in PLAYSHIFT
If snd-aloop device is on playback side, the required sync mode is PLAYSHIFT. That means Loopback ctl elem "PCM Rate Shift 100000" of the corresponding capture side of the Loopback pipe must be controlled (by a reciprocal). ASCII name of the playback rate shift ctl elem is specified with newly added option -x/--prateshift, e.g.: -P hw:Loopback,0 -S playshift \ -x iface=PCM,name='PCM Rate Shift 100000',device=1 Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
e2b167c490
commit
138e53aabb
4 changed files with 78 additions and 24 deletions
|
@ -57,6 +57,11 @@ Use given CTL device for playback.
|
||||||
|
|
||||||
Use given CTL device for capture.
|
Use given CTL device for capture.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fI\-x <ctl_ascii_name>\fP | \fI\-\-prateshift=<ctl_ascii_name>\fP
|
||||||
|
|
||||||
|
Specify ctl ascii name for playshift sync mode (see the Examples section).
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fI\-l <latency>\fP | \fI\-\-latency=<frames>\fP
|
\fI\-l <latency>\fP | \fI\-\-latency=<frames>\fP
|
||||||
|
|
||||||
|
@ -195,10 +200,12 @@ Verbose xrun profiling.
|
||||||
Set process wake timeout.
|
Set process wake timeout.
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
|
.nf
|
||||||
.TP
|
|
||||||
\fBalsaloop \-C hw:0,0 \-P hw:1,0 \-t 50000\fR
|
\fBalsaloop \-C hw:0,0 \-P hw:1,0 \-t 50000\fR
|
||||||
|
|
||||||
|
\fBalsaloop \-C hw:soundcard,0 \-P hw:Loopback,0 \-t 50000 \-S playshift \\
|
||||||
|
\-x iface=PCM,name='PCM Rate Shift 100000',device=1\fR
|
||||||
|
.ne
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
None known.
|
None known.
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
|
|
|
@ -175,6 +175,7 @@ void help(void)
|
||||||
"-C,--cdevice capture device\n"
|
"-C,--cdevice capture device\n"
|
||||||
"-X,--pctl playback ctl device\n"
|
"-X,--pctl playback ctl device\n"
|
||||||
"-Y,--cctl capture ctl device\n"
|
"-Y,--cctl capture ctl device\n"
|
||||||
|
"-x,--prateshift playback 'PCM Rate Shift 100000' ascii ctl name\n"
|
||||||
"-l,--latency requested latency in frames\n"
|
"-l,--latency requested latency in frames\n"
|
||||||
"-t,--tlatency requested latency in usec (1/1000000sec)\n"
|
"-t,--tlatency requested latency in usec (1/1000000sec)\n"
|
||||||
"-f,--format sample format\n"
|
"-f,--format sample format\n"
|
||||||
|
@ -362,6 +363,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
|
||||||
{"cdevice", 1, NULL, 'C'},
|
{"cdevice", 1, NULL, 'C'},
|
||||||
{"pctl", 1, NULL, 'X'},
|
{"pctl", 1, NULL, 'X'},
|
||||||
{"cctl", 1, NULL, 'Y'},
|
{"cctl", 1, NULL, 'Y'},
|
||||||
|
{"prateshift", 1, NULL, 'x'},
|
||||||
{"latency", 1, NULL, 'l'},
|
{"latency", 1, NULL, 'l'},
|
||||||
{"tlatency", 1, NULL, 't'},
|
{"tlatency", 1, NULL, 't'},
|
||||||
{"format", 1, NULL, 'f'},
|
{"format", 1, NULL, 'f'},
|
||||||
|
@ -391,6 +393,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
|
||||||
char *arg_cdevice = NULL;
|
char *arg_cdevice = NULL;
|
||||||
char *arg_pctl = NULL;
|
char *arg_pctl = NULL;
|
||||||
char *arg_cctl = NULL;
|
char *arg_cctl = NULL;
|
||||||
|
char *arg_prateshift = NULL;
|
||||||
unsigned int arg_latency_req = 0;
|
unsigned int arg_latency_req = 0;
|
||||||
unsigned int arg_latency_reqtime = 10000;
|
unsigned int arg_latency_reqtime = 10000;
|
||||||
snd_pcm_format_t arg_format = SND_PCM_FORMAT_S16_LE;
|
snd_pcm_format_t arg_format = SND_PCM_FORMAT_S16_LE;
|
||||||
|
@ -420,7 +423,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
|
||||||
while (1) {
|
while (1) {
|
||||||
int c;
|
int c;
|
||||||
if ((c = getopt_long(argc, argv,
|
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:z",
|
"hdg:P:C:X:Y:x:l:t:F:f:c:r:s:benvA:S:a:m:T:O:w:UW:z",
|
||||||
long_option, NULL)) < 0)
|
long_option, NULL)) < 0)
|
||||||
break;
|
break;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
@ -446,6 +449,9 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
|
||||||
case 'Y':
|
case 'Y':
|
||||||
arg_cctl = strdup(optarg);
|
arg_cctl = strdup(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'x':
|
||||||
|
arg_prateshift = strdup(optarg);
|
||||||
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
err = atoi(optarg);
|
err = atoi(optarg);
|
||||||
arg_latency_req = err >= 4 ? err : 4;
|
arg_latency_req = err >= 4 ? err : 4;
|
||||||
|
@ -627,6 +633,9 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
|
||||||
logit(LOG_CRIT, "Unable to add ossmixer controls.\n");
|
logit(LOG_CRIT, "Unable to add ossmixer controls.\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
if (arg_prateshift)
|
||||||
|
play->prateshift_name = arg_prateshift;
|
||||||
|
|
||||||
#ifdef USE_SAMPLERATE
|
#ifdef USE_SAMPLERATE
|
||||||
loop->src_enable = arg_samplerate > 0;
|
loop->src_enable = arg_samplerate > 0;
|
||||||
if (loop->src_enable)
|
if (loop->src_enable)
|
||||||
|
|
|
@ -127,6 +127,7 @@ struct loopback_handle {
|
||||||
snd_ctl_elem_value_t *ctl_format;
|
snd_ctl_elem_value_t *ctl_format;
|
||||||
snd_ctl_elem_value_t *ctl_rate;
|
snd_ctl_elem_value_t *ctl_rate;
|
||||||
snd_ctl_elem_value_t *ctl_channels;
|
snd_ctl_elem_value_t *ctl_channels;
|
||||||
|
char *prateshift_name; /* ascii name for the playback rate shift ctl elem */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct loopback {
|
struct loopback {
|
||||||
|
|
|
@ -1101,7 +1101,8 @@ void update_pitch(struct loopback *loop)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if (loop->sync == SYNC_TYPE_PLAYRATESHIFT) {
|
else if (loop->sync == SYNC_TYPE_PLAYRATESHIFT) {
|
||||||
set_rate_shift(loop->play, pitch);
|
// pitch is capture-based, playback side requires reciprocal
|
||||||
|
set_rate_shift(loop->play, 1 / pitch);
|
||||||
#ifdef USE_SAMPLERATE
|
#ifdef USE_SAMPLERATE
|
||||||
if (loop->use_samplerate) {
|
if (loop->use_samplerate) {
|
||||||
loop->src_data.src_ratio =
|
loop->src_data.src_ratio =
|
||||||
|
@ -1172,32 +1173,57 @@ static int get_channels(struct loopback_handle *lhandle)
|
||||||
return snd_ctl_elem_value_get_integer(lhandle->ctl_channels, 0);
|
return snd_ctl_elem_value_get_integer(lhandle->ctl_channels, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void openctl_elem(struct loopback_handle *lhandle,
|
static int openctl_elem_id(struct loopback_handle *lhandle, snd_ctl_elem_id_t *id,
|
||||||
int device, int subdevice,
|
snd_ctl_elem_value_t **elem)
|
||||||
const char *name,
|
|
||||||
snd_ctl_elem_value_t **elem)
|
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (snd_ctl_elem_value_malloc(elem) < 0) {
|
if (snd_ctl_elem_value_malloc(elem) < 0) {
|
||||||
*elem = NULL;
|
*elem = NULL;
|
||||||
} else {
|
return -ENOMEM;
|
||||||
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;
|
|
||||||
} else {
|
|
||||||
if (verbose)
|
|
||||||
snd_output_printf(lhandle->loopback->output,
|
|
||||||
"Opened PCM element %s of %s, device %d, subdevice %d\n",
|
|
||||||
name, snd_ctl_name(lhandle->ctl), device, subdevice);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
snd_ctl_elem_value_set_id(*elem, id);
|
||||||
|
snd_ctl_elem_value_set_interface(*elem, SND_CTL_ELEM_IFACE_PCM);
|
||||||
|
err = snd_ctl_elem_read(lhandle->ctl, *elem);
|
||||||
|
if (err < 0) {
|
||||||
|
snd_ctl_elem_value_free(*elem);
|
||||||
|
*elem = NULL;
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
|
snd_output_printf(lhandle->loopback->output,
|
||||||
|
"Opened PCM element %s of %s, device %d, subdevice %d\n",
|
||||||
|
snd_ctl_elem_id_get_name(id), snd_ctl_name(lhandle->ctl),
|
||||||
|
snd_ctl_elem_id_get_device(id),
|
||||||
|
snd_ctl_elem_id_get_subdevice(id));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int openctl_elem(struct loopback_handle *lhandle,
|
||||||
|
int device, int subdevice,
|
||||||
|
const char *name,
|
||||||
|
snd_ctl_elem_value_t **elem)
|
||||||
|
{
|
||||||
|
snd_ctl_elem_id_t *id;
|
||||||
|
|
||||||
|
snd_ctl_elem_id_alloca(&id);
|
||||||
|
snd_ctl_elem_id_set_device(id, device);
|
||||||
|
snd_ctl_elem_id_set_subdevice(id, subdevice);
|
||||||
|
snd_ctl_elem_id_set_name(id, name);
|
||||||
|
return openctl_elem_id(lhandle, id, elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int openctl_elem_ascii(struct loopback_handle *lhandle, char *ascii_name,
|
||||||
|
snd_ctl_elem_value_t **elem)
|
||||||
|
{
|
||||||
|
snd_ctl_elem_id_t *id;
|
||||||
|
|
||||||
|
snd_ctl_elem_id_alloca(&id);
|
||||||
|
if (snd_ctl_ascii_elem_id_parse(id, ascii_name)) {
|
||||||
|
fprintf(stderr, "Wrong control identifier: %s\n", ascii_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return openctl_elem_id(lhandle, id, elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int openctl(struct loopback_handle *lhandle, int device, int subdevice)
|
static int openctl(struct loopback_handle *lhandle, int device, int subdevice)
|
||||||
|
@ -1206,10 +1232,21 @@ static int openctl(struct loopback_handle *lhandle, int device, int subdevice)
|
||||||
|
|
||||||
lhandle->ctl_rate_shift = NULL;
|
lhandle->ctl_rate_shift = NULL;
|
||||||
if (lhandle->loopback->play == lhandle) {
|
if (lhandle->loopback->play == lhandle) {
|
||||||
|
// play only
|
||||||
|
if (lhandle->prateshift_name) {
|
||||||
|
err = openctl_elem_ascii(lhandle, lhandle->prateshift_name,
|
||||||
|
&lhandle->ctl_rate_shift);
|
||||||
|
if (err < 0) {
|
||||||
|
logit(LOG_CRIT, "Unable to open playback PCM Rate Shift elem '%s'.\n",
|
||||||
|
lhandle->prateshift_name);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (lhandle->loopback->controls)
|
if (lhandle->loopback->controls)
|
||||||
goto __events;
|
goto __events;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// capture only
|
||||||
openctl_elem(lhandle, device, subdevice, "PCM Notify",
|
openctl_elem(lhandle, device, subdevice, "PCM Notify",
|
||||||
&lhandle->ctl_notify);
|
&lhandle->ctl_notify);
|
||||||
openctl_elem(lhandle, device, subdevice, "PCM Rate Shift 100000",
|
openctl_elem(lhandle, device, subdevice, "PCM Rate Shift 100000",
|
||||||
|
|
Loading…
Reference in a new issue