一、概述

        本节我们对非线性波束形成算法进行工程化,运行在respeaker core v2平台上,算法实时率在0.046左右。更多资料和代码可以进入https://t.zsxq.com/qgmoN ,同时欢迎大家提出宝贵的建议,以共同探讨学习。

二、算法实现

2.1 代码结构

abf
├── CMakeLists.txt
├── bin
│   ├── CMakeLists.txt
│   └── test-api.c
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.22.1
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   ├── a.out
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       ├── a.out
│   │   │       └── tmp
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeOutput.log
│   │   ├── CMakeTmp
│   │   ├── Makefile.cmake
│   │   ├── Makefile2
│   │   ├── TargetDirectories.txt
│   │   ├── cmake.check_cache
│   │   └── progress.marks
│   ├── Makefile
│   ├── bin
│   │   ├── CMakeFiles
│   │   │   ├── CMakeDirectoryInformation.cmake
│   │   │   ├── progress.marks
│   │   │   └── test-api.dir
│   │   │       ├── DependInfo.cmake
│   │   │       ├── build.make
│   │   │       ├── cmake_clean.cmake
│   │   │       ├── compiler_depend.make
│   │   │       ├── compiler_depend.ts
│   │   │       ├── depend.make
│   │   │       ├── flags.make
│   │   │       ├── link.txt
│   │   │       ├── progress.make
│   │   │       ├── test-api.c.o
│   │   │       └── test-api.c.o.d
│   │   ├── Makefile
│   │   ├── cmake_install.cmake
│   │   └── test-api
│   ├── cmake_install.cmake
│   └── src
│       ├── CMakeFiles
│       │   ├── CMakeDirectoryInformation.cmake
│       │   ├── mouse-abf.dir
│       │   │   ├── DependInfo.cmake
│       │   │   ├── bessel0.c.o
│       │   │   ├── bessel0.c.o.d
│       │   │   ├── bf-api.c.o
│       │   │   ├── bf-api.c.o.d
│       │   │   ├── bf-blocker.c.o
│       │   │   ├── bf-blocker.c.o.d
│       │   │   ├── bf-complex.c.o
│       │   │   ├── bf-complex.c.o.d
│       │   │   ├── bf-fft.c.o
│       │   │   ├── bf-fft.c.o.d
│       │   │   ├── bf-matrix.c.o
│       │   │   ├── bf-matrix.c.o.d
│       │   │   ├── bf-nonlinear.c.o
│       │   │   ├── bf-nonlinear.c.o.d
│       │   │   ├── build.make
│       │   │   ├── cmake_clean.cmake
│       │   │   ├── cmake_clean_target.cmake
│       │   │   ├── compiler_depend.make
│       │   │   ├── compiler_depend.ts
│       │   │   ├── depend.make
│       │   │   ├── fft4g.c.o
│       │   │   ├── fft4g.c.o.d
│       │   │   ├── flags.make
│       │   │   ├── link.txt
│       │   │   ├── progress.make
│       │   │   ├── ring_buffer.c.o
│       │   │   └── ring_buffer.c.o.d
│       │   └── progress.marks
│       ├── Makefile
│       ├── cmake_install.cmake
│       └── libmouse-abf.a
├── src
│   ├── CMakeLists.txt
│   ├── bessel0.c
│   ├── bessel0.h
│   ├── bf-api.c
│   ├── bf-api.h
│   ├── bf-blocker.c
│   ├── bf-blocker.h
│   ├── bf-complex.c
│   ├── bf-complex.h
│   ├── bf-fft.c
│   ├── bf-fft.h
│   ├── bf-matrix.c
│   ├── bf-matrix.h
│   ├── bf-nonlinear.c
│   ├── bf-nonlinear.h
│   ├── bf-types.h
│   ├── fft4g.c
│   ├── fft4g.h
│   ├── ring_buffer.c
│   └── ring_buffer.h
└── test
    ├── simulate_role1_0_t60_0.2_role2_180_t60_0.2.pcm
    ├── simulate_role1_0_t60_0.2_role2_180_t60_0.2.wav
    ├── simulate_role1_0_t60_0.2_role2_180_t60_0.2_nonlinear_bf_0du.pcm
    └── simulate_role1_0_t60_0.2_role2_180_t60_0.2_nonlinear_bf_180du.pcm

2.2 核心代码实现

(1)、src/bf-api.h

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-api.h
 * */
#ifndef __BF_API_H__
#define __BF_API_H__
#ifdef __cplusplus
extern "C" 
{
#endif


void *api_create_bf_handle(const char *mic_positions, int mic_num, float angle);
void api_destroy_bf_handle(void *handle);

int api_start_bf(void *handle);
int api_process_bf(void *handle, float *input_data, float *output_data, int chunk_size);
int api_end_bf(void *handle);


#ifdef __cplusplus
}
#endif

#endif

(2)、src/bf-api.c

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-api.c
 * */
#include "bf-api.h"
#include "bf-blocker.h"
#include "bf-fft.h"
#include "bf-types.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>


static int MouseBF_GetPoint(const char *in, MouseBF_point *array_geometry) {
  char input[128];
  strcpy(input, in);
  int mic_num = 0;
  int mic_idx = 0;
  int tt = 0;
  float value = 0;
  char *p = strtok(input, " ");
  if (p) {
    //printf("%s\n", p);
    value = atof(p);
    mic_num = tt / 3;
    mic_idx = tt % 3;
    array_geometry[mic_num].c[mic_idx] = value;
    tt++;
  }
  while (p = strtok(NULL, " ")) {
    //printf("%s\n", p);
    value = atof(p);
    mic_num = tt / 3;
    mic_idx = tt % 3;
    array_geometry[mic_num].c[mic_idx] = value;
    tt++;
  }
  //printf("mic_num:%d\n", mic_num + 1);
  return mic_num + 1;
}

static void MouseBF_KaiserBesselDerived(float alpha, size_t length, float* window) {
  const size_t half = (length + 1) / 2;
  float sum = 0.0f;

  for (size_t i = 0; i <= half; ++i) {
    MouseBF_complex_f r, t;
    r.real_ = (4.0f * i) / length - 1.0f;
    r.imag_ = 0.0;
    t.real_ = M_PI * alpha * sqrt(1.0f - r.real_ * r.real_);
    t.imag_ = 0.0;
    sum += MouseBF_I0(t).real_;
    window[i] = sum;
  }
  for (size_t i = length - 1; i >= half; --i) {
    window[length - i - 1] = sqrtf(window[length - i - 1] / sum);
    window[i] = window[length - i - 1];
  }
  if (length % 2 == 1) {
    window[half - 1] = sqrtf(window[half - 1] / sum);
  }
}
static float MouseBF_FloatS16ToFloat(float v) {
  static const float kMaxInt16Inverse = 1.0 / 32767;
  static const float kMinInt16Inverse = 1.0 / -32768;
  return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse);
}

static float MouseBF_FloatToFloatS16(float v) {
  return v * (v > 0 ? 32767.0 : 32768.0);
}
typedef struct MouseBF_BeamformerHandle_ {
  MouseBF_blocker *blocker_;
  float **in_buf_;
  float *out_buf_;
  int mic_num_;
  MouseBF_nonlinear *bf_inst_;
} MouseBF_BeamformerHandle;

void *api_create_bf_handle(const char *mic_positions, int mic_num, float angle) {

  MouseBF_point array_geometry[20];
  int mic_num_ = MouseBF_GetPoint(mic_positions, array_geometry);
  assert(mic_num_ == mic_num);

  MouseBF_spherical_pointf target_direction;
  target_direction.s[0] = angle * M_PI / 180.0;
  target_direction.s[1] = 0;
  target_direction.s[2] = 1;

  MouseBF_nonlinear *bf_inst = MouseBF_nonlinear_instance(kSampleRate,
                                                array_geometry,
                                                mic_num,
                                                target_direction);


  float *window = (float*)malloc(sizeof(float)*256);
  MouseBF_KaiserBesselDerived(1.5f, 256, window);
  MouseBF_blocker *blocker = MouseBF_blocker_instance(160,
                                              256,
                                              mic_num,
                                              1,
                                              window,
                                              128,
                                              MouseBF_nonlinear_process_block,
                                              bf_inst);
  free(window);
  MouseBF_BeamformerHandle *handle = (MouseBF_BeamformerHandle*)malloc(sizeof(MouseBF_BeamformerHandle));
  handle->blocker_ = blocker;
  handle->bf_inst_ = bf_inst;
  handle->in_buf_ = (float**)malloc(sizeof(float*) * mic_num);
  handle->mic_num_ = mic_num;
  for (int i = 0; i < mic_num; i++) {
    handle->in_buf_[i] = (float*)malloc(sizeof(float) * 160);
  }
  handle->out_buf_ = (float*)malloc(sizeof(float) * 160);
  return (void*)handle;
}

void api_destroy_bf_handle(void *handle) {
  MouseBF_BeamformerHandle *handle_ = (MouseBF_BeamformerHandle *)handle;
  MouseBF_nonlinear_destroy(handle_->bf_inst_);
  MouseBF_blocker_destroy(handle_->blocker_);
  for (int i = 0; i < handle_->mic_num_; i++) {
    free(handle_->in_buf_[i]);
  }
  free(handle_->in_buf_);
  free(handle_->out_buf_);
  free(handle_);
}

int api_start_bf(void *handle) {
  return 0;
}

int api_process_bf(void *handle, float *input_data, float *output_data, int chunk_size) {
  MouseBF_BeamformerHandle *handle_ = (MouseBF_BeamformerHandle *)handle;
  for (int j = 0; j < chunk_size; j++) {
    for (int i = 0; i < handle_->mic_num_; i++) {
      handle_->in_buf_[i][j] = MouseBF_FloatS16ToFloat(input_data[j*handle_->mic_num_ + i]);
    }
  }
  MouseBF_blocker_process_chunk(handle_->blocker_,
                                  handle_->in_buf_,
                                  chunk_size,
                                  handle_->mic_num_,
                                  1,
                                  &(handle_->out_buf_));
  for (int i = 0; i < chunk_size; i++) {
    output_data[i] = MouseBF_FloatToFloatS16(handle_->out_buf_[i]);
  }
  return 0;
}
int api_end_bf(void *handle) {
  //MouseBF_BeamformerHandle *handle_ = (MouseBF_BeamformerHandle *)handle;
  return 0;
}

