version 1.93, 2021/03/03 10:13:06 |
version 1.94, 2021/03/03 10:19:06 |
|
|
void zomb_eof(void *); |
void zomb_eof(void *); |
void zomb_exit(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_badd(struct dev *, struct slot *); |
void dev_mix_adjvol(struct dev *); |
void dev_mix_adjvol(struct dev *); |
void dev_sub_bcopy(struct dev *, struct slot *); |
void dev_sub_bcopy(struct dev *, struct slot *); |
|
|
void dev_setalt(struct dev *, unsigned int); |
void dev_setalt(struct dev *, unsigned int); |
unsigned int dev_roundof(struct dev *, unsigned int); |
unsigned int dev_roundof(struct dev *, unsigned int); |
void dev_wakeup(struct dev *); |
void dev_wakeup(struct dev *); |
void dev_sync_attach(struct dev *); |
|
|
|
void slot_ctlname(struct slot *, char *, size_t); |
void slot_ctlname(struct slot *, char *, size_t); |
void slot_log(struct slot *); |
void slot_log(struct slot *); |
|
|
struct slot slot_array[DEV_NSLOT]; |
struct slot slot_array[DEV_NSLOT]; |
unsigned int slot_serial; /* for slot allocation */ |
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 |
void |
slot_array_init(void) |
slot_array_init(void) |
{ |
{ |
|
|
* send a quarter frame MTC message |
* send a quarter frame MTC message |
*/ |
*/ |
void |
void |
dev_midi_qfr(struct dev *d, int delta) |
mtc_midi_qfr(struct mtc *mtc, int delta) |
{ |
{ |
unsigned char buf[2]; |
unsigned char buf[2]; |
unsigned int data; |
unsigned int data; |
int qfrlen; |
int qfrlen; |
|
|
d->mtc.delta += delta * MTC_SEC; |
mtc->delta += delta * MTC_SEC; |
qfrlen = d->rate * (MTC_SEC / (4 * d->mtc.fps)); |
qfrlen = mtc->dev->rate * (MTC_SEC / (4 * mtc->fps)); |
while (d->mtc.delta >= qfrlen) { |
while (mtc->delta >= qfrlen) { |
switch (d->mtc.qfr) { |
switch (mtc->qfr) { |
case 0: |
case 0: |
data = d->mtc.fr & 0xf; |
data = mtc->fr & 0xf; |
break; |
break; |
case 1: |
case 1: |
data = d->mtc.fr >> 4; |
data = mtc->fr >> 4; |
break; |
break; |
case 2: |
case 2: |
data = d->mtc.sec & 0xf; |
data = mtc->sec & 0xf; |
break; |
break; |
case 3: |
case 3: |
data = d->mtc.sec >> 4; |
data = mtc->sec >> 4; |
break; |
break; |
case 4: |
case 4: |
data = d->mtc.min & 0xf; |
data = mtc->min & 0xf; |
break; |
break; |
case 5: |
case 5: |
data = d->mtc.min >> 4; |
data = mtc->min >> 4; |
break; |
break; |
case 6: |
case 6: |
data = d->mtc.hr & 0xf; |
data = mtc->hr & 0xf; |
break; |
break; |
case 7: |
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 |
* tick messages are sent 2 frames ahead |
*/ |
*/ |
d->mtc.fr += 2; |
mtc->fr += 2; |
if (d->mtc.fr < d->mtc.fps) |
if (mtc->fr < mtc->fps) |
break; |
break; |
d->mtc.fr -= d->mtc.fps; |
mtc->fr -= mtc->fps; |
d->mtc.sec++; |
mtc->sec++; |
if (d->mtc.sec < 60) |
if (mtc->sec < 60) |
break; |
break; |
d->mtc.sec = 0; |
mtc->sec = 0; |
d->mtc.min++; |
mtc->min++; |
if (d->mtc.min < 60) |
if (mtc->min < 60) |
break; |
break; |
d->mtc.min = 0; |
mtc->min = 0; |
d->mtc.hr++; |
mtc->hr++; |
if (d->mtc.hr < 24) |
if (mtc->hr < 24) |
break; |
break; |
d->mtc.hr = 0; |
mtc->hr = 0; |
break; |
break; |
default: |
default: |
/* NOTREACHED */ |
/* NOTREACHED */ |
data = 0; |
data = 0; |
} |
} |
buf[0] = 0xf1; |
buf[0] = 0xf1; |
buf[1] = (d->mtc.qfr << 4) | data; |
buf[1] = (mtc->qfr << 4) | data; |
d->mtc.qfr++; |
mtc->qfr++; |
d->mtc.qfr &= 7; |
mtc->qfr &= 7; |
dev_midi_send(d, buf, 2); |
dev_midi_send(mtc->dev, buf, 2); |
d->mtc.delta -= qfrlen; |
mtc->delta -= qfrlen; |
} |
} |
} |
} |
|
|
|
|
* send a full frame MTC message |
* send a full frame MTC message |
*/ |
*/ |
void |
void |
dev_midi_full(struct dev *d) |
mtc_midi_full(struct mtc *mtc) |
{ |
{ |
struct sysex x; |
struct sysex x; |
unsigned int fps; |
unsigned int fps; |
|
|
d->mtc.delta = MTC_SEC * dev_getpos(d); |
mtc->delta = MTC_SEC * dev_getpos(mtc->dev); |
if (d->rate % (30 * 4 * d->round) == 0) { |
if (mtc->dev->rate % (30 * 4 * mtc->dev->round) == 0) { |
d->mtc.fps_id = MTC_FPS_30; |
mtc->fps_id = MTC_FPS_30; |
d->mtc.fps = 30; |
mtc->fps = 30; |
} else if (d->rate % (25 * 4 * d->round) == 0) { |
} else if (mtc->dev->rate % (25 * 4 * mtc->dev->round) == 0) { |
d->mtc.fps_id = MTC_FPS_25; |
mtc->fps_id = MTC_FPS_25; |
d->mtc.fps = 25; |
mtc->fps = 25; |
} else { |
} else { |
d->mtc.fps_id = MTC_FPS_24; |
mtc->fps_id = MTC_FPS_24; |
d->mtc.fps = 24; |
mtc->fps = 24; |
} |
} |
#ifdef DEBUG |
#ifdef DEBUG |
if (log_level >= 3) { |
if (log_level >= 3) { |
dev_log(d); |
dev_log(mtc->dev); |
log_puts(": mtc full frame at "); |
log_puts(": mtc full frame at "); |
log_puti(d->mtc.delta); |
log_puti(mtc->delta); |
log_puts(", "); |
log_puts(", "); |
log_puti(d->mtc.fps); |
log_puti(mtc->fps); |
log_puts(" fps\n"); |
log_puts(" fps\n"); |
} |
} |
#endif |
#endif |
fps = d->mtc.fps; |
fps = mtc->fps; |
d->mtc.hr = (d->mtc.origin / (MTC_SEC * 3600)) % 24; |
mtc->hr = (mtc->origin / (MTC_SEC * 3600)) % 24; |
d->mtc.min = (d->mtc.origin / (MTC_SEC * 60)) % 60; |
mtc->min = (mtc->origin / (MTC_SEC * 60)) % 60; |
d->mtc.sec = (d->mtc.origin / (MTC_SEC)) % 60; |
mtc->sec = (mtc->origin / (MTC_SEC)) % 60; |
d->mtc.fr = (d->mtc.origin / (MTC_SEC / fps)) % fps; |
mtc->fr = (mtc->origin / (MTC_SEC / fps)) % fps; |
|
|
x.start = SYSEX_START; |
x.start = SYSEX_START; |
x.type = SYSEX_TYPE_RT; |
x.type = SYSEX_TYPE_RT; |
x.dev = SYSEX_DEV_ANY; |
x.dev = SYSEX_DEV_ANY; |
x.id0 = SYSEX_MTC; |
x.id0 = SYSEX_MTC; |
x.id1 = SYSEX_MTC_FULL; |
x.id1 = SYSEX_MTC_FULL; |
x.u.full.hr = d->mtc.hr | (d->mtc.fps_id << 5); |
x.u.full.hr = mtc->hr | (mtc->fps_id << 5); |
x.u.full.min = d->mtc.min; |
x.u.full.min = mtc->min; |
x.u.full.sec = d->mtc.sec; |
x.u.full.sec = mtc->sec; |
x.u.full.fr = d->mtc.fr; |
x.u.full.fr = mtc->fr; |
x.u.full.end = SYSEX_END; |
x.u.full.end = SYSEX_END; |
d->mtc.qfr = 0; |
mtc->qfr = 0; |
dev_midi_send(d, (unsigned char *)&x, SYSEX_SIZE(full)); |
dev_midi_send(mtc->dev, (unsigned char *)&x, SYSEX_SIZE(full)); |
} |
} |
|
|
/* |
/* |
|
|
* check if the device is actually used. If it isn't, |
* check if the device is actually used. If it isn't, |
* then close it |
* 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) { |
if (log_level >= 2) { |
dev_log(d); |
dev_log(d); |
log_puts(": device stopped\n"); |
log_puts(": device stopped\n"); |
|
|
if (s->delta >= 0) |
if (s->delta >= 0) |
s->ops->onmove(s->arg); |
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 |
void |
|
|
d->pstate = DEV_CFG; |
d->pstate = DEV_CFG; |
d->slot_list = NULL; |
d->slot_list = NULL; |
d->master = MIDI_MAXCTL; |
d->master = MIDI_MAXCTL; |
d->mtc.origin = 0; |
|
d->tstate = MMC_STOP; |
|
snprintf(d->name, CTL_NAMEMAX, "%u", d->num); |
snprintf(d->name, CTL_NAMEMAX, "%u", d->num); |
d->next = dev_list; |
d->next = dev_list; |
dev_list = d; |
dev_list = d; |
|
|
log_putu(d->bufsz / d->round); |
log_putu(d->bufsz / d->round); |
log_puts(" blocks of "); |
log_puts(" blocks of "); |
log_putu(d->round); |
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; |
return 1; |
} |
} |
|
|
int |
int |
dev_reopen(struct dev *d) |
dev_reopen(struct dev *d) |
{ |
{ |
|
struct mtc *mtc; |
struct slot *s; |
struct slot *s; |
long long pos; |
long long pos; |
unsigned int pstate; |
unsigned int pstate; |
|
|
/* reinitilize the format conversion chain */ |
/* reinitilize the format conversion chain */ |
slot_initconv(s); |
slot_initconv(s); |
} |
} |
if (d->tstate == MMC_RUN) { |
mtc = &mtc_array[0]; |
d->mtc.delta -= delta * MTC_SEC; |
if (mtc->dev == d && mtc->tstate == MTC_RUN) { |
|
mtc->delta -= delta * MTC_SEC; |
if (log_level >= 2) { |
if (log_level >= 2) { |
dev_log(d); |
dev_log(mtc->dev); |
log_puts(": adjusted mtc: delta ->"); |
log_puts(": adjusted mtc: delta ->"); |
log_puti(d->mtc.delta); |
log_puti(mtc->delta); |
log_puts("\n"); |
log_puts("\n"); |
} |
} |
} |
} |
|
|
log_puts(": draining\n"); |
log_puts(": draining\n"); |
} |
} |
#endif |
#endif |
if (d->tstate != MMC_STOP) |
if (mtc_array[0].dev == d && mtc_array[0].tstate != MTC_STOP) |
dev_mmcstop(d); |
mtc_stop(&mtc_array[0]); |
if (d->hold) |
if (d->hold) |
dev_unref(d); |
dev_unref(d); |
} |
} |
|
|
* attach them all at the same position |
* attach them all at the same position |
*/ |
*/ |
void |
void |
dev_sync_attach(struct dev *d) |
mtc_trigger(struct mtc *mtc) |
{ |
{ |
int i; |
int i; |
struct slot *s; |
struct slot *s; |
|
|
if (d->tstate != MMC_START) { |
if (mtc->tstate != MTC_START) { |
if (log_level >= 2) { |
if (log_level >= 2) { |
dev_log(d); |
dev_log(mtc->dev); |
log_puts(": not started by mmc yet, waiting...\n"); |
log_puts(": not started by mmc yet, waiting...\n"); |
} |
} |
return; |
return; |
} |
} |
|
|
for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { |
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; |
continue; |
if (s->pstate != SLOT_READY) { |
if (s->pstate != SLOT_READY) { |
#ifdef DEBUG |
#ifdef DEBUG |
|
|
return; |
return; |
} |
} |
} |
} |
if (!dev_ref(d)) |
if (!dev_ref(mtc->dev)) |
return; |
return; |
|
|
for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) { |
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; |
continue; |
slot_attach(s); |
slot_attach(s); |
s->pstate = SLOT_RUN; |
s->pstate = SLOT_RUN; |
} |
} |
d->tstate = MMC_RUN; |
mtc->tstate = MTC_RUN; |
dev_midi_full(d); |
mtc_midi_full(mtc); |
dev_wakeup(d); |
dev_wakeup(mtc->dev); |
} |
} |
|
|
/* |
/* |
* start all slots simultaneously |
* start all slots simultaneously |
*/ |
*/ |
void |
void |
dev_mmcstart(struct dev *d) |
mtc_start(struct mtc *mtc) |
{ |
{ |
if (d->tstate == MMC_STOP) { |
if (mtc->tstate == MTC_STOP) { |
d->tstate = MMC_START; |
mtc->tstate = MTC_START; |
dev_sync_attach(d); |
mtc_trigger(mtc); |
#ifdef DEBUG |
#ifdef DEBUG |
} else { |
} else { |
if (log_level >= 3) { |
if (log_level >= 3) { |
dev_log(d); |
dev_log(mtc->dev); |
log_puts(": ignoring mmc start\n"); |
log_puts(": ignoring mmc start\n"); |
} |
} |
#endif |
#endif |
|
|
* stop all slots simultaneously |
* stop all slots simultaneously |
*/ |
*/ |
void |
void |
dev_mmcstop(struct dev *d) |
mtc_stop(struct mtc *mtc) |
{ |
{ |
switch (d->tstate) { |
switch (mtc->tstate) { |
case MMC_START: |
case MTC_START: |
d->tstate = MMC_STOP; |
mtc->tstate = MTC_STOP; |
return; |
return; |
case MMC_RUN: |
case MTC_RUN: |
d->tstate = MMC_STOP; |
mtc->tstate = MTC_STOP; |
dev_unref(d); |
dev_unref(mtc->dev); |
break; |
break; |
default: |
default: |
#ifdef DEBUG |
#ifdef DEBUG |
if (log_level >= 3) { |
if (log_level >= 3) { |
dev_log(d); |
dev_log(mtc->dev); |
log_puts(": ignored mmc stop\n"); |
log_puts(": ignored mmc stop\n"); |
} |
} |
#endif |
#endif |
|
|
* relocate all slots simultaneously |
* relocate all slots simultaneously |
*/ |
*/ |
void |
void |
dev_mmcloc(struct dev *d, unsigned int origin) |
mtc_loc(struct mtc *mtc, unsigned int origin) |
{ |
{ |
if (log_level >= 2) { |
if (log_level >= 2) { |
dev_log(d); |
dev_log(mtc->dev); |
log_puts(": relocated to "); |
log_puts(": relocated to "); |
log_putu(origin); |
log_putu(origin); |
log_puts("\n"); |
log_puts("\n"); |
} |
} |
if (d->tstate == MMC_RUN) |
if (mtc->tstate == MTC_RUN) |
dev_mmcstop(d); |
mtc_stop(mtc); |
d->mtc.origin = origin; |
mtc->origin = origin; |
if (d->tstate == MMC_RUN) |
if (mtc->tstate == MTC_RUN) |
dev_mmcstart(d); |
mtc_start(mtc); |
} |
} |
|
|
/* |
/* |
|
|
s->mix.nch = s->opt->pmax - s->opt->pmin + 1; |
s->mix.nch = s->opt->pmax - s->opt->pmin + 1; |
if (s->mode & MODE_RECMASK) |
if (s->mode & MODE_RECMASK) |
s->sub.nch = s->opt->rmax - s->opt->rmin + 1; |
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->appbufsz = s->opt->dev->bufsz; |
s->round = s->opt->dev->round; |
s->round = s->opt->dev->round; |
s->rate = s->opt->dev->rate; |
s->rate = s->opt->dev->rate; |
|
|
*/ |
*/ |
if (s->opt->dev->pstate == DEV_CFG) |
if (s->opt->dev->pstate == DEV_CFG) |
return; |
return; |
if (!s->opt->mmc) { |
if (s->opt->mtc == NULL) { |
slot_attach(s); |
slot_attach(s); |
s->pstate = SLOT_RUN; |
s->pstate = SLOT_RUN; |
} else |
} else |
dev_sync_attach(s->opt->dev); |
mtc_trigger(s->opt->mtc); |
} |
} |
|
|
/* |
/* |