Add noise suppression functionality 78/290278/19
authorJaechul Lee <jcsing.lee@samsung.com>
Mon, 13 Feb 2023 06:24:37 +0000 (15:24 +0900)
committerJaechul Lee <jcsing.lee@samsung.com>
Thu, 15 Jun 2023 08:22:06 +0000 (17:22 +0900)
* Added the processor_holder structure for multi preprocessing.
* Added the processor_reference structure for receiving reference data.
* Added a NS method based on rnnoise.
* Added filesrc reference method.
* Replaced tizenaudio-echo-cancel with module-tizenaudio-preprocessor.
* Had dependencies on webrtc-audio-processing and rnnoise.
* Changed folder name echo-cancel to preprocessor.
* Disabled noise suppression functionality in method_webrtc.
* Removed method_adrian.

[Version] 15.0.41
[Issue Type] New feature

Change-Id: I22bfdffadabe4c9dbd08ce432c8d72a8f4d41dc4
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
26 files changed:
Makefile.am
configure.ac
packaging/pulseaudio-modules-tizen.spec
src/echo-cancel/adrian-aec.c [deleted file]
src/echo-cancel/adrian-aec.h [deleted file]
src/echo-cancel/method_adrian.c [deleted file]
src/echo-cancel/module-tizenaudio-echo-cancel.c [deleted file]
src/echo-cancel/processor.c [deleted file]
src/echo-cancel/processor.h [deleted file]
src/preprocessor/method_factory.c [new file with mode: 0644]
src/preprocessor/method_factory.h [new file with mode: 0644]
src/preprocessor/method_reference_copy.c [moved from src/echo-cancel/method_reference_copy.c with 67% similarity]
src/preprocessor/method_rnnoise.c [new file with mode: 0644]
src/preprocessor/method_speex.c [moved from src/echo-cancel/method_speex.c with 100% similarity]
src/preprocessor/method_webrtc.cpp [moved from src/echo-cancel/method_webrtc.cpp with 98% similarity]
src/preprocessor/module-tizenaudio-preprocessor.c [new file with mode: 0644]
src/preprocessor/preprocessor-def.h [moved from src/echo-cancel/echo-cancel-def.h with 63% similarity]
src/preprocessor/processor.c [new file with mode: 0644]
src/preprocessor/processor.h [new file with mode: 0644]
src/preprocessor/processor_holder.c [new file with mode: 0644]
src/preprocessor/processor_holder.h [new file with mode: 0644]
src/preprocessor/processor_reference.c [new file with mode: 0644]
src/preprocessor/processor_reference.h [new file with mode: 0644]
src/preprocessor/reference_method_filesrc.c [new file with mode: 0644]
src/tizenaudio-sink2.c
src/tizenaudio-source2.c

index 3723d0a..91d448d 100644 (file)
@@ -42,7 +42,7 @@ pulsemodlibexec_LTLIBRARIES = \
           module-tizenaudio-policy.la \
           module-tizenaudio-discover.la \
           module-tizenaudio-publish.la \
-          module-tizenaudio-echo-cancel.la \
+          module-tizenaudio-preprocessor.la \
           module-sound-player.la \
           module-tone-player.la \
           module-poweroff.la
@@ -82,9 +82,9 @@ libtizenaudio_util_la_SOURCES = \
           src/tizenaudio-source2.h \
           src/tizenaudio-util.c \
           src/tizenaudio-util.h \
-          src/echo-cancel/echo-cancel-def.h
+          src/preprocessor/preprocessor-def.h
 libtizenaudio_util_la_LDFLAGS = $(AM_LDFLAGS) $(PA_LDFLAGS) -avoid-version
-libtizenaudio_util_la_LIBADD = $(AM_LIBADD) $(PA_LIBS) libhal-interface.la
+libtizenaudio_util_la_LIBADD = $(AM_LIBADD) $(PA_LIBS) libhal-interface.la libprocessor.la
 libtizenaudio_util_la_CFLAGS = $(MODULE_CFLAGS)
 
 module_tizenaudio_sink2_la_SOURCES = src/module-tizenaudio-sink2.c
@@ -98,30 +98,28 @@ 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/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 \
-          src/echo-cancel/adrian-aec.h
+          src/preprocessor/method_speex.c \
+          src/preprocessor/method_rnnoise.c \
+          src/preprocessor/method_reference_copy.c \
+          src/preprocessor/method_webrtc.cpp \
+          src/preprocessor/reference_method_filesrc.c \
+          src/preprocessor/processor.c \
+          src/preprocessor/processor.h \
+          src/preprocessor/method_factory.c \
+          src/preprocessor/method_factory.h \
+          src/preprocessor/processor_reference.c \
+          src/preprocessor/processor_reference.h \
+          src/preprocessor/processor_holder.c \
+          src/preprocessor/processor_holder.h
 libprocessor_la_LDFLAGS = $(AM_LDFLAGS) $(PA_LDFLAGS) -avoid-version