(3)、src/bf-blocker.h

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-blocker.h
 * */


#ifndef __BF_BLOCKER_H__
#define __BF_BLOCKER_H__
#include <stddef.h>
#include "ring_buffer.h"
#include "bf-fft.h"

#include "bf-nonlinear.h"
typedef struct MouseBF_blocker_ {
  size_t chunk_size_;
  size_t block_size_;
  int num_input_channels_;
  int num_output_channels_;
  
  // The number of frames of delay to add at the beginning of the first chunk.
  size_t initial_delay_;
  size_t frame_offset_;

  // Both contain |initial delay| + |chunk_size| frames. The input is a fairly
  // standard FIFO, but due to the overlap-add it's harder to use an
  // AudioRingBuffer for the output.
  RingBuffer **input_buffer_;
  float **output_buffer_;

  // Space for the input block (can't wrap because of windowing).
  float **input_block_;

  // Space for the output block (can't wrap because of overlap/add).
  float **output_block_;

  float *window_;

  // The amount of frames between the start of contiguous blocks. For example,
  // |shift_amount_| = |block_size_| / 2 for a Hann window.
  size_t shift_amount_;

  MouseBF_fft *fft_inst_;
  MouseBF_complex_f **fft_input_block_;
  MouseBF_complex_f **fft_output_block_;

  //bf
  MouseBF_nonlinear *bf_inst_;

  //callback
  MouseBF_Callback callback_;
} MouseBF_blocker;

// create a blocker instance.
MouseBF_blocker *MouseBF_blocker_instance(size_t chunk_size,
                                              size_t block_size,
                                              int num_input_channels,
                                              int num_output_channels,
                                              const float* window,
                                              size_t shift_amount,
                                              MouseBF_Callback callback,
                                              MouseBF_nonlinear *bf_inst
                                              );

// destroy a blocker.
void MouseBF_blocker_destroy(MouseBF_blocker *inst);

//process a chunk data.
int MouseBF_blocker_process_chunk(MouseBF_blocker *inst,
                                    float** input,
                                    size_t chunk_size,
                                    int num_input_channels,
                                    int num_output_channels,
                                    float** output);

#endif

(4)、src/bf-blocker.c

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-blocker.c
 * */

#include "bf-blocker.h"
#include "bf-nonlinear.h"

#include <string.h>
#include <stdlib.h>
#include <assert.h>

// Adds |a| and |b| frame by frame into |result| (basically matrix addition).
static void MouseBF_AddFrames(float** a,
               size_t a_start_index,
               float** b,
               int b_start_index,
               size_t num_frames,
               int num_channels,
               float** result,
               size_t result_start_index) {
  for (int i = 0; i < num_channels; ++i) {
    for (size_t j = 0; j < num_frames; ++j) {
      result[i][j + result_start_index] =
          a[i][j + a_start_index] + b[i][j + b_start_index];
    }
  }
}

// Copies |src| into |dst| channel by channel.
static void MouseBF_CopyFrames(float** src,
                size_t src_start_index,
                size_t num_frames,
                int num_channels,
                float** dst,
                size_t dst_start_index) {
  for (int i = 0; i < num_channels; ++i) {
    memcpy(&dst[i][dst_start_index],
           &src[i][src_start_index],
           num_frames * sizeof(dst[i][dst_start_index]));
  }
}

// Moves |src| into |dst| channel by channel.
static void MouseBF_MoveFrames(float** src,
                size_t src_start_index,
                size_t num_frames,
                int num_channels,
                float** dst,
                size_t dst_start_index) {
  for (int i = 0; i < num_channels; ++i) {
    memmove(&dst[i][dst_start_index],
            &src[i][src_start_index],
            num_frames * sizeof(dst[i][dst_start_index]));
  }
}

static void MouseBF_ZeroOut(float** buffer,
             size_t starting_idx,
             size_t num_frames,
             int num_channels) {
  for (int i = 0; i < num_channels; ++i) {
    memset(&buffer[i][starting_idx], 0,
           num_frames * sizeof(buffer[i][starting_idx]));
  }
}

// Pointwise multiplies each channel of |frames| with |window|. Results are
// stored in |frames|.
static void MouseBF_ApplyWindow(const float* window,
                 size_t num_frames,
                 int num_channels,
                 float* const* frames) {
  for (int i = 0; i < num_channels; ++i) {
    for (size_t j = 0; j < num_frames; ++j) {
      frames[i][j] = frames[i][j] * window[j];
    }
  }
}

static size_t MouseBF_gcd(size_t a, size_t b) {
  size_t tmp;
  while (b) {
     tmp = a;
     a = b;
     b = tmp % b;
  }
  return a;
}

MouseBF_blocker *MouseBF_blocker_instance(size_t chunk_size,
                                              size_t block_size,
                                              int num_input_channels,
                                              int num_output_channels,
                                              const float* window,
                                              size_t shift_amount,
                                              MouseBF_Callback callback,
                                              MouseBF_nonlinear *bf_inst
                                              ) {
  MouseBF_blocker *inst = (MouseBF_blocker*)malloc(sizeof(MouseBF_blocker));
  inst->chunk_size_ = chunk_size;
  inst->block_size_ = block_size;
  inst->num_input_channels_ = num_input_channels;
  inst->num_output_channels_ = num_output_channels;
  inst->initial_delay_ = block_size - MouseBF_gcd(chunk_size, shift_amount);
  inst->frame_offset_ = 0;
  inst->input_buffer_ = (RingBuffer**)malloc(sizeof(RingBuffer*) * num_input_channels);
  for (int i = 0; i < num_input_channels; i++) {
    inst->input_buffer_[i] = WebRtc_CreateBuffer(inst->chunk_size_ + inst->initial_delay_, sizeof(float));
  }
  inst->output_buffer_ = (float**)malloc(sizeof(float*) * num_output_channels);
  for (int i = 0; i < num_output_channels; i++) {
    inst->output_buffer_[i] = (float*)malloc(sizeof(float) * (inst->chunk_size_ + inst->initial_delay_));
    memset(inst->output_buffer_[i], 0, sizeof(float) * (inst->chunk_size_ + inst->initial_delay_));
  }

  inst->input_block_ = (float**)malloc(sizeof(float*) * num_input_channels);
  for (int i = 0; i < num_input_channels; i++) {
    inst->input_block_[i] = (float*)malloc(sizeof(float) * block_size);
  }
  inst->output_block_ = (float**)malloc(sizeof(float*) * num_output_channels);
  for (int i = 0; i < num_output_channels; i++) {
    inst->output_block_[i] = (float*)malloc(sizeof(float) * block_size);
  }
  inst->window_ = (float*)malloc(sizeof(float) * block_size);
  inst->shift_amount_ = shift_amount;

  memcpy(inst->window_, window, block_size * sizeof(float));
  for (int i = 0; i < num_input_channels; i++) {
    int moved = WebRtc_MoveReadPtr(inst->input_buffer_[i], -inst->initial_delay_);
    assert(moved == -inst->initial_delay_);
  }

  inst->fft_inst_ = MouseBF_fft_instance(inst->block_size_);
  inst->fft_input_block_ = (MouseBF_complex_f**)malloc(sizeof(MouseBF_complex_f*) * num_input_channels);
  for (int i = 0; i < num_input_channels; i++) {
    inst->fft_input_block_[i] = (MouseBF_complex_f*)malloc(sizeof(MouseBF_complex_f) * inst->fft_inst_->complex_length_);
  }
  inst->fft_output_block_ = (MouseBF_complex_f**)malloc(sizeof(MouseBF_complex_f*) * num_output_channels);
  for (int i = 0; i < num_output_channels; i++) {
    inst->fft_output_block_[i] = (MouseBF_complex_f*)malloc(sizeof(MouseBF_complex_f) * inst->fft_inst_->complex_length_);
  }

  inst->callback_ = callback;
  inst->bf_inst_  = bf_inst;
  return inst;
}

// destroy a blocker.
void MouseBF_blocker_destroy(MouseBF_blocker *inst) {
  for (int i = 0; i < inst->num_input_channels_; i++) {
    WebRtc_FreeBuffer(inst->input_buffer_[i]);
  }
  free(inst->input_buffer_);
  for (int i = 0; i < inst->num_output_channels_; i++) {
    free(inst->output_buffer_[i]);
  }
  free(inst->output_buffer_);
  for (int i = 0; i < inst->num_input_channels_; i++) {
    free(inst->input_block_[i]);
  }
  free(inst->input_block_);
  for (int i = 0; i < inst->num_output_channels_; i++) {
    free(inst->output_block_[i]);
  }
  free(inst->output_block_);
  free(inst->window_);
  MouseBF_fft_destroy(inst->fft_inst_);

  for (int i = 0; i < inst->num_input_channels_; i++) {
    free(inst->fft_input_block_[i]);
  }
  free(inst->fft_input_block_);
  for (int i = 0; i < inst->num_output_channels_; i++) {
    free(inst->fft_output_block_[i]);
  }
  free(inst->fft_output_block_);
  free(inst);
}

//process a chunk data.
int MouseBF_blocker_process_chunk(MouseBF_blocker *inst,
                                    float** input,
                                    size_t chunk_size,
                                    int num_input_channels,
                                    int num_output_channels,
                                    float** output) {
  for (size_t i = 0; i < num_input_channels; ++i) {
    WebRtc_WriteBuffer(inst->input_buffer_[i], input[i], chunk_size);
  }
  size_t first_frame_in_block = inst->frame_offset_;

  // Loop through blocks.
  while (first_frame_in_block < inst->chunk_size_) {
    for (int i = 0; i < inst->num_input_channels_; i++) {
      WebRtc_ReadBuffer(inst->input_buffer_[i], NULL, inst->input_block_[i], inst->block_size_);
      WebRtc_MoveReadPtr(inst->input_buffer_[i], -(inst->block_size_ - inst->shift_amount_));
    }
    MouseBF_ApplyWindow(inst->window_, inst->block_size_, inst->num_input_channels_, inst->input_block_);
    for (int i = 0; i < inst->num_input_channels_; i++) {
      MouseBF_fft_forward(inst->fft_inst_, inst->input_block_[i], inst->fft_input_block_[i]);
    }
    inst->callback_(inst->bf_inst_, inst->fft_input_block_, 
                    inst->num_input_channels_, kNumFreqBins, 
                    inst->num_output_channels_, 
                    inst->fft_output_block_);
    for (int i = 0; i < inst->num_output_channels_; i++) {
      MouseBF_fft_invert(inst->fft_inst_, inst->fft_output_block_[i], inst->output_block_[i]);
    }
    MouseBF_ApplyWindow(inst->window_, inst->block_size_, inst->num_output_channels_, inst->output_block_);
    MouseBF_AddFrames(inst->output_buffer_, first_frame_in_block, inst->output_block_, 0, inst->block_size_,
                        inst->num_output_channels_, inst->output_buffer_, first_frame_in_block);
    first_frame_in_block += inst->shift_amount_;
  }
  // Copy output buffer to output
  MouseBF_CopyFrames(inst->output_buffer_, 0, inst->chunk_size_, inst->num_output_channels_, output, 0);

  // Copy output buffer [chunk_size_, chunk_size_ + initial_delay]
  // to output buffer [0, initial_delay], zero the rest.
  MouseBF_MoveFrames(inst->output_buffer_, chunk_size, inst->initial_delay_, inst->num_output_channels_, 
                       inst->output_buffer_, 0);
  MouseBF_ZeroOut(inst->output_buffer_, inst->initial_delay_, inst->chunk_size_, inst->num_output_channels_);

  // Calculate new starting frames.
  inst->frame_offset_ = first_frame_in_block - inst->chunk_size_;
  return 0;
}

