reuse midi-control code to implement midi thru boxes and remove

the old midithru implementation; less code, less bugs. As a side
effect, midi output doesn't implement running status "compression"
any more
This commit is contained in:
Alexandre Ratchov 2011-12-02 11:38:53 +01:00
parent a3955999b1
commit 37f7cba0f8
6 changed files with 284 additions and 493 deletions

View File

@ -169,11 +169,9 @@ struct aproc {
int bnext; /* to reach the next byte */ int bnext; /* to reach the next byte */
int snext; /* to reach the next sample */ int snext; /* to reach the next sample */
} conv; } conv;
struct {
struct timo timo; /* timout for throtteling */
} thru;
struct { struct {
struct dev *dev; /* controlled device */ struct dev *dev; /* controlled device */
struct timo timo; /* timout for throtteling */
unsigned fps; /* MTC frames per second */ unsigned fps; /* MTC frames per second */
#define MTC_FPS_24 0 #define MTC_FPS_24 0
#define MTC_FPS_25 1 #define MTC_FPS_25 1
@ -185,7 +183,7 @@ struct aproc {
unsigned fr; /* MTC frames */ unsigned fr; /* MTC frames */
unsigned qfr; /* MTC quarter frames */ unsigned qfr; /* MTC quarter frames */
int delta; /* rel. to the last MTC tick */ int delta; /* rel. to the last MTC tick */
} ctl; } midi;
} u; } u;
}; };

View File

@ -532,7 +532,7 @@ main(int argc, char **argv)
mkdev("loopback", MODE_LOOP, bufsz, round, 1, autovol); mkdev("loopback", MODE_LOOP, bufsz, round, 1, autovol);
break; break;
case 'M': case 'M':
mkdev("midithru", MODE_THRU, 0, 0, 1, 0); mkdev("midithru", MODE_THRU, 0, 0, hold, 0);
break; break;
case 'l': case 'l':
background = 1; background = 1;
@ -609,7 +609,7 @@ main(int argc, char **argv)
dnext = d->next; dnext = d->next;
if (!dev_run(d)) if (!dev_run(d))
goto fatal; goto fatal;
if (!dev_idle(d)) if (d->refcnt > 0)
active = 1; active = 1;
} }
if (dev_list == NULL) if (dev_list == NULL)

View File

