run play and rec cycles together: simpler and more robust

This commit is contained in:
Alexandre Ratchov 2014-02-17 10:21:35 +01:00
parent d02745522d
commit 32d0426047
2 changed files with 161 additions and 192 deletions

View File

@ -56,11 +56,10 @@ int play_filt_dec(struct slot *, void *, void *, int);
void dev_mix_badd(struct dev *, struct slot *);
void dev_empty_cycle(struct dev *);
void dev_mix_adjvol(struct dev *);
void dev_mix_cycle(struct dev *);
int rec_filt_resamp(struct slot *, void *, void *, int);
int rec_filt_enc(struct slot *, void *, void *, int);
void dev_sub_bcopy(struct dev *, struct slot *);
void dev_sub_cycle(struct dev *);
void dev_full_cycle(struct dev *);
void dev_onmove(struct dev *, int);
void dev_master(struct dev *, unsigned int);
@ -96,8 +95,7 @@ void slot_detach(struct slot *);
void slot_stop(struct slot *);
void slot_write(struct slot *);
void slot_read(struct slot *);
void slot_mix_drop(struct slot *);
void slot_sub_sil(struct slot *);
int slot_skip(struct slot *);
struct midiops dev_midiops = {
dev_midi_imsg,
@ -542,44 +540,42 @@ dev_midi_exit(void *arg)
dev_close(d);
}
void
slot_mix_drop(struct slot *s)
{
while (s->mix.drop > 0 && s->mix.buf.used >= s->round * s->mix.bpf) {
#ifdef DEBUG
if (log_level >= 4) {
slot_log(s);
log_puts(": dropped a play block\n");
}
#endif
abuf_rdiscard(&s->mix.buf, s->round * s->mix.bpf);
s->mix.drop--;
}
}
void
slot_sub_sil(struct slot *s)
int
slot_skip(struct slot *s)
{
unsigned char *data;
int count;
int max, count;
while (s->sub.silence > 0) {
data = abuf_wgetblk(&s->sub.buf, &count);
if (count < s->round * s->sub.bpf)
break;
max = s->skip;
while (s->skip > 0) {
if (s->mode & MODE_RECMASK) {
data = abuf_wgetblk(&s->sub.buf, &count);
if (count < s->round * s->sub.bpf)
break;
}
if (s->mode & MODE_PLAY) {
if (s->mix.buf.used < s->round * s->mix.bpf)
break;
}
#ifdef DEBUG
if (log_level >= 4) {
slot_log(s);
log_puts(": inserted a rec block of silence\n");
log_puts(": skipped a cycle\n");
}
#endif
if (s->sub.encbuf)
enc_sil_do(&s->sub.enc, data, s->round);
else
memset(data, 0, s->round * s->sub.bpf);
abuf_wcommit(&s->sub.buf, s->round * s->sub.bpf);
s->sub.silence--;
if (s->mode & MODE_RECMASK) {
if (s->sub.encbuf)
enc_sil_do(&s->sub.enc, data, s->round);
else
memset(data, 0, s->round * s->sub.bpf);
abuf_wcommit(&s->sub.buf, s->round * s->sub.bpf);
}
if (s->mode & MODE_PLAY) {
abuf_rdiscard(&s->mix.buf, s->round * s->mix.bpf);
}
s->skip--;
}
return max - s->skip;
}
/*
@ -717,99 +713,6 @@ dev_mix_adjvol(struct dev *d)
}
}
void
dev_mix_cycle(struct dev *d)
{
struct slot *s, **ps;
unsigned char *base;
int nsamp;
#ifdef DEBUG
if (log_level >= 4) {
dev_log(d);
log_puts(": dev_mix_cycle, poffs = ");
log_puti(d->poffs);
log_puts("\n");
}
#endif
base = (unsigned char *)DEV_PBUF(d);
nsamp = d->round * d->pchan;
memset(base, 0, nsamp * sizeof(adata_t));
ps = &d->slot_list;
while ((s = *ps) != NULL) {
if (!(s->mode & MODE_PLAY)) {
ps = &s->next;
continue;
}
#ifdef DEBUG
if (log_level >= 4) {
slot_log(s);
log_puts(": mixing, drop = ");
log_puti(s->mix.drop);
log_puts(" cycles\n");
}
#endif
slot_mix_drop(s);
if (s->mix.drop < 0) {
s->mix.drop++;
ps = &s->next;
continue;
}
if (s->mix.buf.used < s->round * s->mix.bpf &&
s->pstate == SLOT_STOP) {
/*
* partial blocks are zero-filled by socket
* layer
*/
s->pstate = SLOT_INIT;
abuf_done(&s->mix.buf);
if (s->mix.decbuf)
xfree(s->mix.decbuf);
if (s->mix.resampbuf)
xfree(s->mix.resampbuf);
s->ops->eof(s->arg);
*ps = s->next;
dev_mix_adjvol(d);
continue;
}
if (s->mix.buf.used < s->round * s->mix.bpf &&
!(s->pstate == SLOT_STOP)) {
if (s->xrun == XRUN_IGNORE) {
if (s->mode & MODE_RECMASK)
s->sub.silence--;
s->delta -= s->round;
#ifdef DEBUG
if (log_level >= 3) {
slot_log(s);
log_puts(": underrun, pause cycle\n");
}
#endif
ps = &s->next;
continue;
}
if (s->xrun == XRUN_SYNC) {
s->mix.drop++;
ps = &s->next;
continue;
}
if (s->xrun == XRUN_ERROR) {
s->ops->exit(s->arg);
*ps = s->next;
continue;
}
} else {
dev_mix_badd(d, s);
if (s->pstate != SLOT_STOP)
s->ops->fill(s->arg);
}
ps = &s->next;
}
if (d->encbuf) {
enc_do(&d->enc, (unsigned char *)DEV_PBUF(d),
d->encbuf, d->round);
}
}
int
rec_filt_resamp(struct slot *s, void *in, void *res_out, int todo)
{
@ -873,60 +776,134 @@ dev_sub_bcopy(struct dev *d, struct slot *s)
}
void
dev_sub_cycle(struct dev *d)
dev_full_cycle(struct dev *d)
{
struct slot *s, **ps;
unsigned char *base;
int nsamp;
#ifdef DEBUG
if (log_level >= 4) {
dev_log(d);
log_puts(": dev_sub_cycle\n");
log_puts(": dev_full_cycle");
if (d->mode & MODE_PLAY) {
log_puts(", poffs = ");
log_puti(d->poffs);
}
log_puts("\n");
}
#endif
if (d->mode & MODE_PLAY) {
base = (unsigned char *)DEV_PBUF(d);
nsamp = d->round * d->pchan;
memset(base, 0, nsamp * sizeof(adata_t));
}
if ((d->mode & MODE_REC) && d->decbuf)
dec_do(&d->dec, d->decbuf, (unsigned char *)d->rbuf, d->round);
ps = &d->slot_list;
while ((s = *ps) != NULL) {
if (!(s->mode & MODE_RECMASK) || s->pstate == SLOT_STOP) {
ps = &s->next;
continue;
}
slot_sub_sil(s);
if (s->sub.silence < 0) {
s->sub.silence++;
ps = &s->next;
continue;
}
if (s->sub.buf.len - s->sub.buf.used < s->round * s->sub.bpf) {
if (s->xrun == XRUN_IGNORE) {
if (s->mode & MODE_PLAY)
s->mix.drop--;
s->delta -= s->round;
#ifdef DEBUG
if (log_level >= 3) {
slot_log(s);
log_puts(": overrun, pause cycle\n");
}
if (log_level >= 4) {
slot_log(s);
log_puts(": running");
log_puts(", skip = ");
log_puti(s->skip);
log_puts("\n");
}
#endif
slot_skip(s);
if (s->skip < 0) {
s->skip++;
ps = &s->next;
continue;
}
/*
* check if stopped stream finished draining
*/
if (s->pstate == SLOT_STOP) {
if (s->mode & MODE_PLAY) {
if (s->mix.buf.used < s->round * s->mix.bpf) {
/*
* partial blocks are
* zero-filled by socket layer
*/
s->pstate = SLOT_INIT;
abuf_done(&s->mix.buf);
if (s->mix.decbuf)
xfree(s->mix.decbuf);
if (s->mix.resampbuf)
xfree(s->mix.resampbuf);
s->ops->eof(s->arg);
*ps = s->next;
dev_mix_adjvol(d);
continue;
}
} else {
ps = &s->next;
continue;
}
if (s->xrun == XRUN_SYNC) {
s->sub.silence++;
ps = &s->next;
continue;
}
/*
* check for xruns
*/
if (((s->mode & MODE_PLAY) &&
s->mix.buf.used < s->round * s->mix.bpf &&
!(s->pstate == SLOT_STOP)) ||
((s->mode & MODE_RECMASK) &&
s->sub.buf.len - s->sub.buf.used <
s->round * s->sub.bpf)) {
#ifdef DEBUG
if (log_level >= 3) {
slot_log(s);
log_puts(": xrun, pause cycle\n");
}
if (s->xrun == XRUN_ERROR) {
#endif
if (s->xrun == XRUN_IGNORE) {
s->delta -= s->round;
ps = &s->next;
} else if (s->xrun == XRUN_SYNC) {
s->skip++;
ps = &s->next;
} else if (s->xrun == XRUN_ERROR) {
s->ops->exit(s->arg);
*ps = s->next;
continue;
} else {
#ifdef DEBUG
slot_log(s);
log_puts(": bad xrun mode\n");
panic();
#endif
}
continue;
}
if (s->mode & MODE_PLAY) {
dev_mix_badd(d, s);
if (s->pstate != SLOT_STOP)
s->ops->fill(s->arg);
}
if (s->mode & MODE_RECMASK) {
if (s->sub.prime == 0) {
dev_sub_bcopy(d, s);
s->ops->flush(s->arg);
} else {
#ifdef DEBUG
slot_log(s);
log_puts(": prime = ");
log_puti(s->sub.prime);
log_puts("\n");
#endif
s->sub.prime--;
}
} else {
dev_sub_bcopy(d, s);
s->ops->flush(s->arg);
}
ps = &s->next;
}
if ((d->mode & MODE_PLAY) && d->encbuf) {
enc_do(&d->enc, (unsigned char *)DEV_PBUF(d),
d->encbuf, d->round);
}
}
/*
@ -995,10 +972,7 @@ dev_cycle(struct dev *d)
d->prime -= d->round;
dev_empty_cycle(d);
} else {
if (d->mode & MODE_RECMASK)
dev_sub_cycle(d);
if (d->mode & MODE_PLAY)
dev_mix_cycle(d);
dev_full_cycle(d);
}
}
@ -1715,6 +1689,7 @@ slot_attach(struct slot *s)
#endif
s->next = d->slot_list;
d->slot_list = s;
s->skip = 0;
if (s->mode & MODE_PLAY) {
slot_nch = s->mix.slot_cmax - s->mix.slot_cmin + 1;
dev_nch = s->mix.dev_cmax - s->mix.dev_cmin + 1;
@ -1744,7 +1719,6 @@ slot_attach(struct slot *s)
s->mix.resampbuf =
xmalloc(d->round * slot_nch * sizeof(adata_t));
}
s->mix.drop = 0;
s->mix.vol = MIDI_TO_ADATA(s->vol);
dev_mix_adjvol(d);
}
@ -1781,7 +1755,7 @@ slot_attach(struct slot *s)
/*
* N-th recorded block is the N-th played block
*/
s->sub.silence = startpos / (int)s->round;
s->sub.prime = -startpos / (int)s->round;
}
}
@ -1960,6 +1934,27 @@ slot_stop(struct slot *s)
s->tstate = MMC_STOP;
}
void
slot_skip_update(struct slot *s)
{
int skip;
skip = slot_skip(s);
while (skip > 0) {
#ifdef DEBUG
if (log_level >= 4) {
slot_log(s);
log_puts(": catching skipped block\n");
}
#endif
if (s->mode & MODE_RECMASK)
s->ops->flush(s->arg);
if (s->mode & MODE_PLAY)
s->ops->fill(s->arg);
skip--;
}
}
/*
* notify the slot that we just wrote in the play buffer, must be called
* after each write
@ -1967,8 +1962,6 @@ slot_stop(struct slot *s)
void
slot_write(struct slot *s)
{
int drop;
if (s->pstate == SLOT_START && s->mix.buf.used == s->mix.buf.len) {
#ifdef DEBUG
if (log_level >= 4) {
@ -1979,18 +1972,7 @@ slot_write(struct slot *s)
s->pstate = SLOT_READY;
slot_ready(s);
}
drop = s->mix.drop;
slot_mix_drop(s);
while (drop > s->mix.drop) {
#ifdef DEBUG
if (log_level >= 4) {
slot_log(s);
log_puts(": catching play block\n");
}
#endif
s->ops->fill(s->arg);
drop--;
}
slot_skip_update(s);
}
/*
@ -1999,18 +1981,5 @@ slot_write(struct slot *s)
void
slot_read(struct slot *s)
{
int sil;
sil = s->sub.silence;
slot_sub_sil(s);
while (sil > s->sub.silence) {
#ifdef DEBUG
if (log_level >= 4) {
slot_log(s);
log_puts(": catching rec block\n");
}
#endif
s->ops->flush(s->arg);
sil--;
}
slot_skip_update(s);
}

View File

@ -48,7 +48,6 @@ struct slot {
int weight; /* dynamic range */
int maxweight; /* max dynamic range allowed */
unsigned int vol; /* volume within the vol */
int drop; /* to drop on next read */
struct abuf buf; /* socket side buffer */
int bpf; /* byte per frame */
int slot_cmin, slot_cmax; /* slot source chans */
@ -61,8 +60,8 @@ struct slot {
void *resampbuf, *decbuf; /* tmp buffers */
} mix;
struct {
int silence; /* to add on next write */
struct abuf buf; /* socket side buffer */
int prime; /* initial cycles to skip */
int bpf; /* byte per frame */
int slot_cmin, slot_cmax; /* slot destination chans */
int dev_cmin, dev_cmax; /* device source chans */
@ -74,6 +73,7 @@ struct slot {
void *resampbuf, *encbuf; /* tmp buffers */
} sub;
int xrun; /* underrun policy */
int skip; /* cycles to skip (for xrun) */
int dup; /* mono-to-stereo and alike */
#define SLOT_BUFSZ(s) \
((s)->appbufsz + (s)->dev->bufsz / (s)->dev->round * (s)->round)