(5)、src/bf-complex.h

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-complex.h
 * */

#ifndef __BF_COMPLEX_H__
#define __BF_COMPLEX_H__
typedef struct MouseBF_complex_f_ {
  float real_;
  float imag_;
} MouseBF_complex_f;

MouseBF_complex_f MouseBF_complex_mul(MouseBF_complex_f a, MouseBF_complex_f b);

MouseBF_complex_f MouseBF_complex_add(MouseBF_complex_f a, MouseBF_complex_f b);

MouseBF_complex_f MouseBF_complex_conj(MouseBF_complex_f a);
#endif

(6)、src/bf-complex.c

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-complex.c
 * */

#include "bf-complex.h"

MouseBF_complex_f MouseBF_complex_mul(MouseBF_complex_f a, MouseBF_complex_f b) {
  MouseBF_complex_f c;
  c.real_ = a.real_ * b.real_ - a.imag_ * b.imag_;
  c.imag_ = a.real_ * b.imag_ + a.imag_ * b.real_;
  return c;
}

MouseBF_complex_f MouseBF_complex_add(MouseBF_complex_f a, MouseBF_complex_f b) {
  MouseBF_complex_f c;
  c.real_ = a.real_ + b.real_;
  c.imag_ = a.imag_ + b.imag_;
  return c;
}

MouseBF_complex_f MouseBF_complex_conj(MouseBF_complex_f a) {
  MouseBF_complex_f ret;
  ret.real_ = a.real_;
  ret.imag_ = -1 * a.imag_;
  return ret;
}

(7)、src/bf-fft.h

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-fft.h
 * */

#ifndef __BF_FFT_H__
#define __BF_FFT_H__
#include <stddef.h>
#include "bf-complex.h"
typedef struct MouseBF_fft_ {
  size_t *work_ip_;
  float *work_w_;
  int order_;
  size_t length_;
  size_t complex_length_;
} MouseBF_fft;

// Modified Bessel function of order 0 for complex inputs.
MouseBF_complex_f MouseBF_I0(MouseBF_complex_f x);

//input: len is fft len
//return: fft handle
MouseBF_fft *MouseBF_fft_instance(int len);

//destroy
void MouseBF_fft_destroy(MouseBF_fft *inst);

//fft transform
int MouseBF_fft_forward(MouseBF_fft *inst, float *src, MouseBF_complex_f *des);

//fft invert
int MouseBF_fft_invert(MouseBF_fft *inst, MouseBF_complex_f *src, float *des);

#endif

(8)、src/bf-fft.c

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-fft.c
 * */

#include "bf-fft.h"
#include "fft4g.h"
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#ifndef int16_t
#define int16_t short
#endif
static void MouseBF_Conjugate(MouseBF_complex_f* array, size_t complex_length) {
  for (int i = 0; i < complex_length; i++) {
    array[i].imag_ *= -1;
  }
}

static size_t MouseBF_ComputeWorkIpSize(size_t fft_length) {
  float len = fft_length;
  size_t ret = 2 + ceil(sqrt(len));
  return ret;
}

static int16_t MouseBF_WebRtcSpl_GetSizeInBits(uint32_t n) {
  int16_t bits;

  if (0xFFFF0000 & n) {
    bits = 16;
  } else {
    bits = 0;
  }
  if (0x0000FF00 & (n >> bits)) bits += 8;
  if (0x000000F0 & (n >> bits)) bits += 4;
  if (0x0000000C & (n >> bits)) bits += 2;
  if (0x00000002 & (n >> bits)) bits += 1;
  if (0x00000001 & (n >> bits)) bits += 1;

  return bits;
}

static size_t MouseBF_FftLength(int order) {
  size_t ret = 1 << order;
  return ret;
}

static size_t MouseBF_ComplexLength(int order) {
  size_t ret = MouseBF_FftLength(order) / 2 + 1;
  return ret;
}

MouseBF_fft *MouseBF_fft_instance(int len) {
  MouseBF_fft *inst = (MouseBF_fft*)malloc(sizeof(MouseBF_fft));

  inst->order_ = MouseBF_WebRtcSpl_GetSizeInBits(len - 1);
  inst->length_ = MouseBF_FftLength(inst->order_);
  inst->complex_length_ = MouseBF_ComplexLength(inst->order_);

  size_t ip_len = MouseBF_ComputeWorkIpSize(len);
  inst->work_ip_ = (size_t*)malloc(sizeof(size_t) * ip_len);
  inst->work_ip_[0] = 0;

  inst->work_w_ = (float*)malloc(sizeof(float) * inst->complex_length_);
  return inst;
}

void MouseBF_fft_destroy(MouseBF_fft *inst) {
  free(inst->work_ip_);
  free(inst->work_w_);
  free(inst);
}

int MouseBF_fft_forward(MouseBF_fft *inst, float *src, MouseBF_complex_f *des) {
  float *des_float = (float*)des;
  memcpy(des_float, src, sizeof(float) * inst->length_);

  WebRtc_rdft(inst->length_, 1, des_float, inst->work_ip_, inst->work_w_);

  des[inst->complex_length_ - 1].real_ = des[0].imag_;
  des[inst->complex_length_ - 1].imag_ = 0.0;

  des[0].imag_ = 0.0;
  MouseBF_Conjugate(des, inst->complex_length_);
  return 0;
}

int MouseBF_fft_invert(MouseBF_fft *inst, MouseBF_complex_f *src, float *des) {
  float *src_float = (float*)src;
  MouseBF_complex_f *des_complex = (MouseBF_complex_f*)des;
  memcpy(des, src_float, sizeof(float) * inst->length_);
  MouseBF_Conjugate(des_complex, inst->complex_length_-1);
  des_complex[0].imag_ = src[inst->complex_length_-1].real_;

  WebRtc_rdft(inst->length_, -1, des, inst->work_ip_, inst->work_w_);
  for (int i = 0; i < inst->length_; i++) {
    des[i] *= 2.0/inst->length_;
  }
  return 0;
}

// Modified Bessel function of order 0 for complex inputs.
MouseBF_complex_f MouseBF_I0(MouseBF_complex_f x) {
  MouseBF_complex_f a, y, c;
  a.real_ = 1 / 3.75;
  a.imag_ = 0.0;
  y = MouseBF_complex_mul(x, a);
  y = MouseBF_complex_mul(y, y);
  a.real_ = 0.45813e-2;
  a.imag_ = 0.0;
  c = MouseBF_complex_mul(y, a);
  a.real_ = 0.360768e-1;
  a.imag_ = 0.0;
  c = MouseBF_complex_add(c, a);
  c = MouseBF_complex_mul(y, c);
  a.real_ = 0.2659732;
  a.imag_ = 0.0;
  c = MouseBF_complex_add(c, a);
  c = MouseBF_complex_mul(y, c);
  a.real_ = 1.2067492;
  a.imag_ = 0.0;
  c = MouseBF_complex_add(c, a);
  c = MouseBF_complex_mul(y, c);
  a.real_ = 3.0899424;
  a.imag_ = 0.0;
  c = MouseBF_complex_add(c, a);
  c = MouseBF_complex_mul(y, c);
  a.real_ = 3.5156229;
  a.imag_ = 0.0;
  c = MouseBF_complex_add(c, a);
  c = MouseBF_complex_mul(y, c);
  a.real_ = 1.0;
  a.imag_ = 0.0;
  c = MouseBF_complex_add(c, a);
  return c;
}

(9)、src/bf-matrix.h

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-matrix.h
 * */

#ifndef __BF_MATRIX_H__
#define __BF_MATRIX_H__
#include "bf-complex.h"
#include <stddef.h>

typedef struct MouseBF_complex_matrix_f_ {
    size_t num_rows_;
    size_t num_cols_;
    MouseBF_complex_f* data_;
    MouseBF_complex_f** elements_;
} MouseBF_complex_matrix_f;

void MouseBF_complex_matrix_init(MouseBF_complex_matrix_f *mat, size_t row, size_t col);

void MouseBF_complex_matrix_free(MouseBF_complex_matrix_f *mat);

void MouseBF_complex_matrix_reset(MouseBF_complex_matrix_f *mat, size_t row, size_t col);

void MouseBF_complex_matrix_copy_from_column(MouseBF_complex_matrix_f *mat, size_t column_index, MouseBF_complex_f **src);

void MouseBF_complex_matrix_copy(MouseBF_complex_matrix_f *mat, MouseBF_complex_matrix_f *src);

float MouseBF_SumAbs(MouseBF_complex_matrix_f *mat);

float MouseBF_SumSquares(MouseBF_complex_matrix_f *mat);

MouseBF_complex_f MouseBF_ConjugateDotProduct(MouseBF_complex_matrix_f *lhs, MouseBF_complex_matrix_f *rhs);

float MouseBF_Norm(MouseBF_complex_matrix_f *mat, MouseBF_complex_matrix_f *norm_mat);

void MouseBF_complex_matrix_scale(MouseBF_complex_matrix_f *mat, MouseBF_complex_f scale);

void MouseBF_TransposedConjugatedProduct(MouseBF_complex_matrix_f *in, MouseBF_complex_matrix_f *out);

