From c4c683906482102cf04d1cc55a0556e24e370466 Mon Sep 17 00:00:00 2001 From: Alexandre Ratchov Date: Mon, 9 May 2011 19:40:34 +0200 Subject: [PATCH] add a sysex.h header with all sysex messages and expose new dumpreq, dumpend and mixinfo --- aucat/Makefile.in | 2 +- aucat/midi.c | 233 +++++++++++++++++++++++++++++++--------------- aucat/sysex.h | 113 ++++++++++++++++++++++ 3 files changed, 273 insertions(+), 75 deletions(-) create mode 100644 aucat/sysex.h diff --git a/aucat/Makefile.in b/aucat/Makefile.in index 798e6c9..3bfb6b0 100644 --- a/aucat/Makefile.in +++ b/aucat/Makefile.in @@ -73,7 +73,7 @@ headers.o: headers.c aparams.h conf.h wav.h pipe.h file.h \ listen.o: listen.c conf.h listen.h aparams.h file.h sock.h \ ../libsndio/amsg.h pipe.h ../bsd-compat/bsd-compat.h midi.o: midi.c abuf.h aproc.h aparams.h file.h conf.h dev.h \ - midi.h dbg.h ../bsd-compat/bsd-compat.h + midi.h sysex.h dbg.h ../bsd-compat/bsd-compat.h miofile.o: miofile.c conf.h file.h miofile.h dbg.h opt.o: opt.c dev.h aparams.h conf.h opt.h dbg.h pipe.o: pipe.c conf.h pipe.h file.h dbg.h diff --git a/aucat/midi.c b/aucat/midi.c index 0c6f4d2..80cf54a 100644 --- a/aucat/midi.c +++ b/aucat/midi.c @@ -32,6 +32,7 @@ #include "conf.h" #include "dev.h" #include "midi.h" +#include "sysex.h" #ifdef DEBUG #include "dbg.h" #endif @@ -385,6 +386,47 @@ ctl_slotdbg(struct aproc *p, int slot) } #endif +/* + * send a message to the given output + */ +void +ctl_copymsg(struct abuf *obuf, unsigned char *msg, unsigned len) +{ + unsigned ocount, itodo; + unsigned char *odata, *idata; + + itodo = len; + idata = msg; + while (itodo > 0) { + if (!ABUF_WOK(obuf)) { +#ifdef DEBUG + if (debug_level >= 3) { + abuf_dbg(obuf); + dbg_puts(": overrun, discarding "); + dbg_putu(obuf->used); + dbg_puts(" bytes\n"); + } +#endif + abuf_rdiscard(obuf, obuf->used); + } + odata = abuf_wgetblk(obuf, &ocount, 0); + if (ocount > itodo) + ocount = itodo; +#ifdef DEBUG + if (debug_level >= 4) { + abuf_dbg(obuf); + dbg_puts(": stored "); + dbg_putu(ocount); + dbg_puts(" bytes\n"); + } +#endif + memcpy(odata, idata, ocount); + abuf_wcommit(obuf, ocount); + itodo -= ocount; + idata += ocount; + } +} + /* * broadcast a message to all output buffers on the behalf of ibuf. * ie. don't sent back the message to the sender @@ -392,44 +434,13 @@ ctl_slotdbg(struct aproc *p, int slot) void ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len) { - unsigned ocount, itodo; - unsigned char *odata, *idata; struct abuf *i, *inext; for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) { inext = LIST_NEXT(i, oent); if (i->duplex && i->duplex == ibuf) continue; - itodo = len; - idata = msg; - while (itodo > 0) { - if (!ABUF_WOK(i)) { -#ifdef DEBUG - if (debug_level >= 3) { - abuf_dbg(i); - dbg_puts(": overrun, discarding "); - dbg_putu(i->used); - dbg_puts(" bytes\n"); - } -#endif - abuf_rdiscard(i, i->used); - } - odata = abuf_wgetblk(i, &ocount, 0); - if (ocount > itodo) - ocount = itodo; -#ifdef DEBUG - if (debug_level >= 4) { - abuf_dbg(i); - dbg_puts(": stored "); - dbg_putu(ocount); - dbg_puts(" bytes\n"); - } -#endif - memcpy(odata, idata, ocount); - abuf_wcommit(i, ocount); - itodo -= ocount; - idata += ocount; - } + ctl_copymsg(i, msg, len); (void)abuf_flush(i); } } @@ -527,6 +538,61 @@ ctl_full(struct aproc *p) ctl_sendmsg(p, NULL, buf, 10); } +void +ctl_msg_info(struct aproc *p, int slot, char *msg) +{ + struct ctl_slot *s; + struct sysex *x = (struct sysex *)msg; + + s = p->u.ctl.slot + slot; + memset(x, 0, sizeof(struct sysex)); + x->start = SYSEX_START; + x->type = SYSEX_TYPE_EDU; + x->id0 = SYSEX_AUCAT; + x->id1 = SYSEX_AUCAT_MIXINFO; + if (*s->name != '\0') { + snprintf(x->u.mixinfo.name, + SYSEX_NAMELEN, "%s%u", s->name, s->unit); + } + x->u.mixinfo.chan = slot; + x->u.mixinfo.end = SYSEX_END; +} + +void +ctl_msg_vol(struct aproc *p, int slot, char *msg) +{ + struct ctl_slot *s; + + s = p->u.ctl.slot + slot; + msg[0] = MIDI_CTL | slot; + msg[1] = MIDI_CTLVOL; + msg[2] = s->vol; +} + +void +ctl_dump(struct aproc *p, struct abuf *obuf) +{ + unsigned i; + unsigned char msg[sizeof(struct sysex)]; + struct ctl_slot *s; + + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + ctl_msg_info(p, i, msg); + ctl_copymsg(obuf, msg, SYSEX_SIZE(mixinfo)); + ctl_msg_vol(p, i, msg); + ctl_copymsg(obuf, msg, 3); + } + msg[0] = SYSEX_START; + msg[1] = SYSEX_TYPE_EDU; + msg[2] = 0; + msg[3] = SYSEX_AUCAT; + msg[4] = SYSEX_AUCAT_DUMPEND; + msg[5] = SYSEX_END; + ctl_copymsg(obuf, msg, 6); + dbg_puts("end dump\n"); + abuf_flush(obuf); +} + /* * find the best matching free slot index (ie midi channel). * return -1, if there are no free slots anymore @@ -724,6 +790,7 @@ ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg, int tr) { int idx; struct ctl_slot *s; + unsigned char msg[sizeof(struct sysex)]; if (!APROC_OK(p)) { #ifdef DEBUG @@ -743,7 +810,10 @@ ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg, int tr) s->arg = arg; s->tstate = tr ? CTL_STOP : CTL_OFF; s->ops->vol(s->arg, s->vol); - ctl_slotvol(p, idx, s->vol); + ctl_msg_info(p, idx, msg); + ctl_sendmsg(p, NULL, msg, SYSEX_SIZE(mixinfo)); + ctl_msg_vol(p, idx, msg); + ctl_sendmsg(p, NULL, msg, 3); return idx; } @@ -820,9 +890,7 @@ ctl_slotvol(struct aproc *p, int slot, unsigned vol) } #endif p->u.ctl.slot[slot].vol = vol; - msg[0] = MIDI_CTL | slot; - msg[1] = MIDI_CTLVOL; - msg[2] = vol; + ctl_msg_vol(p, slot, msg); ctl_sendmsg(p, NULL, msg, 3); } @@ -991,7 +1059,8 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) { unsigned chan; struct ctl_slot *slot; - unsigned fps; + struct sysex *x; + unsigned fps, len; #ifdef DEBUG unsigned i; @@ -1006,7 +1075,7 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) } #endif if ((ibuf->r.midi.msg[0] & MIDI_CMDMASK) == MIDI_CTL && - ibuf->r.midi.msg[1] == MIDI_CTLVOL) { + (ibuf->r.midi.msg[1] == MIDI_CTLVOL)) { chan = ibuf->r.midi.msg[0] & MIDI_CHANMASK; if (chan >= CTL_NSLOT) return; @@ -1017,13 +1086,20 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) slot->ops->vol(slot->arg, slot->vol); ctl_sendmsg(p, ibuf, ibuf->r.midi.msg, ibuf->r.midi.len); } - if (ibuf->r.midi.idx == 6 && - ibuf->r.midi.msg[0] == 0xf0 && - ibuf->r.midi.msg[1] == 0x7f && /* type is realtime */ - ibuf->r.midi.msg[3] == 0x06 && /* subtype is mmc */ - ibuf->r.midi.msg[5] == 0xf7) { /* subtype is mmc */ - switch (ibuf->r.midi.msg[4]) { - case 0x01: /* mmc stop */ + x = (struct sysex *)ibuf->r.midi.msg; + len = ibuf->r.midi.idx; + if (x->start != SYSEX_START) + return; + if (len < SYSEX_SIZE(empty)) + return; + switch (x->type) { + case SYSEX_TYPE_RT: + if (x->id0 != SYSEX_MMC) + return; + switch (x->id1) { + case SYSEX_MMC_STOP: + if (len != SYSEX_SIZE(stop)) + return; #ifdef DEBUG if (debug_level >= 3) { abuf_dbg(ibuf); @@ -1032,7 +1108,9 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) #endif ctl_stop(p); break; - case 0x02: /* mmc start */ + case SYSEX_MMC_START: + if (len != SYSEX_SIZE(start)) + return; #ifdef DEBUG if (debug_level >= 3) { abuf_dbg(ibuf); @@ -1041,36 +1119,43 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) #endif ctl_start(p); break; + case SYSEX_MMC_LOC: + if (len != SYSEX_SIZE(loc) || + x->u.loc.len != SYSEX_MMC_LOC_LEN || + x->u.loc.cmd != SYSEX_MMC_LOC_CMD) + return; + switch (x->u.loc.hr >> 5) { + case MTC_FPS_24: + fps = 24; + break; + case MTC_FPS_25: + fps = 25; + break; + case MTC_FPS_30: + fps = 30; + break; + default: + p->u.ctl.origin = 0; + return; + } + ctl_loc(p, + (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC + + x->u.loc.min * 60 * MTC_SEC + + x->u.loc.sec * MTC_SEC + + x->u.loc.fr * (MTC_SEC / fps) + + x->u.loc.cent * (MTC_SEC / 100 / fps)); + break; } - } - if (ibuf->r.midi.idx == 13 && - ibuf->r.midi.msg[0] == 0xf0 && - ibuf->r.midi.msg[1] == 0x7f && - ibuf->r.midi.msg[3] == 0x06 && - ibuf->r.midi.msg[4] == 0x44 && - ibuf->r.midi.msg[5] == 0x06 && - ibuf->r.midi.msg[6] == 0x01 && - ibuf->r.midi.msg[12] == 0xf7) { - switch (ibuf->r.midi.msg[7] >> 5) { - case MTC_FPS_24: - fps = 24; - break; - case MTC_FPS_25: - fps = 25; - break; - case MTC_FPS_30: - fps = 30; - break; - default: - p->u.ctl.origin = 0; + break; + case SYSEX_TYPE_EDU: + if (x->id0 != SYSEX_AUCAT || x->id1 != SYSEX_AUCAT_DUMPREQ) return; - } - ctl_loc(p, - (ibuf->r.midi.msg[7] & 0x1f) * 3600 * MTC_SEC + - ibuf->r.midi.msg[8] * 60 * MTC_SEC + - ibuf->r.midi.msg[9] * MTC_SEC + - ibuf->r.midi.msg[10] * (MTC_SEC / fps) + - ibuf->r.midi.msg[11] * (MTC_SEC / 100 / fps)); + if (len != SYSEX_SIZE(dumpreq)) + return; + dbg_puts("dump request\n"); + if (ibuf->duplex) + ctl_dump(p, ibuf->duplex); + break; } } diff --git a/aucat/sysex.h b/aucat/sysex.h new file mode 100644 index 0000000..edc6d1b --- /dev/null +++ b/aucat/sysex.h @@ -0,0 +1,113 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Alexandre Ratchov + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef AUCAT_SYSEX_H +#define AUCAT_SYSEX_H + +#include + +/* + * start and end markers + */ +#define SYSEX_START 0xf0 +#define SYSEX_END 0xf7 + +/* + * type/vendor namespace IDs we use + */ +#define SYSEX_TYPE_RT 0x7f /* real-time universal */ +#define SYSEX_TYPE_EDU 0x7d /* non-comercial */ + +/* + * realtime messages in the "universal real-time" namespace + */ +#define SYSEX_MTC 0x01 /* mtc messages */ +#define SYSEX_MTC_FULL 0x01 /* mtc full frame message */ +#define SYSEX_MMC 0x06 +#define SYSEX_MMC_STOP 0x01 +#define SYSEX_MMC_START 0x02 +#define SYSEX_MMC_LOC 0x44 +#define SYSEX_MMC_LOC_LEN 0x06 +#define SYSEX_MMC_LOC_CMD 0x01 + +/* + * aucat-specific messages, in the "edu" namespace + */ +#define SYSEX_AUCAT 0x23 /* aucat-specific */ +#define SYSEX_AUCAT_MIXINFO 0x01 /* mixer info */ +#define SYSEX_AUCAT_DUMPREQ 0x02 /* dump request */ +#define SYSEX_AUCAT_DUMPEND 0x03 /* end of dump */ + +/* + * minimum size of sysex message we accept + */ +#define SYSEX_SIZE(m) (5 + sizeof(struct sysex_ ## m)) + +/* + * all possible system exclusive messages we support. For aucat-specific + * messages we use the same header as real-time messages to simplify the + * message parser + */ +struct sysex { + uint8_t start; + uint8_t type; /* type or vendor id */ + uint8_t dev; /* device or product id */ + uint8_t id0; /* message id */ + uint8_t id1; /* sub-id */ + union sysex_all { + struct sysex_empty { + uint8_t end; + } empty; + struct sysex_start { + uint8_t end; + } start; + struct sysex_stop { + uint8_t end; + } stop; + struct sysex_loc { + uint8_t len; + uint8_t cmd; + uint8_t hr; + uint8_t min; + uint8_t sec; + uint8_t fr; + uint8_t cent; + uint8_t end; + } loc; + struct sysex_full { + uint8_t hr; + uint8_t min; + uint8_t sec; + uint8_t fr; + uint8_t end; + } full; + struct sysex_mixinfo { + uint8_t chan; /* channel */ + uint8_t vol; /* current volume */ +#define SYSEX_NAMELEN 10 /* \0 included */ + uint8_t name[SYSEX_NAMELEN]; /* stream name */ + uint8_t end; + } mixinfo; + struct sysex_dumpreq { + uint8_t end; + } dumpreq; + struct sysex_dumpend { + uint8_t end; + } dumpend; + } u; +}; + +#endif /* !defined(AUCAT_SYSEX_H) */