-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/method_webrtc.cpp
-libprocessor_la_LIBADD += $(WEBRTC_LIBS)
-libprocessor_la_CPPFLAGS = $(WEBRTC_CFLAGS) $(PA_CFLAGS) -DSUPPORT_METHOD_WEBRTC -std=c++17
-endif
-
-module_tizenaudio_echo_cancel_la_SOURCES = src/echo-cancel/module-tizenaudio-echo-cancel.c src/echo-cancel/echo-cancel-def.h
-module_tizenaudio_echo_cancel_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_tizenaudio_echo_cancel_la_LIBADD = $(MODULE_LIBADD) libprocessor.la
-module_tizenaudio_echo_cancel_la_CFLAGS = $(MODULE_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_echo_cancel
-if ENABLE_WEBRTC
-module_tizenaudio_echo_cancel_la_CFLAGS += -DSUPPORT_METHOD_WEBRTC
-endif
+libprocessor_la_LIBADD = $(AM_LIBADD) $(LIBSPEEX_LIBS) $(RNNOISE_LIBS) $(WEBRTC_LIBS)
+libprocessor_la_CFLAGS = $(AM_CFLAGS) $(PA_CFLAGS) $(LIBSPEEX_CFLAGS) $(RNNOISE_CFLAGS)
+libprocessor_la_CPPFLAGS = $(AM_CFLAGS) $(PA_CFLAGS) $(WEBRTC_CFLAGS) -DSUPPORT_METHOD_WEBRTC -std=c++17
+
+module_tizenaudio_preprocessor_la_SOURCES = src/preprocessor/module-tizenaudio-preprocessor.c
+module_tizenaudio_preprocessor_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_tizenaudio_preprocessor_la_LIBADD = $(MODULE_LIBADD) libprocessor.la
+module_tizenaudio_preprocessor_la_CFLAGS = $(MODULE_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_preprocessor
 
 module_sound_player_la_SOURCES = src/module-sound-player.c
 module_sound_player_la_LDFLAGS = $(MODULE_LDFLAGS)
index ad76385..6c3637f 100644 (file)
@@ -373,6 +373,14 @@ PKG_CHECK_MODULES(SPEEX, speexdsp)
 AC_SUBST(SPEEX_CFLAGS)
 AC_SUBST(SPEEX_LIBS)
 
+PKG_CHECK_MODULES(WEBRTC, webrtc-audio-processing)
+AC_SUBST(WEBRTC_CFLAGS)
+AC_SUBST(WEBRTC_LIBS)
+
+PKG_CHECK_MODULES(RNNOISE, rnnoise)
+AC_SUBST(RNNOISE_CFLAGS)
+AC_SUBST(RNNOISE_LIBS)
+
 dnl use hal tc ------------------------------------------------------------
 AC_ARG_ENABLE(haltc, AC_HELP_STRING([--enable-haltc], [using haltc]),
 [
@@ -397,22 +405,6 @@ AC_ARG_ENABLE(acm, AC_HELP_STRING([--enable-acm], [using acm]),
 AM_CONDITIONAL(ENABLE_ACM, test "x$ENABLE_ACM" = "xyes")
 dnl end --------------------------------------------------------------------
 
-dnl use webrtc ----------------------------------------------------------------
-AC_ARG_ENABLE(webrtc, AC_HELP_STRING([--enable-webrtc], [using webrtc-audio-processing]),
-[
- case "${enableval}" in
-        yes) ENABLE_WEBRTC=yes ;;
-        no)  ENABLE_WEBRTC=no ;;
-        *)   AC_MSG_ERROR(bad value ${enableval} for --enable-webrtc) ;;
- esac
- ],[USE_WEBRTC=no])
-
-if test "x$ENABLE_WEBRTC" = "xyes"; then
-PKG_CHECK_MODULES(WEBRTC, webrtc-audio-processing)
-AC_SUBST(WEBRTC_CFLAGS)
-AC_SUBST(WEBRTC_LIBS)
-fi
-
 AM_CONDITIONAL(ENABLE_WEBRTC, test "x$ENABLE_WEBRTC" = "xyes")
 dnl end --------------------------------------------------------------------
 
index 98e5213..ee30edf 100644 (file)
@@ -2,7 +2,7 @@
 
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          15.0.40
+Version:          15.0.41
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
@@ -24,9 +24,8 @@ BuildRequires:    pkgconfig(libsystemd)
 BuildRequires:    pkgconfig(dns_sd)
 BuildRequires:    pkgconfig(hal-api-audio)
 BuildRequires:    pkgconfig(speexdsp)
-%if "%{tizen_profile_name}" != "tv"
+BuildRequires:    pkgconfig(rnnoise)
 BuildRequires:    pkgconfig(webrtc-audio-processing)
-%endif
 BuildRequires:    pulseaudio
 BuildRequires:    m4
 Requires(post):   /sbin/ldconfig
@@ -55,9 +54,6 @@ export LD_AS_NEEDED=0
 %reconfigure --prefix=%{_prefix} \
         --disable-static \
         --enable-acm \
-%if "%{tizen_profile_name}" != "tv"
-        --enable-webrtc \
-%endif
 %if "%{tizen_profile_name}" == "tv"
         --enable-vconf-helper
 %endif
@@ -91,7 +87,7 @@ install -m 0644 %SOURCE1 %{buildroot}%{_tmpfilesdir}/pulseaudio.conf
 %{_libdir}/pulse-%{module_ver}/modules/module-tizenaudio-source2.so
 %{_libdir}/pulse-%{module_ver}/modules/module-tizenaudio-discover.so
 %{_libdir}/pulse-%{module_ver}/modules/module-tizenaudio-publish.so
-%{_libdir}/pulse-%{module_ver}/modules/module-tizenaudio-echo-cancel.so
+%{_libdir}/pulse-%{module_ver}/modules/module-tizenaudio-preprocessor.so
 %{_libdir}/pulse-%{module_ver}/modules/libprocessor.so
 %{_libdir}/pulse-%{module_ver}/modules/libtizenaudio-util.so
 %{_libdir}/pulse-%{module_ver}/modules/libhal-interface.so
diff --git a/src/echo-cancel/adrian-aec.c b/src/echo-cancel/adrian-aec.c
deleted file mode 100644 (file)
index aaec55a..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-/* aec.cpp
- *
- * Copyright (C) DFS Deutsche Flugsicherung (2004, 2005).
- * All Rights Reserved.
- *
- * Acoustic Echo Cancellation NLMS-pw algorithm
- *
- * Version 0.3 filter created with www.dsptutor.freeuk.com
- * Version 0.3.1 Allow change of stability parameter delta
- * Version 0.4 Leaky Normalized LMS - pre whitening algorithm
- */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <math.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <pulse/xmalloc.h>
-
-#include "adrian-aec.h"
-
-#ifndef DISABLE_ORC
-#include "adrian-aec-orc-gen.h"
-#endif
-
-#ifdef __SSE__
-#include <xmmintrin.h>
-#endif
-
-/* Double-Talk Detector
- *
- * in d: microphone sample (PCM as REALing point value)
- * in x: loudspeaker sample (PCM as REALing point value)
- * return: from 0 for doubletalk to 1.0 for single talk
-*/
-static  float AEC_dtd(AEC *a, REAL d, REAL x);
-
-static  void AEC_leaky(AEC *a);
-
-/* Normalized Least Mean Square Algorithm pre-whitening (NLMS-pw)
- * The LMS algorithm was developed by Bernard Widrow
- * book: Haykin, Adaptive Filter Theory, 4. edition, Prentice Hall, 2002
- *
- * in d: microphone sample (16bit PCM value)
- * in x_: loudspeaker sample (16bit PCM value)
- * in stepsize: NLMS adaptation variable
- * return: echo cancelled microphone sample
- */
-static  REAL AEC_nlms_pw(AEC *a, REAL d, REAL x_, float stepsize);
-
-static  void AEC_setambient(AEC *a, float Min_xf) {
-    a->dotp_xf_xf -= a->delta;  // subtract old delta
-    a->delta = (NLMS_LEN-1) * Min_xf * Min_xf;
-    a->dotp_xf_xf += a->delta;  // add new delta
-  }
-
-static  REAL IIR1_highpass(IIR1 *i, REAL in) {
-    REAL out = i->a0 * in + i->a1 * i->in0 + i->b1 * i->out0;
-    i->in0 = in;
-    i->out0 = out;
-    return out;
-  }
-
-static  IIR1* IIR1_init(REAL Fc) {
-  IIR1 *i = pa_xnew(IIR1, 1);
-  i->b1 = expf(-2.0f * M_PI * Fc);
-  i->a0 = (1.0f + i->b1) / 2.0f;
-  i->a1 = -(i->a0);
-  i->in0 = 0.0f;
-  i->out0 = 0.0f;
-  return i;
-}
-
-static  REAL IIR_HP_highpass(IIR_HP *i, REAL in) {
-    const REAL a0 = 0.01f;      /* controls Transfer Frequency */
-    /* Highpass = Signal - Lowpass. Lowpass = Exponential Smoothing */
-    i->x += a0 * (in - i->x);
-    return in - i->x;
-  }
-
-static  IIR_HP* IIR_HP_init(void) {
-    IIR_HP *i = pa_xnew(IIR_HP, 1);
-    i->x = 0.0f;
-    return i;
-  }
-
-#if WIDEB==1
-/* 17 taps FIR Finite Impulse Response filter
- * Coefficients calculated with
- * www.dsptutor.freeuk.com/KaiserFilterDesign/KaiserFilterDesign.html
- */
-class FIR_HP_300Hz {
-  REAL z[18];
-
-public:
-   FIR_HP_300Hz() {
-    memset(this, 0, sizeof(FIR_HP_300Hz));
-  }
-
-  REAL highpass(REAL in) {
-    const REAL a[18] = {
-    // Kaiser Window FIR Filter, Filter type: High pass
-    // Passband: 300.0 - 4000.0 Hz, Order: 16
-    // Transition band: 75.0 Hz, Stopband attenuation: 10.0 dB
-    -0.034870606, -0.039650206, -0.044063766, -0.04800318,
-    -0.051370874, -0.054082647, -0.056070227, -0.057283327,
-    0.8214126, -0.057283327, -0.056070227, -0.054082647,
-    -0.051370874, -0.04800318, -0.044063766, -0.039650206,
-    -0.034870606, 0.0
-    };
-    memmove(z + 1, z, 17 * sizeof(REAL));
-    z[0] = in;
-    REAL sum0 = 0.0, sum1 = 0.0;
-    int j;
-
-    for (j = 0; j < 18; j += 2) {
-      // optimize: partial loop unrolling
-      sum0 += a[j] * z[j];
-      sum1 += a[j + 1] * z[j + 1];
-    }
-    return sum0 + sum1;
-  }
-};
-
-#else
-
-/* 35 taps FIR Finite Impulse Response filter
- * Passband 150Hz to 4kHz for 8kHz sample rate, 300Hz to 8kHz for 16kHz
- * sample rate.
- * Coefficients calculated with
- * www.dsptutor.freeuk.com/KaiserFilterDesign/KaiserFilterDesign.html
- */
-struct FIR_HP_300Hz {
-  REAL z[36];
-};
-
-static  FIR_HP_300Hz* FIR_HP_300Hz_init(void) {
-    FIR_HP_300Hz *ret = pa_xnew(FIR_HP_300Hz, 1);
-    memset(ret, 0, sizeof(FIR_HP_300Hz));
-    return ret;
-  }
-
-static  REAL FIR_HP_300Hz_highpass(FIR_HP_300Hz *f, REAL in) {
-    REAL sum0 = 0.0, sum1 = 0.0;
-    int j;
-    const REAL a[36] = {
-      // Kaiser Window FIR Filter, Filter type: High pass
-      // Passband: 150.0 - 4000.0 Hz, Order: 34
-      // Transition band: 34.0 Hz, Stopband attenuation: 10.0 dB
-      -0.016165324, -0.017454365, -0.01871232, -0.019931411,
-      -0.021104068, -0.022222936, -0.02328091, -0.024271343,
-      -0.025187887, -0.02602462, -0.026776174, -0.027437767,
-      -0.028004972, -0.028474221, -0.028842418, -0.029107114,
-      -0.02926664, 0.8524841, -0.02926664, -0.029107114,
-      -0.028842418, -0.028474221, -0.028004972, -0.027437767,
-      -0.026776174, -0.02602462, -0.025187887, -0.024271343,
-      -0.02328091, -0.022222936, -0.021104068, -0.019931411,
-      -0.01871232, -0.017454365, -0.016165324, 0.0
-    };
-    memmove(f->z + 1, f->z, 35 * sizeof(REAL));
-    f->z[0] = in;
-
-    for (j = 0; j < 36; j += 2) {
-      // optimize: partial loop unrolling
-      sum0 += a[j] * f->z[j];
-      sum1 += a[j + 1] * f->z[j + 1];
-    }
-    return sum0 + sum1;
-  }
-#endif
-
-
-
-
-
-/* Vector Dot Product */
-static REAL dotp(REAL a[], REAL b[])
-{
-  REAL sum0 = 0.0f, sum1 = 0.0f;
-  int j;
-
-  for (j = 0; j < NLMS_LEN; j += 2) {
-    // optimize: partial loop unrolling
-    sum0 += a[j] * b[j];
-    sum1 += a[j + 1] * b[j + 1];
-  }
-  return sum0 + sum1;
-}
-
-static REAL dotp_sse(REAL a[], REAL b[])
-{
-#ifdef __SSE__
-  /* This is taken from speex's inner product implementation */
-  int j;
-  REAL sum;
-  __m128 acc = _mm_setzero_ps();
-
-  for (j=0;j<NLMS_LEN;j+=8)
-  {
-    acc = _mm_add_ps(acc, _mm_mul_ps(_mm_load_ps(a+j), _mm_loadu_ps(b+j)));
-    acc = _mm_add_ps(acc, _mm_mul_ps(_mm_load_ps(a+j+4), _mm_loadu_ps(b+j+4)));
-  }
-  acc = _mm_add_ps(acc, _mm_movehl_ps(acc, acc));
-  acc = _mm_add_ss(acc, _mm_shuffle_ps(acc, acc, 0x55));
-  _mm_store_ss(&sum, acc);
-
-  return sum;
-#else
-  return dotp(a, b);
-#endif
-}
-
-
-AEC* AEC_init(int RATE, int have_vector)
-{
-  AEC *a = pa_xnew0(AEC, 1);
-  a->j = NLMS_EXT;
-  AEC_setambient(a, NoiseFloor);
-  a->dfast = a->dslow = M75dB_PCM;
-  a->xfast = a->xslow = M80dB_PCM;
-  a->gain = 1.0f;
-  a->Fx = IIR1_init(2000.0f/RATE);
-  a->Fe = IIR1_init(2000.0f/RATE);
-  a->cutoff = FIR_HP_300Hz_init();
-  a->acMic = IIR_HP_init();
-  a->acSpk = IIR_HP_init();
-
-  a->aes_y2 = M0dB;
-
-  a->fdwdisplay = -1;
-
-  if (have_vector) {
-      /* Get a 16-byte aligned location */
-      a->w = (REAL *) (((uintptr_t) a->w_arr) - (((uintptr_t) a->w_arr) % 16) + 16);
-      a->dotp = dotp_sse;
-  } else {
-      /* We don't care about alignment, just use the array as-is */
-      a->w = a->w_arr;
-      a->dotp = dotp;
-  }
-
-  return a;
-}
-
-void AEC_done(AEC *a) {
-    pa_assert(a);
-
-    pa_xfree(a->Fx);
-    pa_xfree(a->Fe);
-    pa_xfree(a->acMic);
-    pa_xfree(a->acSpk);
-    pa_xfree(a->cutoff);
-    pa_xfree(a);
-}
-
-// Adrian soft decision DTD
-// (Dual Average Near-End to Far-End signal Ratio DTD)
-// This algorithm uses exponential smoothing with different
-// ageing parameters to get fast and slow near-end and far-end
-// signal averages. The ratio of NFRs term
-// (dfast / xfast) / (dslow / xslow) is used to compute the stepsize
-// A ratio value of 2.5 is mapped to stepsize 0, a ratio of 0 is
-// mapped to 1.0 with a limited linear function.
-static float AEC_dtd(AEC *a, REAL d, REAL x)
-{
-  float ratio, stepsize;
-
-  // fast near-end and far-end average
-  a->dfast += ALPHAFAST * (fabsf(d) - a->dfast);
-  a->xfast += ALPHAFAST * (fabsf(x) - a->xfast);
-
-  // slow near-end and far-end average
-  a->dslow += ALPHASLOW * (fabsf(d) - a->dslow);
-  a->xslow += ALPHASLOW * (fabsf(x) - a->xslow);
-
-  if (a->xfast < M70dB_PCM) {
-    return 0.0f;   // no Spk signal
-  }
-
-  if (a->dfast < M70dB_PCM) {
-    return 0.0f;   // no Mic signal
-  }
-
-  // ratio of NFRs
-  ratio = (a->dfast * a->xslow) / (a->dslow * a->xfast);
-
-  // Linear interpolation with clamping at the limits
-  if (ratio < STEPX1)
-    stepsize = STEPY1;
-  else if (ratio > STEPX2)
-    stepsize = STEPY2;
-  else
-    stepsize = STEPY1 + (STEPY2 - STEPY1) * (ratio - STEPX1) / (STEPX2 - STEPX1);
-
-  return stepsize;
-}
-
-
-static void AEC_leaky(AEC *a)
-// The xfast signal is used to charge the hangover timer to Thold.
-// When hangover expires (no Spk signal for some time) the vector w
-// is erased. This is my implementation of Leaky NLMS.
-{
-  if (a->xfast >= M70dB_PCM) {
-    // vector w is valid for hangover Thold time
-    a->hangover = Thold;
-  } else {
-    if (a->hangover > 1) {
-      --(a->hangover);
-    } else if (1 == a->hangover) {
-      --(a->hangover);
-      // My Leaky NLMS is to erase vector w when hangover expires
-      memset(a->w_arr, 0, sizeof(a->w_arr));
-    }
-  }
-}
-
-
-#if 0
-void AEC::openwdisplay() {
-  // open TCP connection to program wdisplay.tcl
-  fdwdisplay = socket_async("127.0.0.1", 50999);
-};
-#endif
-
-
-static REAL AEC_nlms_pw(AEC *a, REAL d, REAL x_, float stepsize)
-{
-  REAL e;
-  REAL ef;
-  a->x[a->j] = x_;
-  a->xf[a->j] = IIR1_highpass(a->Fx, x_);     // pre-whitening of x
-
-  // calculate error value
-  // (mic signal - estimated mic signal from spk signal)
-  e = d;
-  if (a->hangover > 0) {
-    e -= a->dotp(a->w, a->x + a->j);
-  }
-  ef = IIR1_highpass(a->Fe, e);     // pre-whitening of e
-
-  // optimize: iterative dotp(xf, xf)
-  a->dotp_xf_xf += (a->xf[a->j] * a->xf[a->j] - a->xf[a->j + NLMS_LEN - 1] * a->xf[a->j + NLMS_LEN - 1]);
-
-  if (stepsize > 0.0f) {
-    // calculate variable step size
-    REAL mikro_ef = stepsize * ef / a->dotp_xf_xf;
-
-#ifdef DISABLE_ORC
-    // update tap weights (filter learning)
-    int i;
-    for (i = 0; i < NLMS_LEN; i += 2) {
-      // optimize: partial loop unrolling
-      a->w[i] += mikro_ef * a->xf[i + a->j];
-      a->w[i + 1] += mikro_ef * a->xf[i + a->j + 1];
-    }
-#else
-    update_tap_weights(a->w, &a->xf[a->j], mikro_ef, NLMS_LEN);
-#endif
-  }
-
-  if (--(a->j) < 0) {
-    // optimize: decrease number of memory copies
-    a->j = NLMS_EXT;
-    memmove(a->x + a->j + 1, a->x, (NLMS_LEN - 1) * sizeof(REAL));
-    memmove(a->xf + a->j + 1, a->xf, (NLMS_LEN - 1) * sizeof(REAL));
-  }
-
-  // Saturation
-  if (e > MAXPCM) {
-    return MAXPCM;
-  } else if (e < -MAXPCM) {
-    return -MAXPCM;
-  } else {
-    return e;
-  }
-}
-
-
-int AEC_doAEC(AEC *a, int d_, int x_)
-{
-  REAL d = (REAL) d_;
-  REAL x = (REAL) x_;
-
-  // Mic Highpass Filter - to remove DC
-  d = IIR_HP_highpass(a->acMic, d);
-
-  // Mic Highpass Filter - cut-off below 300Hz
-  d = FIR_HP_300Hz_highpass(a->cutoff, d);
-
-  // Amplify, for e.g. Soundcards with -6dB max. volume
-  d *= a->gain;
-
-  // Spk Highpass Filter - to remove DC
-  x = IIR_HP_highpass(a->acSpk, x);
-
-  // Double Talk Detector
-  a->stepsize = AEC_dtd(a, d, x);
-
-  // Leaky (ageing of vector w)
-  AEC_leaky(a);
-
-  // Acoustic Echo Cancellation
-  d = AEC_nlms_pw(a, d, x, a->stepsize);
-
-#if 0
-  if (fdwdisplay >= 0) {
-    if (++dumpcnt >= (WIDEB*RATE/10)) {
-      // wdisplay creates 10 dumps per seconds = large CPU load!
-      dumpcnt = 0;
-      write(fdwdisplay, ws, DUMP_LEN*sizeof(float));
-      // we don't check return value. This is not production quality!!!
-      memset(ws, 0, sizeof(ws));
-    } else {
-      int i;
-      for (i = 0; i < DUMP_LEN; i += 2) {
-        // optimize: partial loop unrolling
-        ws[i] += w[i];
-        ws[i + 1] += w[i + 1];
-      }
-    }
-  }
-#endif
-
-  return (int) d;
-}
diff --git a/src/echo-cancel/adrian-aec.h b/src/echo-cancel/adrian-aec.h
deleted file mode 100644 (file)
index 1482923..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/* aec.h
- *
- * Copyright (C) DFS Deutsche Flugsicherung (2004, 2005).
- * All Rights Reserved.
- * Author: Andre Adrian
- *
- * Acoustic Echo Cancellation Leaky NLMS-pw algorithm
- *
- * Version 0.3 filter created with www.dsptutor.freeuk.com
- * Version 0.3.1 Allow change of stability parameter delta
- * Version 0.4 Leaky Normalized LMS - pre whitening algorithm
- */
-
-#ifndef _AEC_H                  /* include only once */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <pulse/gccmacro.h>
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/macro.h>
-
-#ifdef __TIZEN__
-#define DISABLE_ORC
-#define _USE_MATH_DEFINES
-#include <math.h>
-#endif
-
-#define WIDEB 2
-
-// use double if your CPU does software-emulation of float
-#define REAL float
-
-/* dB Values */
-#define M0dB 1.0f
-#define M3dB 0.71f
-#define M6dB 0.50f
-#define M9dB 0.35f
-#define M12dB 0.25f
-#define M18dB 0.125f
-#define M24dB 0.063f
-
-/* dB values for 16bit PCM */
-/* MxdB_PCM = 32767 * 10 ^(x / 20) */
-#define M10dB_PCM 10362.0f
-#define M20dB_PCM 3277.0f
-#define M25dB_PCM 1843.0f
-#define M30dB_PCM 1026.0f
-#define M35dB_PCM 583.0f
-#define M40dB_PCM 328.0f
-#define M45dB_PCM 184.0f
-#define M50dB_PCM 104.0f
-#define M55dB_PCM 58.0f
-#define M60dB_PCM 33.0f
-#define M65dB_PCM 18.0f
-#define M70dB_PCM 10.0f
-#define M75dB_PCM 6.0f
-#define M80dB_PCM 3.0f
-#define M85dB_PCM 2.0f
-#define M90dB_PCM 1.0f
-
-#define MAXPCM 32767.0f
-
-/* Design constants (Change to fine tune the algorithms */
-
-/* The following values are for hardware AEC and studio quality
- * microphone */
-
-/* NLMS filter length in taps (samples). A longer filter length gives
- * better Echo Cancellation, but maybe slower convergence speed and
- * needs more CPU power (Order of NLMS is linear) */
-#define NLMS_LEN  (100*WIDEB*8)
-
-/* Vector w visualization length in taps (samples).
- * Must match argv value for wdisplay.tcl */
-#define DUMP_LEN  (40*WIDEB*8)
-
-/* minimum energy in xf. Range: M70dB_PCM to M50dB_PCM. Should be equal
- * to microphone ambient Noise level */
-#define NoiseFloor M55dB_PCM
-
-/* Leaky hangover in taps.
- */
-#define Thold (60 * WIDEB * 8)
-
-// Adrian soft decision DTD
-// left point. X is ratio, Y is stepsize
-#define STEPX1 1.0
-#define STEPY1 1.0
-// right point. STEPX2=2.0 is good double talk, 3.0 is good single talk.
-#define STEPX2 2.5
-#define STEPY2 0
-#define ALPHAFAST (1.0f / 100.0f)
-#define ALPHASLOW (1.0f / 20000.0f)
-
-
-
-/* Ageing multiplier for LMS memory vector w */
-#define Leaky 0.9999f
-
-/* Double Talk Detector Speaker/Microphone Threshold. Range <=1
- * Large value (M0dB) is good for Single-Talk Echo cancellation,
- * small value (M12dB) is good for Double-Talk AEC */
-#define GeigelThreshold M6dB
-
-/* for Non Linear Processor. Range >0 to 1. Large value (M0dB) is good
- * for Double-Talk, small value (M12dB) is good for Single-Talk */
-#define NLPAttenuation M12dB
-
-/* Below this line there are no more design constants */
-
-typedef struct IIR_HP IIR_HP;
-
-/* Exponential Smoothing or IIR Infinite Impulse Response Filter */
-struct IIR_HP {
-  REAL x;
-};
-
-typedef struct FIR_HP_300Hz FIR_HP_300Hz;
-
-typedef struct IIR1 IIR1;
-
-/* Recursive single pole IIR Infinite Impulse response High-pass filter
- *
- * Reference: The Scientist and Engineer's Guide to Digital Processing
- *
- *     output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
- *
- *      X  = exp(-2.0 * pi * Fc)
- *      A0 = (1 + X) / 2
- *      A1 = -(1 + X) / 2
- *      B1 = X
- *      Fc = cutoff freq / sample rate
- */
-struct IIR1 {
-  REAL in0, out0;
-  REAL a0, a1, b1;
-};
-
-#if 0
-  IIR1() {
-    memset(this, 0, sizeof(IIR1));
-  }
-#endif
-
-
-#if 0
-/* Recursive two pole IIR Infinite Impulse Response filter
- * Coefficients calculated with
- * http://www.dsptutor.freeuk.com/IIRFilterDesign/IIRFiltDes102.html
- */
-class IIR2 {
-  REAL x[2], y[2];
-
-public:
-   IIR2() {
-    memset(this, 0, sizeof(IIR2));
-  }
-
-  REAL highpass(REAL in) {
-    // Butterworth IIR filter, Filter type: HP
-    // Passband: 2000 - 4000.0 Hz, Order: 2
-    const REAL a[] = { 0.29289323f, -0.58578646f, 0.29289323f };
-    const REAL b[] = { 1.3007072E-16f, 0.17157288f };
-    REAL out =
-      a[0] * in + a[1] * x[0] + a[2] * x[1] - b[0] * y[0] - b[1] * y[1];
-
-    x[1] = x[0];
-    x[0] = in;
-    y[1] = y[0];
-    y[0] = out;
-    return out;
-  }
-};
-#endif
-
-
-// Extension in taps to reduce mem copies
-#define NLMS_EXT  (10*8)
-
-// block size in taps to optimize DTD calculation
-#define DTD_LEN   16
-
-typedef struct AEC AEC;
-
-struct AEC {
-  // Time domain Filters
-  IIR_HP *acMic, *acSpk;        // DC-level remove Highpass)
-  FIR_HP_300Hz *cutoff;         // 150Hz cut-off Highpass
-  REAL gain;                    // Mic signal amplify
-  IIR1 *Fx, *Fe;                // pre-whitening Highpass for x, e
-
-  // Adrian soft decision DTD (Double Talk Detector)
-  REAL dfast, xfast;
-  REAL dslow, xslow;
-
-  // NLMS-pw
-  REAL x[NLMS_LEN + NLMS_EXT];  // tap delayed loudspeaker signal
-  REAL xf[NLMS_LEN + NLMS_EXT]; // pre-whitening tap delayed signal
-  REAL w_arr[NLMS_LEN + (16 / sizeof(REAL))]; // tap weights
-  REAL *w;                      // this will be a 16-byte aligned pointer into w_arr
-  int j;                        // optimize: less memory copies
-  double dotp_xf_xf;            // double to avoid loss of precision
-  float delta;                  // noise floor to stabilize NLMS
-
-  // AES
-  float aes_y2;                 // not in use!
-
-  // w vector visualization
-  REAL ws[DUMP_LEN];            // tap weights sums
-  int fdwdisplay;               // TCP file descriptor
-  int dumpcnt;                  // wdisplay output counter
-
-  // variables are public for visualization
-  int hangover;
-  float stepsize;
-
-  // vfuncs that are picked based on processor features available
-  REAL (*dotp) (REAL[], REAL[]);
-};
-
-AEC* AEC_init(int RATE, int have_vector);
-void AEC_done(AEC *a);
-
-/* Acoustic Echo Cancellation and Suppression of one sample
- * in   d:  microphone signal with echo
- * in   x:  loudspeaker signal
- * return:  echo cancelled microphone signal
- */
-  int AEC_doAEC(AEC *a, int d_, int x_);
-
-PA_GCC_UNUSED static  float AEC_getambient(AEC *a) {
-    return a->dfast;
-  }
-PA_GCC_UNUSED static  void AEC_setgain(AEC *a, float gain_) {
-    a->gain = gain_;
-  }
-#if 0
-  void AEC_openwdisplay(AEC *a);
-#endif
-PA_GCC_UNUSED static  void AEC_setaes(AEC *a, float aes_y2_) {
-    a->aes_y2 = aes_y2_;
-  }
-
-#define _AEC_H
-#endif
diff --git a/src/echo-cancel/method_adrian.c b/src/echo-cancel/method_adrian.c
deleted file mode 100644 (file)
index d673dcd..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 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/module-tizenaudio-echo-cancel.c b/src/echo-cancel/module-tizenaudio-echo-cancel.c
deleted file mode 100644 (file)
index bfa6236..0000000
+++ /dev/null
@@ -1,751 +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 <pulsecore/macro.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/source.h>
-#include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
-#include <pulsecore/rtpoll.h>
-#include <pulsecore/poll.h>
-#include <pulsecore/namereg.h>
-#include <pulse/util.h>
-#include <pulse/timeval.h>
-
-#include "echo-cancel-def.h"
-#include "processor.h"
-
-PA_MODULE_AUTHOR("Tizen");
-PA_MODULE_DESCRIPTION("Tizen Audio Echo Cancel");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(true);
-PA_MODULE_USAGE(
-        "method=<name of method using for echo cancellation. [method]=webrtc, adrian, speex, reference copy > ");
-
-#define DEFAULT_PROCESS_MSEC 10
-
-typedef struct echo_cancel pa_echo_cancel;
-struct userdata {
-    pa_core *core;
-    pa_module *m;
-    pa_sink *sink;
-    pa_source *source;
-
-    pa_hook_slot *sink_unlink_slot;
-    pa_hook_slot *source_unlink_slot;
-
-    pa_hook_slot *source_output_new_slot;
-    pa_hook_slot *source_output_put_slot;
-    pa_hook_slot *source_output_unlink_slot;
-    pa_hook_slot *source_output_unlink_post_slot;
-    pa_hook_slot *sink_state_changed_slot;
-
-    bool enable;
-    uint32_t n_source_output;
-
-    char *force_method;
-
-    pa_thread *thread;
-    pa_thread_mq thread_mq;
-    pa_rtpoll *rtpoll;
-    pa_echo_cancel *echo_cancel;
-
-    /* use in thread */
-    bool enable_in_thread;
-    bool triggered;
-
-    pa_asyncmsgq *asyncmsgq_sink;
-    pa_asyncmsgq *asyncmsgq_source;
-};
-
-struct echo_cancel {
-    pa_msgobject parent;
-    struct userdata *u;
-};
-
-PA_DEFINE_PRIVATE_CLASS(pa_echo_cancel, pa_msgobject);
-#define PA_ECHO_CANCEL(o) (pa_echo_cancel_cast(o))
-
-#define MEMBLOCKQ_MAXLENGTH (16 * 1024 * 1024)
-
-static const char* const valid_modargs[] = {
-    "method",
-    NULL,
-};
-
-static int proplist_get_fragment_size_usec(pa_proplist *p, pa_sample_spec *sample_spec, pa_usec_t *usec) {
-    const char *prop_fragsize;
-    uint32_t fragsize;
-
-    pa_assert(p);
-    pa_assert(sample_spec);
-    pa_assert(usec);
-
-    if (!(prop_fragsize = pa_proplist_gets(p, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE)))
-        return -1;
-
-    if (pa_atou(prop_fragsize, &fragsize))
-        return -1;
-
-    *usec = pa_bytes_to_usec(fragsize, sample_spec);
-
-    return 0;
-}
-
-static int proplist_get_method(pa_proplist *p, pa_processor_method_t *method) {
-    const char *m;
-
-    pa_assert(p);
-    pa_assert(method);
-
-    if (!(m = pa_proplist_gets(p, PA_PROP_MEDIA_ECHO_CANCEL_METHOD)))
-        return -1;
-
-    *method = pa_processor_get_method(m);
-
-    return 0;
-}
-
-static pa_source_output *find_source_output_by_flags(pa_source *s) {
-    pa_source_output *o;
-    void *state = NULL;
-
-    pa_assert(s);
-
-    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
-        pa_source_output_assert_ref(o);
-
-        if (o->flags & PA_SOURCE_OUTPUT_ECHO_CANCEL)
-            break;
-    }
-
-    if (!o)
-        pa_log_error("Failed to find AEC source-output");
-
-    return o ? o : NULL;
-}
-
-static pa_usec_t get_round_trip_latency(struct userdata *u) {
-    pa_usec_t sink_latency = 0ULL;
-    pa_usec_t source_latency = 0ULL;
-
-    pa_assert(u);
-    pa_assert(u->sink);
-
-    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_latency, 0, NULL);
-    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_latency, 0, NULL);
-
-    pa_log_info("sink latency (%" PRIu64 "), source latency(%" PRIu64 ")", sink_latency, source_latency);
-
-    return sink_latency + source_latency;
-}
-
-static int send_rebuild_rtpoll(pa_msgobject *dst, pa_msgobject *src, pa_asyncmsgq *q) {
-    struct arguments {
-        pa_msgobject *o;
-        pa_asyncmsgq *q;
-    } args;
-    pa_asyncmsgq *asyncmsgq;
-    int code;
-
-    pa_assert(dst);
-
-    args.o = src;
-    args.q = q;
-
-    if (pa_sink_isinstance(dst)) {
-        asyncmsgq = PA_SINK(dst)->asyncmsgq;
-        code = PA_SINK_MESSAGE_REBUILD_RTPOLL;
-    } else if (pa_source_isinstance(dst)) {
-        asyncmsgq = PA_SOURCE(dst)->asyncmsgq;
-        code = PA_SOURCE_MESSAGE_REBUILD_RTPOLL;
-    } else {
-        pa_assert_not_reached();
-    }
-
-    pa_asyncmsgq_send(asyncmsgq, dst, code, src ? (void *)&args : NULL, 0, NULL);
-
-    return 0;
-}
-
-/* Call from main thread */
-static void broadcast_echo_cancel_state(struct userdata *u, pa_source_output *o, bool enable) {
-    void *v[2];
-
-    pa_assert(u);
-    pa_assert(u->source);
-    pa_assert(u->sink);
-
-    if (enable) {
-        send_rebuild_rtpoll(PA_MSGOBJECT(u->source), PA_MSGOBJECT(u->echo_cancel), u->asyncmsgq_source);
-        send_rebuild_rtpoll(PA_MSGOBJECT(u->sink), PA_MSGOBJECT(u->echo_cancel), u->asyncmsgq_sink);
-    }
-
-    v[0] = (void *)enable;
-    v[1] = o;
-
-    pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->echo_cancel),
-                        PA_ECHO_CANCEL_MESSAGE_SET_AEC_STATE, (void *)v, 0, NULL);
-
-    pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink),
-                        PA_SINK_MESSAGE_SET_AEC_STATE, (void *)enable, 0, NULL, NULL);
-
-    pa_asyncmsgq_post(u->source->asyncmsgq, PA_MSGOBJECT(u->source),
-                        PA_SOURCE_MESSAGE_SET_AEC_STATE, (void *)enable, 0, NULL, NULL);
-
-    if (!enable) {
-        send_rebuild_rtpoll(PA_MSGOBJECT(u->source), NULL, NULL);
-        send_rebuild_rtpoll(PA_MSGOBJECT(u->sink), NULL, NULL);
-    }
-}
-
-static void set_echo_cancel_state(struct userdata *u, bool enable) {
-    pa_source_output *o;
-
-    pa_assert(u);
-    pa_assert(u->source);
-    pa_assert(u->sink);
-
-    if (u->enable == enable)
-        return;
-
-    o = find_source_output_by_flags(u->source);
-    if (!o)
-        return;
-
-    broadcast_echo_cancel_state(u, o, enable);
-    u->enable = enable;
-
-    pa_log_info("AEC state is changed. enable(%d)", u->enable);
-}
-
-static int update_state_by_sink(struct userdata *u, bool enable) {
-    pa_assert(u);
-
-    if (u->n_source_output == 0)
-        return 0;
-
-    set_echo_cancel_state(u, enable);
-
-    return 0;
-}
-
-static int update_state_by_source(struct userdata *u, bool enable) {
-    pa_assert(u);
-
-    if (enable) {
-        if (u->n_source_output++ == 0) {
-            if (!u->sink || PA_SINK_IS_RUNNING(u->sink->state))
-                set_echo_cancel_state(u, enable);
-        }
-    } else {
-        if (--u->n_source_output == 0)
-            set_echo_cancel_state(u, enable);
-    }
-
-    return 0;
-}
-
-static int post_process(pa_source_output *o, pa_memchunk *chunk, pa_memchunk *ochunk) {
-    int ret = -1;
-
-    pa_assert(o);
-    pa_assert(chunk);
-    pa_assert(ochunk);
-
-    if ((ret = pa_processor_process(o->thread_info.processor, chunk, ochunk)) < 0)
-        pa_log_error("Failed to process data");
-
-    return ret;
-}
-
-/* rendering thread is separated because ec/ns takes much time in I/O thread */
-static int process_msg(
-        pa_msgobject *o,
-        int code,
-        void *data,
-        int64_t offset,
-        pa_memchunk *chunk) {
-
-    struct userdata *u = PA_ECHO_CANCEL(o)->u;
-    pa_source_output *so = NULL;
-
-    /* trigger resolves a race condition related to post_process between source and render thread */
-    if (u->triggered) {
-        if (code == PA_ECHO_CANCEL_MESSAGE_PUSH_ECHO) {
-            pa_usec_t latency;
-
-            so = find_source_output_by_flags(u->source);
-            if (!so) {
-                u->triggered = false;
-                return 0;
-            }
-
-            so->post_process = post_process;
-
-            latency = get_round_trip_latency(u);
-
-            if (pa_processor_setup_reference_memblockq_padding(so->thread_info.processor, latency) < 0)
-                pa_log_warn("Failed to setup reference memblockq padding");
-
-            u->triggered = false;
-
-            pa_log_info("Triggered Echo-Cancellation. index(%d), latency(%" PRIu64 ") usec", so->index, latency);
-        }
-    }
-
-    /* thread that pushes ref data should be called in render thread because of thread safe */
-    switch (code) {
-        case PA_ECHO_CANCEL_MESSAGE_PUSH_DATA:
-            if (u->enable_in_thread)
-                pa_source_post(u->source, chunk);
-
-            break;
-        case PA_ECHO_CANCEL_MESSAGE_PUSH_ECHO:
-            if (u->enable_in_thread) {
-                pa_assert(u->source);
-
-                so = find_source_output_by_flags(u->source);
-                if (!so)
-                    break;
-
-                if (pa_processor_push_reference(so->thread_info.processor, chunk) < 0)
-                    pa_log_error("Failed to push reference data");
-            }
-
-            break;
-        case PA_ECHO_CANCEL_MESSAGE_SET_AEC_STATE: {
-            void **v = (void **)data;
-            bool enable = (bool)v[0];
-            so = (pa_source_output *)v[1];
-
-            u->enable_in_thread = enable;
-
-            if (enable) {
-                u->triggered = true;
-            } else {
-                pa_processor_flush(so->thread_info.processor);
-                so->post_process = NULL;
-            }
-
-            break;
-        }
-        default:
-            break;
-    }
-
-    return 0;
-}
-
-static pa_hook_result_t source_output_new_cb(pa_core *c, pa_source_output_new_data *data, void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-    pa_processor_method_t method;
-    const char *m;
-
-    pa_assert(c);
-    pa_assert(u);
-    pa_assert(data);
-
-    if (!(m = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ECHO_CANCEL_METHOD)))
-        return PA_HOOK_OK;
-
-    method = pa_processor_get_method(m);
-
-    /* TODO: source-output can be moved */
-    data->flags |= PA_SOURCE_OUTPUT_DONT_MOVE;
-    data->flags |= PA_SOURCE_OUTPUT_ECHO_CANCEL;
-
-    if (method == PA_PROCESSOR_REFERENCE_COPY)
-        data->flags |= PA_SOURCE_OUTPUT_NO_REMAP;
-
-    pa_log_info("echo-cancel source-output will be created. method(%d)", method);
-
-    // TODO:add check limitation VARIOUS_RATE?
-    return PA_HOOK_OK;
-}
-
-static pa_sink *find_reference_sink_by_proplist(pa_core *c, pa_proplist *p) {
-    const char *ref_idx;
-    int32_t idx;
-    pa_sink *s;
-
-    pa_assert(c);
-    pa_assert(p);
-
-    ref_idx = pa_proplist_gets(p, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_SINK);
-    if (!ref_idx)
-        return NULL;
-
-    if (pa_atoi(ref_idx, &idx) < 0)
-        return NULL;
-
-    s = pa_idxset_get_by_index(c->sinks, idx);
-    if (!s)
-        return NULL;
-
-    pa_log_info("Found reference sink(%d, %s)", s->index, s->name);
-
-    return s;
-}
-
-static pa_hook_result_t source_output_put_cb(pa_core *c, pa_source_output *o, void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-    pa_processor_method_t method;
-    pa_usec_t process_usec;
-    pa_usec_t sink_process_usec;
-    int r;
-
-    pa_assert(c);
-    pa_assert(o);
-    pa_assert(u);
-    pa_assert(o->source);
-
-    if (!(o->flags & PA_SOURCE_OUTPUT_ECHO_CANCEL))
-        return PA_HOOK_OK;
-
-    if (u->n_source_output > 0) {
-        pa_log_error("Not allow multi aec instance");
-        goto fail;
-    }
-
-    if (u->force_method) {
-        method = pa_processor_get_method(u->force_method);
-    } else {
-        if (proplist_get_method(o->proplist, &method) < 0) {
-            pa_log_error("Failed to get method");
-            goto fail;
-        }
-    }
-
-    u->source = o->source;
-    u->sink = find_reference_sink_by_proplist(c, o->proplist);
-    if (!u->sink) {
-        pa_log_error("Can't find reference sink for AEC");
-        goto fail;
-    }
-
-    /* Get period size of sink and source */
-    if (proplist_get_fragment_size_usec(u->source->proplist, &u->source->sample_spec, &process_usec) < 0) {
-        pa_log_error("Failed to get fragment usec");
-        goto fail;
-    }
-
-    if (proplist_get_fragment_size_usec(u->sink->proplist, &u->sink->sample_spec, &sink_process_usec) < 0) {
-        pa_log_error("Failed to get fragment usec");
-        goto fail;
-    }
-
-    if (sink_process_usec != process_usec)
-        pa_log_info("period size of reference and source isn't same. memchunk will be copied");
-
-    o->thread_info.processor = pa_processor_new(c, process_usec / PA_USEC_PER_MSEC,
-                                                &o->sample_spec,
-                                                &o->channel_map,
-                                                &u->source->sample_spec,
-                                                method);
-    if (!o->thread_info.processor) {
-        pa_log_error("Failed to create pa_processor. echo-cancellation will be disabled");
-        goto fail;
-    }
-
-    r = pa_processor_bind_reference(o->thread_info.processor,
-                                    &u->sink->sample_spec,
-                                    &u->sink->channel_map);
-    if (r < 0) {
-        pa_log_error("Failed to bind reference source");
-        goto fail;
-    }
-
-    pa_log_info("echo-cancel source-output(%u) created. process_msec(%u), sink_process_msec(%u), method(%s)",
-                        o->index,
-                        (uint32_t)(process_usec / PA_USEC_PER_MSEC),
-                        (uint32_t)(sink_process_usec / PA_USEC_PER_MSEC),
-                        pa_processor_method_to_string(method));
-
-    update_state_by_source(u, true);
-
-    return PA_HOOK_OK;
-
-fail:
-    o->flags &= ~PA_SOURCE_OUTPUT_ECHO_CANCEL; // TODO: need to consider DONT_MOVE define
-    if (o->thread_info.processor)
-        pa_processor_free(o->thread_info.processor);
-
-    return PA_HOOK_OK;
-}
-
-/* Call from main thread */
-static pa_hook_result_t source_output_unlink_cb(pa_core *c, pa_source_output *o, void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-
-    pa_assert(c);
-    pa_assert(o);
-    pa_assert(u);
-
-    if (!(o->flags & PA_SOURCE_OUTPUT_ECHO_CANCEL))
-        return PA_HOOK_OK;
-
-    update_state_by_source(u, false);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t source_output_unlink_post_cb(pa_core *c, pa_source_output *o, void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-
-    pa_assert(c);
-    pa_assert(o);
-    pa_assert(u);
-
-    if (!(o->flags & PA_SOURCE_OUTPUT_ECHO_CANCEL))
-        return PA_HOOK_OK;
-
-    pa_processor_free(o->thread_info.processor);
-
-    u->source = NULL;
-    u->sink = NULL;
-
-    pa_log_info("echo-cancel source-output(%u) is unlinked", o->index);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-
-    pa_assert(c);
-    pa_assert(s);
-    pa_assert(u);
-
-    if (s != u->sink)
-        return PA_HOOK_OK;
-
-    if (s->state == PA_SINK_RUNNING)
-        update_state_by_sink(u, true);
-    else if (s->state == PA_SINK_SUSPENDED || s->state == PA_SINK_IDLE)
-        update_state_by_sink(u, false);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t source_unlink_cb(pa_core *core, pa_source *source, void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-
-    pa_assert(u);
-
-    if (!u->source || u->source != source)
-        return PA_HOOK_OK;
-
-    pa_log_warn("echo-cancel source is unlinked during processing.");
-
-    update_state_by_source(u, false);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_unlink_cb(pa_core *core, pa_sink *sink, void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-
-    pa_assert(u);
-
-    if (!u->sink || u->sink != sink)
-        return PA_HOOK_OK;
-
-    pa_log_warn("echo-cancel sink is unlinked during processing.");
-
-    update_state_by_sink(u, false);
-
-    return PA_HOOK_OK;
-}
-
-static void thread_func(void *userdata) {
-    struct userdata *u = (struct userdata *)userdata;
-
-    pa_assert(u);
-
-    pa_log_debug("Thread starting up");
-
-    if (u->core->realtime_scheduling)
-        pa_thread_make_realtime(u->core->realtime_priority);
-
-    pa_thread_mq_install(&u->thread_mq);
-
-    for (;;) {
-        int ret;
-
-        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
-            goto fail;
-    }
-
-fail:
-    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core),
-                            PA_CORE_MESSAGE_UNLOAD_MODULE, u->m, 0, NULL, NULL);
-    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-
-    pa_thread_mq_done(&u->thread_mq);
-
-    pa_log_debug("Thread shutting down");
-}
-
-int pa__init(pa_module *m) {
-    pa_modargs *ma = NULL;
-    struct userdata *u = NULL;
-
-    pa_assert(m);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log_error("Failed to parse module arguments.");
-        return -1;
-    }
-
-    m->userdata = u = pa_xnew0(struct userdata, 1);
-    u->core = m->core;
-    u->m = m;
-    u->force_method = pa_xstrdup(pa_modargs_get_value(ma, "method", NULL));
-
-    u->echo_cancel = pa_msgobject_new(pa_echo_cancel);
-    u->echo_cancel->parent.process_msg = process_msg;
-    u->echo_cancel->u = u;
-
-    u->rtpoll = pa_rtpoll_new();
-    u->asyncmsgq_source = pa_asyncmsgq_new(0);
-    u->asyncmsgq_sink = pa_asyncmsgq_new(0);
-
-    pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY, u->asyncmsgq_sink);
-    pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY, u->asyncmsgq_source);
-    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
-
-    if (!(u->thread = pa_thread_new("tizenaudio-echo-cancel", thread_func, u))) {
-        pa_log_error("Failed to create thread.");
-        goto fail;
-    }
-
-    u->sink_unlink_slot =
-        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_UNLINK],
-                    PA_HOOK_NORMAL, (pa_hook_cb_t) sink_unlink_cb, u);
-    u->source_unlink_slot =
-        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK],
-                    PA_HOOK_NORMAL, (pa_hook_cb_t) source_unlink_cb, u);
-
-    u->source_output_put_slot =
-        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT],
-                    PA_HOOK_EARLY, (pa_hook_cb_t) source_output_put_cb, u);
-
-    u->source_output_unlink_slot =
-        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK],
-                    PA_HOOK_EARLY, (pa_hook_cb_t) source_output_unlink_cb, u);
-
-    u->source_output_unlink_post_slot =
-        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST],
-                    PA_HOOK_EARLY, (pa_hook_cb_t) source_output_unlink_post_cb, u);
-
-    /* source_output_new_cb must be called after new_cb callback in stream manager.
-     * because stream-manager converts the device_id to the index of the sink */
-    u->source_output_new_slot =
-        pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW],
-                    PA_HOOK_LATE, (pa_hook_cb_t) source_output_new_cb, u);
-
-    u->sink_state_changed_slot =
-        pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED],
-                    PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u);
-
-    /* TODO : need to check sink configuration change */
-    pa_modargs_free(ma);
-
-    return 0;
-
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(m);
-
-    return -1;
-}
-
-void pa__done(pa_module *m) {
-    struct userdata *u;
-
-    pa_assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->source_output_put_slot)
-        pa_hook_slot_free(u->source_output_put_slot);
-
-    if (u->source_output_unlink_slot)
-        pa_hook_slot_free(u->source_output_unlink_slot);
-
-    if (u->source_output_unlink_post_slot)
-        pa_hook_slot_free(u->source_output_unlink_post_slot);
-
-    if (u->source_output_new_slot)
-        pa_hook_slot_free(u->source_output_new_slot);
-
-    if (u->sink_state_changed_slot)
-        pa_hook_slot_free(u->sink_state_changed_slot);
-
-    if (u->asyncmsgq_sink) {
-        if (u->sink) {
-            pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink),
-                    PA_SINK_MESSAGE_SET_AEC_STATE, (void *)false, 0, NULL, NULL);
-
-            send_rebuild_rtpoll(PA_MSGOBJECT(u->sink), NULL, NULL);
-        }
-
-        pa_asyncmsgq_unref(u->asyncmsgq_sink);
-    }
-
-    if (u->asyncmsgq_source) {
-        if (u->source) {
-            pa_source_output *o;
-
-            pa_asyncmsgq_post(u->source->asyncmsgq, PA_MSGOBJECT(u->source),
-                    PA_SOURCE_MESSAGE_SET_AEC_STATE, (void *)false, 0, NULL, NULL);
-
-            send_rebuild_rtpoll(PA_MSGOBJECT(u->source), NULL, NULL);
-
-            if ((o = find_source_output_by_flags(u->source)))
-                pa_processor_free(o->thread_info.processor);
-        }
-
-        pa_asyncmsgq_unref(u->asyncmsgq_source);
-    }
-
-    if (u->rtpoll)
-        pa_rtpoll_free(u->rtpoll);
-
-    pa_thread_mq_done(&u->thread_mq);
-
-    pa_xfree(u->force_method);
-
-    pa_xfree(u);
-}
-
diff --git a/src/echo-cancel/processor.c b/src/echo-cancel/processor.c
deleted file mode 100644 (file)
index e7c5af9..0000000
+++ /dev/null
@@ -1,560 +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/xmalloc.h>
-#include <pulse/timeval.h>
-#include <pulse/channelmap.h>
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/resampler.h>
-#include <pulsecore/core-util.h>
-
-#include "processor.h"
-
-//#define __DEBUG__
-#ifdef __DEBUG__
-#include <stdio.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif
-
-#define MEMBLOCKQ_MAXLENGTH (16 * 1024 * 1024)
-#define MEMBLOCKQ_PUSH_REFERENCE_BLOCK_ALIGN
-
-typedef struct pa_processor_method_interface pa_processor_method_interface;
-struct pa_processor {
-    pa_core *core;
-    pa_processor_method_interface *intf;
-    pa_processor_method_t method;
-    void *priv;
-
-    size_t process_frames;
-    pa_usec_t process_usec;
-
-    /* process_bytes and reference_process_bytes must be same logically,
-     * but they have a different scale */
-    size_t process_bytes;
-    size_t reference_process_bytes;
-
-    pa_sample_spec *output_ss;
-    pa_channel_map *output_chmap;
-    pa_sample_spec *source_ss;
-    pa_sample_spec reference_memblockq_ss;
-
-    pa_resampler *resampler;
-    pa_memblockq *reference_memblockq;
-    pa_memblockq *output_memblockq;
-
-#ifdef __DEBUG__
-    int fdrec, fdref, fdout;
-    struct timeval before, after;
-#endif
-};
-
-struct pa_processor_method_interface {
-    const char *name;
-    void *(*create)(size_t nframes, pa_sample_spec *ss);
-    int32_t (*process)(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
-    int32_t (*destroy)(void *priv);
-    int32_t (*change_reference_spec)(void *priv, pa_sample_spec *source_ss, pa_sample_spec *sample_spec, pa_channel_map *map);
-};
-
-extern void *adrian_create(size_t nframes, pa_sample_spec *ss);
-extern int32_t adrian_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
-extern int32_t adrian_destroy(void *priv);
-
-extern void *speex_create(size_t nframes, pa_sample_spec *ss);
-extern int32_t speex_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
-extern int32_t speex_destroy(void *priv);
-
-#ifdef SUPPORT_METHOD_WEBRTC
-extern void *webrtc_audio_create(size_t nframes, pa_sample_spec *ss);
-extern int32_t webrtc_audio_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
-extern int32_t webrtc_audio_destroy(void *priv);
-#endif
-
-extern void *reference_copy_create(size_t nframes, pa_sample_spec *ss);
-extern int32_t reference_copy_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
-extern int32_t reference_copy_destroy(void *priv);
-extern int32_t reference_copy_change_reference_spec(void *priv, pa_sample_spec *source_ss, pa_sample_spec *sample_spec, pa_channel_map *map);
-
-#ifdef __DEBUG__
-static void debug_open_file(pa_processor *processor);
-static void debug_timestamp_begin(pa_processor *processor);
-static void debug_timestamp_end(pa_processor *processor);
-static void debug_write_file(pa_processor *processor, int8_t *rec, int8_t *ref, int8_t *out);
-static void debug_close_file(pa_processor *processor);
-#else
-#define debug_open_file(x)
-#define debug_timestamp_begin(x)
-#define debug_timestamp_end(x)
-#define debug_write_file(x, a, b, c)
-#define debug_close_file(x)
-#endif
-
-static struct pa_processor_method_interface method_table[PA_PROCESSOR_METHOD_MAX] = {
-    {
-        "speex",
-        speex_create,
-        speex_process,
-        speex_destroy,
-        NULL,
-    },
-    {
-        "adrian",
-        adrian_create,
-        adrian_process,
-        adrian_destroy,
-        NULL,
-    },
-#ifdef SUPPORT_METHOD_WEBRTC
-    {
-        "webrtc",
-        webrtc_audio_create,
-        webrtc_audio_process,
-        webrtc_audio_destroy,
-        NULL,
-    },
-#endif
-    {
-        "reference_copy",
-        reference_copy_create,
-        reference_copy_process,
-        reference_copy_destroy,
-        reference_copy_change_reference_spec,
-    },
-};
-
-static size_t pa_processor_usec_to_frame(pa_usec_t usec, pa_sample_spec *spec) {
-    pa_assert(spec);
-
-    return pa_usec_to_bytes(usec, spec) / pa_frame_size(spec);
-}
-
-pa_processor *pa_processor_new(pa_core *core,
-                                uint32_t process_msec,
-                                pa_sample_spec *output_ss,
-                                pa_channel_map *output_map,
-                                pa_sample_spec *source_ss,
-                                pa_processor_method_t method) {
-    pa_processor *processor;
-    pa_memchunk silence;
-
-    pa_assert(core);
-    pa_assert(output_ss);
-    pa_assert(output_map);
-    pa_assert(source_ss);
-    pa_assert(method < PA_PROCESSOR_METHOD_MAX);
-
-    processor = pa_xnew0(pa_processor, 1);
-    processor->intf = &method_table[method];
-    processor->core = core;
-    processor->process_usec = process_msec * PA_USEC_PER_MSEC;
-    processor->output_ss = output_ss;
-    processor->output_chmap = output_map;
-    processor->source_ss = source_ss;
-    processor->method = method;
-    processor->process_frames = pa_processor_usec_to_frame(processor->process_usec, processor->output_ss);
-    processor->process_bytes = pa_usec_to_bytes(processor->process_usec, processor->output_ss);
-
-    if (!(processor->priv = processor->intf->create(processor->process_frames, output_ss))) {
-        pa_log_error("Failed to create processor. rate(%d), channels(%d).", output_ss->rate, output_ss->channels);
-        pa_xfree(processor);
-        return NULL;
-    }
-
-    pa_silence_memchunk_get(&core->silence_cache, core->mempool, &silence, output_ss, 0);
-    processor->output_memblockq = pa_memblockq_new("source-output memblockq",
-                                                    0,
-                                                    MEMBLOCKQ_MAXLENGTH,
-                                                    0,
-                                                    processor->output_ss,
-                                                    0,
-                                                    pa_usec_to_bytes(processor->process_usec, output_ss),
-                                                    0,
-                                                    &silence);
-    pa_memblock_unref(silence.memblock);
-
-    pa_log_info("Created processor. memblockq rate(%d), channels(%d), process_msec(%u), "
-                "process_bytes(%zu), method(%s), source rate(%d), channels(%d)",
-                                                    output_ss->rate,
-                                                    output_ss->channels,
-                                                    process_msec,
-                                                    pa_usec_to_bytes(processor->process_usec, output_ss),
-                                                    method_table[method].name,
-                                                    source_ss->rate,
-                                                    source_ss->channels);
-
-    debug_open_file(processor);
-
-    return processor;
-}
-
-int pa_processor_bind_reference(pa_processor *processor,
-                                pa_sample_spec *reference_ss,
-                                pa_channel_map *reference_chmap) {
-
-    pa_sample_spec sample_spec;
-    pa_channel_map channel_map;
-    pa_memchunk silence;
-
-    pa_assert(processor);
-    pa_assert(processor->intf);
-    pa_assert(processor->output_ss);
-    pa_assert(processor->output_chmap);
-    pa_assert(reference_ss);
-    pa_assert(reference_chmap);
-
-    sample_spec = *processor->output_ss;
-    channel_map = *processor->output_chmap;
-
-    /* select reference memblockq sample_spec and channelmap */
-    if (processor->intf->change_reference_spec) {
-        if (processor->intf->change_reference_spec(processor->priv, processor->source_ss, &sample_spec, &channel_map) < 0) {
-            pa_log_error("Failed to get reference info");
-            return -1;
-        }
-    }
-
-    /* Create resampler */
-    if (!pa_sample_spec_equal(reference_ss, &sample_spec)) {
-        if (processor->resampler)
-            pa_resampler_free(processor->resampler);
-
-        processor->resampler = pa_resampler_new(processor->core->mempool,
-                                                reference_ss, reference_chmap,
-                                                &sample_spec, &channel_map,
-                                                processor->core->lfe_crossover_freq,
-                                                processor->core->resample_method, 0);
-        if (!processor->resampler) {
-            pa_log_error("Failed to allocate reference resampler");
-            return -1;
-        }
-    }
-
-    processor->reference_memblockq_ss = sample_spec;
-    processor->reference_process_bytes = pa_usec_to_bytes(processor->process_usec, &processor->reference_memblockq_ss);
-
-    /* Create memblockq */
-    pa_silence_memchunk_get(&processor->core->silence_cache, processor->core->mempool, &silence, &sample_spec, 0);
-
-    if (processor->reference_memblockq)
-        pa_memblockq_free(processor->reference_memblockq);
-
-    processor->reference_memblockq = pa_memblockq_new("reference memblockq",
-                                                        0,
-                                                        MEMBLOCKQ_MAXLENGTH,
-                                                        0,
-                                                        &processor->reference_memblockq_ss,
-                                                        0,
-                                                        processor->reference_process_bytes,
-                                                        0,
-                                                        &silence);
-    pa_memblock_unref(silence.memblock);
-
-    pa_log_debug("Created reference memblockq rate(%d), channels(%d), msec(%" PRId64 "), minreq bytes(%zu)",
-                                                        sample_spec.rate,
-                                                        sample_spec.channels,
-                                                        processor->process_usec / PA_USEC_PER_MSEC,
-                                                        processor->reference_process_bytes);
-
-    return 0;
-}
-
-int pa_processor_setup_reference_memblockq_padding(pa_processor *processor, pa_usec_t latency) {
-    pa_memchunk silence;
-    int64_t write_index, read_index;
-    size_t n, bytes;
-
-    pa_assert(processor);
-
-    bytes = pa_usec_to_bytes(latency, &processor->reference_memblockq_ss);
-
-#ifdef MEMBLOCKQ_PUSH_REFERENCE_BLOCK_ALIGN
-    n = (bytes + (processor->reference_process_bytes - 1)) / processor->reference_process_bytes;
-    pa_silence_memchunk_get(
-            &processor->core->silence_cache,
-            processor->core->mempool,
-            &silence,
-            &processor->reference_memblockq_ss,
-            processor->reference_process_bytes);
-#else
-    n = 1;
-    pa_silence_memchunk_get(
-            &processor->core->silence_cache,
-            processor->core->mempool,
-            &silence,
-            &processor->reference_memblockq_ss,
-            bytes);
-#endif
-
-    write_index = pa_memblockq_get_write_index(processor->reference_memblockq);
-    read_index = pa_memblockq_get_read_index(processor->reference_memblockq);
-
-    for (; n > 0; n--)
-        pa_memblockq_push(processor->reference_memblockq, &silence);
-
-    pa_memblock_unref(silence.memblock);
-
-    pa_log_info("push n(%u) silence blocks. latency(%" PRId64 "), ref_process_msec(%" PRId64 ") "
-                "write_index(%" PRId64 "->%" PRId64 "), read_index(%" PRId64 "->%" PRId64 ")",
-                        pa_memblockq_get_nblocks(processor->reference_memblockq),
-                        latency,
-                        pa_bytes_to_usec(processor->reference_process_bytes,
-                        &processor->reference_memblockq_ss) / PA_USEC_PER_MSEC,
-                        write_index, pa_memblockq_get_write_index(processor->reference_memblockq),
-                        read_index, pa_memblockq_get_read_index(processor->reference_memblockq));
-
-    return 0;
-}
-
-int pa_processor_process(pa_processor *processor, pa_memchunk *chunk, pa_memchunk *ochunk) {
-    int r = -1;
-    int8_t *recording = NULL;
-    int8_t *reference = NULL;
-    int8_t *output = NULL;
-    bool silence = false;
-
-    pa_memchunk ichunk, rchunk;
-
-    pa_assert(processor);
-    pa_assert(processor->output_memblockq);
-    pa_assert(processor->reference_memblockq);
-    pa_assert(processor->intf);
-    pa_assert(processor->process_bytes > 0ULL);
-    pa_assert(processor->reference_process_bytes > 0ULL);
-    pa_assert(chunk);
-    pa_assert(ochunk);
-
-    if ((r = pa_memblockq_push(processor->output_memblockq, chunk)) < 0) {
-        pa_log_error("Failed to push chunk to reference memblockq");
-        return r;
-    }
-
-    if ((r = pa_memblockq_peek_fixed_size(processor->reference_memblockq, processor->reference_process_bytes, &rchunk)) < 0) {
-        pa_log_error("Failed to get memblock from reference memblockq");
-        return r;
-    }
-    silence = pa_memblock_is_silence(rchunk.memblock);
-
-    if ((r = pa_memblockq_peek_fixed_size(processor->output_memblockq, processor->process_bytes, &ichunk)) < 0) {
-        pa_log_error("Failed to get memblock from output memblockq");
-        return r;
-    }
-
-    ochunk->index = 0;
-    ochunk->length = ichunk.length;
-    ochunk->memblock = pa_memblock_new(processor->core->mempool, ochunk->length);
-
-    recording = pa_memblock_acquire_chunk(&ichunk);
-    reference = pa_memblock_acquire_chunk(&rchunk);
-    output = pa_memblock_acquire_chunk(ochunk);
-
-    debug_timestamp_begin(processor);
-
-    r = processor->intf->process(processor->priv, recording, reference, output);
-
-    debug_timestamp_end(processor);
-    debug_write_file(processor, recording, reference, output);
-
-    pa_memblock_release(ichunk.memblock);
-    pa_memblock_release(rchunk.memblock);
-    pa_memblock_release(ochunk->memblock);
-
-    pa_memblock_unref(rchunk.memblock);
-    pa_memblockq_drop(processor->reference_memblockq, rchunk.length);
-
-    pa_memblock_unref(ichunk.memblock);
-    pa_memblockq_drop(processor->output_memblockq, ichunk.length);
-
-    pa_log_debug("Post-process. rec(%" PRIu64 "ms), ref(%" PRIu64 "ms) out(%" PRIu64 "ms), "
-                "silence(%d), reference memblockq windex:rindex(%" PRId64 ":%" PRId64 ")",
-                        pa_bytes_to_usec(ichunk.length, processor->output_ss) / PA_USEC_PER_MSEC,
-                        pa_bytes_to_usec(rchunk.length, &processor->reference_memblockq_ss) / PA_USEC_PER_MSEC,
-                        pa_bytes_to_usec(ochunk->length, processor->output_ss) / PA_USEC_PER_MSEC,
-                        silence,
-                        pa_memblockq_get_write_index(processor->reference_memblockq),
-                        pa_memblockq_get_read_index(processor->reference_memblockq));
-
-    return r;
-}
-
-int pa_processor_push_reference(pa_processor *processor, pa_memchunk *chunk) {
-    pa_memchunk ochunk;
-    int r;
-
-    pa_assert(processor);
-    pa_assert(chunk);
-
-    if (processor->resampler) {
-        pa_resampler_run(processor->resampler, chunk, &ochunk);
-        chunk = &ochunk;
-    }
-
-    if ((r = pa_memblockq_push(processor->reference_memblockq, chunk)) < 0)
-        pa_log_error("Failed to push chunk to reference memblockq");
-
-    if (processor->resampler)
-        pa_memblock_unref(chunk->memblock);
-
-    pa_log_debug("Pushed reference data. bytes(%zu), msec(%" PRIu64 "ms), nblocks(%d) index(%" PRId64 ":%" PRId64 ")",
-                    chunk->length,
-                    pa_bytes_to_usec(chunk->length, &processor->reference_memblockq_ss) / PA_USEC_PER_MSEC,
-                    pa_memblockq_get_nblocks(processor->reference_memblockq),
-                    pa_memblockq_get_write_index(processor->reference_memblockq),
-                    pa_memblockq_get_read_index(processor->reference_memblockq));
-
-    return r;
-}
-
-void pa_processor_flush(pa_processor *processor) {
-    pa_assert(processor);
-
-    if (processor->reference_memblockq)
-        pa_memblockq_flush_read(processor->reference_memblockq);
-
-    if (processor->output_memblockq)
-        pa_memblockq_flush_read(processor->output_memblockq);
-}
-
-int pa_processor_free(pa_processor *processor) {
-    pa_assert(processor);
-    pa_assert(processor->priv);
-    pa_assert(processor->intf);
-
-    if (processor->intf->destroy(processor->priv) < 0)
-        pa_log_error("Failed to destroy processor");
-
-    if (processor->resampler)
-        pa_resampler_free(processor->resampler);
-
-    if (processor->reference_memblockq)
-        pa_memblockq_free(processor->reference_memblockq);
-
-    if (processor->output_memblockq)
-        pa_memblockq_free(processor->output_memblockq);
-
-    debug_close_file(processor);
-
-    pa_xfree(processor);
-
-    return 0;
-}
-
-const char *pa_processor_method_to_string(pa_processor_method_t method) {
-    if (method >= PA_PROCESSOR_METHOD_MAX)
-        return NULL;
-
-    return method_table[method].name;
-}
-
-pa_processor_method_t pa_processor_get_method(const char *request_method) {
-    pa_processor_method_t method;
-
-    if (pa_streq(request_method, "speex"))
-        method = PA_PROCESSOR_SPEEX;
-    else if (pa_streq(request_method, "adrian"))
-        method = PA_PROCESSOR_ADRIAN;
-#ifdef SUPPORT_METHOD_WEBRTC
-    else if (pa_streq(request_method, "webrtc"))
-        method = PA_PROCESSOR_WEBRTC;
-#endif
-    else if (pa_streq(request_method, "reference_copy"))
-        method = PA_PROCESSOR_REFERENCE_COPY;
-    else
-#ifdef SUPPORT_METHOD_WEBRTC
-        method = PA_PROCESSOR_WEBRTC;
-#else
-        method = PA_PROCESSOR_SPEEX;
-#endif
-
-    pa_log_info("processing method is selected. method(%d), request_method(%s), method_to_string(%s)",
-                method, request_method, pa_processor_method_to_string(method));
-
-    return method;
-}
-
-#ifdef __DEBUG__
-static void debug_open_file(pa_processor *processor) {
-    static int n = 1;
-    char rec[32], ref[32], out[32];
-
-    snprintf(rec, sizeof(rec), "/tmp/rec-%d.raw", n);
-    snprintf(ref, sizeof(ref), "/tmp/ref-%d.raw", n);
-    snprintf(out, sizeof(out), "/tmp/out-%d.raw", n);
-    n += 1;
-
-    unlink(rec);
-    unlink(ref);
-    unlink(out);
-
-    processor->fdrec = open(rec, O_RDWR | O_CREAT | O_TRUNC, 777);
-    processor->fdref = open(ref, O_RDWR | O_CREAT | O_TRUNC, 777);
-    processor->fdout = open(out, O_RDWR | O_CREAT | O_TRUNC, 777);
-}
-
-static void debug_timestamp_begin(pa_processor *processor) {
-    gettimeofday(&processor->before, NULL);
-}
-
-static void debug_timestamp_end(pa_processor *processor) {
-    gettimeofday(&processor->after, NULL);
-
-    pa_log_debug("It takes time (%ld)ms.",
-                    1000 * (processor->after.tv_sec - processor->before.tv_sec)
-                    + (processor->after.tv_usec - processor->before.tv_usec) / 1000);
-}
-
-static void debug_write_file(pa_processor *processor, int8_t *rec, int8_t *ref, int8_t *out) {
-    if (rec && write(processor->fdrec, rec, processor->process_bytes) <= 0)
-        pa_log_error("Failed to write recording buffer");
-
-    if (ref && write(processor->fdref, ref, processor->reference_process_bytes) <= 0)
-        pa_log_error("Failed to write reference buffer");
-
-    if (out && write(processor->fdout, out, processor->process_bytes) <= 0)
-        pa_log_error("Failed to write ref buffer");
-}
-
-static void debug_close_file(pa_processor *processor) {
-    if (processor->fdrec) {
-        close(processor->fdrec);
-        processor->fdrec = -1;
-    }
-
-    if (processor->fdref) {
-        close(processor->fdref);
-        processor->fdref = -1;
-    }
-
-    if (processor->fdout) {
-        close(processor->fdout);
-        processor->fdout = -1;
-    }
-}
-#endif
-
diff --git a/src/echo-cancel/processor.h b/src/echo-cancel/processor.h
deleted file mode 100644 (file)
index c93bf49..0000000
+++ /dev/null
@@ -1,65 +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.
-***/
-
-#ifndef foopulseprocessorfoo
-#define foopulseprocessorfoo
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <pulsecore/core.h>
-#include <pulsecore/memblock.h>
-#include <pulse/channelmap.h>
-#include <pulse/sample.h>
-
-/* These are used as an index to choose a method in method_table. Keep this order */
-typedef enum {
-    PA_PROCESSOR_SPEEX,
-    PA_PROCESSOR_ADRIAN,
-#ifdef SUPPORT_METHOD_WEBRTC
-    PA_PROCESSOR_WEBRTC,
-#endif
-    PA_PROCESSOR_REFERENCE_COPY,
-    PA_PROCESSOR_METHOD_MAX,
-} pa_processor_method_t;
-
-typedef struct pa_processor pa_processor;
-
-pa_processor *pa_processor_new(pa_core *core,
-                                uint32_t process_msec,
-                                pa_sample_spec *output_ss,
-                                pa_channel_map *output_map,
-                                pa_sample_spec *source_ss,
-                                pa_processor_method_t method);
-int pa_processor_bind_reference(pa_processor *processor,
-                                pa_sample_spec *reference_ss,
-                                pa_channel_map *reference_chmap);
-int pa_processor_setup_reference_memblockq_padding(pa_processor *processor, pa_usec_t latency);
-int pa_processor_process(pa_processor *processor, pa_memchunk *rec, pa_memchunk *out);
-int pa_processor_push_reference(pa_processor *processor, pa_memchunk *chunk);
-void pa_processor_flush(pa_processor *processor);
-int pa_processor_free(pa_processor *processor);
-pa_processor_method_t pa_processor_get_method(const char *requset_method);
-const char *pa_processor_method_to_string(pa_processor_method_t method);
-
-#endif
-
diff --git a/src/preprocessor/method_factory.c b/src/preprocessor/method_factory.c
new file mode 100644 (file)
index 0000000..902f7fd
--- /dev/null
@@ -0,0 +1,104 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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 "processor.h"
+
+extern void *speex_create(size_t nframes, pa_sample_spec *ss);
+extern int32_t speex_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
+extern int32_t speex_destroy(void *priv);
+
+extern void *webrtc_audio_create(size_t nframes, pa_sample_spec *ss);
+extern int32_t webrtc_audio_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
+extern int32_t webrtc_audio_destroy(void *priv);
+
+extern void *reference_copy_create(size_t nframes, pa_sample_spec *ss);
+extern int32_t reference_copy_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
+extern int32_t reference_copy_destroy(void *priv);
+
+extern void *rnnoise_ns_create(size_t nframes, pa_sample_spec *ss);
+extern int32_t rnnoise_ns_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
+extern int32_t rnnoise_ns_destroy(void *priv);
+
+extern void *processor_reference_filesrc_create(pa_sample_spec *ss);
+extern size_t processor_reference_filesec_read(void *priv, void *ref, size_t length);
+extern void processor_reference_filesrc_destroy(void *priv);
+
+pa_processor_method *pa_processor_method_create(pa_processor_method_t type) {
+    pa_processor_method *m = pa_xnew0(pa_processor_method, 1);
+
+    switch (type) {
+        case PROCESSOR_METHOD_SPEEX:
+            m->name = "speex";
+            m->create = speex_create;
+            m->process = speex_process;
+            m->destroy = speex_destroy;
+            break;
+        case PROCESSOR_METHOD_WEBRTC:
+            m->name = "webrtc";
+            m->create = webrtc_audio_create;
+            m->process = webrtc_audio_process;
+            m->destroy = webrtc_audio_destroy;
+            break;
+        case PROCESSOR_METHOD_REFERENCE_COPY:
+            m->name = "reference_copy";
+            m->create = reference_copy_create;
+            m->process = reference_copy_process;
+            m->destroy = reference_copy_destroy;
+            break;
+        case PROCESSOR_METHOD_RNNOISE:
+            m->name = "rnnoise";
+            m->create = rnnoise_ns_create;
+            m->process = rnnoise_ns_process;
+            m->destroy = rnnoise_ns_destroy;
+            break;
+        default:
+            pa_assert_not_reached();
+            break;
+    }
+
+    return m;
+}
+
+pa_processor_reference_method *pa_processor_reference_method_create(pa_processor_reference_method_t type) {
+    pa_processor_reference_method *m = pa_xnew0(pa_processor_reference_method, 1);
+
+    switch (type) {
+        case PROCESSOR_REFERENCE_METHOD_AUDIOSHARE:
+            /* TODO: not support yet */
+            break;
+        case PROCESSOR_REFERENCE_METHOD_FILESRC:
+            m->name = "filesrc";
+            m->create = processor_reference_filesrc_create;
+            m->read = processor_reference_filesec_read;
+            m->destroy = processor_reference_filesrc_destroy;
+            break;
+        default:
+            pa_log_error("type %d", type);
+            pa_assert_not_reached();
+            break;
+    }
+
+    return m;
+}
diff --git a/src/preprocessor/method_factory.h b/src/preprocessor/method_factory.h
new file mode 100644 (file)
index 0000000..110d150
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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.
+***/
+
+#ifndef foopulsemethod_factoryfoo
+#define foopulsemethod_factoryfoo
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+typedef enum {
+    PROCESSOR_METHOD_SPEEX,
+    PROCESSOR_METHOD_WEBRTC,
+    PROCESSOR_METHOD_REFERENCE_COPY,
+    PROCESSOR_METHOD_RNNOISE,
+    PROCESSOR_METHOD_MAX,
+} pa_processor_method_t;
+
+typedef enum {
+    PROCESSOR_REFERENCE_METHOD_NONE,
+    PROCESSOR_REFERENCE_METHOD_AUDIOSHARE,
+    PROCESSOR_REFERENCE_METHOD_FILESRC,
+    PROCESSOR_REFERENCE_METHOD_MAX,
+} pa_processor_reference_method_t;
+
+typedef struct pa_processor_method {
+    const char *name;
+    void *(*create)(size_t nframes, pa_sample_spec *ss);
+    int32_t (*process)(void *priv, int8_t *rec, int8_t *ref, int8_t *out);
+    int32_t (*destroy)(void *priv);
+} pa_processor_method;
+
+typedef struct pa_processor_reference_method {
+    const char *name;
+    void *(*create)(pa_sample_spec *ss);
+    size_t (*read)(void *priv, void *ref, size_t n);
+    void (*destroy)(void *priv);
+} pa_processor_reference_method;
+
+pa_processor_method *pa_processor_method_create(pa_processor_method_t type);
+pa_processor_reference_method *pa_processor_reference_method_create(pa_processor_reference_method_t type);
+
+#endif
+
similarity index 67%
rename from src/echo-cancel/method_reference_copy.c
rename to src/preprocessor/method_reference_copy.c
index 7b397ec..b3da526 100644 (file)
@@ -59,7 +59,7 @@ int32_t reference_copy_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out
     pa_assert(out);
 
     rec_bytes = pa_frame_size(&rc->ss);
-    ref_bytes = rc->reference_channels * pa_sample_size(&rc->ss);
+    ref_bytes = pa_sample_size(&rc->ss); /* reference must be 1 channel */
     actual_bytes = rec_bytes - ref_bytes;
     total_bytes = rec_bytes * rc->nframes;
 
@@ -84,32 +84,3 @@ int32_t reference_copy_destroy(void *priv) {
     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/preprocessor/method_rnnoise.c b/src/preprocessor/method_rnnoise.c
new file mode 100644 (file)
index 0000000..ca4de9f
--- /dev/null
@@ -0,0 +1,102 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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 <rnnoise.h>
+
+#include <assert.h>
+
+struct method_rnnoise {
+    DenoiseState *st;
+    pa_sample_spec ss;
+    size_t frames;
+    float *buffer;
+};
+
+void *rnnoise_ns_create(size_t nframes, pa_sample_spec *ss) {
+    struct method_rnnoise *rnnoise = NULL;
+
+    pa_assert(ss);
+
+    if (ss->channels >= 2 || ss->format != PA_SAMPLE_S16LE || ss->rate != 48000) {
+        pa_log_error("rnnoise limitation. not support rate(%d) ch(%d) format(%d)",
+                                        ss->rate, ss->channels, ss->format);
+        return NULL;
+    }
+
+    rnnoise = pa_xnew0(struct method_rnnoise, 1);
+    rnnoise->st = rnnoise_create(NULL);
+    rnnoise->ss = *ss;
+    rnnoise->frames = nframes;
+    rnnoise->buffer = pa_xnew(float, nframes * ss->channels);
+
+    pa_log_info("rnnoise initialized. frame(%zu), rate(%d) channels(%d)",
+                    nframes, ss->rate, ss->channels);
+
+    return rnnoise;
+}
+
+int32_t rnnoise_ns_process(void *priv, int8_t *rec, int8_t *ref, int8_t *out) {
+    struct method_rnnoise *rnnoise = priv;
+    int16_t *ptr;
+    int i;
+
+    assert(rec);
+    assert(out);
+
+    ptr = (int16_t *)rec;
+    for (i=0; i<rnnoise->frames; i++) {
+        rnnoise->buffer[i] = 0;
+        rnnoise->buffer[i] = ptr[i];
+    }
+
+    rnnoise_process_frame(rnnoise->st, rnnoise->buffer, rnnoise->buffer);
+
+    ptr = (int16_t *)out;
+    for (i=0; i<rnnoise->frames; i++) {
+        ptr[i] = 0;
+        ptr[i] = rnnoise->buffer[i];
+    }
+
+    return 0;
+}
+
+int32_t rnnoise_ns_destroy(void *priv) {
+    struct method_rnnoise *rnnoise = priv;
+
+    pa_assert(rnnoise);
+
+    rnnoise_destroy(rnnoise->st);
+
+    pa_xfree(rnnoise->buffer);
+    pa_xfree(rnnoise);
+
+    return 0;
+}
similarity index 98%
rename from src/echo-cancel/method_webrtc.cpp
rename to src/preprocessor/method_webrtc.cpp
index 4b7721c..0572d66 100644 (file)
@@ -118,8 +118,6 @@ void *webrtc_audio_create(size_t nframes, pa_sample_spec *ss) {
     }
 
     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);
diff --git a/src/preprocessor/module-tizenaudio-preprocessor.c b/src/preprocessor/module-tizenaudio-preprocessor.c
new file mode 100644 (file)
index 0000000..bc2fb9b
--- /dev/null
@@ -0,0 +1,695 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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 <pulsecore/macro.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/namereg.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+#include "preprocessor-def.h"
+#include "processor.h"
+#include "processor_holder.h"
+
+PA_MODULE_AUTHOR("Tizen");
+PA_MODULE_DESCRIPTION("Tizen Audio Preprocessor");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
+/*
+PA_MODULE_USAGE(
+        "use_system_reference=<name of method using for reference. [method]=audio-share, filesrc > ");
+        */
+
+#define DEFAULT_PROCESS_USEC 10000
+
+typedef struct preprocessor pa_preprocessor;
+struct userdata {
+    pa_core *core;
+    pa_module *m;
+
+    pa_hook_slot *source_output_fixate_slot;
+    pa_hook_slot *source_output_put_slot;
+    pa_hook_slot *source_output_unlink_post_slot;
+
+    pa_rtpoll *rtpoll;
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_asyncmsgq *asyncmsgq_sink;
+    pa_asyncmsgq *asyncmsgq_source;
+
+    pa_preprocessor *preprocessor;
+    bool enable_in_thread;
+    bool reset_lazy_reference;
+};
+
+struct preprocessor {
+    pa_msgobject parent;
+    struct userdata *u;
+};
+
+PA_DEFINE_PRIVATE_CLASS(pa_preprocessor, pa_msgobject);
+#define PA_PREPROCESSOR(o) (pa_preprocessor_cast(o))
+
+#define MEMBLOCKQ_MAXLENGTH (16 * 1024 * 1024)
+
+static const char* const valid_modargs[] = {
+    NULL,
+};
+
+static int proplist_get_fragment_size_usec(pa_proplist *p, pa_sample_spec *sample_spec, pa_usec_t *usec) {
+    const char *prop_fragsize;
+    uint32_t fragsize;
+
+    pa_assert(p);
+    pa_assert(sample_spec);
+    pa_assert(usec);
+
+    if (!(prop_fragsize = pa_proplist_gets(p, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE)))
+        return -1;
+
+    if (pa_atou(prop_fragsize, &fragsize))
+        return -1;
+
+    *usec = pa_bytes_to_usec(fragsize, sample_spec);
+
+    return 0;
+}
+
+static bool is_preprocessor_source_output(pa_proplist *p) {
+    pa_assert(p);
+
+    if (pa_proplist_gets(p, PA_PROP_MEDIA_PREPROCESSOR_METHOD))
+        return true;
+
+    return false;
+}
+
+static bool is_preprocessor_marked(pa_source_output_flags_t flags) {
+    if (flags & PA_SOURCE_OUTPUT_PREPROCESSOR)
+        return true;
+
+    return false;
+}
+
+static bool lookup_preprocessor_exist(pa_core *c) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_assert(c);
+
+    PA_IDXSET_FOREACH(o, c->source_outputs, idx) {
+        pa_source_output_assert_ref(o);
+
+        if (o->flags & PA_SOURCE_OUTPUT_PREPROCESSOR)
+            return true;
+    }
+
+    return false;
+}
+
+static pa_sink *convert_reference_str_to_sink(pa_core *c, const char *str) {
+    int32_t idx;
+    pa_sink *s;
+
+    pa_assert(c);
+    pa_assert(str);
+
+    if (pa_atoi(str, &idx) < 0)
+        return NULL;
+
+    s = pa_idxset_get_by_index(c->sinks, idx);
+    if (!s)
+        return NULL;
+
+    pa_log_info("Found reference sink(%d, %s)", s->index, s->name);
+
+    return s;
+}
+
+static int send_message_rebuild_rtpoll(pa_msgobject *dst, pa_msgobject *src, pa_asyncmsgq *q, pa_processor_holder *holder) {
+    struct arguments {
+        pa_msgobject *o;
+        pa_asyncmsgq *q;
+        pa_processor_holder *holder;
+    } args;
+
+    pa_asyncmsgq *asyncmsgq;
+    int code;
+
+    pa_assert(dst);
+    pa_assert(pa_sink_isinstance(dst));
+
+    args.o = src;
+    args.q = q;
+    args.holder = holder;
+
+    asyncmsgq = PA_SINK(dst)->asyncmsgq;
+    code = PA_SINK_MESSAGE_PREPROCESSOR_REBUILD_RTPOLL;
+
+    pa_asyncmsgq_send(asyncmsgq, dst, code, src ? (void *)&args : NULL, 0, NULL);
+
+    return 0;
+}
+
+static void connect_to_reference_sink(pa_processor_holder *holder, pa_msgobject *o, pa_asyncmsgq *q, bool enable) {
+    pa_sink *sink;
+    pa_processor_reference *reference;
+
+    pa_assert(holder);
+
+    reference = pa_processor_holder_get_connected_processor_reference(holder);
+    if (!reference)
+        return;
+
+    sink = pa_processor_reference_get_sink(reference);
+    if (!sink)
+        return;
+
+    if (enable)
+        send_message_rebuild_rtpoll(PA_MSGOBJECT(sink), o, q, holder);
+    else
+        send_message_rebuild_rtpoll(PA_MSGOBJECT(sink), NULL, NULL, NULL);
+
+    pa_log_info("connected to reference sink. enable(%d), sink(%s)", enable, sink->name);
+}
+
+static void terminate_preprocessor_holder(struct userdata *u) {
+    pa_processor_holder *holder;
+    pa_source_output *o;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_assert(u);
+
+    PA_IDXSET_FOREACH(o, u->core->source_outputs, idx) {
+        pa_source_output_assert_ref(o);
+
+        if (is_preprocessor_marked(o->flags)) {
+            holder = (pa_processor_holder *)o->thread_info.processor_holder;
+            connect_to_reference_sink(holder, NULL, NULL, false);
+
+            source = pa_processor_holder_get_current_source(holder);
+            pa_assert(source);
+            pa_asyncmsgq_send(source->asyncmsgq, PA_MSGOBJECT(source), PA_SOURCE_MESSAGE_PREPROCESSOR_TERMINATE, NULL, 0, NULL);
+
+            pa_processor_holder_free(holder);
+            o->thread_info.processor_holder = NULL;
+            o->preprocess = NULL;
+
+            o->flags &= ~PA_SOURCE_OUTPUT_PREPROCESSOR;
+        }
+    }
+}
+
+static pa_usec_t get_round_trip_latency(pa_source *source, pa_sink *sink) {
+    pa_usec_t sink_latency = 0ULL;
+    pa_usec_t source_latency = 0ULL;
+
+    pa_assert(sink);
+    pa_assert(source);
+
+    pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_latency, 0, NULL);
+    pa_asyncmsgq_send(source->asyncmsgq, PA_MSGOBJECT(source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_latency, 0, NULL);
+
+    pa_log_info("sink latency (%" PRIu64 "), source latency(%" PRIu64 ")", sink_latency, source_latency);
+
+    return sink_latency + source_latency;
+}
+
+static pa_processor_holder *build_processor_holder(pa_core *core, pa_source_output_new_data *data) {
+    const char *state = NULL;
+    const char *processors_list;
+    char *processor_str;
+
+    pa_processor_holder *holder;
+    pa_usec_t process_usec;
+
+    pa_assert(core);
+    pa_assert(data);
+
+    holder = pa_processor_holder_new(&data->sample_spec);
+    if (!holder) {
+        pa_log_error("Failed to allocate pa_processor_holder");
+        return NULL;
+    }
+
+    processors_list = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_PREPROCESSOR_METHOD);
+    pa_assert(processors_list);
+
+    if (proplist_get_fragment_size_usec(data->source->proplist, &data->source->sample_spec, &process_usec) < 0) {
+        pa_processor_holder_free(holder);
+        pa_log_error("Failed to get source fragment usec. use default process usec");
+        return NULL;
+    }
+
+    while ((processor_str = pa_split(processors_list, ",", &state))) {
+        pa_processor *processor;
+        pa_processor_method_t method = pa_processor_method_enum(processor_str);
+
+        processor = pa_processor_new(core, process_usec / PA_USEC_PER_MSEC,
+                                            &data->sample_spec, method);
+        if (!processor) {
+            pa_log_error("Failed to create pa_processor. preprocessor(aec) will be disabled");
+            continue;
+        }
+
+        /* reference */
+        if (pa_processor_method_need_reference_structure(method)) {
+            pa_sample_spec request_ss;
+            pa_processor_reference *reference;
+            pa_sink *sink = convert_reference_str_to_sink(core,
+                            pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_SINK));
+
+            pa_assert(sink);
+
+            request_ss = data->sample_spec;
+            if (method == PROCESSOR_METHOD_REFERENCE_COPY) {
+                request_ss.channels = 1;
+                data->flags |= PA_SOURCE_OUTPUT_NO_REMAP;
+            }
+
+            reference = pa_processor_reference_new_custom(core, sink, &sink->sample_spec,
+                                                            &request_ss, process_usec,
+                                                            PROCESSOR_REFERENCE_METHOD_NONE);
+            if (!reference) {
+                pa_processor_free(processor);
+                pa_log_error("Failed to create reference custom. processor_str(%s)", processor_str);
+                continue;
+            }
+
+            /* holder -> reference -> processor */
+            pa_processor_attach_reference(processor, reference);
+            if (pa_processor_holder_connect_reference(holder, reference) < 0) {
+                pa_processor_reference_free(reference);
+                pa_processor_free(processor);
+                pa_log_error("Failed to connect holder to reference");
+                continue;
+            }
+        }
+
+        pa_processor_holder_register_processor_sequencial(holder, processor);
+
+        pa_log_info("processor was created. processor(%s), process_msec(%lu)",
+                                processor_str, process_usec / PA_USEC_PER_MSEC);
+
+        pa_xfree(processor_str);
+    }
+
+    return holder;
+}
+
+static void destroy_source_output_preprocessor(pa_source_output *o) {
+    pa_processor_holder *holder;
+
+    pa_assert(o);
+
+    holder = (pa_processor_holder *)o->thread_info.processor_holder;
+    pa_processor_holder_free(holder);
+
+    o->preprocess = NULL;
+    o->thread_info.processor_holder = NULL;
+}
+
+static int preprocess(pa_source_output *o, pa_memchunk *chunk, pa_memchunk *ochunk) {
+    pa_processor_holder *holder;
+
+    pa_assert(o);
+    pa_assert(chunk);
+    pa_assert(ochunk);
+    pa_assert(o->thread_info.processor_holder);
+
+    holder = (pa_processor_holder *)o->thread_info.processor_holder;
+
+    /* chunk must contain resampled sound pcm */
+    pa_processor_holder_push_data(holder, chunk);
+    if (pa_processor_holder_pump(holder) < 0) {
+        pa_log_warn("Failed to pump holder");
+        return -1;
+    }
+
+    /* TODO: need to check return value for pse */
+    pa_processor_holder_pull_data(holder, ochunk);
+
+    return 0;
+}
+
+/* rendering thread is separated because ec/ns takes much time in I/O thread */
+static int process_msg(
+        pa_msgobject *o,
+        int code,
+        void *data,
+        int64_t offset,
+        pa_memchunk *chunk) {
+
+    struct userdata *u = PA_PREPROCESSOR(o)->u;
+
+    switch (code) {
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_ADD_OUTPUT: {
+            pa_source_output *output;
+            pa_processor_holder *holder;
+
+            output = PA_SOURCE_OUTPUT(data);
+            pa_assert(output);
+
+            holder = (pa_processor_holder *)output->thread_info.processor_holder;
+            pa_assert(holder);
+
+            pa_processor_holder_set_current_source(holder, output->source);
+            output->preprocess = preprocess;
+            u->enable_in_thread = true;
+            u->reset_lazy_reference = true;
+
+            pa_processor_holder_dump(holder);
+
+            pa_log_info("source-output with a preprocessor will be added");
+
+            break;
+        }
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_REMOVE_OUTPUT: {
+            pa_source_output *output;
+            pa_processor_holder *holder;
+
+            output = PA_SOURCE_OUTPUT(data);
+            pa_assert(output);
+
+            holder = (pa_processor_holder *)output->thread_info.processor_holder;
+            pa_assert(holder);
+
+            pa_processor_holder_set_current_source(holder, NULL);
+            output->preprocess = NULL;
+            u->enable_in_thread = false;
+
+            pa_processor_holder_dump(holder);
+
+            pa_log_info("source-output with a preprocessor will be removed");
+
+            break;
+        }
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_PUSH_DATA:
+            if (!u->enable_in_thread)
+                break;
+
+            pa_source *source = PA_SOURCE(data);
+            pa_source_post(source, chunk);
+
+            break;
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_PUSH_REFERENCE: {
+            pa_processor_holder *holder = (pa_processor_holder *)data;
+            pa_sink *sink;
+            pa_source *source;
+            pa_processor_reference *reference;
+
+            if (!u->enable_in_thread)
+                break;
+
+            reference = pa_processor_holder_get_connected_processor_reference(holder);
+            pa_assert(reference);
+
+            sink = pa_processor_reference_get_sink(reference);
+            source = pa_processor_holder_get_current_source(holder);
+            pa_assert(sink);
+            pa_assert(source);
+
+            /* first reference memchunk after connecting source-output */
+            if (u->reset_lazy_reference) {
+                pa_processor_reference_reset(reference);
+                pa_processor_reference_add_latency_padding(reference, get_round_trip_latency(source, sink));
+
+                u->reset_lazy_reference = false;
+            }
+
+            if (pa_processor_holder_push_reference_data(holder, chunk) < 0)
+                pa_log_error("Failed to push reference data");
+
+            break;
+        }
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_RESET_REFERENCE:
+            if (!u->enable_in_thread)
+                break;
+
+            if (!!data)
+                u->reset_lazy_reference = true;
+
+            break;
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_DESTROY: {
+            pa_source_output *output;
+
+            output = PA_SOURCE_OUTPUT(data);
+            pa_assert(output);
+
+            destroy_source_output_preprocessor(output);
+
+            u->enable_in_thread = false;
+
+            break;
+        }
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_TERMINATE:
+            terminate_preprocessor_holder(u);
+            u->enable_in_thread = false;
+            break;
+        default:
+            pa_assert(0);
+            break;
+    }
+
+    return 0;
+}
+
+static pa_hook_result_t source_output_fixate_cb(pa_core *c, pa_source_output_new_data *data, void *userdata) {
+    struct userdata *u = (struct userdata *)userdata;
+    pa_processor_holder *holder;
+
+    pa_assert(c);
+    pa_assert(u);
+    pa_assert(data);
+
+    if (!is_preprocessor_source_output(data->proplist))
+        return PA_HOOK_OK;
+
+    if (lookup_preprocessor_exist(c)) {
+        pa_log_warn("Don't allow two instances");
+        return PA_HOOK_OK;
+    }
+
+    holder = build_processor_holder(c, data);
+    if (!holder) {
+        pa_log_error("Failed to build holder");
+        return PA_HOOK_OK;
+    }
+
+    data->processor_holder = (void *)holder;
+    if (!data->processor_holder) {
+        pa_log_error("Failed to create processor_holder");
+        goto fail;
+    }
+
+    data->flags |= PA_SOURCE_OUTPUT_PREPROCESSOR;
+
+    pa_processor_holder_set_private_data(holder, u->preprocessor, u->asyncmsgq_source);
+
+    return PA_HOOK_OK;
+
+fail:
+    data->flags &= ~ PA_SOURCE_OUTPUT_PREPROCESSOR;
+    pa_processor_holder_free(holder);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put_cb(pa_core *c, pa_source_output *o, void *userdata) {
+    struct userdata *u = (struct userdata *)userdata;
+    pa_processor_holder *holder;
+
+    pa_assert(c);
+    pa_assert(o);
+    pa_assert(u);
+
+    if (!is_preprocessor_marked(o->flags))
+        return PA_HOOK_OK;
+
+    holder = (pa_processor_holder *)o->thread_info.processor_holder;
+
+    pa_assert(holder);
+    pa_assert(pa_processor_holder_get_current_source(holder));  /* source must be set at ADD_SOURCE_OUTPUT */
+
+    connect_to_reference_sink(holder, PA_MSGOBJECT(u->preprocessor), u->asyncmsgq_sink, true);
+
+    return PA_HOOK_OK;
+}
+
+/* This function will be also called when source unlink */
+static pa_hook_result_t source_output_unlink_post_cb(pa_core *c, pa_source_output *o, void *userdata) {
+    struct userdata *u = (struct userdata *)userdata;
+    pa_processor_holder *holder;
+
+    pa_assert(c);
+    pa_assert(o);
+    pa_assert(u);
+
+    if (!is_preprocessor_marked(o->flags))
+        return PA_HOOK_OK;
+
+    holder = (pa_processor_holder *)o->thread_info.processor_holder;
+
+    connect_to_reference_sink(holder, NULL, NULL, false);
+
+    pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->preprocessor),
+                        PA_SOURCE_MESSAGE_PREPROCESSOR_DESTROY, (void *)o, 0, NULL);
+
+    return PA_HOOK_OK;
+}
+
+static void thread_func(void *userdata) {
+    struct userdata *u = (struct userdata *)userdata;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    if (u->core->realtime_scheduling)
+        pa_thread_make_realtime(u->core->realtime_priority);
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+
+        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
+            goto fail;
+    }
+
+fail:
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core),
+                            PA_CORE_MESSAGE_UNLOAD_MODULE, u->m, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u = NULL;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log_error("Failed to parse module arguments.");
+        return -1;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->m = m;
+
+    u->preprocessor = pa_msgobject_new(pa_preprocessor);
+    u->preprocessor->parent.process_msg = process_msg;
+    u->preprocessor->u = u;
+
+    u->rtpoll = pa_rtpoll_new();
+    u->asyncmsgq_source = pa_asyncmsgq_new(0);
+    u->asyncmsgq_sink = pa_asyncmsgq_new(0);
+
+    pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY, u->asyncmsgq_sink);
+    pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY, u->asyncmsgq_source);
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+    if (!(u->thread = pa_thread_new("tizenaudio-preprocessor", thread_func, u))) {
+        pa_log_error("Failed to create thread.");
+        goto fail;
+    }
+
+    u->source_output_unlink_post_slot =
+        pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST],
+                    PA_HOOK_EARLY, (pa_hook_cb_t) source_output_unlink_post_cb, u);
+
+    /* source_output_fixate_cb must be called after new_cb callback in stream manager
+     * because stream-manager converts the device_id to the index of the sink */
+    u->source_output_fixate_slot =
+        pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE],
+                    PA_HOOK_LATE, (pa_hook_cb_t) source_output_fixate_cb, u);
+
+    u->source_output_put_slot =
+        pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT],
+                    PA_HOOK_LATE, (pa_hook_cb_t) source_output_put_cb, u);
+
+    /* TODO : need to check sink configuration change */
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->preprocessor),
+                        PA_SOURCE_MESSAGE_PREPROCESSOR_TERMINATE, NULL, 0, NULL);
+
+    if (u->source_output_unlink_post_slot)
+        pa_hook_slot_free(u->source_output_unlink_post_slot);
+
+    if (u->source_output_fixate_slot)
+        pa_hook_slot_free(u->source_output_fixate_slot);
+
+    if (u->source_output_put_slot)
+        pa_hook_slot_free(u->source_output_put_slot);
+
+    if (u->asyncmsgq_sink)
+        pa_asyncmsgq_unref(u->asyncmsgq_sink);
+
+    if (u->asyncmsgq_source)
+        pa_asyncmsgq_unref(u->asyncmsgq_source);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    pa_xfree(u);
+}
+
similarity index 63%
rename from src/echo-cancel/echo-cancel-def.h
rename to src/preprocessor/preprocessor-def.h
index 4dbb240..7e5a856 100644 (file)
   USA.
 ***/
 