MouseBF_complex_f MouseBF_Trace(MouseBF_complex_matrix_f *mat);

void MouseBF_Transpose(MouseBF_complex_matrix_f *in, MouseBF_complex_matrix_f *out);

void MouseBF_PointwiseConjugate(MouseBF_complex_matrix_f *mat);

void MouseBF_ComplexMatrixMultiply(MouseBF_complex_matrix_f *interf_cov_vector_transposed, MouseBF_complex_matrix_f *interf_cov_vector, MouseBF_complex_matrix_f *mat);

void MouseBF_ComplexMatrixAdd(MouseBF_complex_matrix_f *src1, MouseBF_complex_matrix_f *src2, MouseBF_complex_matrix_f *mat);
#endif

(10)、src/bf-matrix.c

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-matrix.c
 * */

#include "bf-matrix.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

void MouseBF_complex_matrix_init(MouseBF_complex_matrix_f *mat, size_t row, size_t col) {
  mat->num_rows_ = row;
  mat->num_cols_ = col;
  mat->data_ = (MouseBF_complex_f*)malloc(row * col * sizeof(MouseBF_complex_f));
  memset(mat->data_, 0, row * col * sizeof(MouseBF_complex_f));
  mat->elements_ = (MouseBF_complex_f**)malloc(row * sizeof(MouseBF_complex_f*));
  for(int i = 0; i < row; i++) {
    mat->elements_[i] = &mat->data_[i * col];
  }
}

void MouseBF_complex_matrix_free(MouseBF_complex_matrix_f *mat) {
  if (mat->data_) {
    free(mat->data_);
    free(mat->elements_);
  }
}
void MouseBF_complex_matrix_reset(MouseBF_complex_matrix_f *mat, size_t row, size_t col) {
  if (mat->data_ != NULL) {
    free(mat->data_);
    free(mat->elements_);
  }
  MouseBF_complex_matrix_init(mat, row, col);
}

void MouseBF_complex_matrix_copy_from_column(MouseBF_complex_matrix_f *mat, size_t column_index, MouseBF_complex_f **src) {
  for (int i = 0; i < mat->num_cols_; ++i) {
    mat->data_[i] = src[i][column_index];
  }
}

void MouseBF_complex_matrix_copy(MouseBF_complex_matrix_f *mat, MouseBF_complex_matrix_f *src) {
  MouseBF_complex_matrix_reset(mat, src->num_rows_, src->num_cols_);
  for (int i = 0; i < mat->num_cols_* mat->num_rows_; ++i) {
    mat->data_[i] = src->data_[i];
  }
}

float MouseBF_SumAbs(MouseBF_complex_matrix_f *mat) {
  float sum_abs = 0.0;
  MouseBF_complex_f **mat_els = mat->elements_;
  for (int i = 0; i < mat->num_rows_; ++i) {
    for (int j = 0; j < mat->num_cols_; ++j) {
      sum_abs += sqrt(mat_els[i][j].real_ * mat_els[i][j].real_ + mat_els[i][j].imag_ * mat_els[i][j].imag_);
    }
  }
  return sum_abs;
}

float MouseBF_SumSquares(MouseBF_complex_matrix_f *mat) {
  float sum_squares = 0.f;
  for (int i = 0; i < mat->num_rows_ * mat->num_cols_; i++) {
    sum_squares += mat->data_[i].real_ * mat->data_[i].real_ + mat->data_[i].imag_ * mat->data_[i].imag_;
  }
  return sum_squares;
}

MouseBF_complex_f MouseBF_ConjugateDotProduct(MouseBF_complex_matrix_f *lhs, MouseBF_complex_matrix_f *rhs) {
  assert(lhs->num_rows_ == 1);
  assert(rhs->num_rows_ == 1);
  assert(rhs->num_cols_ == lhs->num_cols_);
  MouseBF_complex_f result;
  result.real_ = 0.0;
  result.imag_ = 0.0;
  MouseBF_complex_f **lhs_elements = lhs->elements_;
  MouseBF_complex_f **rhs_elements = rhs->elements_;

  for (int i = 0; i < lhs->num_cols_; i++) {
    MouseBF_complex_f tmp = MouseBF_complex_mul(MouseBF_complex_conj(lhs_elements[0][i]), rhs_elements[0][i]);
    result = MouseBF_complex_add(result, tmp);
  }
  return result;
}

float MouseBF_Norm(MouseBF_complex_matrix_f *mat, MouseBF_complex_matrix_f *norm_mat) {
  MouseBF_complex_f first_product, second_product;
  first_product.real_ = 0.0;
  first_product.imag_ = 0.0;
  second_product.real_ = 0.0;
  second_product.imag_ = 0.0;

  MouseBF_complex_f **mat_els = mat->elements_;
  MouseBF_complex_f **norm_mat_els = norm_mat->elements_;

  for (int i = 0; i < norm_mat->num_cols_; i++) {
    for (int j = 0; j < norm_mat->num_cols_; j++) {
      first_product = MouseBF_complex_add(first_product, MouseBF_complex_mul(MouseBF_complex_conj(norm_mat_els[0][j]), mat_els[j][i]));
    }
    second_product = MouseBF_complex_add(second_product, MouseBF_complex_mul(first_product, norm_mat_els[0][i]));
    first_product.real_ = 0.0;
    first_product.imag_ = 0.0;
  }
  float ret = second_product.real_;
  if (ret < 0.f) {
    ret = 0.f;
  }
  return ret;
}

void MouseBF_complex_matrix_scale(MouseBF_complex_matrix_f *mat, MouseBF_complex_f scale) {
  int size = mat->num_cols_ * mat->num_rows_;
  for (int i = 0; i < size; i++) {
    mat->data_[i] = MouseBF_complex_mul(mat->data_[i], scale);
  }
}

void MouseBF_TransposedConjugatedProduct(MouseBF_complex_matrix_f *in, MouseBF_complex_matrix_f *out) {
  assert(in->num_rows_ == 1);
  assert(out->num_rows_ == in->num_cols_);
  assert(out->num_cols_ == in->num_cols_);
  MouseBF_complex_f* in_elements = in->elements_[0];
  MouseBF_complex_f** out_elements = out->elements_;
  for (int i = 0; i < out->num_rows_; ++i) {
    for (int j = 0; j < out->num_cols_; ++j) {
      out_elements[i][j] = MouseBF_complex_mul(in_elements[i], MouseBF_complex_conj(in_elements[j]));
    }
  }
}

MouseBF_complex_f MouseBF_Trace(MouseBF_complex_matrix_f *mat) {
  assert(mat->num_rows_ == mat->num_cols_);
  MouseBF_complex_f trace;
  trace.real_ = 0.0;
  trace.imag_ = 0.0;
  
  for (int i = 0; i < mat->num_rows_; ++i) {
    trace = MouseBF_complex_add(trace, mat->elements_[i][i]);
  }
  return trace;
}

void MouseBF_Transpose(MouseBF_complex_matrix_f *in, MouseBF_complex_matrix_f *out) {
  for (int i = 0; i < out->num_rows_; ++i) {
    for (int j = 0; j < out->num_cols_; ++j) {
      out->elements_[i][j] = in->elements_[j][i];
    }
  }
}

void MouseBF_PointwiseConjugate(MouseBF_complex_matrix_f *mat) {
  for (int i = 0; i < mat->num_rows_; ++i) {
    for (int j = 0; j < mat->num_cols_; ++j) {
      mat->elements_[i][j].imag_ *= -1;
    }
  }
}

void MouseBF_ComplexMatrixMultiply(MouseBF_complex_matrix_f *interf_cov_vector_transposed, MouseBF_complex_matrix_f *interf_cov_vector, MouseBF_complex_matrix_f *mat) {
  for (int row = 0; row < interf_cov_vector_transposed->num_rows_; ++row) {
    for (int col = 0; col < interf_cov_vector->num_cols_; ++col) {
      MouseBF_complex_f cur_element;
      cur_element.real_ = 0.0;
      cur_element.imag_ = 0.0;
      for (int i = 0; i < interf_cov_vector->num_rows_; ++i) {
        cur_element = MouseBF_complex_add(cur_element, MouseBF_complex_mul(interf_cov_vector_transposed->elements_[row][i], interf_cov_vector->elements_[i][col]));
      }
      mat->elements_[row][col] = cur_element;
    }
  }
}

void MouseBF_ComplexMatrixAdd(MouseBF_complex_matrix_f *src1, MouseBF_complex_matrix_f *src2, MouseBF_complex_matrix_f *mat) {
  for (size_t i = 0; i < src1->num_cols_ * src1->num_rows_; ++i) {
    mat->data_[i] = MouseBF_complex_add(src1->data_[i], src2->data_[i]);
  }
}

(11)、src/bf-nonlinear.h

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-nonlinear.h
 * */

#ifndef __BF_NONLINEAR_H__
#define __BF_NONLINEAR_H__
#include "bf-types.h"
#include "bf-matrix.h"
#include "bf-complex.h"
#include <stddef.h>

typedef struct MouseBF_nonlinear_ {
  int sample_rate_hz_;
  int num_input_channels_;
  MouseBF_point *array_geometry_;
  // The normal direction of the array if it has one and it is in the xy-plane.
  MouseBF_point *array_normal_;
  // Minimum spacing between microphone pairs.
  float min_mic_spacing_;

  // Calculated based on user-input and constants
  size_t low_mean_start_bin_;
  size_t low_mean_end_bin_;
  size_t high_mean_start_bin_;
  size_t high_mean_end_bin_;
  // Quickly varying mask updated every block.
  float new_mask_[kNumFreqBins];
  // Time smoothed mask.
  float time_smooth_mask_[kNumFreqBins];
  // Time and frequency smoothed mask.
  float final_mask_[kNumFreqBins];
  
  float target_angle_radians_;
  // Angles of the interferer scenarios.
  float interf_angles_radians_[kInterfNum];
  // The angle between the target and the interferer scenarios.
  float away_radians_;

  // Array of length |kNumFreqBins|, Matrix of size |1| x |num_channels_|.
  MouseBF_complex_matrix_f delay_sum_masks_[kNumFreqBins];
  //MouseBF_complex_matrix_f normalized_delay_sum_masks_[kNumFreqBins];

  // Arrays of length |kNumFreqBins|, Matrix of size |num_input_channels_| x
  // |num_input_channels_|.
  MouseBF_complex_matrix_f target_cov_mats_[kNumFreqBins];
  MouseBF_complex_matrix_f uniform_cov_mat_[kNumFreqBins];

  // Array of length |kNumFreqBins|, Matrix of size |num_input_channels_| x
  // |num_input_channels_|. ScopedVector has a size equal to the number of
  // interferer scenarios.
  MouseBF_complex_matrix_f interf_cov_mats_[kNumFreqBins][kInterfNum];

  // Of length |kNumFreqBins|.
  float mask_thresholds_[kNumFreqBins];
  float wave_numbers_[kNumFreqBins];

  // Of length |kNumFreqBins|.
  float rxiws_[kNumFreqBins];

  // The vector has a size equal to the number of interferer scenarios.
  float rpsiws_[kNumFreqBins][kInterfNum];

  // The microphone normalization factor.
  MouseBF_complex_matrix_f eig_m_;
  // For processing the high-frequency input signal.
  float high_pass_postfilter_mask_;
  // True when the target signal is present.
  int is_target_present_;
  // Number of blocks after which the data is considered interference if the
  // mask does not pass |kMaskSignalThreshold|.
  size_t hold_target_blocks_;

  // Number of blocks since the last mask that passed |kMaskSignalThreshold|.
  size_t interference_blocks_count_;

} MouseBF_nonlinear;

