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 \
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
+++ /dev/null
-/***
- This file is part of PulseAudio.
-
- Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
-
- 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 <config.h>
-#endif
-
-#include <stdint.h>
-#include <pulse/xmalloc.h>
-#include <pulse/sample.h>
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-#include <assert.h>
-
-#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; i<adrian->blocksize; 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;
-}
+++ /dev/null
-/***
- This file is part of PulseAudio.
-
- Copyright 2022 Jaechul Lee <jcsing.lee@samsung.com>
-
- 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 <config.h>
-#endif
-
-#include <stdint.h>
-#include <pulse/xmalloc.h>
-#include <pulse/sample.h>
-#include <pulse/channelmap.h>
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-
-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;
-}
+++ /dev/null
-/***
- This file is part of PulseAudio.
-
- Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
-
- 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 <config.h>
-#endif
-
-#include <stdint.h>
-#include <pulse/xmalloc.h>
-#include <pulse/sample.h>
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-
-#include <speex/speex.h>
-#include <speex/speex_preprocess.h>
-#include <speex/speex_echo.h>
-
-#include <assert.h>
-
-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;
-}
+++ /dev/null
-/***
- This file is part of PulseAudio.
-
- Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
-
- 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 <config.h>
-#endif
-
-#include <pulse/cdecl.h>
-
-#include <webrtc/modules/audio_processing/include/audio_processing.h>
-
-#define DEFAULT_PROCESS_SIZE_MS 10
-
-using namespace webrtc;
-
-PA_C_DECL_BEGIN
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/sconv.h>
-#include <pulsecore/sample-util.h>
-#include <pulse/xmalloc.h>
-#include <pulse/sample.h>
-#include <pulse/timeval.h>
-#include <stdint.h>
-
-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<ExperimentalNs>(new ExperimentalNs(false));
- config.Set<Intelligibility>(new Intelligibility(false));
- config.Set<ExperimentalAgc>(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<NoiseSuppression::Level>(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<EchoCancellation::SuppressionLevel>(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);
-}
-
--- /dev/null
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
+
+ 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 <config.h>
+#endif
+
+#include <stdint.h>
+#include <pulse/xmalloc.h>
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <assert.h>
+
+#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; i<adrian->blocksize; 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;
+}
--- /dev/null
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2022 Jaechul Lee <jcsing.lee@samsung.com>
+
+ 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 <config.h>
+#endif
+
+#include <stdint.h>
+#include <pulse/xmalloc.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+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;
+}
--- /dev/null
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
+
+ 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 <config.h>
+#endif
+
+#include <stdint.h>
+#include <pulse/xmalloc.h>
+#include <pulse/sample.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include <speex/speex.h>
+#include <speex/speex_preprocess.h>
+#include <speex/speex_echo.h>
+
+#include <assert.h>
+
+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;
+}
--- /dev/null
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
+
+ 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 <config.h>
+#endif
+
+#include <pulse/cdecl.h>
+
+#include <webrtc/modules/audio_processing/include/audio_processing.h>
+
+#define DEFAULT_PROCESS_SIZE_MS 10
+
+using namespace webrtc;
+
+PA_C_DECL_BEGIN
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/sconv.h>
+#include <pulsecore/sample-util.h>
+#include <pulse/xmalloc.h>
+#include <pulse/sample.h>
+#include <pulse/timeval.h>
+#include <stdint.h>
+
+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<ExperimentalNs>(new ExperimentalNs(false));
+ config.Set<Intelligibility>(new Intelligibility(false));
+ config.Set<ExperimentalAgc>(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<NoiseSuppression::Level>(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<EchoCancellation::SuppressionLevel>(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);
+}
+