echo-cancel: Replace 'algo' prefix with 'method' 53/277053/3
authorJaechul Lee <jcsing.lee@samsung.com>
Thu, 30 Jun 2022 04:10:12 +0000 (13:10 +0900)
committerJaechul Lee <jcsing.lee@samsung.com>
Fri, 1 Jul 2022 02:11:46 +0000 (11:11 +0900)
[Version] 15.0.23
[Issue Type] Rename

Change-Id: I8eee586036ef0f107f94413285b22a5736284318
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
Makefile.am
src/echo-cancel/algo_adrian.c [deleted file]
src/echo-cancel/algo_reference_copy.c [deleted file]
src/echo-cancel/algo_speex.c [deleted file]
src/echo-cancel/algo_webrtc.cpp [deleted file]
src/echo-cancel/method_adrian.c [new file with mode: 0644]
src/echo-cancel/method_reference_copy.c [new file with mode: 0644]
src/echo-cancel/method_speex.c [new file with mode: 0644]
src/echo-cancel/method_webrtc.cpp [new file with mode: 0644]

index b3d0913bcdcb09a66fa73f1f0e2265c36eb3821e..cd71bb19b1d27a8b15cd324ebc19d2e819dccf0a 100644 (file)
@@ -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 (file)
index a5d2e72..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/***
-  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;
-}
diff --git a/src/echo-cancel/algo_reference_copy.c b/src/echo-cancel/algo_reference_copy.c
deleted file mode 100644 (file)
index 7b397ec..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/***
-  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;
-}
diff --git a/src/echo-cancel/algo_speex.c b/src/echo-cancel/algo_speex.c
deleted file mode 100644 (file)
index 161812a..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/***
-  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;
-}
diff --git a/src/echo-cancel/algo_webrtc.cpp b/src/echo-cancel/algo_webrtc.cpp
deleted file mode 100644 (file)
index 8253820..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/***
-  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);
-}
-
diff --git a/src/echo-cancel/method_adrian.c b/src/echo-cancel/method_adrian.c
new file mode 100644 (file)
index 0000000..d673dcd
--- /dev/null
@@ -0,0 +1,93 @@
+/***
+  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;
+}
diff --git a/src/echo-cancel/method_reference_copy.c b/src/echo-cancel/method_reference_copy.c
new file mode 100644 (file)
index 0000000..7b397ec
--- /dev/null
@@ -0,0 +1,115 @@
+/***
+  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;
+}
diff --git a/src/echo-cancel/method_speex.c b/src/echo-cancel/method_speex.c
new file mode 100644 (file)
index 0000000..5dd0b5f
--- /dev/null
@@ -0,0 +1,138 @@
+/***
+  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;
+}
diff --git a/src/echo-cancel/method_webrtc.cpp b/src/echo-cancel/method_webrtc.cpp
new file mode 100644 (file)
index 0000000..4b7721c
--- /dev/null
@@ -0,0 +1,286 @@
+/***
+  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);
+}
+