MouseBF_nonlinear *MouseBF_nonlinear_instance(int sample_rate_hz, 
                                                  MouseBF_point *array_geometry,
                                                  int mic_num, 
                                                  MouseBF_spherical_pointf target_direction);

void MouseBF_nonlinear_destroy(MouseBF_nonlinear *inst);

void MouseBF_nonlinear_reset(MouseBF_nonlinear *inst, 
                               MouseBF_spherical_pointf target_direction);

void MouseBF_nonlinear_process_block(MouseBF_nonlinear *inst, 
                                       MouseBF_complex_f** input,
                                       int num_input_channels,
                                       size_t num_freq_bins,
                                       int num_output_channels,
                                       MouseBF_complex_f** output);

typedef void (*MouseBF_Callback)(MouseBF_nonlinear*,
                           MouseBF_complex_f**,
                           int,
                           size_t,
                           int,
                           MouseBF_complex_f**);
#endif

(12)、src/bf-nonlinear.c

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-nonlinear.c
 * */

#include "bf-nonlinear.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include "bessel0.h"

static float MouseBF_DotProduct(MouseBF_point a, MouseBF_point b) {
  return a.c[0] * b.c[0] + a.c[1] * b.c[1] + a.c[2] * b.c[2];
}

static MouseBF_point MouseBF_PairDirection(MouseBF_point a, MouseBF_point b) {
  MouseBF_point ret;
  ret.c[0] = b.c[0] - a.c[0];
  ret.c[1] = b.c[1] - a.c[1];
  ret.c[2] = b.c[2] - a.c[2];
  return ret;
}

static MouseBF_point MouseBF_CrossProduct(MouseBF_point a, MouseBF_point b) {
  MouseBF_point ret;
  ret.c[0] = a.c[1] * b.c[2] - a.c[2] * b.c[1];
  ret.c[1] = a.c[2] * b.c[0] - a.c[0] * b.c[2];
  ret.c[2] = a.c[0] * b.c[1] - a.c[1] * b.c[0];
  return ret; 
}

static int MouseBF_AreParallel(MouseBF_point a, MouseBF_point b) {
  MouseBF_point cross_product = MouseBF_CrossProduct(a, b);
  return MouseBF_DotProduct(cross_product, cross_product) < kMaxDotProduct;
}

static MouseBF_point *MouseBF_GetDirectionIfLinear(MouseBF_point *array_geometry, int mic_num) {
  MouseBF_point first_pair_direction = MouseBF_PairDirection(array_geometry[0], array_geometry[1]);
  for (size_t i = 2u; i < mic_num; ++i) {
    MouseBF_point pair_direction = MouseBF_PairDirection(array_geometry[i - 1], array_geometry[i]);
    if (!MouseBF_AreParallel(first_pair_direction, pair_direction)) {
      return NULL;
    }
  }
  MouseBF_point *ret = (MouseBF_point*)malloc(sizeof(MouseBF_point));
  *ret = first_pair_direction;
  return ret;
}
static int MouseBF_ArePerpendicular(MouseBF_point a, MouseBF_point b) {
  return abs(MouseBF_DotProduct(a, b)) < kMaxDotProduct;
}

static MouseBF_point *MouseBF_GetNormalIfPlanar(MouseBF_point *array_geometry, int mic_num) {
  MouseBF_point first_pair_direction = MouseBF_PairDirection(array_geometry[0], array_geometry[1]);
  MouseBF_point pair_direction;
  pair_direction.c[0] = 0.f;
  pair_direction.c[1] = 0.f;
  pair_direction.c[2] = 0.f;
  size_t i = 2;
  int is_linear = 1;
  for (; i < mic_num && is_linear; ++i) {
    pair_direction = MouseBF_PairDirection(array_geometry[i - 1], array_geometry[i]);
    if (!MouseBF_AreParallel(first_pair_direction, pair_direction)) {
      is_linear = 0;
    }
  }
  if (is_linear) {
    return NULL;
  }
  MouseBF_point normal_direction = MouseBF_CrossProduct(first_pair_direction, pair_direction);
  for (; i < mic_num; ++i) {
    pair_direction = MouseBF_PairDirection(array_geometry[i - 1], array_geometry[i]);
    if (!MouseBF_ArePerpendicular(normal_direction, pair_direction)) {
      return NULL;
    }
  }
  MouseBF_point *ret = (MouseBF_point*)malloc(sizeof(MouseBF_point));
  *ret = normal_direction;
  return ret;
}

MouseBF_point MouseBF_AzimuthToPoint(float azimuth) {
  MouseBF_point ret;
  ret.c[0] = cos(azimuth);
  ret.c[1] = sin(azimuth);
  ret.c[2] = 0.f;
  return ret;
}

static MouseBF_point *MouseBF_GetArrayNormalIfExists(MouseBF_point *array_geometry, int mic_num) {
  MouseBF_point *direction = MouseBF_GetDirectionIfLinear(array_geometry, mic_num);
  if (direction) {
    MouseBF_point ret = *direction;
    (*direction).c[0] = ret.c[1];
    (*direction).c[1] = -ret.c[0];
    (*direction).c[2] = 0.f;
    return direction; 
  }
  MouseBF_point *normal = MouseBF_GetNormalIfPlanar(array_geometry, mic_num);
  if (normal && normal->c[2] < kMaxDotProduct) {
    return normal;
  }
  return NULL;
}

static void MouseBF_GetCenteredArray(MouseBF_point *array_geometry_src, int mic_num, MouseBF_point *array_geometry_des) {
  for (int dim = 0; dim < 3; ++dim) {
    float center = 0.f;
    for (size_t i = 0; i < mic_num; ++i) {
      center += array_geometry_src[i].c[dim];
    }
    center /= mic_num;
    for (size_t i = 0; i < mic_num; ++i) {
      array_geometry_des[i].c[dim] = array_geometry_src[i].c[dim] - center;
    }
  }
}

static float MouseBF_Distance(MouseBF_point a, MouseBF_point b) {
  return sqrt((a.c[0] - b.c[0]) * (a.c[0] - b.c[0]) +
              (a.c[1] - b.c[1]) * (a.c[1] - b.c[1]) +
              (a.c[2] - b.c[2]) * (a.c[2] - b.c[2]));
}

static float MouseBF_GetMinimumSpacing(MouseBF_point *array_geometry, int mic_num) {
  float mic_spacing = 10000000.0;
  for (size_t i = 0; i < mic_num - 1; ++i) {
    for (size_t j = i + 1; j < mic_num; ++j) {
      float tmp = MouseBF_Distance(array_geometry[i], array_geometry[j]);
      if (tmp < mic_spacing) {
        mic_spacing = tmp;
      }
    }
  }
  return mic_spacing;
}

static void MouseBF_InitLowFrequencyCorrectionRanges(MouseBF_nonlinear *inst) {
  inst->low_mean_start_bin_ = (size_t)floor(kLowMeanStartHz * kFftSize / inst->sample_rate_hz_ + 0.5);
  inst->low_mean_end_bin_   = (size_t)floor(kLowMeanEndHz   * kFftSize / inst->sample_rate_hz_ + 0.5);
}

static void MouseBF_UniformCovarianceMatrix(float wave_number,
                                              MouseBF_point *geometry, int mic_num,
                                              MouseBF_complex_matrix_f* mat) {
  assert(mic_num == mat->num_rows_);
  assert(mic_num == mat->num_cols_);

  MouseBF_complex_f **mat_els = mat->elements_;
  for (size_t i = 0; i < mic_num; ++i) {
    for (size_t j = 0; j < mic_num; ++j) {
      if (wave_number > 0.f) {
        //mat_els[i][j].real_ = j0(wave_number * MouseBF_Distance(geometry[i], geometry[j]));
        mat_els[i][j].real_ = bessel0(wave_number * MouseBF_Distance(geometry[i], geometry[j]));
      } else {
        mat_els[i][j].real_ = i == j ? 1.f : 0.f;
      }
      mat_els[i][j].imag_ = 0.0;
    }
  }
}

static void MouseBF_InitDiffuseCovMats(MouseBF_nonlinear *inst) {
  for (size_t i = 0; i < kNumFreqBins; ++i) {
    MouseBF_complex_matrix_reset(&(inst->uniform_cov_mat_[i]), inst->num_input_channels_, inst->num_input_channels_); 
    MouseBF_UniformCovarianceMatrix(inst->wave_numbers_[i], inst->array_geometry_, inst->num_input_channels_, &(inst->uniform_cov_mat_[i]));
    MouseBF_complex_f trace = MouseBF_Trace(&(inst->uniform_cov_mat_[i]));

    MouseBF_complex_f normalization_factor;
    normalization_factor.real_ = trace.real_ / (trace.real_*trace.real_ + trace.imag_*trace.imag_);
    normalization_factor.imag_ = -trace.imag_ / (trace.real_*trace.real_ + trace.imag_*trace.imag_);
    MouseBF_complex_matrix_scale(&(inst->uniform_cov_mat_[i]), normalization_factor);
    trace.real_ = 1 - kBalance;
    trace.imag_ = 0.0;
    MouseBF_complex_matrix_scale(&(inst->uniform_cov_mat_[i]), trace);
  }
}

