version 1.38, 2011/11/15 20:41:54 |
version 1.39, 2011/11/20 22:54:51 |
|
|
return p; |
return p; |
} |
} |
|
|
#ifdef DEBUG |
|
void |
|
ctl_slotdbg(struct aproc *p, int slot) |
|
{ |
|
struct ctl_slot *s; |
|
|
|
if (slot < 0) { |
|
dbg_puts("none"); |
|
} else { |
|
s = p->u.ctl.slot + slot; |
|
dbg_puts(s->name); |
|
dbg_putu(s->unit); |
|
dbg_puts("("); |
|
dbg_putu(s->vol); |
|
dbg_puts(")/"); |
|
switch (s->tstate) { |
|
case CTL_OFF: |
|
dbg_puts("off"); |
|
break; |
|
case CTL_RUN: |
|
dbg_puts("run"); |
|
break; |
|
case CTL_START: |
|
dbg_puts("sta"); |
|
break; |
|
case CTL_STOP: |
|
dbg_puts("stp"); |
|
break; |
|
default: |
|
dbg_puts("unk"); |
|
break; |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
/* |
/* |
* send a message to the given output |
* send a message to the given output |
*/ |
*/ |
|
|
* send a quarter frame MTC message |
* send a quarter frame MTC message |
*/ |
*/ |
void |
void |
ctl_qfr(struct aproc *p) |
ctl_qfr(struct aproc *p, unsigned rate, int delta) |
{ |
{ |
unsigned char buf[2]; |
unsigned char buf[2]; |
unsigned data; |
unsigned data; |
|
int qfrlen; |
|
|
switch (p->u.ctl.qfr) { |
p->u.ctl.delta += delta * MTC_SEC; |
case 0: |
|
data = p->u.ctl.fr & 0xf; |
/* |
break; |
* don't send ticks during the count-down |
case 1: |
* XXX: test not useful, given while() condition |
data = p->u.ctl.fr >> 4; |
*/ |
break; |
if (p->u.ctl.delta < 0) |
case 2: |
return; |
data = p->u.ctl.sec & 0xf; |
|
break; |
qfrlen = rate * (MTC_SEC / (4 * p->u.ctl.fps)); |
case 3: |
while (p->u.ctl.delta >= qfrlen) { |
data = p->u.ctl.sec >> 4; |
switch (p->u.ctl.qfr) { |
break; |
case 0: |
case 4: |
data = p->u.ctl.fr & 0xf; |
data = p->u.ctl.min & 0xf; |
|
break; |
|
case 5: |
|
data = p->u.ctl.min >> 4; |
|
break; |
|
case 6: |
|
data = p->u.ctl.hr & 0xf; |
|
break; |
|
case 7: |
|
data = (p->u.ctl.hr >> 4) | (p->u.ctl.fps_id << 1); |
|
/* |
|
* tick messages are sent 2 frames ahead |
|
*/ |
|
p->u.ctl.fr += 2; |
|
if (p->u.ctl.fr < p->u.ctl.fps) |
|
break; |
break; |
p->u.ctl.fr -= p->u.ctl.fps; |
case 1: |
p->u.ctl.sec++; |
data = p->u.ctl.fr >> 4; |
if (p->u.ctl.sec < 60) |
|
break; |
break; |
p->u.ctl.sec = 0; |
case 2: |
p->u.ctl.min++; |
data = p->u.ctl.sec & 0xf; |
if (p->u.ctl.min < 60) |
|
break; |
break; |
p->u.ctl.min = 0; |
case 3: |
p->u.ctl.hr++; |
data = p->u.ctl.sec >> 4; |
if (p->u.ctl.hr < 24) |
|
break; |
break; |
p->u.ctl.hr = 0; |
case 4: |
break; |
data = p->u.ctl.min & 0xf; |
default: |
break; |
/* NOTREACHED */ |
case 5: |
data = 0; |
data = p->u.ctl.min >> 4; |
|
break; |
|
case 6: |
|
data = p->u.ctl.hr & 0xf; |
|
break; |
|
case 7: |
|
data = (p->u.ctl.hr >> 4) | (p->u.ctl.fps_id << 1); |
|
/* |
|
* tick messages are sent 2 frames ahead |
|
*/ |
|
p->u.ctl.fr += 2; |
|
if (p->u.ctl.fr < p->u.ctl.fps) |
|
break; |
|
p->u.ctl.fr -= p->u.ctl.fps; |
|
p->u.ctl.sec++; |
|
if (p->u.ctl.sec < 60) |
|
break; |
|
p->u.ctl.sec = 0; |
|
p->u.ctl.min++; |
|
if (p->u.ctl.min < 60) |
|
break; |
|
p->u.ctl.min = 0; |
|
p->u.ctl.hr++; |
|
if (p->u.ctl.hr < 24) |
|
break; |
|
p->u.ctl.hr = 0; |
|
break; |
|
default: |
|
/* NOTREACHED */ |
|
data = 0; |
|
} |
|
buf[0] = 0xf1; |
|
buf[1] = (p->u.ctl.qfr << 4) | data; |
|
p->u.ctl.qfr++; |
|
p->u.ctl.qfr &= 7; |
|
ctl_sendmsg(p, NULL, buf, 2); |
|
p->u.ctl.delta -= qfrlen; |
} |
} |
buf[0] = 0xf1; |
|
buf[1] = (p->u.ctl.qfr << 4) | data; |
|
p->u.ctl.qfr++; |
|
p->u.ctl.qfr &= 7; |
|
ctl_sendmsg(p, NULL, buf, 2); |
|
} |
} |
|
|
/* |
/* |
* send a full frame MTC message |
* send a full frame MTC message |
*/ |
*/ |
void |
void |
ctl_full(struct aproc *p) |
ctl_full(struct aproc *p, unsigned origin, unsigned rate, unsigned round, unsigned pos) |
{ |
{ |
unsigned char buf[10]; |
unsigned char buf[10]; |
unsigned origin = p->u.ctl.origin; |
unsigned fps; |
unsigned fps = p->u.ctl.fps; |
|
|
|
|
p->u.ctl.delta = MTC_SEC * pos; |
|
if (rate % (30 * 4 * round) == 0) { |
|
p->u.ctl.fps_id = MTC_FPS_30; |
|
p->u.ctl.fps = 30; |
|
} else if (rate % (25 * 4 * round) == 0) { |
|
p->u.ctl.fps_id = MTC_FPS_25; |
|
p->u.ctl.fps = 25; |
|
} else { |
|
p->u.ctl.fps_id = MTC_FPS_24; |
|
p->u.ctl.fps = 24; |
|
} |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
aproc_dbg(p); |
|
dbg_puts(": mtc full frame at "); |
|
dbg_puti(p->u.ctl.delta); |
|
dbg_puts(", "); |
|
dbg_puti(p->u.ctl.fps); |
|
dbg_puts(" fps\n"); |
|
} |
|
#endif |
|
fps = p->u.ctl.fps; |
p->u.ctl.hr = (origin / (3600 * MTC_SEC)) % 24; |
p->u.ctl.hr = (origin / (3600 * MTC_SEC)) % 24; |
p->u.ctl.min = (origin / (60 * MTC_SEC)) % 60; |
p->u.ctl.min = (origin / (60 * MTC_SEC)) % 60; |
p->u.ctl.sec = (origin / MTC_SEC) % 60; |
p->u.ctl.sec = (origin / MTC_SEC) % 60; |
|
|
struct ctl_slot *s; |
struct ctl_slot *s; |
struct sysex *x = (struct sysex *)msg; |
struct sysex *x = (struct sysex *)msg; |
|
|
s = p->u.ctl.slot + slot; |
s = p->u.ctl.dev->slot + slot; |
memset(x, 0, sizeof(struct sysex)); |
memset(x, 0, sizeof(struct sysex)); |
x->start = SYSEX_START; |
x->start = SYSEX_START; |
x->type = SYSEX_TYPE_EDU; |
x->type = SYSEX_TYPE_EDU; |
|
|
{ |
{ |
struct ctl_slot *s; |
struct ctl_slot *s; |
|
|
s = p->u.ctl.slot + slot; |
s = p->u.ctl.dev->slot + slot; |
msg[0] = MIDI_CTL | slot; |
msg[0] = MIDI_CTL | slot; |
msg[1] = MIDI_CTLVOL; |
msg[1] = MIDI_CTLVOL; |
msg[2] = s->vol; |
msg[2] = s->vol; |
|
|
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.slot; i < CTL_NSLOT; i++, s++) { |
for (i = 0, s = p->u.ctl.dev->slot; i < CTL_NSLOT; i++, s++) { |
ctl_msg_info(p, i, msg); |
ctl_msg_info(p, i, msg); |
ctl_copymsg(obuf, msg, SYSEX_SIZE(mixinfo)); |
ctl_copymsg(obuf, msg, SYSEX_SIZE(mixinfo)); |
ctl_msg_vol(p, i, msg); |
ctl_msg_vol(p, i, msg); |
|
|
} |
} |
|
|
/* |
/* |
* find the best matching free slot index (ie midi channel). |
|
* return -1, if there are no free slots anymore |
|
*/ |
|
int |
|
ctl_getidx(struct aproc *p, char *who) |
|
{ |
|
char *s; |
|
struct ctl_slot *slot; |
|
char name[CTL_NAMEMAX]; |
|
unsigned i, unit, umap = 0; |
|
unsigned ser, bestser, bestidx; |
|
|
|
/* |
|
* create a ``valid'' control name (lowcase, remove [^a-z], trucate) |
|
*/ |
|
for (i = 0, s = who; ; s++) { |
|
if (i == CTL_NAMEMAX - 1 || *s == '\0') { |
|
name[i] = '\0'; |
|
break; |
|
} else if (*s >= 'A' && *s <= 'Z') { |
|
name[i++] = *s + 'a' - 'A'; |
|
} else if (*s >= 'a' && *s <= 'z') |
|
name[i++] = *s; |
|
} |
|
if (i == 0) |
|
strlcpy(name, "noname", CTL_NAMEMAX); |
|
|
|
/* |
|
* find the instance number of the control name |
|
*/ |
|
for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) { |
|
if (slot->ops == NULL) |
|
continue; |
|
if (strcmp(slot->name, name) == 0) |
|
umap |= (1 << i); |
|
} |
|
for (unit = 0; ; unit++) { |
|
if (unit == CTL_NSLOT) { |
|
#ifdef DEBUG |
|
if (debug_level >= 1) { |
|
dbg_puts(name); |
|
dbg_puts(": too many instances\n"); |
|
} |
|
#endif |
|
return -1; |
|
} |
|
if ((umap & (1 << unit)) == 0) |
|
break; |
|
} |
|
|
|
/* |
|
* find a free controller slot with the same name/unit |
|
*/ |
|
for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) { |
|
if (slot->ops == NULL && |
|
strcmp(slot->name, name) == 0 && |
|
slot->unit == unit) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
dbg_puts(name); |
|
dbg_putu(unit); |
|
dbg_puts(": found slot "); |
|
dbg_putu(i); |
|
dbg_puts("\n"); |
|
} |
|
#endif |
|
return i; |
|
} |
|
} |
|
|
|
/* |
|
* couldn't find a matching slot, pick oldest free slot |
|
* and set its name/unit |
|
*/ |
|
bestser = 0; |
|
bestidx = CTL_NSLOT; |
|
for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) { |
|
if (slot->ops != NULL) |
|
continue; |
|
ser = p->u.ctl.serial - slot->serial; |
|
if (ser > bestser) { |
|
bestser = ser; |
|
bestidx = i; |
|
} |
|
} |
|
if (bestidx == CTL_NSLOT) { |
|
#ifdef DEBUG |
|
if (debug_level >= 1) { |
|
dbg_puts(name); |
|
dbg_putu(unit); |
|
dbg_puts(": out of mixer slots\n"); |
|
} |
|
#endif |
|
return -1; |
|
} |
|
slot = p->u.ctl.slot + bestidx; |
|
if (slot->name[0] != '\0') |
|
slot->vol = MIDI_MAXCTL; |
|
strlcpy(slot->name, name, CTL_NAMEMAX); |
|
slot->serial = p->u.ctl.serial++; |
|
slot->unit = unit; |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
dbg_puts(name); |
|
dbg_putu(unit); |
|
dbg_puts(": overwritten slot "); |
|
dbg_putu(bestidx); |
|
dbg_puts("\n"); |
|
} |
|
#endif |
|
return bestidx; |
|
} |
|
|
|
/* |
|
* check that all clients controlled by MMC are ready to start, |
|
* if so, start them all but the caller |
|
*/ |
|
int |
|
ctl_trystart(struct aproc *p, int caller) |
|
{ |
|
unsigned i; |
|
struct ctl_slot *s; |
|
|
|
if (p->u.ctl.tstate != CTL_START) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
ctl_slotdbg(p, caller); |
|
dbg_puts(": server not started, delayd\n"); |
|
} |
|
#endif |
|
return 0; |
|
} |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (!s->ops || i == caller) |
|
continue; |
|
if (s->tstate != CTL_OFF && s->tstate != CTL_START) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
ctl_slotdbg(p, i); |
|
dbg_puts(": not ready, server delayed\n"); |
|
} |
|
#endif |
|
return 0; |
|
} |
|
} |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (!s->ops || i == caller) |
|
continue; |
|
if (s->tstate == CTL_START) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
ctl_slotdbg(p, i); |
|
dbg_puts(": started\n"); |
|
} |
|
#endif |
|
s->tstate = CTL_RUN; |
|
s->ops->start(s->arg); |
|
} |
|
} |
|
if (caller >= 0) |
|
p->u.ctl.slot[caller].tstate = CTL_RUN; |
|
p->u.ctl.tstate = CTL_RUN; |
|
p->u.ctl.delta = MTC_SEC * dev_getpos(p->u.ctl.dev); |
|
if (p->u.ctl.dev->rate % (30 * 4 * p->u.ctl.dev->round) == 0) { |
|
p->u.ctl.fps_id = MTC_FPS_30; |
|
p->u.ctl.fps = 30; |
|
} else if (p->u.ctl.dev->rate % (25 * 4 * p->u.ctl.dev->round) == 0) { |
|
p->u.ctl.fps_id = MTC_FPS_25; |
|
p->u.ctl.fps = 25; |
|
} else { |
|
p->u.ctl.fps_id = MTC_FPS_24; |
|
p->u.ctl.fps = 24; |
|
} |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
ctl_slotdbg(p, caller); |
|
dbg_puts(": started server at "); |
|
dbg_puti(p->u.ctl.delta); |
|
dbg_puts(", "); |
|
dbg_puti(p->u.ctl.fps); |
|
dbg_puts(" mtc fps\n"); |
|
} |
|
#endif |
|
dev_wakeup(p->u.ctl.dev); |
|
ctl_full(p); |
|
return 1; |
|
} |
|
|
|
/* |
|
* allocate a new slot and register the given call-backs |
|
*/ |
|
int |
|
ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg, int mmc) |
|
{ |
|
int idx; |
|
struct ctl_slot *s; |
|
unsigned char msg[sizeof(struct sysex)]; |
|
|
|
if (!APROC_OK(p)) { |
|
#ifdef DEBUG |
|
if (debug_level >= 2) { |
|
dbg_puts(who); |
|
dbg_puts(": MIDI control not available\n"); |
|
} |
|
#endif |
|
return -1; |
|
} |
|
idx = ctl_getidx(p, who); |
|
if (idx < 0) |
|
return -1; |
|
|
|
s = p->u.ctl.slot + idx; |
|
s->ops = ops; |
|
s->arg = arg; |
|
s->tstate = mmc ? CTL_STOP : CTL_OFF; |
|
s->ops->vol(s->arg, 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; |
|
} |
|
|
|
/* |
|
* release the given slot |
|
*/ |
|
void |
|
ctl_slotdel(struct aproc *p, int index) |
|
{ |
|
unsigned i; |
|
struct ctl_slot *s; |
|
|
|
if (!APROC_OK(p)) |
|
return; |
|
p->u.ctl.slot[index].ops = NULL; |
|
if (!(p->flags & APROC_QUIT)) |
|
return; |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (s->ops) |
|
return; |
|
} |
|
if (LIST_EMPTY(&p->ins)) |
|
aproc_del(p); |
|
} |
|
|
|
/* |
|
* called at every clock tick by the mixer, delta is positive, unless |
|
* there's an overrun/underrun |
|
*/ |
|
void |
|
ctl_ontick(struct aproc *p, int delta) |
|
{ |
|
int qfrlen; |
|
|
|
/* |
|
* don't send ticks before the start signal |
|
*/ |
|
if (p->u.ctl.tstate != CTL_RUN) |
|
return; |
|
|
|
p->u.ctl.delta += delta * MTC_SEC; |
|
|
|
/* |
|
* don't send ticks during the count-down |
|
*/ |
|
if (p->u.ctl.delta < 0) |
|
return; |
|
|
|
qfrlen = p->u.ctl.dev->rate * (MTC_SEC / (4 * p->u.ctl.fps)); |
|
while (p->u.ctl.delta >= qfrlen) { |
|
ctl_qfr(p); |
|
p->u.ctl.delta -= qfrlen; |
|
} |
|
} |
|
|
|
/* |
|
* notifty the mixer that volume changed, called by whom allocad the slot using |
* notifty the mixer that volume changed, called by whom allocad the slot using |
* ctl_slotnew(). Note: it doesn't make sens to call this from within the |
* ctl_slotnew(). Note: it doesn't make sens to call this from within the |
* call-back. |
* call-back. |
*/ |
*/ |
void |
void |
ctl_slotvol(struct aproc *p, int slot, unsigned vol) |
ctl_vol(struct aproc *p, int slot, unsigned vol) |
{ |
{ |
unsigned char msg[3]; |
unsigned char msg[3]; |
|
|
if (!APROC_OK(p)) |
|
return; |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
ctl_slotdbg(p, slot); |
|
dbg_puts(": changing volume to "); |
|
dbg_putu(vol); |
|
dbg_puts("\n"); |
|
} |
|
#endif |
|
p->u.ctl.slot[slot].vol = vol; |
|
ctl_msg_vol(p, slot, msg); |
ctl_msg_vol(p, slot, msg); |
ctl_sendmsg(p, NULL, msg, 3); |
ctl_sendmsg(p, NULL, msg, 3); |
} |
} |
|
|
/* |
|
* notify the MMC layer that the stream is attempting |
|
* to start. If other streams are not ready, 0 is returned meaning |
|
* that the stream should wait. If other streams are ready, they |
|
* are started, and the caller should start immediately. |
|
*/ |
|
int |
|
ctl_slotstart(struct aproc *p, int slot) |
|
{ |
|
struct ctl_slot *s = p->u.ctl.slot + slot; |
|
|
|
if (!APROC_OK(p)) |
|
return 1; |
|
if (s->tstate == CTL_OFF || p->u.ctl.tstate == CTL_OFF) |
|
return 1; |
|
|
|
/* |
|
* if the server already started (the client missed the |
|
* start rendez-vous) or the server is stopped, then |
|
* tag the client as ``wanting to start'' |
|
*/ |
|
s->tstate = CTL_START; |
|
return ctl_trystart(p, slot); |
|
} |
|
|
|
/* |
|
* notify the MMC layer that the stream no longer is trying to |
|
* start (or that it just stopped), meaning that its ``start'' call-back |
|
* shouldn't be called anymore |
|
*/ |
|
void |
void |
ctl_slotstop(struct aproc *p, int slot) |
ctl_slot(struct aproc *p, int slot) |
{ |
{ |
struct ctl_slot *s = p->u.ctl.slot + slot; |
unsigned char msg[sizeof(struct sysex)]; |
|
|
if (!APROC_OK(p)) |
ctl_msg_info(p, slot, msg); |
return; |
ctl_sendmsg(p, NULL, msg, SYSEX_SIZE(mixinfo)); |
/* |
|
* tag the stream as not trying to start, |
|
* unless MMC is turned off |
|
*/ |
|
if (s->tstate != CTL_OFF) |
|
s->tstate = CTL_STOP; |
|
} |
} |
|
|
/* |
/* |
* start all slots simultaneously |
|
*/ |
|
void |
|
ctl_start(struct aproc *p) |
|
{ |
|
if (!APROC_OK(p)) |
|
return; |
|
if (p->u.ctl.tstate == CTL_STOP) { |
|
p->u.ctl.tstate = CTL_START; |
|
(void)ctl_trystart(p, -1); |
|
#ifdef DEBUG |
|
} else { |
|
if (debug_level >= 3) { |
|
aproc_dbg(p); |
|
dbg_puts(": ignoring mmc start\n"); |
|
} |
|
#endif |
|
} |
|
} |
|
|
|
/* |
|
* stop all slots simultaneously |
|
*/ |
|
void |
|
ctl_stop(struct aproc *p) |
|
{ |
|
unsigned i; |
|
struct ctl_slot *s; |
|
|
|
if (!APROC_OK(p)) |
|
return; |
|
switch (p->u.ctl.tstate) { |
|
case CTL_START: |
|
p->u.ctl.tstate = CTL_STOP; |
|
return; |
|
case CTL_RUN: |
|
p->u.ctl.tstate = CTL_STOP; |
|
break; |
|
default: |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
aproc_dbg(p); |
|
dbg_puts(": ignored mmc stop\n"); |
|
} |
|
#endif |
|
return; |
|
} |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (!s->ops) |
|
continue; |
|
if (s->tstate == CTL_RUN) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) { |
|
ctl_slotdbg(p, i); |
|
dbg_puts(": requested to stop\n"); |
|
} |
|
#endif |
|
s->ops->stop(s->arg); |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* relocate all slots simultaneously |
|
*/ |
|
void |
|
ctl_loc(struct aproc *p, unsigned origin) |
|
{ |
|
unsigned i, tstate; |
|
struct ctl_slot *s; |
|
|
|
if (!APROC_OK(p)) |
|
return; |
|
#ifdef DEBUG |
|
if (debug_level >= 2) { |
|
dbg_puts("server relocated to "); |
|
dbg_putu(origin); |
|
dbg_puts("\n"); |
|
} |
|
#endif |
|
tstate = p->u.ctl.tstate; |
|
if (tstate == CTL_RUN) |
|
ctl_stop(p); |
|
p->u.ctl.origin = origin; |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (!s->ops) |
|
continue; |
|
s->ops->loc(s->arg, p->u.ctl.origin); |
|
} |
|
if (tstate == CTL_RUN) |
|
ctl_start(p); |
|
} |
|
|
|
/* |
|
* check if there are controlled streams |
|
*/ |
|
int |
|
ctl_idle(struct aproc *p) |
|
{ |
|
unsigned i; |
|
struct ctl_slot *s; |
|
|
|
if (!APROC_OK(p)) |
|
return 1; |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (s->ops) |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
/* |
|
* handle a MIDI event received from ibuf |
* handle a MIDI event received from ibuf |
*/ |
*/ |
void |
void |
|
|
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.slot + chan; |
slot = p->u.ctl.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; |
|
|
dbg_puts(": mmc stop\n"); |
dbg_puts(": mmc stop\n"); |
} |
} |
#endif |
#endif |
ctl_stop(p); |
dev_mmcstop(p->u.ctl.dev); |
break; |
break; |
case SYSEX_MMC_START: |
case SYSEX_MMC_START: |
if (len != SYSEX_SIZE(start)) |
if (len != SYSEX_SIZE(start)) |
|
|
dbg_puts(": mmc start\n"); |
dbg_puts(": mmc start\n"); |
} |
} |
#endif |
#endif |
ctl_start(p); |
dev_mmcstart(p->u.ctl.dev); |
break; |
break; |
case SYSEX_MMC_LOC: |
case SYSEX_MMC_LOC: |
if (len != SYSEX_SIZE(loc) || |
if (len != SYSEX_SIZE(loc) || |
|
|
fps = 30; |
fps = 30; |
break; |
break; |
default: |
default: |
p->u.ctl.origin = 0; |
/* XXX: should dev_mmcstop() here */ |
return; |
return; |
} |
} |
ctl_loc(p, |
dev_loc(p->u.ctl.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 + |
|
|
void |
void |
ctl_eof(struct aproc *p, struct abuf *ibuf) |
ctl_eof(struct aproc *p, struct abuf *ibuf) |
{ |
{ |
unsigned i; |
if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins)) |
struct ctl_slot *s; |
|
|
|
if (!(p->flags & APROC_QUIT)) |
|
return; |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (s->ops != NULL) |
|
s->ops->quit(s->arg); |
|
} |
|
if (LIST_EMPTY(&p->ins)) |
|
aproc_del(p); |
aproc_del(p); |
} |
} |
|
|
void |
void |
ctl_hup(struct aproc *p, struct abuf *obuf) |
ctl_hup(struct aproc *p, struct abuf *obuf) |
{ |
{ |
unsigned i; |
if ((p->flags & APROC_QUIT) && LIST_EMPTY(&p->ins)) |
struct ctl_slot *s; |
|
|
|
if (!(p->flags & APROC_QUIT)) |
|
return; |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (s->ops) |
|
return; |
|
} |
|
if (LIST_EMPTY(&p->ins)) |
|
aproc_del(p); |
aproc_del(p); |
} |
} |
|
|
|
|
ibuf->r.midi.st = 0; |
ibuf->r.midi.st = 0; |
} |
} |
|
|
void |
|
ctl_done(struct aproc *p) |
|
{ |
|
unsigned i; |
|
struct ctl_slot *s; |
|
|
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
if (s->ops != NULL) |
|
s->ops->quit(s->arg); |
|
} |
|
} |
|
|
|
struct aproc_ops ctl_ops = { |
struct aproc_ops ctl_ops = { |
"ctl", |
"ctl", |
ctl_in, |
ctl_in, |
|
|
NULL, /* newout */ |
NULL, /* newout */ |
NULL, /* ipos */ |
NULL, /* ipos */ |
NULL, /* opos */ |
NULL, /* opos */ |
ctl_done |
NULL, |
}; |
}; |
|
|
struct aproc * |
struct aproc * |
ctl_new(char *name, struct dev *dev) |
ctl_new(char *name, struct dev *dev) |
{ |
{ |
struct aproc *p; |
struct aproc *p; |
struct ctl_slot *s; |
|
unsigned i; |
|
|
|
p = aproc_new(&ctl_ops, name); |
p = aproc_new(&ctl_ops, name); |
p->u.ctl.dev = dev; |
p->u.ctl.dev = dev; |
p->u.ctl.serial = 0; |
|
p->u.ctl.tstate = CTL_STOP; |
|
for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { |
|
p->u.ctl.slot[i].unit = i; |
|
p->u.ctl.slot[i].ops = NULL; |
|
p->u.ctl.slot[i].vol = MIDI_MAXCTL; |
|
p->u.ctl.slot[i].tstate = CTL_OFF; |
|
p->u.ctl.slot[i].serial = p->u.ctl.serial++; |
|
p->u.ctl.slot[i].name[0] = '\0'; |
|
} |
|
return p; |
return p; |
} |
} |