From e5f270a89f0debdcfb40791ffdce0963b697d020 Mon Sep 17 00:00:00 2001 From: Alexandre Ratchov Date: Mon, 13 Apr 2020 07:48:18 +0200 Subject: [PATCH] If available, use the hardware output.level to control the volume. With this change, there's a single outputs.level control: either the hardware one or software one. Consequently, there can't be control name clashes and there's no need to move hardware's top-level controls into the "hw/" group. --- sndiod/dev.c | 89 +++++++++++++++++++++++++++++++++++++++------ sndiod/dev.h | 4 +- sndiod/dev_sioctl.c | 9 ++--- 3 files changed, 84 insertions(+), 18 deletions(-) diff --git a/sndiod/dev.c b/sndiod/dev.c index 8ee8db7..ea15415 100644 --- a/sndiod/dev.c +++ b/sndiod/dev.c @@ -349,8 +349,26 @@ dev_midi_vol(struct dev *d, struct slot *s) void dev_midi_master(struct dev *d) { + struct ctl *c; + unsigned int master, v; struct sysex x; + if (d->master_enabled) + master = d->master; + else { + master = 0; + for (c = d->ctl_list; c != NULL; c = c->next) { + if (c->type != CTL_NUM || + strcmp(c->group, "") != 0 || + strcmp(c->node0.name, "output") != 0 || + strcmp(c->func, "level") != 0) + continue; + v = (c->curval * 127 + c->maxval / 2) / c->maxval; + if (master < v) + master = v; + } + } + memset(&x, 0, sizeof(struct sysex)); x.start = SYSEX_START; x.type = SYSEX_TYPE_RT; @@ -358,7 +376,7 @@ dev_midi_master(struct dev *d) x.id0 = SYSEX_CONTROL; x.id1 = SYSEX_MASTER; x.u.master.fine = 0; - x.u.master.coarse = d->master; + x.u.master.coarse = master; x.u.master.end = SYSEX_END; midi_send(d->midi, (unsigned char *)&x, SYSEX_SIZE(master)); } @@ -442,8 +460,10 @@ dev_midi_omsg(void *arg, unsigned char *msg, int len) if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) { if (len == SYSEX_SIZE(master)) { dev_master(d, x->u.master.coarse); - dev_onval(d, CTLADDR_MASTER, - x->u.master.coarse); + if (d->master_enabled) { + dev_onval(d, CTLADDR_MASTER, + x->u.master.coarse); + } } return; } @@ -655,7 +675,8 @@ dev_mix_adjvol(struct dev *d) } if (weight > i->opt->maxweight) weight = i->opt->maxweight; - i->mix.weight = ADATA_MUL(weight, MIDI_TO_ADATA(d->master)); + i->mix.weight = d->master_enabled ? + ADATA_MUL(weight, MIDI_TO_ADATA(d->master)) : weight; #ifdef DEBUG if (log_level >= 3) { slot_log(i); @@ -946,15 +967,30 @@ dev_onmove(struct dev *d, int delta) void dev_master(struct dev *d, unsigned int master) { + struct ctl *c; + unsigned int v; + if (log_level >= 2) { dev_log(d); log_puts(": master volume set to "); log_putu(master); log_puts("\n"); } - d->master = master; - if (d->mode & MODE_PLAY) - dev_mix_adjvol(d); + if (d->master_enabled) { + d->master = master; + if (d->mode & MODE_PLAY) + dev_mix_adjvol(d); + } else { + for (c = d->ctl_list; c != NULL; c = c->next) { + if (c->type != CTL_NUM || + strcmp(c->group, "") != 0 || + strcmp(c->node0.name, "output") != 0 || + strcmp(c->func, "level") != 0) + continue; + v = (master * c->maxval + 64) / 127; + dev_setctl(d, c->addr, v); + } + } } /* @@ -1121,6 +1157,7 @@ dev_open(struct dev *d) int i; char name[CTL_NAMEMAX]; + d->master_enabled = 0; d->mode = d->reqmode; d->round = d->reqround; d->bufsz = d->reqbufsz; @@ -1149,8 +1186,6 @@ dev_open(struct dev *d) name, -1, "level", NULL, -1, 127, d->slot[i].vol); } - dev_addctl(d, "", CTL_NUM, - CTLADDR_MASTER, "output", -1, "level", NULL, -1, 127, d->master); d->pstate = DEV_INIT; return 1; @@ -2337,8 +2372,36 @@ dev_rmctl(struct dev *d, int addr) void dev_ctlsync(struct dev *d) { + struct ctl *c; struct ctlslot *s; - int i; + int found, i; + + found = 0; + for (c = d->ctl_list; c != NULL; c = c->next) { + if (c->addr != CTLADDR_MASTER && + c->type == CTL_NUM && + strcmp(c->group, "") == 0 && + strcmp(c->node0.name, "output") == 0 && + strcmp(c->func, "level") == 0) + found = 1; + } + + if (d->master_enabled && found) { + if (log_level >= 2) { + dev_log(d); + log_puts(": software master level control disabled\n"); + } + d->master_enabled = 0; + dev_rmctl(d, CTLADDR_MASTER); + } else if (!d->master_enabled && !found) { + if (log_level >= 2) { + dev_log(d); + log_puts(": software master level control enabled\n"); + } + d->master_enabled = 1; + dev_addctl(d, "", CTL_NUM, CTLADDR_MASTER, + "output", -1, "level", NULL, -1, 127, d->master); + } for (s = d->ctlslot, i = DEV_NCTLSLOT; i > 0; i--, s++) { if (s->ops) @@ -2392,8 +2455,10 @@ dev_setctl(struct dev *d, int addr, int val) dev_ref(d); } else { if (addr == CTLADDR_MASTER) { - dev_master(d, val); - dev_midi_master(d); + if (d->master_enabled) { + dev_master(d, val); + dev_midi_master(d); + } } else { num = addr - CTLADDR_SLOT_LEVEL(0); slot_setvol(d->slot + num, val); diff --git a/sndiod/dev.h b/sndiod/dev.h index 6a3b530..fad647c 100644 --- a/sndiod/dev.h +++ b/sndiod/dev.h @@ -244,7 +244,9 @@ struct dev { #define MMC_START 2 /* attempting to start */ #define MMC_RUN 3 /* started */ unsigned int tstate; /* one of above */ - unsigned int master; /* master volume controller */ + + unsigned int master; /* software vol. knob */ + unsigned int master_enabled; /* 1 if h/w has no vo. knob */ /* * control diff --git a/sndiod/dev_sioctl.c b/sndiod/dev_sioctl.c index e3174c6..30e43fe 100644 --- a/sndiod/dev_sioctl.c +++ b/sndiod/dev_sioctl.c @@ -65,18 +65,17 @@ dev_sioctl_ondesc(void *arg, struct sioctl_desc *desc, int val) dev_rmctl(d, addr); /* - * prefix group names we use (top-level and "app") with "hw." + * prefix group names we use (currently "app") with "hw/" * to ensure that all controls have unique names when multiple * sndiod's are chained */ - if (desc->group[0] == 0) - group = GROUP_PREFIX; - else { + if (strcmp(desc->group, "app") == 0) { group = group_buf; if (snprintf(group_buf, CTL_NAMEMAX, GROUP_PREFIX "/%s", desc->group) >= CTL_NAMEMAX) return; - } + } else + group = desc->group; dev_addctl(d, group, desc->type, addr, desc->node0.name, desc->node0.unit, desc->func,