static void MouseBF_InitHighFrequencyCorrectionRanges(MouseBF_nonlinear *inst) {
  //float kAliasingFreqHz = kSpeedOfSoundMeterSeconds / (inst->min_mic_spacing_ * 
  //                        (1.f + abs(cos(inst->target_angle_radians_))));
  float kHighMeanStartHz = 2000;
  float kHighMeanEndHz = 3000;
  inst->high_mean_start_bin_ = (size_t)floor(kHighMeanStartHz * kFftSize / inst->sample_rate_hz_ + 0.5);
  inst->high_mean_end_bin_   = (size_t)floor(kHighMeanEndHz   * kFftSize / inst->sample_rate_hz_ + 0.5);
}

static void MouseBF_InitInterfAngles(MouseBF_nonlinear *inst) {

  MouseBF_point target_direction = MouseBF_AzimuthToPoint(inst->target_angle_radians_);

  MouseBF_point clockwise_interf_direction = MouseBF_AzimuthToPoint(inst->target_angle_radians_ - inst->away_radians_);
  if (!inst->array_normal_ || MouseBF_DotProduct(*(inst->array_normal_), target_direction) * 
                              MouseBF_DotProduct(*(inst->array_normal_), clockwise_interf_direction) >=  0.f) {
    // The target and clockwise interferer are in the same half-plane defined
    // by the array.
    inst->interf_angles_radians_[0] = inst->target_angle_radians_ - inst->away_radians_;
  } else {
    // Otherwise, the interferer will begin reflecting back at the target.
    // Instead rotate it away 180 degrees.
    inst->interf_angles_radians_[0] = inst->target_angle_radians_ - inst->away_radians_ + M_PI;
  }
  MouseBF_point counterclock_interf_direction = MouseBF_AzimuthToPoint(inst->target_angle_radians_ + inst->away_radians_);
  if (!inst->array_normal_ || MouseBF_DotProduct(*(inst->array_normal_), target_direction) * 
                        MouseBF_DotProduct(*(inst->array_normal_), counterclock_interf_direction) >= 0.f) {
    // The target and counter-clockwise interferer are in the same half-plane
    // defined by the array.
    inst->interf_angles_radians_[1] = inst->target_angle_radians_ + inst->away_radians_;
  } else {
    // Otherwise, the interferer will begin reflecting back at the target.
    // Instead rotate it away 180 degrees.
    inst->interf_angles_radians_[1] = inst->target_angle_radians_ + inst->away_radians_ - M_PI;
  }

  //wj add
  
  clockwise_interf_direction = MouseBF_AzimuthToPoint(inst->target_angle_radians_ - M_PI / 2.0);
  if (!inst->array_normal_ || MouseBF_DotProduct(*(inst->array_normal_), target_direction) *
                              MouseBF_DotProduct(*(inst->array_normal_), clockwise_interf_direction) >=  0.f) {
    // The target and clockwise interferer are in the same half-plane defined
    // by the array.
    inst->interf_angles_radians_[2] = inst->target_angle_radians_ - M_PI / 2.0;
  } else {
    // Otherwise, the interferer will begin reflecting back at the target.
    // Instead rotate it away 180 degrees.
    inst->interf_angles_radians_[2] = inst->target_angle_radians_ - M_PI / 2.0 + M_PI;
  }
  counterclock_interf_direction = MouseBF_AzimuthToPoint(inst->target_angle_radians_ + M_PI / 2.0);
  if (!inst->array_normal_ || MouseBF_DotProduct(*(inst->array_normal_), target_direction) *
                        MouseBF_DotProduct(*(inst->array_normal_), counterclock_interf_direction) >= 0.f) {
    // The target and counter-clockwise interferer are in the same half-plane
    // defined by the array.
    inst->interf_angles_radians_[3] = inst->target_angle_radians_ + M_PI / 2.0;
  } else {
    // Otherwise, the interferer will begin reflecting back at the target.
    // Instead rotate it away 180 degrees.
    inst->interf_angles_radians_[3] = inst->target_angle_radians_ + M_PI / 2.0 - M_PI;
  }
  
}

static void MouseBF_PhaseAlignmentMasks(size_t frequency_bin,
                                          size_t fft_size,
                                          int sample_rate,
                                          float sound_speed,
                                          MouseBF_point *geometry,
                                          int num_mic,
                                          float angle,
                                          MouseBF_complex_matrix_f* mat) {
  assert(1 == mat->num_rows_);
  assert(num_mic == mat->num_cols_);

  float freq_in_hertz = (float)(frequency_bin) / fft_size * sample_rate;

  MouseBF_complex_f **mat_els = mat->elements_;
  for (size_t c_ix = 0; c_ix < num_mic; ++c_ix) {
    float distance = cos(angle) * geometry[c_ix].c[0] +
                     sin(angle) * geometry[c_ix].c[1];
    float phase_shift = 2.f * M_PI * distance * freq_in_hertz / sound_speed;

    // Euler's formula for mat[0][c_ix] = e^(j * phase_shift).
    mat_els[0][c_ix].real_ = cos(phase_shift);
    mat_els[0][c_ix].imag_ = sin(phase_shift);
  }
}

static void MouseBF_InitDelaySumMasks(MouseBF_nonlinear *inst) {
  for (size_t f_ix = 0; f_ix < kNumFreqBins; ++f_ix) {
    MouseBF_complex_matrix_reset(&(inst->delay_sum_masks_[f_ix]), 1, inst->num_input_channels_);
    //MouseBF_complex_matrix_reset(&(inst->normalized_delay_sum_masks_[f_ix]), 1, inst->num_input_channels_);
    MouseBF_PhaseAlignmentMasks(f_ix, kFftSize, inst->sample_rate_hz_, kSpeedOfSoundMeterSeconds,
                                  inst->array_geometry_, inst->num_input_channels_, 
                                  inst->target_angle_radians_, &(inst->delay_sum_masks_[f_ix]));
//_// zoro_ditch
//_//    MouseBF_complex_f norm_factor = MouseBF_ConjugateDotProduct(&(inst->delay_sum_masks_[f_ix]), &(inst->delay_sum_masks_[f_ix]));
    MouseBF_complex_f scale;
//_//    norm_factor.real_ = sqrt(norm_factor.real_);
//_//    norm_factor.imag_ = 0.0;//sqrt(norm_factor.imag_);
//_//    scale.real_ = norm_factor.real_ / (norm_factor.real_ * norm_factor.real_ + norm_factor.imag_ * norm_factor.imag_);
//_// zoro_update
    scale.real_ =  1/sqrt(inst->num_input_channels_);

    scale.imag_ = 0.0;
    MouseBF_complex_matrix_scale(&(inst->delay_sum_masks_[f_ix]), scale);

    /*MouseBF_complex_matrix_copy(&(inst->normalized_delay_sum_masks_[f_ix]), &(inst->delay_sum_masks_[f_ix]));
    norm_factor.real = 1.0 / MouseBF_SumAbs(&(inst->normalized_delay_sum_masks_[f_ix]));
    norm_factor.imag_ = 0.0;
    MouseBF_complex_matrix_scale(&(inst->normalized_delay_sum_masks_[f_ix]), norm_factor);*/
  }
}

static void MouseBF_InitTargetCovMats(MouseBF_nonlinear *inst) {
  for (size_t i = 0; i < kNumFreqBins; ++i) {
    MouseBF_complex_matrix_reset(&(inst->target_cov_mats_[i]), inst->num_input_channels_, inst->num_input_channels_);
    MouseBF_TransposedConjugatedProduct(&(inst->delay_sum_masks_[i]), &(inst->target_cov_mats_[i]));
//_// zoro_ditch
//_//    MouseBF_complex_f trace = MouseBF_Trace(&(inst->target_cov_mats_[i]));
//_//    MouseBF_complex_f norm_factor;
//_//    norm_factor.real_ =  trace.real_ / (trace.real_ * trace.real_ + trace.imag_ * trace.imag_);
//_//    norm_factor.imag_ = -trace.imag_ / (trace.real_ * trace.real_ + trace.imag_ * trace.imag_);
//_//    MouseBF_complex_matrix_scale(&(inst->target_cov_mats_[i]), norm_factor);
  }
}

static void MouseBF_AngledCovarianceMatrix(float sound_speed, float angle, size_t frequency_bin,
                                             size_t fft_size, size_t num_freq_bins, int sample_rate,
                                             MouseBF_point *geometry, int num_mic,
                                             MouseBF_complex_matrix_f *mat) {
  assert(num_mic == mat->num_rows_);
  assert(num_mic == mat->num_cols_);

  MouseBF_complex_matrix_f interf_cov_vector, interf_cov_vector_transposed;
  MouseBF_complex_matrix_init(&interf_cov_vector, 1, num_mic);
  MouseBF_complex_matrix_init(&interf_cov_vector_transposed, num_mic, 1);
  MouseBF_PhaseAlignmentMasks(frequency_bin, fft_size, sample_rate,
                                sound_speed, geometry, num_mic, angle,
                                &interf_cov_vector);
  MouseBF_Transpose(&interf_cov_vector, &interf_cov_vector_transposed);
  MouseBF_PointwiseConjugate(&interf_cov_vector);
  MouseBF_ComplexMatrixMultiply(&interf_cov_vector_transposed, &interf_cov_vector, mat);
  MouseBF_complex_matrix_free(&interf_cov_vector);
  MouseBF_complex_matrix_free(&interf_cov_vector_transposed);
}

