[pulseaudio-discuss] [PATCH 2/3] echo-cancel: Enable different sample specs for rec and out stream
Tanu Kaskinen
tanuk at iki.fi
Sat Feb 16 13:42:07 PST 2013
On Thu, 2013-02-07 at 14:40 +0100, Stefan Huber wrote:
> Enable advanced AEC methods to use different specs (i.e., number of
> channels) for rec and out stream. A typical application is beam forming
> resp. multi-channel AEC, which takes multiple record channels to produce
> an echo-canceled output stream.
> This commit alters the EC API as follows: the EC's init() used to get
> source and sink's sample spec/channel map. The new interface renamed
> source to rec and sink to play and additionally passes sample spec and
> channel map of the out stream. The new parameter names of init()
> {rec,play,out}_{ss,map} are more intuitive and also resemble to the
> parameter names known from run(). Both rec_{ss,map} and out_{ss,map} are
> initialized as we knew it from source_{ss,map} before being passed to
> init(). The previous EC implementations only require trivial changes,
> i.e., setting rec_{ss,map} to out_{ss,map} at the end of init() in case
> that out_{ss,map} is modified in init().
> ---
> src/modules/echo-cancel/adrian.c | 32 +++++++-----
> src/modules/echo-cancel/echo-cancel.h | 34 +++++++-----
> src/modules/echo-cancel/module-echo-cancel.c | 72 ++++++++++++++------------
> src/modules/echo-cancel/null.c | 17 +++---
> src/modules/echo-cancel/speex.c | 34 ++++++------
> src/modules/echo-cancel/webrtc.cc | 27 +++++-----
> 6 files changed, 123 insertions(+), 93 deletions(-)
Some trivial comments below.
> diff --git a/src/modules/echo-cancel/adrian.c b/src/modules/echo-cancel/adrian.c
> index 91e3b35..813c304 100644
> --- a/src/modules/echo-cancel/adrian.c
> +++ b/src/modules/echo-cancel/adrian.c
> @@ -43,20 +43,24 @@ static const char* const valid_modargs[] = {
> NULL
> };
>
> -static void pa_adrian_ec_fixate_spec(pa_sample_spec *source_ss, pa_channel_map *source_map,
> - pa_sample_spec *sink_ss, pa_channel_map *sink_map)
> +static void pa_adrian_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
> + pa_sample_spec *play_ss, pa_channel_map *play_map,
> + pa_sample_spec *out_ss, pa_channel_map *out_map)
Indented one space too little.
> {
> - source_ss->format = PA_SAMPLE_S16NE;
> - source_ss->channels = 1;
> - pa_channel_map_init_mono(source_map);
> -
> - *sink_ss = *source_ss;
> - *sink_map = *source_map;
> + out_ss->format = PA_SAMPLE_S16NE;
> + out_ss->channels = 1;
> + pa_channel_map_init_mono(out_map);
> +
> + *play_ss = *out_ss;
> + *play_map = *out_map;
> + *rec_ss = *out_ss;
> + *rec_map = *out_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,
> + pa_sample_spec *rec_ss, pa_channel_map *rec_map,
> + pa_sample_spec *play_ss, pa_channel_map *play_map,
> + pa_sample_spec *out_ss, pa_channel_map *out_map,
> uint32_t *nframes, const char *args)
Indented one space too little.
> {
> int rate, have_vector = 0;
> @@ -74,13 +78,13 @@ pa_bool_t pa_adrian_ec_init(pa_core *c, pa_echo_canceller *ec,
> goto fail;
> }
>
> - pa_adrian_ec_fixate_spec(source_ss, source_map, sink_ss, sink_map);
> + pa_adrian_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map);
>
> - rate = source_ss->rate;
> + rate = out_ss->rate;
> *nframes = (rate * frame_size_ms) / 1000;
> - ec->params.priv.adrian.blocksize = (*nframes) * pa_frame_size(source_ss);
> + ec->params.priv.adrian.blocksize = (*nframes) * pa_frame_size(out_ss);
>
> - pa_log_debug ("Using nframes %d, blocksize %u, channels %d, rate %d", *nframes, 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, out_ss->channels, out_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 e7eed30..71091ba 100644
> --- a/src/modules/echo-cancel/echo-cancel.h
> +++ b/src/modules/echo-cancel/echo-cancel.h
> @@ -47,7 +47,7 @@ typedef struct pa_echo_canceller_params pa_echo_canceller_params;
> struct pa_echo_canceller_params {
> union {
> struct {
> - pa_sample_spec source_ss;
> + pa_sample_spec out_ss;
> } null;
> #ifdef HAVE_SPEEX
> struct {
> @@ -85,10 +85,12 @@ struct pa_echo_canceller {
> /* Initialise canceller engine. */
> pa_bool_t (*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,
> + pa_sample_spec *rec_ss,
> + pa_channel_map *rec_map,
> + pa_sample_spec *play_ss,
> + pa_channel_map *play_map,
> + pa_sample_spec *out_ss,
> + pa_channel_map *out_map,
> uint32_t *nframes,
> const char *args);
>
> @@ -133,8 +135,9 @@ void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_cvolume *v);
>
> /* Null canceller functions */
> 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,
> + pa_sample_spec *rec_ss, pa_channel_map *rec_map,
> + pa_sample_spec *play_ss, pa_channel_map *play_map,
> + pa_sample_spec *out_ss, pa_channel_map *out_map,
Indented one space too much.
> 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);
> @@ -142,8 +145,9 @@ void pa_null_ec_done(pa_echo_canceller *ec);
> #ifdef HAVE_SPEEX
> /* Speex canceller functions */
> 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,
> + pa_sample_spec *rec_ss, pa_channel_map *rec_map,
> + pa_sample_spec *play_ss, pa_channel_map *play_map,
> + pa_sample_spec *out_ss, pa_channel_map *out_map,
> 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);
> @@ -152,8 +156,9 @@ void pa_speex_ec_done(pa_echo_canceller *ec);
> #ifdef HAVE_ADRIAN_EC
> /* Adrian Andre's echo canceller */
> 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,
> + pa_sample_spec *rec_ss, pa_channel_map *rec_map,
> + pa_sample_spec *play_ss, pa_channel_map *play_map,
> + pa_sample_spec *out_ss, pa_channel_map *out_map,
Indented one space too little.
> 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);
> @@ -163,9 +168,10 @@ void pa_adrian_ec_done(pa_echo_canceller *ec);
> /* WebRTC canceller functions */
> 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 *nframes, const char *args);
> + pa_sample_spec *rec_ss, pa_channel_map *rec_map,
> + pa_sample_spec *play_ss, pa_channel_map *play_map,
> + pa_sample_spec *out_ss, pa_channel_map *out_map,
> + uint32_t *nframes, const char *args);
Indented one space too little.
> 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 d6feaa4..a140902 100644
> --- a/src/modules/echo-cancel/module-echo-cancel.c
> +++ b/src/modules/echo-cancel/module-echo-cancel.c
> @@ -211,6 +211,7 @@ struct userdata {
> pa_bool_t save_aec;
>
> pa_echo_canceller *ec;
> + uint32_t source_output_blocksize;
> uint32_t source_blocksize;
> uint32_t sink_blocksize;
>
> @@ -420,7 +421,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->source_blocksize, &u->source_output->source->sample_spec);
> + pa_bytes_to_usec(u->source_output_blocksize, &u->source_output->source->sample_spec);
>
> return 0;
>
> @@ -739,7 +740,7 @@ static void do_push_drift_comp(struct userdata *u) {
> */
> drift = ((float)(plen - u->sink_rem) - (rlen - u->source_rem)) / ((float)(rlen - u->source_rem));
> u->sink_rem = plen % u->sink_blocksize;
> - u->source_rem = rlen % u->source_blocksize;
> + u->source_rem = rlen % u->source_output_blocksize;
>
> /* Now let the canceller work its drift compensation magic */
> u->ec->set_drift(u->ec, drift);
> @@ -772,14 +773,14 @@ static void do_push_drift_comp(struct userdata *u) {
> }
>
> /* And now the capture samples */
> - while (rlen >= u->source_blocksize) {
> - pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_blocksize, &rchunk);
> + while (rlen >= u->source_output_blocksize) {
> + pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_output_blocksize, &rchunk);
>
> rdata = pa_memblock_acquire(rchunk.memblock);
> rdata += rchunk.index;
>
> cchunk.index = 0;
> - cchunk.length = u->source_blocksize;
> + cchunk.length = u->source_output_blocksize;
> cchunk.memblock = pa_memblock_new(u->source->core->mempool, cchunk.length);
> cdata = pa_memblock_acquire(cchunk.memblock);
>
> @@ -787,11 +788,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->source_blocksize);
> + fprintf(u->drift_file, "c %d\n", u->source_output_blocksize);
> if (u->captured_file)
> - unused = fwrite(rdata, 1, u->source_blocksize, u->captured_file);
> + unused = fwrite(rdata, 1, u->source_output_blocksize, u->captured_file);
> if (u->canceled_file)
> - unused = fwrite(cdata, 1, u->source_blocksize, u->canceled_file);
> + unused = fwrite(cdata, 1, u->source_output_blocksize, u->canceled_file);
> }
>
> pa_memblock_release(cchunk.memblock);
> @@ -802,8 +803,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->source_blocksize);
> - rlen -= u->source_blocksize;
> + pa_memblockq_drop(u->source_memblockq, u->source_output_blocksize);
> + rlen -= u->source_output_blocksize;
> }
> }
>
> @@ -821,10 +822,9 @@ 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->source_blocksize) {
> -
> + while (rlen >= u->source_output_blocksize) {
> /* take fixed block from recorded samples */
> - pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_blocksize, &rchunk);
> + pa_memblockq_peek_fixed_size(u->source_memblockq, u->source_output_blocksize, &rchunk);
>
> if (plen >= u->sink_blocksize) {
> /* take fixed block from played samples */
> @@ -852,7 +852,7 @@ static void do_push(struct userdata *u) {
>
> if (u->save_aec) {
> if (u->captured_file)
> - unused = fwrite(rdata, 1, u->source_blocksize, u->captured_file);
> + unused = fwrite(rdata, 1, u->source_output_blocksize, u->captured_file);
> if (u->played_file)
> unused = fwrite(pdata, 1, u->sink_blocksize, u->played_file);
> }
> @@ -870,9 +870,9 @@ static void do_push(struct userdata *u) {
> pa_memblock_release(rchunk.memblock);
>
> /* drop consumed source samples */
> - pa_memblockq_drop(u->source_memblockq, u->source_blocksize);
> + pa_memblockq_drop(u->source_memblockq, u->source_output_blocksize);
> pa_memblock_unref(rchunk.memblock);
> - rlen -= u->source_blocksize;
> + rlen -= u->source_output_blocksize;
>
> if (plen >= u->sink_blocksize) {
> /* drop consumed sink samples */
> @@ -918,7 +918,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->source_blocksize)
> + if (rlen < u->source_output_blocksize)
> return;
>
> /* See if we need to drop samples in order to sync */
> @@ -934,7 +934,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->source_blocksize;
> + to_skip -= to_skip % u->source_output_blocksize;
>
> if (to_skip) {
> pa_memblockq_peek_fixed_size(u->source_memblockq, to_skip, &rchunk);
> @@ -947,9 +947,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->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);
> + if (rlen && u->source_skip % u->source_output_blocksize) {
> + u->sink_skip += (uint64_t) (u->source_output_blocksize - (u->source_skip % u->source_output_blocksize)) * u->sink_blocksize / u->source_output_blocksize;
> + u->source_skip -= (u->source_skip % u->source_output_blocksize);
> }
> }
>
> @@ -1640,8 +1640,8 @@ fail:
> /* Called from main context. */
> int pa__init(pa_module*m) {
> struct userdata *u;
> - pa_sample_spec source_ss, sink_ss;
> - pa_channel_map source_map, sink_map;
> + pa_sample_spec source_output_ss, source_ss, sink_ss;
> + pa_channel_map source_output_map, source_map, sink_map;
> pa_modargs *ma;
> pa_source *source_master=NULL;
> pa_sink *sink_master=NULL;
> @@ -1740,12 +1740,16 @@ int pa__init(pa_module*m) {
> u->asyncmsgq = pa_asyncmsgq_new(0);
> u->need_realign = TRUE;
>
> + source_output_ss = source_ss;
> + source_output_map = source_map;
> +
> 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))) {
> + if (!u->ec->init(u->core, u->ec, &source_output_ss, &source_output_map, &sink_ss, &sink_map, &source_ss, &source_map, &nframes, pa_modargs_get_value(ma, "aec_args", NULL))) {
> pa_log("Failed to init AEC engine");
> goto fail;
> }
>
> + u->source_output_blocksize = nframes * pa_frame_size(&source_output_ss);
> u->source_blocksize = nframes * pa_frame_size(&source_ss);
> u->sink_blocksize = nframes * pa_frame_size(&sink_ss);
>
> @@ -1864,8 +1868,8 @@ int pa__init(pa_module*m) {
>
> pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Source Stream");
> pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
> - pa_source_output_new_data_set_sample_spec(&source_output_data, &source_ss);
> - pa_source_output_new_data_set_channel_map(&source_output_data, &source_map);
> + pa_source_output_new_data_set_sample_spec(&source_output_data, &source_output_ss);
> + pa_source_output_new_data_set_channel_map(&source_output_data, &source_output_map);
>
> pa_source_output_new(&u->source_output, m->core, &source_output_data);
> pa_source_output_new_data_done(&source_output_data);
> @@ -1932,7 +1936,7 @@ int pa__init(pa_module*m) {
> pa_sink_input_get_silence(u->sink_input, &u->silence);
>
> u->source_memblockq = pa_memblockq_new("module-echo-cancel source_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0,
> - &source_ss, 1, 1, 0, &u->silence);
> + &source_output_ss, 1, 1, 0, &u->silence);
> u->sink_memblockq = pa_memblockq_new("module-echo-cancel sink_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0,
> &sink_ss, 1, 1, 0, &u->silence);
>
> @@ -2076,8 +2080,8 @@ void pa__done(pa_module*m) {
> */
> int main(int argc, char* argv[]) {
> struct userdata u;
> - pa_sample_spec source_ss, sink_ss;
> - pa_channel_map source_map, sink_map;
> + pa_sample_spec source_output_ss, source_ss, sink_ss;
> + pa_channel_map source_output_map, source_map, sink_map;
> pa_modargs *ma = NULL;
> uint8_t *rdata = NULL, *pdata = NULL, *cdata = NULL;
> int unused PA_GCC_UNUSED;
> @@ -2130,11 +2134,15 @@ 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, &nframes,
> + source_output_ss = source_ss;
> + source_output_map = source_map;
> +
> + if (!u.ec->init(u.core, u.ec, &source_output_ss, &source_output_map, &sink_ss, &sink_map, &source_ss, &source_map, &nframes,
> (argc > 5) ? argv[5] : NULL )) {
> pa_log("Failed to init AEC engine");
> goto fail;
> }
> + u.source_output_blocksize = nframes * pa_frame_size(&source_output_ss);
> u.source_blocksize = nframes * pa_frame_size(&source_ss);
> u.sink_blocksize = nframes * pa_frame_size(&sink_ss);
>
> @@ -2152,12 +2160,12 @@ int main(int argc, char* argv[]) {
> }
> }
>
> - rdata = pa_xmalloc(u.source_blocksize);
> + rdata = pa_xmalloc(u.source_output_blocksize);
> pdata = pa_xmalloc(u.sink_blocksize);
> cdata = pa_xmalloc(u.source_blocksize);
>
> if (!u.ec->params.drift_compensation) {
> - while (fread(rdata, u.source_blocksize, 1, u.captured_file) > 0) {
> + while (fread(rdata, u.source_output_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;
> diff --git a/src/modules/echo-cancel/null.c b/src/modules/echo-cancel/null.c
> index 6d9531a..3fb5e8d 100644
> --- a/src/modules/echo-cancel/null.c
> +++ b/src/modules/echo-cancel/null.c
> @@ -26,18 +26,23 @@ PA_C_DECL_BEGIN
> 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,
> + pa_sample_spec *rec_ss, pa_channel_map *rec_map,
> + pa_sample_spec *play_ss, pa_channel_map *play_map,
> + pa_sample_spec *out_ss, pa_channel_map *out_map,
> uint32_t *nframes, const char *args) {
Indented one space too much.
> char strss_source[PA_SAMPLE_SPEC_SNPRINT_MAX];
> char strss_sink[PA_SAMPLE_SPEC_SNPRINT_MAX];
>
> *nframes = 256;
> - ec->params.priv.null.source_ss = *source_ss;
> + ec->params.priv.null.out_ss = *out_ss;
> +
> + /* Isn't required as rec_{ss,map} is initialized to out_{ss,map}
> + *rec_ss = *out_ss;
> + *rec_map = *out_map; */
Either use assertions or just have the code there uncommented (it may
not be a good idea to rely on the caller to initialize the variables in
any particular way, so I'd uncomment the code).
>
> pa_log_debug("null AEC: nframes=%u, sample spec source=%s, sample spec sink=%s", *nframes,
> - pa_sample_spec_snprint(strss_source, sizeof(strss_source), source_ss),
> - pa_sample_spec_snprint(strss_sink, sizeof(strss_sink), sink_ss));
> + pa_sample_spec_snprint(strss_source, sizeof(strss_source), out_ss),
> + pa_sample_spec_snprint(strss_sink, sizeof(strss_sink), play_ss));
Indentation could be increased to match the opening parenthesis.
--
Tanu
More information about the pulseaudio-discuss
mailing list