Download raw body.
aucat: Add generic channel mapping in place of -j and -c options.
aucat: Add generic channel mapping in place of -j and -c options.
aucat: Add generic channel mapping in place of -j and -c options.
On Mon, Mar 18, 2024 at 04:28:36PM +0100, Jan Stary wrote:
>
> I like the producer/consumer nomenclature, as it is the closest
> to how I think about a signal route - not in terms of files and devices.
>
Here's a better diff:
- ranges in the producer/consumer order.
- the mapping is reset after every -i and -o.
Index: aucat.1
===================================================================
RCS file: /cvs/src/usr.bin/aucat/aucat.1,v
diff -u -p -r1.119 aucat.1
--- aucat.1 10 Jan 2023 20:48:34 -0000 1.119
+++ aucat.1 18 Mar 2024 14:54:52 -0000
@@ -24,13 +24,13 @@
.Nm aucat
.Op Fl dn
.Op Fl b Ar size
-.Op Fl c Ar min : Ns Ar max
+.Op Fl c Ar channels
.Op Fl e Ar enc
.Op Fl f Ar device
.Op Fl g Ar position
.Op Fl h Ar fmt
.Op Fl i Ar file
-.Op Fl j Ar flag
+.Op Fl m Ar min : Ns Ar max Ns / Ns Ar min : Ns Ar max
.Op Fl o Ar file
.Op Fl p Ar position
.Op Fl q Ar port
@@ -78,11 +78,9 @@ The options are as follows:
.It Fl b Ar size
The buffer size of the audio device in frames.
Default is 7680.
-.It Fl c Ar min : Ns Ar max
-The range of audio file channel numbers.
-The default is
-.Cm 0:1 ,
-i.e. stereo.
+.It Fl c Ar channels
+The audio file channels count.
+The default is 2, i.e. stereo.
.It Fl d
Increase log verbosity.
.It Fl e Ar enc
@@ -146,21 +144,9 @@ Play this audio file.
If the option argument is
.Sq -
then standard input will be used.
-.It Fl j Ar flag
-Control whether source channels are joined or expanded if
-they don't match the destination number of channels.
-If the flag is
-.Cm off ,
-then each source channel is routed to a single destination channel,
-possibly discarding channels.
-If the flag is
-.Cm on ,
-then a single source may be sent to multiple destinations
-and multiple sources may be mixed into a single destination.
-For instance, this feature could be used to convert
-a stereo file into a mono file mixing left and right channels together.
-The default is
-.Cm off .
+.It Fl m Ar min : Ns Ar max Ns / Ns Ar min : Ns Ar max
+Map the given range of source channels into the given range of
+destination channels.
.It Fl n
Off-line mode.
Read input files and store the result in the output files,
@@ -198,7 +184,7 @@ The default is 127, i.e. no attenuation.
.Pp
On the command line,
per-file parameters
-.Pq Fl cehjrv
+.Pq Fl cehmrv
must precede the file definition
.Pq Fl io .
.Pp
@@ -282,13 +268,13 @@ Record channels 2 and 3 into one stereo
channels 6 and 7 into another stereo file using a 44.1kHz sampling
rate for both:
.Bd -literal -offset indent
-$ aucat -r 44100 -c 2:3 -o file1.wav -c 6:7 -o file2.wav
+$ aucat -r 44100 -m 2:3/0:1 -o file1.wav -m 6:7/0:1 -o file2.wav
.Ed
.Pp
Split a stereo file into two mono files:
.Bd -literal -offset indent
-$ aucat -n -i stereo.wav -c 0:0 -o left.wav \e
- -c 1:1 -o right.wav
+$ aucat -n -i stereo.wav -c 1 -m 0:0/0:0 -o left.wav \e
+ -m 1:1/0:0 -o right.wav
.Ed
.Sh SEE ALSO
.Xr cdio 1 ,
Index: aucat.c
===================================================================
RCS file: /cvs/src/usr.bin/aucat/aucat.c,v
diff -u -p -r1.179 aucat.c
--- aucat.c 1 Feb 2024 05:28:54 -0000 1.179
+++ aucat.c 18 Mar 2024 14:54:52 -0000
@@ -75,14 +75,14 @@ struct slot {
int volctl; /* volume in the 0..127 range */
struct abuf buf; /* file i/o buffer */
int bpf; /* bytes per frame */
- int cmin, cmax; /* file channel range */
+ int imin, imax, omin, omax; /* channel mapping ranges */
struct cmap cmap; /* channel mapper state */
struct resamp resamp; /* resampler state */
struct conv conv; /* format encoder state */
int join; /* channel join factor */
int expand; /* channel expand factor */
void *resampbuf, *convbuf; /* conversion tmp buffers */
- int dup; /* mono-to-stereo and alike */
+ int dup; /* compat with legacy -j option */
int round; /* slot-side block size */
int mode; /* MODE_{PLAY,REC} */
#define SLOT_CFG 0 /* buffers not allocated yet */
@@ -136,9 +136,9 @@ const unsigned int voice_len[] = { 3, 3,
const unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
char usagestr[] = "usage: aucat [-dn] [-b size] "
- "[-c min:max] [-e enc] [-f device] [-g position]\n\t"
- "[-h fmt] [-i file] [-j flag] [-o file] [-p position] [-q port]\n\t"
- "[-r rate] [-v volume]\n";
+ "[-c channels] [-e enc] [-f device] [-g position]\n\t"
+ "[-h fmt] [-i file] [-m min:max/min:max] [-o file] [-p position]\n\t"
+ "[-q port] [-r rate] [-v volume]\n";
static void *
allocbuf(int nfr, int nch)
@@ -218,19 +218,22 @@ slot_fill(struct slot *s)
static int
slot_new(char *path, int mode, struct aparams *par, int hdr,
- int cmin, int cmax, int rate, int dup, int vol, long long pos)
+ int imin, int imax, int omin, int omax, int nch,
+ int rate, int dup, int vol, long long pos)
{
struct slot *s, **ps;
s = xmalloc(sizeof(struct slot));
if (!afile_open(&s->afile, path, hdr,
mode == SIO_PLAY ? AFILE_FREAD : AFILE_FWRITE,
- par, rate, cmax - cmin + 1)) {
+ par, rate, nch)) {
xfree(s);
return 0;
}
- s->cmin = cmin;
- s->cmax = cmin + s->afile.nch - 1;
+ s->imin = (imin != -1) ? imin : 0;
+ s->imax = (imax != -1) ? imax : s->imin + s->afile.nch - 1;
+ s->omin = (omin != -1) ? omin : 0;
+ s->omax = (omax != -1) ? omax : s->omin + s->afile.nch - 1;
s->dup = dup;
s->vol = MIDI_TO_ADATA(vol);
s->mode = mode;
@@ -240,11 +243,17 @@ slot_new(char *path, int mode, struct ap
slot_log(s);
log_puts(": ");
log_puts(s->mode == SIO_PLAY ? "play" : "rec");
- log_puts(", chan ");
- log_putu(s->cmin);
- log_puts(":");
- log_putu(s->cmax);
log_puts(", ");
+ log_putu(s->afile.nch);
+ log_puts("ch (");
+ log_putu(s->imin);
+ log_puts(":");
+ log_putu(s->imax);
+ log_puts("/");
+ log_putu(s->omin);
+ log_puts(":");
+ log_putu(s->omax);
+ log_puts("), ");
log_putu(s->afile.rate);
log_puts("Hz, ");
switch (s->afile.fmt) {
@@ -283,7 +292,7 @@ slot_new(char *path, int mode, struct ap
static void
slot_init(struct slot *s)
{
- unsigned int slot_nch, bufsz;
+ unsigned int inch, onch, bufsz;
#ifdef DEBUG
if (s->pstate != SLOT_CFG) {
@@ -292,7 +301,7 @@ slot_init(struct slot *s)
panic();
}
#endif
- s->bpf = s->afile.par.bps * (s->cmax - s->cmin + 1);
+ s->bpf = s->afile.par.bps * s->afile.nch;
s->round = ((long long)dev_round * s->afile.rate +
dev_rate - 1) / dev_rate;
@@ -310,54 +319,50 @@ slot_init(struct slot *s)
}
#endif
- slot_nch = s->cmax - s->cmin + 1;
s->convbuf = NULL;
s->resampbuf = NULL;
s->join = 1;
s->expand = 1;
+ inch = s->imax - s->imin + 1;
+ onch = s->omax - s->omin + 1;
+ if (s->dup) {
+ /* compat with legacy -j option */
+ if (s->mode == SIO_PLAY)
+ onch = dev_pchan;
+ else
+ inch = dev_rchan;
+ }
+ if (onch > inch)
+ s->expand = onch / inch;
+ else if (onch < inch)
+ s->join = inch / onch;
if (s->mode & SIO_PLAY) {
- if (s->dup) {
- if (dev_pchan > slot_nch)
- s->expand = dev_pchan / slot_nch;
- else if (dev_pchan < slot_nch)
- s->join = slot_nch / dev_pchan;
- }
cmap_init(&s->cmap,
- s->cmin, s->cmax,
- s->cmin, s->cmax,
- 0, dev_pchan - 1,
- 0, dev_pchan - 1);
+ 0, s->afile.nch - 1, s->imin, s->imax,
+ 0, dev_pchan - 1, s->omin, s->omax);
if (s->afile.fmt != AFILE_FMT_PCM ||
!aparams_native(&s->afile.par)) {
- dec_init(&s->conv, &s->afile.par, slot_nch);
- s->convbuf = allocbuf(s->round, slot_nch);
+ dec_init(&s->conv, &s->afile.par, s->afile.nch);
+ s->convbuf = allocbuf(s->round, s->afile.nch);
}
if (s->afile.rate != dev_rate) {
resamp_init(&s->resamp, s->afile.rate, dev_rate,
- slot_nch);
- s->resampbuf = allocbuf(dev_round, slot_nch);
+ s->afile.nch);
+ s->resampbuf = allocbuf(dev_round, s->afile.nch);
}
}
if (s->mode & SIO_REC) {
- if (s->dup) {
- if (dev_rchan > slot_nch)
- s->join = dev_rchan / slot_nch;
- else if (dev_rchan < slot_nch)
- s->expand = slot_nch / dev_rchan;
- }
cmap_init(&s->cmap,
- 0, dev_rchan - 1,
- 0, dev_rchan - 1,
- s->cmin, s->cmax,
- s->cmin, s->cmax);
+ 0, dev_rchan - 1, s->imin, s->imax,
+ 0, s->afile.nch - 1, s->omin, s->omax);
if (s->afile.rate != dev_rate) {
resamp_init(&s->resamp, dev_rate, s->afile.rate,
- slot_nch);
- s->resampbuf = allocbuf(dev_round, slot_nch);
+ s->afile.nch);
+ s->resampbuf = allocbuf(dev_round, s->afile.nch);
}
if (!aparams_native(&s->afile.par)) {
- enc_init(&s->conv, &s->afile.par, slot_nch);
- s->convbuf = allocbuf(s->round, slot_nch);
+ enc_init(&s->conv, &s->afile.par, s->afile.nch);
+ s->convbuf = allocbuf(s->round, s->afile.nch);
}
/*
@@ -368,13 +373,13 @@ slot_init(struct slot *s)
*/
if (s->resampbuf) {
memset(s->resampbuf, 0,
- dev_round * slot_nch * sizeof(adata_t));
+ dev_round * s->afile.nch * sizeof(adata_t));
} else if (s->convbuf) {
memset(s->convbuf, 0,
- s->round * slot_nch * sizeof(adata_t));
+ s->round * s->afile.nch * sizeof(adata_t));
} else {
memset(s->buf.data, 0,
- bufsz * slot_nch * sizeof(adata_t));
+ bufsz * s->afile.nch * sizeof(adata_t));
}
}
s->pstate = SLOT_INIT;
@@ -487,7 +492,7 @@ slot_getcnt(struct slot *s, int *icnt, i
static void
play_filt_resamp(struct slot *s, void *res_in, void *out, int icnt, int ocnt)
{
- int i, offs, vol, nch;
+ int i, offs, vol, inch, onch;
void *in;
if (s->resampbuf) {
@@ -496,18 +501,24 @@ play_filt_resamp(struct slot *s, void *r
} else
in = res_in;
- nch = s->cmap.nch;
+ inch = s->imax - s->imin + 1;
+ onch = s->omax - s->omin + 1;
vol = s->vol / s->join; /* XXX */
cmap_add(&s->cmap, in, out, vol, ocnt);
offs = 0;
for (i = s->join - 1; i > 0; i--) {
- offs += nch;
+ offs += onch;
+ if (offs + s->cmap.nch > s->afile.nch)
+ break;
cmap_add(&s->cmap, (adata_t *)in + offs, out, vol, ocnt);
}
+
offs = 0;
for (i = s->expand - 1; i > 0; i--) {
- offs += nch;
+ offs += inch;
+ if (offs + s->cmap.nch > dev_pchan)
+ break;
cmap_add(&s->cmap, in, (adata_t *)out + offs, vol, ocnt);
}
}
@@ -581,23 +592,28 @@ slot_mix_badd(struct slot *s, adata_t *o
static void
rec_filt_resamp(struct slot *s, void *in, void *res_out, int icnt, int ocnt)
{
- int i, vol, offs, nch;
+ int i, vol, offs, inch, onch;
void *out = res_out;
out = (s->resampbuf) ? s->resampbuf : res_out;
- nch = s->cmap.nch;
+ inch = s->imax - s->imin + 1;
+ onch = s->omax - s->omin + 1;
vol = ADATA_UNIT / s->join;
cmap_copy(&s->cmap, in, out, vol, icnt);
offs = 0;
for (i = s->join - 1; i > 0; i--) {
- offs += nch;
+ offs += onch;
+ if (offs + s->cmap.nch > dev_rchan)
+ break;
cmap_add(&s->cmap, (adata_t *)in + offs, out, vol, icnt);
}
offs = 0;
for (i = s->expand - 1; i > 0; i--) {
- offs += nch;
+ offs += inch;
+ if (offs + s->cmap.nch > s->afile.nch)
+ break;
cmap_copy(&s->cmap, in, (adata_t *)out + offs, vol, icnt);
}
if (s->resampbuf)
@@ -683,12 +699,12 @@ dev_open(char *dev, int mode, int bufsz,
if (s->afile.rate > rate)
rate = s->afile.rate;
if (s->mode == SIO_PLAY) {
- if (s->cmax > pmax)
- pmax = s->cmax;
+ if (s->omax > pmax)
+ pmax = s->omax;
}
if (s->mode == SIO_REC) {
- if (s->cmax > rmax)
- rmax = s->cmax;
+ if (s->imax > rmax)
+ rmax = s->imax;
}
}
sio_initpar(&par);
@@ -1078,8 +1094,10 @@ offline(void)
for (s = slot_list; s != NULL; s = s->next) {
if (s->afile.rate > rate)
rate = s->afile.rate;
- if (s->cmax > cmax)
- cmax = s->cmax;
+ if (s->imax > cmax)
+ cmax = s->imax;
+ if (s->omax > cmax)
+ cmax = s->omax;
}
dev_sh = NULL;
dev_name = "offline";
@@ -1323,26 +1341,78 @@ opt_hdr(char *s, int *hdr)
}
static int
-opt_ch(char *s, int *rcmin, int *rcmax)
+opt_map(char *str, int *rimin, int *rimax, int *romin, int *romax)
{
- char *next, *end;
- long cmin, cmax;
+ char *s, *next;
+ long imin, imax, omin, omax;
errno = 0;
- cmin = strtol(s, &next, 10);
+ s = str;
+ imin = strtol(s, &next, 10);
+ if (next == s || *next != ':')
+ goto failed;
+ s = next + 1;
+ imax = strtol(s, &next, 10);
+ if (next == s || *next != '/')
+ goto failed;
+ s = next + 1;
+ omin = strtol(s, &next, 10);
if (next == s || *next != ':')
goto failed;
- cmax = strtol(++next, &end, 10);
- if (end == next || *end != '\0')
+ s = next + 1;
+ omax = strtol(s, &next, 10);
+ if (next == s || *next != '\0')
goto failed;
- if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
+ if (imin < 0 || imax < imin || imax >= NCHAN_MAX)
goto failed;
- *rcmin = cmin;
- *rcmax = cmax;
+ if (omin < 0 || omax < omin || omax >= NCHAN_MAX)
+ goto failed;
+ *rimin = imin;
+ *rimax = imax;
+ *romin = omin;
+ *romax = omax;
return 1;
failed:
- log_puts(s);
- log_puts(": channel range expected\n");
+ log_puts(str);
+ log_puts(": channel mapping expected\n");
+ return 0;
+}
+
+static int
+opt_nch(char *str, int *rnch, int *roff)
+{
+ char *s, *next;
+ long nch, off, cmin, cmax;
+
+ errno = 0;
+ s = str;
+ nch = strtol(s, &next, 10);
+ if (next == s)
+ goto failed;
+ if (*next == ':') {
+ /* compat with legacy -c syntax */
+ s = next + 1;
+ cmin = nch;
+ cmax = strtol(s, &next, 10);
+ if (next == s)
+ goto failed;
+ if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
+ goto failed;
+ nch = cmax - cmin + 1;
+ off = cmin;
+ } else {
+ off = 0;
+ if (nch < 0 || nch >= NCHAN_MAX)
+ goto failed;
+ }
+ if (*next != '\0')
+ goto failed;
+ *rnch = nch;
+ *roff = off;
+ return 1;
+failed:
+ log_puts(str);
+ log_puts(": channel count expected\n");
return 0;
}
@@ -1381,7 +1451,7 @@ opt_pos(char *s, long long *pos)
int
main(int argc, char **argv)
{
- int dup, cmin, cmax, rate, vol, bufsz, hdr, mode;
+ int dup, imin, imax, omin, omax, nch, off, rate, vol, bufsz, hdr, mode;
char *port, *dev;
struct aparams par;
int n_flag, c;
@@ -1393,9 +1463,10 @@ main(int argc, char **argv)
vol = 127;
dup = 0;
bufsz = 0;
+ nch = 2;
+ off = 0;
rate = DEFAULT_RATE;
- cmin = 0;
- cmax = 1;
+ imin = imax = omin = omax = -1;
par.bits = ADATA_BITS;
par.bps = APARAMS_BPS(par.bits);
par.le = ADATA_LE;
@@ -1409,14 +1480,14 @@ main(int argc, char **argv)
pos = 0;
while ((c = getopt(argc, argv,
- "b:c:de:f:g:h:i:j:no:p:q:r:t:v:")) != -1) {
+ "b:c:de:f:g:h:i:j:m:no:p:q:r:t:v:")) != -1) {
switch (c) {
case 'b':
if (!opt_num(optarg, 1, RATE_MAX, &bufsz))
return 1;
break;
case 'c':
- if (!opt_ch(optarg, &cmin, &cmax))
+ if (!opt_nch(optarg, &nch, &off))
return 1;
break;
case 'd':
@@ -1438,22 +1509,41 @@ main(int argc, char **argv)
return 1;
break;
case 'i':
+ if (off > 0) {
+ /* compat with legacy -c syntax */
+ omin = off;
+ omax = off + nch - 1;
+ }
if (!slot_new(optarg, SIO_PLAY,
- &par, hdr, cmin, cmax, rate, dup, vol, pos))
+ &par, hdr, imin, imax, omin, omax,
+ nch, rate, dup, vol, pos))
return 1;
mode |= SIO_PLAY;
+ imin = imax = omin = omax = -1;
break;
case 'j':
+ /* compat with legacy -j option */
if (!opt_onoff(optarg, &dup))
return 1;
break;
+ case 'm':
+ if (!opt_map(optarg, &imin, &imax, &omin, &omax))
+ return 1;
+ break;
case 'n':
n_flag = 1;
break;
case 'o':
+ if (off > 0) {
+ /* compat with legacy -c syntax */
+ imin = off;
+ imax = off + nch - 1;
+ }
if (!slot_new(optarg, SIO_REC,
- &par, hdr, cmin, cmax, rate, dup, 0, pos))
+ &par, hdr, imin, imax, omin, omax,
+ nch, rate, dup, 0, pos))
return 1;
+ imin = imax = omin = omax = -1;
mode |= SIO_REC;
break;
case 'p':
Index: dsp.c
===================================================================
RCS file: /cvs/src/usr.bin/aucat/dsp.c,v
diff -u -p -r1.18 dsp.c
--- dsp.c 26 Dec 2022 19:16:00 -0000 1.18
+++ dsp.c 18 Mar 2024 14:54:53 -0000
@@ -992,33 +992,35 @@ cmap_init(struct cmap *p,
int imin, int imax, int isubmin, int isubmax,
int omin, int omax, int osubmin, int osubmax)
{
- int cmin, cmax;
+ int inch, onch, nch;
- cmin = -NCHAN_MAX;
- if (osubmin > cmin)
- cmin = osubmin;
- if (omin > cmin)
- cmin = omin;
- if (isubmin > cmin)
- cmin = isubmin;
- if (imin > cmin)
- cmin = imin;
+ /*
+ * Ignore channels outside of the available sets
+ */
+ if (isubmin < imin)
+ isubmin = imin;
+ if (isubmax > imax)
+ isubmax = imax;
+ if (osubmin < omin)
+ osubmin = omin;
+ if (osubmax > omax)
+ osubmax = omax;
- cmax = NCHAN_MAX;
- if (osubmax < cmax)
- cmax = osubmax;
- if (omax < cmax)
- cmax = omax;
- if (isubmax < cmax)
- cmax = isubmax;
- if (imax < cmax)
- cmax = imax;
+ /*
+ * Shrink the input or the output subset to make both subsets of
+ * the same size
+ */
+ inch = isubmax - isubmin + 1;
+ onch = osubmax - osubmin + 1;
+ nch = (inch < onch) ? inch : onch;
+ isubmax = isubmin + nch - 1;
+ osubmax = osubmin + nch - 1;
- p->ostart = cmin - omin;
- p->onext = omax - cmax;
- p->istart = cmin - imin;
- p->inext = imax - cmax;
- p->nch = cmax - cmin + 1;
+ p->ostart = osubmin - omin;
+ p->onext = omax - osubmax;
+ p->istart = isubmin - imin;
+ p->inext = imax - isubmax;
+ p->nch = nch;
#ifdef DEBUG
if (log_level >= 3) {
log_puts("cmap: nch = ");
aucat: Add generic channel mapping in place of -j and -c options.
aucat: Add generic channel mapping in place of -j and -c options.
aucat: Add generic channel mapping in place of -j and -c options.