static void MouseBF_InitInterfCovMats(MouseBF_nonlinear *inst) {
  for (size_t i = 0; i < kNumFreqBins; ++i) {
    for (size_t j = 0; j < kInterfNum; ++j) {
      MouseBF_complex_matrix_reset(&(inst->interf_cov_mats_[i][j]), inst->num_input_channels_, inst->num_input_channels_);
      MouseBF_complex_matrix_f angled_cov_mat;
      MouseBF_complex_matrix_init(&(angled_cov_mat), inst->num_input_channels_, inst->num_input_channels_);
      MouseBF_AngledCovarianceMatrix(kSpeedOfSoundMeterSeconds, inst->interf_angles_radians_[j],
                                       i, kFftSize, kNumFreqBins, inst->sample_rate_hz_,
                                       inst->array_geometry_, inst->num_input_channels_,  &angled_cov_mat);
      // Normalize matrices before averaging them.
      //complex_f normalization_factor = angled_cov_mat.elements()[0][0];
//_//      MouseBF_complex_f trace = MouseBF_Trace(&angled_cov_mat);
//_// zoro_ditch
//_//
      MouseBF_complex_f norm_factor;
//_//      norm_factor.real_ =  trace.real_ / (trace.real_ * trace.real_ + trace.imag_ * trace.imag_);
//_//      norm_factor.imag_ = -trace.imag_ / (trace.real_ * trace.real_ + trace.imag_ * trace.imag_);
//_// zoro_update
      norm_factor.real_ = 1/inst->num_input_channels_;
    norm_factor.imag_ = 0;
      MouseBF_complex_matrix_scale(&(angled_cov_mat), norm_factor);

      MouseBF_complex_f trace; 
      trace.real_ = kBalance;
      trace.imag_ = 0.0;
      MouseBF_complex_matrix_scale(&(angled_cov_mat), trace);
      MouseBF_ComplexMatrixAdd(&(inst->uniform_cov_mat_[i]), &(angled_cov_mat), &(inst->interf_cov_mats_[i][j]));
      MouseBF_complex_matrix_free(&(angled_cov_mat));
    }
  }
}

static void MouseBF_NormalizeCovMats(MouseBF_nonlinear *inst) {
  for (size_t i = 0; i < kNumFreqBins; ++i) {
//zoro_ditch  equal 1
//_//    inst->rxiws_[i] = MouseBF_Norm(&(inst->target_cov_mats_[i]), &(inst->delay_sum_masks_[i]));
    for (size_t j = 0; j < kInterfNum; ++j) {
      inst->rpsiws_[i][j] = MouseBF_Norm(&(inst->interf_cov_mats_[i][j]), &(inst->delay_sum_masks_[i]));
    }
  }
}

static float MouseBF_CalculatePostfilterMask(MouseBF_nonlinear *inst,
                                               MouseBF_complex_matrix_f *interf_cov_mat,
                                               float rpsiw,
                                               float ratio_rxiw_rxim,
                                               float rmw_r, float mask_threshold) {
  const float kMaskMinimum = 0.01f;
  float rpsim = MouseBF_Norm(interf_cov_mat, &(inst->eig_m_));

  // Find lambda.
  float ratio = 0.f;
  if (rpsim > 0.f) {
    ratio = rpsiw / rpsim;
  }
  float numerator = rmw_r - ratio;
  float denominator = ratio_rxiw_rxim - ratio;

  float mask = 1.f;
  if (denominator > mask_threshold) {
    float lambda = numerator / denominator;
    mask = lambda * ratio_rxiw_rxim / rmw_r;
    if (mask < kMaskMinimum) {
      mask = kMaskMinimum;
    }
  }
  return mask;
}

static void MouseBF_ApplyMaskTimeSmoothing(MouseBF_nonlinear *inst) {
  for (size_t i = inst->low_mean_start_bin_; i <= inst->high_mean_end_bin_; ++i) {
    inst->time_smooth_mask_[i] = kMaskTimeSmoothAlpha * inst->new_mask_[i] +
                           (1 - kMaskTimeSmoothAlpha) * inst->time_smooth_mask_[i];
  }  
}

static void MouseBF_ApplyLowFrequencyCorrection(MouseBF_nonlinear *inst) {
  float low_frequency_mask = 0;
  for (int i = inst->low_mean_start_bin_; i < inst->low_mean_end_bin_ + 1; i++) {
    low_frequency_mask += inst->time_smooth_mask_[i];
  }
  low_frequency_mask = low_frequency_mask / (inst->low_mean_end_bin_ - inst->low_mean_start_bin_ + 1);

  for (int i = 0; i < inst->low_mean_start_bin_; i++) {
    inst->time_smooth_mask_[i] = low_frequency_mask;
  }
}

static void MouseBF_ApplyHighFrequencyCorrection(MouseBF_nonlinear *inst) {
  inst->high_pass_postfilter_mask_ = 0;
  for (int i = inst->high_mean_start_bin_; i < inst->high_mean_end_bin_ + 1; i++) {
    inst->high_pass_postfilter_mask_ += inst->time_smooth_mask_[i];
  }
  inst->high_pass_postfilter_mask_ = inst->high_pass_postfilter_mask_ / (inst->high_mean_end_bin_ - inst->high_mean_start_bin_ + 1);

  for (int i = inst->high_mean_end_bin_+1; i < kNumFreqBins; i++) {
    inst->time_smooth_mask_[i] = inst->high_pass_postfilter_mask_;
  }
}

static void MouseBF_ApplyMaskFrequencySmoothing(MouseBF_nonlinear *inst) {
  memcpy(inst->final_mask_, inst->time_smooth_mask_, kNumFreqBins * sizeof(float));
  for (size_t i = inst->low_mean_start_bin_; i < kNumFreqBins; ++i) {
    inst->final_mask_[i] = kMaskFrequencySmoothAlpha * inst->final_mask_[i] +
                           (1 - kMaskFrequencySmoothAlpha) * inst->final_mask_[i - 1];
  }
  for (size_t i = inst->high_mean_end_bin_ + 1; i > 0; --i) {
    inst->final_mask_[i - 1] = kMaskFrequencySmoothAlpha * inst->final_mask_[i - 1] +
                               (1 - kMaskFrequencySmoothAlpha) * inst->final_mask_[i];
  }
}

static void MouseBF_ApplyMasks(MouseBF_nonlinear *inst, MouseBF_complex_f **input, MouseBF_complex_f **output) {
  MouseBF_complex_f* output_channel = output[0];
  for (size_t f_ix = 0; f_ix < kNumFreqBins; ++f_ix) {
    output_channel[f_ix].real_ = 0.0;
    output_channel[f_ix].imag_ = 0.0;

    MouseBF_complex_f* delay_sum_mask_els = inst->delay_sum_masks_[f_ix].elements_[0];
    for (int c_ix = 0; c_ix < inst->num_input_channels_; ++c_ix) {
      output_channel[f_ix] = MouseBF_complex_add(output_channel[f_ix], MouseBF_complex_mul(input[c_ix][f_ix], MouseBF_complex_conj(delay_sum_mask_els[c_ix])));
    }
    MouseBF_complex_f mask;
    mask.real_ = inst->final_mask_[f_ix] * kCompensationGain;
    mask.imag_ = 0;
    output_channel[f_ix] = MouseBF_complex_mul(output_channel[f_ix], mask);
  }
  for (size_t f_ix = 0; f_ix < 4; ++f_ix) {
    output_channel[f_ix].real_ = 0;
    output_channel[f_ix].imag_ = 0; 
  }
}
MouseBF_nonlinear *MouseBF_nonlinear_instance(int sample_rate_hz,
                                                  MouseBF_point *array_geometry,
                                                  int mic_num,
                                                  MouseBF_spherical_pointf target_direction) {
  MouseBF_nonlinear *inst = (MouseBF_nonlinear*)malloc(sizeof(MouseBF_nonlinear));
  inst->sample_rate_hz_ = sample_rate_hz;
  inst->num_input_channels_ = mic_num;
  inst->array_geometry_ = (MouseBF_point*)malloc(sizeof(MouseBF_point) * mic_num);

  MouseBF_GetCenteredArray(array_geometry, mic_num, inst->array_geometry_);

  inst->array_normal_ = MouseBF_GetArrayNormalIfExists(array_geometry, mic_num);

  inst->min_mic_spacing_ = MouseBF_GetMinimumSpacing(array_geometry, mic_num);
  inst->target_angle_radians_ = target_direction.s[0];
  inst->away_radians_ = M_PI / 4.0;

  inst->high_pass_postfilter_mask_ = 1.0;
  for (size_t i = 0; i < kNumFreqBins; ++i) {
    inst->time_smooth_mask_[i] = 1.f;
    inst->final_mask_[i] = 1.f;
    float freq_hz = ((float)i / kFftSize) * inst->sample_rate_hz_;
    inst->wave_numbers_[i] = 2 * M_PI * freq_hz / kSpeedOfSoundMeterSeconds;
    inst->mask_thresholds_[i] = inst->num_input_channels_ * inst->num_input_channels_ *
                                kBeamwidthConstant * inst->wave_numbers_[i] *
                                inst->wave_numbers_[i];
  }

  for (size_t i = 0; i < kNumFreqBins; ++i) {
    memset(&(inst->delay_sum_masks_[i]), 0, sizeof(MouseBF_complex_matrix_f));
    //memset(&(inst->normalized_delay_sum_masks_[i]), 0, sizeof(MouseBF_complex_matrix_f));
    memset(&(inst->target_cov_mats_[i]), 0, sizeof(MouseBF_complex_matrix_f));
    memset(&(inst->uniform_cov_mat_[i]), 0, sizeof(MouseBF_complex_matrix_f));
    for (size_t j = 0; j < kInterfNum; ++j) {
      memset(&(inst->interf_cov_mats_[i][j]), 0, sizeof(MouseBF_complex_matrix_f));
    }
  }
  memset(&(inst->eig_m_), 0, sizeof(MouseBF_complex_matrix_f));
  MouseBF_complex_matrix_init(&(inst->eig_m_), 1, inst->num_input_channels_);
  MouseBF_InitLowFrequencyCorrectionRanges(inst);
  MouseBF_InitDiffuseCovMats(inst);
  target_direction.s[1] = 0.f;
  target_direction.s[2] = 1.f;
  
  MouseBF_nonlinear_reset(inst, target_direction);
  return inst;
}

void MouseBF_nonlinear_reset(MouseBF_nonlinear *inst,
                               MouseBF_spherical_pointf target_direction) {
  inst->target_angle_radians_ = target_direction.s[0];
  MouseBF_InitHighFrequencyCorrectionRanges(inst);
  MouseBF_InitInterfAngles(inst);
  MouseBF_InitDelaySumMasks(inst);
  MouseBF_InitTargetCovMats(inst);
  MouseBF_InitInterfCovMats(inst);
  MouseBF_NormalizeCovMats(inst);
}

