version 1.11, 2014/03/05 20:24:16 |
version 1.12, 2014/03/05 20:31:22 |
|
|
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); |
|
|
void slot_start(struct slot *); |
void slot_start(struct slot *); |
void slot_detach(struct slot *); |
void slot_detach(struct slot *); |
void slot_stop(struct slot *); |
void slot_stop(struct slot *); |
|
void slot_skip_update(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, |
|
|
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) { |
unsigned char *data = (unsigned char *)0xdeadbeef; /* please gcc */ |
#ifdef DEBUG |
int max, count; |
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 |
max = s->skip; |
slot_sub_sil(struct slot *s) |
while (s->skip > 0) { |
{ |
if (s->pstate != SLOT_STOP && (s->mode & MODE_RECMASK)) { |
unsigned char *data; |
data = abuf_wgetblk(&s->sub.buf, &count); |
int count; |
if (count < s->round * s->sub.bpf) |
|
break; |
while (s->sub.silence > 0) { |
} |
data = abuf_wgetblk(&s->sub.buf, &count); |
if (s->mode & MODE_PLAY) { |
if (count < s->round * s->sub.bpf) |
if (s->mix.buf.used < s->round * s->mix.bpf) |
break; |
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->pstate != SLOT_STOP && (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; |
} |
} |
|
|
/* |
/* |
|
|
} |
} |
} |
} |
|
|
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) |
{ |
{ |
|
|
} |
} |
|
|
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; |
|
|
|
d->delta -= d->round; |
#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: clk="); |
|
log_puti(d->delta); |
|
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) { |
#ifdef DEBUG |
|
if (log_level >= 4) { |
|
slot_log(s); |
|
log_puts(": running"); |
|
log_puts(", skip = "); |
|
log_puti(s->skip); |
|
log_puts("\n"); |
|
} |
|
#endif |
|
/* |
|
* skip cycles for XRUN_SYNC correction |
|
*/ |
|
slot_skip(s); |
|
if (s->skip < 0) { |
|
s->skip++; |
ps = &s->next; |
ps = &s->next; |
continue; |
continue; |
} |
} |
slot_sub_sil(s); |
|
if (s->sub.silence < 0) { |
#ifdef DEBUG |
s->sub.silence++; |
if (s->pstate == SLOT_STOP && !(s->mode & MODE_PLAY)) { |
ps = &s->next; |
slot_log(s); |
|
log_puts(": rec-only slots can't be drained\n"); |
|
panic(); |
|
} |
|
#endif |
|
/* |
|
* check if stopped stream finished draining |
|
*/ |
|
if (s->pstate == SLOT_STOP && |
|
s->mix.buf.used < s->round * s->mix.bpf) { |
|
/* |
|
* partial blocks are zero-filled by socket |
|
* layer, so s->mix.buf.used == 0 and we can |
|
* destroy the buffer |
|
*/ |
|
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; |
continue; |
} |
} |
if (s->sub.buf.len - s->sub.buf.used < s->round * s->sub.bpf) { |
|
|
/* |
|
* check for xruns |
|
*/ |
|
if (((s->mode & MODE_PLAY) && |
|
s->mix.buf.used < s->round * s->mix.bpf) || |
|
((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"); |
|
} |
|
#endif |
if (s->xrun == XRUN_IGNORE) { |
if (s->xrun == XRUN_IGNORE) { |
if (s->mode & MODE_PLAY) |
|
s->mix.drop--; |
|
s->delta -= s->round; |
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; |
|
} else { |
#ifdef DEBUG |
#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) && !(s->pstate == SLOT_STOP)) { |
|
if (s->sub.prime == 0) { |
|
dev_sub_bcopy(d, s); |
|
s->ops->flush(s->arg); |
|
} else { |
|
#ifdef DEBUG |
if (log_level >= 3) { |
if (log_level >= 3) { |
slot_log(s); |
slot_log(s); |
log_puts(": overrun, pause cycle\n"); |
log_puts(": prime = "); |
|
log_puti(s->sub.prime); |
|
log_puts("\n"); |
} |
} |
#endif |
#endif |
ps = &s->next; |
s->sub.prime--; |
continue; |
|
} |
} |
if (s->xrun == XRUN_SYNC) { |
|
s->sub.silence++; |
|
ps = &s->next; |
|
continue; |
|
} |
|
if (s->xrun == XRUN_ERROR) { |
|
s->ops->exit(s->arg); |
|
*ps = s->next; |
|
continue; |
|
} |
|
} 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); |
|
} |
} |
} |
|
|
/* |
/* |
|
|
dev_onmove(struct dev *d, int delta) |
dev_onmove(struct dev *d, int delta) |
{ |
{ |
long long pos; |
long long pos; |
struct slot *s, *snext; |
struct slot *s, *snext; |
|
|
/* |
d->delta += delta; |
* s->ops->onmove() may remove the slot |
|
*/ |
|
for (s = d->slot_list; s != NULL; s = snext) { |
for (s = d->slot_list; s != NULL; s = snext) { |
|
/* |
|
* s->ops->onmove() may remove the slot |
|
*/ |
snext = s->next; |
snext = s->next; |
pos = (long long)delta * s->round + s->delta_rem; |
pos = (long long)delta * s->round + s->delta_rem; |
s->delta_rem = pos % d->round; |
s->delta_rem = pos % d->round; |
|
|
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); |
|
} |
} |
} |
} |
|
|
|
|
} else { |
} else { |
d->prime = 0; |
d->prime = 0; |
} |
} |
|
|
|
/* empty cycles don't increment delta */ |
|
d->delta = 0; |
|
|
d->pstate = DEV_RUN; |
d->pstate = DEV_RUN; |
dev_sio_start(d); |
dev_sio_start(d); |
} |
} |
|
|
{ |
{ |
struct dev *d = s->dev; |
struct dev *d = s->dev; |
unsigned int slot_nch, dev_nch; |
unsigned int slot_nch, dev_nch; |
|
long long pos; |
int startpos; |
int startpos; |
|
|
/* |
/* |
|
|
* played and/or recorded |
* played and/or recorded |
*/ |
*/ |
startpos = dev_getpos(d) * (int)s->round / (int)d->round; |
startpos = dev_getpos(d) * (int)s->round / (int)d->round; |
s->delta = startpos; |
|
s->delta_rem = 0; |
/* |
|
* adjust initial clock |
|
*/ |
|
pos = (long long)d->delta * s->round; |
|
s->delta = startpos + pos / (int)d->round; |
|
s->delta_rem = pos % d->round; |
|
|
s->pstate = SLOT_RUN; |
s->pstate = SLOT_RUN; |
#ifdef DEBUG |
#ifdef DEBUG |
if (log_level >= 3) { |
if (log_level >= 0) { |
slot_log(s); |
slot_log(s); |
log_puts(": attached at "); |
log_puts(": attached at "); |
log_puti(startpos); |
log_puti(startpos); |
|
log_puts(", delta = "); |
|
log_puti(d->delta); |
log_puts("\n"); |
log_puts("\n"); |
} |
} |
#endif |
#endif |
|
|
#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; |
|
|
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); |
} |
} |
|
|
/* |
/* |
* 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; |
} |
} |
} |
} |
|
|
|
|
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 |
|
|
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) { |
|
|
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--; |
|
} |
|
} |
} |
|
|
/* |
/* |
|
|
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--; |
|
} |
|
} |
} |