1*7b639200Sratchov /* $OpenBSD: dev.c,v 1.119 2024/12/20 07:35:56 ratchov Exp $ */ 287bc9f6aSratchov /* 387bc9f6aSratchov * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org> 487bc9f6aSratchov * 587bc9f6aSratchov * Permission to use, copy, modify, and distribute this software for any 687bc9f6aSratchov * purpose with or without fee is hereby granted, provided that the above 787bc9f6aSratchov * copyright notice and this permission notice appear in all copies. 887bc9f6aSratchov * 987bc9f6aSratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1087bc9f6aSratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1187bc9f6aSratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1287bc9f6aSratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1387bc9f6aSratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1487bc9f6aSratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1587bc9f6aSratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1687bc9f6aSratchov */ 1787bc9f6aSratchov #include <stdio.h> 1887bc9f6aSratchov #include <string.h> 1987bc9f6aSratchov 2087bc9f6aSratchov #include "abuf.h" 2187bc9f6aSratchov #include "defs.h" 2287bc9f6aSratchov #include "dev.h" 2387bc9f6aSratchov #include "dsp.h" 2487bc9f6aSratchov #include "siofile.h" 2587bc9f6aSratchov #include "midi.h" 26db7ff504Sratchov #include "opt.h" 2787bc9f6aSratchov #include "sysex.h" 2887bc9f6aSratchov #include "utils.h" 2987bc9f6aSratchov 306f413563Sratchov void zomb_onmove(void *); 314182f7f9Sratchov void zomb_onvol(void *); 3287bc9f6aSratchov void zomb_fill(void *); 3387bc9f6aSratchov void zomb_flush(void *); 3487bc9f6aSratchov void zomb_eof(void *); 3587bc9f6aSratchov void zomb_exit(void *); 3687bc9f6aSratchov 37fcda7a7eSratchov void dev_mix_badd(struct dev *, struct slot *); 38fcda7a7eSratchov void dev_mix_adjvol(struct dev *); 39fcda7a7eSratchov void dev_sub_bcopy(struct dev *, struct slot *); 40fcda7a7eSratchov 41fcda7a7eSratchov void dev_onmove(struct dev *, int); 42fcda7a7eSratchov void dev_master(struct dev *, unsigned int); 43fcda7a7eSratchov void dev_cycle(struct dev *); 444182f7f9Sratchov void dev_adjpar(struct dev *, int, int, int); 45500ebc2cSratchov int dev_allocbufs(struct dev *); 46500ebc2cSratchov void dev_freebufs(struct dev *); 47fcda7a7eSratchov int dev_ref(struct dev *); 48fcda7a7eSratchov void dev_unref(struct dev *); 49fcda7a7eSratchov int dev_init(struct dev *); 50fcda7a7eSratchov void dev_done(struct dev *); 51fcda7a7eSratchov struct dev *dev_bynum(int); 52fcda7a7eSratchov void dev_del(struct dev *); 53fcda7a7eSratchov unsigned int dev_roundof(struct dev *, unsigned int); 54fcda7a7eSratchov void dev_wakeup(struct dev *); 55fcda7a7eSratchov 56d07fece6Sratchov void slot_ctlname(struct slot *, char *, size_t); 57fcda7a7eSratchov void slot_del(struct slot *); 58fcda7a7eSratchov void slot_setvol(struct slot *, unsigned int); 59fcda7a7eSratchov void slot_ready(struct slot *); 6052217fe5Sratchov void slot_allocbufs(struct slot *); 6152217fe5Sratchov void slot_freebufs(struct slot *); 62f75a7ae7Sratchov void slot_skip_update(struct slot *); 63fcda7a7eSratchov void slot_write(struct slot *); 64fcda7a7eSratchov void slot_read(struct slot *); 65f75a7ae7Sratchov int slot_skip(struct slot *); 66fcda7a7eSratchov 6787bc9f6aSratchov struct slotops zomb_slotops = { 6887bc9f6aSratchov zomb_onmove, 6987bc9f6aSratchov zomb_onvol, 7087bc9f6aSratchov zomb_fill, 7187bc9f6aSratchov zomb_flush, 7287bc9f6aSratchov zomb_eof, 7387bc9f6aSratchov zomb_exit 7487bc9f6aSratchov }; 7587bc9f6aSratchov 7699580020Sratchov struct ctl *ctl_list = NULL; 7787bc9f6aSratchov struct dev *dev_list = NULL; 7887bc9f6aSratchov unsigned int dev_sndnum = 0; 7987bc9f6aSratchov 80e5be4cb8Sratchov struct ctlslot ctlslot_array[DEV_NCTLSLOT]; 810600d38bSratchov struct slot slot_array[DEV_NSLOT]; 820600d38bSratchov unsigned int slot_serial; /* for slot allocation */ 830600d38bSratchov 840e6be583Sratchov /* 850e6be583Sratchov * we support/need a single MTC clock source only 860e6be583Sratchov */ 870e6be583Sratchov struct mtc mtc_array[1] = { 880e6be583Sratchov {.dev = NULL, .tstate = MTC_STOP} 890e6be583Sratchov }; 900e6be583Sratchov 910600d38bSratchov void 920600d38bSratchov slot_array_init(void) 930600d38bSratchov { 940600d38bSratchov unsigned int i; 950600d38bSratchov 960600d38bSratchov for (i = 0; i < DEV_NSLOT; i++) { 970600d38bSratchov slot_array[i].unit = i; 980600d38bSratchov slot_array[i].ops = NULL; 990600d38bSratchov slot_array[i].vol = MIDI_MAXCTL; 100c67d5b9aSratchov slot_array[i].opt = NULL; 1010600d38bSratchov slot_array[i].serial = slot_serial++; 1020600d38bSratchov memset(slot_array[i].name, 0, SLOT_NAMEMAX); 1030600d38bSratchov } 1040600d38bSratchov } 1050600d38bSratchov 10687bc9f6aSratchov void 107d07fece6Sratchov slot_ctlname(struct slot *s, char *name, size_t size) 108d07fece6Sratchov { 109d07fece6Sratchov snprintf(name, size, "%s%u", s->name, s->unit); 110d07fece6Sratchov } 111d07fece6Sratchov 112d07fece6Sratchov void 1136f413563Sratchov zomb_onmove(void *arg) 11487bc9f6aSratchov { 11587bc9f6aSratchov } 11687bc9f6aSratchov 11787bc9f6aSratchov void 1184182f7f9Sratchov zomb_onvol(void *arg) 11987bc9f6aSratchov { 12087bc9f6aSratchov } 12187bc9f6aSratchov 12287bc9f6aSratchov void 12387bc9f6aSratchov zomb_fill(void *arg) 12487bc9f6aSratchov { 12587bc9f6aSratchov } 12687bc9f6aSratchov 12787bc9f6aSratchov void 12887bc9f6aSratchov zomb_flush(void *arg) 12987bc9f6aSratchov { 13087bc9f6aSratchov } 13187bc9f6aSratchov 13287bc9f6aSratchov void 13387bc9f6aSratchov zomb_eof(void *arg) 13487bc9f6aSratchov { 13587bc9f6aSratchov struct slot *s = arg; 13687bc9f6aSratchov 13787bc9f6aSratchov #ifdef DEBUG 138*7b639200Sratchov logx(3, "%s%u: %s", s->name, s->unit, __func__); 13987bc9f6aSratchov #endif 14087bc9f6aSratchov s->ops = NULL; 14187bc9f6aSratchov } 14287bc9f6aSratchov 14387bc9f6aSratchov void 14487bc9f6aSratchov zomb_exit(void *arg) 14587bc9f6aSratchov { 14687bc9f6aSratchov #ifdef DEBUG 14787bc9f6aSratchov struct slot *s = arg; 14887bc9f6aSratchov 149*7b639200Sratchov logx(3, "%s%u: %s", s->name, s->unit, __func__); 15087bc9f6aSratchov #endif 15187bc9f6aSratchov } 15287bc9f6aSratchov 153*7b639200Sratchov size_t 154*7b639200Sratchov chans_fmt(char *buf, size_t size, int mode, int pmin, int pmax, int rmin, int rmax) 155*7b639200Sratchov { 156*7b639200Sratchov const char *sep = ""; 157*7b639200Sratchov char *end = buf + size; 158*7b639200Sratchov char *p = buf; 159*7b639200Sratchov 160*7b639200Sratchov if (mode & MODE_PLAY) { 161*7b639200Sratchov p += snprintf(p, p < end ? end - p : 0, "play %d:%d", pmin, pmax); 162*7b639200Sratchov sep = ", "; 163*7b639200Sratchov } 164*7b639200Sratchov if (mode & MODE_RECMASK) { 165*7b639200Sratchov p += snprintf(p, p < end ? end - p : 0, "%s%s %d:%d", sep, 166*7b639200Sratchov (mode & MODE_MON) ? "mon" : "rec", rmin, rmax); 167*7b639200Sratchov } 168*7b639200Sratchov 169*7b639200Sratchov return p - buf; 170*7b639200Sratchov } 171*7b639200Sratchov 17287bc9f6aSratchov /* 1735fb8f3c9Sratchov * Broadcast MIDI data to all opts using this device 1745fb8f3c9Sratchov */ 1755fb8f3c9Sratchov void 1765fb8f3c9Sratchov dev_midi_send(struct dev *d, void *msg, int msglen) 1775fb8f3c9Sratchov { 1785fb8f3c9Sratchov struct opt *o; 1795fb8f3c9Sratchov 1805fb8f3c9Sratchov for (o = opt_list; o != NULL; o = o->next) { 1815fb8f3c9Sratchov if (o->dev != d) 1825fb8f3c9Sratchov continue; 1835fb8f3c9Sratchov midi_send(o->midi, msg, msglen); 1845fb8f3c9Sratchov } 1855fb8f3c9Sratchov } 1865fb8f3c9Sratchov 1875fb8f3c9Sratchov /* 18887bc9f6aSratchov * send a quarter frame MTC message 18987bc9f6aSratchov */ 19087bc9f6aSratchov void 1910e6be583Sratchov mtc_midi_qfr(struct mtc *mtc, int delta) 19287bc9f6aSratchov { 19387bc9f6aSratchov unsigned char buf[2]; 19487bc9f6aSratchov unsigned int data; 19587bc9f6aSratchov int qfrlen; 19687bc9f6aSratchov 1970e6be583Sratchov mtc->delta += delta * MTC_SEC; 1980e6be583Sratchov qfrlen = mtc->dev->rate * (MTC_SEC / (4 * mtc->fps)); 1990e6be583Sratchov while (mtc->delta >= qfrlen) { 2000e6be583Sratchov switch (mtc->qfr) { 20187bc9f6aSratchov case 0: 2020e6be583Sratchov data = mtc->fr & 0xf; 20387bc9f6aSratchov break; 20487bc9f6aSratchov case 1: 2050e6be583Sratchov data = mtc->fr >> 4; 20687bc9f6aSratchov break; 20787bc9f6aSratchov case 2: 2080e6be583Sratchov data = mtc->sec & 0xf; 20987bc9f6aSratchov break; 21087bc9f6aSratchov case 3: 2110e6be583Sratchov data = mtc->sec >> 4; 21287bc9f6aSratchov break; 21387bc9f6aSratchov case 4: 2140e6be583Sratchov data = mtc->min & 0xf; 21587bc9f6aSratchov break; 21687bc9f6aSratchov case 5: 2170e6be583Sratchov data = mtc->min >> 4; 21887bc9f6aSratchov break; 21987bc9f6aSratchov case 6: 2200e6be583Sratchov data = mtc->hr & 0xf; 22187bc9f6aSratchov break; 22287bc9f6aSratchov case 7: 2230e6be583Sratchov data = (mtc->hr >> 4) | (mtc->fps_id << 1); 22487bc9f6aSratchov /* 22587bc9f6aSratchov * tick messages are sent 2 frames ahead 22687bc9f6aSratchov */ 2270e6be583Sratchov mtc->fr += 2; 2280e6be583Sratchov if (mtc->fr < mtc->fps) 22987bc9f6aSratchov break; 2300e6be583Sratchov mtc->fr -= mtc->fps; 2310e6be583Sratchov mtc->sec++; 2320e6be583Sratchov if (mtc->sec < 60) 23387bc9f6aSratchov break; 2340e6be583Sratchov mtc->sec = 0; 2350e6be583Sratchov mtc->min++; 2360e6be583Sratchov if (mtc->min < 60) 23787bc9f6aSratchov break; 2380e6be583Sratchov mtc->min = 0; 2390e6be583Sratchov mtc->hr++; 2400e6be583Sratchov if (mtc->hr < 24) 24187bc9f6aSratchov break; 2420e6be583Sratchov mtc->hr = 0; 24387bc9f6aSratchov break; 24487bc9f6aSratchov default: 24587bc9f6aSratchov /* NOTREACHED */ 24687bc9f6aSratchov data = 0; 24787bc9f6aSratchov } 24887bc9f6aSratchov buf[0] = 0xf1; 2490e6be583Sratchov buf[1] = (mtc->qfr << 4) | data; 2500e6be583Sratchov mtc->qfr++; 2510e6be583Sratchov mtc->qfr &= 7; 2520e6be583Sratchov dev_midi_send(mtc->dev, buf, 2); 2530e6be583Sratchov mtc->delta -= qfrlen; 25487bc9f6aSratchov } 25587bc9f6aSratchov } 25687bc9f6aSratchov 25787bc9f6aSratchov /* 25887bc9f6aSratchov * send a full frame MTC message 25987bc9f6aSratchov */ 26087bc9f6aSratchov void 2610e6be583Sratchov mtc_midi_full(struct mtc *mtc) 26287bc9f6aSratchov { 26387bc9f6aSratchov struct sysex x; 26487bc9f6aSratchov unsigned int fps; 26587bc9f6aSratchov 266f51cdaacSratchov mtc->delta = -MTC_SEC * (int)mtc->dev->bufsz; 2670e6be583Sratchov if (mtc->dev->rate % (30 * 4 * mtc->dev->round) == 0) { 2680e6be583Sratchov mtc->fps_id = MTC_FPS_30; 2690e6be583Sratchov mtc->fps = 30; 2700e6be583Sratchov } else if (mtc->dev->rate % (25 * 4 * mtc->dev->round) == 0) { 2710e6be583Sratchov mtc->fps_id = MTC_FPS_25; 2720e6be583Sratchov mtc->fps = 25; 27387bc9f6aSratchov } else { 2740e6be583Sratchov mtc->fps_id = MTC_FPS_24; 2750e6be583Sratchov mtc->fps = 24; 27687bc9f6aSratchov } 27787bc9f6aSratchov #ifdef DEBUG 278*7b639200Sratchov logx(3, "%s: mtc full frame at %d, %d fps", mtc->dev->path, mtc->delta, mtc->fps); 27987bc9f6aSratchov #endif 2800e6be583Sratchov fps = mtc->fps; 2810e6be583Sratchov mtc->hr = (mtc->origin / (MTC_SEC * 3600)) % 24; 2820e6be583Sratchov mtc->min = (mtc->origin / (MTC_SEC * 60)) % 60; 2830e6be583Sratchov mtc->sec = (mtc->origin / (MTC_SEC)) % 60; 2840e6be583Sratchov mtc->fr = (mtc->origin / (MTC_SEC / fps)) % fps; 28587bc9f6aSratchov 28687bc9f6aSratchov x.start = SYSEX_START; 28787bc9f6aSratchov x.type = SYSEX_TYPE_RT; 28820b458daSratchov x.dev = SYSEX_DEV_ANY; 28987bc9f6aSratchov x.id0 = SYSEX_MTC; 29087bc9f6aSratchov x.id1 = SYSEX_MTC_FULL; 2910e6be583Sratchov x.u.full.hr = mtc->hr | (mtc->fps_id << 5); 2920e6be583Sratchov x.u.full.min = mtc->min; 2930e6be583Sratchov x.u.full.sec = mtc->sec; 2940e6be583Sratchov x.u.full.fr = mtc->fr; 29587bc9f6aSratchov x.u.full.end = SYSEX_END; 2960e6be583Sratchov mtc->qfr = 0; 2970e6be583Sratchov dev_midi_send(mtc->dev, (unsigned char *)&x, SYSEX_SIZE(full)); 29887bc9f6aSratchov } 29987bc9f6aSratchov 30087bc9f6aSratchov /* 30187bc9f6aSratchov * send a volume change MIDI message 30287bc9f6aSratchov */ 30387bc9f6aSratchov void 30487bc9f6aSratchov dev_midi_vol(struct dev *d, struct slot *s) 30587bc9f6aSratchov { 30687bc9f6aSratchov unsigned char msg[3]; 30787bc9f6aSratchov 3080600d38bSratchov msg[0] = MIDI_CTL | (s - slot_array); 30987bc9f6aSratchov msg[1] = MIDI_CTL_VOL; 31087bc9f6aSratchov msg[2] = s->vol; 3115fb8f3c9Sratchov dev_midi_send(d, msg, 3); 31287bc9f6aSratchov } 31387bc9f6aSratchov 31487bc9f6aSratchov /* 31587bc9f6aSratchov * send a master volume MIDI message 31687bc9f6aSratchov */ 31787bc9f6aSratchov void 31887bc9f6aSratchov dev_midi_master(struct dev *d) 31987bc9f6aSratchov { 320f08c7e02Sratchov struct ctl *c; 321f08c7e02Sratchov unsigned int master, v; 32287bc9f6aSratchov struct sysex x; 32387bc9f6aSratchov 324f08c7e02Sratchov if (d->master_enabled) 325f08c7e02Sratchov master = d->master; 326f08c7e02Sratchov else { 327f08c7e02Sratchov master = 0; 32899580020Sratchov for (c = ctl_list; c != NULL; c = c->next) { 329f08c7e02Sratchov if (c->type != CTL_NUM || 33099580020Sratchov strcmp(c->group, d->name) != 0 || 331f08c7e02Sratchov strcmp(c->node0.name, "output") != 0 || 332f08c7e02Sratchov strcmp(c->func, "level") != 0) 333f08c7e02Sratchov continue; 33499580020Sratchov if (c->u.any.arg0 != d) 33599580020Sratchov continue; 336f08c7e02Sratchov v = (c->curval * 127 + c->maxval / 2) / c->maxval; 337f08c7e02Sratchov if (master < v) 338f08c7e02Sratchov master = v; 339f08c7e02Sratchov } 340f08c7e02Sratchov } 341f08c7e02Sratchov 34287bc9f6aSratchov memset(&x, 0, sizeof(struct sysex)); 34387bc9f6aSratchov x.start = SYSEX_START; 34487bc9f6aSratchov x.type = SYSEX_TYPE_RT; 34520b458daSratchov x.dev = SYSEX_DEV_ANY; 34687bc9f6aSratchov x.id0 = SYSEX_CONTROL; 34787bc9f6aSratchov x.id1 = SYSEX_MASTER; 34887bc9f6aSratchov x.u.master.fine = 0; 349f08c7e02Sratchov x.u.master.coarse = master; 35087bc9f6aSratchov x.u.master.end = SYSEX_END; 3515fb8f3c9Sratchov dev_midi_send(d, (unsigned char *)&x, SYSEX_SIZE(master)); 35287bc9f6aSratchov } 35387bc9f6aSratchov 35487bc9f6aSratchov /* 35587bc9f6aSratchov * send a sndiod-specific slot description MIDI message 35687bc9f6aSratchov */ 35787bc9f6aSratchov void 35887bc9f6aSratchov dev_midi_slotdesc(struct dev *d, struct slot *s) 35987bc9f6aSratchov { 36087bc9f6aSratchov struct sysex x; 36187bc9f6aSratchov 36287bc9f6aSratchov memset(&x, 0, sizeof(struct sysex)); 36387bc9f6aSratchov x.start = SYSEX_START; 36487bc9f6aSratchov x.type = SYSEX_TYPE_EDU; 36520b458daSratchov x.dev = SYSEX_DEV_ANY; 36687bc9f6aSratchov x.id0 = SYSEX_AUCAT; 36787bc9f6aSratchov x.id1 = SYSEX_AUCAT_SLOTDESC; 368c67d5b9aSratchov if (s->opt != NULL && s->opt->dev == d) 369d07fece6Sratchov slot_ctlname(s, (char *)x.u.slotdesc.name, SYSEX_NAMELEN); 3700600d38bSratchov x.u.slotdesc.chan = (s - slot_array); 37187bc9f6aSratchov x.u.slotdesc.end = SYSEX_END; 3725fb8f3c9Sratchov dev_midi_send(d, (unsigned char *)&x, SYSEX_SIZE(slotdesc)); 37387bc9f6aSratchov } 37487bc9f6aSratchov 37587bc9f6aSratchov void 37687bc9f6aSratchov dev_midi_dump(struct dev *d) 37787bc9f6aSratchov { 37887bc9f6aSratchov struct sysex x; 37987bc9f6aSratchov struct slot *s; 38087bc9f6aSratchov int i; 38187bc9f6aSratchov 38287bc9f6aSratchov dev_midi_master(d); 3830600d38bSratchov for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 384c67d5b9aSratchov if (s->opt != NULL && s->opt->dev != d) 3850600d38bSratchov continue; 38687bc9f6aSratchov dev_midi_slotdesc(d, s); 38787bc9f6aSratchov dev_midi_vol(d, s); 38887bc9f6aSratchov } 38987bc9f6aSratchov x.start = SYSEX_START; 39087bc9f6aSratchov x.type = SYSEX_TYPE_EDU; 39120b458daSratchov x.dev = SYSEX_DEV_ANY; 39287bc9f6aSratchov x.id0 = SYSEX_AUCAT; 39387bc9f6aSratchov x.id1 = SYSEX_AUCAT_DUMPEND; 39487bc9f6aSratchov x.u.dumpend.end = SYSEX_END; 3955fb8f3c9Sratchov dev_midi_send(d, (unsigned char *)&x, SYSEX_SIZE(dumpend)); 39687bc9f6aSratchov } 39787bc9f6aSratchov 398f75a7ae7Sratchov int 399f75a7ae7Sratchov slot_skip(struct slot *s) 40087bc9f6aSratchov { 401f75a7ae7Sratchov unsigned char *data = (unsigned char *)0xdeadbeef; /* please gcc */ 402f75a7ae7Sratchov int max, count; 40387bc9f6aSratchov 404f75a7ae7Sratchov max = s->skip; 405f75a7ae7Sratchov while (s->skip > 0) { 406f75a7ae7Sratchov if (s->pstate != SLOT_STOP && (s->mode & MODE_RECMASK)) { 40787bc9f6aSratchov data = abuf_wgetblk(&s->sub.buf, &count); 40887bc9f6aSratchov if (count < s->round * s->sub.bpf) 40987bc9f6aSratchov break; 410f75a7ae7Sratchov } 411f75a7ae7Sratchov if (s->mode & MODE_PLAY) { 412f75a7ae7Sratchov if (s->mix.buf.used < s->round * s->mix.bpf) 413f75a7ae7Sratchov break; 414f75a7ae7Sratchov } 41587bc9f6aSratchov #ifdef DEBUG 416*7b639200Sratchov logx(4, "%s%u: skipped a cycle", s->name, s->unit); 41787bc9f6aSratchov #endif 418f75a7ae7Sratchov if (s->pstate != SLOT_STOP && (s->mode & MODE_RECMASK)) { 41987bc9f6aSratchov if (s->sub.encbuf) 42087bc9f6aSratchov enc_sil_do(&s->sub.enc, data, s->round); 42187bc9f6aSratchov else 42287bc9f6aSratchov memset(data, 0, s->round * s->sub.bpf); 42387bc9f6aSratchov abuf_wcommit(&s->sub.buf, s->round * s->sub.bpf); 42487bc9f6aSratchov } 425f75a7ae7Sratchov if (s->mode & MODE_PLAY) { 426f75a7ae7Sratchov abuf_rdiscard(&s->mix.buf, s->round * s->mix.bpf); 427f75a7ae7Sratchov } 428f75a7ae7Sratchov s->skip--; 429f75a7ae7Sratchov } 430f75a7ae7Sratchov return max - s->skip; 43187bc9f6aSratchov } 43287bc9f6aSratchov 43387bc9f6aSratchov /* 434b4304b39Sratchov * Mix the slot input block over the output block 43587bc9f6aSratchov */ 43687bc9f6aSratchov void 43787bc9f6aSratchov dev_mix_badd(struct dev *d, struct slot *s) 43887bc9f6aSratchov { 439b4304b39Sratchov adata_t *idata, *odata, *in; 440b4304b39Sratchov int icount, i, offs, vol, nch; 44187bc9f6aSratchov 44287bc9f6aSratchov odata = DEV_PBUF(d); 44387bc9f6aSratchov idata = (adata_t *)abuf_rgetblk(&s->mix.buf, &icount); 44487bc9f6aSratchov #ifdef DEBUG 44587bc9f6aSratchov if (icount < s->round * s->mix.bpf) { 446*7b639200Sratchov logx(0, "%s%u: not enough data to mix (%u bytes)", 447*7b639200Sratchov s->name, s->unit, icount); 44887bc9f6aSratchov panic(); 44987bc9f6aSratchov } 45087bc9f6aSratchov #endif 45194f976feSratchov if (!(s->opt->mode & MODE_PLAY)) { 45294f976feSratchov /* 45394f976feSratchov * playback not allowed in opt structure, produce silence 45494f976feSratchov */ 45594f976feSratchov abuf_rdiscard(&s->mix.buf, s->round * s->mix.bpf); 45694f976feSratchov return; 45794f976feSratchov } 45894f976feSratchov 459b4304b39Sratchov 460b4304b39Sratchov /* 461b4304b39Sratchov * Apply the following processing chain: 462b4304b39Sratchov * 463b4304b39Sratchov * dec -> resamp-> cmap 464b4304b39Sratchov * 465b4304b39Sratchov * where the first two are optional. 466b4304b39Sratchov */ 467b4304b39Sratchov 468b4304b39Sratchov in = idata; 469b4304b39Sratchov 470b4304b39Sratchov if (s->mix.decbuf) { 471b4304b39Sratchov dec_do(&s->mix.dec, (void *)in, s->mix.decbuf, s->round); 472b4304b39Sratchov in = s->mix.decbuf; 473b4304b39Sratchov } 474b4304b39Sratchov 475b4304b39Sratchov if (s->mix.resampbuf) { 476b47fb7daSratchov resamp_do(&s->mix.resamp, 477b47fb7daSratchov in, s->mix.resampbuf, s->round, d->round); 478b4304b39Sratchov in = s->mix.resampbuf; 479b4304b39Sratchov } 480b4304b39Sratchov 481b4304b39Sratchov nch = s->mix.cmap.nch; 482b4304b39Sratchov vol = ADATA_MUL(s->mix.weight, s->mix.vol) / s->mix.join; 483b4304b39Sratchov cmap_add(&s->mix.cmap, in, odata, vol, d->round); 484b4304b39Sratchov 485b4304b39Sratchov offs = 0; 486b4304b39Sratchov for (i = s->mix.join - 1; i > 0; i--) { 487b4304b39Sratchov offs += nch; 488b4304b39Sratchov cmap_add(&s->mix.cmap, in + offs, odata, vol, d->round); 489b4304b39Sratchov } 490b4304b39Sratchov 491b4304b39Sratchov offs = 0; 492b4304b39Sratchov for (i = s->mix.expand - 1; i > 0; i--) { 493b4304b39Sratchov offs += nch; 494b4304b39Sratchov cmap_add(&s->mix.cmap, in, odata + offs, vol, d->round); 495b4304b39Sratchov } 496b4304b39Sratchov 49787bc9f6aSratchov abuf_rdiscard(&s->mix.buf, s->round * s->mix.bpf); 49887bc9f6aSratchov } 49987bc9f6aSratchov 50087bc9f6aSratchov /* 50187bc9f6aSratchov * Normalize input levels. 50287bc9f6aSratchov */ 50387bc9f6aSratchov void 50487bc9f6aSratchov dev_mix_adjvol(struct dev *d) 50587bc9f6aSratchov { 50687bc9f6aSratchov unsigned int n; 50787bc9f6aSratchov struct slot *i, *j; 50823321a5cSratchov int jcmax, icmax, weight; 50987bc9f6aSratchov 51087bc9f6aSratchov for (i = d->slot_list; i != NULL; i = i->next) { 51187bc9f6aSratchov if (!(i->mode & MODE_PLAY)) 51287bc9f6aSratchov continue; 51323321a5cSratchov icmax = i->opt->pmin + i->mix.nch - 1; 51487bc9f6aSratchov weight = ADATA_UNIT; 51587bc9f6aSratchov if (d->autovol) { 51687bc9f6aSratchov /* 51787bc9f6aSratchov * count the number of inputs that have 51887bc9f6aSratchov * overlapping channel sets 51987bc9f6aSratchov */ 52087bc9f6aSratchov n = 0; 52187bc9f6aSratchov for (j = d->slot_list; j != NULL; j = j->next) { 52287bc9f6aSratchov if (!(j->mode & MODE_PLAY)) 52387bc9f6aSratchov continue; 52423321a5cSratchov jcmax = j->opt->pmin + j->mix.nch - 1; 52523321a5cSratchov if (i->opt->pmin <= jcmax && 52623321a5cSratchov icmax >= j->opt->pmin) 52787bc9f6aSratchov n++; 52887bc9f6aSratchov } 52987bc9f6aSratchov weight /= n; 53087bc9f6aSratchov } 53102eb71b7Sratchov if (weight > i->opt->maxweight) 53202eb71b7Sratchov weight = i->opt->maxweight; 533f08c7e02Sratchov i->mix.weight = d->master_enabled ? 534f08c7e02Sratchov ADATA_MUL(weight, MIDI_TO_ADATA(d->master)) : weight; 53587bc9f6aSratchov #ifdef DEBUG 536*7b639200Sratchov logx(3, "%s%u: set weight: %d / %d", i->name, i->unit, i->mix.weight, 537*7b639200Sratchov i->opt->maxweight); 53887bc9f6aSratchov #endif 53987bc9f6aSratchov } 54087bc9f6aSratchov } 54187bc9f6aSratchov 54287bc9f6aSratchov /* 54387bc9f6aSratchov * Copy data from slot to device 54487bc9f6aSratchov */ 54587bc9f6aSratchov void 54687bc9f6aSratchov dev_sub_bcopy(struct dev *d, struct slot *s) 54787bc9f6aSratchov { 548b4304b39Sratchov adata_t *idata, *enc_out, *resamp_out, *cmap_out; 549b4304b39Sratchov void *odata; 5508aadda71Sratchov int ocount, moffs; 55187bc9f6aSratchov 552b4304b39Sratchov int i, vol, offs, nch; 553b4304b39Sratchov 554b4304b39Sratchov 55587bc9f6aSratchov odata = (adata_t *)abuf_wgetblk(&s->sub.buf, &ocount); 55687bc9f6aSratchov #ifdef DEBUG 55787bc9f6aSratchov if (ocount < s->round * s->sub.bpf) { 558*7b639200Sratchov logx(0, "dev_sub_bcopy: not enough space"); 55987bc9f6aSratchov panic(); 56087bc9f6aSratchov } 56187bc9f6aSratchov #endif 56294f976feSratchov if (s->opt->mode & MODE_MON) { 56394f976feSratchov moffs = d->poffs + d->round; 56494f976feSratchov if (moffs == d->psize) 56594f976feSratchov moffs = 0; 56694f976feSratchov idata = d->pbuf + moffs * d->pchan; 56794f976feSratchov } else if (s->opt->mode & MODE_REC) { 56894f976feSratchov idata = d->rbuf; 56994f976feSratchov } else { 57094f976feSratchov /* 57194f976feSratchov * recording not allowed in opt structure, produce silence 57294f976feSratchov */ 57394f976feSratchov enc_sil_do(&s->sub.enc, odata, s->round); 57494f976feSratchov abuf_wcommit(&s->sub.buf, s->round * s->sub.bpf); 57594f976feSratchov return; 57694f976feSratchov } 577b4304b39Sratchov 578b4304b39Sratchov /* 579b4304b39Sratchov * Apply the following processing chain: 580b4304b39Sratchov * 581b4304b39Sratchov * cmap -> resamp -> enc 582b4304b39Sratchov * 583b4304b39Sratchov * where the last two are optional. 584b4304b39Sratchov */ 585b4304b39Sratchov 586b4304b39Sratchov enc_out = odata; 587b4304b39Sratchov resamp_out = s->sub.encbuf ? s->sub.encbuf : enc_out; 588b4304b39Sratchov cmap_out = s->sub.resampbuf ? s->sub.resampbuf : resamp_out; 589b4304b39Sratchov 590b4304b39Sratchov nch = s->sub.cmap.nch; 591b4304b39Sratchov vol = ADATA_UNIT / s->sub.join; 592b4304b39Sratchov cmap_copy(&s->sub.cmap, idata, cmap_out, vol, d->round); 593b4304b39Sratchov 594b4304b39Sratchov offs = 0; 595b4304b39Sratchov for (i = s->sub.join - 1; i > 0; i--) { 596b4304b39Sratchov offs += nch; 597b4304b39Sratchov cmap_add(&s->sub.cmap, idata + offs, cmap_out, vol, d->round); 598b4304b39Sratchov } 599b4304b39Sratchov 600b4304b39Sratchov offs = 0; 601b4304b39Sratchov for (i = s->sub.expand - 1; i > 0; i--) { 602b4304b39Sratchov offs += nch; 603b4304b39Sratchov cmap_copy(&s->sub.cmap, idata, cmap_out + offs, vol, d->round); 604b4304b39Sratchov } 605b4304b39Sratchov 606b4304b39Sratchov if (s->sub.resampbuf) { 607b4304b39Sratchov resamp_do(&s->sub.resamp, 608b47fb7daSratchov s->sub.resampbuf, resamp_out, d->round, s->round); 609b4304b39Sratchov } 610b4304b39Sratchov 611b4304b39Sratchov if (s->sub.encbuf) 612b4304b39Sratchov enc_do(&s->sub.enc, s->sub.encbuf, (void *)enc_out, s->round); 613b4304b39Sratchov 614b4304b39Sratchov abuf_wcommit(&s->sub.buf, s->round * s->sub.bpf); 61587bc9f6aSratchov } 61687bc9f6aSratchov 617b85bdbe2Sratchov /* 618b85bdbe2Sratchov * run a one block cycle: consume one recorded block from 619b85bdbe2Sratchov * rbuf and produce one play block in pbuf 620b85bdbe2Sratchov */ 62187bc9f6aSratchov void 622b85bdbe2Sratchov dev_cycle(struct dev *d) 62387bc9f6aSratchov { 62487bc9f6aSratchov struct slot *s, **ps; 625f75a7ae7Sratchov unsigned char *base; 626f75a7ae7Sratchov int nsamp; 62787bc9f6aSratchov 628b85bdbe2Sratchov /* 629b85bdbe2Sratchov * check if the device is actually used. If it isn't, 630b85bdbe2Sratchov * then close it 631b85bdbe2Sratchov */ 6320add8717Sratchov if (d->slot_list == NULL && d->idle >= d->bufsz && 6330add8717Sratchov (mtc_array[0].dev != d || mtc_array[0].tstate != MTC_RUN)) { 634*7b639200Sratchov logx(2, "%s: device stopped", d->path); 635b85bdbe2Sratchov dev_sio_stop(d); 636b85bdbe2Sratchov d->pstate = DEV_INIT; 637b85bdbe2Sratchov if (d->refcnt == 0) 638b85bdbe2Sratchov dev_close(d); 639b85bdbe2Sratchov return; 640b85bdbe2Sratchov } 641b85bdbe2Sratchov 642b85bdbe2Sratchov if (d->prime > 0) { 643b85bdbe2Sratchov #ifdef DEBUG 644*7b639200Sratchov logx(4, "%s: empty cycle, prime = %u", d->path, d->prime); 645b85bdbe2Sratchov #endif 646b85bdbe2Sratchov base = (unsigned char *)DEV_PBUF(d); 647b85bdbe2Sratchov nsamp = d->round * d->pchan; 648b85bdbe2Sratchov memset(base, 0, nsamp * sizeof(adata_t)); 649b85bdbe2Sratchov if (d->encbuf) { 650b85bdbe2Sratchov enc_do(&d->enc, (unsigned char *)DEV_PBUF(d), 651b85bdbe2Sratchov d->encbuf, d->round); 652b85bdbe2Sratchov } 653b85bdbe2Sratchov d->prime -= d->round; 654b85bdbe2Sratchov return; 655b85bdbe2Sratchov } 656b85bdbe2Sratchov 657f75a7ae7Sratchov d->delta -= d->round; 65887bc9f6aSratchov #ifdef DEBUG 659*7b639200Sratchov logx(4, "%s: full cycle: delta = %d", d->path, d->delta); 66087bc9f6aSratchov #endif 661f75a7ae7Sratchov if (d->mode & MODE_PLAY) { 662f75a7ae7Sratchov base = (unsigned char *)DEV_PBUF(d); 663f75a7ae7Sratchov nsamp = d->round * d->pchan; 664f75a7ae7Sratchov memset(base, 0, nsamp * sizeof(adata_t)); 665f75a7ae7Sratchov } 66687bc9f6aSratchov if ((d->mode & MODE_REC) && d->decbuf) 66787bc9f6aSratchov dec_do(&d->dec, d->decbuf, (unsigned char *)d->rbuf, d->round); 66887bc9f6aSratchov ps = &d->slot_list; 66987bc9f6aSratchov while ((s = *ps) != NULL) { 670f75a7ae7Sratchov #ifdef DEBUG 671*7b639200Sratchov logx(4, "%s%u: running, skip = %d", s->name, s->unit, s->skip); 672f75a7ae7Sratchov #endif 6730add8717Sratchov d->idle = 0; 6740add8717Sratchov 675f75a7ae7Sratchov /* 676f75a7ae7Sratchov * skip cycles for XRUN_SYNC correction 677f75a7ae7Sratchov */ 678f75a7ae7Sratchov slot_skip(s); 679f75a7ae7Sratchov if (s->skip < 0) { 680f75a7ae7Sratchov s->skip++; 68187bc9f6aSratchov ps = &s->next; 68287bc9f6aSratchov continue; 68387bc9f6aSratchov } 684f75a7ae7Sratchov 685f75a7ae7Sratchov #ifdef DEBUG 686f75a7ae7Sratchov if (s->pstate == SLOT_STOP && !(s->mode & MODE_PLAY)) { 687*7b639200Sratchov logx(0, "%s%u: rec-only slots can't be drained", 688*7b639200Sratchov s->name, s->unit); 689f75a7ae7Sratchov panic(); 690f75a7ae7Sratchov } 691f75a7ae7Sratchov #endif 692f75a7ae7Sratchov /* 693f75a7ae7Sratchov * check if stopped stream finished draining 694f75a7ae7Sratchov */ 695f75a7ae7Sratchov if (s->pstate == SLOT_STOP && 696f75a7ae7Sratchov s->mix.buf.used < s->round * s->mix.bpf) { 697f75a7ae7Sratchov /* 698f75a7ae7Sratchov * partial blocks are zero-filled by socket 699f75a7ae7Sratchov * layer, so s->mix.buf.used == 0 and we can 700f75a7ae7Sratchov * destroy the buffer 701f75a7ae7Sratchov */ 702f75a7ae7Sratchov *ps = s->next; 70352217fe5Sratchov s->pstate = SLOT_INIT; 70452217fe5Sratchov s->ops->eof(s->arg); 70552217fe5Sratchov slot_freebufs(s); 706f75a7ae7Sratchov dev_mix_adjvol(d); 70752217fe5Sratchov #ifdef DEBUG 708*7b639200Sratchov logx(3, "%s%u: drained", s->name, s->unit); 70952217fe5Sratchov #endif 71087bc9f6aSratchov continue; 71187bc9f6aSratchov } 712f75a7ae7Sratchov 713f75a7ae7Sratchov /* 714f75a7ae7Sratchov * check for xruns 715f75a7ae7Sratchov */ 716f75a7ae7Sratchov if (((s->mode & MODE_PLAY) && 717f75a7ae7Sratchov s->mix.buf.used < s->round * s->mix.bpf) || 718f75a7ae7Sratchov ((s->mode & MODE_RECMASK) && 719f75a7ae7Sratchov s->sub.buf.len - s->sub.buf.used < 720f75a7ae7Sratchov s->round * s->sub.bpf)) { 721f75a7ae7Sratchov 72287bc9f6aSratchov #ifdef DEBUG 723*7b639200Sratchov logx(3, "%s%u: xrun, pause cycle", s->name, s->unit); 72487bc9f6aSratchov #endif 725f75a7ae7Sratchov if (s->xrun == XRUN_IGNORE) { 726f75a7ae7Sratchov s->delta -= s->round; 72787bc9f6aSratchov ps = &s->next; 728f75a7ae7Sratchov } else if (s->xrun == XRUN_SYNC) { 729f75a7ae7Sratchov s->skip++; 73087bc9f6aSratchov ps = &s->next; 731f75a7ae7Sratchov } else if (s->xrun == XRUN_ERROR) { 73287bc9f6aSratchov s->ops->exit(s->arg); 73387bc9f6aSratchov *ps = s->next; 734f75a7ae7Sratchov } else { 735f75a7ae7Sratchov #ifdef DEBUG 736*7b639200Sratchov logx(0, "%s%u: bad xrun mode", s->name, s->unit); 737f75a7ae7Sratchov panic(); 738f75a7ae7Sratchov #endif 739f75a7ae7Sratchov } 74087bc9f6aSratchov continue; 74187bc9f6aSratchov } 742f75a7ae7Sratchov if ((s->mode & MODE_RECMASK) && !(s->pstate == SLOT_STOP)) { 743f75a7ae7Sratchov if (s->sub.prime == 0) { 74487bc9f6aSratchov dev_sub_bcopy(d, s); 74587bc9f6aSratchov s->ops->flush(s->arg); 746f75a7ae7Sratchov } else { 747f75a7ae7Sratchov #ifdef DEBUG 748*7b639200Sratchov logx(3, "%s%u: prime = %d", s->name, s->unit, 749*7b639200Sratchov s->sub.prime); 750f75a7ae7Sratchov #endif 751f75a7ae7Sratchov s->sub.prime--; 752f75a7ae7Sratchov } 75387bc9f6aSratchov } 7548aadda71Sratchov if (s->mode & MODE_PLAY) { 7558aadda71Sratchov dev_mix_badd(d, s); 7568aadda71Sratchov if (s->pstate != SLOT_STOP) 7578aadda71Sratchov s->ops->fill(s->arg); 7588aadda71Sratchov } 75987bc9f6aSratchov ps = &s->next; 76087bc9f6aSratchov } 761f75a7ae7Sratchov if ((d->mode & MODE_PLAY) && d->encbuf) { 762f75a7ae7Sratchov enc_do(&d->enc, (unsigned char *)DEV_PBUF(d), 763f75a7ae7Sratchov d->encbuf, d->round); 764f75a7ae7Sratchov } 76587bc9f6aSratchov } 76687bc9f6aSratchov 76787bc9f6aSratchov /* 76887bc9f6aSratchov * called at every clock tick by the device 76987bc9f6aSratchov */ 77087bc9f6aSratchov void 77187bc9f6aSratchov dev_onmove(struct dev *d, int delta) 77287bc9f6aSratchov { 77387bc9f6aSratchov long long pos; 77487bc9f6aSratchov struct slot *s, *snext; 77587bc9f6aSratchov 776f75a7ae7Sratchov d->delta += delta; 777f75a7ae7Sratchov 7780add8717Sratchov if (d->slot_list == NULL) 7790add8717Sratchov d->idle += delta; 7800add8717Sratchov 781f75a7ae7Sratchov for (s = d->slot_list; s != NULL; s = snext) { 78287bc9f6aSratchov /* 78387bc9f6aSratchov * s->ops->onmove() may remove the slot 78487bc9f6aSratchov */ 78587bc9f6aSratchov snext = s->next; 786540a9b64Sratchov pos = s->delta_rem + 787540a9b64Sratchov (long long)s->delta * d->round + 788540a9b64Sratchov (long long)delta * s->round; 789540a9b64Sratchov s->delta = pos / (int)d->round; 79087bc9f6aSratchov s->delta_rem = pos % d->round; 791540a9b64Sratchov if (s->delta_rem < 0) { 792540a9b64Sratchov s->delta_rem += d->round; 793540a9b64Sratchov s->delta--; 794540a9b64Sratchov } 79587bc9f6aSratchov if (s->delta >= 0) 7966f413563Sratchov s->ops->onmove(s->arg); 79787bc9f6aSratchov } 7980e6be583Sratchov 7990e6be583Sratchov if (mtc_array[0].dev == d && mtc_array[0].tstate == MTC_RUN) 8000e6be583Sratchov mtc_midi_qfr(&mtc_array[0], delta); 80187bc9f6aSratchov } 80287bc9f6aSratchov 80387bc9f6aSratchov void 80487bc9f6aSratchov dev_master(struct dev *d, unsigned int master) 80587bc9f6aSratchov { 806f08c7e02Sratchov struct ctl *c; 807f08c7e02Sratchov unsigned int v; 808f08c7e02Sratchov 809*7b639200Sratchov logx(2, "%s: master volume set to %u", d->path, master); 810*7b639200Sratchov 811f08c7e02Sratchov if (d->master_enabled) { 81287bc9f6aSratchov d->master = master; 81387bc9f6aSratchov if (d->mode & MODE_PLAY) 81487bc9f6aSratchov dev_mix_adjvol(d); 815f08c7e02Sratchov } else { 81699580020Sratchov for (c = ctl_list; c != NULL; c = c->next) { 81799580020Sratchov if (c->scope != CTL_HW || c->u.hw.dev != d) 81899580020Sratchov continue; 819f08c7e02Sratchov if (c->type != CTL_NUM || 82099580020Sratchov strcmp(c->group, d->name) != 0 || 821f08c7e02Sratchov strcmp(c->node0.name, "output") != 0 || 822f08c7e02Sratchov strcmp(c->func, "level") != 0) 823f08c7e02Sratchov continue; 824f08c7e02Sratchov v = (master * c->maxval + 64) / 127; 82599580020Sratchov ctl_setval(c, v); 826f08c7e02Sratchov } 827f08c7e02Sratchov } 82887bc9f6aSratchov } 82987bc9f6aSratchov 83087bc9f6aSratchov /* 83187bc9f6aSratchov * Create a sndio device 83287bc9f6aSratchov */ 83387bc9f6aSratchov struct dev * 83487bc9f6aSratchov dev_new(char *path, struct aparams *par, 83587bc9f6aSratchov unsigned int mode, unsigned int bufsz, unsigned int round, 83687bc9f6aSratchov unsigned int rate, unsigned int hold, unsigned int autovol) 83787bc9f6aSratchov { 8384e3adc96Sratchov struct dev *d, **pd; 83987bc9f6aSratchov 84087bc9f6aSratchov if (dev_sndnum == DEV_NMAX) { 841*7b639200Sratchov logx(1, "too many devices"); 84287bc9f6aSratchov return NULL; 84387bc9f6aSratchov } 84487bc9f6aSratchov d = xmalloc(sizeof(struct dev)); 84536355b88Sratchov d->path = path; 84687bc9f6aSratchov d->num = dev_sndnum++; 84787bc9f6aSratchov 84887bc9f6aSratchov d->reqpar = *par; 84987bc9f6aSratchov d->reqmode = mode; 85087bc9f6aSratchov d->reqpchan = d->reqrchan = 0; 85187bc9f6aSratchov d->reqbufsz = bufsz; 85287bc9f6aSratchov d->reqround = round; 85387bc9f6aSratchov d->reqrate = rate; 85487bc9f6aSratchov d->hold = hold; 85587bc9f6aSratchov d->autovol = autovol; 85687bc9f6aSratchov d->refcnt = 0; 85787bc9f6aSratchov d->pstate = DEV_CFG; 85887bc9f6aSratchov d->slot_list = NULL; 85987bc9f6aSratchov d->master = MIDI_MAXCTL; 8602e623bebSratchov d->master_enabled = 0; 86136355b88Sratchov d->alt_next = d; 86299580020Sratchov snprintf(d->name, CTL_NAMEMAX, "%u", d->num); 8634e3adc96Sratchov for (pd = &dev_list; *pd != NULL; pd = &(*pd)->next) 8644e3adc96Sratchov ; 8654e3adc96Sratchov d->next = *pd; 8664e3adc96Sratchov *pd = d; 86787bc9f6aSratchov return d; 86887bc9f6aSratchov } 86987bc9f6aSratchov 87087bc9f6aSratchov /* 87187bc9f6aSratchov * adjust device parameters and mode 87287bc9f6aSratchov */ 87387bc9f6aSratchov void 87487bc9f6aSratchov dev_adjpar(struct dev *d, int mode, 8754182f7f9Sratchov int pmax, int rmax) 87687bc9f6aSratchov { 87787bc9f6aSratchov d->reqmode |= mode & MODE_AUDIOMASK; 87887bc9f6aSratchov if (mode & MODE_PLAY) { 87987bc9f6aSratchov if (d->reqpchan < pmax + 1) 88087bc9f6aSratchov d->reqpchan = pmax + 1; 88187bc9f6aSratchov } 88287bc9f6aSratchov if (mode & MODE_REC) { 88387bc9f6aSratchov if (d->reqrchan < rmax + 1) 88487bc9f6aSratchov d->reqrchan = rmax + 1; 88587bc9f6aSratchov } 88687bc9f6aSratchov } 88787bc9f6aSratchov 88887bc9f6aSratchov /* 88987bc9f6aSratchov * Open the device with the dev_reqxxx capabilities. Setup a mixer, demuxer, 89087bc9f6aSratchov * monitor, midi control, and any necessary conversions. 891bd94a039Sedd * 892bd94a039Sedd * Note that record and play buffers are always allocated, even if the 893bd94a039Sedd * underlying device doesn't support both modes. 89487bc9f6aSratchov */ 89587bc9f6aSratchov int 896500ebc2cSratchov dev_allocbufs(struct dev *d) 89787bc9f6aSratchov { 898*7b639200Sratchov char enc_str[ENCMAX], chans_str[64]; 899*7b639200Sratchov 90087bc9f6aSratchov /* 901bd94a039Sedd * Create record buffer. 90287bc9f6aSratchov */ 903bd94a039Sedd 904bd94a039Sedd /* Create device <-> demuxer buffer */ 90587bc9f6aSratchov d->rbuf = xmalloc(d->round * d->rchan * sizeof(adata_t)); 90687bc9f6aSratchov 907bd94a039Sedd /* Insert a converter, if needed. */ 90887bc9f6aSratchov if (!aparams_native(&d->par)) { 90987bc9f6aSratchov dec_init(&d->dec, &d->par, d->rchan); 91087bc9f6aSratchov d->decbuf = xmalloc(d->round * d->rchan * d->par.bps); 91187bc9f6aSratchov } else 91287bc9f6aSratchov d->decbuf = NULL; 913bd94a039Sedd 91487bc9f6aSratchov /* 915bd94a039Sedd * Create play buffer 91687bc9f6aSratchov */ 917bd94a039Sedd 918bd94a039Sedd /* Create device <-> mixer buffer */ 91987bc9f6aSratchov d->poffs = 0; 9208aadda71Sratchov d->psize = d->bufsz + d->round; 9218aadda71Sratchov d->pbuf = xmalloc(d->psize * d->pchan * sizeof(adata_t)); 92287bc9f6aSratchov d->mode |= MODE_MON; 92387bc9f6aSratchov 924bd94a039Sedd /* Append a converter, if needed. */ 92587bc9f6aSratchov if (!aparams_native(&d->par)) { 92687bc9f6aSratchov enc_init(&d->enc, &d->par, d->pchan); 92787bc9f6aSratchov d->encbuf = xmalloc(d->round * d->pchan * d->par.bps); 92887bc9f6aSratchov } else 92987bc9f6aSratchov d->encbuf = NULL; 930bd94a039Sedd 931bd94a039Sedd /* 932bd94a039Sedd * Initially fill the record buffer with zeroed samples. This ensures 933bd94a039Sedd * that when a client records from a play-only device the client just 934bd94a039Sedd * gets silence. 935bd94a039Sedd */ 936bd94a039Sedd memset(d->rbuf, 0, d->round * d->rchan * sizeof(adata_t)); 937bd94a039Sedd 938*7b639200Sratchov logx(2, "%s: %dHz, %s, %s, %d blocks of %d frames", 939*7b639200Sratchov d->path, d->rate, 940*7b639200Sratchov (aparams_enctostr(&d->par, enc_str), enc_str), 941*7b639200Sratchov (chans_fmt(chans_str, sizeof(chans_str), 942*7b639200Sratchov d->mode & (MODE_PLAY | MODE_REC), 943*7b639200Sratchov 0, d->pchan - 1, 0, d->rchan - 1), chans_str), 944*7b639200Sratchov d->bufsz / d->round, d->round); 945*7b639200Sratchov 94687bc9f6aSratchov return 1; 94787bc9f6aSratchov } 94887bc9f6aSratchov 94987bc9f6aSratchov /* 950fa6c3c1fSratchov * Reset parameters and open the device. 951fa6c3c1fSratchov */ 952fa6c3c1fSratchov int 953fa6c3c1fSratchov dev_open(struct dev *d) 954fa6c3c1fSratchov { 955fa6c3c1fSratchov d->mode = d->reqmode; 956fa6c3c1fSratchov d->round = d->reqround; 957fa6c3c1fSratchov d->bufsz = d->reqbufsz; 958fa6c3c1fSratchov d->rate = d->reqrate; 959fa6c3c1fSratchov d->pchan = d->reqpchan; 960fa6c3c1fSratchov d->rchan = d->reqrchan; 961fa6c3c1fSratchov d->par = d->reqpar; 962fa6c3c1fSratchov if (d->pchan == 0) 963fa6c3c1fSratchov d->pchan = 2; 964fa6c3c1fSratchov if (d->rchan == 0) 965fa6c3c1fSratchov d->rchan = 2; 966500ebc2cSratchov if (!dev_sio_open(d)) { 967*7b639200Sratchov logx(1, "%s: failed to open audio device", d->path); 968fa6c3c1fSratchov return 0; 969500ebc2cSratchov } 970500ebc2cSratchov if (!dev_allocbufs(d)) 971500ebc2cSratchov return 0; 972d07fece6Sratchov 973500ebc2cSratchov d->pstate = DEV_INIT; 974fa6c3c1fSratchov return 1; 975fa6c3c1fSratchov } 976fa6c3c1fSratchov 977fa6c3c1fSratchov /* 9784a2af8d6Sratchov * Force all slots to exit and close device, called after an error 979a88e8d4bSratchov */ 980a88e8d4bSratchov void 9814a2af8d6Sratchov dev_abort(struct dev *d) 982a88e8d4bSratchov { 983a88e8d4bSratchov int i; 984a88e8d4bSratchov struct slot *s; 985d07fece6Sratchov struct ctlslot *c; 9865fb8f3c9Sratchov struct opt *o; 987a88e8d4bSratchov 9880600d38bSratchov for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 989c67d5b9aSratchov if (s->opt == NULL || s->opt->dev != d) 9900600d38bSratchov continue; 991c67d5b9aSratchov if (s->ops) { 992a88e8d4bSratchov s->ops->exit(s->arg); 993a88e8d4bSratchov s->ops = NULL; 994a88e8d4bSratchov } 995c67d5b9aSratchov } 996a88e8d4bSratchov d->slot_list = NULL; 997d07fece6Sratchov 9985fb8f3c9Sratchov for (o = opt_list; o != NULL; o = o->next) { 9995fb8f3c9Sratchov if (o->dev != d) 10005fb8f3c9Sratchov continue; 100136355b88Sratchov for (c = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, c++) { 100236355b88Sratchov if (c->ops == NULL) 100336355b88Sratchov continue; 100436355b88Sratchov if (c->opt == o) { 1005ae90d84dSratchov c->ops->exit(c->arg); 100636355b88Sratchov c->ops = NULL; 100736355b88Sratchov } 100836355b88Sratchov } 100936355b88Sratchov 10105fb8f3c9Sratchov midi_abort(o->midi); 10115fb8f3c9Sratchov } 1012f7f6d88aSratchov 10134a2af8d6Sratchov if (d->pstate != DEV_CFG) 10144a2af8d6Sratchov dev_close(d); 1015a88e8d4bSratchov } 1016a88e8d4bSratchov 1017a88e8d4bSratchov /* 101887bc9f6aSratchov * force the device to go in DEV_CFG state, the caller is supposed to 101987bc9f6aSratchov * ensure buffers are drained 102087bc9f6aSratchov */ 102187bc9f6aSratchov void 1022500ebc2cSratchov dev_freebufs(struct dev *d) 102387bc9f6aSratchov { 102487bc9f6aSratchov #ifdef DEBUG 1025*7b639200Sratchov logx(3, "%s: closing", d->path); 102687bc9f6aSratchov #endif 102787bc9f6aSratchov if (d->mode & MODE_PLAY) { 102887bc9f6aSratchov if (d->encbuf != NULL) 102987bc9f6aSratchov xfree(d->encbuf); 103087bc9f6aSratchov xfree(d->pbuf); 103187bc9f6aSratchov } 103287bc9f6aSratchov if (d->mode & MODE_REC) { 103387bc9f6aSratchov if (d->decbuf != NULL) 103487bc9f6aSratchov xfree(d->decbuf); 103587bc9f6aSratchov xfree(d->rbuf); 103687bc9f6aSratchov } 103787bc9f6aSratchov } 103887bc9f6aSratchov 10399962bb0bSratchov /* 10409962bb0bSratchov * Close the device and exit all slots 10419962bb0bSratchov */ 10429962bb0bSratchov void 10439962bb0bSratchov dev_close(struct dev *d) 10449962bb0bSratchov { 1045500ebc2cSratchov d->pstate = DEV_CFG; 1046500ebc2cSratchov dev_sio_close(d); 1047500ebc2cSratchov dev_freebufs(d); 1048d07fece6Sratchov 104999580020Sratchov if (d->master_enabled) { 105099580020Sratchov d->master_enabled = 0; 105199580020Sratchov ctl_del(CTL_DEV_MASTER, d, NULL); 1052d07fece6Sratchov } 1053731605d7Sratchov } 1054731605d7Sratchov 105587bc9f6aSratchov int 105687bc9f6aSratchov dev_ref(struct dev *d) 105787bc9f6aSratchov { 105887bc9f6aSratchov #ifdef DEBUG 1059*7b639200Sratchov logx(3, "%s: device requested", d->path); 106087bc9f6aSratchov #endif 106187bc9f6aSratchov if (d->pstate == DEV_CFG && !dev_open(d)) 106287bc9f6aSratchov return 0; 106387bc9f6aSratchov d->refcnt++; 106487bc9f6aSratchov return 1; 106587bc9f6aSratchov } 106687bc9f6aSratchov 106787bc9f6aSratchov void 106887bc9f6aSratchov dev_unref(struct dev *d) 106987bc9f6aSratchov { 107087bc9f6aSratchov #ifdef DEBUG 1071*7b639200Sratchov logx(3, "%s: device released", d->path); 107287bc9f6aSratchov #endif 107387bc9f6aSratchov d->refcnt--; 107487bc9f6aSratchov if (d->refcnt == 0 && d->pstate == DEV_INIT) 107587bc9f6aSratchov dev_close(d); 107687bc9f6aSratchov } 107787bc9f6aSratchov 107887bc9f6aSratchov /* 107987bc9f6aSratchov * initialize the device with the current parameters 108087bc9f6aSratchov */ 108187bc9f6aSratchov int 108287bc9f6aSratchov dev_init(struct dev *d) 108387bc9f6aSratchov { 108487bc9f6aSratchov if ((d->reqmode & MODE_AUDIOMASK) == 0) { 108587bc9f6aSratchov #ifdef DEBUG 1086*7b639200Sratchov logx(1, "%s: has no streams", d->path); 108787bc9f6aSratchov #endif 108887bc9f6aSratchov return 0; 108987bc9f6aSratchov } 109087bc9f6aSratchov if (d->hold && !dev_ref(d)) 109187bc9f6aSratchov return 0; 109287bc9f6aSratchov return 1; 109387bc9f6aSratchov } 109487bc9f6aSratchov 109587bc9f6aSratchov /* 109687bc9f6aSratchov * Unless the device is already in process of closing, request it to close 109787bc9f6aSratchov */ 109887bc9f6aSratchov void 109987bc9f6aSratchov dev_done(struct dev *d) 110087bc9f6aSratchov { 110187bc9f6aSratchov #ifdef DEBUG 1102*7b639200Sratchov logx(3, "%s: draining", d->path); 110387bc9f6aSratchov #endif 11040e6be583Sratchov if (mtc_array[0].dev == d && mtc_array[0].tstate != MTC_STOP) 11050e6be583Sratchov mtc_stop(&mtc_array[0]); 110687bc9f6aSratchov if (d->hold) 110787bc9f6aSratchov dev_unref(d); 110887bc9f6aSratchov } 110987bc9f6aSratchov 111087bc9f6aSratchov struct dev * 111187bc9f6aSratchov dev_bynum(int num) 111287bc9f6aSratchov { 111387bc9f6aSratchov struct dev *d; 111487bc9f6aSratchov 111587bc9f6aSratchov for (d = dev_list; d != NULL; d = d->next) { 1116e425de5dSratchov if (d->num == num) 111787bc9f6aSratchov return d; 111887bc9f6aSratchov } 111987bc9f6aSratchov return NULL; 112087bc9f6aSratchov } 112187bc9f6aSratchov 112287bc9f6aSratchov /* 112387bc9f6aSratchov * Free the device 112487bc9f6aSratchov */ 112587bc9f6aSratchov void 112687bc9f6aSratchov dev_del(struct dev *d) 112787bc9f6aSratchov { 112887bc9f6aSratchov struct dev **p; 112987bc9f6aSratchov 113087bc9f6aSratchov #ifdef DEBUG 1131*7b639200Sratchov logx(3, "%s: deleting", d->path); 113287bc9f6aSratchov #endif 113387bc9f6aSratchov if (d->pstate != DEV_CFG) 113487bc9f6aSratchov dev_close(d); 113587bc9f6aSratchov for (p = &dev_list; *p != d; p = &(*p)->next) { 113687bc9f6aSratchov #ifdef DEBUG 113787bc9f6aSratchov if (*p == NULL) { 1138*7b639200Sratchov logx(0, "%s: not on the list", d->path); 113987bc9f6aSratchov panic(); 114087bc9f6aSratchov } 114187bc9f6aSratchov #endif 114287bc9f6aSratchov } 114387bc9f6aSratchov *p = d->next; 114487bc9f6aSratchov xfree(d); 114587bc9f6aSratchov } 114687bc9f6aSratchov 114787bc9f6aSratchov unsigned int 114887bc9f6aSratchov dev_roundof(struct dev *d, unsigned int newrate) 114987bc9f6aSratchov { 115087bc9f6aSratchov return (d->round * newrate + d->rate / 2) / d->rate; 115187bc9f6aSratchov } 115287bc9f6aSratchov 115387bc9f6aSratchov /* 115487bc9f6aSratchov * If the device is paused, then resume it. 115587bc9f6aSratchov */ 115687bc9f6aSratchov void 115787bc9f6aSratchov dev_wakeup(struct dev *d) 115887bc9f6aSratchov { 115987bc9f6aSratchov if (d->pstate == DEV_INIT) { 1160*7b639200Sratchov logx(2, "%s: started", d->path); 1161*7b639200Sratchov 116287bc9f6aSratchov if (d->mode & MODE_PLAY) { 116387bc9f6aSratchov d->prime = d->bufsz; 116487bc9f6aSratchov } else { 116587bc9f6aSratchov d->prime = 0; 116687bc9f6aSratchov } 11670add8717Sratchov d->idle = 0; 1168b85bdbe2Sratchov d->poffs = 0; 1169f75a7ae7Sratchov 1170b85bdbe2Sratchov /* 1171b85bdbe2Sratchov * empty cycles don't increment delta, so it's ok to 1172b85bdbe2Sratchov * start at 0 1173b85bdbe2Sratchov **/ 1174f75a7ae7Sratchov d->delta = 0; 1175f75a7ae7Sratchov 117687bc9f6aSratchov d->pstate = DEV_RUN; 117787bc9f6aSratchov dev_sio_start(d); 117887bc9f6aSratchov } 117987bc9f6aSratchov } 118087bc9f6aSratchov 118187bc9f6aSratchov /* 118236355b88Sratchov * Return true if both of the given devices can run the same 118336355b88Sratchov * clients 118436355b88Sratchov */ 118536355b88Sratchov int 118636355b88Sratchov dev_iscompat(struct dev *o, struct dev *n) 118736355b88Sratchov { 118836355b88Sratchov if (((long long)o->round * n->rate != (long long)n->round * o->rate) || 118936355b88Sratchov ((long long)o->bufsz * n->rate != (long long)n->bufsz * o->rate)) { 1190*7b639200Sratchov logx(1, "%s: not compatible with %s", n->name, o->name); 119136355b88Sratchov return 0; 119236355b88Sratchov } 119336355b88Sratchov return 1; 119436355b88Sratchov } 119536355b88Sratchov 119636355b88Sratchov /* 119736355b88Sratchov * Close the device, but attempt to migrate everything to a new sndio 119836355b88Sratchov * device. 119936355b88Sratchov */ 120036355b88Sratchov struct dev * 120136355b88Sratchov dev_migrate(struct dev *odev) 120236355b88Sratchov { 120336355b88Sratchov struct dev *ndev; 120436355b88Sratchov struct opt *o; 120536355b88Sratchov struct slot *s; 120636355b88Sratchov int i; 120736355b88Sratchov 120836355b88Sratchov /* not opened */ 120936355b88Sratchov if (odev->pstate == DEV_CFG) 121036355b88Sratchov return odev; 121136355b88Sratchov 121236355b88Sratchov ndev = odev; 121336355b88Sratchov while (1) { 121436355b88Sratchov /* try next one, circulating through the list */ 121536355b88Sratchov ndev = ndev->alt_next; 121636355b88Sratchov if (ndev == odev) { 1217*7b639200Sratchov logx(1, "%s: no fall-back device found", odev->path); 121836355b88Sratchov return NULL; 121936355b88Sratchov } 122036355b88Sratchov 122136355b88Sratchov 122236355b88Sratchov if (!dev_ref(ndev)) 122336355b88Sratchov continue; 122436355b88Sratchov 122536355b88Sratchov /* check if new parameters are compatible with old ones */ 122636355b88Sratchov if (!dev_iscompat(odev, ndev)) { 122736355b88Sratchov dev_unref(ndev); 122836355b88Sratchov continue; 122936355b88Sratchov } 123036355b88Sratchov 123136355b88Sratchov /* found it!*/ 123236355b88Sratchov break; 123336355b88Sratchov } 123436355b88Sratchov 1235*7b639200Sratchov logx(1, "%s: switching to %s", odev->path, ndev->path); 123636355b88Sratchov 123736355b88Sratchov if (mtc_array[0].dev == odev) 123836355b88Sratchov mtc_setdev(&mtc_array[0], ndev); 123936355b88Sratchov 124036355b88Sratchov /* move opts to new device (also moves clients using the opts) */ 124136355b88Sratchov for (o = opt_list; o != NULL; o = o->next) { 124236355b88Sratchov if (o->dev != odev) 124336355b88Sratchov continue; 124436355b88Sratchov if (strcmp(o->name, o->dev->name) == 0) 124536355b88Sratchov continue; 124636355b88Sratchov opt_setdev(o, ndev); 124736355b88Sratchov } 124836355b88Sratchov 124936355b88Sratchov /* terminate remaining clients */ 125036355b88Sratchov for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 125136355b88Sratchov if (s->opt == NULL || s->opt->dev != odev) 125236355b88Sratchov continue; 125336355b88Sratchov if (s->ops != NULL) { 12542da54102Sratchov s->ops->exit(s->arg); 125536355b88Sratchov s->ops = NULL; 125636355b88Sratchov } 125736355b88Sratchov } 125836355b88Sratchov 125936355b88Sratchov /* slots and/or MMC hold refs, drop ours */ 126036355b88Sratchov dev_unref(ndev); 126136355b88Sratchov 126236355b88Sratchov return ndev; 126336355b88Sratchov } 126436355b88Sratchov 126536355b88Sratchov /* 126687bc9f6aSratchov * check that all clients controlled by MMC are ready to start, if so, 126787bc9f6aSratchov * attach them all at the same position 126887bc9f6aSratchov */ 126987bc9f6aSratchov void 12700e6be583Sratchov mtc_trigger(struct mtc *mtc) 127187bc9f6aSratchov { 127287bc9f6aSratchov int i; 127387bc9f6aSratchov struct slot *s; 127487bc9f6aSratchov 12750e6be583Sratchov if (mtc->tstate != MTC_START) { 1276*7b639200Sratchov logx(2, "%s: not started by mmc yet, waiting.", mtc->dev->path); 127787bc9f6aSratchov return; 127887bc9f6aSratchov } 12790600d38bSratchov 12800600d38bSratchov for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 12810e6be583Sratchov if (s->opt == NULL || s->opt->mtc != mtc) 128287bc9f6aSratchov continue; 12832eeb13a4Sratchov if (s->pstate != SLOT_READY) { 128487bc9f6aSratchov #ifdef DEBUG 1285*7b639200Sratchov logx(3, "%s%u: not ready, start delayed", s->name, s->unit); 128687bc9f6aSratchov #endif 128787bc9f6aSratchov return; 128887bc9f6aSratchov } 128987bc9f6aSratchov } 12900e6be583Sratchov if (!dev_ref(mtc->dev)) 129187bc9f6aSratchov return; 12920600d38bSratchov 12930600d38bSratchov for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 12940e6be583Sratchov if (s->opt == NULL || s->opt->mtc != mtc) 129587bc9f6aSratchov continue; 129687bc9f6aSratchov slot_attach(s); 12970257ec39Sratchov s->pstate = SLOT_RUN; 129887bc9f6aSratchov } 12990e6be583Sratchov mtc->tstate = MTC_RUN; 13000e6be583Sratchov mtc_midi_full(mtc); 13010e6be583Sratchov dev_wakeup(mtc->dev); 130287bc9f6aSratchov } 130387bc9f6aSratchov 130487bc9f6aSratchov /* 130587bc9f6aSratchov * start all slots simultaneously 130687bc9f6aSratchov */ 130787bc9f6aSratchov void 13080e6be583Sratchov mtc_start(struct mtc *mtc) 130987bc9f6aSratchov { 13100e6be583Sratchov if (mtc->tstate == MTC_STOP) { 13110e6be583Sratchov mtc->tstate = MTC_START; 13120e6be583Sratchov mtc_trigger(mtc); 131387bc9f6aSratchov #ifdef DEBUG 131487bc9f6aSratchov } else { 1315*7b639200Sratchov logx(3, "%s: ignoring mmc start", mtc->dev->path); 131687bc9f6aSratchov #endif 131787bc9f6aSratchov } 131887bc9f6aSratchov } 131987bc9f6aSratchov 132087bc9f6aSratchov /* 132187bc9f6aSratchov * stop all slots simultaneously 132287bc9f6aSratchov */ 132387bc9f6aSratchov void 13240e6be583Sratchov mtc_stop(struct mtc *mtc) 132587bc9f6aSratchov { 13260e6be583Sratchov switch (mtc->tstate) { 13270e6be583Sratchov case MTC_START: 13280e6be583Sratchov mtc->tstate = MTC_STOP; 132987bc9f6aSratchov return; 13300e6be583Sratchov case MTC_RUN: 13310e6be583Sratchov mtc->tstate = MTC_STOP; 13320e6be583Sratchov dev_unref(mtc->dev); 133387bc9f6aSratchov break; 133487bc9f6aSratchov default: 133587bc9f6aSratchov #ifdef DEBUG 1336*7b639200Sratchov logx(3, "%s: ignored mmc stop", mtc->dev->path); 133787bc9f6aSratchov #endif 133887bc9f6aSratchov return; 133987bc9f6aSratchov } 134087bc9f6aSratchov } 134187bc9f6aSratchov 134287bc9f6aSratchov /* 134387bc9f6aSratchov * relocate all slots simultaneously 134487bc9f6aSratchov */ 134587bc9f6aSratchov void 13460e6be583Sratchov mtc_loc(struct mtc *mtc, unsigned int origin) 134787bc9f6aSratchov { 1348*7b639200Sratchov logx(2, "%s: relocated to %u", mtc->dev->path, origin); 1349*7b639200Sratchov 13500e6be583Sratchov if (mtc->tstate == MTC_RUN) 13510e6be583Sratchov mtc_stop(mtc); 13520e6be583Sratchov mtc->origin = origin; 13530e6be583Sratchov if (mtc->tstate == MTC_RUN) 13540e6be583Sratchov mtc_start(mtc); 135587bc9f6aSratchov } 135687bc9f6aSratchov 135752217fe5Sratchov /* 135836355b88Sratchov * set MMC device 135936355b88Sratchov */ 136036355b88Sratchov void 136136355b88Sratchov mtc_setdev(struct mtc *mtc, struct dev *d) 136236355b88Sratchov { 136336355b88Sratchov struct opt *o; 136436355b88Sratchov 136536355b88Sratchov if (mtc->dev == d) 136636355b88Sratchov return; 136736355b88Sratchov 1368*7b639200Sratchov logx(2, "%s: set to be MIDI clock source", d->path); 136936355b88Sratchov 137036355b88Sratchov /* adjust clock and ref counter, if needed */ 137136355b88Sratchov if (mtc->tstate == MTC_RUN) { 137236355b88Sratchov mtc->delta -= mtc->dev->delta; 137336355b88Sratchov dev_unref(mtc->dev); 137436355b88Sratchov } 137536355b88Sratchov 137636355b88Sratchov mtc->dev = d; 137736355b88Sratchov 137836355b88Sratchov if (mtc->tstate == MTC_RUN) { 137936355b88Sratchov mtc->delta += mtc->dev->delta; 138036355b88Sratchov dev_ref(mtc->dev); 138136355b88Sratchov dev_wakeup(mtc->dev); 138236355b88Sratchov } 138336355b88Sratchov 138436355b88Sratchov /* move in once anything using MMC */ 138536355b88Sratchov for (o = opt_list; o != NULL; o = o->next) { 138636355b88Sratchov if (o->mtc == mtc) 138736355b88Sratchov opt_setdev(o, mtc->dev); 138836355b88Sratchov } 138936355b88Sratchov } 139036355b88Sratchov 139136355b88Sratchov /* 139252217fe5Sratchov * allocate buffers & conversion chain 139352217fe5Sratchov */ 139452217fe5Sratchov void 13952d234d21Sratchov slot_initconv(struct slot *s) 139652217fe5Sratchov { 13971debcc99Sratchov unsigned int dev_nch; 1398c67d5b9aSratchov struct dev *d = s->opt->dev; 139952217fe5Sratchov 140052217fe5Sratchov if (s->mode & MODE_PLAY) { 140152217fe5Sratchov cmap_init(&s->mix.cmap, 140223321a5cSratchov s->opt->pmin, s->opt->pmin + s->mix.nch - 1, 140323321a5cSratchov s->opt->pmin, s->opt->pmin + s->mix.nch - 1, 140452217fe5Sratchov 0, d->pchan - 1, 1405521be3b0Sratchov s->opt->pmin, s->opt->pmax); 1406af42dcefSratchov s->mix.decbuf = NULL; 1407af42dcefSratchov s->mix.resampbuf = NULL; 140852217fe5Sratchov if (!aparams_native(&s->par)) { 140923321a5cSratchov dec_init(&s->mix.dec, &s->par, s->mix.nch); 1410af42dcefSratchov s->mix.decbuf = 1411af42dcefSratchov xmalloc(s->round * s->mix.nch * sizeof(adata_t)); 141252217fe5Sratchov } 141352217fe5Sratchov if (s->rate != d->rate) { 141452217fe5Sratchov resamp_init(&s->mix.resamp, s->round, d->round, 141523321a5cSratchov s->mix.nch); 1416af42dcefSratchov s->mix.resampbuf = 1417af42dcefSratchov xmalloc(d->round * s->mix.nch * sizeof(adata_t)); 141852217fe5Sratchov } 141925479e21Sratchov s->mix.join = 1; 142025479e21Sratchov s->mix.expand = 1; 14211debcc99Sratchov if (s->opt->dup && s->mix.cmap.nch > 0) { 14221debcc99Sratchov dev_nch = d->pchan < (s->opt->pmax + 1) ? 14231debcc99Sratchov d->pchan - s->opt->pmin : 14241debcc99Sratchov s->opt->pmax - s->opt->pmin + 1; 14251debcc99Sratchov if (dev_nch > s->mix.nch) 14261debcc99Sratchov s->mix.expand = dev_nch / s->mix.nch; 14271debcc99Sratchov else if (s->mix.nch > dev_nch) 14281debcc99Sratchov s->mix.join = s->mix.nch / dev_nch; 142925479e21Sratchov } 143052217fe5Sratchov } 143152217fe5Sratchov 143252217fe5Sratchov if (s->mode & MODE_RECMASK) { 143394f976feSratchov unsigned int outchan = (s->opt->mode & MODE_MON) ? 14341debcc99Sratchov d->pchan : d->rchan; 14351debcc99Sratchov 1436af42dcefSratchov s->sub.encbuf = NULL; 1437af42dcefSratchov s->sub.resampbuf = NULL; 143852217fe5Sratchov cmap_init(&s->sub.cmap, 14391debcc99Sratchov 0, outchan - 1, 1440521be3b0Sratchov s->opt->rmin, s->opt->rmax, 144123321a5cSratchov s->opt->rmin, s->opt->rmin + s->sub.nch - 1, 144223321a5cSratchov s->opt->rmin, s->opt->rmin + s->sub.nch - 1); 144352217fe5Sratchov if (s->rate != d->rate) { 144452217fe5Sratchov resamp_init(&s->sub.resamp, d->round, s->round, 144523321a5cSratchov s->sub.nch); 1446af42dcefSratchov s->sub.resampbuf = 1447af42dcefSratchov xmalloc(d->round * s->sub.nch * sizeof(adata_t)); 144852217fe5Sratchov } 144952217fe5Sratchov if (!aparams_native(&s->par)) { 145023321a5cSratchov enc_init(&s->sub.enc, &s->par, s->sub.nch); 1451af42dcefSratchov s->sub.encbuf = 1452af42dcefSratchov xmalloc(s->round * s->sub.nch * sizeof(adata_t)); 145352217fe5Sratchov } 145425479e21Sratchov s->sub.join = 1; 145525479e21Sratchov s->sub.expand = 1; 14561debcc99Sratchov if (s->opt->dup && s->sub.cmap.nch > 0) { 14571debcc99Sratchov dev_nch = outchan < (s->opt->rmax + 1) ? 14581debcc99Sratchov outchan - s->opt->rmin : 14591debcc99Sratchov s->opt->rmax - s->opt->rmin + 1; 14601debcc99Sratchov if (dev_nch > s->sub.nch) 14611debcc99Sratchov s->sub.join = dev_nch / s->sub.nch; 14621debcc99Sratchov else if (s->sub.nch > dev_nch) 14631debcc99Sratchov s->sub.expand = s->sub.nch / dev_nch; 146425479e21Sratchov } 146552217fe5Sratchov 146652217fe5Sratchov /* 146752217fe5Sratchov * cmap_copy() doesn't write samples in all channels, 146852217fe5Sratchov * for instance when mono->stereo conversion is 146952217fe5Sratchov * disabled. So we have to prefill cmap_copy() output 147052217fe5Sratchov * with silence. 147152217fe5Sratchov */ 147252217fe5Sratchov if (s->sub.resampbuf) { 147352217fe5Sratchov memset(s->sub.resampbuf, 0, 147423321a5cSratchov d->round * s->sub.nch * sizeof(adata_t)); 147552217fe5Sratchov } else if (s->sub.encbuf) { 147652217fe5Sratchov memset(s->sub.encbuf, 0, 147723321a5cSratchov s->round * s->sub.nch * sizeof(adata_t)); 147852217fe5Sratchov } else { 147952217fe5Sratchov memset(s->sub.buf.data, 0, 148023321a5cSratchov s->appbufsz * s->sub.nch * sizeof(adata_t)); 148152217fe5Sratchov } 148252217fe5Sratchov } 14832d234d21Sratchov } 14842d234d21Sratchov 14852d234d21Sratchov /* 14862d234d21Sratchov * allocate buffers & conversion chain 14872d234d21Sratchov */ 14882d234d21Sratchov void 14892d234d21Sratchov slot_allocbufs(struct slot *s) 14902d234d21Sratchov { 14912d234d21Sratchov if (s->mode & MODE_PLAY) { 14922d234d21Sratchov s->mix.bpf = s->par.bps * s->mix.nch; 14932d234d21Sratchov abuf_init(&s->mix.buf, s->appbufsz * s->mix.bpf); 14942d234d21Sratchov } 14952d234d21Sratchov 14962d234d21Sratchov if (s->mode & MODE_RECMASK) { 14972d234d21Sratchov s->sub.bpf = s->par.bps * s->sub.nch; 14982d234d21Sratchov abuf_init(&s->sub.buf, s->appbufsz * s->sub.bpf); 14992d234d21Sratchov } 150052217fe5Sratchov 150152217fe5Sratchov #ifdef DEBUG 1502*7b639200Sratchov logx(3, "%s%u: allocated %u/%u fr buffers", 1503*7b639200Sratchov s->name, s->unit, s->appbufsz, SLOT_BUFSZ(s)); 150452217fe5Sratchov #endif 150552217fe5Sratchov } 150652217fe5Sratchov 150752217fe5Sratchov /* 150852217fe5Sratchov * free buffers & conversion chain 150952217fe5Sratchov */ 151052217fe5Sratchov void 151152217fe5Sratchov slot_freebufs(struct slot *s) 151252217fe5Sratchov { 151352217fe5Sratchov if (s->mode & MODE_RECMASK) { 151452217fe5Sratchov abuf_done(&s->sub.buf); 151552217fe5Sratchov } 151652217fe5Sratchov 151752217fe5Sratchov if (s->mode & MODE_PLAY) { 151852217fe5Sratchov abuf_done(&s->mix.buf); 151952217fe5Sratchov } 152052217fe5Sratchov } 152152217fe5Sratchov 152287bc9f6aSratchov /* 152387bc9f6aSratchov * allocate a new slot and register the given call-backs 152487bc9f6aSratchov */ 152587bc9f6aSratchov struct slot * 1526c67d5b9aSratchov slot_new(struct opt *opt, unsigned int id, char *who, 1527f94911feSratchov struct slotops *ops, void *arg, int mode) 152887bc9f6aSratchov { 152987bc9f6aSratchov char *p; 153087bc9f6aSratchov char name[SLOT_NAMEMAX]; 1531d0ac6fcaSratchov char ctl_name[CTL_NAMEMAX]; 15322d7fd59cSratchov unsigned int i, ser, bestser, bestidx; 15332d7fd59cSratchov struct slot *unit[DEV_NSLOT]; 153487bc9f6aSratchov struct slot *s; 153587bc9f6aSratchov 153687bc9f6aSratchov /* 1537da3adc54Sratchov * create a ``valid'' control name (lowcase, remove [^a-z], truncate) 153887bc9f6aSratchov */ 153987bc9f6aSratchov for (i = 0, p = who; ; p++) { 154087bc9f6aSratchov if (i == SLOT_NAMEMAX - 1 || *p == '\0') { 154187bc9f6aSratchov name[i] = '\0'; 154287bc9f6aSratchov break; 154387bc9f6aSratchov } else if (*p >= 'A' && *p <= 'Z') { 154487bc9f6aSratchov name[i++] = *p + 'a' - 'A'; 154587bc9f6aSratchov } else if (*p >= 'a' && *p <= 'z') 154687bc9f6aSratchov name[i++] = *p; 154787bc9f6aSratchov } 154887bc9f6aSratchov if (i == 0) 154987bc9f6aSratchov strlcpy(name, "noname", SLOT_NAMEMAX); 155087bc9f6aSratchov 155187bc9f6aSratchov /* 15522d7fd59cSratchov * build a unit-to-slot map for this name 155387bc9f6aSratchov */ 15542d7fd59cSratchov for (i = 0; i < DEV_NSLOT; i++) 15552d7fd59cSratchov unit[i] = NULL; 15560600d38bSratchov for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 155787bc9f6aSratchov if (strcmp(s->name, name) == 0) 15582d7fd59cSratchov unit[s->unit] = s; 155987bc9f6aSratchov } 156087bc9f6aSratchov 156187bc9f6aSratchov /* 15622988007fSratchov * find the free slot with the least unit number and same id 15632988007fSratchov */ 15642988007fSratchov for (i = 0; i < DEV_NSLOT; i++) { 15652988007fSratchov s = unit[i]; 15662988007fSratchov if (s != NULL && s->ops == NULL && s->id == id) 15672988007fSratchov goto found; 15682988007fSratchov } 15692988007fSratchov 15702988007fSratchov /* 15712d7fd59cSratchov * find the free slot with the least unit number 157287bc9f6aSratchov */ 15732d7fd59cSratchov for (i = 0; i < DEV_NSLOT; i++) { 15742d7fd59cSratchov s = unit[i]; 15752988007fSratchov if (s != NULL && s->ops == NULL) { 15762988007fSratchov s->id = id; 157787bc9f6aSratchov goto found; 157887bc9f6aSratchov } 15792988007fSratchov } 158087bc9f6aSratchov 158187bc9f6aSratchov /* 1582bce4c6aeSratchov * couldn't find a matching slot, pick oldest free slot 158387bc9f6aSratchov * and set its name/unit 158487bc9f6aSratchov */ 158587bc9f6aSratchov bestser = 0; 158687bc9f6aSratchov bestidx = DEV_NSLOT; 15870600d38bSratchov for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { 158887bc9f6aSratchov if (s->ops != NULL) 158987bc9f6aSratchov continue; 15900600d38bSratchov ser = slot_serial - s->serial; 159187bc9f6aSratchov if (ser > bestser) { 159287bc9f6aSratchov bestser = ser; 159387bc9f6aSratchov bestidx = i; 159487bc9f6aSratchov } 159587bc9f6aSratchov } 1596b0bb1edfSratchov 1597b0bb1edfSratchov if (bestidx == DEV_NSLOT) { 1598*7b639200Sratchov logx(1, "%s: out of sub-device slots", name); 1599b0bb1edfSratchov return NULL; 1600b0bb1edfSratchov } 1601b0bb1edfSratchov 16020600d38bSratchov s = slot_array + bestidx; 1603d0ac6fcaSratchov ctl_del(CTL_SLOT_LEVEL, s, NULL); 160487bc9f6aSratchov s->vol = MIDI_MAXCTL; 160587bc9f6aSratchov strlcpy(s->name, name, SLOT_NAMEMAX); 16060600d38bSratchov s->serial = slot_serial++; 16072d7fd59cSratchov for (i = 0; unit[i] != NULL; i++) 16082d7fd59cSratchov ; /* nothing */ 16092d7fd59cSratchov s->unit = i; 16102988007fSratchov s->id = id; 1611d0ac6fcaSratchov s->opt = opt; 1612d0ac6fcaSratchov slot_ctlname(s, ctl_name, CTL_NAMEMAX); 1613d0ac6fcaSratchov ctl_new(CTL_SLOT_LEVEL, s, NULL, 16144d8b188fSratchov CTL_NUM, "", "app", ctl_name, -1, "level", 1615d0ac6fcaSratchov NULL, -1, 127, s->vol); 161687bc9f6aSratchov 161787bc9f6aSratchov found: 161836355b88Sratchov /* open device, this may change opt's device */ 16191d8c7cc5Sratchov if (!opt_ref(opt)) 1620c67d5b9aSratchov return NULL; 1621f94911feSratchov s->opt = opt; 162287bc9f6aSratchov s->ops = ops; 162387bc9f6aSratchov s->arg = arg; 162487bc9f6aSratchov s->pstate = SLOT_INIT; 162587bc9f6aSratchov s->mode = mode; 16266fdcc03cSratchov aparams_init(&s->par); 16273b704fe7Sratchov if (s->mode & MODE_PLAY) 162823321a5cSratchov s->mix.nch = s->opt->pmax - s->opt->pmin + 1; 16293b704fe7Sratchov if (s->mode & MODE_RECMASK) 163023321a5cSratchov s->sub.nch = s->opt->rmax - s->opt->rmin + 1; 16310e6be583Sratchov s->xrun = s->opt->mtc != NULL ? XRUN_SYNC : XRUN_IGNORE; 1632c67d5b9aSratchov s->appbufsz = s->opt->dev->bufsz; 1633c67d5b9aSratchov s->round = s->opt->dev->round; 1634c67d5b9aSratchov s->rate = s->opt->dev->rate; 1635c67d5b9aSratchov dev_midi_slotdesc(s->opt->dev, s); 1636c67d5b9aSratchov dev_midi_vol(s->opt->dev, s); 1637edbb4404Sratchov #ifdef DEBUG 1638*7b639200Sratchov logx(3, "%s%u: using %s, mode = %x", s->name, s->unit, s->opt->name, mode); 1639edbb4404Sratchov #endif 164087bc9f6aSratchov return s; 164187bc9f6aSratchov } 164287bc9f6aSratchov 164387bc9f6aSratchov /* 164487bc9f6aSratchov * release the given slot 164587bc9f6aSratchov */ 164687bc9f6aSratchov void 164787bc9f6aSratchov slot_del(struct slot *s) 164887bc9f6aSratchov { 164987bc9f6aSratchov s->arg = s; 165087bc9f6aSratchov s->ops = &zomb_slotops; 165187bc9f6aSratchov switch (s->pstate) { 165287bc9f6aSratchov case SLOT_INIT: 165387bc9f6aSratchov s->ops = NULL; 165487bc9f6aSratchov break; 165587bc9f6aSratchov case SLOT_START: 165687bc9f6aSratchov case SLOT_READY: 165787bc9f6aSratchov case SLOT_RUN: 165887bc9f6aSratchov case SLOT_STOP: 1659dff6ad49Sratchov slot_stop(s, 0); 166087bc9f6aSratchov break; 166187bc9f6aSratchov } 166236355b88Sratchov opt_unref(s->opt); 166387bc9f6aSratchov } 166487bc9f6aSratchov 166587bc9f6aSratchov /* 166687bc9f6aSratchov * change the slot play volume; called either by the slot or by MIDI 166787bc9f6aSratchov */ 166887bc9f6aSratchov void 166987bc9f6aSratchov slot_setvol(struct slot *s, unsigned int vol) 167087bc9f6aSratchov { 167187bc9f6aSratchov #ifdef DEBUG 1672*7b639200Sratchov logx(3, "%s%u: setting volume %u", s->name, s->unit, vol); 167387bc9f6aSratchov #endif 167487bc9f6aSratchov s->vol = vol; 167587bc9f6aSratchov s->mix.vol = MIDI_TO_ADATA(s->vol); 167687bc9f6aSratchov } 167787bc9f6aSratchov 167887bc9f6aSratchov /* 167936355b88Sratchov * set device for this slot 168036355b88Sratchov */ 168136355b88Sratchov void 168236355b88Sratchov slot_setopt(struct slot *s, struct opt *o) 168336355b88Sratchov { 168436355b88Sratchov struct opt *t; 168536355b88Sratchov struct dev *odev, *ndev; 168636355b88Sratchov struct ctl *c; 168736355b88Sratchov 168836355b88Sratchov if (s->opt == NULL || s->opt == o) 168936355b88Sratchov return; 169036355b88Sratchov 1691*7b639200Sratchov logx(2, "%s%u: moving to opt %s", s->name, s->unit, o->name); 169236355b88Sratchov 169336355b88Sratchov odev = s->opt->dev; 169436355b88Sratchov if (s->ops != NULL) { 169536355b88Sratchov ndev = opt_ref(o); 169636355b88Sratchov if (ndev == NULL) 169736355b88Sratchov return; 169836355b88Sratchov 169936355b88Sratchov if (!dev_iscompat(odev, ndev)) { 170036355b88Sratchov opt_unref(o); 170136355b88Sratchov return; 170236355b88Sratchov } 170336355b88Sratchov } 170436355b88Sratchov 170536355b88Sratchov if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) 170636355b88Sratchov slot_detach(s); 170736355b88Sratchov 170836355b88Sratchov t = s->opt; 170936355b88Sratchov s->opt = o; 171036355b88Sratchov 171136355b88Sratchov c = ctl_find(CTL_SLOT_LEVEL, s, NULL); 171236355b88Sratchov ctl_update(c); 171336355b88Sratchov 171436355b88Sratchov if (o->dev != t->dev) { 171536355b88Sratchov dev_midi_slotdesc(odev, s); 171636355b88Sratchov dev_midi_slotdesc(ndev, s); 171736355b88Sratchov dev_midi_vol(ndev, s); 171836355b88Sratchov } 171936355b88Sratchov 172036355b88Sratchov if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) 172136355b88Sratchov slot_attach(s); 172236355b88Sratchov 172336355b88Sratchov if (s->ops != NULL) { 172436355b88Sratchov opt_unref(t); 172536355b88Sratchov return; 172636355b88Sratchov } 172736355b88Sratchov } 172836355b88Sratchov 172936355b88Sratchov /* 173087bc9f6aSratchov * attach the slot to the device (ie start playing & recording 173187bc9f6aSratchov */ 173287bc9f6aSratchov void 173387bc9f6aSratchov slot_attach(struct slot *s) 173487bc9f6aSratchov { 1735c67d5b9aSratchov struct dev *d = s->opt->dev; 1736f75a7ae7Sratchov long long pos; 173787bc9f6aSratchov 173894f976feSratchov if (((s->mode & MODE_PLAY) && !(s->opt->mode & MODE_PLAY)) || 173994f976feSratchov ((s->mode & MODE_RECMASK) && !(s->opt->mode & MODE_RECMASK))) { 1740*7b639200Sratchov logx(1, "%s%u at %s: mode not allowed", s->name, s->unit, s->opt->name); 1741*7b639200Sratchov return; 174294f976feSratchov } 174394f976feSratchov 174487bc9f6aSratchov /* 1745d9a51c35Sjmc * setup conversions layer 1746af42dcefSratchov */ 1747af42dcefSratchov slot_initconv(s); 1748af42dcefSratchov 1749af42dcefSratchov /* 175087bc9f6aSratchov * start the device if not started 175187bc9f6aSratchov */ 175287bc9f6aSratchov dev_wakeup(d); 175387bc9f6aSratchov 175487bc9f6aSratchov /* 1755f75a7ae7Sratchov * adjust initial clock 1756f75a7ae7Sratchov */ 17570257ec39Sratchov pos = s->delta_rem + 17580257ec39Sratchov (long long)s->delta * d->round + 17590257ec39Sratchov (long long)d->delta * s->round; 17600257ec39Sratchov s->delta = pos / (int)d->round; 1761f75a7ae7Sratchov s->delta_rem = pos % d->round; 17620257ec39Sratchov if (s->delta_rem < 0) { 17630257ec39Sratchov s->delta_rem += d->round; 17640257ec39Sratchov s->delta--; 17650257ec39Sratchov } 1766f75a7ae7Sratchov 176787bc9f6aSratchov #ifdef DEBUG 1768*7b639200Sratchov logx(2, "%s%u: attached at %d + %d / %d", 1769*7b639200Sratchov s->name, s->unit, s->delta, s->delta_rem, s->round); 177087bc9f6aSratchov #endif 177187bc9f6aSratchov 177287bc9f6aSratchov /* 177387bc9f6aSratchov * We dont check whether the device is dying, 177487bc9f6aSratchov * because dev_xxx() functions are supposed to 177587bc9f6aSratchov * work (i.e., not to crash) 177687bc9f6aSratchov */ 1777bd94a039Sedd 177887bc9f6aSratchov s->next = d->slot_list; 177987bc9f6aSratchov d->slot_list = s; 178087bc9f6aSratchov if (s->mode & MODE_PLAY) { 178187bc9f6aSratchov s->mix.vol = MIDI_TO_ADATA(s->vol); 178287bc9f6aSratchov dev_mix_adjvol(d); 178387bc9f6aSratchov } 178487bc9f6aSratchov } 178587bc9f6aSratchov 178687bc9f6aSratchov /* 178787bc9f6aSratchov * if MMC is enabled, and try to attach all slots synchronously, else 178887bc9f6aSratchov * simply attach the slot 178987bc9f6aSratchov */ 179087bc9f6aSratchov void 179187bc9f6aSratchov slot_ready(struct slot *s) 179287bc9f6aSratchov { 1793e1367898Sratchov /* 1794e1367898Sratchov * device may be disconnected, and if so we're called from 1795e1367898Sratchov * slot->ops->exit() on a closed device 1796e1367898Sratchov */ 1797c67d5b9aSratchov if (s->opt->dev->pstate == DEV_CFG) 1798e1367898Sratchov return; 17990e6be583Sratchov if (s->opt->mtc == NULL) { 180087bc9f6aSratchov slot_attach(s); 18010257ec39Sratchov s->pstate = SLOT_RUN; 18020257ec39Sratchov } else 18030e6be583Sratchov mtc_trigger(s->opt->mtc); 180487bc9f6aSratchov } 180587bc9f6aSratchov 180687bc9f6aSratchov /* 180787bc9f6aSratchov * setup buffers & conversion layers, prepare the slot to receive data 180887bc9f6aSratchov * (for playback) or start (recording). 180987bc9f6aSratchov */ 181087bc9f6aSratchov void 181187bc9f6aSratchov slot_start(struct slot *s) 181287bc9f6aSratchov { 1813c67d5b9aSratchov struct dev *d = s->opt->dev; 181487bc9f6aSratchov #ifdef DEBUG 1815*7b639200Sratchov char enc_str[ENCMAX], chans_str[64]; 1816*7b639200Sratchov 181787bc9f6aSratchov if (s->pstate != SLOT_INIT) { 1818*7b639200Sratchov logx(0, "%s%u: slot_start: wrong state", s->name, s->unit); 181987bc9f6aSratchov panic(); 182087bc9f6aSratchov } 1821*7b639200Sratchov 1822*7b639200Sratchov logx(2, "%s%u: %dHz, %s, %s, %d blocks of %d frames", 1823*7b639200Sratchov s->name, s->unit, s->rate, 1824*7b639200Sratchov (aparams_enctostr(&s->par, enc_str), enc_str), 1825*7b639200Sratchov (chans_fmt(chans_str, sizeof(chans_str), s->mode, 1826*7b639200Sratchov s->opt->pmin, s->opt->pmin + s->mix.nch - 1, 1827*7b639200Sratchov s->opt->rmin, s->opt->rmin + s->sub.nch - 1), chans_str), 1828*7b639200Sratchov s->appbufsz / s->round, s->round); 182952217fe5Sratchov #endif 183052217fe5Sratchov slot_allocbufs(s); 1831d18b419cSratchov 1832d18b419cSratchov if (s->mode & MODE_RECMASK) { 1833d18b419cSratchov /* 1834d18b419cSratchov * N-th recorded block is the N-th played block 1835d18b419cSratchov */ 1836f51cdaacSratchov s->sub.prime = d->bufsz / d->round; 1837d18b419cSratchov } 1838d18b419cSratchov s->skip = 0; 1839d18b419cSratchov 18400257ec39Sratchov /* 18410257ec39Sratchov * get the current position, the origin is when the first sample 18420257ec39Sratchov * played and/or recorded 18430257ec39Sratchov */ 1844f51cdaacSratchov s->delta = -(long long)d->bufsz * s->round / d->round; 18450257ec39Sratchov s->delta_rem = 0; 18460257ec39Sratchov 184787bc9f6aSratchov if (s->mode & MODE_PLAY) { 184887bc9f6aSratchov s->pstate = SLOT_START; 184987bc9f6aSratchov } else { 185087bc9f6aSratchov s->pstate = SLOT_READY; 185187bc9f6aSratchov slot_ready(s); 185287bc9f6aSratchov } 185387bc9f6aSratchov } 185487bc9f6aSratchov 185587bc9f6aSratchov /* 185687bc9f6aSratchov * stop playback and recording, and free conversion layers 185787bc9f6aSratchov */ 185887bc9f6aSratchov void 185987bc9f6aSratchov slot_detach(struct slot *s) 186087bc9f6aSratchov { 186187bc9f6aSratchov struct slot **ps; 1862c67d5b9aSratchov struct dev *d = s->opt->dev; 18630257ec39Sratchov long long pos; 186487bc9f6aSratchov 1865758b5165Sratchov for (ps = &d->slot_list; *ps != s; ps = &(*ps)->next) { 186687bc9f6aSratchov #ifdef DEBUG 18672b204a4bSratchov if (*ps == NULL) { 1868*7b639200Sratchov logx(0, "%s%u: can't detach, not on list", s->name, s->unit); 186987bc9f6aSratchov panic(); 187087bc9f6aSratchov } 187187bc9f6aSratchov #endif 187287bc9f6aSratchov } 187387bc9f6aSratchov *ps = s->next; 18740257ec39Sratchov 18750257ec39Sratchov /* 18760257ec39Sratchov * adjust clock, go back d->delta ticks so that slot_attach() 18770257ec39Sratchov * could be called with the resulting state 18780257ec39Sratchov */ 18790257ec39Sratchov pos = s->delta_rem + 18800257ec39Sratchov (long long)s->delta * d->round - 18810257ec39Sratchov (long long)d->delta * s->round; 18820257ec39Sratchov s->delta = pos / (int)d->round; 18830257ec39Sratchov s->delta_rem = pos % d->round; 18840257ec39Sratchov if (s->delta_rem < 0) { 18850257ec39Sratchov s->delta_rem += d->round; 18860257ec39Sratchov s->delta--; 18870257ec39Sratchov } 18880257ec39Sratchov 18890257ec39Sratchov #ifdef DEBUG 1890*7b639200Sratchov logx(2, "%s%u: detached at %d + %d / %d", 1891*7b639200Sratchov s->name, s->unit, s->delta, s->delta_rem, d->round); 18920257ec39Sratchov #endif 189352217fe5Sratchov if (s->mode & MODE_PLAY) 1894758b5165Sratchov dev_mix_adjvol(d); 1895af42dcefSratchov 1896af42dcefSratchov if (s->mode & MODE_RECMASK) { 1897af42dcefSratchov if (s->sub.encbuf) { 1898af42dcefSratchov xfree(s->sub.encbuf); 1899af42dcefSratchov s->sub.encbuf = NULL; 1900af42dcefSratchov } 1901af42dcefSratchov if (s->sub.resampbuf) { 1902af42dcefSratchov xfree(s->sub.resampbuf); 1903af42dcefSratchov s->sub.resampbuf = NULL; 1904af42dcefSratchov } 1905af42dcefSratchov } 1906af42dcefSratchov 1907af42dcefSratchov if (s->mode & MODE_PLAY) { 1908af42dcefSratchov if (s->mix.decbuf) { 1909af42dcefSratchov xfree(s->mix.decbuf); 1910af42dcefSratchov s->mix.decbuf = NULL; 1911af42dcefSratchov } 1912af42dcefSratchov if (s->mix.resampbuf) { 1913af42dcefSratchov xfree(s->mix.resampbuf); 1914af42dcefSratchov s->mix.resampbuf = NULL; 1915af42dcefSratchov } 1916af42dcefSratchov } 191787bc9f6aSratchov } 191887bc9f6aSratchov 191987bc9f6aSratchov /* 192087bc9f6aSratchov * put the slot in stopping state (draining play buffers) or 192187bc9f6aSratchov * stop & detach if no data to drain. 192287bc9f6aSratchov */ 192387bc9f6aSratchov void 1924dff6ad49Sratchov slot_stop(struct slot *s, int drain) 192587bc9f6aSratchov { 192687bc9f6aSratchov #ifdef DEBUG 1927*7b639200Sratchov logx(3, "%s%u: stopping (drain = %d)", s->name, s->unit, drain); 192887bc9f6aSratchov #endif 192987bc9f6aSratchov if (s->pstate == SLOT_START) { 1930ae520f47Sratchov /* 1931ae520f47Sratchov * If in rec-only mode, we're already in the READY or 1932ae520f47Sratchov * RUN states. We're here because the play buffer was 1933ae520f47Sratchov * not full enough, try to start so it's drained. 1934ae520f47Sratchov */ 193587bc9f6aSratchov s->pstate = SLOT_READY; 193687bc9f6aSratchov slot_ready(s); 193787bc9f6aSratchov } 19380627169cSratchov 19390627169cSratchov if (s->pstate == SLOT_RUN) { 1940dff6ad49Sratchov if ((s->mode & MODE_PLAY) && drain) { 19410627169cSratchov /* 19420627169cSratchov * Don't detach, dev_cycle() will do it for us 19430627169cSratchov * when the buffer is drained. 19440627169cSratchov */ 19450627169cSratchov s->pstate = SLOT_STOP; 19460627169cSratchov return; 19470627169cSratchov } 19480627169cSratchov slot_detach(s); 1949dff6ad49Sratchov } else if (s->pstate == SLOT_STOP) { 1950dff6ad49Sratchov slot_detach(s); 19510627169cSratchov } else { 195287bc9f6aSratchov #ifdef DEBUG 1953*7b639200Sratchov logx(3, "%s%u: not drained (blocked by mmc)", s->name, s->unit); 195487bc9f6aSratchov #endif 19550627169cSratchov } 195652217fe5Sratchov 195787bc9f6aSratchov s->pstate = SLOT_INIT; 195887bc9f6aSratchov s->ops->eof(s->arg); 195952217fe5Sratchov slot_freebufs(s); 196087bc9f6aSratchov } 196187bc9f6aSratchov 1962f75a7ae7Sratchov void 1963f75a7ae7Sratchov slot_skip_update(struct slot *s) 1964f75a7ae7Sratchov { 1965f75a7ae7Sratchov int skip; 1966f75a7ae7Sratchov 1967f75a7ae7Sratchov skip = slot_skip(s); 1968f75a7ae7Sratchov while (skip > 0) { 1969f75a7ae7Sratchov #ifdef DEBUG 1970*7b639200Sratchov logx(4, "%s%u: catching skipped block", s->name, s->unit); 1971f75a7ae7Sratchov #endif 1972f75a7ae7Sratchov if (s->mode & MODE_RECMASK) 1973f75a7ae7Sratchov s->ops->flush(s->arg); 1974f75a7ae7Sratchov if (s->mode & MODE_PLAY) 1975f75a7ae7Sratchov s->ops->fill(s->arg); 1976f75a7ae7Sratchov skip--; 1977f75a7ae7Sratchov } 1978f75a7ae7Sratchov } 1979f75a7ae7Sratchov 198087bc9f6aSratchov /* 198187bc9f6aSratchov * notify the slot that we just wrote in the play buffer, must be called 198287bc9f6aSratchov * after each write 198387bc9f6aSratchov */ 198487bc9f6aSratchov void 198587bc9f6aSratchov slot_write(struct slot *s) 198687bc9f6aSratchov { 198787bc9f6aSratchov if (s->pstate == SLOT_START && s->mix.buf.used == s->mix.buf.len) { 198887bc9f6aSratchov #ifdef DEBUG 1989*7b639200Sratchov logx(4, "%s%u: switching to READY state", s->name, s->unit); 199087bc9f6aSratchov #endif 199187bc9f6aSratchov s->pstate = SLOT_READY; 199287bc9f6aSratchov slot_ready(s); 199387bc9f6aSratchov } 1994f75a7ae7Sratchov slot_skip_update(s); 199587bc9f6aSratchov } 199687bc9f6aSratchov 199787bc9f6aSratchov /* 199887bc9f6aSratchov * notify the slot that we freed some space in the rec buffer 199987bc9f6aSratchov */ 200087bc9f6aSratchov void 200187bc9f6aSratchov slot_read(struct slot *s) 200287bc9f6aSratchov { 2003f75a7ae7Sratchov slot_skip_update(s); 200487bc9f6aSratchov } 2005d07fece6Sratchov 2006d07fece6Sratchov /* 2007d07fece6Sratchov * allocate at control slot 2008d07fece6Sratchov */ 2009d07fece6Sratchov struct ctlslot * 2010bb6cfcd4Sratchov ctlslot_new(struct opt *o, struct ctlops *ops, void *arg) 2011d07fece6Sratchov { 2012d07fece6Sratchov struct ctlslot *s; 2013d07fece6Sratchov struct ctl *c; 2014d07fece6Sratchov int i; 2015d07fece6Sratchov 2016d07fece6Sratchov i = 0; 2017d07fece6Sratchov for (;;) { 2018d07fece6Sratchov if (i == DEV_NCTLSLOT) 2019d07fece6Sratchov return NULL; 2020e5be4cb8Sratchov s = ctlslot_array + i; 2021d07fece6Sratchov if (s->ops == NULL) 2022d07fece6Sratchov break; 2023d07fece6Sratchov i++; 2024d07fece6Sratchov } 2025bb6cfcd4Sratchov s->opt = o; 2026c7054416Sratchov s->self = 1 << i; 202736355b88Sratchov if (!opt_ref(o)) 2028d07fece6Sratchov return NULL; 2029d07fece6Sratchov s->ops = ops; 2030d07fece6Sratchov s->arg = arg; 203199580020Sratchov for (c = ctl_list; c != NULL; c = c->next) { 203299580020Sratchov if (!ctlslot_visible(s, c)) 203399580020Sratchov continue; 2034c7054416Sratchov c->refs_mask |= s->self; 203599580020Sratchov } 2036d07fece6Sratchov return s; 2037d07fece6Sratchov } 2038d07fece6Sratchov 2039d07fece6Sratchov /* 2040d07fece6Sratchov * free control slot 2041d07fece6Sratchov */ 2042d07fece6Sratchov void 2043d07fece6Sratchov ctlslot_del(struct ctlslot *s) 2044d07fece6Sratchov { 2045d07fece6Sratchov struct ctl *c, **pc; 2046d07fece6Sratchov 204799580020Sratchov pc = &ctl_list; 2048d07fece6Sratchov while ((c = *pc) != NULL) { 2049c7054416Sratchov c->refs_mask &= ~s->self; 2050d07fece6Sratchov if (c->refs_mask == 0) { 2051d07fece6Sratchov *pc = c->next; 2052d07fece6Sratchov xfree(c); 2053d07fece6Sratchov } else 2054d07fece6Sratchov pc = &c->next; 2055d07fece6Sratchov } 2056d07fece6Sratchov s->ops = NULL; 205736355b88Sratchov opt_unref(s->opt); 2058d07fece6Sratchov } 2059d07fece6Sratchov 206099580020Sratchov int 206199580020Sratchov ctlslot_visible(struct ctlslot *s, struct ctl *c) 206299580020Sratchov { 206399580020Sratchov if (s->opt == NULL) 206499580020Sratchov return 1; 206599580020Sratchov switch (c->scope) { 206699580020Sratchov case CTL_HW: 206795096bd8Sratchov /* 206895096bd8Sratchov * Disable hardware's server.device control as its 206995096bd8Sratchov * replaced by sndiod's one 207095096bd8Sratchov */ 207195096bd8Sratchov if (strcmp(c->node0.name, "server") == 0 && 207295096bd8Sratchov strcmp(c->func, "device") == 0) 207395096bd8Sratchov return 0; 20747f17d764Sjsg /* FALLTHROUGH */ 207599580020Sratchov case CTL_DEV_MASTER: 207699580020Sratchov return (s->opt->dev == c->u.any.arg0); 207736355b88Sratchov case CTL_OPT_DEV: 207836355b88Sratchov return (s->opt == c->u.any.arg0); 207999580020Sratchov case CTL_SLOT_LEVEL: 208099580020Sratchov return (s->opt->dev == c->u.slot_level.slot->opt->dev); 208199580020Sratchov default: 208299580020Sratchov return 0; 208399580020Sratchov } 208499580020Sratchov } 208599580020Sratchov 208699580020Sratchov struct ctl * 208799580020Sratchov ctlslot_lookup(struct ctlslot *s, int addr) 208899580020Sratchov { 208999580020Sratchov struct ctl *c; 209099580020Sratchov 209199580020Sratchov c = ctl_list; 209299580020Sratchov while (1) { 209399580020Sratchov if (c == NULL) 209499580020Sratchov return NULL; 209599580020Sratchov if (c->type != CTL_NONE && c->addr == addr) 209699580020Sratchov break; 209799580020Sratchov c = c->next; 209899580020Sratchov } 209999580020Sratchov if (!ctlslot_visible(s, c)) 210099580020Sratchov return NULL; 210199580020Sratchov return c; 210299580020Sratchov } 210399580020Sratchov 2104d07fece6Sratchov void 210536355b88Sratchov ctlslot_update(struct ctlslot *s) 210636355b88Sratchov { 210736355b88Sratchov struct ctl *c; 210836355b88Sratchov unsigned int refs_mask; 210936355b88Sratchov 211036355b88Sratchov for (c = ctl_list; c != NULL; c = c->next) { 211136355b88Sratchov if (c->type == CTL_NONE) 211236355b88Sratchov continue; 211336355b88Sratchov refs_mask = ctlslot_visible(s, c) ? s->self : 0; 211436355b88Sratchov 211536355b88Sratchov /* nothing to do if no visibility change */ 211636355b88Sratchov if (((c->refs_mask & s->self) ^ refs_mask) == 0) 211736355b88Sratchov continue; 2118d9a51c35Sjmc /* if control becomes visible */ 211936355b88Sratchov if (refs_mask) 212036355b88Sratchov c->refs_mask |= s->self; 212136355b88Sratchov /* if control is hidden */ 212236355b88Sratchov c->desc_mask |= s->self; 212336355b88Sratchov } 21247680e5d3Sratchov if (s->ops) 21257680e5d3Sratchov s->ops->sync(s->arg); 212636355b88Sratchov } 212736355b88Sratchov 2128*7b639200Sratchov size_t 2129*7b639200Sratchov ctl_node_fmt(char *buf, size_t size, struct ctl_node *c) 2130d07fece6Sratchov { 2131*7b639200Sratchov char *end = buf + size; 2132*7b639200Sratchov char *p = buf; 2133*7b639200Sratchov 2134*7b639200Sratchov p += snprintf(buf, size, "%s", c->name); 2135*7b639200Sratchov 2136d07fece6Sratchov if (c->unit >= 0) 2137*7b639200Sratchov p += snprintf(p, p < end ? end - p : 0, "%d", c->unit); 2138*7b639200Sratchov 2139*7b639200Sratchov return p - buf; 2140d07fece6Sratchov } 2141d07fece6Sratchov 2142*7b639200Sratchov size_t 2143*7b639200Sratchov ctl_scope_fmt(char *buf, size_t size, struct ctl *c) 2144d07fece6Sratchov { 2145*7b639200Sratchov switch (c->scope) { 2146*7b639200Sratchov case CTL_HW: 2147*7b639200Sratchov return snprintf(buf, size, "hw:%s/%u", 2148*7b639200Sratchov c->u.hw.dev->name, c->u.hw.addr); 2149*7b639200Sratchov case CTL_DEV_MASTER: 2150*7b639200Sratchov return snprintf(buf, size, "dev_master:%s", 2151*7b639200Sratchov c->u.dev_master.dev->name); 2152*7b639200Sratchov case CTL_SLOT_LEVEL: 2153*7b639200Sratchov return snprintf(buf, size, "slot_level:%s%u", 2154*7b639200Sratchov c->u.slot_level.slot->name, c->u.slot_level.slot->unit); 2155*7b639200Sratchov case CTL_OPT_DEV: 2156*7b639200Sratchov return snprintf(buf, size, "opt_dev:%s/%s", 2157*7b639200Sratchov c->u.opt_dev.opt->name, c->u.opt_dev.dev->name); 2158*7b639200Sratchov default: 2159*7b639200Sratchov return snprintf(buf, size, "unknown"); 2160d07fece6Sratchov } 2161*7b639200Sratchov } 2162*7b639200Sratchov 2163*7b639200Sratchov size_t 2164*7b639200Sratchov ctl_fmt(char *buf, size_t size, struct ctl *c) 2165*7b639200Sratchov { 2166*7b639200Sratchov char *end = buf + size; 2167*7b639200Sratchov char *p = buf; 2168*7b639200Sratchov 2169*7b639200Sratchov p += snprintf(p, size, "%s/", c->group); 2170*7b639200Sratchov p += ctl_node_fmt(p, p < end ? end - p : 0, &c->node0); 2171*7b639200Sratchov p += snprintf(p, p < end ? end - p : 0, ".%s", c->func); 2172*7b639200Sratchov 2173d07fece6Sratchov switch (c->type) { 2174d07fece6Sratchov case CTL_VEC: 2175d07fece6Sratchov case CTL_LIST: 217649f67e12Sratchov case CTL_SEL: 2177*7b639200Sratchov p += snprintf(p, p < end ? end - p : 0, "["); 2178*7b639200Sratchov p += ctl_node_fmt(p, p < end ? end - p : 0, &c->node1); 2179*7b639200Sratchov p += snprintf(p, p < end ? end - p : 0, "]"); 2180d07fece6Sratchov } 2181*7b639200Sratchov 2182*7b639200Sratchov if (c->display[0] != 0) 2183*7b639200Sratchov p += snprintf(p, size, " (%s)", c->display); 2184*7b639200Sratchov 2185*7b639200Sratchov return p - buf; 218699580020Sratchov } 218799580020Sratchov 218899580020Sratchov int 218999580020Sratchov ctl_setval(struct ctl *c, int val) 219099580020Sratchov { 219199580020Sratchov if (c->curval == val) { 2192*7b639200Sratchov logx(3, "ctl%u: already set", c->addr); 219399580020Sratchov return 1; 219499580020Sratchov } 219599580020Sratchov if (val < 0 || val > c->maxval) { 2196*7b639200Sratchov logx(3, "ctl%u: %d: out of range", c->addr, val); 219799580020Sratchov return 0; 219899580020Sratchov } 219999580020Sratchov 220099580020Sratchov switch (c->scope) { 220199580020Sratchov case CTL_HW: 2202*7b639200Sratchov logx(3, "ctl%u: marked as dirty", c->addr); 220399580020Sratchov c->curval = val; 220499580020Sratchov c->dirty = 1; 220599580020Sratchov return dev_ref(c->u.hw.dev); 220699580020Sratchov case CTL_DEV_MASTER: 220799580020Sratchov if (!c->u.dev_master.dev->master_enabled) 220899580020Sratchov return 1; 220999580020Sratchov dev_master(c->u.dev_master.dev, val); 221099580020Sratchov dev_midi_master(c->u.dev_master.dev); 221199580020Sratchov c->val_mask = ~0U; 221299580020Sratchov c->curval = val; 221399580020Sratchov return 1; 221499580020Sratchov case CTL_SLOT_LEVEL: 221599580020Sratchov slot_setvol(c->u.slot_level.slot, val); 221699580020Sratchov // XXX change dev_midi_vol() into slot_midi_vol() 221799580020Sratchov dev_midi_vol(c->u.slot_level.slot->opt->dev, c->u.slot_level.slot); 221899580020Sratchov c->val_mask = ~0U; 221999580020Sratchov c->curval = val; 222099580020Sratchov return 1; 222136355b88Sratchov case CTL_OPT_DEV: 22220058b113Sratchov if (opt_setdev(c->u.opt_dev.opt, c->u.opt_dev.dev)) 222336355b88Sratchov c->u.opt_dev.opt->alt_first = c->u.opt_dev.dev; 222436355b88Sratchov return 1; 222599580020Sratchov default: 2226*7b639200Sratchov logx(2, "ctl%u: not writable", c->addr); 222799580020Sratchov return 1; 222899580020Sratchov } 2229d07fece6Sratchov } 2230d07fece6Sratchov 2231d07fece6Sratchov /* 2232d07fece6Sratchov * add a ctl 2233d07fece6Sratchov */ 2234d07fece6Sratchov struct ctl * 223599580020Sratchov ctl_new(int scope, void *arg0, void *arg1, 22364d8b188fSratchov int type, char *display, char *gstr, 223799580020Sratchov char *str0, int unit0, char *func, 223899580020Sratchov char *str1, int unit1, int maxval, int val) 2239d07fece6Sratchov { 2240*7b639200Sratchov #ifdef DEBUG 2241*7b639200Sratchov char ctl_str[64], scope_str[32]; 2242*7b639200Sratchov #endif 2243d07fece6Sratchov struct ctl *c, **pc; 224499580020Sratchov struct ctlslot *s; 224599580020Sratchov int addr; 2246d07fece6Sratchov int i; 2247d07fece6Sratchov 224899580020Sratchov /* 224999580020Sratchov * find the smallest unused addr number and 225099580020Sratchov * the last position in the list 225199580020Sratchov */ 225299580020Sratchov addr = 0; 225399580020Sratchov for (pc = &ctl_list; (c = *pc) != NULL; pc = &c->next) { 225499580020Sratchov if (c->addr > addr) 225599580020Sratchov addr = c->addr; 225699580020Sratchov } 225799580020Sratchov addr++; 225899580020Sratchov 2259d07fece6Sratchov c = xmalloc(sizeof(struct ctl)); 2260d07fece6Sratchov c->type = type; 2261d07fece6Sratchov strlcpy(c->func, func, CTL_NAMEMAX); 2262d07fece6Sratchov strlcpy(c->group, gstr, CTL_NAMEMAX); 22634d8b188fSratchov strlcpy(c->display, display, CTL_DISPLAYMAX); 2264d07fece6Sratchov strlcpy(c->node0.name, str0, CTL_NAMEMAX); 2265d07fece6Sratchov c->node0.unit = unit0; 226649f67e12Sratchov if (c->type == CTL_VEC || c->type == CTL_LIST || c->type == CTL_SEL) { 2267d07fece6Sratchov strlcpy(c->node1.name, str1, CTL_NAMEMAX); 2268d07fece6Sratchov c->node1.unit = unit1; 2269d07fece6Sratchov } else 2270d07fece6Sratchov memset(&c->node1, 0, sizeof(struct ctl_node)); 227199580020Sratchov c->scope = scope; 227299580020Sratchov c->u.any.arg0 = arg0; 227399580020Sratchov switch (scope) { 227499580020Sratchov case CTL_HW: 227599580020Sratchov c->u.hw.addr = *(unsigned int *)arg1; 227699580020Sratchov break; 227736355b88Sratchov case CTL_OPT_DEV: 227836355b88Sratchov c->u.any.arg1 = arg1; 227999580020Sratchov break; 228099580020Sratchov default: 228199580020Sratchov c->u.any.arg1 = NULL; 228299580020Sratchov } 2283d07fece6Sratchov c->addr = addr; 2284d07fece6Sratchov c->maxval = maxval; 2285d07fece6Sratchov c->val_mask = ~0; 2286d07fece6Sratchov c->desc_mask = ~0; 2287d07fece6Sratchov c->curval = val; 2288d07fece6Sratchov c->dirty = 0; 228999580020Sratchov c->refs_mask = CTL_DEVMASK; 229099580020Sratchov for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) { 229199580020Sratchov if (s->ops == NULL) 229299580020Sratchov continue; 229399580020Sratchov if (ctlslot_visible(s, c)) 2294d07fece6Sratchov c->refs_mask |= 1 << i; 2295d07fece6Sratchov } 229699580020Sratchov c->next = *pc; 2297d07fece6Sratchov *pc = c; 2298d07fece6Sratchov #ifdef DEBUG 2299*7b639200Sratchov logx(2, "ctl%u: %s = %d at %s: added", c->addr, 2300*7b639200Sratchov (ctl_fmt(ctl_str, sizeof(ctl_str), c), ctl_str), c->curval, 2301*7b639200Sratchov (ctl_scope_fmt(scope_str, sizeof(scope_str), c), scope_str)); 2302d07fece6Sratchov #endif 2303d07fece6Sratchov return c; 2304d07fece6Sratchov } 2305d07fece6Sratchov 2306d07fece6Sratchov void 230799580020Sratchov ctl_update(struct ctl *c) 230899580020Sratchov { 230999580020Sratchov struct ctlslot *s; 231099580020Sratchov unsigned int refs_mask; 231199580020Sratchov int i; 231299580020Sratchov 231399580020Sratchov for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) { 231499580020Sratchov if (s->ops == NULL) 231599580020Sratchov continue; 231699580020Sratchov refs_mask = ctlslot_visible(s, c) ? s->self : 0; 231799580020Sratchov 231899580020Sratchov /* nothing to do if no visibility change */ 231999580020Sratchov if (((c->refs_mask & s->self) ^ refs_mask) == 0) 232099580020Sratchov continue; 2321d9a51c35Sjmc /* if control becomes visible */ 232299580020Sratchov if (refs_mask) 232399580020Sratchov c->refs_mask |= s->self; 232499580020Sratchov /* if control is hidden */ 232599580020Sratchov c->desc_mask |= s->self; 23267680e5d3Sratchov s->ops->sync(s->arg); 232799580020Sratchov } 232899580020Sratchov } 232999580020Sratchov 233099580020Sratchov int 233199580020Sratchov ctl_match(struct ctl *c, int scope, void *arg0, void *arg1) 233299580020Sratchov { 233399580020Sratchov if (c->type == CTL_NONE || c->scope != scope || c->u.any.arg0 != arg0) 233499580020Sratchov return 0; 233599580020Sratchov if (arg0 != NULL && c->u.any.arg0 != arg0) 233699580020Sratchov return 0; 233799580020Sratchov switch (scope) { 233899580020Sratchov case CTL_HW: 233999580020Sratchov if (arg1 != NULL && c->u.hw.addr != *(unsigned int *)arg1) 234099580020Sratchov return 0; 234199580020Sratchov break; 234236355b88Sratchov case CTL_OPT_DEV: 234336355b88Sratchov if (arg1 != NULL && c->u.any.arg1 != arg1) 234499580020Sratchov return 0; 234599580020Sratchov break; 234699580020Sratchov } 234799580020Sratchov return 1; 234899580020Sratchov } 234999580020Sratchov 235099580020Sratchov struct ctl * 235199580020Sratchov ctl_find(int scope, void *arg0, void *arg1) 235299580020Sratchov { 235399580020Sratchov struct ctl *c; 235499580020Sratchov 235599580020Sratchov for (c = ctl_list; c != NULL; c = c->next) { 235699580020Sratchov if (ctl_match(c, scope, arg0, arg1)) 235799580020Sratchov return c; 235899580020Sratchov } 235999580020Sratchov return NULL; 236099580020Sratchov } 236199580020Sratchov 236299580020Sratchov int 236399580020Sratchov ctl_onval(int scope, void *arg0, void *arg1, int val) 236499580020Sratchov { 236599580020Sratchov struct ctl *c; 236699580020Sratchov 236799580020Sratchov c = ctl_find(scope, arg0, arg1); 236899580020Sratchov if (c == NULL) 236999580020Sratchov return 0; 237099580020Sratchov c->curval = val; 237199580020Sratchov c->val_mask = ~0U; 237299580020Sratchov return 1; 237399580020Sratchov } 237499580020Sratchov 2375ea04d488Sratchov int 237699580020Sratchov ctl_del(int scope, void *arg0, void *arg1) 2377d07fece6Sratchov { 2378*7b639200Sratchov #ifdef DEBUG 2379*7b639200Sratchov char str[64]; 2380*7b639200Sratchov #endif 2381d07fece6Sratchov struct ctl *c, **pc; 2382ea04d488Sratchov int found; 2383d07fece6Sratchov 2384ea04d488Sratchov found = 0; 238599580020Sratchov pc = &ctl_list; 2386d07fece6Sratchov for (;;) { 2387d07fece6Sratchov c = *pc; 2388d07fece6Sratchov if (c == NULL) 2389ea04d488Sratchov return found; 239099580020Sratchov if (ctl_match(c, scope, arg0, arg1)) { 2391d07fece6Sratchov #ifdef DEBUG 2392*7b639200Sratchov logx(2, "ctl%u: %s: removed", c->addr, 2393*7b639200Sratchov (ctl_fmt(str, sizeof(str), c), str)); 2394d07fece6Sratchov #endif 2395ea04d488Sratchov found++; 2396d07fece6Sratchov c->refs_mask &= ~CTL_DEVMASK; 2397105cbcdeSratchov if (c->refs_mask == 0) { 2398d07fece6Sratchov *pc = c->next; 2399d07fece6Sratchov xfree(c); 240099580020Sratchov continue; 2401105cbcdeSratchov } 240299580020Sratchov c->type = CTL_NONE; 2403105cbcdeSratchov c->desc_mask = ~0; 2404d07fece6Sratchov } 240599580020Sratchov pc = &c->next; 240699580020Sratchov } 240799580020Sratchov } 2408d07fece6Sratchov 24094d8b188fSratchov char * 24104d8b188fSratchov dev_getdisplay(struct dev *d) 24114d8b188fSratchov { 24124d8b188fSratchov struct ctl *c; 24134d8b188fSratchov char *display; 24144d8b188fSratchov 24154d8b188fSratchov display = ""; 24164d8b188fSratchov for (c = ctl_list; c != NULL; c = c->next) { 24174d8b188fSratchov if (c->scope == CTL_HW && 24184d8b188fSratchov c->u.hw.dev == d && 24194d8b188fSratchov c->type == CTL_SEL && 24204d8b188fSratchov strcmp(c->group, d->name) == 0 && 24214d8b188fSratchov strcmp(c->node0.name, "server") == 0 && 24224d8b188fSratchov strcmp(c->func, "device") == 0 && 24234d8b188fSratchov c->curval == 1) 24244d8b188fSratchov display = c->display; 24254d8b188fSratchov } 24264d8b188fSratchov return display; 24274d8b188fSratchov } 24284d8b188fSratchov 2429e575fbdeSratchov void 2430e575fbdeSratchov dev_ctlsync(struct dev *d) 2431e575fbdeSratchov { 2432f08c7e02Sratchov struct ctl *c; 2433e575fbdeSratchov struct ctlslot *s; 24344d8b188fSratchov const char *display; 2435f08c7e02Sratchov int found, i; 2436f08c7e02Sratchov 2437f08c7e02Sratchov found = 0; 243899580020Sratchov for (c = ctl_list; c != NULL; c = c->next) { 243999580020Sratchov if (c->scope == CTL_HW && 244099580020Sratchov c->u.hw.dev == d && 2441f08c7e02Sratchov c->type == CTL_NUM && 244299580020Sratchov strcmp(c->group, d->name) == 0 && 2443f08c7e02Sratchov strcmp(c->node0.name, "output") == 0 && 2444f08c7e02Sratchov strcmp(c->func, "level") == 0) 2445f08c7e02Sratchov found = 1; 2446f08c7e02Sratchov } 2447f08c7e02Sratchov 2448f08c7e02Sratchov if (d->master_enabled && found) { 2449*7b639200Sratchov logx(2, "%s: software master level control disabled", d->path); 2450f08c7e02Sratchov d->master_enabled = 0; 245199580020Sratchov ctl_del(CTL_DEV_MASTER, d, NULL); 2452f08c7e02Sratchov } else if (!d->master_enabled && !found) { 2453*7b639200Sratchov logx(2, "%s: software master level control enabled", d->path); 2454f08c7e02Sratchov d->master_enabled = 1; 245599580020Sratchov ctl_new(CTL_DEV_MASTER, d, NULL, 24564d8b188fSratchov CTL_NUM, "", d->name, "output", -1, "level", 245799580020Sratchov NULL, -1, 127, d->master); 2458f08c7e02Sratchov } 2459e575fbdeSratchov 24604d8b188fSratchov /* 24614d8b188fSratchov * if the hardware's server.device changed, update the display name 24624d8b188fSratchov */ 24634d8b188fSratchov display = dev_getdisplay(d); 24644d8b188fSratchov for (c = ctl_list; c != NULL; c = c->next) { 24654d8b188fSratchov if (c->scope != CTL_OPT_DEV || 24664d8b188fSratchov c->u.opt_dev.dev != d || 24674d8b188fSratchov strcmp(c->display, display) == 0) 24684d8b188fSratchov continue; 24694d8b188fSratchov strlcpy(c->display, display, CTL_DISPLAYMAX); 24704d8b188fSratchov c->desc_mask = ~0; 24714d8b188fSratchov } 24724d8b188fSratchov 2473971ff709Sratchov for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) { 2474971ff709Sratchov if (s->ops == NULL) 2475971ff709Sratchov continue; 2476971ff709Sratchov if (s->opt->dev == d) 2477e575fbdeSratchov s->ops->sync(s->arg); 2478e575fbdeSratchov } 2479e575fbdeSratchov } 2480