void MouseBF_nonlinear_process_block(MouseBF_nonlinear *inst,
                                       MouseBF_complex_f** input,
                                       int num_input_channels,
                                       size_t num_freq_bins,
                                       int num_output_channels,
                                       MouseBF_complex_f** output) {
  #ifdef _debug_
  FILE *fp = fopen("input.txt", "a");
  fprintf(fp, "[");
  for (int i = 0; i < num_input_channels; i++) {
    for (int j = 0; j < num_freq_bins; j++) {
      char flag[2];
      if (input[i][j].imag_ > 0) {
        strcpy(flag,"+");
      } else {
        strcpy(flag,"");
      }
      if (i == 0 && j == 0) {
        fprintf(fp, "%f%s%fi", input[i][j].real_, flag, input[i][j].imag_);
      } else {
        fprintf(fp, " %f%s%fi", input[i][j].real_, flag, input[i][j].imag_);
      }
    }
  }
  fprintf(fp, "]\n");
  fclose(fp);
  #endif
  // Calculating the post-filter masks. Note that we need two for each
  // frequency bin to account for the positive and negative interferer
  // angle.
  for (size_t i = inst->low_mean_start_bin_; i <= inst->high_mean_end_bin_; ++i) {
    MouseBF_complex_matrix_copy_from_column(&(inst->eig_m_), i, input);
    float eig_m_norm_factor = sqrt(MouseBF_SumSquares(&(inst->eig_m_)));
    if (eig_m_norm_factor != 0.f) {
      MouseBF_complex_f a;
      a.real_ = 1.0 / eig_m_norm_factor;
      a.imag_ = 0.0;
      MouseBF_complex_matrix_scale(&(inst->eig_m_), a);
    }
//_// zoro_ditch
//_//    float rxim = MouseBF_Norm(&(inst->target_cov_mats_[i]), &(inst->eig_m_));
//_//    float ratio_rxiw_rxim = 0.f;
//_//    if (rxim > 0.f) {
//_//       ratio_rxiw_rxim = inst->rxiws_[i] / rxim;
//_//    }
//_//    MouseBF_complex_f rmw = MouseBF_ConjugateDotProduct(&(inst->delay_sum_masks_[i]), &(inst->eig_m_));
//_//    //abs
//_//    rmw.real_ = sqrt(rmw.real_ * rmw.real_ + rmw.imag_ * rmw.imag_);
//_//    rmw.imag_ = 0.0;
//_//    rmw = MouseBF_complex_mul(rmw, rmw);
//_//    float rmw_r = rmw.real_;
//_//    inst->new_mask_[i] = MouseBF_CalculatePostfilterMask(inst, &(inst->interf_cov_mats_[i][0]),
//_//                                                           inst->rpsiws_[i][0],
//_//                                                           ratio_rxiw_rxim,
//_//                                                           rmw_r,
//_//                                                           inst->mask_thresholds_[i]);
//
//zoro_update
    const float kMaskMinimum = 0.01f;
    float rxim = MouseBF_Norm(&(inst->target_cov_mats_[i]), &(inst->eig_m_));  // P_x_target
    float rpsim = MouseBF_Norm(&(inst->interf_cov_mats_[i][0]), &(inst->eig_m_));  // P_x_gamma
    float ratio_rpsim_rpsiwm = rpsim/inst->rpsiws_[i][0];
    float mask = 1;
    if(ratio_rpsim_rpsiwm-rxim > inst->mask_thresholds_[i]*rxim*ratio_rpsim_rpsiwm){
      mask = (1-rxim*ratio_rpsim_rpsiwm)/(rxim*rxim-rxim*ratio_rpsim_rpsiwm);
    }
    if(mask < kMaskMinimum){ 
      mask = kMaskMinimum;
    }
    inst->new_mask_[i] = mask;
  
  
    for (int j = 1; j < kInterfNum; j++) {
      //_// zoro_update
      ratio_rpsim_rpsiwm = rpsim/inst->rpsiws_[i][j];
      mask = 1;
      if(ratio_rpsim_rpsiwm-rxim > inst->mask_thresholds_[i]*rxim*ratio_rpsim_rpsiwm){
        mask = (1-rxim*ratio_rpsim_rpsiwm)/(rxim*rxim-rxim*ratio_rpsim_rpsiwm);
      }
      if(mask < kMaskMinimum){ 
        mask = kMaskMinimum;
      }
      inst->new_mask_[i] *= mask;
//_//      inst->new_mask_[i] *= MouseBF_CalculatePostfilterMask(inst, &(inst->interf_cov_mats_[i][j]),
//_//                                                             inst->rpsiws_[i][j],
//_//                                                             ratio_rxiw_rxim,
//_//                                                             rmw_r,
//_//                                                             inst->mask_thresholds_[i]);
    }
  }
  MouseBF_ApplyMaskTimeSmoothing(inst);
  //MouseBF_EstimateTargetPresence(inst);
  MouseBF_ApplyLowFrequencyCorrection(inst);
  MouseBF_ApplyHighFrequencyCorrection(inst);
  MouseBF_ApplyMaskFrequencySmoothing(inst);
  MouseBF_ApplyMasks(inst, input, output);
}

void MouseBF_nonlinear_destroy(MouseBF_nonlinear *inst) {
  free(inst->array_geometry_);
  free(inst->array_normal_);

  for (size_t i = 0; i < kNumFreqBins; ++i) {
    MouseBF_complex_matrix_free(&(inst->delay_sum_masks_[i]));
    //MouseBF_complex_matrix_free(&(inst->normalized_delay_sum_masks_[i]));
    MouseBF_complex_matrix_free(&(inst->target_cov_mats_[i]));
    MouseBF_complex_matrix_free(&(inst->uniform_cov_mat_[i]));
    for (size_t j = 0; j < kInterfNum; ++j) {
      MouseBF_complex_matrix_free(&(inst->interf_cov_mats_[i][j]));
    }
  }
  MouseBF_complex_matrix_free(&(inst->eig_m_));
  free(inst);

}

(13)、src/bf-types.h

/*
 *@author : aflyingwolf
 *@date   : 2025.4.10
 *@file   : bf-types.h
 * */

#ifndef __BF_TYPES_H__
#define __BF_TYPES_H__

#define M_PI 3.14159265358979323846264338327950288
#define kFftSize 256
#define kNumFreqBins (kFftSize / 2 + 1)
#define kMaskTimeSmoothAlpha 0.2
#define kMaskFrequencySmoothAlpha 0.6
#define kCompensationGain 0.2
#define kSpeedOfSoundMeterSeconds 343.0
#define kBalance 0.90
#define kBeamwidthConstant 0.00002
#define kInterfNum 4
#define kMaxDotProduct 1e-6f
#define kLowMeanStartHz 125
#define kLowMeanEndHz 400
#define kSampleRate 16000

typedef struct MouseBF_spherical_pointf_ {
  float s[3]; //azimuth, elevation, radius
} MouseBF_spherical_pointf;

typedef struct MouseBF_point_ {
  float c[3]; // x,y,z
} MouseBF_point;

#endif

(14)、src/CMakeLists.txt

set(SOURCES
  ring_buffer.c
  fft4g.c
  bf-fft.c
  bf-blocker.c
  bf-api.c
  bf-complex.c
  bf-matrix.c
  bf-nonlinear.c
  bessel0.c
)

add_library("${NAME}" STATIC ${SOURCES})

(15)、bin/test-api.c

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "bf-api.h"

#define NUM_FRAMES 160

int main(int argc, char* argv[]) {
  if (argc < 3) {
    printf("please input in.pcm out.pcm mic_positions angles(40) mic_num(6)\n");
    return -1;
  }
  const char *i = argv[1];
  const char *o = argv[2];
  const char *mic_positions = argv[3];
  float angles = atof(argv[4]);
  int mic_num = atoi(argv[5]);
  void *handle = api_create_bf_handle(mic_positions, mic_num, angles);
  FILE *fp_r = fopen(i, "rb");
  FILE *fp_w = fopen(o, "wb");
  api_start_bf(handle);
  
  short *input_data = (short*)malloc(sizeof(short) * mic_num * NUM_FRAMES);
  float *inp_float = (float*)malloc(sizeof(float) * mic_num * NUM_FRAMES);
  short *out_data  = (short*)malloc(sizeof(short) * NUM_FRAMES);
  float *out_float = (float*)malloc(sizeof(float) * NUM_FRAMES);
  while (fread(input_data, sizeof(short), mic_num*NUM_FRAMES, fp_r) == mic_num*NUM_FRAMES) {
    for (int i = 0; i < mic_num*NUM_FRAMES; i++) {
      inp_float[i] = input_data[i];
    }
    api_process_bf(handle, inp_float, out_float, NUM_FRAMES);
    for (int i = 0; i < NUM_FRAMES; i++) {
      out_data[i] = out_float[i];
    }
    fwrite(out_data, sizeof(short), NUM_FRAMES, fp_w);
  }

  api_end_bf(handle);
  
  fclose(fp_r);
  fclose(fp_w);
  api_destroy_bf_handle(handle);

  free(input_data);
  free(inp_float);
  free(out_data);
  free(out_float);
  return 0;
}

(16)、bin/CMakeLists.txt

add_executable(test-api test-api.c)
target_link_libraries(test-api ${NAME} -lm)

(17)、CMakeLists.txt

cmake_minimum_required (VERSION 2.6)
project (mouse-abf)

#SET(CMAKE_BUILD_TYPE "DEBUG")
SET(CMAKE_BUILD_TYPE "RELEASE")
SET(CMAKE_C_FLAGS_DEBUG "$ENV{CFLAGS} -g")
SET(CMAKE_C_FLAGS_RELEASE "$ENV{CFLAGS} -O3")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

add_compile_options(-Wall)
add_compile_options(-Wno-sign-compare)
add_compile_options(-Wno-unused-local-typedefs)
add_compile_options(-Wno-deprecated-declarations)
add_compile_options(-fPIC)
add_compile_options(-Wno-deprecated)
add_compile_options(-Wwrite-strings)
add_compile_options(-Wno-sign-compare -Wno-unused-local-typedefs -Winit-self)

SET(BUILD_PATH ${CMAKE_BINARY_DIR})
SET(NAME "mouse-abf")

MESSAGE(STATUS "NAME:${NAME}")

link_directories(${LIB_PATH} ${CMAKE_BINARY_DIR}/src)
include_directories("${CMAKE_SOURCE_DIR}/src")

add_subdirectory(src)
add_subdirectory(bin)

2.4 demo

三、结果展示

3.1 0度方向波束结果

3.2 180度方向波束结果

四、总结

        本节我们完成了非线性波束形成算法的工程实现,可以作为单独的波束形成系统来使用。完整代码可以进入https://t.zsxq.com/qgmoN 获取。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