From 3a92be3c5cee24fafae99dc20e4973adba9a5e57 Mon Sep 17 00:00:00 2001 From: Stefan Huber Date: Thu, 20 Dec 2012 11:33:04 +0100 Subject: [PATCH] echo-cancel: Enable different blocksizes for sink and source In order to support different blocksizes for source and sink (e.g, for 4-to-1 beamforming/echo canceling which involves 4 record channels and 1 playback channel) the AEC API is altered: The blocksize for source and sink may differ (due to different sample specs) but the number of frames that are processed in one invokation of the AEC implementation's run() function is the same for the playback and the record stream. Consequently, the AEC implementation's init() function initalizes 'nframes' instead of 'blocksize' and the source's and sink's blocksizes are derived from 'nframes'. The old API also caused code duplication in each AEC implementation's init function for the compution of the blocksize, which is eliminated by the new API. Signed-off-by: Stefan Huber Acked-by: Peter Meerwald --- src/modules/echo-cancel/adrian.c | 11 +- src/modules/echo-cancel/echo-cancel.h | 18 ++-- src/modules/echo-cancel/module-echo-cancel.c | 101 ++++++++++--------- src/modules/echo-cancel/null.c | 9 +- src/modules/echo-cancel/speex.c | 28 +++-- src/modules/echo-cancel/webrtc.cc | 5 +- 6 files changed, 88 insertions(+), 84 deletions(-) diff --git a/src/modules/echo-cancel/adrian.c b/src/modules/echo-cancel/adrian.c index ab3858a4..91e3b35f 100644 --- a/src/modules/echo-cancel/adrian.c +++ b/src/modules/echo-cancel/adrian.c @@ -57,9 +57,9 @@ static void pa_adrian_ec_fixate_spec(pa_sample_spec *source_ss, pa_channel_map * 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) { - int framelen, rate, have_vector = 0; + int rate, have_vector = 0; uint32_t frame_size_ms; pa_modargs *ma; @@ -77,11 +77,10 @@ pa_bool_t pa_adrian_ec_init(pa_core *c, pa_echo_canceller *ec, pa_adrian_ec_fixate_spec(source_ss, source_map, sink_ss, sink_map); rate = source_ss->rate; - framelen = (rate * frame_size_ms) / 1000; + *nframes = (rate * frame_size_ms) / 1000; + ec->params.priv.adrian.blocksize = (*nframes) * pa_frame_size(source_ss); - *blocksize = ec->params.priv.adrian.blocksize = framelen * pa_frame_size (source_ss); - - pa_log_debug ("Using framelen %d, blocksize %u, channels %d, rate %d", framelen, ec->params.priv.adrian.blocksize, source_ss->channels, source_ss->rate); + pa_log_debug ("Using nframes %d, blocksize %u, channels %d, rate %d", *nframes, ec->params.priv.adrian.blocksize, source_ss->channels, source_ss->rate); /* For now we only support SSE */ if (c->cpu_info.cpu_type == PA_CPU_X86 && (c->cpu_info.flags.x86 & PA_CPU_X86_SSE)) diff --git a/src/modules/echo-cancel/echo-cancel.h b/src/modules/echo-cancel/echo-cancel.h index 9ca78ff0..9f14adea 100644 --- a/src/modules/echo-cancel/echo-cancel.h +++ b/src/modules/echo-cancel/echo-cancel.h @@ -86,7 +86,7 @@ struct pa_echo_canceller { 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 @@ -94,13 +94,13 @@ struct pa_echo_canceller { * 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); @@ -132,7 +132,7 @@ void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_cvolume *v); 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); @@ -141,7 +141,7 @@ 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 @@ -151,7 +151,7 @@ void pa_speex_ec_done(pa_echo_canceller *ec); 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 @@ -162,7 +162,7 @@ PA_C_DECL_BEGIN 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); diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 103aef0e..11ad1de2 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -211,7 +211,8 @@ struct userdata { 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; @@ -417,7 +418,7 @@ static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t /* 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; @@ -735,8 +736,8 @@ static void do_push_drift_comp(struct userdata *u) { * 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); @@ -747,8 +748,8 @@ static void do_push_drift_comp(struct userdata *u) { } /* 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; @@ -756,27 +757,27 @@ static void do_push_drift_comp(struct userdata *u) { 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); @@ -784,11 +785,11 @@ static void do_push_drift_comp(struct userdata *u) { 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); @@ -799,8 +800,8 @@ static void do_push_drift_comp(struct userdata *u) { 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; } } @@ -818,13 +819,13 @@ static void do_push(struct userdata *u) { 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; @@ -832,15 +833,15 @@ static void do_push(struct userdata *u) { 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 */ @@ -848,7 +849,7 @@ static void do_push(struct userdata *u) { 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); @@ -856,7 +857,7 @@ static void do_push(struct userdata *u) { 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); @@ -864,15 +865,15 @@ static void do_push(struct userdata *u) { * 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; } } @@ -907,7 +908,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) 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 */ @@ -923,7 +924,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) * 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); @@ -936,9 +937,9 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) 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); } } @@ -1640,6 +1641,7 @@ int pa__init(pa_module*m) { pa_sink_new_data sink_data; pa_memchunk silence; uint32_t temp; + uint32_t nframes = 0; pa_assert(m); @@ -1729,13 +1731,15 @@ int pa__init(pa_module*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); @@ -2071,6 +2075,7 @@ int main(int argc, char* argv[]) { int ret = 0, i; char c; float drift; + uint32_t nframes; pa_memzero(&u, sizeof(u)); @@ -2116,11 +2121,13 @@ int main(int argc, char* argv[]) { 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) { @@ -2136,20 +2143,20 @@ int main(int argc, char* argv[]) { } } - 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) { diff --git a/src/modules/echo-cancel/null.c b/src/modules/echo-cancel/null.c index 1820661b..bfd6c30a 100644 --- a/src/modules/echo-cancel/null.c +++ b/src/modules/echo-cancel/null.c @@ -28,8 +28,8 @@ PA_C_DECL_END 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) { - unsigned framelen = 256; + uint32_t *nframes, const char *args) { + *nframes = 256; source_ss->format = PA_SAMPLE_S16NE; source_ss->channels = 1; @@ -37,14 +37,13 @@ pa_bool_t pa_null_ec_init(pa_core *c, pa_echo_canceller *ec, *sink_ss = *source_ss; *sink_map = *source_map; - *blocksize = framelen * pa_frame_size(source_ss); - - pa_log_debug("null AEC: framelen %u, blocksize %u, channels %d, rate %d", framelen, *blocksize, source_ss->channels, source_ss->rate); + pa_log_debug("null AEC: nframes %u, channels %d, rate %d", *nframes, source_ss->channels, source_ss->rate); return TRUE; } void pa_null_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) { + // blocksize is nframes * frame-size memcpy(out, rec, 256 * 2); } diff --git a/src/modules/echo-cancel/speex.c b/src/modules/echo-cancel/speex.c index d6331fc5..5ebd0b3f 100644 --- a/src/modules/echo-cancel/speex.c +++ b/src/modules/echo-cancel/speex.c @@ -58,7 +58,7 @@ static void pa_speex_ec_fixate_spec(pa_sample_spec *source_ss, pa_channel_map *s *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; @@ -111,7 +111,7 @@ static pa_bool_t pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_ 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); @@ -148,10 +148,10 @@ fail: 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))) { @@ -174,25 +174,23 @@ pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec, 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); diff --git a/src/modules/echo-cancel/webrtc.cc b/src/modules/echo-cancel/webrtc.cc index 633b7232..6ced28ce 100644 --- a/src/modules/echo-cancel/webrtc.cc +++ b/src/modules/echo-cancel/webrtc.cc @@ -79,7 +79,7 @@ static int routing_mode_from_string(const char *rmode) { 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) { webrtc::AudioProcessing *apm = NULL; pa_bool_t hpf, ns, agc, dgc, mobile, cn; @@ -216,7 +216,8 @@ pa_bool_t pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec, ec->params.priv.webrtc.apm = apm; ec->params.priv.webrtc.sample_spec = *source_ss; - ec->params.priv.webrtc.blocksize = *blocksize = (uint64_t)pa_bytes_per_second(source_ss) * BLOCK_SIZE_US / PA_USEC_PER_SEC; + ec->params.priv.webrtc.blocksize = (uint64_t)pa_bytes_per_second(source_ss) * BLOCK_SIZE_US / PA_USEC_PER_SEC; + *nframes = ec->params.priv.webrtc.blocksize / pa_frame_size(source_ss); pa_modargs_free(ma); return TRUE; -- 2.39.2