Annotation of src/usr.bin/aucat/midi.c, Revision 1.8
1.8 ! ratchov 1: /* $OpenBSD: midi.c,v 1.7 2009/08/27 06:31:13 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 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: /*
18: * TODO
19: *
1.7 ratchov 20: * use shadow variables (to save NRPNs, LSB of controller)
21: * in the midi merger
1.1 ratchov 22: *
23: * make output and input identical when only one
24: * input is used (fix running status)
25: */
26: #include <stdio.h>
27: #include <stdlib.h>
28: #include <string.h>
29:
30: #include "abuf.h"
31: #include "aproc.h"
1.3 ratchov 32: #include "conf.h"
33: #include "dev.h"
1.1 ratchov 34: #include "midi.h"
35:
36: /*
37: * input data rate is XFER / TIMO (in bytes per microsecond),
38: * it must be slightly larger than the MIDI standard 3125 bytes/s
39: */
40: #define MIDITHRU_XFER 340
41: #define MIDITHRU_TIMO 100000
42:
1.3 ratchov 43: /*
44: * masks to extract command and channel of status byte
45: */
46: #define MIDI_CMDMASK 0xf0
47: #define MIDI_CHANMASK 0x0f
48:
49: /*
50: * MIDI status bytes of voice messages
51: */
52: #define MIDI_NOFF 0x80 /* note off */
53: #define MIDI_NON 0x90 /* note on */
54: #define MIDI_KAT 0xa0 /* key after touch */
55: #define MIDI_CTL 0xb0 /* controller */
56: #define MIDI_PC 0xc0 /* program change */
57: #define MIDI_CAT 0xd0 /* channel after touch */
58: #define MIDI_BEND 0xe0 /* pitch bend */
59:
60: /*
61: * MIDI controller numbers
62: */
63: #define MIDI_CTLVOL 7 /* volume */
64: #define MIDI_CTLPAN 11 /* pan */
65:
66: /*
67: * length of voice and common messages (status byte included)
68: */
1.1 ratchov 69: unsigned voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
70: unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
71:
1.7 ratchov 72: /*
73: * send the message stored in of ibuf->mdata to obuf
74: */
1.1 ratchov 75: void
76: thru_flush(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
77: {
78: unsigned ocount, itodo;
79: unsigned char *odata, *idata;
80:
81: itodo = ibuf->mused;
82: idata = ibuf->mdata;
83: DPRINTFN(4, "thru_flush: mused = %u\n", itodo);
84: while (itodo > 0) {
85: if (!ABUF_WOK(obuf)) {
86: abuf_rdiscard(obuf, obuf->used);
87: DPRINTFN(2, "thru_flush: discarded %u\n", obuf->used);
88: if (p->u.thru.owner == ibuf)
89: p->u.thru.owner = NULL;
90: return;
91: }
92: odata = abuf_wgetblk(obuf, &ocount, 0);
93: if (ocount > itodo)
94: ocount = itodo;
95: memcpy(odata, idata, ocount);
96: abuf_wcommit(obuf, ocount);
97: itodo -= ocount;
98: idata += ocount;
99: }
100: ibuf->mused = 0;
101: p->u.thru.owner = ibuf;
102: }
103:
1.7 ratchov 104: /*
105: * send the real-time message (one byte) to obuf, similar to thrui_flush()
106: */
1.1 ratchov 107: void
108: thru_rt(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned c)
109: {
110: unsigned ocount;
111: unsigned char *odata;
112:
113: DPRINTFN(4, "thru_rt:\n");
114: if (!ABUF_WOK(obuf)) {
115: DPRINTFN(2, "thru_rt: discarded %u\n", obuf->used);
116: abuf_rdiscard(obuf, obuf->used);
117: if (p->u.thru.owner == ibuf)
118: p->u.thru.owner = NULL;
119: }
120: odata = abuf_wgetblk(obuf, &ocount, 0);
121: odata[0] = c;
122: abuf_wcommit(obuf, 1);
123: }
124:
1.7 ratchov 125: /*
126: * parse ibuf contents and store each message into obuf,
127: * use at most ``todo'' bytes (for throttling)
128: */
1.1 ratchov 129: void
130: thru_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned todo)
131: {
132: unsigned char *idata;
133: unsigned c, icount, ioffs;
134:
135: idata = NULL;
136: icount = ioffs = 0;
137: for (;;) {
138: if (icount == 0) {
139: if (todo == 0)
140: break;
141: idata = abuf_rgetblk(ibuf, &icount, ioffs);
142: if (icount > todo)
143: icount = todo;
144: if (icount == 0)
145: break;
146: todo -= icount;
147: ioffs += icount;
148: }
149: c = *idata++;
150: icount--;
151: if (c < 0x80) {
152: if (ibuf->mindex == 0 && ibuf->mstatus) {
153: ibuf->mdata[ibuf->mused++] = ibuf->mstatus;
154: ibuf->mindex++;
155: }
156: ibuf->mdata[ibuf->mused++] = c;
157: ibuf->mindex++;
158: if (ibuf->mindex == ibuf->mlen) {
159: thru_flush(p, ibuf, obuf);
160: if (ibuf->mstatus >= 0xf0)
161: ibuf->mstatus = 0;
162: ibuf->mindex = 0;
163: }
164: if (ibuf->mused == MDATA_NMAX) {
165: if (ibuf->mused == ibuf->mindex ||
166: p->u.thru.owner == ibuf)
167: thru_flush(p, ibuf, obuf);
168: else
169: ibuf->mused = 0;
170: }
171: } else if (c < 0xf8) {
172: if (ibuf->mused == ibuf->mindex ||
173: p->u.thru.owner == ibuf) {
174: thru_flush(p, ibuf, obuf);
175: } else
176: ibuf->mused = 0;
177: ibuf->mdata[0] = c;
178: ibuf->mused = 1;
179: ibuf->mlen = (c >= 0xf0) ?
180: common_len[c & 7] :
181: voice_len[(c >> 4) & 7];
182: if (ibuf->mlen == 1) {
183: thru_flush(p, ibuf, obuf);
184: ibuf->mindex = 0;
185: ibuf->mstatus = 0;
186: ibuf->mlen = 0;
187: } else {
188: ibuf->mstatus = c;
189: ibuf->mindex = 1;
190: }
191: } else {
192: thru_rt(p, ibuf, obuf, c);
193: }
194: }
195: }
196:
197: int
198: thru_in(struct aproc *p, struct abuf *ibuf)
199: {
200: struct abuf *i, *inext;
201: unsigned todo;
202:
203: DPRINTFN(3, "thru_in: %s\n", p->name);
204:
205: if (!ABUF_ROK(ibuf))
206: return 0;
207: if (ibuf->mtickets == 0) {
208: DPRINTFN(2, "thru_in: out of tickets\n");
209: return 0;
210: }
211: todo = ibuf->used;
212: if (todo > ibuf->mtickets)
213: todo = ibuf->mtickets;
214: ibuf->mtickets -= todo;
215: for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) {
216: inext = LIST_NEXT(i, oent);
217: if (ibuf->duplex == i)
218: continue;
219: thru_bcopy(p, ibuf, i, todo);
220: (void)abuf_flush(i);
221: }
222: abuf_rdiscard(ibuf, todo);
223: return 1;
224: }
225:
226: int
227: thru_out(struct aproc *p, struct abuf *obuf)
228: {
229: return 0;
230: }
231:
232: void
233: thru_eof(struct aproc *p, struct abuf *ibuf)
234: {
235: DPRINTF("thru_eof: %s: eof\n", p->name);
236: }
237:
238: void
239: thru_hup(struct aproc *p, struct abuf *obuf)
240: {
241: DPRINTF("thru_hup: %s: detached\n", p->name);
242: }
243:
244: void
245: thru_newin(struct aproc *p, struct abuf *ibuf)
246: {
247: ibuf->mused = 0;
248: ibuf->mlen = 0;
249: ibuf->mindex = 0;
250: ibuf->mstatus = 0;
251: ibuf->mtickets = MIDITHRU_XFER;
252: }
253:
254: void
255: thru_done(struct aproc *p)
256: {
257: timo_del(&p->u.thru.timo);
258: }
259:
260: struct aproc_ops thru_ops = {
261: "thru",
262: thru_in,
263: thru_out,
264: thru_eof,
265: thru_hup,
266: thru_newin,
267: NULL, /* newout */
268: NULL, /* ipos */
269: NULL, /* opos */
270: thru_done
271: };
272:
1.7 ratchov 273: /*
274: * call-back invoked periodically to implement throttling at each invocation
275: * gain more ``tickets'' for processing. If one of the buffer was blocked by
276: * the throttelling mechanism, then run it
277: */
1.1 ratchov 278: void
279: thru_cb(void *addr)
280: {
281: struct aproc *p = (struct aproc *)addr;
282: struct abuf *i, *inext;
283: unsigned tickets;
284:
285: timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
286:
287: for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) {
288: inext = LIST_NEXT(i, ient);
289: tickets = i->mtickets;
290: i->mtickets = MIDITHRU_XFER;
291: if (tickets == 0)
292: abuf_run(i);
293: }
294: }
295:
296: struct aproc *
297: thru_new(char *name)
298: {
299: struct aproc *p;
300:
301: p = aproc_new(&thru_ops, name);
302: p->u.thru.owner = NULL;
303: timo_set(&p->u.thru.timo, thru_cb, p);
304: timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
1.3 ratchov 305: return p;
306: }
307:
1.7 ratchov 308: /*
309: * broadcast a message to all output buffers on the behalf of ibuf.
310: * ie. don't sent back the message to the sender
311: */
1.3 ratchov 312: void
313: ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len)
314: {
315: unsigned ocount, itodo;
316: unsigned char *odata, *idata;
317: struct abuf *i, *inext;
318:
319: for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) {
320: inext = LIST_NEXT(i, oent);
321: if (i->duplex == ibuf)
322: continue;
323: itodo = len;
324: idata = msg;
325: while (itodo > 0) {
326: if (!ABUF_WOK(i)) {
327: DPRINTFN(2, "ctl_sendmsg: lost %u\n", i->used);
328: abuf_rdiscard(i, i->used);
329: }
330: odata = abuf_wgetblk(i, &ocount, 0);
331: if (ocount > itodo)
332: ocount = itodo;
333: DPRINTFN(2, "ctl_sendmsg: xfer %u\n", ocount);
334: memcpy(odata, idata, ocount);
335: abuf_wcommit(i, ocount);
336: itodo -= ocount;
337: idata += ocount;
338: }
339: (void)abuf_flush(i);
340: }
341: }
342:
1.7 ratchov 343: /*
344: * allocate a new slot (ie midi channel), register the given call-back
345: * to be called volume is changed by MIDI. The call-back is invoked at
346: * initialization to restore the saved volume.
347: */
1.3 ratchov 348: int
1.5 ratchov 349: ctl_slotnew(struct aproc *p, char *who, void (*cb)(void *, unsigned), void *arg)
1.3 ratchov 350: {
351: char *s;
352: struct ctl_slot *slot;
1.4 ratchov 353: char name[CTL_NAMEMAX];
354: unsigned i, unit, umap = 0;
1.6 ratchov 355: unsigned ser, bestser, bestidx;
1.3 ratchov 356:
1.4 ratchov 357: /*
358: * create a ``valid'' control name (lowcase, remove [^a-z], trucate)
359: */
1.5 ratchov 360: for (i = 0, s = who; ; s++) {
1.4 ratchov 361: if (i == CTL_NAMEMAX - 1 || *s == '\0') {
362: name[i] = '\0';
363: break;
364: } else if (*s >= 'A' && *s <= 'Z') {
365: name[i++] = *s + 'a' - 'A';
366: } else if (*s >= 'a' && *s <= 'z')
367: name[i++] = *s;
368: }
369: if (i == 0)
370: strlcpy(name, "noname", CTL_NAMEMAX);
371:
372: /*
373: * find the instance number of the control name
374: */
375: for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
1.5 ratchov 376: if (slot->cb == NULL)
1.4 ratchov 377: continue;
378: if (strcmp(slot->name, name) == 0)
379: umap |= (1 << i);
380: }
381: for (unit = 0; unit < CTL_NSLOT; unit++) {
382: if (unit == CTL_NSLOT)
1.3 ratchov 383: return -1;
1.4 ratchov 384: if ((umap & (1 << i)) == 0)
1.3 ratchov 385: break;
386: }
1.4 ratchov 387:
388: DPRINTF("ctl_newslot: using %s%u as control name\n", name, unit);
389:
390: /*
391: * find a free controller slot with the same name/unit
392: */
393: for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
1.5 ratchov 394: if (slot->cb == NULL &&
1.4 ratchov 395: strcmp(slot->name, name) == 0 &&
396: slot->unit == unit) {
1.7 ratchov 397: DPRINTFN(1, "ctl_newslot: reusing %u\n", i);
1.5 ratchov 398: slot->cb = cb;
399: slot->arg = arg;
1.7 ratchov 400: slot->cb(slot->arg, slot->vol);
401: ctl_slotvol(p, i, slot->vol);
1.4 ratchov 402: return i;
403: }
404: }
405:
406: /*
1.6 ratchov 407: * couldn't find a matching slot, pick oldest free slot
1.4 ratchov 408: */
1.6 ratchov 409: bestser = 0;
410: bestidx = CTL_NSLOT;
411: for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
412: if (slot->cb != NULL)
413: continue;
414: ser = p->u.ctl.serial - slot->serial;
415: if (ser > bestser) {
416: bestser = ser;
417: bestidx = i;
418: }
1.3 ratchov 419: }
1.6 ratchov 420: if (bestidx == CTL_NSLOT)
421: return -1;
422: slot = p->u.ctl.slot + bestidx;
1.4 ratchov 423: strlcpy(slot->name, name, CTL_NAMEMAX);
1.6 ratchov 424: slot->serial = p->u.ctl.serial++;
1.4 ratchov 425: slot->unit = unit;
1.7 ratchov 426: slot->vol = MIDI_MAXCTL;
427: DPRINTFN(1, "ctl_newslot: %u overwritten)\n", bestidx);
1.5 ratchov 428: slot->cb = cb;
429: slot->arg = arg;
1.7 ratchov 430: slot->cb(slot->arg, slot->vol);
431: ctl_slotvol(p, bestidx, slot->vol);
1.6 ratchov 432: return bestidx;
1.3 ratchov 433: }
434:
1.7 ratchov 435: /*
436: * release the given slot
437: */
1.3 ratchov 438: void
439: ctl_slotdel(struct aproc *p, int index)
440: {
1.5 ratchov 441: p->u.ctl.slot[index].cb = NULL;
1.3 ratchov 442: }
443:
1.7 ratchov 444: /*
445: * notifty the mixer that volume changed, called by whom allocad the slot using
446: * ctl_slotnew(). Note: it doesn't make sens to call this from within the
447: * call-back.
448: */
1.3 ratchov 449: void
450: ctl_slotvol(struct aproc *p, int slot, unsigned vol)
451: {
452: unsigned char msg[3];
453:
454: DPRINTFN(1, "ctl_slotvol: [%u] -> %u\n", slot, vol);
1.7 ratchov 455: p->u.ctl.slot[slot].vol = vol;
1.3 ratchov 456: msg[0] = MIDI_CTL | slot;
457: msg[1] = MIDI_CTLVOL;
458: msg[2] = vol;
459: ctl_sendmsg(p, NULL, msg, 3);
460: }
461:
1.7 ratchov 462: /*
463: * handle a MIDI event received from ibuf
464: */
1.3 ratchov 465: void
466: ctl_ev(struct aproc *p, struct abuf *ibuf)
467: {
468: unsigned i;
469: unsigned chan;
1.5 ratchov 470: struct ctl_slot *slot;
1.3 ratchov 471:
472: #ifdef DEBUG
473: if (debug_level > 0) {
474: fprintf(stderr, "ctl_ev:");
1.8 ! ratchov 475: for (i = 0; i < ibuf->mindex; i++)
1.3 ratchov 476: fprintf(stderr, " %02x", ibuf->mdata[i]);
477: fprintf(stderr, "\n");
478: }
479: #endif
480: if ((ibuf->mdata[0] & MIDI_CMDMASK) == MIDI_CTL &&
481: ibuf->mdata[1] == MIDI_CTLVOL) {
482: chan = ibuf->mdata[0] & MIDI_CHANMASK;
483: if (chan >= CTL_NSLOT)
484: return;
1.5 ratchov 485: slot = p->u.ctl.slot + chan;
486: if (slot->cb == NULL)
1.3 ratchov 487: return;
1.7 ratchov 488: slot->vol = ibuf->mdata[2];
489: slot->cb(slot->arg, slot->vol);
1.3 ratchov 490: ctl_sendmsg(p, ibuf, ibuf->mdata, ibuf->mlen);
491: }
492: }
493:
494: int
495: ctl_in(struct aproc *p, struct abuf *ibuf)
496: {
497: unsigned char *idata;
498: unsigned c, i, icount;
499:
500: if (!ABUF_ROK(ibuf))
501: return 0;
502: idata = abuf_rgetblk(ibuf, &icount, 0);
503: for (i = 0; i < icount; i++) {
504: c = *idata++;
1.8 ! ratchov 505: if (c >= 0xf8) {
! 506: /* clock events not used yet */
! 507: } else if (c >= 0xf0) {
! 508: if (ibuf->mstatus == 0xf0 && c == 0xf7 &&
! 509: ibuf->mindex < MDATA_NMAX) {
! 510: ibuf->mdata[ibuf->mindex++] = c;
! 511: ctl_ev(p, ibuf);
! 512: continue;
! 513: }
! 514: ibuf->mdata[0] = c;
! 515: ibuf->mlen = common_len[c & 7];
! 516: ibuf->mstatus = c;
! 517: ibuf->mindex = 1;
1.3 ratchov 518: } else if (c >= 0x80) {
519: ibuf->mdata[0] = c;
520: ibuf->mlen = voice_len[(c >> 4) & 7];
521: ibuf->mstatus = c;
522: ibuf->mindex = 1;
523: } else if (ibuf->mstatus) {
1.8 ! ratchov 524: if (ibuf->mindex == MDATA_NMAX)
! 525: continue;
1.3 ratchov 526: if (ibuf->mindex == 0)
527: ibuf->mdata[ibuf->mindex++] = ibuf->mstatus;
528: ibuf->mdata[ibuf->mindex++] = c;
529: if (ibuf->mindex == ibuf->mlen) {
530: ctl_ev(p, ibuf);
531: ibuf->mindex = 0;
532: }
533: }
534: }
535: abuf_rdiscard(ibuf, icount);
536: return 1;
537: }
538:
539: int
540: ctl_out(struct aproc *p, struct abuf *obuf)
541: {
542: return 0;
543: }
544:
545: void
546: ctl_eof(struct aproc *p, struct abuf *ibuf)
547: {
548: DPRINTF("ctl_eof: %s: eof\n", p->name);
549: }
550:
551: void
552: ctl_hup(struct aproc *p, struct abuf *obuf)
553: {
554: DPRINTF("ctl_hup: %s: detached\n", p->name);
555: }
556:
557: void
558: ctl_newin(struct aproc *p, struct abuf *ibuf)
559: {
560: ibuf->mused = 0;
561: ibuf->mlen = 0;
562: ibuf->mindex = 0;
563: ibuf->mstatus = 0;
564: }
565:
566: void
567: ctl_done(struct aproc *p)
568: {
569: unsigned i;
570: struct ctl_slot *s;
571:
572: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1.7 ratchov 573: /*
574: * XXX: shouldn't we abord() here ?
575: */
1.5 ratchov 576: if (s->cb != NULL)
1.6 ratchov 577: DPRINTF("ctl_done: %s%u in use\n", s->name, s->unit);
1.3 ratchov 578: }
579: }
580:
581: struct aproc_ops ctl_ops = {
582: "ctl",
583: ctl_in,
584: ctl_out,
585: ctl_eof,
586: ctl_hup,
587: ctl_newin,
588: NULL, /* newout */
589: NULL, /* ipos */
590: NULL, /* opos */
591: ctl_done
592: };
593:
594: struct aproc *
595: ctl_new(char *name)
596: {
597: struct aproc *p;
1.5 ratchov 598: struct ctl_slot *s;
1.3 ratchov 599: unsigned i;
600:
601: p = aproc_new(&ctl_ops, name);
1.6 ratchov 602: p->u.ctl.serial = 0;
1.5 ratchov 603: for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1.3 ratchov 604: p->u.ctl.slot[i].unit = i;
1.5 ratchov 605: p->u.ctl.slot[i].cb = NULL;
1.7 ratchov 606: p->u.ctl.slot[i].vol = MIDI_MAXCTL;
1.6 ratchov 607: p->u.ctl.slot[i].serial = p->u.ctl.serial++;
1.3 ratchov 608: strlcpy(p->u.ctl.slot[i].name, "unknown", CTL_NAMEMAX);
609: }
1.1 ratchov 610: return p;
611: }
612: