pa_channel_map *source_map,
pa_sample_spec *sink_ss,
pa_channel_map *sink_map,
- uint32_t *blocksize,
+ uint32_t *nframes,
const char *args);
/* You should have only one of play()+record() or run() set. The first
* samples yourself. If you set run(), module-echo-cancel will handle
* synchronising the playback and record streams. */
- /* Feed the engine 'blocksize' playback bytes.. */
+ /* Feed the engine 'nframes' playback frames. */
void (*play) (pa_echo_canceller *ec, const uint8_t *play);
- /* Feed the engine 'blocksize' record bytes. blocksize processed bytes are
+ /* Feed the engine 'nframes' record frames. nframes processed frames are
* returned in out. */
void (*record) (pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out);
- /* Feed the engine blocksize playback and record streams, with a reasonable
- * effort at keeping the two in sync. blocksize processed bytes are
+ /* Feed the engine nframes playback and record frames, with a reasonable
+ * effort at keeping the two in sync. nframes processed frames are
* returned in out. */
void (*run) (pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
pa_bool_t pa_null_ec_init(pa_core *c, pa_echo_canceller *ec,
pa_sample_spec *source_ss, pa_channel_map *source_map,
pa_sample_spec *sink_ss, pa_channel_map *sink_map,
- uint32_t *blocksize, const char *args);
+ uint32_t *nframes, const char *args);
void pa_null_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
void pa_null_ec_done(pa_echo_canceller *ec);
pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
pa_sample_spec *source_ss, pa_channel_map *source_map,
pa_sample_spec *sink_ss, pa_channel_map *sink_map,
- uint32_t *blocksize, const char *args);
+ uint32_t *nframes, const char *args);
void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
void pa_speex_ec_done(pa_echo_canceller *ec);
#endif
pa_bool_t pa_adrian_ec_init(pa_core *c, pa_echo_canceller *ec,
pa_sample_spec *source_ss, pa_channel_map *source_map,
pa_sample_spec *sink_ss, pa_channel_map *sink_map,
- uint32_t *blocksize, const char *args);
+ uint32_t *nframes, const char *args);
void pa_adrian_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out);
void pa_adrian_ec_done(pa_echo_canceller *ec);
#endif
pa_bool_t pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
pa_sample_spec *source_ss, pa_channel_map *source_map,
pa_sample_spec *sink_ss, pa_channel_map *sink_map,
- uint32_t *blocksize, const char *args);
+ uint32_t *nframes, const char *args);
void pa_webrtc_ec_play(pa_echo_canceller *ec, const uint8_t *play);
void pa_webrtc_ec_record(pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out);
void pa_webrtc_ec_set_drift(pa_echo_canceller *ec, float drift);
pa_bool_t save_aec;
pa_echo_canceller *ec;
- uint32_t blocksize;
+ uint32_t source_blocksize;
+ uint32_t sink_blocksize;
pa_bool_t need_realign;
/* Add the latency internal to our source output on top */
pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec) +
/* and the buffering we do on the source */
- pa_bytes_to_usec(u->blocksize, &u->source_output->source->sample_spec);
+ pa_bytes_to_usec(u->source_blocksize, &u->source_output->source->sample_spec);
return 0;
* those remainder samples.
*/
drift = ((float)(plen - u->sink_rem) - (rlen - u->source_rem)) / ((float)(rlen - u->source_rem));
- u->sink_rem = plen % u->blocksize;
- u->source_rem = rlen % u->blocksize;
+ u->sink_rem = plen % u->sink_blocksize;
+ u->source_rem = rlen % u->source_blocksize;
/* Now let the canceller work its drift compensation magic */
u->ec->set_drift(u->ec, drift);
}
/* Send in the playback samples first */
- while (plen >= u->blocksize) {
- pa_memblockq_peek_fixed_size(u->sink_memblockq, u->blocksize, &pchunk);
+ while (plen >= u->sink_blocksize) {
+ pa_memblockq_peek_fixed_size(u->sink_memblockq, u->sink_blocksize, &pchunk);
pdata = pa_memblock_acquire(pchunk.memblock);
pdata += pchunk.index;
if (u->save_aec) {
if (u->drift_file)
- fprintf(u->drift_file, "p %d\n", u->blocksize);
+ fprintf(u->drift_file, "p %d\n", u->sink_blocksize);
if (u->played_file)
- unused = fwrite(pdata, 1, u->blocksize, u->played_file);
+ unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file);
}
pa_memblock_release(pchunk.memblock);
- pa_memblockq_drop(u->sink_memblockq, u->blocksize);
+ pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize);
pa_memblock_unref(pchunk.memblock);
- plen -= u->blocksize;
+ plen -= u->sink_blocksize;
}
/* And now the capture samples */
- while (rlen >= u->blocksize) {
- pa_memblockq_peek_fixed_size(u->source_memblockq, u->blocksize, &rchunk);
+ while (rlen >= u->source_blocksize) {
+ pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_blocksize, &rchunk);
rdata = pa_memblock_acquire(rchunk.memblock);
rdata += rchunk.index;
cchunk.index = 0;
- cchunk.length = u->blocksize;
+ cchunk.length = u->source_blocksize;
cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
cdata = pa_memblock_acquire(cchunk.memblock);
if (u->save_aec) {
if (u->drift_file)
- fprintf(u->drift_file, "c %d\n", u->blocksize);
+ fprintf(u->drift_file, "c %d\n", u->source_blocksize);
if (u->captured_file)
- unused = fwrite(rdata, 1, u->blocksize, u->captured_file);
+ unused = fwrite(rdata, 1, u->source_blocksize, u->captured_file);
if (u->canceled_file)
- unused = fwrite(cdata, 1, u->blocksize, u->canceled_file);
+ unused = fwrite(cdata, 1, u->source_blocksize, u->canceled_file);
}
pa_memblock_release(cchunk.memblock);
pa_source_post(u->source, &cchunk);
pa_memblock_unref(cchunk.memblock);
- pa_memblockq_drop(u->source_memblockq, u->blocksize);
- rlen -= u->blocksize;
+ pa_memblockq_drop(u->source_memblockq, u->source_blocksize);
+ rlen -= u->source_blocksize;
}
}
rlen = pa_memblockq_get_length(u->source_memblockq);
plen = pa_memblockq_get_length(u->sink_memblockq);
- while (rlen >= u->blocksize) {
+ while (rlen >= u->source_blocksize) {
/* 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->source_blocksize, &rchunk);
- if (plen >= u->blocksize) {
+ if (plen >= u->sink_blocksize) {
/* take fixed block from played samples */
- pa_memblockq_peek_fixed_size(u->sink_memblockq, u->blocksize, &pchunk);
+ pa_memblockq_peek_fixed_size(u->sink_memblockq, u->sink_blocksize, &pchunk);
rdata = pa_memblock_acquire(rchunk.memblock);
rdata += rchunk.index;
pdata += pchunk.index;
cchunk.index = 0;
- cchunk.length = u->blocksize;
+ cchunk.length = u->source_blocksize;
cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
cdata = pa_memblock_acquire(cchunk.memblock);
if (u->save_aec) {
if (u->captured_file)
- unused = fwrite(rdata, 1, u->blocksize, u->captured_file);
+ unused = fwrite(rdata, 1, u->source_blocksize, u->captured_file);
if (u->played_file)
- unused = fwrite(pdata, 1, u->blocksize, u->played_file);
+ unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file);
}
/* perform echo cancellation */
if (u->save_aec) {
if (u->canceled_file)
- unused = fwrite(cdata, 1, u->blocksize, u->canceled_file);
+ unused = fwrite(cdata, 1, u->source_blocksize, u->canceled_file);
}
pa_memblock_release(cchunk.memblock);
pa_memblock_release(rchunk.memblock);
/* drop consumed sink samples */
- pa_memblockq_drop(u->sink_memblockq, u->blocksize);
+ pa_memblockq_drop(u->sink_memblockq, u->sink_blocksize);
pa_memblock_unref(pchunk.memblock);
pa_memblock_unref(rchunk.memblock);
* source */
rchunk = cchunk;
- plen -= u->blocksize;
+ plen -= u->sink_blocksize;
}
/* forward the (echo-canceled) data to the virtual source */
pa_source_post(u->source, &rchunk);
pa_memblock_unref(rchunk.memblock);
- pa_memblockq_drop(u->source_memblockq, u->blocksize);
- rlen -= u->blocksize;
+ pa_memblockq_drop(u->source_memblockq, u->source_blocksize);
+ rlen -= u->source_blocksize;
}
}
plen = pa_memblockq_get_length(u->sink_memblockq);
/* Let's not do anything else till we have enough data to process */
- if (rlen < u->blocksize)
+ if (rlen < u->source_blocksize)
return;
/* See if we need to drop samples in order to sync */
* means the only way to try to catch up is drop sink samples and let
* the canceller cope up with this. */
to_skip = rlen >= u->source_skip ? u->source_skip : rlen;
- to_skip -= to_skip % u->blocksize;
+ to_skip -= to_skip % u->source_blocksize;
if (to_skip) {
pa_memblockq_peek_fixed_size(u->source_memblockq, to_skip, &rchunk);
u->source_skip -= to_skip;
}
- if (rlen && u->source_skip % u->blocksize) {
- u->sink_skip += u->blocksize - (u->source_skip % u->blocksize);
- u->source_skip -= (u->source_skip % u->blocksize);
+ if (rlen && u->source_skip % u->source_blocksize) {
+ u->sink_skip += (uint64_t) (u->source_blocksize - (u->source_skip % u->source_blocksize)) * u->sink_blocksize / u->source_blocksize;
+ u->source_skip -= (u->source_skip % u->source_blocksize);
}
}
pa_sink_new_data sink_data;
pa_memchunk silence;
uint32_t temp;
+ uint32_t nframes = 0;
pa_assert(m);
u->asyncmsgq = pa_asyncmsgq_new(0);
u->need_realign = TRUE;
- if (u->ec->init) {
- if (!u->ec->init(u->core, u->ec, &source_ss, &source_map, &sink_ss, &sink_map, &u->blocksize, pa_modargs_get_value(ma, "aec_args", NULL))) {
- pa_log("Failed to init AEC engine");
- goto fail;
- }
+ pa_assert(u->ec->init);
+ if (!u->ec->init(u->core, u->ec, &source_ss, &source_map, &sink_ss, &sink_map, &nframes, pa_modargs_get_value(ma, "aec_args", NULL))) {
+ pa_log("Failed to init AEC engine");
+ goto fail;
}
+ u->source_blocksize = nframes * pa_frame_size(&source_ss);
+ u->sink_blocksize = nframes * pa_frame_size(&sink_ss);
+
if (u->ec->params.drift_compensation)
pa_assert(u->ec->set_drift);
int ret = 0, i;
char c;
float drift;
+ uint32_t nframes;
pa_memzero(&u, sizeof(u));
if (init_common(ma, &u, &source_ss, &source_map) < 0)
goto fail;
- if (!u.ec->init(u.core, u.ec, &source_ss, &source_map, &sink_ss, &sink_map, &u.blocksize,
+ if (!u.ec->init(u.core, u.ec, &source_ss, &source_map, &sink_ss, &sink_map, &nframes,
(argc > 5) ? argv[5] : NULL )) {
pa_log("Failed to init AEC engine");
goto fail;
}
+ u.source_blocksize = nframes * pa_frame_size(&source_ss);
+ u.sink_blocksize = nframes * pa_frame_size(&sink_ss);
if (u.ec->params.drift_compensation) {
if (argc < 7) {
}
}
- rdata = pa_xmalloc(u.blocksize);
- pdata = pa_xmalloc(u.blocksize);
- cdata = pa_xmalloc(u.blocksize);
+ rdata = pa_xmalloc(u.source_blocksize);
+ pdata = pa_xmalloc(u.sink_blocksize);
+ cdata = pa_xmalloc(u.source_blocksize);
if (!u.ec->params.drift_compensation) {
- while (fread(rdata, u.blocksize, 1, u.captured_file) > 0) {
- if (fread(pdata, u.blocksize, 1, u.played_file) == 0) {
+ while (fread(rdata, u.source_blocksize, 1, u.captured_file) > 0) {
+ if (fread(pdata, u.sink_blocksize, 1, u.played_file) == 0) {
perror("Played file ended before captured file");
goto fail;
}
u.ec->run(u.ec, rdata, pdata, cdata);
- unused = fwrite(cdata, u.blocksize, 1, u.canceled_file);
+ unused = fwrite(cdata, u.source_blocksize, 1, u.canceled_file);
}
} else {
while (fscanf(u.drift_file, "%c", &c) > 0) {
*sink_map = *source_map;
}
-static pa_bool_t pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_spec *source_ss, uint32_t blocksize, pa_modargs *ma) {
+static pa_bool_t pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_spec *source_ss, uint32_t nframes, pa_modargs *ma) {
pa_bool_t agc;
pa_bool_t denoise;
pa_bool_t echo_suppress;
goto fail;
}
- ec->params.priv.speex.pp_state = speex_preprocess_state_init(blocksize / pa_frame_size(source_ss), source_ss->rate);
+ ec->params.priv.speex.pp_state = speex_preprocess_state_init(nframes, source_ss->rate);
tmp = agc;
speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp);
pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
pa_sample_spec *source_ss, pa_channel_map *source_map,
pa_sample_spec *sink_ss, pa_channel_map *sink_map,
- uint32_t *blocksize, const char *args)
+ uint32_t *nframes, const char *args)
{
- int framelen, y, rate;
- uint32_t frame_size_ms, filter_size_ms;
+ int rate;
+ uint32_t y, frame_size_ms, filter_size_ms;
pa_modargs *ma;
if (!(ma = pa_modargs_new(args, valid_modargs))) {
pa_speex_ec_fixate_spec(source_ss, source_map, sink_ss, sink_map);
rate = source_ss->rate;
- framelen = (rate * frame_size_ms) / 1000;
- /* framelen should be a power of 2, round down to nearest power of two */
- y = 1 << ((8 * sizeof (int)) - 2);
- while (y > framelen)
+ *nframes = (rate * frame_size_ms) / 1000;
+ /* nframes should be a power of 2, round down to nearest power of two */
+ y = 1 << ((8 * sizeof (uint32_t)) - 2);
+ while (y > *nframes)
y >>= 1;
- framelen = y;
+ *nframes = y;
- *blocksize = framelen * pa_frame_size (source_ss);
+ pa_log_debug ("Using nframes %d, channels %d, rate %d", *nframes, source_ss->channels, source_ss->rate);
- pa_log_debug ("Using framelen %d, blocksize %u, channels %d, rate %d", framelen, *blocksize, source_ss->channels, source_ss->rate);
-
- ec->params.priv.speex.state = speex_echo_state_init_mc (framelen, (rate * filter_size_ms) / 1000, source_ss->channels, source_ss->channels);
+ ec->params.priv.speex.state = speex_echo_state_init_mc (*nframes, (rate * filter_size_ms) / 1000, source_ss->channels, source_ss->channels);
if (!ec->params.priv.speex.state)
goto fail;
speex_echo_ctl(ec->params.priv.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);
- if (!pa_speex_ec_preprocessor_init(ec, source_ss, *blocksize, ma))
+ if (!pa_speex_ec_preprocessor_init(ec, source_ss, *nframes, ma))
goto fail;
pa_modargs_free(ma);