xref: /openbsd-src/usr.bin/sndiod/opt.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: opt.c,v 1.8 2021/03/03 10:19:06 ratchov Exp $	*/
2 /*
3  * Copyright (c) 2008-2011 Alexandre Ratchov <alex@caoua.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <string.h>
18 
19 #include "dev.h"
20 #include "midi.h"
21 #include "opt.h"
22 #include "sysex.h"
23 #include "utils.h"
24 
25 struct opt *opt_list;
26 
27 void opt_midi_imsg(void *, unsigned char *, int);
28 void opt_midi_omsg(void *, unsigned char *, int);
29 void opt_midi_fill(void *, int);
30 void opt_midi_exit(void *);
31 
32 struct midiops opt_midiops = {
33 	opt_midi_imsg,
34 	opt_midi_omsg,
35 	opt_midi_fill,
36 	opt_midi_exit
37 };
38 
39 void
40 opt_midi_imsg(void *arg, unsigned char *msg, int len)
41 {
42 #ifdef DEBUG
43 	struct opt *o = arg;
44 
45 	log_puts(o->name);
46 	log_puts(": can't receive midi messages\n");
47 	panic();
48 #endif
49 }
50 
51 void
52 opt_midi_omsg(void *arg, unsigned char *msg, int len)
53 {
54 	struct opt *o = arg;
55 	struct sysex *x;
56 	unsigned int fps, chan;
57 
58 	if ((msg[0] & MIDI_CMDMASK) == MIDI_CTL && msg[1] == MIDI_CTL_VOL) {
59 		chan = msg[0] & MIDI_CHANMASK;
60 		if (chan >= DEV_NSLOT)
61 			return;
62 		if (slot_array[chan].opt == NULL ||
63 		    slot_array[chan].opt->dev != o->dev)
64 			return;
65 		slot_setvol(slot_array + chan, msg[2]);
66 		ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]);
67 		return;
68 	}
69 	x = (struct sysex *)msg;
70 	if (x->start != SYSEX_START)
71 		return;
72 	if (len < SYSEX_SIZE(empty))
73 		return;
74 	switch (x->type) {
75 	case SYSEX_TYPE_RT:
76 		if (x->id0 == SYSEX_CONTROL && x->id1 == SYSEX_MASTER) {
77 			if (len == SYSEX_SIZE(master)) {
78 				dev_master(o->dev, x->u.master.coarse);
79 				if (o->dev->master_enabled) {
80 					ctl_onval(CTL_DEV_MASTER, o->dev, NULL,
81 					   x->u.master.coarse);
82 				}
83 			}
84 			return;
85 		}
86 		if (x->id0 != SYSEX_MMC)
87 			return;
88 		switch (x->id1) {
89 		case SYSEX_MMC_STOP:
90 			if (len != SYSEX_SIZE(stop))
91 				return;
92 			if (o->mtc == NULL)
93 				return;
94 			if (log_level >= 2) {
95 				log_puts(o->name);
96 				log_puts(": mmc stop\n");
97 			}
98 			mtc_stop(o->mtc);
99 			break;
100 		case SYSEX_MMC_START:
101 			if (len != SYSEX_SIZE(start))
102 				return;
103 			if (o->mtc == NULL)
104 				return;
105 			if (log_level >= 2) {
106 				log_puts(o->name);
107 				log_puts(": mmc start\n");
108 			}
109 			mtc_start(o->mtc);
110 			break;
111 		case SYSEX_MMC_LOC:
112 			if (len != SYSEX_SIZE(loc) ||
113 			    x->u.loc.len != SYSEX_MMC_LOC_LEN ||
114 			    x->u.loc.cmd != SYSEX_MMC_LOC_CMD)
115 				return;
116 			if (o->mtc == NULL)
117 				return;
118 			switch (x->u.loc.hr >> 5) {
119 			case MTC_FPS_24:
120 				fps = 24;
121 				break;
122 			case MTC_FPS_25:
123 				fps = 25;
124 				break;
125 			case MTC_FPS_30:
126 				fps = 30;
127 				break;
128 			default:
129 				mtc_stop(o->mtc);
130 				return;
131 			}
132 			mtc_loc(o->mtc,
133 			    (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
134 			     x->u.loc.min * 60 * MTC_SEC +
135 			     x->u.loc.sec * MTC_SEC +
136 			     x->u.loc.fr * (MTC_SEC / fps));
137 			break;
138 		}
139 		break;
140 	case SYSEX_TYPE_EDU:
141 		if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ)
142 			return;
143 		if (len != SYSEX_SIZE(dumpreq))
144 			return;
145 		dev_midi_dump(o->dev);
146 		break;
147 	}
148 }
149 
150 void
151 opt_midi_fill(void *arg, int count)
152 {
153 	/* nothing to do */
154 }
155 
156 void
157 opt_midi_exit(void *arg)
158 {
159 	struct opt *o = arg;
160 
161 	if (log_level >= 1) {
162 		log_puts(o->name);
163 		log_puts(": midi end point died\n");
164 		panic();
165 	}
166 }
167 
168 /*
169  * create a new audio sub-device "configuration"
170  */
171 struct opt *
172 opt_new(struct dev *d, char *name,
173     int pmin, int pmax, int rmin, int rmax,
174     int maxweight, int mmc, int dup, unsigned int mode)
175 {
176 	struct opt *o, **po;
177 	unsigned int len, num;
178 	char c;
179 
180 	for (len = 0; name[len] != '\0'; len++) {
181 		if (len == OPT_NAMEMAX) {
182 			log_puts(name);
183 			log_puts(": too long\n");
184 			return NULL;
185 		}
186 		c = name[len];
187 		if ((c < 'a' || c > 'z') &&
188 		    (c < 'A' || c > 'Z')) {
189 			log_puts(name);
190 			log_puts(": only alphabetic chars allowed\n");
191 			return NULL;
192 		}
193 	}
194 	num = 0;
195 	for (po = &opt_list; *po != NULL; po = &(*po)->next)
196 		num++;
197 	if (num >= OPT_NMAX) {
198 		log_puts(name);
199 		log_puts(": too many opts\n");
200 		return NULL;
201 	}
202 	if (opt_byname(d, name)) {
203 		dev_log(d);
204 		log_puts(".");
205 		log_puts(name);
206 		log_puts(": already defined\n");
207 		return NULL;
208 	}
209 
210 	if (mmc) {
211 		if (mtc_array[0].dev != NULL && mtc_array[0].dev != d) {
212 			log_puts(name);
213 			log_puts(": MTC already setup for another device\n");
214 			return NULL;
215 		}
216 		mtc_array[0].dev = d;
217 		if (log_level >= 2) {
218 			dev_log(d);
219 			log_puts(": initial MTC source, controlled by MMC\n");
220 		}
221 	}
222 
223 	o = xmalloc(sizeof(struct opt));
224 	o->num = num;
225 	o->dev = d;
226 
227 	/*
228 	 * XXX: below, we allocate a midi input buffer, since we don't
229 	 *	receive raw midi data, so no need to allocate a input
230 	 *	ibuf.  Possibly set imsg & fill callbacks to NULL and
231 	 *	use this to in midi_new() to check if buffers need to be
232 	 *	allocated
233 	 */
234 	o->midi = midi_new(&opt_midiops, o, MODE_MIDIIN | MODE_MIDIOUT);
235 	midi_tag(o->midi, o->num);
236 
237 	if (mode & MODE_PLAY) {
238 		o->pmin = pmin;
239 		o->pmax = pmax;
240 	}
241 	if (mode & MODE_RECMASK) {
242 		o->rmin = rmin;
243 		o->rmax = rmax;
244 	}
245 	o->maxweight = maxweight;
246 	o->mtc = mmc ? &mtc_array[0] : NULL;
247 	o->dup = dup;
248 	o->mode = mode;
249 	memcpy(o->name, name, len + 1);
250 	o->next = *po;
251 	*po = o;
252 	if (log_level >= 2) {
253 		dev_log(d);
254 		log_puts(".");
255 		log_puts(o->name);
256 		log_puts(":");
257 		if (o->mode & MODE_REC) {
258 			log_puts(" rec=");
259 			log_putu(o->rmin);
260 			log_puts(":");
261 			log_putu(o->rmax);
262 		}
263 		if (o->mode & MODE_PLAY) {
264 			log_puts(" play=");
265 			log_putu(o->pmin);
266 			log_puts(":");
267 			log_putu(o->pmax);
268 			log_puts(" vol=");
269 			log_putu(o->maxweight);
270 		}
271 		if (o->mode & MODE_MON) {
272 			log_puts(" mon=");
273 			log_putu(o->rmin);
274 			log_puts(":");
275 			log_putu(o->rmax);
276 		}
277 		if (o->mode & (MODE_RECMASK | MODE_PLAY)) {
278 			if (o->mtc)
279 				log_puts(" mtc");
280 			if (o->dup)
281 				log_puts(" dup");
282 		}
283 		log_puts("\n");
284 	}
285 	return o;
286 }
287 
288 struct opt *
289 opt_byname(struct dev *d, char *name)
290 {
291 	struct opt *o;
292 
293 	for (o = opt_list; o != NULL; o = o->next) {
294 		if (d != NULL && o->dev != d)
295 			continue;
296 		if (strcmp(name, o->name) == 0)
297 			return o;
298 	}
299 	return NULL;
300 }
301 
302 void
303 opt_del(struct opt *o)
304 {
305 	struct opt **po;
306 
307 	for (po = &opt_list; *po != o; po = &(*po)->next) {
308 #ifdef DEBUG
309 		if (*po == NULL) {
310 			log_puts("opt_del: not on list\n");
311 			panic();
312 		}
313 #endif
314 	}
315 	midi_del(o->midi);
316 	*po = o->next;
317 	xfree(o);
318 }
319