From: Jaechul Lee Date: Thu, 30 Jun 2022 04:10:12 +0000 (+0900) Subject: echo-cancel: Replace 'algo' prefix with 'method' X-Git-Tag: submit/tizen/20220704.084748~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ae4ac2a78f77f549956f504f2bc13f003ba2bc4f;p=platform%2Fcore%2Fmultimedia%2Fpulseaudio-modules-tizen.git echo-cancel: Replace 'algo' prefix with 'method' [Version] 15.0.23 [Issue Type] Rename Change-Id: I8eee586036ef0f107f94413285b22a5736284318 Signed-off-by: Jaechul Lee --- diff --git a/Makefile.am b/Makefile.am index b3d0913..cd71bb1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,9 +98,9 @@ module_tizenaudio_source2_la_LIBADD = $(MODULE_LIBADD) libtizenaudio-util.la module_tizenaudio_source2_la_CFLAGS = $(MODULE_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_source2 libprocessor_la_SOURCES = \ - src/echo-cancel/algo_speex.c \ - src/echo-cancel/algo_reference_copy.c \ - src/echo-cancel/algo_adrian.c \ + src/echo-cancel/method_speex.c \ + src/echo-cancel/method_reference_copy.c \ + src/echo-cancel/method_adrian.c \ src/echo-cancel/adrian-aec.c \ src/echo-cancel/processor.c \ src/echo-cancel/processor.h \ @@ -110,7 +110,7 @@ libprocessor_la_LIBADD = $(AM_LIBADD) $(LIBSPEEX_LIBS) libprocessor_la_CFLAGS = $(AM_CFLAGS) $(PA_CFLAGS) $(LIBSPEEX_CFLAGS) if ENABLE_WEBRTC -libprocessor_la_SOURCES += src/echo-cancel/algo_webrtc.cpp +libprocessor_la_SOURCES += src/echo-cancel/method_webrtc.cpp libprocessor_la_LIBADD += $(WEBRTC_LIBS) libprocessor_la_CPPFLAGS = $(WEBRTC_CFLAGS) $(PA_CFLAGS) -DSUPPORT_METHOD_WEBRTC -std=c++17 endif diff --git a/src/echo-cancel/algo_adrian.c b/src/echo-cancel/algo_adrian.c deleted file mode 100644 index a5d2e72..0000000 --- a/src/echo-cancel/algo_adrian.c +++ /dev/null @@ -1,93 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2021 Jaechul Lee - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "adrian-aec.h" - -struct ec_adrian { - int blocksize; - AEC *aec; -}; - -void *adrian_create(size_t nframes, pa_sample_spec *ss) { - struct ec_adrian *adrian = NULL; - - pa_assert(ss); - - if (ss->channels > 2 || ss->format != PA_SAMPLE_S16LE) { - pa_log_error("Invalid channels(%d) or format(%d)", ss->channels, ss->format); - return NULL; - } - - adrian = pa_xnew0(struct ec_adrian, 1); - - if (!(adrian->aec = AEC_init(ss->rate, 0))) { - pa_log_error("Failed to init AEC"); - goto fail; - } - adrian->blocksize = nframes * ss->channels * 2; /* format */ - - return adrian; - -fail: - pa_xfree(adrian); - - return NULL; -} - -int32_t adrian_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { - struct ec_adrian *adrian = priv; - int i; - - assert(rec); - assert(ref); - assert(out); - - for (i=0; iblocksize; i+=2) { - int r = *(int16_t *)(rec + i); - int p = *(int16_t *)(ref + i); - *(int16_t *)(out + i) = (int16_t) AEC_doAEC(adrian->aec, r, p); - } - - return 0; -} - -int32_t adrian_destroy(void *priv) { - struct ec_adrian *adrian = priv; - - pa_assert(adrian); - - AEC_done(adrian->aec); - - pa_xfree(adrian); - - return 0; -} diff --git a/src/echo-cancel/algo_reference_copy.c b/src/echo-cancel/algo_reference_copy.c deleted file mode 100644 index 7b397ec..0000000 --- a/src/echo-cancel/algo_reference_copy.c +++ /dev/null @@ -1,115 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2022 Jaechul Lee - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -struct reference_copy { - size_t nframes; - pa_sample_spec ss; - int reference_channels; -}; - -void *reference_copy_create(size_t nframes, pa_sample_spec *ss) { - struct reference_copy *rc; - - pa_assert(ss); - - rc = pa_xnew0(struct reference_copy, 1); - rc->nframes = nframes; - rc->ss = *ss; - - return rc; -} - -int32_t reference_copy_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { - struct reference_copy *rc = priv; - size_t rec_bytes, ref_bytes, actual_bytes, total_bytes; - int8_t *dst = out; - - pa_assert(rc); - pa_assert(rec); - pa_assert(ref); - pa_assert(out); - - rec_bytes = pa_frame_size(&rc->ss); - ref_bytes = rc->reference_channels * pa_sample_size(&rc->ss); - actual_bytes = rec_bytes - ref_bytes; - total_bytes = rec_bytes * rc->nframes; - - while ((dst - out) < total_bytes) { - memcpy(dst, rec, actual_bytes); - memcpy(dst + actual_bytes, ref, ref_bytes); - dst += rec_bytes; - rec += rec_bytes; - ref += ref_bytes; - } - - return 0; -} - -int32_t reference_copy_destroy(void *priv) { - struct reference_copy *rc = priv; - - pa_assert(rc); - - pa_xfree(rc); - - return 0; -} - -int32_t reference_copy_change_reference_spec(void *priv, pa_sample_spec *source_ss, pa_sample_spec *sample_spec, pa_channel_map *map) { - struct reference_copy *rc = priv; - int channels; - - pa_assert(rc); - pa_assert(source_ss); - pa_assert(sample_spec); - pa_assert(map); - - channels = rc->ss.channels - source_ss->channels; - if (channels <= 0) { - pa_log_error("No empty channels. reference copy will be disabled"); - return -1; - } - - /* TODO: temporary code for supporting mchstreamer. reference copy uses the last 2ch of its channels */ - if (rc->ss.channels == 16) - channels = 2; - - *sample_spec = rc->ss; - sample_spec->channels = rc->reference_channels = channels; - - pa_channel_map_init_auto(map, channels, PA_CHANNEL_MAP_AIFF); - - pa_log_info("reference will be copied to empty channels(%d). source-output ch(%d), source ch(%d)", - channels, rc->ss.channels, source_ss->channels); - - return 0; -} diff --git a/src/echo-cancel/algo_speex.c b/src/echo-cancel/algo_speex.c deleted file mode 100644 index 161812a..0000000 --- a/src/echo-cancel/algo_speex.c +++ /dev/null @@ -1,138 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2021 Jaechul Lee - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -struct algo_speex { - SpeexEchoState *echo_state; - SpeexPreprocessState *preprocess; -}; - -void *speex_create(size_t nframes, pa_sample_spec *ss) { - struct algo_speex *speex = NULL; - spx_int32_t value = 1; - int rate; - - pa_assert(ss); - - if (ss->channels > 2 || ss->format != PA_SAMPLE_S16LE) { - pa_log_error("Invalid channels(%d) or format(%d)", ss->channels, ss->format); - return NULL; - } - - speex = pa_xnew0(struct algo_speex, 1); - - /* TODO: need to check. weird behavior */ - if (ss->channels == 2) - nframes *= 2; - - speex->echo_state = speex_echo_state_init(nframes, nframes * 10); - - if (!speex->echo_state) { - pa_log_error("_echo_state_init_mc failed"); - goto fail; - } - - rate = ss->rate; - if (!(speex->preprocess = speex_preprocess_state_init(nframes, rate))) { - pa_log_error("_preprocess_state_init failed"); - goto fail; - } - - if (speex_echo_ctl(speex->echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate)) { - pa_log_error("_echo_ctl SET_SAMPLING_RATE failed"); - goto fail; - } - - if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_AGC, &value)) { - pa_log_error("_echo_ctl SPEEX_PREPROCESS_SET_AGC failed"); - goto fail; - } - - if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_DENOISE, &value)) { - pa_log_error("_echo_ctl SPEEX_PREPROCESS_SET_DENOISE failed"); - goto fail; - } - - if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, &value)) { - pa_log_error("_echo_ctl SPEEX_PREPROCESS_SET_DEREVERB failed"); - goto fail; - } - - if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, speex->echo_state)) { - pa_log_error("_echo_ctl SET_ECHO_STATE failed"); - goto fail; - } - - pa_log_info("speex echo-canceller initialized. frame(%zu), rate(%d) channels(%d)", - nframes, ss->rate, ss->channels); - - return speex; - -fail: - pa_xfree(speex); - - return NULL; -} - -int32_t speex_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { - struct algo_speex *speex = priv; - - assert(rec); - assert(ref); - assert(out); - - speex_echo_cancellation(speex->echo_state, - (const spx_int16_t *)rec, - (const spx_int16_t *)ref, - (spx_int16_t *)out); - - speex_preprocess_run(speex->preprocess, (spx_int16_t *)out); - - return 0; -} - -int32_t speex_destroy(void *priv) { - struct algo_speex *speex = priv; - - if (speex->echo_state) - speex_echo_state_destroy(speex->echo_state); - if (speex->preprocess) - speex_preprocess_state_destroy(speex->preprocess); - - pa_xfree(speex); - - return 0; -} diff --git a/src/echo-cancel/algo_webrtc.cpp b/src/echo-cancel/algo_webrtc.cpp deleted file mode 100644 index 8253820..0000000 --- a/src/echo-cancel/algo_webrtc.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2021 Jaechul Lee - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#define DEFAULT_PROCESS_SIZE_MS 10 - -using namespace webrtc; - -PA_C_DECL_BEGIN -#include -#include -#include -#include -#include -#include -#include -#include - -static void allocate_stream_buffer(struct algo_webrtc *webrtc, size_t nframes); -static void deallocate_stream_buffer(struct algo_webrtc *webrtc); -static void convert_s16_to_float(float *dst, int16_t *src, size_t n); -static void convert_float_to_s16(int16_t *dst, float *src, size_t n); - -void *webrtc_audio_create(size_t nframes, pa_sample_spec *ss); -int32_t webrtc_audio_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out); -int32_t webrtc_audio_destroy(void *priv); -PA_C_DECL_END - -struct algo_webrtc { - AudioProcessing *ap; - Config config; - StreamConfig *sconfig; - - float *rec_fbuf; - float *rec_dbuf[PA_CHANNELS_MAX]; - - float *ref_fbuf; - float *ref_dbuf[PA_CHANNELS_MAX]; - - float *out_fbuf; - float *out_dbuf[PA_CHANNELS_MAX]; - - pa_sample_spec ss; - size_t frames; - - /* Currently, webrtc uses fixed size(10ms) buffer */ - int loops; - size_t fixed_bytes; - size_t fixed_frames; -}; - -void *webrtc_audio_create(size_t nframes, pa_sample_spec *ss) { - struct algo_webrtc *webrtc = NULL; - size_t fixed_bytes, request_bytes; - Config config; - - pa_assert(ss); - - if (ss->channels > 2 || ss->format != PA_SAMPLE_S16LE) { - pa_log_error("Invalid channels (%d) or format (%d)", ss->channels, ss->format); - return NULL; - } - - fixed_bytes = pa_usec_to_bytes(DEFAULT_PROCESS_SIZE_MS * PA_USEC_PER_MSEC, ss); - request_bytes = nframes * pa_frame_size(ss); - - if (fixed_bytes > request_bytes) { - pa_log_error("nframes should be bigger than %dms. nframes(%zu) request_bytes(%zu)", - DEFAULT_PROCESS_SIZE_MS, nframes, request_bytes); - return NULL; - } - - if (request_bytes % fixed_bytes) { - pa_log_error("request_bytes(%zu) should be multiples of fixed_bytes(%zu)", - nframes, request_bytes); - return NULL; - } - - webrtc = pa_xnew0(struct algo_webrtc, 1); - webrtc->ss = *ss; - webrtc->fixed_bytes = fixed_bytes; - webrtc->fixed_frames = fixed_bytes / pa_frame_size(ss); - webrtc->loops = request_bytes / fixed_bytes; - - config.Set(new ExperimentalNs(false)); - config.Set(new Intelligibility(false)); - config.Set(new ExperimentalAgc(false)); - - webrtc->ap = AudioProcessing::Create(config); - if (!webrtc->ap) { - pa_log_error("Failed to create audio processing"); - goto fail; - } - - webrtc->ap->echo_cancellation()->Enable(true); - webrtc->ap->noise_suppression()->Enable(true); - webrtc->ap->noise_suppression()->set_level(static_cast(1)); - - webrtc->ap->gain_control()->set_mode(GainControl::kAdaptiveDigital); - //webrtc->ap->gain_control()->set_target_level_dbfs(30); - //webrtc->ap->gain_control()->set_stream_analog_level(30); - webrtc->ap->echo_cancellation()->set_suppression_level(static_cast(1)); - - webrtc->sconfig = new StreamConfig(ss->rate, ss->channels, false); - if (!webrtc->sconfig) { - pa_log_error("Failed to create stream config"); - goto fail; - } - - webrtc->sconfig->set_sample_rate_hz(ss->rate); - webrtc->sconfig->set_num_channels(ss->channels); - - /* webrtc supports 10ms by default */ - webrtc->frames = webrtc->sconfig->num_frames(); - if (webrtc->frames != webrtc->fixed_frames) { - pa_log_error("Failed to set frames. frames(%zu), fixed_frames(%zu)", - webrtc->frames, webrtc->fixed_frames); - goto fail; - } - - allocate_stream_buffer(webrtc, webrtc->fixed_frames); - - pa_log_info("webrtc processes request block(%llumsec) block(%llumsec) n(%d)\n", - pa_bytes_to_usec(request_bytes, ss) / PA_USEC_PER_MSEC, - pa_bytes_to_usec(fixed_bytes, ss) / PA_USEC_PER_MSEC, - webrtc->loops); - - return webrtc; - -fail: - if (webrtc->ap) - delete webrtc->ap; - if (webrtc->sconfig) - delete webrtc->sconfig; - - pa_xfree(webrtc); - - return NULL; -} - -int32_t webrtc_audio_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { - struct algo_webrtc *webrtc = (struct algo_webrtc *)priv; - pa_sample_spec ss; - size_t frames; - - pa_assert(webrtc); - pa_assert(rec); - pa_assert(ref); - pa_assert(out); - - ss.format = PA_SAMPLE_FLOAT32LE; - ss.rate = webrtc->ss.rate; - ss.channels = webrtc->ss.channels; - - frames = webrtc->fixed_frames; - - for (int i = 0; i < webrtc->loops; i++) { - int ret; - - convert_s16_to_float(webrtc->ref_fbuf, (int16_t *)ref, frames * ss.channels); - pa_deinterleave(webrtc->ref_fbuf, (void **)webrtc->ref_dbuf, ss.channels, - pa_sample_size(&ss), frames); - - /* reference */ - ret = webrtc->ap->ProcessReverseStream(webrtc->ref_dbuf, - *webrtc->sconfig, - *webrtc->sconfig, - webrtc->ref_dbuf); - if (ret != AudioProcessing::kNoError) { - pa_log_error("Failed to process reverse stream"); - return -1; - } - - webrtc->ap->set_stream_delay_ms(0); - - /* capture */ - convert_s16_to_float(webrtc->rec_fbuf, (int16_t *)rec, frames * ss.channels); - pa_deinterleave(webrtc->rec_fbuf, (void **)webrtc->rec_dbuf, ss.channels, - pa_sample_size(&ss), frames); - - ret = webrtc->ap->ProcessStream(webrtc->rec_dbuf, - *webrtc->sconfig, - *webrtc->sconfig, - webrtc->out_dbuf); - if (ret != AudioProcessing::kNoError) { - pa_log_error("Failed to process stream"); - return -1; - } - - pa_interleave((const void **)webrtc->out_dbuf, ss.channels, webrtc->out_fbuf, - pa_sample_size(&ss), frames); - convert_float_to_s16((int16_t *)out, webrtc->out_fbuf, frames * ss.channels); - - rec += webrtc->fixed_bytes; - ref += webrtc->fixed_bytes; - out += webrtc->fixed_bytes; - } - - return 0; -} - -int32_t webrtc_audio_destroy(void *priv) { - struct algo_webrtc *webrtc = (struct algo_webrtc *)priv; - - pa_assert(webrtc); - - delete webrtc->sconfig; - delete webrtc->ap; - - deallocate_stream_buffer(webrtc); - pa_xfree(webrtc); - - return 0; -} - -static void deallocate_stream_buffer(struct algo_webrtc *webrtc) { - pa_assert(webrtc); - - for (int i = 0; i < webrtc->ss.channels; i++) { - pa_xfree(webrtc->rec_dbuf[i]); - pa_xfree(webrtc->ref_dbuf[i]); - pa_xfree(webrtc->out_dbuf[i]); - } - - pa_xfree(webrtc->rec_fbuf); - pa_xfree(webrtc->ref_fbuf); - pa_xfree(webrtc->out_fbuf); -} - -static void allocate_stream_buffer(struct algo_webrtc *webrtc, size_t nframes) { - int channels; - - pa_assert(webrtc); - - channels = webrtc->ss.channels; - - webrtc->rec_fbuf = pa_xnew(float, nframes * channels); - webrtc->ref_fbuf = pa_xnew(float, nframes * channels); - webrtc->out_fbuf = pa_xnew(float, nframes * channels); - - for (int i = 0; i < channels; i++) { - webrtc->rec_dbuf[i] = pa_xnew(float, nframes); - webrtc->ref_dbuf[i] = pa_xnew(float, nframes); - webrtc->out_dbuf[i] = pa_xnew(float, nframes); - } -} - -static void convert_s16_to_float(float *dst, int16_t *src, size_t n) { - pa_assert(dst); - pa_assert(src); - - ((pa_convert_func_t)pa_get_convert_to_float32ne_function(PA_SAMPLE_S16LE))(n, src, dst); -} - -static void convert_float_to_s16(int16_t *dst, float *src, size_t n) { - pa_assert(dst); - pa_assert(src); - - ((pa_convert_func_t)pa_get_convert_to_s16ne_function(PA_SAMPLE_FLOAT32LE))(n, src, dst); -} - diff --git a/src/echo-cancel/method_adrian.c b/src/echo-cancel/method_adrian.c new file mode 100644 index 0000000..d673dcd --- /dev/null +++ b/src/echo-cancel/method_adrian.c @@ -0,0 +1,93 @@ +/*** + This file is part of PulseAudio. + + Copyright 2021 Jaechul Lee + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "adrian-aec.h" + +struct method_adrian { + int blocksize; + AEC *aec; +}; + +void *adrian_create(size_t nframes, pa_sample_spec *ss) { + struct method_adrian *adrian = NULL; + + pa_assert(ss); + + if (ss->channels > 2 || ss->format != PA_SAMPLE_S16LE) { + pa_log_error("Invalid channels(%d) or format(%d)", ss->channels, ss->format); + return NULL; + } + + adrian = pa_xnew0(struct method_adrian, 1); + + if (!(adrian->aec = AEC_init(ss->rate, 0))) { + pa_log_error("Failed to init AEC"); + goto fail; + } + adrian->blocksize = nframes * ss->channels * 2; /* format */ + + return adrian; + +fail: + pa_xfree(adrian); + + return NULL; +} + +int32_t adrian_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { + struct method_adrian *adrian = priv; + int i; + + assert(rec); + assert(ref); + assert(out); + + for (i=0; iblocksize; i+=2) { + int r = *(int16_t *)(rec + i); + int p = *(int16_t *)(ref + i); + *(int16_t *)(out + i) = (int16_t) AEC_doAEC(adrian->aec, r, p); + } + + return 0; +} + +int32_t adrian_destroy(void *priv) { + struct method_adrian *adrian = priv; + + pa_assert(adrian); + + AEC_done(adrian->aec); + + pa_xfree(adrian); + + return 0; +} diff --git a/src/echo-cancel/method_reference_copy.c b/src/echo-cancel/method_reference_copy.c new file mode 100644 index 0000000..7b397ec --- /dev/null +++ b/src/echo-cancel/method_reference_copy.c @@ -0,0 +1,115 @@ +/*** + This file is part of PulseAudio. + + Copyright 2022 Jaechul Lee + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +struct reference_copy { + size_t nframes; + pa_sample_spec ss; + int reference_channels; +}; + +void *reference_copy_create(size_t nframes, pa_sample_spec *ss) { + struct reference_copy *rc; + + pa_assert(ss); + + rc = pa_xnew0(struct reference_copy, 1); + rc->nframes = nframes; + rc->ss = *ss; + + return rc; +} + +int32_t reference_copy_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { + struct reference_copy *rc = priv; + size_t rec_bytes, ref_bytes, actual_bytes, total_bytes; + int8_t *dst = out; + + pa_assert(rc); + pa_assert(rec); + pa_assert(ref); + pa_assert(out); + + rec_bytes = pa_frame_size(&rc->ss); + ref_bytes = rc->reference_channels * pa_sample_size(&rc->ss); + actual_bytes = rec_bytes - ref_bytes; + total_bytes = rec_bytes * rc->nframes; + + while ((dst - out) < total_bytes) { + memcpy(dst, rec, actual_bytes); + memcpy(dst + actual_bytes, ref, ref_bytes); + dst += rec_bytes; + rec += rec_bytes; + ref += ref_bytes; + } + + return 0; +} + +int32_t reference_copy_destroy(void *priv) { + struct reference_copy *rc = priv; + + pa_assert(rc); + + pa_xfree(rc); + + return 0; +} + +int32_t reference_copy_change_reference_spec(void *priv, pa_sample_spec *source_ss, pa_sample_spec *sample_spec, pa_channel_map *map) { + struct reference_copy *rc = priv; + int channels; + + pa_assert(rc); + pa_assert(source_ss); + pa_assert(sample_spec); + pa_assert(map); + + channels = rc->ss.channels - source_ss->channels; + if (channels <= 0) { + pa_log_error("No empty channels. reference copy will be disabled"); + return -1; + } + + /* TODO: temporary code for supporting mchstreamer. reference copy uses the last 2ch of its channels */ + if (rc->ss.channels == 16) + channels = 2; + + *sample_spec = rc->ss; + sample_spec->channels = rc->reference_channels = channels; + + pa_channel_map_init_auto(map, channels, PA_CHANNEL_MAP_AIFF); + + pa_log_info("reference will be copied to empty channels(%d). source-output ch(%d), source ch(%d)", + channels, rc->ss.channels, source_ss->channels); + + return 0; +} diff --git a/src/echo-cancel/method_speex.c b/src/echo-cancel/method_speex.c new file mode 100644 index 0000000..5dd0b5f --- /dev/null +++ b/src/echo-cancel/method_speex.c @@ -0,0 +1,138 @@ +/*** + This file is part of PulseAudio. + + Copyright 2021 Jaechul Lee + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct method_speex { + SpeexEchoState *echo_state; + SpeexPreprocessState *preprocess; +}; + +void *speex_create(size_t nframes, pa_sample_spec *ss) { + struct method_speex *speex = NULL; + spx_int32_t value = 1; + int rate; + + pa_assert(ss); + + if (ss->channels > 2 || ss->format != PA_SAMPLE_S16LE) { + pa_log_error("Invalid channels(%d) or format(%d)", ss->channels, ss->format); + return NULL; + } + + speex = pa_xnew0(struct method_speex, 1); + + /* TODO: need to check. weird behavior */ + if (ss->channels == 2) + nframes *= 2; + + speex->echo_state = speex_echo_state_init(nframes, nframes * 10); + + if (!speex->echo_state) { + pa_log_error("_echo_state_init_mc failed"); + goto fail; + } + + rate = ss->rate; + if (!(speex->preprocess = speex_preprocess_state_init(nframes, rate))) { + pa_log_error("_preprocess_state_init failed"); + goto fail; + } + + if (speex_echo_ctl(speex->echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate)) { + pa_log_error("_echo_ctl SET_SAMPLING_RATE failed"); + goto fail; + } + + if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_AGC, &value)) { + pa_log_error("_echo_ctl SPEEX_PREPROCESS_SET_AGC failed"); + goto fail; + } + + if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_DENOISE, &value)) { + pa_log_error("_echo_ctl SPEEX_PREPROCESS_SET_DENOISE failed"); + goto fail; + } + + if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, &value)) { + pa_log_error("_echo_ctl SPEEX_PREPROCESS_SET_DEREVERB failed"); + goto fail; + } + + if (speex_preprocess_ctl(speex->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, speex->echo_state)) { + pa_log_error("_echo_ctl SET_ECHO_STATE failed"); + goto fail; + } + + pa_log_info("speex echo-canceller initialized. frame(%zu), rate(%d) channels(%d)", + nframes, ss->rate, ss->channels); + + return speex; + +fail: + pa_xfree(speex); + + return NULL; +} + +int32_t speex_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { + struct method_speex *speex = priv; + + assert(rec); + assert(ref); + assert(out); + + speex_echo_cancellation(speex->echo_state, + (const spx_int16_t *)rec, + (const spx_int16_t *)ref, + (spx_int16_t *)out); + + speex_preprocess_run(speex->preprocess, (spx_int16_t *)out); + + return 0; +} + +int32_t speex_destroy(void *priv) { + struct method_speex *speex = priv; + + if (speex->echo_state) + speex_echo_state_destroy(speex->echo_state); + if (speex->preprocess) + speex_preprocess_state_destroy(speex->preprocess); + + pa_xfree(speex); + + return 0; +} diff --git a/src/echo-cancel/method_webrtc.cpp b/src/echo-cancel/method_webrtc.cpp new file mode 100644 index 0000000..4b7721c --- /dev/null +++ b/src/echo-cancel/method_webrtc.cpp @@ -0,0 +1,286 @@ +/*** + This file is part of PulseAudio. + + Copyright 2021 Jaechul Lee + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#define DEFAULT_PROCESS_SIZE_MS 10 + +using namespace webrtc; + +PA_C_DECL_BEGIN +#include +#include +#include +#include +#include +#include +#include +#include + +static void allocate_stream_buffer(struct method_webrtc *webrtc, size_t nframes); +static void deallocate_stream_buffer(struct method_webrtc *webrtc); +static void convert_s16_to_float(float *dst, int16_t *src, size_t n); +static void convert_float_to_s16(int16_t *dst, float *src, size_t n); + +void *webrtc_audio_create(size_t nframes, pa_sample_spec *ss); +int32_t webrtc_audio_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out); +int32_t webrtc_audio_destroy(void *priv); +PA_C_DECL_END + +struct method_webrtc { + AudioProcessing *ap; + Config config; + StreamConfig *sconfig; + + float *rec_fbuf; + float *rec_dbuf[PA_CHANNELS_MAX]; + + float *ref_fbuf; + float *ref_dbuf[PA_CHANNELS_MAX]; + + float *out_fbuf; + float *out_dbuf[PA_CHANNELS_MAX]; + + pa_sample_spec ss; + size_t frames; + + /* Currently, webrtc uses fixed size(10ms) buffer */ + int loops; + size_t fixed_bytes; + size_t fixed_frames; +}; + +void *webrtc_audio_create(size_t nframes, pa_sample_spec *ss) { + struct method_webrtc *webrtc = NULL; + size_t fixed_bytes, request_bytes; + Config config; + + pa_assert(ss); + + if (ss->channels > 2 || ss->format != PA_SAMPLE_S16LE) { + pa_log_error("Invalid channels (%d) or format (%d)", ss->channels, ss->format); + return NULL; + } + + fixed_bytes = pa_usec_to_bytes(DEFAULT_PROCESS_SIZE_MS * PA_USEC_PER_MSEC, ss); + request_bytes = nframes * pa_frame_size(ss); + + if (fixed_bytes > request_bytes) { + pa_log_error("nframes should be bigger than %dms. nframes(%zu) request_bytes(%zu)", + DEFAULT_PROCESS_SIZE_MS, nframes, request_bytes); + return NULL; + } + + if (request_bytes % fixed_bytes) { + pa_log_error("request_bytes(%zu) should be multiples of fixed_bytes(%zu)", + nframes, request_bytes); + return NULL; + } + + webrtc = pa_xnew0(struct method_webrtc, 1); + webrtc->ss = *ss; + webrtc->fixed_bytes = fixed_bytes; + webrtc->fixed_frames = fixed_bytes / pa_frame_size(ss); + webrtc->loops = request_bytes / fixed_bytes; + + config.Set(new ExperimentalNs(false)); + config.Set(new Intelligibility(false)); + config.Set(new ExperimentalAgc(false)); + + webrtc->ap = AudioProcessing::Create(config); + if (!webrtc->ap) { + pa_log_error("Failed to create audio processing"); + goto fail; + } + + webrtc->ap->echo_cancellation()->Enable(true); + webrtc->ap->noise_suppression()->Enable(true); + webrtc->ap->noise_suppression()->set_level(static_cast(1)); + + webrtc->ap->gain_control()->set_mode(GainControl::kAdaptiveDigital); + //webrtc->ap->gain_control()->set_target_level_dbfs(30); + //webrtc->ap->gain_control()->set_stream_analog_level(30); + webrtc->ap->echo_cancellation()->set_suppression_level(static_cast(1)); + + webrtc->sconfig = new StreamConfig(ss->rate, ss->channels, false); + if (!webrtc->sconfig) { + pa_log_error("Failed to create stream config"); + goto fail; + } + + webrtc->sconfig->set_sample_rate_hz(ss->rate); + webrtc->sconfig->set_num_channels(ss->channels); + + /* webrtc supports 10ms by default */ + webrtc->frames = webrtc->sconfig->num_frames(); + if (webrtc->frames != webrtc->fixed_frames) { + pa_log_error("Failed to set frames. frames(%zu), fixed_frames(%zu)", + webrtc->frames, webrtc->fixed_frames); + goto fail; + } + + allocate_stream_buffer(webrtc, webrtc->fixed_frames); + + pa_log_info("webrtc processes request block(%llumsec) block(%llumsec) n(%d)\n", + pa_bytes_to_usec(request_bytes, ss) / PA_USEC_PER_MSEC, + pa_bytes_to_usec(fixed_bytes, ss) / PA_USEC_PER_MSEC, + webrtc->loops); + + return webrtc; + +fail: + if (webrtc->ap) + delete webrtc->ap; + if (webrtc->sconfig) + delete webrtc->sconfig; + + pa_xfree(webrtc); + + return NULL; +} + +int32_t webrtc_audio_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) { + struct method_webrtc *webrtc = (struct method_webrtc *)priv; + pa_sample_spec ss; + size_t frames; + + pa_assert(webrtc); + pa_assert(rec); + pa_assert(ref); + pa_assert(out); + + ss.format = PA_SAMPLE_FLOAT32LE; + ss.rate = webrtc->ss.rate; + ss.channels = webrtc->ss.channels; + + frames = webrtc->fixed_frames; + + for (int i = 0; i < webrtc->loops; i++) { + int ret; + + convert_s16_to_float(webrtc->ref_fbuf, (int16_t *)ref, frames * ss.channels); + pa_deinterleave(webrtc->ref_fbuf, (void **)webrtc->ref_dbuf, ss.channels, + pa_sample_size(&ss), frames); + + /* reference */ + ret = webrtc->ap->ProcessReverseStream(webrtc->ref_dbuf, + *webrtc->sconfig, + *webrtc->sconfig, + webrtc->ref_dbuf); + if (ret != AudioProcessing::kNoError) { + pa_log_error("Failed to process reverse stream"); + return -1; + } + + webrtc->ap->set_stream_delay_ms(0); + + /* capture */ + convert_s16_to_float(webrtc->rec_fbuf, (int16_t *)rec, frames * ss.channels); + pa_deinterleave(webrtc->rec_fbuf, (void **)webrtc->rec_dbuf, ss.channels, + pa_sample_size(&ss), frames); + + ret = webrtc->ap->ProcessStream(webrtc->rec_dbuf, + *webrtc->sconfig, + *webrtc->sconfig, + webrtc->out_dbuf); + if (ret != AudioProcessing::kNoError) { + pa_log_error("Failed to process stream"); + return -1; + } + + pa_interleave((const void **)webrtc->out_dbuf, ss.channels, webrtc->out_fbuf, + pa_sample_size(&ss), frames); + convert_float_to_s16((int16_t *)out, webrtc->out_fbuf, frames * ss.channels); + + rec += webrtc->fixed_bytes; + ref += webrtc->fixed_bytes; + out += webrtc->fixed_bytes; + } + + return 0; +} + +int32_t webrtc_audio_destroy(void *priv) { + struct method_webrtc *webrtc = (struct method_webrtc *)priv; + + pa_assert(webrtc); + + delete webrtc->sconfig; + delete webrtc->ap; + + deallocate_stream_buffer(webrtc); + pa_xfree(webrtc); + + return 0; +} + +static void deallocate_stream_buffer(struct method_webrtc *webrtc) { + pa_assert(webrtc); + + for (int i = 0; i < webrtc->ss.channels; i++) { + pa_xfree(webrtc->rec_dbuf[i]); + pa_xfree(webrtc->ref_dbuf[i]); + pa_xfree(webrtc->out_dbuf[i]); + } + + pa_xfree(webrtc->rec_fbuf); + pa_xfree(webrtc->ref_fbuf); + pa_xfree(webrtc->out_fbuf); +} + +static void allocate_stream_buffer(struct method_webrtc *webrtc, size_t nframes) { + int channels; + + pa_assert(webrtc); + + channels = webrtc->ss.channels; + + webrtc->rec_fbuf = pa_xnew(float, nframes * channels); + webrtc->ref_fbuf = pa_xnew(float, nframes * channels); + webrtc->out_fbuf = pa_xnew(float, nframes * channels); + + for (int i = 0; i < channels; i++) { + webrtc->rec_dbuf[i] = pa_xnew(float, nframes); + webrtc->ref_dbuf[i] = pa_xnew(float, nframes); + webrtc->out_dbuf[i] = pa_xnew(float, nframes); + } +} + +static void convert_s16_to_float(float *dst, int16_t *src, size_t n) { + pa_assert(dst); + pa_assert(src); + + ((pa_convert_func_t)pa_get_convert_to_float32ne_function(PA_SAMPLE_S16LE))(n, src, dst); +} + +static void convert_float_to_s16(int16_t *dst, float *src, size_t n) { + pa_assert(dst); + pa_assert(src); + + ((pa_convert_func_t)pa_get_convert_to_s16ne_function(PA_SAMPLE_FLOAT32LE))(n, src, dst); +} +