xref: /openbsd-src/usr.bin/sndiod/dev.c (revision 7b6392009e6e5a7f8e494c162a4d259ea5e13a62)
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