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_mix_badd(struct dev *, struct slot *);
void dev_empty_cycle(struct dev *); void dev_empty_cycle(struct dev *);
void dev_mix_adjvol(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_resamp(struct slot *, void *, void *, int);
int rec_filt_enc(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_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_onmove(struct dev *, int);
void dev_master(struct dev *, unsigned int); void dev_master(struct dev *, unsigned int);
@ -96,8 +95,7 @@ void slot_detach(struct slot *);
void slot_stop(struct slot *); void slot_stop(struct slot *);
void slot_write(struct slot *); void slot_write(struct slot *);
void slot_read(struct slot *); void slot_read(struct slot *);
void slot_mix_drop(struct slot *); int slot_skip(struct slot *);
void slot_sub_sil(struct slot *);
struct midiops dev_midiops = { struct midiops dev_midiops = {
dev_midi_imsg, dev_midi_imsg,
@ -542,44 +540,42 @@ dev_midi_exit(void *arg)
dev_close(d); dev_close(d);
} }
void int
slot_mix_drop(struct slot *s) slot_skip(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)
{ {
unsigned char *data; unsigned char *data;
int count; int max, count;
while (s->sub.silence > 0) { max = s->skip;
data = abuf_wgetblk(&s->sub.buf, &count); while (s->skip > 0) {
if (count < s->round * s->sub.bpf) if (s->mode & MODE_RECMASK) {
break; 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 #ifdef DEBUG
if (log_level >= 4) { if (log_level >= 4) {
slot_log(s); slot_log(s);
log_puts(": inserted a rec block of silence\n"); log_puts(": skipped a cycle\n");
} }
#endif #endif
if (s->sub.encbuf) if (s->mode & MODE_RECMASK) {
enc_sil_do(&s->sub.enc, data, s->round); if (s->sub.encbuf)
else enc_sil_do(&s->sub.enc, data, s->round);
memset(data, 0, s->round * s->sub.bpf); else
abuf_wcommit(&s->sub.buf, s->round * s->sub.bpf); memset(data, 0, s->round * s->sub.bpf);
s->sub.silence--; 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 int
rec_filt_resamp(struct slot *s, void *in, void *res_out, int todo) 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 void
dev_sub_cycle(struct dev *d) dev_full_cycle(struct dev *d)
{ {
struct slot *s, **ps; struct slot *s, **ps;
unsigned char *base;
int nsamp;
#ifdef DEBUG #ifdef DEBUG
if (log_level >= 4) { if (log_level >= 4) {
dev_log(d); 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 #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) if ((d->mode & MODE_REC) && d->decbuf)
dec_do(&d->dec, d->decbuf, (unsigned char *)d->rbuf, d->round); dec_do(&d->dec, d->decbuf, (unsigned char *)d->rbuf, d->round);
ps = &d->slot_list; ps = &d->slot_list;
while ((s = *ps) != NULL) { 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 #ifdef DEBUG
if (log_level >= 3) { if (log_level >= 4) {
slot_log(s); slot_log(s);
log_puts(": overrun, pause cycle\n"); log_puts(": running");
} log_puts(", skip = ");
log_puti(s->skip);
log_puts("\n");
}
#endif #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; ps = &s->next;
continue; 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); s->ops->exit(s->arg);
*ps = s->next; *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; 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; d->prime -= d->round;
dev_empty_cycle(d); dev_empty_cycle(d);
} else { } else {
if (d->mode & MODE_RECMASK) dev_full_cycle(d);
dev_sub_cycle(d);
if (d->mode & MODE_PLAY)
dev_mix_cycle(d);
} }
} }
@ -1715,6 +1689,7 @@ slot_attach(struct slot *s)
#endif #endif
s->next = d->slot_list; s->next = d->slot_list;
d->slot_list = s; d->slot_list = s;
s->skip = 0;
if (s->mode & MODE_PLAY) { if (s->mode & MODE_PLAY) {
slot_nch = s->mix.slot_cmax - s->mix.slot_cmin + 1; slot_nch = s->mix.slot_cmax - s->mix.slot_cmin + 1;
dev_nch = s->mix.dev_cmax - s->mix.dev_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 = s->mix.resampbuf =
xmalloc(d->round * slot_nch * sizeof(adata_t)); xmalloc(d->round * slot_nch * sizeof(adata_t));
} }
s->mix.drop = 0;
s->mix.vol = MIDI_TO_ADATA(s->vol); s->mix.vol = MIDI_TO_ADATA(s->vol);
dev_mix_adjvol(d); dev_mix_adjvol(d);
} }
@ -1781,7 +1755,7 @@ slot_attach(struct slot *s)
/* /*
* N-th recorded block is the N-th played block * 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; 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 * notify the slot that we just wrote in the play buffer, must be called
* after each write * after each write
@ -1967,8 +1962,6 @@ slot_stop(struct slot *s)
void void
slot_write(struct slot *s) slot_write(struct slot *s)
{ {
int drop;
if (s->pstate == SLOT_START && s->mix.buf.used == s->mix.buf.len) { if (s->pstate == SLOT_START && s->mix.buf.used == s->mix.buf.len) {
#ifdef DEBUG #ifdef DEBUG
if (log_level >= 4) { if (log_level >= 4) {
@ -1979,18 +1972,7 @@ slot_write(struct slot *s)
s->pstate = SLOT_READY; s->pstate = SLOT_READY;
slot_ready(s); slot_ready(s);
} }
drop = s->mix.drop; slot_skip_update(s);
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--;
}
} }
/* /*
@ -1999,18 +1981,5 @@ slot_write(struct slot *s)
void void
slot_read(struct slot *s) slot_read(struct slot *s)
{ {
int sil; slot_skip_update(s);
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--;
}
} }

View File

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