version 1.91, 2021/03/02 12:15:46 |
version 1.92, 2021/03/03 10:00:27 |
|
|
zomb_exit |
zomb_exit |
}; |
}; |
|
|
|
struct ctl *ctl_list = NULL; |
struct dev *dev_list = NULL; |
struct dev *dev_list = NULL; |
unsigned int dev_sndnum = 0; |
unsigned int dev_sndnum = 0; |
|
|
|
|
master = d->master; |
master = d->master; |
else { |
else { |
master = 0; |
master = 0; |
for (c = d->ctl_list; c != NULL; c = c->next) { |
for (c = ctl_list; c != NULL; c = c->next) { |
if (c->type != CTL_NUM || |
if (c->type != CTL_NUM || |
strcmp(c->group, "") != 0 || |
strcmp(c->group, d->name) != 0 || |
strcmp(c->node0.name, "output") != 0 || |
strcmp(c->node0.name, "output") != 0 || |
strcmp(c->func, "level") != 0) |
strcmp(c->func, "level") != 0) |
continue; |
continue; |
|
if (c->u.any.arg0 != d) |
|
continue; |
v = (c->curval * 127 + c->maxval / 2) / c->maxval; |
v = (c->curval * 127 + c->maxval / 2) / c->maxval; |
if (master < v) |
if (master < v) |
master = v; |
master = v; |
|
|
slot_array[chan].opt->dev != d) |
slot_array[chan].opt->dev != d) |
return; |
return; |
slot_setvol(slot_array + chan, msg[2]); |
slot_setvol(slot_array + chan, msg[2]); |
dev_onval(d, CTLADDR_SLOT_LEVEL(chan), msg[2]); |
ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]); |
return; |
return; |
} |
} |
x = (struct sysex *)msg; |
x = (struct sysex *)msg; |
|
|
if (len == SYSEX_SIZE(master)) { |
if (len == SYSEX_SIZE(master)) { |
dev_master(d, x->u.master.coarse); |
dev_master(d, x->u.master.coarse); |
if (d->master_enabled) { |
if (d->master_enabled) { |
dev_onval(d, CTLADDR_MASTER, |
ctl_onval(CTL_DEV_MASTER, d, NULL, |
x->u.master.coarse); |
x->u.master.coarse); |
} |
} |
} |
} |
|
|
if (d->mode & MODE_PLAY) |
if (d->mode & MODE_PLAY) |
dev_mix_adjvol(d); |
dev_mix_adjvol(d); |
} else { |
} else { |
for (c = d->ctl_list; c != NULL; c = c->next) { |
for (c = ctl_list; c != NULL; c = c->next) { |
|
if (c->scope != CTL_HW || c->u.hw.dev != d) |
|
continue; |
if (c->type != CTL_NUM || |
if (c->type != CTL_NUM || |
strcmp(c->group, "") != 0 || |
strcmp(c->group, d->name) != 0 || |
strcmp(c->node0.name, "output") != 0 || |
strcmp(c->node0.name, "output") != 0 || |
strcmp(c->func, "level") != 0) |
strcmp(c->func, "level") != 0) |
continue; |
continue; |
v = (master * c->maxval + 64) / 127; |
v = (master * c->maxval + 64) / 127; |
dev_setctl(d, c->addr, v); |
ctl_setval(c, v); |
} |
} |
} |
} |
} |
} |
|
|
d->master = MIDI_MAXCTL; |
d->master = MIDI_MAXCTL; |
d->mtc.origin = 0; |
d->mtc.origin = 0; |
d->tstate = MMC_STOP; |
d->tstate = MMC_STOP; |
d->ctl_list = NULL; |
snprintf(d->name, CTL_NAMEMAX, "%u", d->num); |
d->next = dev_list; |
d->next = dev_list; |
dev_list = d; |
dev_list = d; |
return d; |
return d; |
|
|
int |
int |
dev_open(struct dev *d) |
dev_open(struct dev *d) |
{ |
{ |
int i; |
|
char name[CTL_NAMEMAX]; |
char name[CTL_NAMEMAX]; |
struct dev_alt *a; |
struct dev_alt *a; |
struct slot *s; |
|
|
|
d->master_enabled = 0; |
d->master_enabled = 0; |
d->mode = d->reqmode; |
d->mode = d->reqmode; |
|
|
if (!dev_allocbufs(d)) |
if (!dev_allocbufs(d)) |
return 0; |
return 0; |
|
|
for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { |
|
if (s->opt == NULL || s->opt->dev != d || s->name[0] == 0) |
|
continue; |
|
slot_ctlname(s, name, CTL_NAMEMAX); |
|
dev_addctl(d, "app", CTL_NUM, |
|
CTLADDR_SLOT_LEVEL(i), |
|
name, -1, "level", |
|
NULL, -1, 127, s->vol); |
|
} |
|
|
|
/* if there are multiple alt devs, add server.device knob */ |
/* if there are multiple alt devs, add server.device knob */ |
if (d->alt_list->next != NULL) { |
if (d->alt_list->next != NULL) { |
for (a = d->alt_list; a != NULL; a = a->next) { |
for (a = d->alt_list; a != NULL; a = a->next) { |
snprintf(name, sizeof(name), "%d", a->idx); |
snprintf(name, sizeof(name), "%d", a->idx); |
dev_addctl(d, "", CTL_SEL, |
ctl_new(CTL_DEV_ALT, d, &a->idx, |
CTLADDR_ALT_SEL + a->idx, |
CTL_SEL, d->name, "server", -1, "device", |
"server", -1, "device", |
|
name, -1, 1, a->idx == d->alt_num); |
name, -1, 1, a->idx == d->alt_num); |
} |
} |
} |
} |
|
|
void |
void |
dev_close(struct dev *d) |
dev_close(struct dev *d) |
{ |
{ |
struct ctl *c; |
struct dev_alt *a; |
|
unsigned int idx; |
|
|
d->pstate = DEV_CFG; |
d->pstate = DEV_CFG; |
dev_sio_close(d); |
dev_sio_close(d); |
dev_freebufs(d); |
dev_freebufs(d); |
|
|
/* there are no clients, just free remaining local controls */ |
if (d->master_enabled) { |
while ((c = d->ctl_list) != NULL) { |
d->master_enabled = 0; |
d->ctl_list = c->next; |
ctl_del(CTL_DEV_MASTER, d, NULL); |
xfree(c); |
|
} |
} |
|
for (idx = 0, a = d->alt_list; a != NULL; idx++, a = a->next) |
|
ctl_del(CTL_DEV_ALT, d, &idx); |
} |
} |
|
|
/* |
/* |
|
|
return NULL; |
return NULL; |
s->ops = ops; |
s->ops = ops; |
s->arg = arg; |
s->arg = arg; |
for (c = o->dev->ctl_list; c != NULL; c = c->next) |
for (c = ctl_list; c != NULL; c = c->next) { |
|
if (!ctlslot_visible(s, c)) |
|
continue; |
c->refs_mask |= s->self; |
c->refs_mask |= s->self; |
|
} |
return s; |
return s; |
} |
} |
|
|
|
|
{ |
{ |
struct ctl *c, **pc; |
struct ctl *c, **pc; |
|
|
pc = &s->opt->dev->ctl_list; |
pc = &ctl_list; |
while ((c = *pc) != NULL) { |
while ((c = *pc) != NULL) { |
c->refs_mask &= ~s->self; |
c->refs_mask &= ~s->self; |
if (c->refs_mask == 0) { |
if (c->refs_mask == 0) { |
|
|
dev_unref(s->opt->dev); |
dev_unref(s->opt->dev); |
} |
} |
|
|
|
int |
|
ctlslot_visible(struct ctlslot *s, struct ctl *c) |
|
{ |
|
if (s->opt == NULL) |
|
return 1; |
|
switch (c->scope) { |
|
case CTL_HW: |
|
case CTL_DEV_MASTER: |
|
case CTL_DEV_ALT: |
|
return (s->opt->dev == c->u.any.arg0); |
|
case CTL_SLOT_LEVEL: |
|
return (s->opt->dev == c->u.slot_level.slot->opt->dev); |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
struct ctl * |
|
ctlslot_lookup(struct ctlslot *s, int addr) |
|
{ |
|
struct ctl *c; |
|
|
|
c = ctl_list; |
|
while (1) { |
|
if (c == NULL) |
|
return NULL; |
|
if (c->type != CTL_NONE && c->addr == addr) |
|
break; |
|
c = c->next; |
|
} |
|
if (!ctlslot_visible(s, c)) |
|
return NULL; |
|
return c; |
|
} |
|
|
void |
void |
ctl_node_log(struct ctl_node *c) |
ctl_node_log(struct ctl_node *c) |
{ |
{ |
|
|
} |
} |
log_puts(" at "); |
log_puts(" at "); |
log_putu(c->addr); |
log_putu(c->addr); |
|
log_puts(" -> "); |
|
switch (c->scope) { |
|
case CTL_HW: |
|
log_puts("hw:"); |
|
log_puts(c->u.hw.dev->name); |
|
log_puts("/"); |
|
log_putu(c->u.hw.addr); |
|
break; |
|
case CTL_DEV_MASTER: |
|
log_puts("dev_master:"); |
|
log_puts(c->u.dev_master.dev->name); |
|
break; |
|
case CTL_DEV_ALT: |
|
log_puts("dev_alt:"); |
|
log_puts(c->u.dev_alt.dev->name); |
|
log_putu(c->u.dev_alt.idx); |
|
break; |
|
case CTL_SLOT_LEVEL: |
|
log_puts("slot_level:"); |
|
log_puts(c->u.slot_level.slot->name); |
|
log_putu(c->u.slot_level.slot->unit); |
|
break; |
|
default: |
|
log_puts("unknown"); |
|
} |
} |
} |
|
|
|
int |
|
ctl_setval(struct ctl *c, int val) |
|
{ |
|
if (c->curval == val) { |
|
if (log_level >= 3) { |
|
ctl_log(c); |
|
log_puts(": already set\n"); |
|
} |
|
return 1; |
|
} |
|
if (val < 0 || val > c->maxval) { |
|
if (log_level >= 3) { |
|
log_putu(val); |
|
log_puts(": ctl val out of bounds\n"); |
|
} |
|
return 0; |
|
} |
|
|
|
switch (c->scope) { |
|
case CTL_HW: |
|
if (log_level >= 3) { |
|
ctl_log(c); |
|
log_puts(": marked as dirty\n"); |
|
} |
|
c->curval = val; |
|
c->dirty = 1; |
|
return dev_ref(c->u.hw.dev); |
|
case CTL_DEV_MASTER: |
|
if (!c->u.dev_master.dev->master_enabled) |
|
return 1; |
|
dev_master(c->u.dev_master.dev, val); |
|
dev_midi_master(c->u.dev_master.dev); |
|
c->val_mask = ~0U; |
|
c->curval = val; |
|
return 1; |
|
case CTL_DEV_ALT: |
|
dev_setalt (c->u.dev_alt.dev, c->u.dev_alt.idx); |
|
return 1; |
|
case CTL_SLOT_LEVEL: |
|
slot_setvol(c->u.slot_level.slot, val); |
|
// XXX change dev_midi_vol() into slot_midi_vol() |
|
dev_midi_vol(c->u.slot_level.slot->opt->dev, c->u.slot_level.slot); |
|
c->val_mask = ~0U; |
|
c->curval = val; |
|
return 1; |
|
default: |
|
if (log_level >= 2) { |
|
ctl_log(c); |
|
log_puts(": not writable\n"); |
|
} |
|
return 1; |
|
} |
|
} |
|
|
/* |
/* |
* add a ctl |
* add a ctl |
*/ |
*/ |
struct ctl * |
struct ctl * |
dev_addctl(struct dev *d, char *gstr, int type, int addr, |
ctl_new(int scope, void *arg0, void *arg1, |
char *str0, int unit0, char *func, char *str1, int unit1, int maxval, int val) |
int type, char *gstr, |
|
char *str0, int unit0, char *func, |
|
char *str1, int unit1, int maxval, int val) |
{ |
{ |
struct ctl *c, **pc; |
struct ctl *c, **pc; |
|
struct ctlslot *s; |
|
int addr; |
int i; |
int i; |
|
|
|
/* |
|
* find the smallest unused addr number and |
|
* the last position in the list |
|
*/ |
|
addr = 0; |
|
for (pc = &ctl_list; (c = *pc) != NULL; pc = &c->next) { |
|
if (c->addr > addr) |
|
addr = c->addr; |
|
} |
|
addr++; |
|
|
c = xmalloc(sizeof(struct ctl)); |
c = xmalloc(sizeof(struct ctl)); |
c->type = type; |
c->type = type; |
strlcpy(c->func, func, CTL_NAMEMAX); |
strlcpy(c->func, func, CTL_NAMEMAX); |
|
|
c->node1.unit = unit1; |
c->node1.unit = unit1; |
} else |
} else |
memset(&c->node1, 0, sizeof(struct ctl_node)); |
memset(&c->node1, 0, sizeof(struct ctl_node)); |
|
c->scope = scope; |
|
c->u.any.arg0 = arg0; |
|
switch (scope) { |
|
case CTL_HW: |
|
c->u.hw.addr = *(unsigned int *)arg1; |
|
break; |
|
case CTL_DEV_ALT: |
|
c->u.dev_alt.idx = *(unsigned int *)arg1; |
|
break; |
|
default: |
|
c->u.any.arg1 = NULL; |
|
} |
c->addr = addr; |
c->addr = addr; |
c->maxval = maxval; |
c->maxval = maxval; |
c->val_mask = ~0; |
c->val_mask = ~0; |
c->desc_mask = ~0; |
c->desc_mask = ~0; |
c->curval = val; |
c->curval = val; |
c->dirty = 0; |
c->dirty = 0; |
c->refs_mask = 0; |
c->refs_mask = CTL_DEVMASK; |
for (i = 0; i < DEV_NCTLSLOT; i++) { |
for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) { |
c->refs_mask |= CTL_DEVMASK; |
if (s->ops == NULL) |
if (ctlslot_array[i].ops != NULL) |
continue; |
|
if (ctlslot_visible(s, c)) |
c->refs_mask |= 1 << i; |
c->refs_mask |= 1 << i; |
} |
} |
for (pc = &d->ctl_list; *pc != NULL; pc = &(*pc)->next) |
c->next = *pc; |
; /* nothing */ |
|
c->next = NULL; |
|
*pc = c; |
*pc = c; |
#ifdef DEBUG |
#ifdef DEBUG |
if (log_level >= 3) { |
if (log_level >= 2) { |
dev_log(d); |
|
log_puts(": adding "); |
|
ctl_log(c); |
ctl_log(c); |
log_puts("\n"); |
log_puts(": added\n"); |
} |
} |
#endif |
#endif |
return c; |
return c; |
} |
} |
|
|
void |
void |
dev_rmctl(struct dev *d, int addr) |
ctl_update(struct ctl *c) |
{ |
{ |
|
struct ctlslot *s; |
|
unsigned int refs_mask; |
|
int i; |
|
|
|
for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) { |
|
if (s->ops == NULL) |
|
continue; |
|
refs_mask = ctlslot_visible(s, c) ? s->self : 0; |
|
|
|
/* nothing to do if no visibility change */ |
|
if (((c->refs_mask & s->self) ^ refs_mask) == 0) |
|
continue; |
|
/* if control becomes visble */ |
|
if (refs_mask) |
|
c->refs_mask |= s->self; |
|
/* if control is hidden */ |
|
c->desc_mask |= s->self; |
|
} |
|
} |
|
|
|
int |
|
ctl_match(struct ctl *c, int scope, void *arg0, void *arg1) |
|
{ |
|
if (c->type == CTL_NONE || c->scope != scope || c->u.any.arg0 != arg0) |
|
return 0; |
|
if (arg0 != NULL && c->u.any.arg0 != arg0) |
|
return 0; |
|
switch (scope) { |
|
case CTL_HW: |
|
if (arg1 != NULL && c->u.hw.addr != *(unsigned int *)arg1) |
|
return 0; |
|
break; |
|
case CTL_DEV_ALT: |
|
if (arg1 != NULL && c->u.dev_alt.idx != *(unsigned int *)arg1) |
|
return 0; |
|
break; |
|
} |
|
return 1; |
|
} |
|
|
|
struct ctl * |
|
ctl_find(int scope, void *arg0, void *arg1) |
|
{ |
|
struct ctl *c; |
|
|
|
for (c = ctl_list; c != NULL; c = c->next) { |
|
if (ctl_match(c, scope, arg0, arg1)) |
|
return c; |
|
} |
|
return NULL; |
|
} |
|
|
|
int |
|
ctl_onval(int scope, void *arg0, void *arg1, int val) |
|
{ |
|
struct ctl *c; |
|
|
|
c = ctl_find(scope, arg0, arg1); |
|
if (c == NULL) |
|
return 0; |
|
c->curval = val; |
|
c->val_mask = ~0U; |
|
return 1; |
|
} |
|
|
|
void |
|
ctl_del(int scope, void *arg0, void *arg1) |
|
{ |
struct ctl *c, **pc; |
struct ctl *c, **pc; |
|
|
pc = &d->ctl_list; |
pc = &ctl_list; |
for (;;) { |
for (;;) { |
c = *pc; |
c = *pc; |
if (c == NULL) |
if (c == NULL) |
return; |
return; |
if (c->type != CTL_NONE && c->addr == addr) |
if (ctl_match(c, scope, arg0, arg1)) { |
break; |
|
pc = &c->next; |
|
} |
|
c->type = CTL_NONE; |
|
#ifdef DEBUG |
#ifdef DEBUG |
if (log_level >= 3) { |
if (log_level >= 2) { |
dev_log(d); |
ctl_log(c); |
log_puts(": removing "); |
log_puts(": removed\n"); |
ctl_log(c); |
} |
log_puts(", refs_mask = 0x"); |
|
log_putx(c->refs_mask); |
|
log_puts("\n"); |
|
} |
|
#endif |
#endif |
c->refs_mask &= ~CTL_DEVMASK; |
c->refs_mask &= ~CTL_DEVMASK; |
if (c->refs_mask == 0) { |
if (c->refs_mask == 0) { |
*pc = c->next; |
*pc = c->next; |
xfree(c); |
xfree(c); |
return; |
continue; |
|
} |
|
c->type = CTL_NONE; |
|
c->desc_mask = ~0; |
|
} |
|
pc = &c->next; |
} |
} |
c->desc_mask = ~0; |
|
} |
} |
|
|
void |
void |
|
|
int found, i; |
int found, i; |
|
|
found = 0; |
found = 0; |
for (c = d->ctl_list; c != NULL; c = c->next) { |
for (c = ctl_list; c != NULL; c = c->next) { |
if (c->addr != CTLADDR_MASTER && |
if (c->scope == CTL_HW && |
|
c->u.hw.dev == d && |
c->type == CTL_NUM && |
c->type == CTL_NUM && |
strcmp(c->group, "") == 0 && |
strcmp(c->group, d->name) == 0 && |
strcmp(c->node0.name, "output") == 0 && |
strcmp(c->node0.name, "output") == 0 && |
strcmp(c->func, "level") == 0) |
strcmp(c->func, "level") == 0) |
found = 1; |
found = 1; |
|
|
log_puts(": software master level control disabled\n"); |
log_puts(": software master level control disabled\n"); |
} |
} |
d->master_enabled = 0; |
d->master_enabled = 0; |
dev_rmctl(d, CTLADDR_MASTER); |
ctl_del(CTL_DEV_MASTER, d, NULL); |
} else if (!d->master_enabled && !found) { |
} else if (!d->master_enabled && !found) { |
if (log_level >= 2) { |
if (log_level >= 2) { |
dev_log(d); |
dev_log(d); |
log_puts(": software master level control enabled\n"); |
log_puts(": software master level control enabled\n"); |
} |
} |
d->master_enabled = 1; |
d->master_enabled = 1; |
dev_addctl(d, "", CTL_NUM, CTLADDR_MASTER, |
ctl_new(CTL_DEV_MASTER, d, NULL, |
"output", -1, "level", NULL, -1, 127, d->master); |
CTL_NUM, d->name, "output", -1, "level", |
|
NULL, -1, 127, d->master); |
} |
} |
|
|
for (s = ctlslot_array, i = DEV_NCTLSLOT; i > 0; i--, s++) { |
for (s = ctlslot_array, i = DEV_NCTLSLOT; i > 0; i--, s++) { |
|
|
} |
} |
} |
} |
|
|
int |
|
dev_setctl(struct dev *d, int addr, int val) |
|
{ |
|
struct ctl *c; |
|
struct slot *s; |
|
int num; |
|
|
|
c = d->ctl_list; |
|
for (;;) { |
|
if (c == NULL) { |
|
if (log_level >= 3) { |
|
dev_log(d); |
|
log_puts(": "); |
|
log_putu(addr); |
|
log_puts(": no such ctl address\n"); |
|
} |
|
return 0; |
|
} |
|
if (c->type != CTL_NONE && c->addr == addr) |
|
break; |
|
c = c->next; |
|
} |
|
if (c->curval == val) { |
|
if (log_level >= 3) { |
|
ctl_log(c); |
|
log_puts(": already set\n"); |
|
} |
|
return 1; |
|
} |
|
if (val < 0 || val > c->maxval) { |
|
if (log_level >= 3) { |
|
dev_log(d); |
|
log_puts(": "); |
|
log_putu(val); |
|
log_puts(": ctl val out of bounds\n"); |
|
} |
|
return 0; |
|
} |
|
if (addr >= CTLADDR_END) { |
|
if (log_level >= 3) { |
|
ctl_log(c); |
|
log_puts(": marked as dirty\n"); |
|
} |
|
c->dirty = 1; |
|
dev_ref(d); |
|
} else { |
|
if (addr >= CTLADDR_ALT_SEL) { |
|
if (val) { |
|
num = addr - CTLADDR_ALT_SEL; |
|
dev_setalt(d, num); |
|
} |
|
return 1; |
|
} else if (addr == CTLADDR_MASTER) { |
|
if (d->master_enabled) { |
|
dev_master(d, val); |
|
dev_midi_master(d); |
|
} |
|
} else { |
|
num = addr - CTLADDR_SLOT_LEVEL(0); |
|
s = slot_array + num; |
|
if (s->opt->dev != d) |
|
return 1; |
|
slot_setvol(s, val); |
|
dev_midi_vol(d, s); |
|
} |
|
c->val_mask = ~0U; |
|
} |
|
c->curval = val; |
|
return 1; |
|
} |
|
|
|
int |
|
dev_onval(struct dev *d, int addr, int val) |
|
{ |
|
struct ctl *c; |
|
|
|
c = d->ctl_list; |
|
for (;;) { |
|
if (c == NULL) |
|
return 0; |
|
if (c->type != CTL_NONE && c->addr == addr) |
|
break; |
|
c = c->next; |
|
} |
|
c->curval = val; |
|
c->val_mask = ~0U; |
|
return 1; |
|
} |
|
|
|
void |
void |
dev_label(struct dev *d, int i) |
dev_label(struct dev *d, int i) |
{ |
{ |
|
|
|
|
slot_ctlname(&slot_array[i], name, CTL_NAMEMAX); |
slot_ctlname(&slot_array[i], name, CTL_NAMEMAX); |
|
|
c = d->ctl_list; |
c = ctl_list; |
for (;;) { |
for (;;) { |
if (c == NULL) { |
if (c == NULL) { |
dev_addctl(d, "app", CTL_NUM, |
ctl_new(CTL_SLOT_LEVEL, slot_array + i, NULL, |
CTLADDR_SLOT_LEVEL(i), |
CTL_NUM, "app", name, -1, "level", |
name, -1, "level", |
|
NULL, -1, 127, slot_array[i].vol); |
NULL, -1, 127, slot_array[i].vol); |
return; |
return; |
} |
} |
if (c->addr == CTLADDR_SLOT_LEVEL(i)) |
if (ctl_match(c, CTL_SLOT_LEVEL, slot_array + i, NULL)) |
break; |
break; |
c = c->next; |
c = c->next; |
} |
} |