Move MTC/MMC to its own structure

This commit is contained in:
Alexandre Ratchov 2021-01-28 10:34:59 +01:00
parent ada702087d
commit 5451c3c55e
5 changed files with 173 additions and 148 deletions

View File

@ -35,13 +35,6 @@ void zomb_flush(void *);
void zomb_eof(void *);
void zomb_exit(void *);
void dev_midi_qfr(struct dev *, int);
void dev_midi_full(struct dev *);
void dev_midi_vol(struct dev *, struct slot *);
void dev_midi_master(struct dev *);
void dev_midi_slotdesc(struct dev *, struct slot *);
void dev_midi_dump(struct dev *);
void dev_mix_badd(struct dev *, struct slot *);
void dev_mix_adjvol(struct dev *);
void dev_sub_bcopy(struct dev *, struct slot *);
@ -66,7 +59,6 @@ void dev_del(struct dev *);
void dev_setalt(struct dev *, unsigned int);
unsigned int dev_roundof(struct dev *, unsigned int);
void dev_wakeup(struct dev *);
void dev_sync_attach(struct dev *);
void slot_ctlname(struct slot *, char *, size_t);
void slot_log(struct slot *);
@ -100,6 +92,13 @@ struct ctlslot ctlslot_array[DEV_NCTLSLOT];
struct slot slot_array[DEV_NSLOT];
unsigned int slot_serial; /* for slot allocation */
/*
* we support/need a single MTC clock source only
*/
struct mtc mtc_array[1] = {
{.dev = NULL, .tstate = MTC_STOP}
};
void
slot_array_init(void)
{
@ -228,69 +227,69 @@ dev_midi_send(struct dev *d, void *msg, int msglen)
* send a quarter frame MTC message
*/
void
dev_midi_qfr(struct dev *d, int delta)
mtc_midi_qfr(struct mtc *mtc, int delta)
{
unsigned char buf[2];
unsigned int data;
int qfrlen;
d->mtc.delta += delta * MTC_SEC;
qfrlen = d->rate * (MTC_SEC / (4 * d->mtc.fps));
while (d->mtc.delta >= qfrlen) {
switch (d->mtc.qfr) {
mtc->delta += delta * MTC_SEC;
qfrlen = mtc->dev->rate * (MTC_SEC / (4 * mtc->fps));
while (mtc->delta >= qfrlen) {
switch (mtc->qfr) {
case 0:
data = d->mtc.fr & 0xf;
data = mtc->fr & 0xf;
break;
case 1:
data = d->mtc.fr >> 4;
data = mtc->fr >> 4;
break;
case 2:
data = d->mtc.sec & 0xf;
data = mtc->sec & 0xf;
break;
case 3:
data = d->mtc.sec >> 4;
data = mtc->sec >> 4;
break;
case 4:
data = d->mtc.min & 0xf;
data = mtc->min & 0xf;
break;
case 5:
data = d->mtc.min >> 4;
data = mtc->min >> 4;
break;
case 6:
data = d->mtc.hr & 0xf;
data = mtc->hr & 0xf;
break;
case 7:
data = (d->mtc.hr >> 4) | (d->mtc.fps_id << 1);
data = (mtc->hr >> 4) | (mtc->fps_id << 1);
/*
* tick messages are sent 2 frames ahead
*/
d->mtc.fr += 2;
if (d->mtc.fr < d->mtc.fps)
mtc->fr += 2;
if (mtc->fr < mtc->fps)
break;
d->mtc.fr -= d->mtc.fps;
d->mtc.sec++;
if (d->mtc.sec < 60)
mtc->fr -= mtc->fps;
mtc->sec++;
if (mtc->sec < 60)
break;
d->mtc.sec = 0;
d->mtc.min++;
if (d->mtc.min < 60)
mtc->sec = 0;
mtc->min++;
if (mtc->min < 60)
break;
d->mtc.min = 0;
d->mtc.hr++;
if (d->mtc.hr < 24)
mtc->min = 0;
mtc->hr++;
if (mtc->hr < 24)
break;
d->mtc.hr = 0;
mtc->hr = 0;
break;
default:
/* NOTREACHED */
data = 0;
}
buf[0] = 0xf1;
buf[1] = (d->mtc.qfr << 4) | data;
d->mtc.qfr++;
d->mtc.qfr &= 7;
dev_midi_send(d, buf, 2);
d->mtc.delta -= qfrlen;
buf[1] = (mtc->qfr << 4) | data;
mtc->qfr++;
mtc->qfr &= 7;
dev_midi_send(mtc->dev, buf, 2);
mtc->delta -= qfrlen;
}
}
@ -298,50 +297,50 @@ dev_midi_qfr(struct dev *d, int delta)
* send a full frame MTC message
*/
void
dev_midi_full(struct dev *d)
mtc_midi_full(struct mtc *mtc)
{
struct sysex x;
unsigned int fps;
d->mtc.delta = MTC_SEC * dev_getpos(d);
if (d->rate % (30 * 4 * d->round) == 0) {
d->mtc.fps_id = MTC_FPS_30;
d->mtc.fps = 30;
} else if (d->rate % (25 * 4 * d->round) == 0) {
d->mtc.fps_id = MTC_FPS_25;
d->mtc.fps = 25;
mtc->delta = MTC_SEC * dev_getpos(mtc->dev);
if (mtc->dev->rate % (30 * 4 * mtc->dev->round) == 0) {
mtc->fps_id = MTC_FPS_30;
mtc->fps = 30;
} else if (mtc->dev->rate % (25 * 4 * mtc->dev->round) == 0) {
mtc->fps_id = MTC_FPS_25;
mtc->fps = 25;
} else {
d->mtc.fps_id = MTC_FPS_24;
d->mtc.fps = 24;
mtc->fps_id = MTC_FPS_24;
mtc->fps = 24;
}
#ifdef DEBUG
if (log_level >= 3) {
dev_log(d);
dev_log(mtc->dev);
log_puts(": mtc full frame at ");
log_puti(d->mtc.delta);
log_puti(mtc->delta);
log_puts(", ");
log_puti(d->mtc.fps);
log_puti(mtc->fps);
log_puts(" fps\n");
}
#endif
fps = d->mtc.fps;
d->mtc.hr = (d->mtc.origin / (MTC_SEC * 3600)) % 24;
d->mtc.min = (d->mtc.origin / (MTC_SEC * 60)) % 60;
d->mtc.sec = (d->mtc.origin / (MTC_SEC)) % 60;
d->mtc.fr = (d->mtc.origin / (MTC_SEC / fps)) % fps;
fps = mtc->fps;
mtc->hr = (mtc->origin / (MTC_SEC * 3600)) % 24;
mtc->min = (mtc->origin / (MTC_SEC * 60)) % 60;
mtc->sec = (mtc->origin / (MTC_SEC)) % 60;
mtc->fr = (mtc->origin / (MTC_SEC / fps)) % fps;
x.start = SYSEX_START;
x.type = SYSEX_TYPE_RT;
x.dev = SYSEX_DEV_ANY;
x.id0 = SYSEX_MTC;
x.id1 = SYSEX_MTC_FULL;
x.u.full.hr = d->mtc.hr | (d->mtc.fps_id << 5);
x.u.full.min = d->mtc.min;
x.u.full.sec = d->mtc.sec;
x.u.full.fr = d->mtc.fr;
x.u.full.hr = mtc->hr | (mtc->fps_id << 5);
x.u.full.min = mtc->min;
x.u.full.sec = mtc->sec;
x.u.full.fr = mtc->fr;
x.u.full.end = SYSEX_END;
d->mtc.qfr = 0;
dev_midi_send(d, (unsigned char *)&x, SYSEX_SIZE(full));
mtc->qfr = 0;
dev_midi_send(mtc->dev, (unsigned char *)&x, SYSEX_SIZE(full));
}
/*
@ -670,7 +669,8 @@ dev_cycle(struct dev *d)
* check if the device is actually used. If it isn't,
* then close it
*/
if (d->slot_list == NULL && d->tstate != MMC_RUN) {
if (d->slot_list == NULL && (mtc_array[0].dev != d ||
mtc_array[0].tstate != MTC_RUN)) {
if (log_level >= 2) {
dev_log(d);
log_puts(": device stopped\n");
@ -864,8 +864,9 @@ dev_onmove(struct dev *d, int delta)
if (s->delta >= 0)
s->ops->onmove(s->arg);
}
if (d->tstate == MMC_RUN)
dev_midi_qfr(d, delta);
if (mtc_array[0].dev == d && mtc_array[0].tstate == MTC_RUN)
mtc_midi_qfr(&mtc_array[0], delta);
}
void
@ -941,8 +942,6 @@ dev_new(char *path, struct aparams *par,
d->pstate = DEV_CFG;
d->slot_list = NULL;
d->master = MIDI_MAXCTL;
d->mtc.origin = 0;
d->tstate = MMC_STOP;
snprintf(d->name, CTL_NAMEMAX, "%u", d->num);
d->next = dev_list;
dev_list = d;
@ -1078,7 +1077,10 @@ dev_allocbufs(struct dev *d)
log_putu(d->bufsz / d->round);
log_puts(" blocks of ");
log_putu(d->round);
log_puts(" frames\n");
log_puts(" frames");
if (d == mtc_array[0].dev)
log_puts(", mtc");
log_puts("\n");
}
return 1;
}
@ -1221,6 +1223,7 @@ dev_close(struct dev *d)
int
dev_reopen(struct dev *d)
{
struct mtc *mtc;
struct slot *s;
long long pos;
unsigned int pstate;
@ -1265,12 +1268,13 @@ dev_reopen(struct dev *d)
/* reinitilize the format conversion chain */
slot_initconv(s);
}
if (d->tstate == MMC_RUN) {
d->mtc.delta -= delta * MTC_SEC;
mtc = &mtc_array[0];
if (mtc->dev == d && mtc->tstate == MTC_RUN) {
mtc->delta -= delta * MTC_SEC;
if (log_level >= 2) {
dev_log(d);
dev_log(mtc->dev);
log_puts(": adjusted mtc: delta ->");
log_puti(d->mtc.delta);
log_puti(mtc->delta);
log_puts("\n");
}
}
@ -1345,8 +1349,8 @@ dev_done(struct dev *d)
log_puts(": draining\n");
}
#endif
if (d->tstate != MMC_STOP)
dev_mmcstop(d);
if (mtc_array[0].dev == d && mtc_array[0].tstate != MTC_STOP)
mtc_stop(&mtc_array[0]);
if (d->hold)
dev_unref(d);
}
@ -1437,21 +1441,21 @@ dev_wakeup(struct dev *d)
* attach them all at the same position
*/
void
dev_sync_attach(struct dev *d)
mtc_trigger(struct mtc *mtc)
{
int i;
struct slot *s;
if (d->tstate != MMC_START) {
if (mtc->tstate != MTC_START) {
if (log_level >= 2) {
dev_log(d);
dev_log(mtc->dev);
log_puts(": not started by mmc yet, waiting...\n");
}
return;
}
for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
if (s->opt == NULL || s->opt->dev != d || !s->ops || !s->opt->mmc)
if (s->opt == NULL || s->opt->mtc != mtc)
continue;
if (s->pstate != SLOT_READY) {
#ifdef DEBUG
@ -1463,33 +1467,33 @@ dev_sync_attach(struct dev *d)
return;
}
}
if (!dev_ref(d))
if (!dev_ref(mtc->dev))
return;
for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
if (s->opt == NULL || s->opt->dev != d || !s->ops || !s->opt->mmc)
if (s->opt == NULL || s->opt->mtc != mtc)
continue;
slot_attach(s);
s->pstate = SLOT_RUN;
}
d->tstate = MMC_RUN;
dev_midi_full(d);
dev_wakeup(d);
mtc->tstate = MTC_RUN;
mtc_midi_full(mtc);
dev_wakeup(mtc->dev);
}
/*
* start all slots simultaneously
*/
void
dev_mmcstart(struct dev *d)
mtc_start(struct mtc *mtc)
{
if (d->tstate == MMC_STOP) {
d->tstate = MMC_START;
dev_sync_attach(d);
if (mtc->tstate == MTC_STOP) {
mtc->tstate = MTC_START;
mtc_trigger(mtc);
#ifdef DEBUG
} else {
if (log_level >= 3) {
dev_log(d);
dev_log(mtc->dev);
log_puts(": ignoring mmc start\n");
}
#endif
@ -1500,20 +1504,20 @@ dev_mmcstart(struct dev *d)
* stop all slots simultaneously
*/
void
dev_mmcstop(struct dev *d)
mtc_stop(struct mtc *mtc)
{
switch (d->tstate) {
case MMC_START:
d->tstate = MMC_STOP;
switch (mtc->tstate) {
case MTC_START:
mtc->tstate = MTC_STOP;
return;
case MMC_RUN:
d->tstate = MMC_STOP;
dev_unref(d);
case MTC_RUN:
mtc->tstate = MTC_STOP;
dev_unref(mtc->dev);
break;
default:
#ifdef DEBUG
if (log_level >= 3) {
dev_log(d);
dev_log(mtc->dev);
log_puts(": ignored mmc stop\n");
}
#endif
@ -1525,19 +1529,19 @@ dev_mmcstop(struct dev *d)
* relocate all slots simultaneously
*/
void
dev_mmcloc(struct dev *d, unsigned int origin)
mtc_loc(struct mtc *mtc, unsigned int origin)
{
if (log_level >= 2) {
dev_log(d);
dev_log(mtc->dev);
log_puts(": relocated to ");
log_putu(origin);
log_puts("\n");
}
if (d->tstate == MMC_RUN)
dev_mmcstop(d);
d->mtc.origin = origin;
if (d->tstate == MMC_RUN)
dev_mmcstart(d);
if (mtc->tstate == MTC_RUN)
mtc_stop(mtc);
mtc->origin = origin;
if (mtc->tstate == MTC_RUN)
mtc_start(mtc);
}
/*
@ -1820,7 +1824,7 @@ found:
s->mix.nch = s->opt->pmax - s->opt->pmin + 1;
if (s->mode & MODE_RECMASK)
s->sub.nch = s->opt->rmax - s->opt->rmin + 1;
s->xrun = s->opt->mmc ? XRUN_SYNC : XRUN_IGNORE;
s->xrun = s->opt->mtc != NULL ? XRUN_SYNC : XRUN_IGNORE;
s->appbufsz = s->opt->dev->bufsz;
s->round = s->opt->dev->round;
s->rate = s->opt->dev->rate;
@ -1949,11 +1953,11 @@ slot_ready(struct slot *s)
*/
if (s->opt->dev->pstate == DEV_CFG)
return;
if (!s->opt->mmc) {
if (s->opt->mtc == NULL) {
slot_attach(s);
s->pstate = SLOT_RUN;
} else
dev_sync_attach(s->opt->dev);
mtc_trigger(s->opt->mtc);
}
/*

View File

@ -176,6 +176,34 @@ struct ctlslot {
unsigned int mode;
};
/*
* MIDI time code (MTC)
*/
struct mtc {
/*
* MIDI time code (MTC) states
*/
#define MTC_STOP 1 /* stopped, can't start */
#define MTC_START 2 /* attempting to start */
#define MTC_RUN 3 /* started */
unsigned int tstate; /* one of MTC_* constants */
struct dev *dev;
unsigned int origin; /* MTC start time */
unsigned int fps; /* MTC frames per second */
#define MTC_FPS_24 0
#define MTC_FPS_25 1
#define MTC_FPS_30 3
unsigned int fps_id; /* one of above */
unsigned int hr; /* MTC hours */
unsigned int min; /* MTC minutes */
unsigned int sec; /* MTC seconds */
unsigned int fr; /* MTC frames */
unsigned int qfr; /* MTC quarter frames */
int delta; /* rel. to the last MTC tick */
int refs;
};
/*
* audio device with plenty of slots
*/
@ -242,33 +270,6 @@ struct dev {
unsigned int bufsz, round, rate;
unsigned int prime;
/*
* MIDI time code (MTC)
*/
struct {
unsigned int origin; /* MTC start time */
unsigned int fps; /* MTC frames per second */
#define MTC_FPS_24 0
#define MTC_FPS_25 1
#define MTC_FPS_30 3
unsigned int fps_id; /* one of above */
unsigned int hr; /* MTC hours */
unsigned int min; /* MTC minutes */
unsigned int sec; /* MTC seconds */
unsigned int fr; /* MTC frames */
unsigned int qfr; /* MTC quarter frames */
int delta; /* rel. to the last MTC tick */
int refs;
} mtc;
/*
* MIDI machine control (MMC)
*/
#define MMC_STOP 1 /* stopped, can't start */
#define MMC_START 2 /* attempting to start */
#define MMC_RUN 3 /* started */
unsigned int tstate; /* one of above */
unsigned int master; /* software vol. knob */
unsigned int master_enabled; /* 1 if h/w has no vo. knob */
};
@ -277,6 +278,7 @@ extern struct dev *dev_list;
extern struct ctl *ctl_list;
extern struct slot slot_array[DEV_NSLOT];
extern struct ctlslot ctlslot_array[DEV_NCTLSLOT];
extern struct mtc mtc_array[1];
void slot_array_init(void);
@ -312,9 +314,13 @@ void dev_midi_master(struct dev *);
void dev_midi_slotdesc(struct dev *, struct slot *);
void dev_midi_dump(struct dev *);
void dev_mmcstart(struct dev *);
void dev_mmcstop(struct dev *);
void dev_mmcloc(struct dev *, unsigned int);
void mtc_midi_qfr(struct mtc *, int);
void mtc_midi_full(struct mtc *);
void mtc_trigger(struct mtc *);
void mtc_start(struct mtc *);
void mtc_stop(struct mtc *);
void mtc_loc(struct mtc *, unsigned int);
void mtc_setdev(struct mtc *, struct dev *);
/*
* sio_open(3) like interface for clients

View File

@ -89,31 +89,31 @@ opt_midi_omsg(void *arg, unsigned char *msg, int len)
case SYSEX_MMC_STOP:
if (len != SYSEX_SIZE(stop))
return;
if (!o->mmc)
if (o->mtc == NULL)
return;
if (log_level >= 2) {
log_puts(o->name);
log_puts(": mmc stop\n");
}
dev_mmcstop(o->dev);
mtc_stop(o->mtc);
break;
case SYSEX_MMC_START:
if (len != SYSEX_SIZE(start))
return;
if (!o->mmc)
if (o->mtc == NULL)
return;
if (log_level >= 2) {
log_puts(o->name);
log_puts(": mmc start\n");
}
dev_mmcstart(o->dev);
mtc_start(o->mtc);
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;
if (!o->mmc)
if (o->mtc == NULL)
return;
switch (x->u.loc.hr >> 5) {
case MTC_FPS_24:
@ -126,10 +126,10 @@ opt_midi_omsg(void *arg, unsigned char *msg, int len)
fps = 30;
break;
default:
dev_mmcstop(o->dev);
mtc_stop(o->mtc);
return;
}
dev_mmcloc(o->dev,
mtc_loc(o->mtc,
(x->u.loc.hr & 0x1f) * 3600 * MTC_SEC +
x->u.loc.min * 60 * MTC_SEC +
x->u.loc.sec * MTC_SEC +
@ -206,6 +206,20 @@ opt_new(struct dev *d, char *name,
log_puts(": already defined\n");
return NULL;
}
if (mmc) {
if (mtc_array[0].dev != NULL && mtc_array[0].dev != d) {
log_puts(name);
log_puts(": MTC already setup for another device\n");
return NULL;
}
mtc_array[0].dev = d;
if (log_level >= 2) {
dev_log(d);
log_puts(": initial MTC source, controlled by MMC\n");
}
}
o = xmalloc(sizeof(struct opt));
o->num = num;
o->dev = d;
@ -229,7 +243,7 @@ opt_new(struct dev *d, char *name,
o->rmax = rmax;
}
o->maxweight = maxweight;
o->mmc = mmc;
o->mtc = mmc ? &mtc_array[0] : NULL;
o->dup = dup;
o->mode = mode;
memcpy(o->name, name, len + 1);
@ -261,8 +275,8 @@ opt_new(struct dev *d, char *name,
log_putu(o->rmax);
}
if (o->mode & (MODE_RECMASK | MODE_PLAY)) {
if (o->mmc)
log_puts(" mmc");
if (o->mtc)
log_puts(" mtc");
if (o->dup)
log_puts(" dup");
}

View File

@ -25,13 +25,14 @@ struct opt {
struct opt *next;
struct dev *dev;
struct midi *midi;
struct mtc *mtc; /* if set, MMC-controlled MTC source */
int num;
#define OPT_NAMEMAX 11
char name[OPT_NAMEMAX + 1];
int maxweight; /* max dynamic range for clients */
int pmin, pmax; /* play channels */
int rmin, rmax; /* recording channels */
int mmc; /* true if MMC control enabled */
int dup; /* true if join/expand enabled */
int mode; /* bitmap of MODE_XXX */
};

View File

@ -748,7 +748,7 @@ sock_setpar(struct sock *f)
return 0;
}
s->xrun = p->xrun;
if (s->opt->mmc && s->xrun == XRUN_IGNORE)
if (s->opt->mtc != NULL && s->xrun == XRUN_IGNORE)
s->xrun = XRUN_SYNC;
#ifdef DEBUG
if (log_level >= 3) {