diff --git a/alsaloop/alsaloop.1 b/alsaloop/alsaloop.1 index 6bacfd5..006494d 100644 --- a/alsaloop/alsaloop.1 +++ b/alsaloop/alsaloop.1 @@ -57,6 +57,11 @@ Use given CTL device for playback. Use given CTL device for capture. +.TP +\fI\-x \fP | \fI\-\-prateshift=\fP + +Specify ctl ascii name for playshift sync mode (see the Examples section). + .TP \fI\-l \fP | \fI\-\-latency=\fP @@ -195,10 +200,12 @@ Verbose xrun profiling. Set process wake timeout. .SH EXAMPLES - -.TP +.nf \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 None known. .SH AUTHOR diff --git a/alsaloop/alsaloop.c b/alsaloop/alsaloop.c index 06ffadf..4192712 100644 --- a/alsaloop/alsaloop.c +++ b/alsaloop/alsaloop.c @@ -175,6 +175,7 @@ void help(void) "-C,--cdevice capture device\n" "-X,--pctl playback 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" "-t,--tlatency requested latency in usec (1/1000000sec)\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'}, {"pctl", 1, NULL, 'X'}, {"cctl", 1, NULL, 'Y'}, + {"prateshift", 1, NULL, 'x'}, {"latency", 1, NULL, 'l'}, {"tlatency", 1, NULL, 't'}, {"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_pctl = NULL; char *arg_cctl = NULL; + char *arg_prateshift = NULL; unsigned int arg_latency_req = 0; unsigned int arg_latency_reqtime = 10000; 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) { 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: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) break; switch (c) { @@ -446,6 +449,9 @@ static int parse_config(int argc, char *argv[], snd_output_t *output, case 'Y': arg_cctl = strdup(optarg); break; + case 'x': + arg_prateshift = strdup(optarg); + break; case 'l': err = atoi(optarg); 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"); exit(EXIT_FAILURE); } + if (arg_prateshift) + play->prateshift_name = arg_prateshift; + #ifdef USE_SAMPLERATE loop->src_enable = arg_samplerate > 0; if (loop->src_enable) diff --git a/alsaloop/alsaloop.h b/alsaloop/alsaloop.h index 1dbcefe..7a98ef3 100644 --- a/alsaloop/alsaloop.h +++ b/alsaloop/alsaloop.h @@ -127,6 +127,7 @@ struct loopback_handle { snd_ctl_elem_value_t *ctl_format; snd_ctl_elem_value_t *ctl_rate; snd_ctl_elem_value_t *ctl_channels; + char *prateshift_name; /* ascii name for the playback rate shift ctl elem */ }; struct loopback { diff --git a/alsaloop/pcmjob.c b/alsaloop/pcmjob.c index 845ab82..f87283d 100644 --- a/alsaloop/pcmjob.c +++ b/alsaloop/pcmjob.c @@ -1101,7 +1101,8 @@ void update_pitch(struct loopback *loop) #endif } 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 if (loop->use_samplerate) { 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); } -static void openctl_elem(struct loopback_handle *lhandle, - int device, int subdevice, - const char *name, - snd_ctl_elem_value_t **elem) +static int openctl_elem_id(struct loopback_handle *lhandle, snd_ctl_elem_id_t *id, + 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; - } 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); - } + return -ENOMEM; } + 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) @@ -1206,10 +1232,21 @@ static int openctl(struct loopback_handle *lhandle, int device, int subdevice) lhandle->ctl_rate_shift = NULL; 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) goto __events; return 0; } + // capture only openctl_elem(lhandle, device, subdevice, "PCM Notify", &lhandle->ctl_notify); openctl_elem(lhandle, device, subdevice, "PCM Rate Shift 100000",