echo-cancel: rework alignment code

Rework the code to align capture and playback samples so that we can keep more
accurate timings.
This commit is contained in:
Wim Taymans 2010-09-08 18:49:48 +02:00 committed by Colin Guthrie
parent f29acfd0e0
commit 771fb0b59b

View file

@ -173,6 +173,7 @@ struct userdata {
pa_source_output *source_output; pa_source_output *source_output;
pa_memblockq *source_memblockq; /* echo canceler needs fixed sized chunks */ pa_memblockq *source_memblockq; /* echo canceler needs fixed sized chunks */
pa_atomic_t source_active; pa_atomic_t source_active;
size_t source_skip;
pa_sink *sink; pa_sink *sink;
pa_bool_t sink_auto_desc; pa_bool_t sink_auto_desc;
@ -181,6 +182,7 @@ struct userdata {
int64_t send_counter; /* updated in sink IO thread */ int64_t send_counter; /* updated in sink IO thread */
int64_t recv_counter; int64_t recv_counter;
pa_atomic_t sink_active; pa_atomic_t sink_active;
size_t sink_skip;
pa_atomic_t request_resync; pa_atomic_t request_resync;
@ -594,8 +596,8 @@ static void apply_diff_time(struct userdata *u, int64_t diff_time) {
if (diff > 0) { if (diff > 0) {
pa_log_info("Playback after capture (%lld), drop sink %lld", (long long) diff_time, (long long) diff); pa_log_info("Playback after capture (%lld), drop sink %lld", (long long) diff_time, (long long) diff);
/* go forwards on the read side */ u->sink_skip = diff;
pa_memblockq_drop(u->sink_memblockq, diff); u->source_skip = 0;
} }
} else if (diff_time > 0) { } else if (diff_time > 0) {
diff = pa_usec_to_bytes (diff_time, &u->source_output->sample_spec); diff = pa_usec_to_bytes (diff_time, &u->source_output->sample_spec);
@ -603,8 +605,8 @@ static void apply_diff_time(struct userdata *u, int64_t diff_time) {
if (diff > 0) { if (diff > 0) {
pa_log_info("playback too far ahead (%lld), drop source %lld", (long long) diff_time, (long long) diff); pa_log_info("playback too far ahead (%lld), drop source %lld", (long long) diff_time, (long long) diff);
/* go back on the read side */ u->source_skip = diff;
pa_memblockq_rewind(u->sink_memblockq, diff); u->sink_skip = 0;
} }
} }
} }
@ -662,55 +664,66 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
/* take fixed block from recorded samples */ /* take fixed block from recorded samples */
pa_memblockq_peek_fixed_size(u->source_memblockq, u->blocksize, &rchunk); pa_memblockq_peek_fixed_size(u->source_memblockq, u->blocksize, &rchunk);
if (plen > u->blocksize) { if (plen > u->blocksize && u->source_skip == 0) {
uint8_t *rdata, *pdata, *cdata; uint8_t *rdata, *pdata, *cdata;
pa_memchunk cchunk; pa_memchunk cchunk;
/* take fixed block from played samples */ if (u->sink_skip) {
pa_memblockq_peek_fixed_size(u->sink_memblockq, u->blocksize, &pchunk); size_t to_skip;
rdata = pa_memblock_acquire(rchunk.memblock); if (u->sink_skip > plen)
rdata += rchunk.index; to_skip = plen;
pdata = pa_memblock_acquire(pchunk.memblock); else
pdata += pchunk.index; to_skip = u->sink_skip;
cchunk.index = 0; pa_memblockq_drop(u->sink_memblockq, to_skip);
cchunk.length = u->blocksize; plen -= to_skip;
cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
cdata = pa_memblock_acquire(cchunk.memblock);
/* perform echo cancelation */ u->sink_skip -= to_skip;
u->ec->run(u->ec, rdata, pdata, cdata);
if (u->save_aec) {
if (u->captured_file)
fwrite(rdata, 1, u->blocksize, u->captured_file);
if (u->played_file)
fwrite(pdata, 1, u->blocksize, u->played_file);
if (u->canceled_file)
fwrite(cdata, 1, u->blocksize, u->canceled_file);
pa_log_debug("AEC frame saved.");
} }
pa_memblock_release(cchunk.memblock); if (plen > u->blocksize && u->sink_skip == 0) {
pa_memblock_release(pchunk.memblock); /* take fixed block from played samples */
pa_memblock_release(rchunk.memblock); pa_memblockq_peek_fixed_size(u->sink_memblockq, u->blocksize, &pchunk);
/* drop consumed sink samples */ rdata = pa_memblock_acquire(rchunk.memblock);
pa_memblockq_drop(u->sink_memblockq, u->blocksize); rdata += rchunk.index;
pa_memblock_unref(pchunk.memblock); pdata = pa_memblock_acquire(pchunk.memblock);
pdata += pchunk.index;
pa_memblock_unref(rchunk.memblock); cchunk.index = 0;
/* the filtered samples now become the samples from our cchunk.length = u->blocksize;
* source */ cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
rchunk = cchunk; cdata = pa_memblock_acquire(cchunk.memblock);
plen -= u->blocksize; /* perform echo cancelation */
} else { u->ec->run(u->ec, rdata, pdata, cdata);
/* not enough played samples to perform echo cancelation,
* drop what we have */ if (u->save_aec) {
pa_memblockq_drop(u->sink_memblockq, u->blocksize - plen); if (u->captured_file)
plen = 0; fwrite(rdata, 1, u->blocksize, u->captured_file);
if (u->played_file)
fwrite(pdata, 1, u->blocksize, u->played_file);
if (u->canceled_file)
fwrite(cdata, 1, u->blocksize, u->canceled_file);
pa_log_debug("AEC frame saved.");
}
pa_memblock_release(cchunk.memblock);
pa_memblock_release(pchunk.memblock);
pa_memblock_release(rchunk.memblock);
/* drop consumed sink samples */
pa_memblockq_drop(u->sink_memblockq, u->blocksize);
pa_memblock_unref(pchunk.memblock);
pa_memblock_unref(rchunk.memblock);
/* the filtered samples now become the samples from our
* source */
rchunk = cchunk;
plen -= u->blocksize;
}
} }
/* forward the (echo-canceled) data to the virtual source */ /* forward the (echo-canceled) data to the virtual source */
@ -718,8 +731,17 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
pa_memblock_unref(rchunk.memblock); pa_memblock_unref(rchunk.memblock);
pa_memblockq_drop(u->source_memblockq, u->blocksize); pa_memblockq_drop(u->source_memblockq, u->blocksize);
rlen -= u->blocksize; rlen -= u->blocksize;
if (u->source_skip) {
if (u->source_skip > u->blocksize) {
u->source_skip -= u->blocksize;
}
else {
u->sink_skip += (u->blocksize - u->source_skip);
u->source_skip = 0;
}
}
} }
} }
@ -773,19 +795,13 @@ static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes)
/* Called from I/O thread context */ /* Called from I/O thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct userdata *u; struct userdata *u;
size_t amount = 0;
pa_sink_input_assert_ref(i); pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata); pa_assert_se(u = i->userdata);
pa_log_debug("Sink process rewind %lld", (long long) nbytes); pa_log_debug("Sink process rewind %lld", (long long) nbytes);
if (u->sink->thread_info.rewind_nbytes > 0) { pa_sink_process_rewind(u->sink, nbytes);
amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
u->sink->thread_info.rewind_nbytes = 0;
}
pa_sink_process_rewind(u->sink, amount);
pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL); pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
u->send_counter -= nbytes; u->send_counter -= nbytes;
@ -807,8 +823,8 @@ static void source_output_snapshot_within_thread(struct userdata *u, struct snap
snapshot->source_latency = latency; snapshot->source_latency = latency;
snapshot->source_delay = delay; snapshot->source_delay = delay;
snapshot->recv_counter = u->recv_counter; snapshot->recv_counter = u->recv_counter;
snapshot->rlen = rlen; snapshot->rlen = rlen + u->sink_skip;
snapshot->plen = plen; snapshot->plen = plen + u->source_skip;
} }
@ -900,6 +916,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_log_debug("Sink input update max rewind %lld", (long long) nbytes); pa_log_debug("Sink input update max rewind %lld", (long long) nbytes);
pa_memblockq_set_maxrewind (u->sink_memblockq, nbytes);
pa_sink_set_max_rewind_within_thread(u->sink, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
} }
@ -922,7 +939,7 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i); pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata); pa_assert_se(u = i->userdata);
pa_log_debug("Sink input update max rewind %lld", (long long) nbytes); pa_log_debug("Sink input update max request %lld", (long long) nbytes);
pa_sink_set_max_request_within_thread(u->sink, nbytes); pa_sink_set_max_request_within_thread(u->sink, nbytes);
} }