@ -141,6 +141,7 @@ dev_new(char *path, unsigned mode,
d->hold = hold; d->hold = hold;
d->autovol = autovol; d->autovol = autovol;
d->autostart = 0; d->autostart = 0;
d->refcnt = 0;
d->pstate = DEV_CLOSED; d->pstate = DEV_CLOSED;
d->serial = 0; d->serial = 0;
for (i = 0; i < CTL_NSLOT; i++) { for (i = 0; i < CTL_NSLOT; i++) {
@ -395,8 +396,7 @@ dev_open(struct dev *d)
* if there's no device * if there's no device
*/ */
if (d->mode & MODE_MIDIMASK) { if (d->mode & MODE_MIDIMASK) {
d->midi = (d->mode & MODE_THRU) ? d->midi = midi_new("midi", (d->mode & MODE_THRU) ? NULL : d);
thru_new("thru") : ctl_new("ctl", d);
d->midi->refs++; d->midi->refs++;
} }
@ -1450,8 +1450,9 @@ dev_slotnew(struct dev *d, char *who, struct ctl_ops *ops, void *arg, int mmc)
s->ops->vol(s->arg, s->vol); s->ops->vol(s->arg, s->vol);
if (APROC_OK(d->midi)) { if (APROC_OK(d->midi)) {
ctl_slot(d->midi, slot); midi_send_slot(d->midi, slot);
ctl_vol(d->midi, slot, s->vol); midi_send_vol(d->midi, slot, s->vol);
midi_flush(d->midi);
} else { } else {
#ifdef DEBUG #ifdef DEBUG
if (debug_level >= 2) { if (debug_level >= 2) {
@ -1495,38 +1496,10 @@ dev_slotvol(struct dev *d, int slot, unsigned vol)
} }
#endif #endif
d->slot[slot].vol = vol; d->slot[slot].vol = vol;
if (APROC_OK(d->midi)) if (APROC_OK(d->midi)) {
ctl_vol(d->midi, slot, vol); midi_send_vol(d->midi, slot, vol);
} midi_flush(d->midi);
/*
* check if there are controlled streams
*/
int
dev_idle(struct dev *d)
{
unsigned i;
struct ctl_slot *s;
/*
* XXX: this conditions breaks -aoff for thru boxes
*/
if (d->mode & MODE_THRU)
return 0;
if (d->pstate != DEV_CLOSED)
return 0;
/*
* XXX: if the device is closed, we're sure there are no
* slots in use, so the following test is useless
*/
for (i = 0, s = d->slot; i < CTL_NSLOT; i++, s++) {
if (s->ops)
return 0;
} }
return 1;
} }
/* /*
@ -1578,8 +1551,11 @@ dev_try(struct dev *d, int slot)
if (slot >= 0) if (slot >= 0)
d->slot[slot].tstate = CTL_RUN; d->slot[slot].tstate = CTL_RUN;
d->tstate = CTL_RUN; d->tstate = CTL_RUN;
if (APROC_OK(d->midi)) if (APROC_OK(d->midi)) {
ctl_full(d->midi, d->origin, d->rate, d->round, dev_getpos(d)); midi_send_full(d->midi,
d->origin, d->rate, d->round, dev_getpos(d));
midi_flush(d->midi);
}
dev_wakeup(d); dev_wakeup(d);
return 1; return 1;
} }
@ -1726,6 +1702,8 @@ dev_onmove(void *arg, int delta)
*/ */
if (d->tstate != CTL_RUN) if (d->tstate != CTL_RUN)
return; return;
if (APROC_OK(d->midi)) if (APROC_OK(d->midi)) {
ctl_qfr(d->midi, d->rate, delta); midi_send_qfr(d->midi, d->rate, delta);
midi_flush(d->midi);
}
} }

View File

@ -32,7 +32,6 @@ struct dev {
struct aparams reqipar, reqopar; /* parameters */ struct aparams reqipar, reqopar; /* parameters */
unsigned reqbufsz; /* buffer size */ unsigned reqbufsz; /* buffer size */
unsigned reqround; /* block size */ unsigned reqround; /* block size */
unsigned reqrate; /* sample rate */
unsigned hold; /* hold the device open ? */ unsigned hold; /* hold the device open ? */
unsigned autovol; /* auto adjust playvol ? */ unsigned autovol; /* auto adjust playvol ? */
unsigned autostart; /* don't wait for MMC start */ unsigned autostart; /* don't wait for MMC start */
@ -120,6 +119,5 @@ void dev_slotstop(struct dev *, int);
void dev_mmcstart(struct dev *); void dev_mmcstart(struct dev *);
void dev_mmcstop(struct dev *); void dev_mmcstop(struct dev *);
void dev_loc(struct dev *, unsigned); void dev_loc(struct dev *, unsigned);
int dev_idle(struct dev *);
#endif /* !define(DEV_H) */ #endif /* !define(DEV_H) */

View File

@ -19,9 +19,6 @@
* *
* use shadow variables (to save NRPNs, LSB of controller) * use shadow variables (to save NRPNs, LSB of controller)
* in the midi merger * in the midi merger
*
* make output and input identical when only one
* input is used (fix running status)
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -76,25 +73,71 @@ unsigned voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 }; unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
/* /*
* send the message stored in of ibuf->r.midi.msg to obuf * call-back invoked periodically to implement throttling; at each invocation
* gain more ``tickets'' for processing. If one of the buffer was blocked by
* the throttelling mechanism, then run it
*/ */
void void
thru_flush(struct abuf *ibuf, struct abuf *obuf) midi_cb(void *addr)
{ {
unsigned ocount, itodo; struct aproc *p = (struct aproc *)addr;
unsigned char *odata, *idata; struct abuf *i, *inext;
unsigned tickets;
itodo = ibuf->r.midi.used; timo_add(&p->u.midi.timo, MIDITHRU_TIMO);
idata = ibuf->r.midi.msg;
#ifdef DEBUG for (i = LIST_FIRST(&p->ins); i != NULL; i = inext) {
if (debug_level >= 4) { inext = LIST_NEXT(i, ient);
abuf_dbg(obuf); tickets = i->tickets;
dbg_puts(": flushing "); i->tickets = MIDITHRU_XFER;
dbg_putu(itodo); if (tickets == 0)
dbg_puts(" byte message\n"); abuf_run(i);
} }
#endif }
while (itodo > 0) {
void
midi_msg_info(struct aproc *p, int slot, char *msg)
{
struct ctl_slot *s;
struct sysex *x = (struct sysex *)msg;
s = p->u.midi.dev->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
midi_msg_vol(struct aproc *p, int slot, char *msg)
{
struct ctl_slot *s;
s = p->u.midi.dev->slot + slot;
msg[0] = MIDI_CTL | slot;
msg[1] = MIDI_CTLVOL;
msg[2] = s->vol;
}
/*
* send a message to the given output
*/
void
midi_copy(struct abuf *ibuf, struct abuf *obuf, unsigned char *msg, unsigned len)
{
unsigned ocount;
unsigned char *odata;
if (msg[0] == SYSEX_START)
obuf->w.midi.owner = ibuf;
while (len > 0) {
if (!ABUF_WOK(obuf)) { if (!ABUF_WOK(obuf)) {
#ifdef DEBUG #ifdef DEBUG
if (debug_level >= 3) { if (debug_level >= 3) {
@ -110,277 +153,8 @@ thru_flush(struct abuf *ibuf, struct abuf *obuf)
return; return;
} }
odata = abuf_wgetblk(obuf, &ocount, 0); odata = abuf_wgetblk(obuf, &ocount, 0);
if (ocount > itodo) if (ocount > len)
ocount = itodo; ocount = len;
memcpy(odata, idata, ocount);
abuf_wcommit(obuf, ocount);
itodo -= ocount;
idata += ocount;
}
ibuf->r.midi.used = 0;
obuf->w.midi.owner = ibuf;
}
/*
* send the real-time message (one byte) to obuf, similar to thru_flush()
*/
void
thru_rt(struct abuf *ibuf, struct abuf *obuf, unsigned c)
{
unsigned ocount;
unsigned char *odata;
#ifdef DEBUG
if (debug_level >= 4) {
abuf_dbg(obuf);
dbg_puts(": ");
dbg_putx(c);
dbg_puts(": flushing realtime message\n");
}
#endif
if (c == MIDI_ACK)
return;
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);
if (obuf->w.midi.owner == ibuf)
obuf->w.midi.owner = NULL;
}
odata = abuf_wgetblk(obuf, &ocount, 0);
odata[0] = c;
abuf_wcommit(obuf, 1);
}
/*
* parse ibuf contents and store each message into obuf,
* use at most ``todo'' bytes (for throttling)
*/
void
thru_bcopy(struct abuf *ibuf, struct abuf *obuf, unsigned todo)
{
unsigned char *idata;
unsigned c, icount, ioffs;
idata = NULL;
icount = ioffs = 0;
for (;;) {
if (icount == 0) {
if (todo == 0)
break;
idata = abuf_rgetblk(ibuf, &icount, ioffs);
if (icount > todo)
icount = todo;
if (icount == 0)
break;
todo -= icount;
ioffs += icount;
}
c = *idata++;
icount--;
if (c < 0x80) {
if (ibuf->r.midi.idx == 0 && ibuf->r.midi.st) {
ibuf->r.midi.msg[ibuf->r.midi.used++] = ibuf->r.midi.st;
ibuf->r.midi.idx++;
}
ibuf->r.midi.msg[ibuf->r.midi.used++] = c;
ibuf->r.midi.idx++;
if (ibuf->r.midi.idx == ibuf->r.midi.len) {
thru_flush(ibuf, obuf);
if (ibuf->r.midi.st >= 0xf0)
ibuf->r.midi.st = 0;
ibuf->r.midi.idx = 0;
}
if (ibuf->r.midi.used == MIDI_MSGMAX) {
if (ibuf->r.midi.used == ibuf->r.midi.idx ||
obuf->w.midi.owner == ibuf)
thru_flush(ibuf, obuf);
else
ibuf->r.midi.used = 0;
}
} else if (c < 0xf8) {
if (ibuf->r.midi.used == ibuf->r.midi.idx ||
obuf->w.midi.owner == ibuf) {
thru_flush(ibuf, obuf);
} else
ibuf->r.midi.used = 0;
ibuf->r.midi.msg[0] = c;
ibuf->r.midi.used = 1;
ibuf->r.midi.len = (c >= 0xf0) ?
common_len[c & 7] :
voice_len[(c >> 4) & 7];
if (ibuf->r.midi.len == 1) {
thru_flush(ibuf, obuf);
ibuf->r.midi.idx = 0;
ibuf->r.midi.st = 0;
ibuf->r.midi.len = 0;
} else {
ibuf->r.midi.st = c;
ibuf->r.midi.idx = 1;
}
} else {
thru_rt(ibuf, obuf, c);
}
}
}
int
thru_in(struct aproc *p, struct abuf *ibuf)
{
struct abuf *i, *inext;
unsigned todo;
if (!ABUF_ROK(ibuf))
return 0;
if (ibuf->tickets == 0) {
#ifdef DEBUG
if (debug_level >= 4) {
abuf_dbg(ibuf);
dbg_puts(": out of tickets, blocking\n");
}
#endif
return 0;
}
todo = ibuf->used;
if (todo > ibuf->tickets)
todo = ibuf->tickets;
ibuf->tickets -= todo;
for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
inext = LIST_NEXT(i, oent);
if (ibuf->duplex == i)
continue;
thru_bcopy(ibuf, i, todo);
(void)abuf_flush(i);
}
abuf_rdiscard(ibuf, todo);
return 1;
}
int
thru_out(struct aproc *p, struct abuf *obuf)
{
return 0;
}
void
thru_eof(struct aproc *p, struct abuf *ibuf)
{
if (!(p->flags & APROC_QUIT))
return;
if (LIST_EMPTY(&p->ins))
aproc_del(p);
}
void
thru_hup(struct aproc *p, struct abuf *obuf)
{
if (!(p->flags & APROC_QUIT))
return;
if (LIST_EMPTY(&p->ins))
aproc_del(p);
}
void
thru_newin(struct aproc *p, struct abuf *ibuf)
{
ibuf->r.midi.used = 0;
ibuf->r.midi.len = 0;
ibuf->r.midi.idx = 0;
ibuf->r.midi.st = 0;
ibuf->tickets = MIDITHRU_XFER;
}
void
thru_newout(struct aproc *p, struct abuf *obuf)
{
obuf->w.midi.owner = NULL;
}
void
thru_done(struct aproc *p)
{
timo_del(&p->u.thru.timo);
}
struct aproc_ops thru_ops = {
"thru",
thru_in,
thru_out,
thru_eof,
thru_hup,
thru_newin,
thru_newout,
NULL, /* ipos */
NULL, /* opos */
thru_done
};
/*
* call-back invoked periodically to implement throttling at each invocation
* gain more ``tickets'' for processing. If one of the buffer was blocked by
* the throttelling mechanism, then run it
*/
void
thru_cb(void *addr)
{
struct aproc *p = (struct aproc *)addr;
struct abuf *i, *inext;
unsigned tickets;
timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
for (i = LIST_FIRST(&p->ins); i != NULL; i = inext) {
inext = LIST_NEXT(i, ient);
tickets = i->tickets;
i->tickets = MIDITHRU_XFER;
if (tickets == 0)
abuf_run(i);
}
}
struct aproc *
thru_new(char *name)
{
struct aproc *p;
p = aproc_new(&thru_ops, name);
timo_set(&p->u.thru.timo, thru_cb, p);
timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
return p;
}
/*
* 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 #ifdef DEBUG
if (debug_level >= 4) { if (debug_level >= 4) {
abuf_dbg(obuf); abuf_dbg(obuf);
@ -389,10 +163,27 @@ ctl_copymsg(struct abuf *obuf, unsigned char *msg, unsigned len)
dbg_puts(" bytes\n"); dbg_puts(" bytes\n");
} }
#endif #endif
memcpy(odata, idata, ocount); memcpy(odata, msg, ocount);
abuf_wcommit(obuf, ocount); abuf_wcommit(obuf, ocount);
itodo -= ocount; len -= ocount;
idata += ocount; msg += ocount;
}
}
/*
* flush all buffers. Since most of the MIDI traffic is broadcasted to
* all outputs, the flush is delayed to avoid flushing all outputs for
* each message.
*/
void
midi_flush(struct aproc *p)
{
struct abuf *i, *inext;
for (i = LIST_FIRST(&p->outs); i != NULL; i = inext) {
inext = LIST_NEXT(i, oent);
if (ABUF_ROK(i))
(void)abuf_flush(i);
} }
} }
@ -401,7 +192,7 @@ ctl_copymsg(struct abuf *obuf, unsigned char *msg, unsigned len)
* ie. don't sent back the message to the sender * ie. don't sent back the message to the sender
*/ */
void void
ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len) midi_send(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len)
{ {
struct abuf *i, *inext; struct abuf *i, *inext;
@ -409,8 +200,7 @@ ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len
inext = LIST_NEXT(i, oent); inext = LIST_NEXT(i, oent);
if (i->duplex && i->duplex == ibuf) if (i->duplex && i->duplex == ibuf)
continue; continue;
ctl_copymsg(i, msg, len); midi_copy(ibuf, i, msg, len);
(void)abuf_flush(i);
} }
} }
@ -418,77 +208,69 @@ ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len
* send a quarter frame MTC message * send a quarter frame MTC message
*/ */
void void
ctl_qfr(struct aproc *p, unsigned rate, int delta) midi_send_qfr(struct aproc *p, unsigned rate, int delta)
{ {
unsigned char buf[2]; unsigned char buf[2];
unsigned data; unsigned data;
int qfrlen; int qfrlen;
p->u.ctl.delta += delta * MTC_SEC; p->u.midi.delta += delta * MTC_SEC;
qfrlen = rate * (MTC_SEC / (4 * p->u.midi.fps));
/* while (p->u.midi.delta >= qfrlen) {
* don't send ticks during the count-down switch (p->u.midi.qfr) {
* XXX: test not useful, given while() condition
*/
if (p->u.ctl.delta < 0)
return;
qfrlen = rate * (MTC_SEC / (4 * p->u.ctl.fps));
while (p->u.ctl.delta >= qfrlen) {
switch (p->u.ctl.qfr) {
case 0: case 0:
data = p->u.ctl.fr & 0xf; data = p->u.midi.fr & 0xf;
break; break;
case 1: case 1:
data = p->u.ctl.fr >> 4; data = p->u.midi.fr >> 4;
break; break;
case 2: case 2:
data = p->u.ctl.sec & 0xf; data = p->u.midi.sec & 0xf;
break; break;
case 3: case 3:
data = p->u.ctl.sec >> 4; data = p->u.midi.sec >> 4;
break; break;
case 4: case 4:
data = p->u.ctl.min & 0xf; data = p->u.midi.min & 0xf;
break; break;
case 5: case 5:
data = p->u.ctl.min >> 4; data = p->u.midi.min >> 4;
break; break;
case 6: case 6:
data = p->u.ctl.hr & 0xf; data = p->u.midi.hr & 0xf;
break; break;
case 7: case 7:
data = (p->u.ctl.hr >> 4) | (p->u.ctl.fps_id << 1); data = (p->u.midi.hr >> 4) | (p->u.midi.fps_id << 1);
/* /*
* tick messages are sent 2 frames ahead * tick messages are sent 2 frames ahead
*/ */
p->u.ctl.fr += 2; p->u.midi.fr += 2;
if (p->u.ctl.fr < p->u.ctl.fps) if (p->u.midi.fr < p->u.midi.fps)
break; break;
p->u.ctl.fr -= p->u.ctl.fps; p->u.midi.fr -= p->u.midi.fps;
p->u.ctl.sec++; p->u.midi.sec++;
if (p->u.ctl.sec < 60) if (p->u.midi.sec < 60)
break; break;
p->u.ctl.sec = 0; p->u.midi.sec = 0;
p->u.ctl.min++; p->u.midi.min++;
if (p->u.ctl.min < 60) if (p->u.midi.min < 60)
break; break;
p->u.ctl.min = 0; p->u.midi.min = 0;
p->u.ctl.hr++; p->u.midi.hr++;
if (p->u.ctl.hr < 24) if (p->u.midi.hr < 24)
break; break;
p->u.ctl.hr = 0; p->u.midi.hr = 0;
break; break;
default: default:
/* NOTREACHED */ /* NOTREACHED */
data = 0; data = 0;
} }
buf[0] = 0xf1; buf[0] = 0xf1;
buf[1] = (p->u.ctl.qfr << 4) | data; buf[1] = (p->u.midi.qfr << 4) | data;
p->u.ctl.qfr++; p->u.midi.qfr++;
p->u.ctl.qfr &= 7; p->u.midi.qfr &= 7;
ctl_sendmsg(p, NULL, buf, 2); midi_send(p, NULL, buf, 2);
p->u.ctl.delta -= qfrlen; p->u.midi.delta -= qfrlen;
} }
} }
@ -496,95 +278,64 @@ ctl_qfr(struct aproc *p, unsigned rate, int delta)
* send a full frame MTC message * send a full frame MTC message
*/ */
void void
ctl_full(struct aproc *p, unsigned origin, unsigned rate, unsigned round, unsigned pos) midi_send_full(struct aproc *p, unsigned origin, unsigned rate, unsigned round, unsigned pos)
{ {
unsigned char buf[10]; unsigned char buf[10];
unsigned fps; unsigned fps;
p->u.ctl.delta = MTC_SEC * pos; p->u.midi.delta = MTC_SEC * pos;
if (rate % (30 * 4 * round) == 0) { if (rate % (30 * 4 * round) == 0) {
p->u.ctl.fps_id = MTC_FPS_30; p->u.midi.fps_id = MTC_FPS_30;
p->u.ctl.fps = 30; p->u.midi.fps = 30;
} else if (rate % (25 * 4 * round) == 0) { } else if (rate % (25 * 4 * round) == 0) {
p->u.ctl.fps_id = MTC_FPS_25; p->u.midi.fps_id = MTC_FPS_25;
p->u.ctl.fps = 25; p->u.midi.fps = 25;
} else { } else {
p->u.ctl.fps_id = MTC_FPS_24; p->u.midi.fps_id = MTC_FPS_24;
p->u.ctl.fps = 24; p->u.midi.fps = 24;
} }
#ifdef DEBUG #ifdef DEBUG
if (debug_level >= 3) { if (debug_level >= 3) {
aproc_dbg(p); aproc_dbg(p);
dbg_puts(": mtc full frame at "); dbg_puts(": mtc full frame at ");
dbg_puti(p->u.ctl.delta); dbg_puti(p->u.midi.delta);
dbg_puts(", "); dbg_puts(", ");
dbg_puti(p->u.ctl.fps); dbg_puti(p->u.midi.fps);
dbg_puts(" fps\n"); dbg_puts(" fps\n");
} }
#endif #endif
fps = p->u.ctl.fps; fps = p->u.midi.fps;
p->u.ctl.hr = (origin / (3600 * MTC_SEC)) % 24; p->u.midi.hr = (origin / (3600 * MTC_SEC)) % 24;
p->u.ctl.min = (origin / (60 * MTC_SEC)) % 60; p->u.midi.min = (origin / (60 * MTC_SEC)) % 60;
p->u.ctl.sec = (origin / MTC_SEC) % 60; p->u.midi.sec = (origin / MTC_SEC) % 60;
p->u.ctl.fr = (origin / (MTC_SEC / fps)) % fps; p->u.midi.fr = (origin / (MTC_SEC / fps)) % fps;
buf[0] = 0xf0; buf[0] = 0xf0;
buf[1] = 0x7f; buf[1] = 0x7f;
buf[2] = 0x7f; buf[2] = 0x7f;
buf[3] = 0x01; buf[3] = 0x01;
buf[4] = 0x01; buf[4] = 0x01;
buf[5] = p->u.ctl.hr | (p->u.ctl.fps_id << 5); buf[5] = p->u.midi.hr | (p->u.midi.fps_id << 5);
buf[6] = p->u.ctl.min; buf[6] = p->u.midi.min;
buf[7] = p->u.ctl.sec; buf[7] = p->u.midi.sec;
buf[8] = p->u.ctl.fr; buf[8] = p->u.midi.fr;
buf[9] = 0xf7; buf[9] = 0xf7;
p->u.ctl.qfr = 0; p->u.midi.qfr = 0;
ctl_sendmsg(p, NULL, buf, 10); midi_send(p, NULL, buf, 10);
} }
void void
ctl_msg_info(struct aproc *p, int slot, char *msg) midi_copy_dump(struct aproc *p, struct abuf *obuf)
{
struct ctl_slot *s;
struct sysex *x = (struct sysex *)msg;
s = p->u.ctl.dev->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.dev->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 i;
unsigned char msg[sizeof(struct sysex)]; unsigned char msg[sizeof(struct sysex)];
struct ctl_slot *s; struct ctl_slot *s;
for (i = 0, s = p->u.ctl.dev->slot; i < CTL_NSLOT; i++, s++) { for (i = 0, s = p->u.midi.dev->slot; i < CTL_NSLOT; i++, s++) {
ctl_msg_info(p, i, msg); midi_msg_info(p, i, msg);
ctl_copymsg(obuf, msg, SYSEX_SIZE(mixinfo)); midi_copy(NULL, obuf, msg, SYSEX_SIZE(mixinfo));
ctl_msg_vol(p, i, msg); midi_msg_vol(p, i, msg);
ctl_copymsg(obuf, msg, 3); midi_copy(NULL, obuf, msg, 3);
} }
msg[0] = SYSEX_START; msg[0] = SYSEX_START;
msg[1] = SYSEX_TYPE_EDU; msg[1] = SYSEX_TYPE_EDU;
@ -592,8 +343,7 @@ ctl_dump(struct aproc *p, struct abuf *obuf)
msg[3] = SYSEX_AUCAT; msg[3] = SYSEX_AUCAT;
msg[4] = SYSEX_AUCAT_DUMPEND; msg[4] = SYSEX_AUCAT_DUMPEND;
msg[5] = SYSEX_END; msg[5] = SYSEX_END;
ctl_copymsg(obuf, msg, 6); midi_copy(NULL, obuf, msg, 6);
abuf_flush(obuf);
} }
/* /*
@ -602,39 +352,37 @@ ctl_dump(struct aproc *p, struct abuf *obuf)
* call-back. * call-back.
*/ */
void void
ctl_vol(struct aproc *p, int slot, unsigned vol) midi_send_vol(struct aproc *p, int slot, unsigned vol)
{ {
unsigned char msg[3]; unsigned char msg[3];
ctl_msg_vol(p, slot, msg); midi_msg_vol(p, slot, msg);
ctl_sendmsg(p, NULL, msg, 3); midi_send(p, NULL, msg, 3);
} }
void void
ctl_slot(struct aproc *p, int slot) midi_send_slot(struct aproc *p, int slot)
{ {
unsigned char msg[sizeof(struct sysex)]; unsigned char msg[sizeof(struct sysex)];
ctl_msg_info(p, slot, msg); midi_msg_info(p, slot, msg);
ctl_sendmsg(p, NULL, msg, SYSEX_SIZE(mixinfo)); midi_send(p, NULL, msg, SYSEX_SIZE(mixinfo));
} }
/* /*
* handle a MIDI event received from ibuf * handle a MIDI voice event received from ibuf
*/ */
void void
ctl_ev(struct aproc *p, struct abuf *ibuf) midi_onvoice(struct aproc *p, struct abuf *ibuf)
{ {
unsigned chan;
struct ctl_slot *slot; struct ctl_slot *slot;
struct sysex *x; unsigned chan;
unsigned fps, len;
#ifdef DEBUG #ifdef DEBUG
unsigned i; unsigned i;
if (debug_level >= 3) { if (debug_level >= 3) {
abuf_dbg(ibuf); abuf_dbg(ibuf);
dbg_puts(": got event:"); dbg_puts(": got voice event:");
for (i = 0; i < ibuf->r.midi.idx; i++) { for (i = 0; i < ibuf->r.midi.idx; i++) {
dbg_puts(" "); dbg_puts(" ");
dbg_putx(ibuf->r.midi.msg[i]); dbg_putx(ibuf->r.midi.msg[i]);
@ -647,13 +395,35 @@ ctl_ev(struct aproc *p, struct abuf *ibuf)
chan = ibuf->r.midi.msg[0] & MIDI_CHANMASK; chan = ibuf->r.midi.msg[0] & MIDI_CHANMASK;
if (chan >= CTL_NSLOT) if (chan >= CTL_NSLOT)
return; return;
slot = p->u.ctl.dev->slot + chan; slot = p->u.midi.dev->slot + chan;
slot->vol = ibuf->r.midi.msg[2]; slot->vol = ibuf->r.midi.msg[2];
if (slot->ops == NULL) if (slot->ops == NULL)
return; return;
slot->ops->vol(slot->arg, slot->vol); slot->ops->vol(slot->arg, slot->vol);
ctl_sendmsg(p, ibuf, ibuf->r.midi.msg, ibuf->r.midi.len);
} }
}
/*
* handle a MIDI sysex received from ibuf
*/
void
midi_onsysex(struct aproc *p, struct abuf *ibuf)
{
struct sysex *x;
unsigned fps, len;
#ifdef DEBUG
unsigned i;
if (debug_level >= 3) {
abuf_dbg(ibuf);
dbg_puts(": got sysex:");
for (i = 0; i < ibuf->r.midi.idx; i++) {
dbg_puts(" ");
dbg_putx(ibuf->r.midi.msg[i]);
}
dbg_puts("\n");
}
#endif
x = (struct sysex *)ibuf->r.midi.msg; x = (struct sysex *)ibuf->r.midi.msg;
len = ibuf->r.midi.idx; len = ibuf->r.midi.idx;
if (x->start != SYSEX_START) if (x->start != SYSEX_START)
@ -674,7 +444,7 @@ ctl_ev(struct aproc *p, struct abuf *ibuf)
dbg_puts(": mmc stop\n"); dbg_puts(": mmc stop\n");
} }
#endif #endif
dev_mmcstop(p->u.ctl.dev); dev_mmcstop(p->u.midi.dev);
break; break;
case SYSEX_MMC_START: case SYSEX_MMC_START:
if (len != SYSEX_SIZE(start)) if (len != SYSEX_SIZE(start))
@ -685,7 +455,7 @@ ctl_ev(struct aproc *p, struct abuf *ibuf)
dbg_puts(": mmc start\n"); dbg_puts(": mmc start\n");
} }
#endif #endif
dev_mmcstart(p->u.ctl.dev); dev_mmcstart(p->u.midi.dev);
break; break;
case SYSEX_MMC_LOC: case SYSEX_MMC_LOC:
if (len != SYSEX_SIZE(loc) || if (len != SYSEX_SIZE(loc) ||
@ -706,7 +476,7 @@ ctl_ev(struct aproc *p, struct abuf *ibuf)
/* XXX: should dev_mmcstop() here */ /* XXX: should dev_mmcstop() here */
return; return;
} }
dev_loc(p->u.ctl.dev, dev_loc(p->u.midi.dev,
(x->u.loc.hr & 0x1f) * 3600 * MTC_SEC + (x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
x->u.loc.min * 60 * MTC_SEC + x->u.loc.min * 60 * MTC_SEC +
x->u.loc.sec * MTC_SEC + x->u.loc.sec * MTC_SEC +
@ -721,31 +491,49 @@ ctl_ev(struct aproc *p, struct abuf *ibuf)
if (len != SYSEX_SIZE(dumpreq)) if (len != SYSEX_SIZE(dumpreq))
return; return;
if (ibuf->duplex) if (ibuf->duplex)
ctl_dump(p, ibuf->duplex); midi_copy_dump(p, ibuf->duplex);
break; break;
} }
} }
int int
ctl_in(struct aproc *p, struct abuf *ibuf) midi_in(struct aproc *p, struct abuf *ibuf)
{ {
unsigned char *idata; unsigned char c, *idata;
unsigned c, i, icount; unsigned i, icount;
if (!ABUF_ROK(ibuf)) if (!ABUF_ROK(ibuf))
return 0; return 0;
if (ibuf->tickets == 0) {
#ifdef DEBUG
if (debug_level >= 4) {
abuf_dbg(ibuf);
dbg_puts(": out of tickets, blocking\n");
}
#endif
return 0;
}
idata = abuf_rgetblk(ibuf, &icount, 0); idata = abuf_rgetblk(ibuf, &icount, 0);
if (icount > ibuf->tickets)
icount = ibuf->tickets;
ibuf->tickets -= icount;
for (i = 0; i < icount; i++) { for (i = 0; i < icount; i++) {
c = *idata++; c = *idata++;
if (c >= 0xf8) { if (c >= 0xf8) {
/* clock events not used yet */ if (!p->u.midi.dev && c != MIDI_ACK)
} else if (c >= 0xf0) { midi_send(p, ibuf, &c, 1);
if (ibuf->r.midi.st == 0xf0 && c == 0xf7 && } else if (c == SYSEX_END) {
ibuf->r.midi.idx < MIDI_MSGMAX) { if (ibuf->r.midi.st == SYSEX_START) {
ibuf->r.midi.msg[ibuf->r.midi.idx++] = c; ibuf->r.midi.msg[ibuf->r.midi.idx++] = c;
ctl_ev(p, ibuf); if (!p->u.midi.dev) {
continue; midi_send(p, ibuf,
ibuf->r.midi.msg, ibuf->r.midi.idx);
} else
midi_onsysex(p, ibuf);
} }
ibuf->r.midi.st = 0;
ibuf->r.midi.idx = 0;
} else if (c >= 0xf0) {
ibuf->r.midi.msg[0] = c; ibuf->r.midi.msg[0] = c;
ibuf->r.midi.len = common_len[c & 7]; ibuf->r.midi.len = common_len[c & 7];
ibuf->r.midi.st = c; ibuf->r.midi.st = c;
@ -756,69 +544,98 @@ ctl_in(struct aproc *p, struct abuf *ibuf)
ibuf->r.midi.st = c; ibuf->r.midi.st = c;
ibuf->r.midi.idx = 1; ibuf->r.midi.idx = 1;
} else if (ibuf->r.midi.st) { } else if (ibuf->r.midi.st) {
if (ibuf->r.midi.idx == MIDI_MSGMAX) if (ibuf->r.midi.idx == 0 &&
continue; ibuf->r.midi.st != SYSEX_START) {
if (ibuf->r.midi.idx == 0) ibuf->r.midi.msg[ibuf->r.midi.idx++] =
ibuf->r.midi.msg[ibuf->r.midi.idx++] = ibuf->r.midi.st; ibuf->r.midi.st;
}
ibuf->r.midi.msg[ibuf->r.midi.idx++] = c; ibuf->r.midi.msg[ibuf->r.midi.idx++] = c;
if (ibuf->r.midi.idx == ibuf->r.midi.len) { if (ibuf->r.midi.idx == ibuf->r.midi.len) {
ctl_ev(p, ibuf); if (!p->u.midi.dev) {
midi_send(p, ibuf,
ibuf->r.midi.msg, ibuf->r.midi.idx);
} else
midi_onvoice(p, ibuf);
if (ibuf->r.midi.st >= 0xf0)
ibuf->r.midi.st = 0;
ibuf->r.midi.idx = 0;
} else if (ibuf->r.midi.idx == MIDI_MSGMAX) {
if (!p->u.midi.dev) {
midi_send(p, ibuf,
ibuf->r.midi.msg, ibuf->r.midi.idx);
}
ibuf->r.midi.idx = 0; ibuf->r.midi.idx = 0;
} }
} }
} }
/*
* XXX: if the sysex is received byte by byte, partial messages
* won't be sent until the end byte is received. On the other
* hand we can't flush it here, since we would loose messages
* we parse
*/
abuf_rdiscard(ibuf, icount); abuf_rdiscard(ibuf, icount);
midi_flush(p);
return 1; return 1;
} }
int int
ctl_out(struct aproc *p, struct abuf *obuf) midi_out(struct aproc *p, struct abuf *obuf)
{ {
return 0; return 0;
} }
void void
ctl_eof(struct aproc *p, struct abuf *ibuf) midi_eof(struct aproc *p, struct abuf *ibuf)
{ {
if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins)) if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins))
aproc_del(p); aproc_del(p);
} }
void void
ctl_hup(struct aproc *p, struct abuf *obuf) midi_hup(struct aproc *p, struct abuf *obuf)
{ {
if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins)) if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins))
aproc_del(p); aproc_del(p);
} }
void void
ctl_newin(struct aproc *p, struct abuf *ibuf) midi_newin(struct aproc *p, struct abuf *ibuf)
{ {
ibuf->r.midi.used = 0; ibuf->r.midi.used = 0;
ibuf->r.midi.len = 0; ibuf->r.midi.len = 0;
ibuf->r.midi.idx = 0; ibuf->r.midi.idx = 0;
ibuf->r.midi.st = 0; ibuf->r.midi.st = 0;
ibuf->tickets = MIDITHRU_XFER;
} }
struct aproc_ops ctl_ops = { void
"ctl", midi_done(struct aproc *p)
ctl_in, {
ctl_out, timo_del(&p->u.midi.timo);
ctl_eof, }
ctl_hup,
ctl_newin, struct aproc_ops midi_ops = {
"midi",
midi_in,
midi_out,
midi_eof,
midi_hup,
midi_newin,
NULL, /* newout */ NULL, /* newout */
NULL, /* ipos */ NULL, /* ipos */
NULL, /* opos */ NULL, /* opos */
NULL, midi_done,
}; };
struct aproc * struct aproc *
ctl_new(char *name, struct dev *dev) midi_new(char *name, struct dev *dev)
{ {
struct aproc *p; struct aproc *p;
p = aproc_new(&ctl_ops, name); p = aproc_new(&midi_ops, name);
p->u.ctl.dev = dev; timo_set(&p->u.midi.timo, midi_cb, p);
timo_add(&p->u.midi.timo, MIDITHRU_TIMO);
p->u.midi.dev = dev;
return p; return p;
} }

View File

@ -19,13 +19,13 @@
struct dev; struct dev;
struct aproc *thru_new(char *); struct aproc *midi_new(char *, struct dev *);
struct aproc *ctl_new(char *, struct dev *);
void ctl_ontick(struct aproc *, int); void midi_ontick(struct aproc *, int);
void ctl_slot(struct aproc *, int); void midi_send_slot(struct aproc *, int);
void ctl_vol(struct aproc *, int, unsigned); void midi_send_vol(struct aproc *, int, unsigned);
void ctl_full(struct aproc *, unsigned, unsigned, unsigned, unsigned); void midi_send_full(struct aproc *, unsigned, unsigned, unsigned, unsigned);
void ctl_qfr(struct aproc *, unsigned, int); void midi_send_qfr(struct aproc *, unsigned, int);
void midi_flush(struct aproc *);
#endif /* !defined(MIDI_H) */ #endif /* !defined(MIDI_H) */