+#ifndef __PREPROCESSOR_DEF_H__
+#define __PREPROCESSOR_DEF_H__
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 enum {
-    PA_ECHO_CANCEL_MESSAGE_PUSH_DATA,
-    PA_ECHO_CANCEL_MESSAGE_PUSH_ECHO,
-    PA_ECHO_CANCEL_MESSAGE_SET_AEC_STATE,
-    PA_ECHO_CANCEL_MESSAGE_SOURCE_OUTPUT_UNLINK,
+    PA_SOURCE_MESSAGE_PREPROCESSOR_ADD_OUTPUT = PA_SOURCE_MESSAGE_MAX + 1, /* 21 */
+    PA_SOURCE_MESSAGE_PREPROCESSOR_REMOVE_OUTPUT,
+    PA_SOURCE_MESSAGE_PREPROCESSOR_PUSH_DATA,
+    PA_SOURCE_MESSAGE_PREPROCESSOR_PUSH_REFERENCE,
+    PA_SOURCE_MESSAGE_PREPROCESSOR_RESET_REFERENCE,
+    PA_SOURCE_MESSAGE_PREPROCESSOR_DESTROY,
+    PA_SOURCE_MESSAGE_PREPROCESSOR_TERMINATE,
 };
 
 enum {
-    PA_SINK_MESSAGE_SET_AEC_STATE = PA_SINK_MESSAGE_MAX,
-    PA_SINK_MESSAGE_REBUILD_RTPOLL,
-    PA_SOURCE_MESSAGE_SET_AEC_STATE = PA_SOURCE_MESSAGE_MAX,
-    PA_SOURCE_MESSAGE_REBUILD_RTPOLL,
+    PA_SINK_MESSAGE_PREPROCESSOR_REBUILD_RTPOLL = PA_SINK_MESSAGE_MAX,
 };
 
