波束形成(BF)从算法仿真到工程源码实现-第十一节-非线性波束形成算法工程化
一、概述
本节我们对非线性波束形成算法进行工程化,运行在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"
{
#endifvoid *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_;//bfMouseBF_nonlinear *bf_inst_;//callbackMouseBF_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 outputMouseBF_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 constantssize_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 addclockwise_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_updatescale.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_updatenorm_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_updateconst float kMaskMinimum = 0.01f;float rxim = MouseBF_Norm(&(inst->target_cov_mats_[i]), &(inst->eig_m_)); // P_x_targetfloat rpsim = MouseBF_Norm(&(inst->interf_cov_mats_[i][0]), &(inst->eig_m_)); // P_x_gammafloat 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_updateratio_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 16000typedef 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(SOURCESring_buffer.cfft4g.cbf-fft.cbf-blocker.cbf-api.cbf-complex.cbf-matrix.cbf-nonlinear.cbessel0.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 160int 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 获取。
相关文章:
波束形成(BF)从算法仿真到工程源码实现-第十一节-非线性波束形成算法工程化
一、概述 本节我们对非线性波束形成算法进行工程化,运行在respeaker core v2平台上,算法实时率在0.046左右。更多资料和代码可以进入https://t.zsxq.com/qgmoN ,同时欢迎大家提出宝贵的建议,以共同探讨学习。 二、算法实现 2.1 …...
Windows安装Rust版本GDAL
前言 笔者想安装GDAL,这是一个开源的地理数据库, 笔者到处搜索,最后看到这位大佬写的这篇文章,终于成功了。 aliothor/Windows-Install-Rust-Gdal-Tutorial: Windows Install Rust Version Gdal Stepshttps://github.com/aliot…...
OpenCv高阶(六)——图像的透视变换
目录 一、透视变换的定义与作用 二、透视变换的过程 三、OpenCV 中的透视变换函数 1. cv2.getPerspectiveTransform(src, dst) 2. cv2.warpPerspective(src, H, dsize, dstNone, flagscv2.INTER_LINEAR, borderModecv2.BORDER_CONSTANT, borderValue0) 四、文档扫描校正&a…...
常用正则化技术dropout
在深度学习中,Dropout 是一种常用的正则化技术,用于防止神经网络过拟合。它的核心思想是随机丢弃(临时关闭)网络中的部分神经元,迫使模型不依赖单一神经元,从而提升泛化能力。 1. Dropout…...
66.加1
目录 一、问题描述 二、解题思路 三、代码 四、复杂度分析 一、问题描述 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外&#…...
Tecnomatix Plant Simulation 2302安装教程
Tecnomatix Plant Simulation 2302安装教程,这个比较简单,只有4步即可完成。 第1步:获取并下载安装包 Follow WX account and reply: 2302, get the installation package link. 下载安装包至电脑本地,打开安装包文件如下图所示…...
Flutter 与原生通信
Flutter 与原生之间的通信主要基于通道机制,包括 MethodChannel、EventChannel 和 BasicMessageChannel。 MethodChannel:用于 Flutter 与原生之间的方法调用,实现双向通信,适合一次性的方法调用并获取返回值,如 Flut…...
关于postman的使用(一)
postman创建被测系统结构 改为被测系统名称 添加一级功能 添加接口测试 请求发起前脚本和请求发起后脚本 请求前运行脚本(需要一个随机的岗位名称): 上述脚本功能是自动生成一个岗位名称并且配置它为postman的变量下面是调用 请求后运行脚本…...
【c语言】深入理解指针1
深入理解指针1 一、数组名的理解二、使用指针访问数组三、一维数组传参本质四、二级指针 一、数组名的理解 数组名就是数组首元素的地址,类型是指针类型,但是存在两个例外: sizeof(arr) : 整个数组在内存中的大小 &arr : 整个数组的地址…...
leetcode14.最长公共前缀
暴力逐个比对最长前缀 class Solution {public String longestCommonPrefix(String[] strs) {String prefix strs[0];for (int i 1; i < strs.length; i) {prefix longestCommonPrefix(prefix, strs[i]);}return prefix;}private String longestCommonPrefix(String st…...
云服务器X86计算和Arm计算架构有什么区别?
阿里云服务器架构X86计算和ARM计算有什么区别?x86架构是最常见的,CPU采用Intel或AMD处理器;ARM架构具有低功耗的特性,CPU采用Ampere Altra / AltraMax或阿里自研倚天710处理器。如何选择?阿里云服务器网aliyunfuwuqi.com建议根据实际使用场景选择,X86架构兼容性更广,适合…...
leetcode0079. 单词搜索-medium
1 题目: 单词搜索 官方标定难度:中 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字…...
ShellScript脚本编程
语法基础 脚本结构 我们先从这个小demo程序来窥探一下我们shell脚本的程序结构 #!/bin/bash# 注释信息echo_str"hello world"test(){echo $echo_str }test echo_str 首先我们可以通过文本编辑器(在这里我们使用linux自带文本编辑神器vim),新建一个文件…...
【leetcode100】整数拆分
1、题目描述 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k > 2 ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输入: n 10 输出: 36…...
leetcode:2899. 上一个遍历的整数(python3解法)
难度:简单 给你一个整数数组 nums ,其中 nums[i] 要么是一个正整数,要么是 -1 。我们需要为每个 -1 找到相应的正整数,我们称之为最后访问的整数。 为了达到这个目标,定义两个空数组:seen 和 ans。 从数组 …...
Mysql读写分离(2)-中间件mycat和实践方案
系统环境要求 Mysql版本5.5版本以上jdk1.7Mycat1.6 mycat使用Java开发,因为用到了JDK 7的部分功能,所以在使用前请确保安装了JDK 7.0,并设置了正确的Java环境变量(可在命令行窗口输入:“java –version”获知是否安装…...
QT之在多线程中如何优雅的处理资源泄漏
概述 在多线程编程中,资源泄漏是一个常见且需要特别关注的问题。资源泄漏通常指的是程序未能正确释放分配给它的资源(如内存、文件句柄、数据库连接等),这可能导致系统性能下降甚至崩溃。尤其是在多线程环境中,由于多个线程可能同时访问相同的资源,增加了管理这些资源的…...
SPA 收入支出/技师提成自动统计系统——仙盟共创平台——未来之窗
支出 spa服务 使用开始:https://mp.weixin.qq.com/s/Ok3wuSYAPhd-6N8DrK7jwg 收入清晰呈现:自动整合 SPA 门店各类服务项目收入数据,包括面部护理、身体按摩、特色疗程等。通过对接收银系统,实时记录每笔消费金额,按不…...
Linux的应用领域,测试与Linux,Linux的介绍,VirtualBox和Ubuntu的安装,VMware的安装和打开虚拟机CentOS
目录 Linux的应用领域 测试人员在Linux的工作 测试人员需要掌握Linux的程度 Linux的介绍 Linux的介绍 Linux发行版 Unix和Linux的渊源 虚拟机和Linux的安装 VirtualBox和Ubuntu的安装 安装VirtualBox 安装Ubuntu 下载Ubuntu操作系统的镜像文件 创建虚拟机 虚拟机…...
《Not All Tokens Are What You Need for Pretraining》全文翻译
《Not All Tokens Are What You Need for Pretraining》 不是所有的词元都是预训练所需 摘要 先前的语言模型预训练方法通常对所有训练词元均匀地应用下一词预测损失。对此常规做法提出挑战,我们认为“语料库中的并非所有词元对于语言模型训练同等重要”。我们的…...
vscode终端运行windows服务器的conda出错
远程windows服务器可以运行,本地vscode不能。 打开vscode settings.json文件 添加conda所在路径...
使用基数树优化高并发内存池(替代加锁访问的哈希表和红黑树)
前言: 本篇旨在熟悉 基于tcmalloc的高性能并发内存池项目之后,对于最后的优化进行的笔记梳理,项目完整代码 可点击:项目代码 进行查看。 优化思想风暴: 为了方便根据页号查找到对应的span, 这里我们可以使用红黑树或…...
【bash】.bashrc
查看当前路径文件数量 alias file_num"ls -l | grep ^- | wc -l"查看文件大小 alias file_size"du -sh"alias ll alias ll"ls -ltrh"cd的同时执行ll alias cdcdls; function cdls() {builtin cd "$1" && ll }自定义prompt…...
自我生成,自我训练:大模型用合成数据实现“自我学习”机制实战解析
目录 自我生成,自我训练:大模型用合成数据实现“自我学习”机制实战解析 一、什么是自我学习机制? 二、实现机制:如何用合成数据实现自我训练? ✅ 方式一:Prompt强化生成 → 自我采样再训练 ✅ 方式二…...
Linux的命令格式,运行级别,找回root密码,Linux用户的分类,绝对路径和相对路径,硬链接和软链接,实用按键
目录 Linux的命令格式 运行级别 运行级别说明 切换运行级别 指定默认的运行级别 找回root密码 找回root密码 可能会出现的问题 Linux的用户分类 绝对路径和相对路径 硬链接和软链接 实用按键 Linux的命令格式 Linux的命令格式: command [-options] [par…...
C# JSON
在C#中,你可以使用System.Text.Json或Newtonsoft.Json库来解析JSON字符串。以下是使用这两种库分别解析你提供的JSON字符串的示例。 1. 使用 System.Text.Json System.Text.Json 是 .NET Core 3.0 及以上版本中包含的内置JSON库。以下是如何使用它来解析你的JSON字…...
入门-C编程基础部分:6、常量
飞书文档https://x509p6c8to.feishu.cn/wiki/MnkLwEozRidtw6kyeW9cwClbnAg C 常量 常量是固定值,在程序执行期间不会改变,可以让我们编程更加规范。 常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字…...
数字时代的AI与大数据:用高级AI开发技术革新大数据管理
李升伟 编译 在当今数字时代,数据的爆炸式增长令人惊叹 从社交媒体互动到物联网设备的传感器数据,企业正被海量信息淹没。但如何将这种无序的数据洪流转化为有价值的洞察?答案在于人工智能(AI)开发技术的革新&#x…...
数据结构与算法入门 Day 0:程序世界的基石与密码
🌟数据结构与算法入门 Day 0:程序世界的基石与密码🔑 ps:接受到了不少的私信反馈,说应该先把前置的知识内容做一个梳理,所以把昨天的文章删除了,重新开启今天的博文写作 Hey 小伙伴们ÿ…...
20250416在荣品的PRO-RK3566开发板的Android13下编译native C的应用程序的步骤
mm编译的简略步骤以及详细LOG,仅供参考: rootrootrootroot-X99-Turbo:~/hailuo_temp/Android13.0$ source build/envsetup.sh rootrootrootroot-X99-Turbo:~/hailuo_temp/Android13.0$ lunch 57. rk3566_t-userdebug Pick from common choices abo…...
Pikachu靶场——Cross-Site Scripting
使用ubantu-linux虚拟机通过docker镜像本地搭建 一,反射型xss(get) 1,观察靶场环境,功能是提交你最喜欢的NBA球星 2,可以通过burp suite抓包分析一下 通过GET请求提交输入的姓名,这是及其危险的 3,尝试使用…...
ArkTS组件的三个通用(通用事件、通用属性、通用手势)
文章目录 通用事件点击事件 onClick触摸事件 onTouch挂载、卸载事件拖拽事件按键事件 onKeyEvent焦点事件鼠标事件悬浮事件组件区域变化事件 onAreaChange组件尺寸变化事件组件可见区域变化事件组件快捷键事件自定义事件分发自定义事件拦截 通用属性尺寸设置位置设置布局约束边…...
双token实现无感刷新
一、方案说明 1. 核心流程 用户登录 提交账号密码 → 服务端验证 → 返回Access Token(前端存储) Refresh Token(HttpOnly Cookie) 业务请求 请求头携带Access Token → 服务端验证有效性 → 有效则返回数据 Token过…...
UE5游戏分辨率设置和窗口模式
第一种方法: 在项目配置Config文件夹下新建 DefaultGameUserSettings.ini 输入代码 [/Script/Engine.GameUserSettings] bUseVSyncFalse ResolutionSizeX1960 ResolutionSizeY1080 LastUserConfirmedResolutionSizeX800 LastUserConfirmedResolutionSizeY600 WindowPosX-1 …...
Java 线程中断 Interrupted
线程中断是 Java 中的一种协作机制,用于通知线程应该停止当前工作并退出。 中断就好比其它线程跟当前线程打了个招呼,告诉他可以执行中断操作。其他线程通过调用该线程的interrupt()方法对其进行中断操作。 中断并不会直接终止线程,而是设置…...
Android Jetpack是什么与原生android 有什么区别
Android Jetpack是什么 Android Jetpack是Google推出的一套开发组件工具集,旨在帮助开发者更高效地构建高质量的Android应用。它包含多个库和工具,被分为架构、用户界面、行为和基础四大类。以下是一些Android Jetpack的示例: 架构组件 ViewModel:用于以生命周期的方式管理…...
从0~1写一个starer启动器
从0到1编写一个Spring Boot Starter 前言 使用过Spring框架的伙伴都知道,虽然Spring在一定程度上帮助我们简化了集成其他框架,但在集成框架的同时仍少不了大量的XML配置,这些繁琐的工作无疑会加重我们的工作任务。而Spring Boot相较于Sprin…...
prime-2 靶场笔记(vuInhub靶场)
前言: 在本次靶场环境中涉及的知识点,主要包含LFI和SMB以及Lxd组提权,具体内容包括主机探测、端口扫描、目录扫描、wpscan扫描、反弹shell、一句话木马、容器、linux各种提权和维持。 环境介绍: 本靶场使用了kali(192…...
Node.js 中的 Buffer(缓冲区)
下面是关于 Node.js 中的 Buffer(缓冲区) 的系统总结,涵盖了定义、创建、读取修改、溢出处理、中文编码问题以及字符串转换等关键用法👇 🧱 一、什么是 Buffer? Buffer 是 Node.js 提供的用于处理二进制数…...
如何学习嵌入式
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.16 请各位前辈能否给我提点建议,或者学习路线指导一下 STM32单片机学习总…...
高版本Android (AIDL HAL) 使用HIDL方法
目录 修改步骤和编译方法 注意事项 Android 11 引入了使用 AIDL 实现 HAL 的功能。 后续Android新版本,HAL默认切到了使用AIDL. 因此当导入旧HIDL实现方式时,需要做一些修改。 1.将HAL HIDL模块拷贝到相应目录,进行编译 source build/envsetup.sh lunch xxx mmm 模块路径 1.…...
Cribl (实验) vpc-flow 数据抽样
先看文档: Firewall Logs: VPC Flow Logs, Cisco ASA, Etc. | Cribl Docs Firewall Logs: VPC Flow Logs, Cisco ASA, Etc. Recipe for Sampling Firewall Logs Firewall logs are another source of important operational (and security) data. Typical examples include Ama…...
RK3568 更换显示logo
文章目录 1、环境介绍2、替换logo 1、环境介绍 硬件:飞凌ok3568-c开发板 软件:原厂rk356x sdk 屏幕:1024*600 hdmi屏 2、替换logo 这是一件提无语的事。本来替换logo是很平常的一件事。即替换kernel目录下的logo图片即可: 但…...
修改wsl中发行版Ubuntu的主机名
我wsl2中装了两个ubuntu的发行版本,默认下主机名和我的windows主机名都一样,而且包含大写字母,在配置其他应用时经常会出问题,按照下面的顺序修改了一下: 1、打开ubuntu发行版 现在显示包含大写字母和数字的主机名。 …...
Python学习之路(三)
将 Python 与数据库对接是开发过程中常见的任务,可以使用多种数据库(如 SQLite、MySQL、PostgreSQL、Oracle、MongoDB 等)。以下是一些常见的数据库及其与 Python 的对接方法,包括安装库、连接数据库、执行查询和操作数据的示例。…...
多功能门禁系统的设计
本课题为多功能门禁系统的设计,其系统架构如图2.1所示,整个系统由STM32F103单片机和MaixBit开发板两部分构成,其中MaixBit是基于K210芯片的开发板,在此主要负责人脸的录入,识别,液晶显示等功能,…...
C/C++---头文件保护机制
在 C 和 C 编程里,头文件保护机制是一种防止头文件被重复包含的技术,它主要借助 #ifndef、#define 和 #endif 这些预处理指令来达成,也可以使用 #pragma once 这一编译器特定指令。下面详细阐述这一机制: 1. 头文件重复包含的问题…...
双指针算法(一)
目录 一、力扣——283、移动零 二、力扣——1089、复写零 三、力扣——11、盛最多的水 四、力扣——202、快乐数 一、力扣——283、移动零 题目如下: 这里我们用双指针算法,用的是双指针的思想,我们在这道题在数组下操作可以用数组下标。…...
LNMP架构部署论坛
目录 1.安装Nginx服务 1.系统初始化 2.安装工具包及依赖包 3.创建运行用户 4.编译安装 5.优化路径 6.添加 Nginx 系统服 2.安装MySQL服务 1.确定GLIBC版本 2.上传二进制压缩包并解压 3. 创建运行用户 4. 创建 mysql 配置文件 5.更改mysql安装目录和配…...
微信小程序边框容器带三角指向
效果图 .wxml <view class"tb"><view class"tb-pointer" style"--n:{{n}}rpx;" /> </view> <button bind:tap"addPixel">增加三角一个像素</button>.js Page({data: {n:16,},addPixel(){this.setData…...