arduino-audio-tools
Loading...
Searching...
No Matches
MDFEchoCancellation.h
Go to the documentation of this file.
1/* Copyright (C) 2003-2008 Jean-Marc Valin
2 * Copyright (C) 2024 Phil Schatzmann (Header-only adaptation)
3 *
4 * Echo canceller based on the MDF algorithm
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#pragma once
33
34#include <algorithm>
35#include <cmath>
36#include <cstdint>
37#include <cstdlib>
38#include <cstring>
39#include <type_traits>
40
42#include "PseudoFloat.h"
46
47// Control requests
48#define ECHO_GET_FRAME_SIZE 3
49#define ECHO_SET_SAMPLING_RATE 24
50#define ECHO_GET_SAMPLING_RATE 25
51#define ECHO_GET_IMPULSE_RESPONSE_SIZE 27
52#define ECHO_GET_IMPULSE_RESPONSE 29
53
54namespace audio_tools {
55
56// Type definitions for the fixed-width PCM/control values that are always
57// concrete integers, regardless of the internal DSP numeric representation
58// chosen via SampleType (see MDFFloat / MDFFixedPoint below).
59using echo_int16_t = int16_t;
60using echo_uint16_t = uint16_t;
61using echo_int32_t = int32_t;
62using echo_uint32_t = uint32_t;
63
72struct MDFFloat {
73 using word16_t = float;
74 using word32_t = float;
75 using float_t = float;
76};
77
97
114template <typename T, typename SampleType>
115struct EchoState {
116 using Word16 = typename SampleType::word16_t;
117 using Word32 = typename SampleType::word32_t;
118 using Num = typename SampleType::float_t;
119 using Mem = Word32;
120
123 int M;
128 int C;
129 int K;
136
137 Word16* e; /* scratch */
138 Word16* x; /* Far-end input buffer (2N) */
139 Word16* X; /* Far-end buffer (M+1 frames) in frequency domain */
140 Word16* input; /* scratch */
141 Word16* y; /* scratch */
143 Word16* Y; /* scratch */
145 Word32* PHI; /* scratch */
146 Word32* W; /* (Background) filter weights */
147#ifdef TWO_PATH
148 Word16* foreground; /* Foreground filter weights */
149 Word32
150 Davg1; /* 1st recursive average of the residual power difference */
151 Word32
152 Davg2; /* 2nd recursive average of the residual power difference */
153 Num Dvar1; /* Estimated variance of 1st estimator */
154 Num Dvar2; /* Estimated variance of 2nd estimator */
155#endif
156 Word32* power; /* Power of the far-end signal */
157 Num* power_1; /* Inverse power of far-end */
158 Word16* wtmp; /* scratch */
159 Word32* Rf; /* scratch */
160 Word32* Yf; /* scratch */
161 Word32* Xf; /* scratch */
173
177};
178
188struct fft_state {
189 // The MDF algorithm needs synchronous per-sample setValue()/fft()/getValue()
190 // access at a window size (2x frame size) that is independent of whatever
191 // AudioFFTBase::config().length the caller set up. That low-level, single-
192 // shot API only exists on FFTDriver (e.g. FFTDriverRealFFT), not on the
193 // higher-level AudioFFTBase stream wrapper, so we operate on the raw driver
194 // obtained via AudioFFTBase::driver().
196 int N;
203 fft_state(int size, FFTDriver* drv) : driver(drv), N(size) {}
204};
205
206// Forward declarations: these are defined at the bottom of this file but
207// referenced from inside MDFEchoCancellation below. echo_fft/echo_ifft are
208// templated on the sample element type (T = SampleType::word16_t, i.e.
209// float or PseudoFloat) since they convert to/from the FFT driver's plain
210// float bins at the boundary.
211inline void* echo_fft_init(int size, FFTDriver* driver);
212inline void echo_fft_destroy(void* table);
213template <typename T>
214inline void echo_fft(void* table, T* in, T* out);
215template <typename T>
216inline void echo_ifft(void* table, T* in, T* out);
217
296template <typename T = int16_t, typename SampleType = MDFFloat>
298 public:
299 using Word16 = typename SampleType::word16_t;
300 using Word32 = typename SampleType::word32_t;
301 using Num = typename SampleType::float_t;
302 using Mem = Word32;
304
311 MDFEchoCancellation(int filterLength, AudioFFTBase& fftDriver, Allocator& alloc = DefaultAllocator)
312 : fft_driver(&fftDriver), allocator(alloc),
313 filter_length(filterLength), nb_mic(1), nb_speakers(1) {}
314
323 MDFEchoCancellation(int filterLength, int nbMic, int nbSpeakers,
324 AudioFFTBase& fftDriver, Allocator& alloc = DefaultAllocator)
325 : fft_driver(&fftDriver), allocator(alloc),
326 filter_length(filterLength), nb_mic(nbMic), nb_speakers(nbSpeakers) {}
327
330 if (!state) return;
331
333
334 int N = state->window_size;
335 int M = state->M;
336 int C = state->C;
337 int K = state->K;
338 int frame_size = state->frame_size;
339
340 echoFree(state->e, C * N);
341 echoFree(state->x, K * N);
342 echoFree(state->input, C * frame_size);
343 echoFree(state->y, C * N);
344 echoFree(state->last_y, C * N);
345 echoFree(state->Yf, frame_size + 1);
346 echoFree(state->Rf, frame_size + 1);
347 echoFree(state->Xf, frame_size + 1);
348 echoFree(state->Yh, frame_size + 1);
349 echoFree(state->Eh, frame_size + 1);
350 echoFree(state->X, K * (M + 1) * N);
351 echoFree(state->Y, C * N);
352 echoFree(state->E, C * N);
353 echoFree(state->W, C * K * M * N);
354#ifdef TWO_PATH
355 echoFree(state->foreground, M * N * C * K);
356#endif
357 echoFree(state->PHI, N);
358 echoFree(state->power, frame_size + 1);
359 echoFree(state->power_1, frame_size + 1);
360 echoFree(state->window, N);
361 echoFree(state->prop, M);
362 echoFree(state->wtmp, N);
363 echoFree(state->memX, K);
364 echoFree(state->memD, C);
365 echoFree(state->memE, C);
366 echoFree(state->notch_mem, 2 * C);
367 echoFree(state->play_buf, K * (PLAYBACK_DELAY + 1) * frame_size);
368 delete state;
369 state = nullptr;
370 }
371
378 void cancel(const T* rec, const T* play,
379 T* out) {
381 echoCancellationImpl(state, rec, play, out);
382 }
383
388 void capture(const T* rec, T* out) {
394 std::memmove(state->play_buf,
396 state->play_buf_pos * sizeof(T));
397 } else {
398 echoWarning("No playback frame available");
399 if (state->play_buf_pos != 0) {
400 echoWarning("Internal playback buffer corruption");
401 state->play_buf_pos = 0;
402 }
403 for (int i = 0; i < state->frame_size; i++) out[i] = rec[i];
404 }
405 }
406
410 void playback(const T* play) {
412 if (!state->play_buf_started) {
413 echoWarning("Discarded first playback frame");
414 return;
415 }
417 for (int i = 0; i < state->frame_size; i++)
421 echoWarning("Auto-filling buffer");
422 for (int i = 0; i < state->frame_size; i++)
425 }
426 } else {
427 echoWarning("Had to discard playback frame");
428 }
429 }
430
432 void reset() {
434 int N = state->window_size;
435 int M = state->M;
436 int C = state->C;
437 int K = state->K;
438
439 state->cancel_count = 0;
440 state->screwed_up = 0;
441
442 for (int i = 0; i < N * M; i++) state->W[i] = 0;
443#ifdef TWO_PATH
444 for (int i = 0; i < N * M; i++) state->foreground[i] = 0;
445#endif
446 for (int i = 0; i < N * (M + 1); i++) state->X[i] = 0;
447 for (int i = 0; i <= state->frame_size; i++) {
448 state->power[i] = 0;
449 state->power_1[i] = 1.0f;
450 state->Eh[i] = 0;
451 state->Yh[i] = 0;
452 }
453 for (int i = 0; i < state->frame_size; i++) state->last_y[i] = 0;
454 for (int i = 0; i < N * C; i++) state->E[i] = 0;
455 for (int i = 0; i < N * K; i++) state->x[i] = 0;
456 for (int i = 0; i < 2 * C; i++) state->notch_mem[i] = 0;
457 for (int i = 0; i < C; i++) state->memD[i] = state->memE[i] = 0;
458 for (int i = 0; i < K; i++) state->memX[i] = 0;
459
460 state->saturated = 0;
461 state->adapted = 0;
462 state->sum_adapt = 0;
463 state->Pey = state->Pyy = 1.0f;
464#ifdef TWO_PATH
465 state->Davg1 = state->Davg2 = 0;
466 state->Dvar1 = state->Dvar2 = 0.0f;
467#endif
468 for (int i = 0; i < 3 * state->frame_size; i++) state->play_buf[i] = 0;
471 }
472
478 int control(int request, void* ptr) {
479 switch (request) {
481 (*(int*)ptr) = state->frame_size;
482 break;
484 state->sampling_rate = (*(int*)ptr);
488 if (state->sampling_rate < 12000)
489 state->notch_radius = .9f;
490 else if (state->sampling_rate < 24000)
491 state->notch_radius = .982f;
492 else
493 state->notch_radius = .992f;
494 break;
496 (*(int*)ptr) = state->sampling_rate;
497 break;
499 *((echo_int32_t*)ptr) = state->M * state->frame_size;
500 break;
502 int M = state->M, N = state->window_size, n = state->frame_size;
503 echo_int32_t* filt = (echo_int32_t*)ptr;
504 for (int j = 0; j < M; j++) {
505 echo_ifft(state->fft_table, &state->W[j * N], state->wtmp);
506 for (int i = 0; i < n; i++) filt[j * n + i] = 32767 * state->wtmp[i];
507 }
508 } break;
509 default:
510 return -1;
511 }
512 return 0;
513 }
514
516 int getFrameSize() { return state->frame_size; }
517
522
525
528
534 }
535
539 void setFilterLength(int len) {
540 if (initialized) {
541 echoWarning("Cannot change filter length after initialization");
542 return;
543 }
544 filter_length = len;
545 }
546
549
553 void setMicChannels(int num) {
554 if (initialized) {
555 echoWarning("Cannot change mic channels after initialization");
556 return;
557 }
558 nb_mic = num;
559 }
560
562 int getMicChannels() { return nb_mic; }
563
567 void setSpeakerChannels(int num) {
568 if (initialized) {
569 echoWarning("Cannot change speaker channels after initialization");
570 return;
571 }
572 nb_speakers = num;
573 }
574
577
581 void setFFTDriver(AudioFFTBase& fftDriver) {
582 if (initialized) {
583 echoWarning("Cannot change FFT driver after initialization");
584 return;
585 }
586 fft_driver = &fftDriver;
587 }
588
590 State* getState() { return state; }
591
592 protected:
593 State* state = nullptr;
597 int nb_mic;
599 bool initialized = false;
605 if (initialized) return;
606
607 int frameSize = fft_driver->config().length;
609 if (state && fft_driver) {
611 }
612 initialized = true;
613 }
614
622 template <typename A>
623 A* echoAlloc(size_t count) {
624 return allocator.createArray<A>(count);
625 }
626
634 template <typename A>
635 void echoFree(A* ptr, size_t count) {
636 allocator.removeArray<A>(ptr, count);
637 }
638
639 inline void echoWarning(const char* str) {
640 LOGW("EchoCanceller Warning: %s", str);
641 }
642
643 inline void echoFatal(const char* str) {
644 LOGE("EchoCanceller Error: %s", str);
645 }
646
647 template <typename A>
648 inline A spxSqrt(A x) { return A(sqrtf((float)x)); }
649
651 int r = 0;
652 if (x >= (echo_int32_t)65536) {
653 x >>= 16;
654 r += 16;
655 }
656 if (x >= 256) {
657 x >>= 8;
658 r += 8;
659 }
660 if (x >= 16) {
661 x >>= 4;
662 r += 4;
663 }
664 if (x >= 4) {
665 x >>= 2;
666 r += 2;
667 }
668 if (x >= 2) {
669 r += 1;
670 }
671 return r;
672 }
673
679 inline float spxExp(float x) { return expf(x); }
680
686 inline float spxCos(float x) { return cosf(x); }
687
697 inline void filterDcNotch16(const T* in, Word16 radius,
698 Word16* out, int len, Mem* mem,
699 int stride) {
700 Word16 den2 = radius * radius + .7f * (1 - radius) * (1 - radius);
701 for (int i = 0; i < len; i++) {
702 Word16 vin = in[i * stride];
703 Word32 vout = mem[0] + vin;
704 mem[0] = mem[1] + 2 * (-vin + radius * vout);
705 mem[1] = vin - den2 * vout;
706 out[i] = radius * vout;
707 }
708 }
709
717 inline Word32 mdfInnerProd(const Word16* x, const Word16* y,
718 int len) {
719 float sum = 0;
720 for (int i = 0; i < len; i++) {
721 sum += x[i] * y[i];
722 }
723 return sum;
724 }
725
732 inline void powerSpectrum(const Word16* X, Word32* ps, int N) {
733 ps[0] = X[0] * X[0];
734 for (int i = 1, j = 1; i < N - 1; i += 2, j++) {
735 ps[j] = X[i] * X[i] + X[i + 1] * X[i + 1];
736 }
737 ps[N / 2] = X[N - 1] * X[N - 1];
738 }
739
746 inline void powerSpectrumAccum(const Word16* X, Word32* ps,
747 int N) {
748 ps[0] += X[0] * X[0];
749 for (int i = 1, j = 1; i < N - 1; i += 2, j++) {
750 ps[j] += X[i] * X[i] + X[i + 1] * X[i + 1];
751 }
752 ps[N / 2] += X[N - 1] * X[N - 1];
753 }
754
763 inline void spectralMulAccum(const Word16* X, const Word32* Y,
764 Word16* acc, int N, int M) {
765 for (int i = 0; i < N; i++) acc[i] = 0;
766
767 for (int j = 0; j < M; j++) {
768 acc[0] += X[0] * Y[0];
769 for (int i = 1; i < N - 1; i += 2) {
770 acc[i] += (X[i] * Y[i] - X[i + 1] * Y[i + 1]);
771 acc[i + 1] += (X[i + 1] * Y[i] + X[i] * Y[i + 1]);
772 }
773 acc[N - 1] += X[N - 1] * Y[N - 1];
774 X += N;
775 Y += N;
776 }
777 }
778
788 inline void weightedSpectralMulConj(const Num* w, const Num p,
789 const Word16* X,
790 const Word16* Y, Word32* prod,
791 int N) {
792 // NOTE: the (int32_t) casts this code originally had here were only
793 // correct in true fixed-point mode (X/Y as int16_t, needing widening
794 // before multiplying to avoid 16-bit overflow). With Word16 /
795 // Word32 now float (see the aliases above), those casts instead
796 // truncated every frequency-domain sample to an integer before ever
797 // multiplying it by anything -- e.g. a bin magnitude of 0.03 became 0,
798 // silently zeroing out the filter's gradient. Removed.
799 Num W;
800 W = p * w[0];
801 prod[0] = W * (X[0] * Y[0]);
802 for (int i = 1, j = 1; i < N - 1; i += 2, j++) {
803 W = p * w[j];
804 prod[i] = W * (X[i] * Y[i] + X[i + 1] * Y[i + 1]);
805 prod[i + 1] = W * (-X[i + 1] * Y[i] + X[i] * Y[i + 1]);
806 }
807 W = p * w[N / 2];
808 prod[N - 1] = W * (X[N - 1] * Y[N - 1]);
809 }
810
819 inline void mdfAdjustProp(const Word32* W, int N, int M, int P,
820 Word16* prop) {
821 Word16 max_sum = 1;
822 Word32 prop_sum = 1;
823
824 for (int i = 0; i < M; i++) {
825 Word32 tmp = 1;
826 for (int p = 0; p < P; p++) {
827 for (int j = 0; j < N; j++) {
828 float val = W[p * N * M + i * N + j];
829 tmp += val * val;
830 }
831 }
832 prop[i] = spxSqrt(tmp);
833 if (prop[i] > max_sum) max_sum = prop[i];
834 }
835
836 for (int i = 0; i < M; i++) {
837 prop[i] += 0.1f * max_sum;
838 prop_sum += prop[i];
839 }
840
841 for (int i = 0; i < M; i++) {
842 prop[i] = 0.99f * prop[i] / prop_sum;
843 }
844 }
845
854 State* echoStateInitMc(int frame_size, int filter_length, int nb_mic,
855 int nb_speakers) {
856 int N = frame_size * 2;
857 int M = (filter_length + frame_size - 1) / frame_size;
858 int C = nb_mic;
859 int K = nb_speakers;
860
861 State* st = new State();
862 if (!st) return nullptr;
863
864 st->K = K;
865 st->C = C;
866 st->frame_size = frame_size;
867 st->window_size = N;
868 st->M = M;
869 st->cancel_count = 0;
870 st->sum_adapt = 0;
871 st->saturated = 0;
872 st->screwed_up = 0;
873 st->sampling_rate = 8000;
874 st->spec_average = st->frame_size / (float)st->sampling_rate;
875 st->beta0 = (2.0f * st->frame_size) / st->sampling_rate;
876 st->beta_max = (.5f * st->frame_size) / st->sampling_rate;
877 st->leak_estimate = 0;
878
879 // Allocate buffers
880 st->e = echoAlloc<Word16>(C * N);
881 st->x = echoAlloc<Word16>(K * N);
882 st->input = echoAlloc<Word16>(C * st->frame_size);
883 st->y = echoAlloc<Word16>(C * N);
884 st->last_y = echoAlloc<Word16>(C * N);
885 st->Yf = echoAlloc<Word32>(st->frame_size + 1);
886 st->Rf = echoAlloc<Word32>(st->frame_size + 1);
887 st->Xf = echoAlloc<Word32>(st->frame_size + 1);
888 st->Yh = echoAlloc<Word32>(st->frame_size + 1);
889 st->Eh = echoAlloc<Word32>(st->frame_size + 1);
890 st->X = echoAlloc<Word16>(K * (M + 1) * N);
891 st->Y = echoAlloc<Word16>(C * N);
892 st->E = echoAlloc<Word16>(C * N);
893 st->W = echoAlloc<Word32>(C * K * M * N);
894
895#ifdef TWO_PATH
896 st->foreground = echoAlloc<Word16>(M * N * C * K);
897#endif
898
899 st->PHI = echoAlloc<Word32>(N);
900 st->power = echoAlloc<Word32>(frame_size + 1);
901 st->power_1 = echoAlloc<Num>(frame_size + 1);
902 st->window = echoAlloc<Word16>(N);
903 st->prop = echoAlloc<Word16>(M);
904 st->wtmp = echoAlloc<Word16>(N);
905
906 // Initialize window
907 for (int i = 0; i < N; i++)
908 st->window[i] = .5f - .5f * cosf(2 * M_PI * i / N);
909
910 // Initialize power_1
911 for (int i = 0; i <= st->frame_size; i++) st->power_1[i] = 1.0f;
912
913 // Initialize W
914 for (int i = 0; i < N * M * K * C; i++) st->W[i] = 0;
915
916 // Initialize prop
917 {
918 Word32 sum = 0;
919 float decay = expf(-2.4f / M);
920 st->prop[0] = .7f;
921 sum = st->prop[0];
922 for (int i = 1; i < M; i++) {
923 st->prop[i] = st->prop[i - 1] * decay;
924 sum += st->prop[i];
925 }
926 for (int i = M - 1; i >= 0; i--) {
927 st->prop[i] = .8f * st->prop[i] / sum;
928 }
929 }
930
931 st->memX = echoAlloc<Word16>(K);
932 st->memD = echoAlloc<Word16>(C);
933 st->memE = echoAlloc<Word16>(C);
934 st->preemph = .9f;
935
936 if (st->sampling_rate < 12000)
937 st->notch_radius = .9f;
938 else if (st->sampling_rate < 24000)
939 st->notch_radius = .982f;
940 else
941 st->notch_radius = .992f;
942
943 st->notch_mem = echoAlloc<Mem>(2 * C);
944 st->adapted = 0;
945 st->Pey = st->Pyy = 1.0f;
946
947#ifdef TWO_PATH
948 st->Davg1 = st->Davg2 = 0;
949 st->Dvar1 = st->Dvar2 = 0.0f;
950#endif
951
952 st->play_buf =
953 echoAlloc<T>(K * (PLAYBACK_DELAY + 1) * st->frame_size);
954 st->play_buf_pos = PLAYBACK_DELAY * st->frame_size;
955 st->play_buf_started = 0;
956
957 st->fft_table = nullptr;
958
959 return st;
960 }
961
970 inline T saturateToSample(Num x) const {
971 float v = (float)x;
972 float mn = NumberConverter::minValueT<T>();
973 float mx = NumberConverter::maxValueT<T>();
974 if (v < mn + 0.5f) return (T)mn;
975 if (v > mx - 0.5f) return (T)mx;
976 return (T)floorf(0.5f + v);
977 }
978
995 inline void echoCancellationImpl(State* st, const T* in,
996 const T* far_end,
997 T* out) {
998 int N = st->window_size;
999 int M = st->M;
1000 int C = st->C;
1001 int K = st->K;
1002
1003 // Wide-dynamic-range tuning constants, as Num (float or PseudoFloat)
1004 // so they multiply/compare directly against Num-typed state below.
1005 const Num min_leak(0.005f); // Minimum leak estimate for the adaptive filter
1006 const Num var1_smooth(0.36f); // Smoothing coefficient, 1st variance estimator
1007 const Num var2_smooth(0.7225f); // Smoothing coefficient, 2nd variance estimator
1008 const Num var1_update(0.5f); // Update threshold, 1st variance estimator
1009 const Num var2_update(0.25f); // Update threshold, 2nd variance estimator
1010 const Num var_backtrack(4.0f); // Backtrack threshold for filter reset
1011
1012 st->cancel_count++;
1013 float ss = .35f / M;
1014 float ss_1 = 1 - ss;
1015
1016 // Apply notch filter and pre-emphasis to input
1017 for (int chan = 0; chan < C; chan++) {
1018 filterDcNotch16(in + chan, st->notch_radius,
1019 st->input + chan * st->frame_size, st->frame_size,
1020 st->notch_mem + 2 * chan, C);
1021
1022 for (int i = 0; i < st->frame_size; i++) {
1023 Word32 tmp32 =
1024 st->input[chan * st->frame_size + i] - st->preemph * st->memD[chan];
1025 st->memD[chan] = st->input[chan * st->frame_size + i];
1026 st->input[chan * st->frame_size + i] = tmp32;
1027 }
1028 }
1029
1030 // Process far-end signal
1031 for (int speak = 0; speak < K; speak++) {
1032 std::memmove(&st->x[speak * N],
1033 &st->x[speak * N + st->frame_size],
1034 st->frame_size * sizeof(Word16));
1035 for (int i = 0; i < st->frame_size; i++) {
1036 Word32 tmp32 =
1037 far_end[i * K + speak] - st->preemph * st->memX[speak];
1038 st->x[speak * N + i + st->frame_size] = tmp32;
1039 st->memX[speak] = far_end[i * K + speak];
1040 }
1041 }
1042
1043 // Shift memory and compute FFT of far-end
1044 for (int speak = 0; speak < K; speak++) {
1045 for (int j = M - 1; j >= 0; j--) {
1046 std::memmove(&st->X[(j + 1) * N * K + speak * N],
1047 &st->X[j * N * K + speak * N],
1048 N * sizeof(Word16));
1049 }
1050 echo_fft(st->fft_table, st->x + speak * N, &st->X[speak * N]);
1051 }
1052
1053 // Compute power spectrum of far-end
1054 Word32 Sxx = 0;
1055 for (int speak = 0; speak < K; speak++) {
1056 Sxx += mdfInnerProd(st->x + speak * N + st->frame_size,
1057 st->x + speak * N + st->frame_size, st->frame_size);
1058 powerSpectrumAccum(st->X + speak * N, st->Xf, N);
1059 }
1060
1061 // Compute foreground filter output and residual
1062 Word32 Sff = 0;
1063 for (int chan = 0; chan < C; chan++) {
1064#ifdef TWO_PATH
1065 spectralMulAccum(st->X, st->foreground + chan * N * K * M,
1066 st->Y + chan * N, N, M * K);
1067 echo_ifft(st->fft_table, st->Y + chan * N, st->e + chan * N);
1068 for (int i = 0; i < st->frame_size; i++)
1069 st->e[chan * N + i] = st->input[chan * st->frame_size + i] -
1070 st->e[chan * N + i + st->frame_size];
1071 Sff += mdfInnerProd(st->e + chan * N, st->e + chan * N, st->frame_size);
1072#endif
1073 }
1074
1075 // Adjust proportional adaptation
1076 if (st->adapted) mdfAdjustProp(st->W, N, M, C * K, st->prop);
1077
1078 // Compute weight gradient
1079 if (st->saturated == 0) {
1080 for (int chan = 0; chan < C; chan++) {
1081 for (int speak = 0; speak < K; speak++) {
1082 for (int j = M - 1; j >= 0; j--) {
1083 weightedSpectralMulConj(st->power_1, st->prop[j],
1084 &st->X[(j + 1) * N * K + speak * N],
1085 st->E + chan * N, st->PHI, N);
1086 for (int i = 0; i < N; i++)
1087 st->W[chan * N * K * M + j * N * K + speak * N + i] += st->PHI[i];
1088 }
1089 }
1090 }
1091 } else {
1092 st->saturated--;
1093 }
1094
1095 // Update weights (AUMDF)
1096 for (int chan = 0; chan < C; chan++) {
1097 for (int speak = 0; speak < K; speak++) {
1098 for (int j = 0; j < M; j++) {
1099 if (j == 0 || st->cancel_count % (M - 1) == j - 1) {
1100 echo_ifft(
1101 st->fft_table, &st->W[chan * N * K * M + j * N * K + speak * N],
1102 st->wtmp);
1103 for (int i = st->frame_size; i < N; i++) st->wtmp[i] = 0;
1104 echo_fft(
1105 st->fft_table, st->wtmp,
1106 &st->W[chan * N * K * M + j * N * K + speak * N]);
1107 }
1108 }
1109 }
1110 }
1111
1112 // Initialize spectrum buffers
1113 for (int i = 0; i <= st->frame_size; i++)
1114 st->Rf[i] = st->Yf[i] = st->Xf[i] = 0;
1115
1116 Word32 Dbf = 0;
1117 Word32 See = 0;
1118
1119#ifdef TWO_PATH
1120 // Compute background filter output
1121 for (int chan = 0; chan < C; chan++) {
1122 spectralMulAccum(st->X, st->W + chan * N * K * M, st->Y + chan * N, N,
1123 M * K);
1124 echo_ifft(st->fft_table, st->Y + chan * N, st->y + chan * N);
1125 for (int i = 0; i < st->frame_size; i++)
1126 st->e[chan * N + i] = st->e[chan * N + i + st->frame_size] -
1127 st->y[chan * N + i + st->frame_size];
1128 Dbf +=
1129 10 + mdfInnerProd(st->e + chan * N, st->e + chan * N, st->frame_size);
1130 for (int i = 0; i < st->frame_size; i++)
1131 st->e[chan * N + i] = st->input[chan * st->frame_size + i] -
1132 st->y[chan * N + i + st->frame_size];
1133 See += mdfInnerProd(st->e + chan * N, st->e + chan * N, st->frame_size);
1134 }
1135#endif
1136
1137#ifndef TWO_PATH
1138 Sff = See;
1139#endif
1140
1141#ifdef TWO_PATH
1142 // Two-path filter logic
1143 st->Davg1 = .6f * st->Davg1 + .4f * (Sff - See);
1144 st->Davg2 = .85f * st->Davg2 + .15f * (Sff - See);
1145 st->Dvar1 = var1_smooth * st->Dvar1 + (.4f * Sff) * (.4f * Dbf);
1146 st->Dvar2 = var2_smooth * st->Dvar2 + (.15f * Sff) * (.15f * Dbf);
1147
1148 int update_foreground = 0;
1149 if ((Sff - See) * fabsf(Sff - See) > Sff * Dbf)
1151 else if (st->Davg1 * fabsf((float)st->Davg1) > var1_update * st->Dvar1)
1153 else if (st->Davg2 * fabsf((float)st->Davg2) > var2_update * st->Dvar2)
1155
1156 if (update_foreground) {
1157 st->Davg1 = st->Davg2 = 0;
1158 st->Dvar1 = st->Dvar2 = 0.0f;
1159 std::memcpy(st->foreground, st->W, N * M * C * K * sizeof(Word16));
1160 for (int chan = 0; chan < C; chan++)
1161 for (int i = 0; i < st->frame_size; i++)
1162 st->e[chan * N + i + st->frame_size] =
1163 st->window[i + st->frame_size] *
1164 st->e[chan * N + i + st->frame_size] +
1165 st->window[i] * st->y[chan * N + i + st->frame_size];
1166 } else {
1167 int reset_background = 0;
1168 if ((-(Sff - See)) * fabsf((float)(Sff - See)) >
1169 var_backtrack * (Sff * Dbf))
1170 reset_background = 1;
1171 if ((-st->Davg1) * fabsf((float)st->Davg1) > var_backtrack * st->Dvar1)
1172 reset_background = 1;
1173 if ((-st->Davg2) * fabsf((float)st->Davg2) > var_backtrack * st->Dvar2)
1174 reset_background = 1;
1175
1176 if (reset_background) {
1177 std::memcpy(st->W, st->foreground, N * M * C * K * sizeof(Word32));
1178 for (int chan = 0; chan < C; chan++) {
1179 for (int i = 0; i < st->frame_size; i++)
1180 st->y[chan * N + i + st->frame_size] =
1181 st->e[chan * N + i + st->frame_size];
1182 for (int i = 0; i < st->frame_size; i++)
1183 st->e[chan * N + i] = st->input[chan * st->frame_size + i] -
1184 st->y[chan * N + i + st->frame_size];
1185 }
1186 See = Sff;
1187 st->Davg1 = st->Davg2 = 0;
1188 st->Dvar1 = st->Dvar2 = 0.0f;
1189 }
1190 }
1191#endif
1192
1193 Word32 Sey = 0, Syy = 0, Sdd = 0;
1194 const float sat_threshold = NumberConverter::maxValueT<T>() * (32000.0f / 32767.0f);
1195 for (int chan = 0; chan < C; chan++) {
1196 // Compute output with de-emphasis
1197 for (int i = 0; i < st->frame_size; i++) {
1199#ifdef TWO_PATH
1200 tmp_out = st->input[chan * st->frame_size + i] -
1201 st->e[chan * N + i + st->frame_size];
1202#else
1203 tmp_out = st->input[chan * st->frame_size + i] -
1204 st->y[chan * N + i + st->frame_size];
1205#endif
1206 tmp_out = tmp_out + st->preemph * st->memE[chan];
1207 if (in[i * C + chan] <= -sat_threshold || in[i * C + chan] >= sat_threshold) {
1208 if (st->saturated == 0) st->saturated = 1;
1209 }
1210 out[i * C + chan] = saturateToSample(tmp_out);
1211 st->memE[chan] = tmp_out;
1212 }
1213
1214 // Prepare error signal for filter update
1215 for (int i = 0; i < st->frame_size; i++) {
1216 st->e[chan * N + i + st->frame_size] = st->e[chan * N + i];
1217 st->e[chan * N + i] = 0;
1218 }
1219
1220 // Compute correlations
1221 Sey += mdfInnerProd(st->e + chan * N + st->frame_size,
1222 st->y + chan * N + st->frame_size, st->frame_size);
1223 Syy += mdfInnerProd(st->y + chan * N + st->frame_size,
1224 st->y + chan * N + st->frame_size, st->frame_size);
1225 Sdd += mdfInnerProd(st->input + chan * st->frame_size,
1226 st->input + chan * st->frame_size, st->frame_size);
1227
1228 // Convert error to frequency domain
1229 echo_fft(st->fft_table, st->e + chan * N, st->E + chan * N);
1230
1231 for (int i = 0; i < st->frame_size; i++) st->y[i + chan * N] = 0;
1232 echo_fft(st->fft_table, st->y + chan * N, st->Y + chan * N);
1233
1234 // Compute power spectra
1235 powerSpectrumAccum(st->E + chan * N, st->Rf, N);
1236 powerSpectrumAccum(st->Y + chan * N, st->Yf, N);
1237 }
1238
1239 // Sanity checks
1240 if (!(Syy >= 0 && Sxx >= 0 && See >= 0) ||
1241 !(Sff < N * 1e9f && Syy < N * 1e9f && Sxx < N * 1e9f)) {
1242 st->screwed_up += 50;
1243 for (int i = 0; i < st->frame_size * C; i++) out[i] = 0;
1244 } else if (Sff > Sdd + N * 10000.0f) {
1245 st->screwed_up++;
1246 } else {
1247 st->screwed_up = 0;
1248 }
1249
1250 if (st->screwed_up >= 50) {
1251 echoWarning("Echo canceller reset");
1252 reset();
1253 return;
1254 }
1255
1256 if (See < N * 100.0f) See = N * 100.0f;
1257
1258 for (int speak = 0; speak < K; speak++) {
1259 Sxx += mdfInnerProd(st->x + speak * N + st->frame_size,
1260 st->x + speak * N + st->frame_size, st->frame_size);
1261 powerSpectrumAccum(st->X + speak * N, st->Xf, N);
1262 }
1263
1264 // Smooth far-end energy
1265 for (int j = 0; j <= st->frame_size; j++)
1266 st->power[j] = ss_1 * st->power[j] + 1 + ss * st->Xf[j];
1267
1268 // Compute filtered spectra and correlations
1269 Num Pey = 0.0f, Pyy = 0.0f;
1270 for (int j = st->frame_size; j >= 0; j--) {
1271 Num Eh = st->Rf[j] - st->Eh[j];
1272 Num Yh = st->Yf[j] - st->Yh[j];
1273 Pey = Pey + Eh * Yh;
1274 Pyy = Pyy + Yh * Yh;
1275 st->Eh[j] =
1276 (1 - st->spec_average) * st->Eh[j] + st->spec_average * st->Rf[j];
1277 st->Yh[j] =
1278 (1 - st->spec_average) * st->Yh[j] + st->spec_average * st->Yf[j];
1279 }
1280
1281 Pyy = spxSqrt(Pyy);
1282 // Pyy is 0 on the very first frames (no echo spectrum has built up yet),
1283 // which would make Pey/Pyy evaluate to 0/0 = NaN and permanently poison
1284 // st->Pey / leak_estimate (a NaN comparison is always false, so the
1285 // clamps below can never recover from it). Floor it at 1.0f, same
1286 // as the clamp already applied to st->Pyy a few lines down.
1287 if (Pyy < 1.0f) Pyy = 1.0f;
1288 Pey = Pey / Pyy;
1289
1290 // Compute correlation update rate
1291 Word32 tmp32 = st->beta0 * Syy;
1292 if (tmp32 > st->beta_max * See) tmp32 = st->beta_max * See;
1293 Num alpha = tmp32 / See;
1294 Num alpha_1 = 1.0f - alpha;
1295
1296 st->Pey = alpha_1 * st->Pey + alpha * Pey;
1297 st->Pyy = alpha_1 * st->Pyy + alpha * Pyy;
1298 if (st->Pyy < 1.0f) st->Pyy = 1.0f;
1299 if (st->Pey < min_leak * st->Pyy) st->Pey = min_leak * st->Pyy;
1300 if (st->Pey > st->Pyy) st->Pey = st->Pyy;
1301
1302 st->leak_estimate = st->Pey / st->Pyy;
1303 if (st->leak_estimate > 16383)
1304 st->leak_estimate = 32767;
1305 else
1306 st->leak_estimate = st->leak_estimate * 2;
1307
1308 // Compute RER
1309 Word16 RER;
1310 RER = (.0001f * Sxx + 3.f * st->leak_estimate * Syy) / See;
1311 if (RER < Sey * Sey / (1 + See * Syy)) RER = Sey * Sey / (1 + See * Syy);
1312 if (RER > .5f) RER = .5f;
1313
1314 if (!st->adapted && st->sum_adapt > M &&
1315 st->leak_estimate * Syy > .03f * Syy) {
1316 st->adapted = 1;
1317 }
1318
1319 if (st->adapted) {
1320 for (int i = 0; i <= st->frame_size; i++) {
1321 Word32 r = st->leak_estimate * st->Yf[i];
1322 Word32 e = st->Rf[i] + 1;
1323 if (r > .5f * e) r = .5f * e;
1324 r = .7f * r + .3f * (RER * e);
1325 st->power_1[i] = r / (e * (st->power[i] + 10));
1326 }
1327 } else {
1328 Word16 adapt_rate = 0;
1329 if (Sxx > N * 1000.0f) {
1330 tmp32 = .25f * Sxx;
1331 if (tmp32 > .25f * See) tmp32 = .25f * See;
1332 adapt_rate = tmp32 / See;
1333 }
1334 for (int i = 0; i <= st->frame_size; i++)
1335 st->power_1[i] = adapt_rate / (st->power[i] + 10);
1336 st->sum_adapt = st->sum_adapt + adapt_rate;
1337 }
1338
1339 std::memmove(st->last_y,
1340 &st->last_y[st->frame_size],
1341 st->frame_size * sizeof(Word16));
1342 if (st->adapted) {
1343 for (int i = 0; i < st->frame_size; i++)
1344 st->last_y[st->frame_size + i] = in[i] - out[i];
1345 }
1346 }
1347};
1348
1349// ============================================================================
1350// FFT Implementation
1351// ============================================================================
1361inline void* echo_fft_init(int size, FFTDriver* driver) {
1362 if (!driver) {
1363 return nullptr;
1364 }
1365
1366 // Re-initialize the raw driver at the window size the MDF algorithm
1367 // actually needs (independent of the caller's AudioFFTBase::config()).
1368 if (!driver->begin(size)) {
1369 return nullptr;
1370 }
1371 return new fft_state(size, driver);
1372}
1373
1378inline void echo_fft_destroy(void* table) {
1379 if (table) {
1380 auto* st = static_cast<fft_state*>(table);
1381 st->driver->end();
1382 delete st;
1383 }
1384}
1385
1394template <typename T>
1395inline void echo_fft(void* table, T* in, T* out) {
1396 auto* st = static_cast<fft_state*>(table);
1397 if (!st || !st->driver) return;
1398
1399 // Set input values
1400 for (int i = 0; i < st->N; i++) {
1401 st->driver->setValue(i, (float)in[i] / (float)st->N);
1402 }
1403
1404 // Perform FFT
1405 st->driver->fft();
1406
1407 // Get output in packed format: out[0]=real[0], out[1]=real[1],
1408 // out[2]=img[1],
1409 // ...
1410 // Note: getValue() only reflects the frequency domain after rfft()
1411 // (inverse); right after fft() (forward), it is still the raw input, so
1412 // DC/Nyquist must come from getBin() too (its imaginary part is always 0
1413 // for those two bins).
1414 FFTBin dc_bin;
1415 st->driver->getBin(0, dc_bin);
1416 out[0] = dc_bin.real; // DC component
1417 for (int i = 1; i < st->N - 1; i += 2) {
1418 int bin = (i + 1) / 2;
1419 FFTBin fft_bin;
1420 st->driver->getBin(bin, fft_bin);
1421 out[i] = fft_bin.real;
1422 out[i + 1] = fft_bin.img;
1423 }
1424 FFTBin nyquist_bin;
1425 st->driver->getBin(st->N / 2, nyquist_bin);
1426 out[st->N - 1] = nyquist_bin.real; // Nyquist
1427}
1428
1437template <typename T>
1438inline void echo_ifft(void* table, T* in, T* out) {
1439 auto* st = static_cast<fft_state*>(table);
1440 if (!st || !st->driver || !st->driver->isReverseFFT()) return;
1441
1442 // Set bins from packed format
1443 st->driver->setBin(0, (float)in[0], 0);
1444 for (int i = 1; i < st->N - 1; i += 2) {
1445 int bin = (i + 1) / 2;
1446 st->driver->setBin(bin, (float)in[i], (float)in[i + 1]);
1447 }
1448 st->driver->setBin(st->N / 2, (float)in[st->N - 1], 0);
1449
1450 // Perform inverse FFT
1451 st->driver->rfft();
1452
1453 // Get output
1454 for (int i = 0; i < st->N; i++) {
1455 out[i] = st->driver->getValue(i);
1456 }
1457}
1458
1459} // namespace audio_tools
#define ss
Definition AudioLoRa.h:8
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define ECHO_GET_IMPULSE_RESPONSE
Definition MDFEchoCancellation.h:52
#define ECHO_GET_SAMPLING_RATE
Definition MDFEchoCancellation.h:50
#define ECHO_SET_SAMPLING_RATE
Definition MDFEchoCancellation.h:49
#define ECHO_GET_IMPULSE_RESPONSE_SIZE
Definition MDFEchoCancellation.h:51
#define ECHO_GET_FRAME_SIZE
Definition MDFEchoCancellation.h:48
#define PLAYBACK_DELAY
Definition MDFEchoCancellationConfig.h:58
#define M_PI
Definition MDFEchoCancellationConfig.h:66
void play(maxi_float_t *channels)
Memory allocateator which uses malloc.
Definition Allocator.h:24
T * createArray(int len)
Definition Allocator.h:45
void removeArray(T *obj, int len)
Definition Allocator.h:62
Executes FFT using audio data privded by write() and/or an inverse FFT where the samples are made ava...
Definition AudioFFT.h:200
AudioFFTConfig & config()
Provides the actual configuration.
Definition AudioFFT.h:648
FFTDriver * driver()
Definition AudioFFT.h:560
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:120
RingBuffer< uint8_t > tmp_out
Definition BaseStream.h:96
Abstract Class which defines the basic FFT functionality.
Definition AudioFFT.h:165
virtual bool setBin(int idx, float real, float img)
sets the value of a bin
Definition AudioFFT.h:185
virtual void end()=0
virtual void setValue(int pos, float value)=0
Sets the real value.
virtual bool begin(int len)=0
Acoustic echo canceller using MDF algorithm.
Definition MDFEchoCancellation.h:297
void cancel(const T *rec, const T *play, T *out)
Definition MDFEchoCancellation.h:378
int getMicChannels()
Definition MDFEchoCancellation.h:562
int getSamplingRate()
Definition MDFEchoCancellation.h:524
Word32 mdfInnerProd(const Word16 *x, const Word16 *y, int len)
Compute inner product of two vectors.
Definition MDFEchoCancellation.h:717
MDFEchoCancellation(int filterLength, int nbMic, int nbSpeakers, AudioFFTBase &fftDriver, Allocator &alloc=DefaultAllocator)
Definition MDFEchoCancellation.h:323
void echoCancellationImpl(State *st, const T *in, const T *far_end, T *out)
Core echo cancellation implementation.
Definition MDFEchoCancellation.h:995
void spectralMulAccum(const Word16 *X, const Word32 *Y, Word16 *acc, int N, int M)
Accumulate spectral multiplication across multiple frames.
Definition MDFEchoCancellation.h:763
~MDFEchoCancellation()
Definition MDFEchoCancellation.h:329
void getImpulseResponse(echo_int32_t *response)
Definition MDFEchoCancellation.h:532
echo_int16_t spxIlog2(echo_uint32_t x)
Definition MDFEchoCancellation.h:650
MDFEchoCancellation(int filterLength, AudioFFTBase &fftDriver, Allocator &alloc=DefaultAllocator)
Definition MDFEchoCancellation.h:311
void filterDcNotch16(const T *in, Word16 radius, Word16 *out, int len, Mem *mem, int stride)
Apply DC notch filter to remove DC offset.
Definition MDFEchoCancellation.h:697
typename SampleType::word32_t Word32
Definition MDFEchoCancellation.h:300
void playback(const T *play)
Definition MDFEchoCancellation.h:410
void setSpeakerChannels(int num)
Definition MDFEchoCancellation.h:567
AudioFFTBase * fft_driver
Definition MDFEchoCancellation.h:594
typename SampleType::word16_t Word16
Definition MDFEchoCancellation.h:299
A spxSqrt(A x)
Definition MDFEchoCancellation.h:648
void setMicChannels(int num)
Definition MDFEchoCancellation.h:553
void powerSpectrumAccum(const Word16 *X, Word32 *ps, int N)
Accumulate power spectrum from FFT output.
Definition MDFEchoCancellation.h:746
void powerSpectrum(const Word16 *X, Word32 *ps, int N)
Compute power spectrum from FFT output.
Definition MDFEchoCancellation.h:732
State * getState()
Definition MDFEchoCancellation.h:590
int control(int request, void *ptr)
Definition MDFEchoCancellation.h:478
int nb_speakers
Definition MDFEchoCancellation.h:598
int getFilterLength()
Definition MDFEchoCancellation.h:548
void ensureInitialized()
Ensure echo canceller is initialized (lazy initialization)
Definition MDFEchoCancellation.h:604
A * echoAlloc(size_t count)
Allocate a zero-initialized array of type A using the configured Allocator (see AudioTools/CoreAudio/...
Definition MDFEchoCancellation.h:623
State * echoStateInitMc(int frame_size, int filter_length, int nb_mic, int nb_speakers)
Initialize multi-channel echo canceller state.
Definition MDFEchoCancellation.h:854
Allocator & allocator
Definition MDFEchoCancellation.h:595
Word32 Mem
Definition MDFEchoCancellation.h:302
void weightedSpectralMulConj(const Num *w, const Num p, const Word16 *X, const Word16 *Y, Word32 *prod, int N)
Compute weighted spectral multiplication with conjugate.
Definition MDFEchoCancellation.h:788
void setFilterLength(int len)
Definition MDFEchoCancellation.h:539
void echoFatal(const char *str)
Definition MDFEchoCancellation.h:643
int getImpulseResponseSize()
Definition MDFEchoCancellation.h:527
int getFrameSize()
Definition MDFEchoCancellation.h:516
void echoWarning(const char *str)
Definition MDFEchoCancellation.h:639
void setSamplingRate(int rate)
Definition MDFEchoCancellation.h:521
T saturateToSample(Num x) const
Rounds and saturates a de-emphasized output sample to T's representable range. Replaces the original ...
Definition MDFEchoCancellation.h:970
typename SampleType::float_t Num
Definition MDFEchoCancellation.h:301
int nb_mic
Definition MDFEchoCancellation.h:597
void mdfAdjustProp(const Word32 *W, int N, int M, int P, Word16 *prop)
Adjust proportional adaptation weights.
Definition MDFEchoCancellation.h:819
void capture(const T *rec, T *out)
Definition MDFEchoCancellation.h:388
void echoFree(A *ptr, size_t count)
Deallocate an array previously returned by echoAlloc()
Definition MDFEchoCancellation.h:635
EchoState< T, SampleType > State
Definition MDFEchoCancellation.h:303
void setFFTDriver(AudioFFTBase &fftDriver)
Definition MDFEchoCancellation.h:581
void reset()
Definition MDFEchoCancellation.h:432
State * state
Definition MDFEchoCancellation.h:593
float spxExp(float x)
Compute exponential function.
Definition MDFEchoCancellation.h:679
bool initialized
Definition MDFEchoCancellation.h:599
int getSpeakerChannels()
Definition MDFEchoCancellation.h:576
float spxCos(float x)
Compute cosine function.
Definition MDFEchoCancellation.h:686
int filter_length
Definition MDFEchoCancellation.h:596
Software "pseudo-float": a real number represented as a signed 16-bit mantissa plus a 16-bit exponent...
Definition PseudoFloat.h:45
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
static TAllocatorExt DefaultAllocator
Definition Allocator.h:207
void * echo_fft_init(int size, FFTDriver *driver)
Initialize FFT state.
Definition MDFEchoCancellation.h:1361
uint16_t echo_uint16_t
Definition MDFEchoCancellation.h:60
int16_t echo_int16_t
Definition MDFEchoCancellation.h:59
void echo_ifft(void *table, T *in, T *out)
Perform inverse FFT.
Definition MDFEchoCancellation.h:1438
void echo_fft(void *table, T *in, T *out)
Perform forward FFT.
Definition MDFEchoCancellation.h:1395
uint32_t echo_uint32_t
Definition MDFEchoCancellation.h:62
int32_t echo_int32_t
Definition MDFEchoCancellation.h:61
void echo_fft_destroy(void *table)
Destroy FFT state and release resources.
Definition MDFEchoCancellation.h:1378
int length
Definition AudioFFT.h:55
Internal echo canceller state structure.
Definition MDFEchoCancellation.h:115
Word32 Davg1
Definition MDFEchoCancellation.h:150
Word16 * input
Definition MDFEchoCancellation.h:140
void * fft_table
Definition MDFEchoCancellation.h:168
Word16 * window
Definition MDFEchoCancellation.h:166
Word32 sum_adapt
Definition MDFEchoCancellation.h:134
int window_size
Definition MDFEchoCancellation.h:122
Word16 * x
Definition MDFEchoCancellation.h:138
int play_buf_pos
Definition MDFEchoCancellation.h:175
int K
Definition MDFEchoCancellation.h:129
Word32 * Xf
Definition MDFEchoCancellation.h:161
Word32 * power
Definition MDFEchoCancellation.h:156
Word32 * Yf
Definition MDFEchoCancellation.h:160
Word32 * PHI
Definition MDFEchoCancellation.h:145
Word16 * memE
Definition MDFEchoCancellation.h:169
Mem * notch_mem
Definition MDFEchoCancellation.h:172
Word32 * Yh
Definition MDFEchoCancellation.h:163
typename SampleType::word32_t Word32
Definition MDFEchoCancellation.h:117
Word16 leak_estimate
Definition MDFEchoCancellation.h:135
typename SampleType::word16_t Word16
Definition MDFEchoCancellation.h:116
Word16 * last_y
Definition MDFEchoCancellation.h:142
Word16 beta_max
Definition MDFEchoCancellation.h:133
Word16 beta0
Definition MDFEchoCancellation.h:132
Word32 * Rf
Definition MDFEchoCancellation.h:159
int C
Definition MDFEchoCancellation.h:128
Word16 * E
Definition MDFEchoCancellation.h:144
Word32 * W
Definition MDFEchoCancellation.h:146
int M
Definition MDFEchoCancellation.h:123
Word16 * memX
Definition MDFEchoCancellation.h:169
echo_int32_t sampling_rate
Definition MDFEchoCancellation.h:130
Word16 * Y
Definition MDFEchoCancellation.h:143
Word16 spec_average
Definition MDFEchoCancellation.h:131
Word16 * e
Definition MDFEchoCancellation.h:137
Word16 * y
Definition MDFEchoCancellation.h:141
Word32 * Eh
Definition MDFEchoCancellation.h:162
Word16 * prop
Definition MDFEchoCancellation.h:167
Word16 notch_radius
Definition MDFEchoCancellation.h:171
Word16 * wtmp
Definition MDFEchoCancellation.h:158
Word32 Mem
Definition MDFEchoCancellation.h:119
Num Pyy
Definition MDFEchoCancellation.h:165
T * play_buf
Definition MDFEchoCancellation.h:174
int adapted
Definition MDFEchoCancellation.h:125
Word16 * X
Definition MDFEchoCancellation.h:139
int saturated
Definition MDFEchoCancellation.h:126
Num Dvar1
Definition MDFEchoCancellation.h:153
int screwed_up
Definition MDFEchoCancellation.h:127
typename SampleType::float_t Num
Definition MDFEchoCancellation.h:118
int cancel_count
Definition MDFEchoCancellation.h:124
Num Dvar2
Definition MDFEchoCancellation.h:154
Num Pey
Definition MDFEchoCancellation.h:164
Word16 * memD
Definition MDFEchoCancellation.h:169
Word32 Davg2
Definition MDFEchoCancellation.h:152
int frame_size
Definition MDFEchoCancellation.h:121
Word16 preemph
Definition MDFEchoCancellation.h:170
Num * power_1
Definition MDFEchoCancellation.h:157
Word16 * foreground
Definition MDFEchoCancellation.h:148
int play_buf_started
Definition MDFEchoCancellation.h:176
And individual FFT Bin.
Definition AudioFFT.h:70
float real
Definition AudioFFT.h:71
float img
Definition AudioFFT.h:72
Selects PseudoFloat (see PseudoFloat.h) as MDFEchoCancellation's internal numeric representation,...
Definition MDFEchoCancellation.h:92
Selects float as MDFEchoCancellation's internal numeric representation for sample/spectrum arrays (wo...
Definition MDFEchoCancellation.h:72
float word32_t
Definition MDFEchoCancellation.h:74
float word16_t
Definition MDFEchoCancellation.h:73
float float_t
Definition MDFEchoCancellation.h:75
FFT state management structure.
Definition MDFEchoCancellation.h:188
int N
Definition MDFEchoCancellation.h:196
FFTDriver * driver
Definition MDFEchoCancellation.h:195
fft_state(int size, FFTDriver *drv)
Construct FFT state with specified size and driver.
Definition MDFEchoCancellation.h:203