+#endif
diff --git a/src/preprocessor/processor.c b/src/preprocessor/processor.c
new file mode 100644 (file)
index 0000000..510987a
--- /dev/null
@@ -0,0 +1,355 @@
+/***
+  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/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/core-util.h>
+
+#include "processor.h"
+
+#ifdef __DEBUG__
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
+#define MEMBLOCKQ_MAXLENGTH (16 * 1024 * 1024)
+
+#ifdef __DEBUG__
+static void debug_open_file(pa_processor *processor);
+static void debug_timestamp_begin(pa_processor *processor);
+static void debug_timestamp_end(pa_processor *processor);
+static void debug_write_file(pa_processor *processor, int8_t *rec, int8_t *ref, int8_t *out);
+static void debug_close_file(pa_processor *processor);
+#else
+#define debug_open_file(x)
+#define debug_timestamp_begin(x)
+#define debug_timestamp_end(x)
+#define debug_write_file(x, a, b, c)
+#define debug_close_file(x)
+#endif
+
+static size_t pa_processor_usec_to_frame(pa_usec_t usec, pa_sample_spec *spec) {
+    pa_assert(spec);
+
+    return pa_usec_to_bytes(usec, spec) / pa_frame_size(spec);
+}
+
+pa_processor *pa_processor_new(pa_core *core,
+                                uint32_t process_msec,
+                                pa_sample_spec *ss,
+                                pa_processor_method_t method) {
+    pa_processor *processor;
+    pa_memchunk silence;
+
+    pa_assert(core);
+    pa_assert(ss);
+    pa_assert(method < PROCESSOR_METHOD_MAX);
+
+    processor = pa_xnew0(pa_processor, 1);
+    processor->core = core;
+    processor->interface = pa_processor_method_create(method);
+    processor->process_usec = process_msec * PA_USEC_PER_MSEC;
+    processor->ss = *ss;
+    processor->method = method;
+    processor->process_frames = pa_processor_usec_to_frame(processor->process_usec, &processor->ss);
+    processor->process_bytes = pa_usec_to_bytes(processor->process_usec, &processor->ss);
+
+    pa_assert(processor->interface->create);
+    pa_assert(processor->interface->process);
+    pa_assert(processor->interface->destroy);
+
+    if (!(processor->priv = processor->interface->create(processor->process_frames, &processor->ss))) {
+        pa_log_error("Failed to create processor. rate(%d), channels(%d).", processor->ss.rate, processor->ss.channels);
+        pa_xfree(processor);
+        return NULL;
+    }
+
+    pa_silence_memchunk_get(&core->silence_cache, core->mempool, &silence, &processor->ss, 0);
+    processor->result_memblockq = pa_memblockq_new("source-output memblockq",
+                                                    0,
+                                                    MEMBLOCKQ_MAXLENGTH,
+                                                    0,
+                                                    &processor->ss,
+                                                    0,
+                                                    pa_usec_to_bytes(processor->process_usec, &processor->ss),
+                                                    0,
+                                                    &silence);
+    pa_memblock_unref(silence.memblock);
+
+    pa_log_info("Created processor. memblockq rate(%d), channels(%d), process_msec(%u), process_bytes(%zu), method(%d)",
+                                                    processor->ss.rate,
+                                                    processor->ss.channels,
+                                                    process_msec,
+                                                    pa_usec_to_bytes(processor->process_usec, &processor->ss),
+                                                    method);
+
+    debug_open_file(processor);
+
+    return processor;
+}
+
+void pa_processor_free(pa_processor *processor) {
+
+    pa_assert(processor);
+    pa_assert(processor->priv);
+    pa_assert(processor->interface);
+
+    if (processor->interface->destroy(processor->priv) < 0)
+        pa_log_error("Failed to destroy processor");
+
+    if (processor->result_memblockq)
+        pa_memblockq_free(processor->result_memblockq);
+
+    if (processor->reference)
+        pa_processor_reference_free(processor->reference);
+
+    pa_xfree(processor->interface);
+
+    debug_close_file(processor);
+
+    pa_xfree(processor);
+}
+
+size_t pa_processor_get_process_bytes(pa_processor *p) {
+    pa_assert(p);
+
+    return p->process_bytes;
+}
+
+/* Do not touch chunk value */
+int pa_processor_process(pa_processor *processor, pa_memchunk *chunk) {
+    int r = -1;
+    int8_t *recording = NULL;
+    int8_t *reference = NULL;
+    int8_t *output = NULL;
+
+    pa_memchunk rchunk, ochunk;
+
+    pa_assert(processor);
+    pa_assert(processor->result_memblockq);
+    pa_assert(processor->interface);
+    pa_assert(processor->process_bytes > 0ULL);
+    pa_assert(chunk);
+
+    if (processor->reference) {
+        if (pa_processor_reference_pull(processor->reference, &rchunk) < 0)
+            pa_log_warn("Failed to get memblock from reference memblockq. It will be used silence chunk");
+
+        reference = pa_memblock_acquire_chunk(&rchunk);
+    }
+
+    ochunk.index = 0;
+    ochunk.length = chunk->length;
+    ochunk.memblock = pa_memblock_new(processor->core->mempool, ochunk.length);
+
+    recording = pa_memblock_acquire_chunk(chunk);
+    output = pa_memblock_acquire_chunk(&ochunk);
+
+    debug_timestamp_begin(processor);
+
+    r = processor->interface->process(processor->priv, recording, reference, output);
+    if (r < 0) {
+        pa_log_warn("Failed to process memchunk");
+        goto fail;
+    }
+
+    debug_timestamp_end(processor);
+    debug_write_file(processor, recording, reference, output);
+
+    pa_memblock_release(chunk->memblock);
+    pa_memblock_release(ochunk.memblock);
+
+    if (!processor->reference) {
+        pa_log_debug("Post-process(%s). rec(%" PRIu64 "ms), out(%" PRIu64 "ms)",
+                            pa_processor_method_str(processor->method),
+                            pa_bytes_to_usec(chunk->length, &processor->ss) / PA_USEC_PER_MSEC,
+                            pa_bytes_to_usec(ochunk.length, &processor->ss) / PA_USEC_PER_MSEC);
+    } else {
+        char *reference_dump = pa_processor_reference_dump_index(processor->reference);
+
+        // need to add chunk->index
+        pa_log_debug("Post-process(%s). rec(%" PRIu64 "ms), ref(%" PRIu64 "ms), out(%" PRIu64 "ms), " \
+                            "reference memblockq %s, silence %d",
+                            pa_processor_method_str(processor->method),
+                            pa_bytes_to_usec(chunk->length, &processor->ss) / PA_USEC_PER_MSEC,
+                            pa_processor_reference_chunk_to_usec(processor->reference, &rchunk) / PA_USEC_PER_MSEC,
+                            pa_bytes_to_usec(ochunk.length, &processor->ss) / PA_USEC_PER_MSEC,
+                            reference_dump ? reference_dump : "None", pa_memblock_is_silence(rchunk.memblock));
+
+        pa_xfree(reference_dump);
+    }
+
+    if (processor->reference) {
+        pa_memblock_release(rchunk.memblock);
+        pa_processor_reference_drop(processor->reference, &rchunk);
+    }
+
+    /* Keep this memchunk for next processor */
+    if (pa_memblockq_push(processor->result_memblockq, &ochunk) < 0)
+        pa_log_error("Failed to push out chunk to result memblockq");
+
+    /* The memchunk in the result memblockq's refcount must be one */
+    pa_memblock_unref(ochunk.memblock);
+
+    return r;
+
+fail:
+    pa_memblock_release(chunk->memblock);
+    pa_memblock_release(ochunk.memblock);
+
+    if (processor->reference) {
+        pa_memblock_release(rchunk.memblock);
+        pa_processor_reference_drop(processor->reference, &rchunk);
+    }
+
+    pa_memblock_unref(ochunk.memblock);
+
+    return -1;
+}
+
+pa_memblockq *pa_processor_get_result_memblockq(pa_processor *processor) {
+    pa_assert(processor);
+
+    return processor->result_memblockq;
+}
+
+const char *pa_processor_method_str(pa_processor_method_t method) {
+    static const char *method_table[] = { "speex", "webrtc", "reference_copy", "rnnoise", "none-pse" };
+
+    if (method >= PROCESSOR_METHOD_MAX)
+        return NULL;
+
+    return method_table[method];
+}
+
+pa_processor_method_t pa_processor_method_enum(const char *method) {
+    pa_processor_method_t m;
+
+    if (pa_streq(method, "speex"))
+        m = PROCESSOR_METHOD_SPEEX;
+    else if (pa_streq(method, "webrtc"))
+        m = PROCESSOR_METHOD_WEBRTC;
+    else if (pa_streq(method, "reference_copy"))
+        m = PROCESSOR_METHOD_REFERENCE_COPY;
+    else if (pa_streq(method, "rnnoise"))
+        m = PROCESSOR_METHOD_RNNOISE;
+    else
+        pa_assert(0);
+
+    return m;
+}
+
+bool pa_processor_method_need_reference_structure(pa_processor_method_t m) {
+    if (m == PROCESSOR_METHOD_SPEEX)
+        return true;
+    else if (m == PROCESSOR_METHOD_WEBRTC)
+        return true;
+    else if (m == PROCESSOR_METHOD_REFERENCE_COPY)
+        return true;
+
+    return false;
+}
+
+void pa_processor_attach_reference(pa_processor *processor, pa_processor_reference *reference) {
+    pa_assert(processor);
+    pa_assert(reference);
+
+    processor->reference = reference;
+}
+
+pa_processor_reference  *pa_processor_get_reference(pa_processor *processor) {
+    pa_assert(processor);
+
+    return processor->reference;
+}
+
+#ifdef __DEBUG__
+static void debug_open_file(pa_processor *processor) {
+    static int n = 1;
+    char rec[64], ref[64], out[64];
+    const char *method = pa_processor_method_str(processor->method);
+
+    snprintf(rec, sizeof(rec), "/tmp/processor-%s-rec-%d.raw", method, n);
+    snprintf(ref, sizeof(ref), "/tmp/processor-%s-ref-%d.raw", method, n);
+    snprintf(out, sizeof(out), "/tmp/processor-%s-out-%d.raw", method, n);
+    n += 1;
+
+    unlink(rec);
+    unlink(ref);
+    unlink(out);
+
+    processor->fdrec = open(rec, O_RDWR | O_CREAT | O_TRUNC, 777);
+    processor->fdref = open(ref, O_RDWR | O_CREAT | O_TRUNC, 777);
+    processor->fdout = open(out, O_RDWR | O_CREAT | O_TRUNC, 777);
+}
+
+static void debug_timestamp_begin(pa_processor *processor) {
+    gettimeofday(&processor->before, NULL);
+}
+
+static void debug_timestamp_end(pa_processor *processor) {
+    gettimeofday(&processor->after, NULL);
+
+    pa_log_debug("It takes time (%ld)ms.",
+                    1000 * (processor->after.tv_sec - processor->before.tv_sec)
+                    + (processor->after.tv_usec - processor->before.tv_usec) / 1000);
+}
+
+static void debug_write_file(pa_processor *processor, int8_t *rec, int8_t *ref, int8_t *out) {
+    if (rec && write(processor->fdrec, rec, processor->process_bytes) <= 0)
+        pa_log_error("Failed to write recording buffer");
+
+    if (ref && write(processor->fdref, ref, pa_processor_reference_process_usec_to_bytes(processor->reference)) <= 0)
+        pa_log_error("Failed to write reference buffer");
+
+    if (out && write(processor->fdout, out, processor->process_bytes) <= 0)
+        pa_log_error("Failed to write ref buffer");
+}
+
+static void debug_close_file(pa_processor *processor) {
+    if (processor->fdrec) {
+        close(processor->fdrec);
+        processor->fdrec = -1;
+    }
+
+    if (processor->fdref) {
+        close(processor->fdref);
+        processor->fdref = -1;
+    }
+
+    if (processor->fdout) {
+        close(processor->fdout);
+        processor->fdout = -1;
+    }
+}
+#endif
+
diff --git a/src/preprocessor/processor.h b/src/preprocessor/processor.h
new file mode 100644 (file)
index 0000000..ef999c2
--- /dev/null
@@ -0,0 +1,79 @@
+/***
+  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.
+***/
+
+#ifndef foopulseprocessorfoo
+#define foopulseprocessorfoo
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core.h>
+#include <pulsecore/memblock.h>
+#include <pulse/channelmap.h>
+#include <pulse/sample.h>
+
+#include "processor_reference.h"
+#include "method_factory.h"
+
+typedef struct pa_processor_holder pa_processor_holder;
+typedef struct pa_processor {
+    pa_core *core;
+    pa_processor_holder *holder;
+
+    size_t process_frames;
+    size_t process_bytes;
+    pa_usec_t process_usec;
+    pa_sample_spec ss;
+    pa_memblockq *result_memblockq;
+
+    void *priv;
+    pa_processor_method *interface;
+    pa_processor_method_t method;
+
+    pa_processor_reference *reference;
+#ifdef __DEBUG__
+    int fdrec, fdref, fdout;
+    struct timeval before, after;
+#endif
+} pa_processor;
+
+pa_processor *pa_processor_new(pa_core *core,
+                                uint32_t process_msec,
+                                pa_sample_spec *ss,
+                                pa_processor_method_t method);
+
+int pa_processor_process(pa_processor *processor, pa_memchunk *chunk);
+void pa_processor_free(pa_processor *p);
+void pa_processor_reset(pa_processor *p);
+pa_memblockq *pa_processor_get_result_memblockq(pa_processor *processor);
+size_t pa_processor_get_process_bytes(pa_processor *p);
+
+/* reference */
+void pa_processor_attach_reference(pa_processor *processor, pa_processor_reference *reference);
+pa_processor_reference  *pa_processor_get_reference(pa_processor *processor);
+
+pa_processor_method_t pa_processor_method_enum(const char *method);
+const char *pa_processor_method_str(pa_processor_method_t method);
+bool pa_processor_method_need_reference_structure(pa_processor_method_t m);
+
+#endif
+
diff --git a/src/preprocessor/processor_holder.c b/src/preprocessor/processor_holder.c
new file mode 100644 (file)
index 0000000..a466411
--- /dev/null
@@ -0,0 +1,258 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/core-util.h>
+
+#include "processor_holder.h"
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+pa_processor_holder *pa_processor_holder_new(pa_sample_spec *ss) {
+    pa_processor_holder *holder;
+
+    pa_assert(ss);
+
+    holder = pa_xnew0(pa_processor_holder, 1);
+    holder->processors = pa_idxset_new(NULL, NULL);
+
+    holder->input= pa_memblockq_new("holder input memblockq",
+                                        0,
+                                        MEMBLOCKQ_MAXLENGTH,
+                                        0,
+                                        ss,
+                                        0,
+                                        0,
+                                        0,
+                                        NULL);
+
+    holder->output = pa_memblockq_new("holder output memblockq",
+                                        0,
+                                        MEMBLOCKQ_MAXLENGTH,
+                                        0,
+                                        ss,
+                                        0,
+                                        0,
+                                        0,
+                                        NULL);
+
+    return holder;
+}
+
+void pa_processor_holder_register_processor_sequencial(pa_processor_holder *holder, pa_processor *processor) {
+    pa_assert(holder);
+    pa_assert(processor);
+
+    if (pa_idxset_put(holder->processors, processor, NULL) < 0)
+        pa_log_warn("processor was already registered");
+
+    processor->holder = holder;
+}
+
+int pa_processor_holder_connect_reference(pa_processor_holder *holder, pa_processor_reference *reference) {
+    pa_assert(holder);
+    pa_assert(reference);
+
+    if (holder->reference) {
+        pa_log_error("Don't allow multi referenced processor");
+        return -1;
+    }
+
+    holder->reference = reference;
+
+    return 0;
+}
+
+int pa_processor_holder_push_data(pa_processor_holder *holder, pa_memchunk *chunk) {
+    pa_assert(holder);
+    pa_assert(chunk);
+
+    return pa_memblockq_push(holder->input, chunk);
+}
+
+int pa_processor_holder_push_reference_data(pa_processor_holder *holder, pa_memchunk *chunk) {
+    pa_assert(holder);
+    pa_assert(chunk);
+    pa_assert(holder->reference);
+
+    return pa_processor_reference_push(holder->reference, chunk);
+}
+
+int pa_processor_holder_pull_data(pa_processor_holder *holder, pa_memchunk *chunk) {
+    int r;
+
+    pa_assert(holder);
+    pa_assert(chunk);
+
+    if ((r = pa_memblockq_peek(holder->output, chunk)) < 0)
+        pa_log_error("Failed to get memblock from output memblockq");
+
+    /* chunk ref count must be one after dropping */
+    pa_memblockq_drop(holder->output, chunk->length);
+
+    return r;
+}
+
+int pa_processor_holder_pump(pa_processor_holder *holder) {
+    pa_memblockq *pull_queue;
+    pa_memchunk chunk;
+    pa_processor *p;
+    int ret;
+    size_t size;
+    uint32_t idx;
+
+    pa_assert(holder);
+
+    pull_queue = holder->input;
+
+    PA_IDXSET_FOREACH(p, holder->processors, idx) {
+        if (pa_memblockq_peek(pull_queue, &chunk)) {
+            pa_log_info("Failed to peek data. processor(%s)", pa_processor_method_str(p->method));
+            return -1;
+        }
+
+        size = pa_processor_get_process_bytes(p);
+        if (chunk.length < size) {
+            pa_log_info("Not enough buffer. need to wait more buffer");
+            pa_memblock_unref(chunk.memblock);
+            return -1;
+        }
+
+        /* chunk will be copied */
+        /* TODO : ERROR BUFFERING */
+        if ((ret = pa_processor_process(p, &chunk))) {
+            if (ret == -2) {
+                pa_log_info("Tried to process but processor has a latency");
+                return 0;
+            } else {
+                pa_log_info("Failed to process");
+                return 0;
+            }
+        }
+
+        pa_memblock_unref(chunk.memblock);
+        pa_memblockq_drop(pull_queue, chunk.length);
+
+        pull_queue = pa_processor_get_result_memblockq(p);
+    }
+
+    /* in case of only pass though */
+    if (pull_queue == holder->input)
+        return -1;
+
+    pa_memblockq_peek(pull_queue, &chunk);
+    pa_memblockq_push(holder->output, &chunk);
+    pa_memblock_unref(chunk.memblock);
+    pa_memblockq_drop(pull_queue, chunk.length);
+
+    return 0;
+}
+
+static void processor_holder_destroy_processor(void *data) {
+    pa_processor *p = data;
+
+    pa_assert(p);
+    pa_processor_free(p);
+}
+
+void pa_processor_holder_free(pa_processor_holder *holder) {
+    pa_assert(holder);
+    pa_assert(holder->processors);
+
+    pa_idxset_remove_all(holder->processors, processor_holder_destroy_processor);
+
+    pa_memblockq_free(holder->output);
+    pa_memblockq_free(holder->input);
+
+    pa_xfree(holder);
+}
+
+pa_processor_reference *pa_processor_holder_get_connected_processor_reference(pa_processor_holder *holder) {
+    pa_assert(holder);
+
+    /* It could be NULL */
+    return holder->reference;
+}
+
+void pa_processor_holder_set_current_source(pa_processor_holder *holder, pa_source *source) {
+    pa_assert(holder);
+
+    holder->source = source;
+}
+
+pa_source *pa_processor_holder_get_current_source(pa_processor_holder *holder)  {
+    pa_assert(holder);
+
+    return holder->source;
+}
+
+void pa_processor_holder_set_private_data(pa_processor_holder *holder, void *data1, void *data2) {
+    pa_assert(holder);
+
+    holder->data1 = data1;
+    holder->data2 = data2;
+}
+
+void pa_processor_holder_get_private_data(pa_processor_holder *holder, void **data1, void **data2) {
+    pa_assert(holder);
+
+    *data1 = holder->data1;
+    *data2 = holder->data2;
+}
+
+void pa_processor_holder_dump(pa_processor_holder *holder) {
+    pa_memblockq *pull_queue;
+    pa_processor *p;
+    uint32_t idx;
+
+    pa_assert(holder);
+
+    pull_queue = holder->input;
+
+    pa_log_info("processor holder input blocks(%d) index(%" PRId64 "->%" PRId64 ")",
+                    pa_memblockq_get_nblocks(pull_queue),
+                    pa_memblockq_get_write_index(pull_queue),
+                    pa_memblockq_get_write_index(pull_queue));
+
+    PA_IDXSET_FOREACH(p, holder->processors, idx) {
+        pull_queue = pa_processor_get_result_memblockq(p);
+        pa_log_info("processor holder processor(%s) blocks(%d) index(%" PRId64 "->%" PRId64 "), reference(%d)",
+                        pa_processor_method_str(p->method),
+                        pa_memblockq_get_nblocks(pull_queue),
+                        pa_memblockq_get_write_index(pull_queue),
+                        pa_memblockq_get_write_index(pull_queue),
+                        p->reference ? pa_processor_reference_get_nblocks(p->reference) : 0);
+    }
+
+    pa_log_info("processor holder output blocks(%d) index(%" PRId64 "->%" PRId64 ")",
+                    pa_memblockq_get_nblocks(pull_queue),
+                    pa_memblockq_get_write_index(pull_queue),
+                    pa_memblockq_get_write_index(pull_queue));
+}
diff --git a/src/preprocessor/processor_holder.h b/src/preprocessor/processor_holder.h
new file mode 100644 (file)
index 0000000..f756514
--- /dev/null
@@ -0,0 +1,60 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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.
+***/
+
+#ifndef __PROCESSOR_HOLDER_H__
+#define __PROCESSOR_HOLDER_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/memblock.h>
+#include <pulsecore/memchunk.h>
+#include "processor.h"
+
+typedef struct pa_processor_holder {
+    size_t process_bytes;
+    pa_memblockq *input;
+    pa_memblockq *output;
+    pa_idxset *processors;
+    pa_processor_reference *reference; /* for exporting reference processor */
+    pa_source *source;
+    bool enable;
+    void *data1;
+    void *data2;
+} pa_processor_holder;
+
+pa_processor_holder *pa_processor_holder_new(pa_sample_spec *ss);
+void pa_processor_holder_register_processor_sequencial(pa_processor_holder *holder, pa_processor *processor);
+int pa_processor_holder_connect_reference(pa_processor_holder *holder, pa_processor_reference *reference);
+int pa_processor_holder_push_data(pa_processor_holder *holder, pa_memchunk *chunk);
+int pa_processor_holder_push_reference_data(pa_processor_holder *holder, pa_memchunk *chunk);
+int pa_processor_holder_pull_data(pa_processor_holder *holder, pa_memchunk *chunk);
+int pa_processor_holder_pump(pa_processor_holder *holder);
+void pa_processor_holder_free(pa_processor_holder *holder);
+void pa_processor_holder_set_current_source(pa_processor_holder *holder, pa_source *source);
+pa_source *pa_processor_holder_get_current_source(pa_processor_holder *holder);
+pa_processor_reference *pa_processor_holder_get_connected_processor_reference(pa_processor_holder *holder);
+void pa_processor_holder_set_private_data(pa_processor_holder *holder, void *data1, void *data2);
+void pa_processor_holder_get_private_data(pa_processor_holder *holder, void **data1, void **data2);
+void pa_processor_holder_dump(pa_processor_holder *holder);
+
+#endif
diff --git a/src/preprocessor/processor_reference.c b/src/preprocessor/processor_reference.c
new file mode 100644 (file)
index 0000000..6ade8c1
--- /dev/null
@@ -0,0 +1,329 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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/timeval.h>
+#include <pulsecore/core-util.h>
+
+#include "processor_reference.h"
+
+#define MEMBLOCKQ_MAXLENGTH (16 * 1024 * 1024)
+
+typedef struct pa_processor_reference {
+    pa_core *core;
+    pa_sink *sink;
+    pa_memblockq *memblockq;
+    pa_resampler *resampler;
+    pa_usec_t process_usec;
+    size_t process_bytes;
+    pa_sample_spec ss;
+
+    /* only for pull mode (audio-share) */
+    void *priv;
+    pa_processor_reference_method *interface;
+} pa_processor_reference;
+
+pa_processor_reference *pa_processor_reference_new_custom(pa_core *core,
+                                                            pa_sink *sink,
+                                                            pa_sample_spec *sink_ss,
+                                                            pa_sample_spec *request_ss,
+                                                            pa_usec_t process_usec,
+                                                            pa_processor_reference_method_t method) {
+    pa_processor_reference *reference;
+
+    pa_assert(core);
+    pa_assert(sink_ss);
+    pa_assert(request_ss);
+
+    reference = pa_processor_reference_new(core, sink, sink_ss, request_ss, process_usec);
+    if (!reference) {
+        pa_log_error("Failed to create reference processor");
+        return NULL;
+    }
+
+
+    if (method > PROCESSOR_REFERENCE_METHOD_NONE && method < PROCESSOR_REFERENCE_METHOD_MAX) {
+        reference->interface = pa_processor_reference_method_create(method);
+        if (!reference->interface) {
+            pa_log_error("Failed to create reference interface");
+            return NULL;
+        }
+
+        pa_assert(reference->interface->create);
+        pa_assert(reference->interface->read);
+        pa_assert(reference->interface->destroy);
+
+        if (!(reference->priv = reference->interface->create(request_ss))) {
+            pa_processor_reference_free(reference);
+            pa_log_error("Failed to create custom reference");
+            return NULL;
+        }
+    }
+
+    return reference;
+}
+
+pa_processor_reference *pa_processor_reference_new(pa_core *core,
+                                                    pa_sink *sink,
+                                                    pa_sample_spec *sink_ss,
+                                                    pa_sample_spec *request_ss,
+                                                    pa_usec_t process_usec) {
+    pa_processor_reference *reference;
+    pa_memchunk silence;
+
+    pa_assert(core);
+    pa_assert(sink_ss);
+    pa_assert(request_ss);
+
+    reference = pa_xnew0(pa_processor_reference, 1);
+    reference->core = core;
+    reference->process_usec = process_usec;
+    reference->ss = *request_ss;
+    reference->process_bytes = pa_usec_to_bytes(reference->process_usec, &reference->ss);
+    reference->sink = sink;
+
+    pa_silence_memchunk_get(&core->silence_cache, core->mempool, &silence, request_ss, 0);
+    reference->memblockq = pa_memblockq_new("reference memblockq",
+                                                        0,
+                                                        MEMBLOCKQ_MAXLENGTH,
+                                                        0,
+                                                        request_ss,
+                                                        0,
+                                                        0,
+                                                        0,
+                                                        &silence);
+    pa_memblock_unref(silence.memblock);
+
+    if (!pa_sample_spec_equal(sink_ss, request_ss)) {
+        reference->resampler = pa_resampler_new(core->mempool,
+                                                sink_ss, NULL,
+                                                request_ss, NULL,
+                                                core->lfe_crossover_freq,
+                                                core->resample_method, 0);
+        if (!reference->resampler) {
+            pa_log_error("Failed to create reference resampler");
+            goto fail;
+        }
+
+        pa_log_info("resampler was created for reference");
+    }
+
+    pa_log_info("Created reference memblockq rate(%d), channels(%d)", request_ss->rate, request_ss->channels);
+
+    return reference;
+
+fail:
+    if (reference->memblockq)
+        pa_memblockq_free(reference->memblockq);
+    if (reference->resampler)
+        pa_resampler_free(reference->resampler);
+
+    return NULL;
+}
+
+void pa_processor_reference_free(pa_processor_reference *reference) {
+    pa_assert(reference);
+
+    if (reference->resampler)
+        pa_resampler_free(reference->resampler);
+
+    if (reference->memblockq)
+        pa_memblockq_free(reference->memblockq);
+
+    if (reference->interface && reference->interface->destroy) {
+        reference->interface->destroy(reference->priv);
+        pa_xfree(reference->interface);
+    }
+
+    pa_xfree(reference);
+}
+
+void pa_processor_reference_reset(pa_processor_reference *reference) {
+    pa_assert(reference);
+
+    pa_memblockq_silence(reference->memblockq);
+    pa_memblockq_flush_write(reference->memblockq, false);
+    pa_resampler_reset(reference->resampler);
+}
+
+int pa_processor_reference_push(pa_processor_reference *reference, pa_memchunk *chunk) {
+    pa_memchunk ochunk;
+    int r;
+
+    pa_assert(reference);
+    pa_assert(chunk);
+    pa_assert(!reference->interface);
+
+    if (reference->resampler) {
+        pa_resampler_run(reference->resampler, chunk, &ochunk);
+        chunk = &ochunk;
+    }
+
+    if ((r = pa_memblockq_push(reference->memblockq, chunk)) < 0)
+        pa_log_error("Failed to push chunk to reference memblockq");
+
+    if (reference->resampler)
+        pa_memblock_unref(chunk->memblock);
+
+    pa_log_debug("Pushed reference data. bytes(%zu), msec(%" PRIu64 "ms), nblocks(%d) index(%" PRId64 ":%" PRId64 ")",
+                    chunk->length,
+                    reference->process_usec / PA_USEC_PER_MSEC,
+                    pa_memblockq_get_nblocks(reference->memblockq),
+                    pa_memblockq_get_write_index(reference->memblockq),
+                    pa_memblockq_get_read_index(reference->memblockq));
+
+    return r;
+}
+
+int pa_processor_reference_pull(pa_processor_reference *reference, pa_memchunk *chunk) {
+    int r;
+    size_t length;
+
+    pa_assert(reference);
+    pa_assert(chunk);
+
+    length = reference->process_bytes;
+
+    if (reference->interface) {
+        pa_memchunk rchunk;
+        void *buffer;
+        size_t ret;
+
+        pa_assert(reference->interface->read);
+
+        rchunk.index = 0;
+        rchunk.length = length;
+        rchunk.memblock = pa_memblock_new(reference->core->mempool, rchunk.length);
+
+        buffer = pa_memblock_acquire_chunk(&rchunk);
+        if (reference->interface->read(reference->priv, buffer, length) < 0) {
+            pa_memblock_release(rchunk.memblock);
+            pa_log_error("Failed to read reference source. length(%zu), ret(%zu)", length, ret);
+            goto exit;
+        }
+
+        pa_memblock_release(rchunk.memblock);
+
+        pa_memblockq_push(reference->memblockq, &rchunk);
+        pa_memblock_unref(rchunk.memblock);
+    }
+
+exit:
+    if ((r = pa_memblockq_peek_fixed_size(reference->memblockq, length, chunk)) < 0) {
+        pa_log_error("Failed to get memblock from reference memblockq");
+        return r;
+    }
+
+    return r;
+}
+
+// TODO: It could be pushed memblock precisely.
+void pa_processor_reference_add_latency_padding(pa_processor_reference *reference, pa_usec_t latency) {
+    int64_t write_index, read_index;
+    pa_memchunk silence;
+    unsigned int n = 1; /* it must be added 1 block at least to prevent glitch sound */
+
+    pa_assert(reference);
+
+    if (latency > reference->process_usec)
+        n = (unsigned int)(latency / reference->process_usec);
+
+    pa_silence_memchunk_get(&reference->core->silence_cache,
+                            reference->core->mempool, &silence,
+                            &reference->ss,
+                            pa_usec_to_bytes(reference->process_usec, &reference->ss));
+
+    write_index = pa_memblockq_get_write_index(reference->memblockq);
+    read_index = pa_memblockq_get_read_index(reference->memblockq);
+
+    while (n--)
+        pa_memblockq_push(reference->memblockq, &silence);
+
+    pa_memblock_unref(silence.memblock);
+
+    pa_log_info("push n(%u) silence blocks. latency(%" PRId64 "), ref_process_msec(%" PRId64 ") "
+                "write_index(%" PRId64 "->%" PRId64 "), read_index(%" PRId64 "->%" PRId64 ")",
+                        pa_memblockq_get_nblocks(reference->memblockq),
+                        latency,
+                        pa_bytes_to_usec(silence.length, &reference->ss) / PA_USEC_PER_MSEC,
+                        write_index, pa_memblockq_get_write_index(reference->memblockq),
+                        read_index, pa_memblockq_get_read_index(reference->memblockq));
+}
+
+void pa_processor_reference_drop(pa_processor_reference *reference, pa_memchunk *chunk) {
+    pa_assert(reference);
+    pa_assert(chunk);
+
+    pa_memblock_unref(chunk->memblock);
+    pa_memblockq_drop(reference->memblockq, chunk->length);
+}
+
+unsigned pa_processor_reference_get_nblocks(pa_processor_reference *reference) {
+    pa_assert(reference);
+
+    return pa_memblockq_get_nblocks(reference->memblockq);
+}
+
+pa_usec_t pa_processor_reference_chunk_to_usec(pa_processor_reference *reference, pa_memchunk *chunk) {
+    pa_assert(reference);
+    pa_assert(chunk);
+
+    return pa_bytes_to_usec(chunk->length, &reference->ss);
+}
+
+size_t pa_processor_reference_process_usec_to_bytes(pa_processor_reference *reference) {
+    pa_assert(reference);
+
+    return pa_usec_to_bytes(reference->process_usec, &reference->ss);
+
+}
+
+char *pa_processor_reference_dump_index(pa_processor_reference *reference) {
+    pa_assert(reference);
+
+    return pa_sprintf_malloc("windex:rindex(%" PRId64 ":%" PRId64 ")",
+                                pa_memblockq_get_write_index(reference->memblockq),
+                                pa_memblockq_get_read_index(reference->memblockq));
+}
+
+pa_sink *pa_processor_reference_get_sink(pa_processor_reference *reference) {
+    pa_assert(reference);
+
+    return reference->sink;
+}
+
+pa_processor_reference_method_t pa_processor_reference_method_enum(const char *method) {
+    pa_processor_reference_method_t m;
+
+    pa_assert(method);
+
+    if (pa_streq(method, "audio-share"))
+        m = PROCESSOR_REFERENCE_METHOD_AUDIOSHARE;
+    else if (pa_streq(method, "filesrc"))
+        m = PROCESSOR_REFERENCE_METHOD_FILESRC;
+    else
+        pa_assert(0);
+
+    return m;
+}
diff --git a/src/preprocessor/processor_reference.h b/src/preprocessor/processor_reference.h
new file mode 100644 (file)
index 0000000..cacd5f5
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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.
+***/
+
+#ifndef foopulseprocessorreferencefoo
+#define foopulseprocessorreferencefoo
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core.h>
+#include <pulsecore/memblock.h>
+#include <pulse/sample.h>
+
+#include "method_factory.h"
+
+typedef struct pa_processor_reference pa_processor_reference;
+
+pa_processor_reference *pa_processor_reference_new(pa_core *core,
+                                                    pa_sink *sink,
+                                                    pa_sample_spec *sink_ss,
+                                                    pa_sample_spec *request_ss,
+                                                    pa_usec_t process_usec);
+pa_processor_reference *pa_processor_reference_new_custom(pa_core *core,
+                                                            pa_sink *sink,
+                                                            pa_sample_spec *sink_ss,
+                                                            pa_sample_spec *request_ss,
+                                                            pa_usec_t process_usec,
+                                                            pa_processor_reference_method_t method);
+void pa_processor_reference_free(pa_processor_reference *reference);
+void pa_processor_reference_reset(pa_processor_reference *reference);
+int pa_processor_reference_push(pa_processor_reference *reference, pa_memchunk *chunk);
+int pa_processor_reference_pull(pa_processor_reference *reference, pa_memchunk *chunk);
+void pa_processor_reference_add_latency_padding(pa_processor_reference *reference, pa_usec_t latency);
+void pa_processor_reference_drop(pa_processor_reference *reference, pa_memchunk *chunk);
+unsigned pa_processor_reference_get_nblocks(pa_processor_reference *reference);
+pa_usec_t pa_processor_reference_chunk_to_usec(pa_processor_reference *reference, pa_memchunk *chunk);
+size_t pa_processor_reference_process_usec_to_bytes(pa_processor_reference *reference);
+char *pa_processor_reference_dump_index(pa_processor_reference *reference);
+
+pa_sink *pa_processor_reference_get_sink(pa_processor_reference *reference);
+pa_processor_reference_method_t pa_processor_reference_method_enum(const char *method);
+
+#endif
diff --git a/src/preprocessor/reference_method_filesrc.c b/src/preprocessor/reference_method_filesrc.c
new file mode 100644 (file)
index 0000000..59b60b4
--- /dev/null
@@ -0,0 +1,62 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2023 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 <stdio.h>
+#include <pulse/sample.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+void *processor_reference_filesrc_create(pa_sample_spec *ss) {
+    FILE *f;
+
+    f = fopen("/opt/usr/media/reference.raw", "r");
+    if (!f)
+        pa_log_error("Failed to open reference");
+
+    return (void *)f;
+}
+
+size_t processor_reference_filesec_read(void *priv, void *ref, size_t length) {
+    size_t n;
+    FILE *f = priv;
+
+    pa_assert(f);
+
+    n = fread(ref, sizeof(char), length, f);
+
+    if (n != length) {
+        pa_log_error("invalid read length");
+        return 0;
+    }
+
+    return n;
+}
+
+void processor_reference_filesrc_destroy(void *priv) {
+    FILE *f = priv;
+
+    pa_assert(f);
+    fclose(f);
+}
index 04c217f..4b3f888 100644 (file)
@@ -47,7 +47,9 @@
 
 #include "hal-interface.h"
 #include "tizenaudio-util.h"
-#include "echo-cancel/echo-cancel-def.h"
+#include "preprocessor/preprocessor-def.h"
+#include "preprocessor/processor_holder.h"
+#include "preprocessor/processor_reference.h"
 
 #define DEFAULT_SINK_NAME "tizenaudio-sink2"
 
@@ -72,16 +74,17 @@ struct userdata {
     char* card;
     char* device;
     bool first;
-    bool echo_on;
+    bool preprocess_state;
 
     pa_rtpoll_item *rtpoll_item;
 
     uint64_t write_count;
     pa_hal_interface *hal_interface;
 
-    pa_msgobject *ec_object;
-    pa_asyncmsgq *ec_asyncmsgq;
-    pa_rtpoll_item *ec_poll_item;
+    pa_msgobject *preprocessor;
+    pa_asyncmsgq *preprocessor_asyncmsgq;
+    void *holder;
+    pa_rtpoll_item *preprocessor_poll_item;
 };
 
 static void userdata_free(struct userdata *u);
@@ -241,11 +244,6 @@ static int sink_process_msg(
     struct userdata *u = PA_SINK(o)->userdata;
 
     switch (code) {
-        case PA_SINK_MESSAGE_SET_AEC_STATE: {
-            u->echo_on = !!data;
-            pa_log_info("EC state changed (%d)", u->echo_on);
-            return 0;
-        }
         case PA_SINK_MESSAGE_GET_LATENCY: {
             int64_t r = 0;
 
@@ -261,26 +259,63 @@ static int sink_process_msg(
 
             return 0;
         }
-        case PA_SINK_MESSAGE_REBUILD_RTPOLL: {
+        case PA_SINK_MESSAGE_PREPROCESSOR_REBUILD_RTPOLL: {
             struct arguments {
                 pa_msgobject *o;
                 pa_asyncmsgq *q;
+                void *holder;
             } *args;
 
             args = (struct arguments *)data;
 
             if (args) {
-                u->ec_object = args->o;
-                u->ec_asyncmsgq = args->q;
-                u->ec_poll_item = pa_rtpoll_item_new_asyncmsgq_write(u->rtpoll, PA_RTPOLL_EARLY, args->q);
+                u->preprocessor = args->o;
+                u->preprocessor_asyncmsgq = args->q;
+                u->holder = args->holder;
+                u->preprocessor_poll_item = pa_rtpoll_item_new_asyncmsgq_write(u->rtpoll, PA_RTPOLL_EARLY, args->q);
+                u->preprocess_state = true;
             } else {
-                pa_rtpoll_item_free(u->ec_poll_item);
-                u->ec_poll_item = NULL;
-                u->ec_object = NULL;
+                pa_rtpoll_item_free(u->preprocessor_poll_item);
+                u->preprocessor_poll_item = NULL;
+                u->preprocessor = NULL;
+                u->holder = NULL;
+                u->preprocess_state = false;
             }
 
             return 0;
         }
+        case PA_SINK_MESSAGE_SET_STATE: {
+            struct state_data {
+                pa_sink_state_t state;
+                pa_suspend_cause_t suspend_cause;
+            } state;
+
+            pa_processor_reference *reference;
+            pa_sink *sink;
+
+            state = *((struct state_data *)data);
+
+            if (!u->holder)
+                break;
+
+            reference = pa_processor_holder_get_connected_processor_reference(u->holder);
+            if (!reference)
+                break;
+
+            sink = pa_processor_reference_get_sink(reference);
+            if (!sink)
+                break;
+
+            if (sink == u->sink && state.state == PA_SINK_RUNNING) {
+                u->preprocess_state = true;
+                pa_asyncmsgq_post(u->preprocessor_asyncmsgq, u->preprocessor,
+                                    PA_SOURCE_MESSAGE_PREPROCESSOR_RESET_REFERENCE, (void *)true, 0, NULL, NULL);
+            } else {
+                u->preprocess_state = false;
+            }
+
+            break;
+        }
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -316,9 +351,9 @@ static int process_render(struct userdata *u) {
 
     pa_memblock_release(chunk.memblock);
 
-    if (u->echo_on) {
-        pa_asyncmsgq_post(u->ec_asyncmsgq, u->ec_object,
-                            PA_ECHO_CANCEL_MESSAGE_PUSH_ECHO, NULL, 0, &chunk, NULL);
+    if (u->preprocess_state) {
+        pa_asyncmsgq_post(u->preprocessor_asyncmsgq, u->preprocessor,
+                            PA_SOURCE_MESSAGE_PREPROCESSOR_PUSH_REFERENCE, u->holder, 0, &chunk, NULL);
     }
 
     pa_memblock_unref(chunk.memblock);
index 05c5f76..0718eb6 100644 (file)
@@ -47,7 +47,8 @@
 
 #include "hal-interface.h"
 #include "tizenaudio-util.h"
-#include "echo-cancel/echo-cancel-def.h"
+#include "preprocessor/preprocessor-def.h"
+#include "preprocessor/processor_holder.h"
 
 #define DEFAULT_SOURCE_NAME "tizenaudio-source2"
 
@@ -73,7 +74,7 @@ struct userdata {
     char* card;
     char* device;
     bool first;
-    bool echo_on;
+    bool preprocess_state;
 
     pa_rtpoll_item *rtpoll_item;
 
@@ -81,10 +82,9 @@ struct userdata {
     pa_usec_t latency_time;
     pa_hal_interface *hal_interface;
 
-    /* for echo-cancel */
-    pa_msgobject *ec_object;
-    pa_asyncmsgq *ec_asyncmsgq;
-    pa_rtpoll_item *ec_poll_item;
+    /* for preprocessor */
+    pa_msgobject *preprocessor;
+    pa_asyncmsgq *preprocessor_asyncmsgq;
 };
 
 static void userdata_free(struct userdata *u);
@@ -245,11 +245,6 @@ static int source_process_msg(
     struct userdata *u = PA_SOURCE(o)->userdata;
 
     switch (code) {
-        case PA_SOURCE_MESSAGE_SET_AEC_STATE: {
-            u->echo_on = !!data;
-            pa_log_info("EC state changed (%d)", u->echo_on);
-            return 0;
-        }
         case PA_SOURCE_MESSAGE_GET_LATENCY: {
             uint64_t r = 0;
 
@@ -265,26 +260,41 @@ static int source_process_msg(
 
             return 0;
         }
-        case PA_SOURCE_MESSAGE_REBUILD_RTPOLL: {
-            struct arguments {
-                pa_msgobject *o;
-                pa_asyncmsgq *q;
-            } *args;
-
-            args = (struct arguments *)data;
-
-            if (args) {
-                u->ec_object = args->o;
-                u->ec_asyncmsgq = args->q;
-                u->ec_poll_item = pa_rtpoll_item_new_asyncmsgq_write(u->rtpoll, PA_RTPOLL_EARLY, args->q);
-            } else {
-                pa_rtpoll_item_free(u->ec_poll_item);
-                u->ec_poll_item = NULL;
-                u->ec_object = NULL;
+        case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
+            pa_source_output *output = PA_SOURCE_OUTPUT(data);
+
+            if (output->flags & PA_SOURCE_OUTPUT_PREPROCESSOR) {
+                pa_processor_holder *holder = (pa_processor_holder *)output->thread_info.processor_holder;
+
+                pa_assert(holder);
+
+                pa_processor_holder_get_private_data(holder, (void *)&u->preprocessor, (void *)&u->preprocessor_asyncmsgq);
+                pa_asyncmsgq_send(u->preprocessor_asyncmsgq, u->preprocessor, PA_SOURCE_MESSAGE_PREPROCESSOR_ADD_OUTPUT, data, offset, chunk);
+
+                u->preprocess_state = true;
             }
+            break;
+        }
+        case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
+            pa_source_output *output = PA_SOURCE_OUTPUT(data);
 
-            return 0;
+            if (output->flags & PA_SOURCE_OUTPUT_PREPROCESSOR) {
+                pa_processor_holder *holder = (pa_processor_holder *)output->thread_info.processor_holder;
+
+                pa_assert(holder);
+
+                pa_processor_holder_get_private_data(holder, (void *)&u->preprocessor, (void *)&u->preprocessor_asyncmsgq);
+                pa_asyncmsgq_send(u->preprocessor_asyncmsgq, u->preprocessor, PA_SOURCE_MESSAGE_PREPROCESSOR_REMOVE_OUTPUT, data, offset, chunk);
+
+                u->preprocess_state = false;
+            }
+            break;
         }
+        case PA_SOURCE_MESSAGE_PREPROCESSOR_TERMINATE:
+            u->preprocess_state = false;
+            break;
+        default:
+            break;
     }
 
     return pa_source_process_msg(o, code, data, offset, chunk);
@@ -320,9 +330,9 @@ static int process_render(struct userdata *u) {
     chunk.index = 0;
     chunk.length = (size_t)frames_to_read * frame_size;
 
-    if (u->echo_on) {
-        pa_asyncmsgq_post(u->ec_asyncmsgq, u->ec_object,
-                            PA_ECHO_CANCEL_MESSAGE_PUSH_DATA, NULL, 0, &chunk, NULL);
+    if (u->preprocess_state) {
+        pa_asyncmsgq_post(u->preprocessor_asyncmsgq, u->preprocessor,
+                            PA_SOURCE_MESSAGE_PREPROCESSOR_PUSH_DATA, u->source, 0, &chunk, NULL);
     } else {
         pa_source_post(u->source, &chunk);
     }