UHSDR/UHSDR-active-devel/mchf-eclipse/drivers/audio/audio_nr.c

2538 lines
106 KiB
C
Raw Normal View History

2022-08-24 08:39:13 +02:00
/************************************************************************************
** **
** UHSDR Firmware **
** **
**---------------------------------------------------------------------------------**
** **
** Description: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "uhsdr_board_config.h"
#include "audio_nr.h"
#include "arm_const_structs.h"
#include "profiling.h"
//#define debug_alternate_NR
#ifdef USE_ALTERNATE_NR
static void alt_noise_blanking();
static void spectral_noise_reduction_3();
static void AudioNr_RunNoiseReduction(float32_t* inputsamples, float32_t* outputsamples );
audio_nr_params_t nr_params;
typedef struct NoiseReduction // declaration
{
float32_t last_iFFT_result [NR_FFT_L_2 / 2];
float32_t last_sample_buffer_L [NR_FFT_L_2 / 2];
float32_t FFT_buffer[NR_FFT_L_2 * 2];
float32_t Nest[NR_FFT_L_2 / 2][2]; // noise estimates for the current and the last FFT frame
float32_t vk; // saved 0.24kbytes
float32_t SNR_prio[NR_FFT_L_2 / 2];
float32_t SNR_post[NR_FFT_L_2 / 2];
float32_t SNR_post_pos; // saved 0.24kbytes
float32_t Hk_old[NR_FFT_L_2 / 2];
// float32_t VAD;
// float32_t VAD_Esch; // holds the VAD sum for the Esh & Vary 2009 type of VAD
// float32_t notch1_f;
// bool notch1_enable;
// float32_t notch2_f;
// bool notch2_enable;
//ulong long_tone_counter;
uint16_t current_buffer_idx;
bool was_here;
} NoiseReduction;
NoiseReduction __MCHF_SPECIALMEM NR; // definition
NoiseReduction2 NR2; // definition
__IO int32_t NR_in_head = 0;
__IO int32_t NR_in_tail = 0;
__IO int32_t NR_out_head = 0;
__IO int32_t NR_out_tail = 0;
NR_Buffer* NR_in_buffers[NR_BUFFER_FIFO_SIZE];
NR_Buffer* NR_out_buffers[NR_BUFFER_FIFO_SIZE];
/*const float32_t SQRT_van_hann[128]= {0.000000000, 0.024734427, 0.04945372, 0.074142753, 0.098786418, 0.123369638, 0.14787737, 0.172294617,
0.196606441, 0.220797963, 0.244854382, 0.268760979, 0.292503125, 0.316066292, 0.339436063, 0.362598137,
0.385538344, 0.408242645, 0.430697148, 0.452888114, 0.474801964, 0.49642529, 0.51774486, 0.53874763,
0.559420747, 0.579751564, 0.599727639, 0.619336749, 0.638566896, 0.657406313, 0.675843473, 0.693867094,
0.711466148, 0.728629866, 0.745347746, 0.761609559, 0.777405353, 0.792725465, 0.807560519, 0.821901439,
0.835739449, 0.849066083, 0.861873185, 0.874152919, 0.885897772, 0.897100557, 0.907754419, 0.91785284,
0.927389639, 0.936358983, 0.944755382, 0.952573698, 0.959809149, 0.966457306, 0.972514103, 0.977975832,
0.982839151, 0.987101086, 0.990759028, 0.993810738, 0.996254351, 0.99808837, 0.999311673, 0.999923511,
0.999923511, 0.999311673, 0.99808837, 0.996254351, 0.993810738, 0.990759028, 0.987101086, 0.982839151,
0.977975832, 0.972514103, 0.966457306, 0.959809149, 0.952573698, 0.944755382, 0.936358983, 0.927389639,
0.91785284, 0.907754419, 0.897100557, 0.885897772, 0.874152919, 0.861873185, 0.849066083, 0.835739449,
0.821901439, 0.807560519, 0.792725465, 0.777405353, 0.761609559, 0.745347746, 0.728629866, 0.711466148,
0.693867094, 0.675843473, 0.657406313, 0.638566896, 0.619336749, 0.599727639, 0.579751564, 0.559420747,
0.53874763, 0.51774486, 0.49642529, 0.474801964, 0.452888114, 0.430697148, 0.408242645, 0.385538344,
0.362598137, 0.339436063, 0.316066292, 0.292503125, 0.268760979, 0.244854382, 0.220797963, 0.196606441,
0.172294617, 0.14787737, 0.123369638, 0.098786418, 0.074142753, 0.04945372, 0.024734427, 0.00000000};
*/
const float32_t SQRT_von_Hann_256[256] = {0,0.01231966,0.024637449,0.036951499,0.049259941,0.061560906,0.073852527,0.086132939,0.098400278,0.110652682,0.122888291,0.135105247,0.147301698,0.159475791,0.171625679,0.183749518,0.195845467,0.207911691,0.219946358,0.231947641,0.24391372,0.255842778,0.267733003,0.279582593,0.291389747,0.303152674,0.314869589,0.326538713,0.338158275,0.349726511,0.361241666,0.372701992,0.384105749,0.395451207,0.406736643,0.417960345,0.429120609,0.440215741,0.451244057,0.462203884,0.473093557,0.483911424,0.494655843,0.505325184,0.515917826,0.526432163,0.536866598,0.547219547,0.557489439,0.567674716,0.577773831,0.587785252,0.597707459,0.607538946,0.617278221,0.626923806,0.636474236,0.645928062,0.65528385,0.664540179,0.673695644,0.682748855,0.691698439,0.700543038,0.709281308,0.717911923,0.726433574,0.734844967,0.743144825,0.75133189,0.759404917,0.767362681,0.775203976,0.78292761,0.790532412,0.798017227,0.805380919,0.812622371,0.819740483,0.826734175,0.833602385,0.840344072,0.846958211,0.853443799,0.859799851,0.866025404,0.872119511,0.878081248,0.88390971,0.889604013,0.895163291,0.900586702,0.905873422,0.911022649,0.916033601,0.920905518,0.92563766,0.930229309,0.934679767,0.938988361,0.943154434,0.947177357,0.951056516,0.954791325,0.958381215,0.961825643,0.965124085,0.968276041,0.971281032,0.974138602,0.976848318,0.979409768,0.981822563,0.984086337,0.986200747,0.988165472,0.989980213,0.991644696,0.993158666,0.994521895,0.995734176,0.996795325,0.99770518,0.998463604,0.999070481,0.99952572,0.99982925,0.999981027,0.999981027,0.99982925,0.99952572,0.999070481,0.998463604,0.99770518,0.996795325,0.995734176,0.994521895,0.993158666,0.991644696,0.989980213,0.988165472,0.986200747,0.984086337,0.981822563,0.979409768,0.976848318,0.974138602,0.971281032,0.968276041,0.965124085,0.961825643,0.958381215,0.954791325,0.951056516,0.947177357,0.943154434,0.938988361,0.934679767,0.930229309,0.92563766,0.920905518,0.916033601,0.911022649,0.905873422,0.900586702,0.895163291,0.889604013,0.88390971,0.878081248,0.872119511,0.866025404,0.859799851,0.853443799,0.846958211,0.840344072,0.833602385,0.826734175,0.819740483,0.812622371,0.805380919,0.798017227,0.790532412,0.78292761,0.775203976,0.767362681,0.759404917,0.75133189,0.743144825,0.734844967,0.726433574,0.717911923,0.709281308,0.700543038,0.691698439,0.682748855,0.673695644,0.664540179,0.65528385,0.645928062,0.636474236,0.626923806,0.617278221,0.607538946,0.597707459,0.587785252,0.577773831,0.567674716,0.557489439,0.547219547,0.536866598,0.526432163,0.515917826,0.505325184,0.494655843,0.483911424,0.473093557,0.462203884,0.451244057,0.440215741,0.429120609,0.417960345,0.406736643,0.395451207,0.384105749,0.372701992,0.361241666,0.349726511,0.338158275,0.326538713,0.314869589,0.303152674,0.291389747,0.279582593,0.267733003,0.255842778,0.24391372,0.231947641,0.219946358,0.207911691,0.195845467,0.183749518,0.171625679,0.159475791,0.147301698,0.135105247,0.122888291,0.110652682,0.098400278,0.086132939,0.073852527,0.061560906,0.049259941,0.036951499,0.024637449,0.01231966,0};
void NR_Init()
{
nr_params.alpha = 0.94; // spectral noise reduction
nr_params.beta = 0.96;
nr_params.enable = false;
nr_params.NR_FFT_L = 256;
nr_params.NR_FFT_LOOP_NO = 1;
nr_params.first_time = 1;
nr_params.NR_decimation_enable = true;
nr_params.fft_256_enable = true;
ts.special_functions_enabled = 0;
NR2.width = 4;
NR2.power_threshold = 0.40;
NR2.asnr = 30;
// TODO: Decide to through out these unused parameters
// nr_params.gain_smooth_enable = false;
// nr_params.gain_smooth_alpha = 0.25;
// nr_params.long_tone_enable = false;
// nr_params.long_tone_alpha = 0.999;
// nr_params.long_tone_thresh = 10000;
// nr_params.long_tone_reset = true;
// nr_params.vad_thresh = 4.0;
// nr_params.vad_delay = 7;
}
#if 0
// biquad IIR filter with a maximum of four notch filters
// pre-filled with passthrough coefficients
static arm_biquad_casd_df1_inst_f32 NR_notch_biquad =
{
.numStages = 4,
.pCoeffs = (float32_t *)(float32_t [])
{
1,0,0,0,0, 1,0,0,0,0, 1,0,0,0,0, 1,0,0,0,0
}, // 4 x 5 = 20 coefficients
.pState = (float32_t *)(float32_t [])
{
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
} // 4 x 4 = 16 state variables
};
static const float32_t biquad_passthrough[] = { 1, 0, 0, 0, 0 };
static float32_t NR_notch_coeffs[5];
void AudioNR_SetBiquadCoeffs(float32_t* coeffsTo,const float32_t* coeffsFrom, float scaling)
{
coeffsTo[0] = coeffsFrom[0]/scaling;
coeffsTo[1] = coeffsFrom[1]/scaling;
coeffsTo[2] = coeffsFrom[2]/scaling;
coeffsTo[3] = coeffsFrom[3]/scaling;
coeffsTo[4] = coeffsFrom[4]/scaling;
}
void AudioNr_CalculateAutoNotch(float32_t coeffs[6], uint8_t notch1_bin, bool notch1_active, float32_t BW, float32_t FS)
{
// formula taken from:
// DSP Audio-EQ-cookbook for generating the coeffs of the filters on the fly
// www.musicdsp.org/files/Audio-EQ-Cookbook.txt [by Robert Bristow-Johnson]
float32_t bin_BW = FS / nr_params.NR_FFT_L;
float32_t f0 = ((float32_t)notch1_bin + 0.5) * bin_BW; // where its happening, man ;-) centre f of the bin
float32_t Q = 10; // larger Q gives narrower notch
float32_t w0 = 6.28318530717958 * f0 / FS;
float32_t alpha = sinf(w0) / (2 * Q);
// float32_t alpha = sinf(w0)*sinh( 0.3465735902799726 * BW * w0/sinf(w0) );
coeffs[0] = 1;
coeffs[1] = - 2 * cosf(w0);
coeffs[2] = 1;
coeffs[3] = 1 + alpha;
coeffs[4] = 2 * cosf(w0); // already negated!
coeffs[5] = alpha - 1; // already negated!
}
void AudioNr_ActivateAutoNotch(uint8_t notch1_bin, bool notch1_active)
{
if(notch1_active)
{ // set coeffs to new notch frequency
AudioNr_CalculateAutoNotch(NR_notch_coeffs, notch1_bin, notch1_active, 100, 12000);
AudioNR_SetBiquadCoeffs(&NR_notch_biquad.pCoeffs[0], NR_notch_coeffs, NR_notch_coeffs[3]); // first biquad
}
else
{ // set coeffs to passthrough = NO notch
AudioNR_SetBiquadCoeffs(&NR_notch_biquad.pCoeffs[0], biquad_passthrough, 1.0); // first biquad --> passthrough coeffs
}
// second biquad
// AudioNR_SetBiquadCoeffs(&NR_notch_biquad.pCoeffs[5], NR_notch_coeffs, NR_notch_coeffs[0],1.0); // second biquad
}
#endif
int NR_in_buffer_peek(NR_Buffer** c_ptr)
{
int ret = 0;
if (NR_in_head != NR_in_tail)
{
NR_Buffer* c = NR_in_buffers[NR_in_tail];
*c_ptr = c;
ret++;
}
return ret;
}
int NR_in_buffer_remove(NR_Buffer** c_ptr)
{
int ret = 0;
if (NR_in_head != NR_in_tail)
{
NR_Buffer* c = NR_in_buffers[NR_in_tail];
NR_in_tail = (NR_in_tail + 1) % NR_BUFFER_FIFO_SIZE;
*c_ptr = c;
ret++;
}
return ret;
}
/* no room left in the buffer returns 0 */
int NR_in_buffer_add(NR_Buffer* c)
{
int ret = 0;
int32_t next_head = (NR_in_head + 1) % NR_BUFFER_FIFO_SIZE;
if (next_head != NR_in_tail)
{
/* there is room */
NR_in_buffers[NR_in_head] = c;
NR_in_head = next_head;
ret ++;
}
return ret;
}
void NR_in_buffer_reset()
{
NR_in_tail = NR_in_head;
}
int8_t NR_in_has_data()
{
int32_t len = NR_in_head - NR_in_tail;
return len < 0?len+NR_BUFFER_FIFO_SIZE:len;
}
int32_t NR_in_has_room()
{
// FIXME: Since we cannot completely fill the buffer
// we need to say full 1 element earlier
return NR_BUFFER_FIFO_SIZE - 1 - NR_in_has_data();
}
//*********Out Buffer handling
int NR_out_buffer_peek(NR_Buffer** c_ptr)
{
int ret = 0;
if (NR_out_head != NR_out_tail)
{
NR_Buffer* c = NR_out_buffers[NR_out_tail];
*c_ptr = c;
ret++;
}
return ret;
}
int NR_out_buffer_remove(NR_Buffer** c_ptr)
{
int ret = 0;
if (NR_out_head != NR_out_tail)
{
NR_Buffer* c = NR_out_buffers[NR_out_tail];
NR_out_tail = (NR_out_tail + 1) % NR_BUFFER_FIFO_SIZE;
*c_ptr = c;
ret++;
}
return ret;
}
/* no room left in the buffer returns 0 */
int NR_out_buffer_add(NR_Buffer* c)
{
int ret = 0;
int32_t next_head = (NR_out_head + 1) % NR_BUFFER_FIFO_SIZE;
if (next_head != NR_out_tail)
{
/* there is room */
NR_out_buffers[NR_out_head] = c;
NR_out_head = next_head;
ret ++;
}
return ret;
}
void NR_out_buffer_reset()
{
NR_out_tail = NR_out_head;
}
int8_t NR_out_has_data()
{
int32_t len = NR_out_head - NR_out_tail;
return len < 0?len+NR_BUFFER_FIFO_SIZE:len;
}
int32_t NR_out_has_room()
{
// FIXME: Since we cannot completely fill the buffer
// we need to say full 1 element earlier
return NR_BUFFER_FIFO_SIZE - 1 - NR_out_has_data();
}
void AudioNr_Prepare()
{
NR.current_buffer_idx = 0;
NR.was_here = false;
memset(&mmb.nr_audio_buff[0].samples[0], 0, NR_BUFFER_NUM * sizeof(mmb.nr_audio_buff[0]));
}
/**
* External "interface" of the noise reduction, handles the buffer communication and runs the actual processing algorithm
* Runs for a significant time (couple of milliseconds on a STM32F4) so must be run in an interruptible context.
* All "communication" with the outside world is through parameter and the input and output buffers.
*/
void AudioNr_HandleNoiseReduction()
{
if (NR.was_here == false)
{
NR.was_here = true;
NR.current_buffer_idx = 0;
NR_in_buffer_reset();
NR_out_buffer_reset();
}
if ( NR_in_has_data() && NR_out_has_room())
{ // audio data is ready to be processed
NR.current_buffer_idx %= NR_BUFFER_NUM;
NR_Buffer* input_buf = NULL;
NR_in_buffer_remove(&input_buf); //&input_buffer points to the current valid audio data
// inside here do all the necessary noise reduction stuff!!!!!
// here are the current input samples: input_buf->samples
// NR_output samples have to be placed here: fdv_iq_buff[NR.current_buffer_idx].samples
// but starting at an offset of NR_FFT_SIZE as we are using the same buffer for in and out
// here is the only place where we are referring to fdv_iq... as this is the name of the used freedv buffer
//profileTimedEventStart(ProfileTP8);
AudioNr_RunNoiseReduction(&input_buf->samples[0].real,&mmb.nr_audio_buff[NR.current_buffer_idx].samples[NR_FFT_SIZE].real);
//profileTimedEventStop(ProfileTP8);
NR_out_buffer_add(&mmb.nr_audio_buff[NR.current_buffer_idx]);
NR.current_buffer_idx++;
}
}
/**
* This is an internal function doing the actual noise reduction
* @param inputsamples buffer with input samples
* @param outputsamples buffer with processed output samples
*/
static void AudioNr_RunNoiseReduction(float32_t* inputsamples, float32_t* outputsamples )
{
float32_t* Energy=0;
if(is_dsp_nb_active())
{
alt_noise_blanking(inputsamples,NR_FFT_SIZE,Energy);
}
// if((ts.dsp_active & DSP_NR_ENABLE) || (ts.dsp_active & DSP_NOTCH_ENABLE))
if(is_dsp_nr() && !is_dsp_anr())
{
profileTimedEventStart(ProfileTP8);
/* // spectral_noise_reduction_2(inputsamples);
if (nr_params.mode == 0)
spectral_noise_reduction(inputsamples);
else
if (nr_params.mode == 1)
spectral_noise_reduction_2(inputsamples);
else
*/
spectral_noise_reduction_3(inputsamples);
profileTimedEventStop(ProfileTP8);
}
for (int k=0; k < NR_FFT_SIZE; k++)
{
outputsamples[k] = inputsamples[k];
}
}
// debugging switches
//#define OLD_LONG_TONE_DETECTION
//#define NR_NOTCHTEST
//#define NR_FFT_256 // if you change this, also changnr_params.NR_FFT_LFFT_L in audio_nr.h
// window switches
// choose exactly ONE window
//#define NR_WINDOW_HANN
//#define NR_WINDOW_SIN2
//#define NR_WINDOW_SIN4
//#define NR_WINDOW_HANN_CONST
#if 0
void spectral_noise_reduction (float* in_buffer)
{
// arm_rfft_fast_instance_f32 fftInstance;
////////////////////////////////////////////////////////////////////////////////////////
#ifdef NR_NOTCHTEST
// Result of the test: I cannot find a combination of
// bin gains, where the distortion is away
// the brizzling sound is always there
// more pronounced, when gain combination along the bins is steeper
// bin width is 12000ksps / 128 bins = 93.75Hz
// bin_c = 11 is a notch centred at 10 * 93.75Hz + 93.75Hz / 2 = 984.38 Hz
#ifdef NR_FFT_256
const uint8_t bin_c = 22; // centre_bin to be notched
#else
const uint8_t bin_c = 11; // centre_bin to be notched
#endif
const uint8_t bin_p1 = bin_c + 1;
const uint8_t bin_p2 = bin_c + 2;
const uint8_t bin_p3 = bin_c + 3;
const uint8_t bin_p4 = bin_c + 4;
const uint8_t bin_m1 = bin_c - 1;
const uint8_t bin_m2 = bin_c - 2;
const uint8_t bin_m3 = bin_c - 3;
const uint8_t bin_m4 = bin_c - 4;
const float32_t bin1_att = 0.0; // -24dB
const float32_t bin2_att = 0.15; // -12dB
const float32_t bin3_att = 0.3; // -6.0dB
const float32_t bin4_att = 0.55; // -2.5dB
const float32_t bin5_att = 0.7; // -2.5dB
// const float32_t bin1_att = 0.05; // -26dB
// const float32_t bin2_att = 0.15; // -16.5dB
// const float32_t bin3_att = 0.45; // -10.5dB
// linear gains mean logarithmic bin powers
// the best
// const float32_t bin1_att = 0.001; // -24dB
// const float32_t bin2_att = 0.25; // -12dB
// const float32_t bin3_att = 0.5; // -6.0dB
// const float32_t bin4_att = 0.75; // -2.5dB
// one of the best combinations so far
// const float32_t bin1_att = 0.01; // -40dB
// const float32_t bin2_att = 0.25; // dB
// const float32_t bin3_att = 0.5; // dB
// const float32_t bin4_att = 0.75; // dB
// GOOD!
// const float32_t bin1_att = 0.1; // dB
// const float32_t bin2_att = 0.2; // dB
// const float32_t bin3_att = 0.3; // dB
// const float32_t bin4_att = 0.4; // dB
#endif
////////////////////////////////////////////////////////////////////////////////////////
// Frank DD4WH & Michael DL2FW, November 2017
// NOISE REDUCTION BASED ON SPECTRAL SUBTRACTION
// following Romanin et al. 2009 on the basis of Ephraim & Malah 1984 and Hu et al. 2001
// detailed technical description of the implemented algorithm
// can be found in our WIKI
// https://github.com/df8oe/UHSDR/wiki/Noise-reduction
//
// half-overlapping input buffers (= overlap 50%)
// Hann window on 128 samples
// FFT128 - inverse FFT128
// overlap-add
/* const float32_t v_hann_window[128]= {0 , 0.0006117919, 0.0024456704, 0.0054971478, 0.0097587564 ,0.0152200676, 0.0218677165, 0.0296854352, 0.0386540925,
0.0487517405, 0.0599536686, 0.0722324638 ,0.0855580779, 0.0998979008, 0.1152168405, 0.1314774092, 0.1486398144,
0.1666620568 ,0.1855000331, 0.2051076434, 0.2254369048, 0.2464380681, 0.2680597399, 0.2902490084, 0.3129515727,
0.3361118759, 0.3596732407, 0.3835780087, 0.4077676808, 0.4321830608, 0.4567644003, 0.4814515445, 0.5061840798,
0.5309014817, 0.5555432625, 0.5800491196, 0.6043590832, 0.6284136625, 0.6521539921, 0.6755219754, 0.698460427,
0.7209132127, 0.7428253867, 0.7641433263, 0.7848148629, 0.8047894099, 0.8240180861, 0.8424538357, 0.8600515434,
0.8767681446, 0.8925627311, 0.9073966507, 0.9212336025, 0.9340397251, 0.9457836798, 0.956436727, 0.9659727972,
0.9743685538, 0.981603451, 0.987659784, 0.9925227317, 0.9961803937, 0.9986238192, 0.9998470286, 0.9998470286,
0.9986238192, 0.9961803937, 0.9925227317, 0.987659784, 0.981603451, 0.9743685538, 0.9659727972, 0.956436727,
0.9457836798, 0.9340397251, 0.9212336025, 0.9073966507, 0.8925627311, 0.8767681446, 0.8600515434, 0.8424538357,
0.8240180861, 0.8047894099, 0.7848148629, 0.7641433263, 0.7428253867, 0.7209132127, 0.698460427, 0.6755219754,
0.6521539921, 0.6284136625, 0.6043590832, 0.5800491196, 0.5555432625, 0.5309014817, 0.5061840798, 0.4814515445,
0.4567644003, 0.4321830608, 0.4077676808, 0.3835780087, 0.3596732407, 0.3361118759, 0.3129515727, 0.2902490084,
0.2680597399, 0.2464380681, 0.2254369048, 0.2051076434, 0.1855000331, 0.1666620568, 0.1486398144, 0.1314774092,
0.1152168405, 0.0998979008, 0.0855580779, 0.0722324638, 0.0599536686, 0.0487517405, 0.0386540925, 0.0296854352,
0.0218677165, 0.0152200676, 0.0097587564, 0.0054971478, 0.0024456704, 0.0006117919, 0};
*/
float32_t NR_sample_rate = 12000.0;
if(nr_params.NR_decimation_enable)
{
NR_sample_rate = 6000.0;
}
uint8_t zero_cross_count=0;
bool VAD_ZCR, VAD_EN, VAD_E_Z;
float32_t VAD_E,VAD_energy_ratio;
static uint8_t NR_init_counter = 0;
uint8_t VAD_low=0;
uint8_t VAD_high=63;
float32_t NR_temp_sum = 0.0;
float32_t width = FilterInfo[ts.filters_p->id].width;
float32_t offset = ts.filters_p->offset;
float32_t lf_freq = (offset - width/2) / (NR_sample_rate / nr_params.NR_FFT_L); // bin BW is 93.75Hz [12000Hz / 128 bins]
float32_t uf_freq = (offset + width/2) / (NR_sample_rate / nr_params.NR_FFT_L);
if(nr_params.first_time == 1)
{ // TODO: properly initialize all the variables
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
if (nr_params.mode==0)
{
NR.last_sample_buffer_L[bindx] = 20.0;
NR.Hk_old[bindx] = 0.1; // old gain or xu in development mode
NR.Nest[bindx][0] = 50000.0;
NR.Nest[bindx][1] = 40000.0;
NR2.X[bindx][1] = 15000.0;
NR.SNR_post[bindx] = 2.0;
NR.SNR_prio[bindx] = 1.0;
}
else //development mode
{
NR.last_sample_buffer_L[bindx] = 0.0;
NR2.Hk[bindx] = 1.0;
//xu[bindx] = 1.0; //has to be replaced by other variable
NR2.Hk_old[bindx] = 1.0; // old gain or xu in development mode
NR.Nest[bindx][0] = 0.0;
NR.Nest[bindx][1] = 1.0;
//NR2.X[bindx][1] = 0.0;
//NR.SNR_post[bindx] = 1.0;
//NR.SNR_prio[bindx] = 1.0;
}
}
nr_params.first_time = 2; // we need to do some more a bit later down
}
if(nr_params.long_tone_reset)
{
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR2.long_tone_gain[bindx] = 1.0;
NR2.long_tone_counter[bindx] = 10;
}
nr_params.long_tone_reset = false;
NR2.notch_change = false;
NR2.notch1_bin = 10; // frequency bin where notch filter 1 has to work
NR2.max_bin = 10; // holds the bin number of the strongest persistent tone during tone detection
NR2.long_tone_max = 100.0; // power value of the strongest persistent tone, used for max search
NR2.notch1_active = false; // is notch1 active?
NR2.notch2_active = false; // is notch21 active?
NR2.notch3_active = false; // is notch3 active?
NR2.notch4_active = false; // is notch4 active?
}
for(int k = 0; k < nr_params.NR_FFT_LOOP_NO; k++)
{
// NR_FFT_buffer is 256 floats big
// interleaved r, i, r, i . . .
// fill first half of FFT_buffer with last events audio samples
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.FFT_buffer[i * 2] = NR.last_sample_buffer_L[i]; // real
NR.FFT_buffer[i * 2 + 1] = 0.0; // imaginary
}
// copy recent samples to last_sample_buffer for next time!
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.last_sample_buffer_L [i] = in_buffer[i + k * (nr_params.NR_FFT_L / 2)];
}
// now fill recent audio samples into second half of FFT_buffer
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.FFT_buffer[nr_params.NR_FFT_L + i * 2] = in_buffer[i+ k * (nr_params.NR_FFT_L / 2)]; // real
NR.FFT_buffer[nr_params.NR_FFT_L + i * 2 + 1] = 0.0;
}
/////////////////////////////////7
// WINDOWING
if(nr_params.fft_256_enable)
{
arm_cfft_f32(&arm_cfft_sR_f32_len256, NR.FFT_buffer, 0, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_von_Hann_256[idx];
}
}
else
{
arm_cfft_f32(&arm_cfft_sR_f32_len128, NR.FFT_buffer, 0, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_van_hann[idx];
}
}
#ifdef NR_WINDOW_HANN
// perform windowing on 128 real samples in the NR_FFT_buffer
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{ // Hann window
float32_t temp_sample = 0.5 * (float32_t)(1.0 - (cosf(PI* 2.0 * (float32_t)idx / (float32_t)((nr_params.NR_FFT_L) - 1))));
NR.FFT_buffer[idx * 2] *= temp_sample;
}
#endif
#ifdef NR_WINDOW_SIN2
// perform windowing on 128 real samples in the NR_FFT_buffer
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
// SIN^2 window
float32_t SINF = (sinf(PI * (float32_t)idx / (float32_t)(nr_params.NR_FFT_L - 1)));
SINF = (SINF * SINF);
NR.FFT_buffer[idx * 2] *= SINF;
}
#endif
#ifdef NR_WINDOW_SIN4
// perform windowing on 128 real samples in the NR_FFT_buffer
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
// SIN^2 window
float32_t SINF = (sinf(PI * (float32_t)idx / (float32_t)(nr_params.NR_FFT_L - 1)));
SINF *= SINF;
SINF *= SINF;
NR.FFT_buffer[idx * 2] *= SINF;
}
#endif
#ifndef NR_NOTCHTEST
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
// this is magnitude for the current frame
if (nr_params.mode==0)
NR2.X[bindx][0] = sqrtf(NR.FFT_buffer[bindx * 2] * NR.FFT_buffer[bindx * 2] + NR.FFT_buffer[bindx * 2 + 1] * NR.FFT_buffer[bindx * 2 + 1]);
else //here we need only the squared magnitude
NR2.X[bindx][0] = (NR.FFT_buffer[bindx * 2] * NR.FFT_buffer[bindx * 2] + NR.FFT_buffer[bindx * 2 + 1] * NR.FFT_buffer[bindx * 2 + 1]);
}
if(nr_params.first_time == 2)
{ // TODO: properly initialize all the variables
if (nr_params.mode==1)
{
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR.Nest[bindx][0] = NR.Nest[bindx][0] + 0.05* NR2.X[bindx][0];// we do it 20 times to average over 20 frames for app. 100ms only on NR_on/bandswitch/modeswitch,...
}
NR_init_counter++;
if (NR_init_counter > 19)//average over 20 frames for app. 100ms
{
NR_init_counter = 0;
nr_params.first_time = 3; // now we did all the necessary initialization to actually start the noise reduction
}
}
else nr_params.first_time = 3;
}
if (nr_params.first_time == 3)
{
if((ts.dsp_active & DSP_NOTCH_ENABLE))
{
// detection of long tones
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR2.long_tone[bindx][0] = (nr_params.long_tone_alpha) * NR2.long_tone[bindx][1] + (1.0 - nr_params.long_tone_alpha) * NR2.X[bindx][0]; //
NR2.long_tone[bindx][1] = NR2.long_tone[bindx][0];
}
}
if (nr_params.mode == 1) // in development mode we start here with the calculation of the SNRs
{
// 3 calculate gamma (SNRpost)
// and also xi (SNRprio)
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR.SNR_post[bindx] = fmax(fmin(NR2.X[bindx][0] / NR.Nest[bindx][0],1000.0),0.001); // limited to +30 /-30 dB, might be still too much of reduction, let's try it?
NR.SNR_prio[bindx] = fmax(nr_params.alpha * NR.Hk_old[bindx] + (1.0 - nr_params.alpha)*fmax(NR.SNR_post[bindx]-1.0,0.0),0.0);
}
}
// 2b.) voice activity detector
// we restrict the calculation of the VAD to the bins in the filter bandwidth
// float32_t NR_temp_sum = 0.0;
// float32_t width = FilterInfo[ts.filters_p->id].width;
// float32_t offset = ts.filters_p->offset;
if (offset == 0)
{
offset = width/2;
}
// float32_t lf_freq = (offset - width/2) / (12000 / NR_FFT_L); // bin BW is 93.75Hz [12000Hz / 128 bins]
// float32_t uf_freq = (offset + width/2) / (12000 / NR_FFT_L);
VAD_low = (int)lf_freq;
VAD_high = (int)uf_freq;
if(VAD_low == VAD_high)
{
VAD_high++;
}
if(VAD_low < 1)
{
VAD_low = 1;
}
else
if(VAD_low > nr_params.NR_FFT_L / 2 - 2)
{
VAD_low = nr_params.NR_FFT_L / 2 - 2;
}
if(VAD_high < 1)
{
VAD_high = 1;
}
else
if(VAD_high > nr_params.NR_FFT_L / 2)
{
VAD_high = nr_params.NR_FFT_L / 2;
}
// ******* alternative VAD trials
if (nr_params.mode == 0) //mode "0" is the older version
{
zero_cross_count=0;
for (int i = 1; i < nr_params.NR_FFT_L/2; i++)
{
if ((in_buffer[i + k * (nr_params.NR_FFT_L / 2)] * in_buffer[ i-1 + k * (nr_params.NR_FFT_L / 2)]) < 0)
{
zero_cross_count++; //if product of current sample with last sample is negative, we had a zero crossing
}
} // # of zero crossing tend to be higher at only noise containing frames
if (zero_cross_count > 10)
{
VAD_ZCR = false;
//Board_RedLed(LED_STATE_OFF);
}
else
{
VAD_ZCR = true;
//Board_RedLed(LED_STATE_ON);
}
VAD_E = 0.0;
for(int bindx = VAD_low; bindx < VAD_high; bindx++)
{
if (nr_params.mode == 0) //if mode=1, we need to take the squareroot to have the same performance - will later e fixed
VAD_E = VAD_E + NR2.X[bindx][0];
else
VAD_E = VAD_E + sqrtf(NR2.X[bindx][0]);
}
VAD_energy_ratio = VAD_E / (VAD_high-VAD_low);
if (VAD_energy_ratio > (350 * nr_params.vad_thresh)) //nr_params.vad_thresh is per default 1000/1000!!!
{
VAD_EN=true;
//Board_RedLed(LED_STATE_OFF);
}
else
{
VAD_EN=false;
//Board_RedLed(LED_STATE_ON);
}
if (VAD_EN && VAD_ZCR)
{
VAD_E_Z=true;
//Board_RedLed(LED_STATE_ON);
}
else
{
VAD_E_Z=false;
//Board_RedLed(LED_STATE_OFF);
}
// ******* end of alternative VAD trials
float32_t NR_VAD_temp;
for(int bindx = VAD_low; bindx < VAD_high; bindx++) // try 128:
{
float32_t D_squared = NR.Nest[bindx][0] * NR.Nest[bindx][0];
// NR_temp_sum += (NR_X[bindx][0]/ (D_squared) ) - logf((NR_X[bindx][0] / (D_squared) )) - 1.0; // unpredictable behaviour
// NR_temp_sum += (NR_X[bindx][0] * NR_X[bindx][0]/ (D_squared) ) - logf((NR_X[bindx][0] * NR_X[bindx][0] / (D_squared) )) - 1.0; //nice behaviour
NR_temp_sum += (NR2.X[bindx][0] * NR2.X[bindx][0]/ (D_squared) ); // try without log
}
NR.VAD = NR_temp_sum / (VAD_high - VAD_low);
//float32_t NR_VAD_temp;
if(NR2.VAD_type == 0)
{
NR_VAD_temp = NR.VAD;
}
else
if(NR2.VAD_type == 1)
{
NR_VAD_temp = 10.0 * NR.VAD_Esch;
}
else
if(NR2.VAD_type == 2)
{
if (VAD_E_Z == true)
{
NR_VAD_temp = 1000000;// just very high, because we already have a boolean decision
}
else
{
NR_VAD_temp = 0;
}
}
if((NR_VAD_temp < nr_params.vad_thresh) || nr_params.first_time == 2)
{ // VAD has detected NOISE
// noise estimation with exponential averager
NR2.VAD_duration=0;
NR2.VAD_crash_detector = 0;
if (NR2.VAD_delay == 0) //update noise level after Speech is really over
{
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{ // exponential averager for current noise estimate
if (nr_params.mode == 0)
{
NR.Nest[bindx][0] = (1.0 - nr_params.beta) * NR2.X[bindx][0] + nr_params.beta * NR.Nest[bindx][1]; //
NR.Nest[bindx][1] = NR.Nest[bindx][0];
}
else
{
NR.Nest[bindx][0] = NR.Nest[bindx][0] * nr_params.beta + (1.0 - nr_params.beta) * NR2.X[bindx][0]; //already squared!
}
}
Board_RedLed(LED_STATE_OFF);
}
else // we wait a little until the last vowel has vanished
{
if (NR2.VAD_delay > 0) NR2.VAD_delay--;
}
}
else // VAD has detected speech
{
NR2.VAD_crash_detector++;
NR2.VAD_duration++;
if (NR2.VAD_duration > 1) //a vowel should be longer than app. 20ms
{
NR2.VAD_delay = nr_params.vad_delay; // we wait 1 times app. 10ms before we start updating the noisefloor
Board_RedLed(LED_STATE_ON);
}
}
// this helps to get the noise estimate out of a deep depression
// sometimes the VAD is not possible to become lower than VAD_thresh, because Nest is too small
// this helps avoiding this
if(NR2.VAD_crash_detector > 100)
{
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{ // increase noise estimate
NR.Nest[bindx][0] = NR2.X[bindx][0] * 1.2; //
NR.Nest[bindx][1] = NR.Nest[bindx][0];
}
}
// 3 calculate SNRpost (n, bin[i]) = (X(n, bin[i])^2 / Nest(n, bin[i])^2) - 1 (eq. 13 of Schmitt et al. 2002)
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
// (Yk)^2 / Dk (eq 11, Romanin et al. 2009)
if(NR.Nest[bindx][0] != 0.0)
{ // do we have to square the noise estimate NR_Nest[bindx] or not? Schmitt says yes, Romanin says no . . .
// NR_SNR_post[bindx] = NR_X[bindx][0] / (NR_Nest[bindx][0] * NR_Nest[bindx][0]); // audio crushed
NR.SNR_post[bindx] = NR2.X[bindx][0] / (NR.Nest[bindx][0]); //
}
// "half-wave rectification" of NR_SR_post_pos --> always >= 0
if(NR.SNR_post[bindx] >= 0.0)
{
NR.SNR_post_pos = NR.SNR_post[bindx];
}
else
{
NR.SNR_post_pos = 0.0;
}
// 3 calculate SNRprio (n, bin[i]) = (1 - alpha) * Q(SNRpost(n, bin[i]) + alpha * (Hk(n - 1, bin[i]) * X(n - 1, bin[i])^2 / Nest(n, bin[i])^2 (eq. 14 of Schmitt et al. 2002, eq. 13 of Romanin et al. 2009) [Q[x] = x if x>=0, else Q[x] = 0]
// again: do we have to square the noise estimate NR_M[bindx] or not? Schmitt says yes, Romanin says no . . .
if(NR.Nest[bindx][0] != 0.0)
{
NR.SNR_prio[bindx] = (1.0 - nr_params.alpha) * NR.SNR_post_pos +
// nr_params.alpha * ((NR_Hk_old[bindx] * NR_Hk_old[bindx] * NR_X[bindx][1]) / (NR_Nest[bindx][0])); // no NR effect
// nr_params.alpha * ((NR_Hk_old[bindx] * NR_Hk_old[bindx] * NR_X[bindx][1] * NR_X[bindx][1]) / (NR_Nest[bindx][0])); // no NR effect
// nr_params.alpha * ((NR_Hk_old[bindx] * NR_Hk_old[bindx] * NR_X[bindx][1] * NR_X[bindx][1]) / (NR_Nest[bindx][0] * NR_Nest[bindx][0])); // very strong, but strange effect
nr_params.alpha * ((NR.Hk_old[bindx] * NR.Hk_old[bindx] * NR2.X[bindx][1]) / (NR.Nest[bindx][0])); // working
}
// 4 calculate vk = SNRprio(n, bin[i]) / (SNRprio(n, bin[i]) + 1) * SNRpost(n, bin[i]) (eq. 12 of Schmitt et al. 2002, eq. 9 of Romanin et al. 2009)
NR.vk = NR.SNR_post[bindx] * NR.SNR_prio[bindx] / (1.0 + NR.SNR_prio[bindx]);
// calculate Hk
// 5 finally calculate the weighting function for each bin: Hk(n, bin[i]) = 1 / SNRpost(n, [i]) * sqrtf(0.7212 * vk + vk * vk) (eq. 26 of Romanin et al. 2009)
if(NR.vk > 0.0 && NR.SNR_post[bindx] != 0.0) // prevent sqrtf of negatives
{
NR2.Hk[bindx] = 1.0 / NR.SNR_post[bindx] * sqrtf(0.7212 * NR.vk + NR.vk * NR.vk);
}
else
{
NR2.Hk[bindx] = 1.0;
}
if(!(ts.dsp_active & DSP_NR_ENABLE)) // if NR is not enabled (but notch is enabled !)
{
NR2.Hk[bindx] = 1.0;
}
NR.Hk_old[bindx] = NR2.Hk[bindx];
NR2.X[bindx][1] = NR2.X[bindx][0];
}
}
else //new mode under development, following the same equations, sligthly different implemented
{
// ***** Starting with the voice activity detection **************
float32_t frame_speech=0.0;
for(int bindx = VAD_low; bindx < VAD_high; bindx++) // maybe we should limit this to the signal containing bins (filtering!!)
{
frame_speech = frame_speech + (NR.SNR_post[bindx] * NR.SNR_prio[bindx]/(1.0 + NR.SNR_prio[bindx]))
- logf(1.0 + NR.SNR_prio[bindx]); // looks like we had a similar one before, Frank?? ?
}
if (frame_speech < (0.15 * (VAD_high - VAD_low + 1.0)))// then we have noise!!!!
{
Board_RedLed(LED_STATE_OFF); //Noise!
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++) //update the noise estimate
{
NR.Nest[bindx][0] = nr_params.beta * NR.Nest[bindx][0] + (1.0 - nr_params.beta) * NR2.X[bindx][0];
}
}
else
{
Board_RedLed(LED_STATE_ON);//Speech!
}
// 4 calculate v = SNRprio(n, bin[i]) / (SNRprio(n, bin[i]) + 1) * SNRpost(n, bin[i]) (eq. 12 of Schmitt et al. 2002, eq. 9 of Romanin et al. 2009)
// and calculate the HK's
for(int bindx = VAD_low; bindx < VAD_high; bindx++)// maybe we should limit this to the signal containing bins (filtering!!)
{
float32_t v = NR.SNR_prio[bindx] * NR.SNR_post[bindx] / (1.0 + NR.SNR_prio[bindx]);
NR2.Hk[bindx] = 1.0 / NR.SNR_post[bindx] * sqrtf((0.7212 * v + v * v));
NR.Hk_old[bindx] = NR.SNR_post[bindx] * NR2.Hk[bindx] * NR2.Hk[bindx]; //
if(!(ts.dsp_active & DSP_NR_ENABLE)) // if NR is not enabled (but notch is enabled !)
{
NR2.Hk[bindx] = 1.0;
}
}
}
#ifdef OLD_LONG_TONE_DETECTION
if((ts.dsp_active & DSP_NOTCH_ENABLE))
{
// long tone attenuation = automatic notch filter
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
if(NR2.long_tone[bindx][0] > (float32_t)nr_params.long_tone_thresh)
{
NR2.long_tone_gain[bindx] = NR2.long_tone_gain[bindx] * 0.99;
if(bindx != 0)
{
NR2.long_tone_gain[bindx - 1] = NR2.long_tone_gain[bindx - 1] * 0.9995;
if(NR2.long_tone_gain[bindx - 1] < 0.2)
{
NR2.long_tone_gain[bindx - 1] = 0.2;
}
}
else
if(bindx != (nr_params.NR_FFT_L / 2 - 1))
{
NR2.long_tone_gain[bindx + 1] = NR2.long_tone_gain[bindx + 1] * 0.9995;
if(NR2.long_tone_gain[bindx + 1] < 0.2)
{
NR2.long_tone_gain[bindx + 1] = 0.2;
}
}
if(NR2.long_tone_gain[bindx] < 0.05)
{
NR2.long_tone_gain[bindx] = 0.05;
}
}
else
{
NR2.long_tone_gain[bindx] *= 1.01;
if(bindx != 0)
{
NR2.long_tone_gain[bindx - 1] = NR2.long_tone_gain[bindx - 1] * 1.0005;
if(NR2.long_tone_gain[bindx - 1] > 1.0)
{
NR2.long_tone_gain[bindx - 1] = 1.0;
}
}
else
if(bindx != (nr_params.NR_FFT_L / 2 - 1))
{
NR2.long_tone_gain[bindx + 1] = NR2.long_tone_gain[bindx + 1] * 1.0005;
if(NR2.long_tone_gain[bindx + 1] > 1.0)
{
NR2.long_tone_gain[bindx + 1] = 1.0;
}
}
if(NR2.long_tone_gain[bindx] > 1.0)
{
NR2.long_tone_gain[bindx] = 1.0;
}
}
}
}
#else
if((ts.dsp_active & DSP_NOTCH_ENABLE))
{
NR2.notch_change = false;
// long tone attenuation = automatic notch filter
// first version, only one notch implemented - finds the largest persisting signal and notches it with an IIR
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
// if the (strongly time smoothed) signal in a bin exceeds the threshold,
// increase the counter for that bin
if(NR2.long_tone[bindx][0] > (float32_t)nr_params.long_tone_thresh)
{
NR2.long_tone_counter[bindx]++;
}
// if it does not exceed the threshold, decrement its counter
else
{
NR2.long_tone_counter[bindx]--;
}
// care for low counter values
if(NR2.long_tone_counter[bindx] < 1)
{
NR2.long_tone_counter[bindx] = 0;
}
// care for high counter values
else if (NR2.long_tone_counter[bindx] > 200)
{
NR2.long_tone_counter[bindx] = 200;
}
}
// before we look for new notches, we have to care for existing notches
if(NR2.notch1_active == true)
{ // Is notch1 till notchworthy ?
if(NR2.long_tone_counter[NR2.notch1_bin] > 100)
{
}
else
{
// if a notch is no longer notchworthy, switch it off
// and do not switch it on again for at least one second --> set counter to 0
NR2.long_tone_counter[NR2.notch1_bin] = 0;
NR2.notch1_active = false;
NR2.notch_change = true;
}
}
// set max long tone to zero for maximum search
NR2.long_tone_max = 0.0;
NR2.max_bin = -99; // -99 is the indication for reset value
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
// look for new notches
// if a tone persists strong for at least one second = 100 frames
// its a notchworthy tone
if(NR2.long_tone_counter[bindx] > 100)
{
if(NR2.long_tone[bindx][0] > NR2.long_tone_max)
// find out, if this is the loudest long tone
{
NR2.long_tone_max = NR2.long_tone[bindx][0];
NR2.max_bin = bindx;
}
}
}
if(NR2.max_bin != -99)
{ // yes, we found a notchworthy bin
NR2.notch1_active = true;
NR2.long_tone_counter[NR2.max_bin] = 200; // hysteresis ! This notch will stay at least one second
// was this bin already notched last round?
if(NR2.notch1_bin != NR2.max_bin)
{
NR2.notch1_bin = NR2.max_bin;
NR2.notch_change = true; // indicate a change
}
else
{
}
}
// this activates (and deactivates) the autonotch(es)
if(NR2.notch_change)
{
AudioNr_ActivateAutoNotch(NR2.notch1_bin, NR2.notch1_active);
}
} // END NOTCH_ENABLE
#endif
if(nr_params.gain_smooth_enable)
{
// we hear considerable distortion in the end result
// this can be healed significantly by frequency smoothing the gain values
// this is a trial for smoothing among the gain values
//remark: if we smooth the gains and really modify the HK's like here, the noise reduction algorithm might directly "fight" against this!
// might be better to keep the HK's internaly and smooth a copy of the gains which are than working on the signal.
for(int bindx = 1; bindx < (nr_params.NR_FFT_L / 2) - 1; bindx++)
{
NR2.Hk[bindx] = nr_params.gain_smooth_alpha * NR2.Hk[bindx - 1] + (1.0 - 2.0 * nr_params.gain_smooth_alpha) * NR2.Hk[bindx] + nr_params.gain_smooth_alpha * NR2.Hk[bindx + 1];
}
NR2.Hk[0] = (1.0 - nr_params.gain_smooth_alpha) * NR2.Hk[0] + nr_params.gain_smooth_alpha * NR2.Hk[1];
NR2.Hk[(nr_params.NR_FFT_L / 2) - 1] = (1.0 - nr_params.gain_smooth_alpha) * NR2.Hk[(nr_params.NR_FFT_L / 2) - 1] + nr_params.gain_smooth_alpha * NR2.Hk[(nr_params.NR_FFT_L / 2) - 2];
}
} //end of "if nr_params.first_time == 3"
#if 1
// FINAL SPECTRAL WEIGHTING: Multiply current FFT results with NR_FFT_buffer for 64 bins with the 64 bin-specific gain factors
// only do this for the bins inside the filter passband
// if you do this for all the bins, you will get distorted audio: plopping !
// for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++) // plopping !!!!
for(int bindx = VAD_low; bindx < VAD_high; bindx++) // no plopping
{
NR.FFT_buffer[bindx * 2] = NR.FFT_buffer [bindx * 2] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // real part
NR.FFT_buffer[bindx * 2 + 1] = NR.FFT_buffer [bindx * 2 + 1] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // imag part
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // real part conjugate symmetric
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // imag part conjugate symmetric
}
#endif
// this is another VAD
// following Esh & Vary 2009
// https://pdfs.semanticscholar.org/8fdb/fef3bac889a4f7f5ac382377fa7e1a2b8a7f.pdf
if(NR2.VAD_type == 1)
{
NR_temp_sum = 0;
float32_t NR_temp_sum2 = 0;
for(int bindx = VAD_low; bindx < VAD_high; bindx++)
{
NR_temp_sum += NR2.Hk[bindx] * NR2.X[bindx][0];
NR_temp_sum2 += NR2.X[bindx][0];
}
NR.VAD_Esch = NR_temp_sum / NR_temp_sum2;
}
#endif
/*****************************************************************
* NOISE REDUCTION CODE ENDS HERE
*****************************************************************/
// very interesting!
// if I leave the FFT_buffer as is and just multiply the 19 bins below with 0.1, the audio
// is distorted a little bit !
// To me, this is an indicator of a problem with windowing . . .
// OR: smooth the bin gains by frequency, because the problem could be that one bin has gain 1.0 and
// the adjacent bin has gain 0.1 --> a 20dB difference!
#if 0
for(int bindx = 1; bindx < 20; bindx++)
// bins 2 to 29 attenuated
// set real values to 0.1 of their original value
{
NR.FFT_buffer[bindx * 2] *= 0.1;
// NR_FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] *= 0.1; //NR_iFFT_buffer[idx] * 0.1;
NR.FFT_buffer[bindx * 2 + 1] *= 0.1; //NR_iFFT_buffer[idx] * 0.1;
// NR_FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] *= 0.1; //NR_iFFT_buffer[idx] * 0.1;
}
#endif
#ifdef NR_NOTCHTEST // this is a test of a smoother notch filter
// centre bin to be notched
NR.FFT_buffer[ bin_c * 2] *= bin1_att; // real
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_c * 2 - 2] *= bin1_att; // imaginary
NR.FFT_buffer[ bin_c * 2 + 1] *= bin1_att; // real conjugate symmetric
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_c * 2 - 1] *= bin1_att; // imaginary conjugate symmetric
// centre_bin + 1 to be notched
NR.FFT_buffer[ bin_p1 * 2] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p1 * 2 - 2] *= bin2_att;
NR.FFT_buffer[ bin_p1 * 2 + 1] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p1 * 2 - 1] *= bin2_att;
// centre_bin - 1 to be notched
NR.FFT_buffer[ bin_m1 * 2] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m1 * 2 - 2] *= bin2_att;
NR.FFT_buffer[ bin_m1 * 2 + 1] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m1 * 2 - 1] *= bin2_att;
// centre_bin + 2 to be notched
NR.FFT_buffer[ bin_p2 * 2] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p2 * 2 - 2] *= bin3_att;
NR.FFT_buffer[ bin_p2 * 2 + 1] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p2 * 2 - 1] *= bin3_att;
// centre_bin - 2 to be notched
NR.FFT_buffer[ bin_m2 * 2] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m2 * 2 - 2] *= bin3_att;
NR.FFT_buffer[ bin_m2 * 2 + 1] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m2 * 2 - 1] *= bin3_att;
// centre_bin + 3 to be notched
NR.FFT_buffer[ bin_p3 * 2] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p3 * 2 - 2] *= bin4_att;
NR.FFT_buffer[ bin_p3 * 2 + 1] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p3 * 2 - 1] *= bin4_att;
// centre_bin - 3 to be notched
NR.FFT_buffer[ bin_m3 * 2] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m3 * 2 - 2] *= bin4_att;
NR.FFT_buffer[ bin_m3 * 2 + 1] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m3 * 2 - 1] *= bin4_att;
// centre_bin + 4 to be notched
NR.FFT_buffer[ bin_p4 * 2] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p4 * 2 - 2] *= bin5_att;
NR.FFT_buffer[ bin_p4 * 2 + 1] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p4 * 2 - 1] *= bin5_att;
// centre_bin - 4 to be notched
NR.FFT_buffer[ bin_m4 * 2] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m4 * 2 - 2] *= bin5_att;
NR.FFT_buffer[ bin_m4 * 2 + 1] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m4 * 2 - 1] *= bin5_att;
#endif
// Window on exit!
if(nr_params.fft_256_enable)
{
arm_cfft_f32(&arm_cfft_sR_f32_len256, NR.FFT_buffer, 1, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_von_Hann_256[idx];
}
}
else
{
arm_cfft_f32(&arm_cfft_sR_f32_len128, NR.FFT_buffer, 1, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_van_hann[idx];
}
}
// do the overlap & add
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{ // take real part of first half of current iFFT result and add to 2nd half of last iFFT_result
// NR_output_audio_buffer[i + k * (nr_params.NR_FFT_L / 2)] = NR_FFT_buffer[i * 2] + NR_last_iFFT_result[i];
in_buffer[i + k * (nr_params.NR_FFT_L / 2)] = NR.FFT_buffer[i * 2] + NR.last_iFFT_result[i];
// FIXME: take out scaling !
// in_buffer[i + k * (nr_params.NR_FFT_L / 2)] *= 0.3;
}
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.last_iFFT_result[i] = NR.FFT_buffer[nr_params.NR_FFT_L + i * 2];
}
// end of "for" loop which repeats the FFT_iFFT_chain two times !!!
}
// IIR biquad notch filter with four independent notches
// arm_biquad_cascade_df1_f32 (&NR_notch_biquad, in_buffer, in_buffer, nr_params.NR_FFT_L);
}
void spectral_noise_reduction_2 (float* in_buffer)
{
// arm_rfft_fast_instance_f32 fftInstance;
////////////////////////////////////////////////////////////////////////////////////////
// Frank DD4WH & Michael DL2FW, November 2017
// NOISE REDUCTION BASED ON SPECTRAL SUBTRACTION
// following Romanin et al. 2009 on the basis of Ephraim & Malah 1984 and Hu et al. 2001
// detailed technical description of the implemented algorithm
// can be found in our WIKI
// https://github.com/df8oe/UHSDR/wiki/Noise-reduction
//
// half-overlapping input buffers (= overlap 50%)
// Hann window on 128 samples
// FFT128 - inverse FFT128
// overlap-add
float32_t NR_sample_rate = 12000.0;
if(nr_params.NR_decimation_enable)
{
NR_sample_rate = 6000.0;
}
static uint8_t NR_init_counter = 0;
uint8_t VAD_low=0;
uint8_t VAD_high=63;
//float32_t NR_temp_sum = 0.0;
float32_t width = FilterInfo[ts.filters_p->id].width;
float32_t offset = ts.filters_p->offset;
float32_t lf_freq = (offset - width/2) / (NR_sample_rate / nr_params.NR_FFT_L); // bin BW is 93.75Hz [12000Hz / 128 bins]
float32_t uf_freq = (offset + width/2) / (NR_sample_rate / nr_params.NR_FFT_L);
if(nr_params.first_time == 1)
{ // TODO: properly initialize all the variables
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR.last_sample_buffer_L[bindx] = 0.0;
NR2.Hk[bindx] = 1.0;
//xu[bindx] = 1.0; //has to be replaced by other variable
NR.Hk_old[bindx] = 1.0; // old gain or xu in development mode
NR.Nest[bindx][0] = 0.0;
NR.Nest[bindx][1] = 1.0;
}
nr_params.first_time = 2; // we need to do some more a bit later down
}
if(nr_params.long_tone_reset)
{
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR2.long_tone_gain[bindx] = 1.0;
NR2.long_tone_counter[bindx] = 10;
}
nr_params.long_tone_reset = false;
NR2.notch_change = false;
NR2.notch1_bin = 10; // frequency bin where notch filter 1 has to work
NR2.max_bin = 10; // holds the bin number of the strongest persistent tone during tone detection
NR2.long_tone_max = 100.0; // power value of the strongest persistent tone, used for max search
NR2.notch1_active = false; // is notch1 active?
NR2.notch2_active = false; // is notch21 active?
NR2.notch3_active = false; // is notch3 active?
NR2.notch4_active = false; // is notch4 active?
}
for(int k = 0; k < nr_params.NR_FFT_LOOP_NO; k++)
{
// NR_FFT_buffer is 256 floats big
// interleaved r, i, r, i . . .
// fill first half of FFT_buffer with last events audio samples
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.FFT_buffer[i * 2] = NR.last_sample_buffer_L[i]; // real
NR.FFT_buffer[i * 2 + 1] = 0.0; // imaginary
}
// copy recent samples to last_sample_buffer for next time!
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.last_sample_buffer_L [i] = in_buffer[i + k * (nr_params.NR_FFT_L / 2)];
}
// now fill recent audio samples into second half of FFT_buffer
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.FFT_buffer[nr_params.NR_FFT_L + i * 2] = in_buffer[i+ k * (nr_params.NR_FFT_L / 2)]; // real
NR.FFT_buffer[nr_params.NR_FFT_L + i * 2 + 1] = 0.0;
}
if(nr_params.fft_256_enable)
{
arm_cfft_f32(&arm_cfft_sR_f32_len256, NR.FFT_buffer, 0, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_von_Hann_256[idx];
}
}
else
{
arm_cfft_f32(&arm_cfft_sR_f32_len128, NR.FFT_buffer, 0, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_van_hann[idx];
}
}
#ifndef NR_NOTCHTEST
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
// this is magnitude for the current frame
if (nr_params.mode==0)
NR2.X[bindx][0] = sqrtf(NR.FFT_buffer[bindx * 2] * NR.FFT_buffer[bindx * 2] + NR.FFT_buffer[bindx * 2 + 1] * NR.FFT_buffer[bindx * 2 + 1]);
else //here we need only the squared magnitude
NR2.X[bindx][0] = (NR.FFT_buffer[bindx * 2] * NR.FFT_buffer[bindx * 2] + NR.FFT_buffer[bindx * 2 + 1] * NR.FFT_buffer[bindx * 2 + 1]);
}
if(nr_params.first_time == 2)
{ // TODO: properly initialize all the variables
if (nr_params.mode==1)
{
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR.Nest[bindx][0] = NR.Nest[bindx][0] + 0.05* NR2.X[bindx][0];// we do it 20 times to average over 20 frames for app. 100ms only on NR_on/bandswitch/modeswitch,...
}
NR_init_counter++;
if (NR_init_counter > 19)//average over 20 frames for app. 100ms
{
NR_init_counter = 0;
nr_params.first_time = 3; // now we did all the necessary initialization to actually start the noise reduction
}
}
else nr_params.first_time = 3;
}
if (nr_params.first_time == 3)
{
if((ts.dsp_active & DSP_NOTCH_ENABLE))
{
// detection of long tones
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR2.long_tone[bindx][0] = (nr_params.long_tone_alpha) * NR2.long_tone[bindx][1] + (1.0 - nr_params.long_tone_alpha) * NR2.X[bindx][0]; //
NR2.long_tone[bindx][1] = NR2.long_tone[bindx][0];
}
}
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)// 1. Step of NR - calculate the SNR's
{
NR.SNR_post[bindx] = fmax(fmin(NR2.X[bindx][0] / NR.Nest[bindx][0],1000.0),0.03); // limited to +30 /-15 dB, might be still too much of reduction, let's try it?
NR.SNR_prio[bindx] = fmax(nr_params.alpha * NR.Hk_old[bindx] + (1.0 - nr_params.alpha)*fmax(NR.SNR_post[bindx]-1.0,0.0),0.0);
}
if (offset == 0)
{
offset = width/2;
}
VAD_low = (int)lf_freq;
VAD_high = (int)uf_freq;
if(VAD_low == VAD_high)
{
VAD_high++;
}
if(VAD_low < 1)
{
VAD_low = 1;
}
else
if(VAD_low > nr_params.NR_FFT_L / 2 - 2)
{
VAD_low = nr_params.NR_FFT_L / 2 - 2;
}
if(VAD_high < 1)
{
VAD_high = 1;
}
else
if(VAD_high > nr_params.NR_FFT_L / 2)
{
VAD_high = nr_params.NR_FFT_L / 2;
}
// ******* alternative VAD trials
//new mode under development, following the same equations, sligthly different implemented
// ***** Starting with the voice activity detection **************
float32_t frame_speech=0.0;
for(int bindx = VAD_low; bindx < VAD_high; bindx++) // maybe we should limit this to the signal containing bins (filtering!!)
{
frame_speech = frame_speech + (NR.SNR_post[bindx] * NR.SNR_prio[bindx]/(1.0 + NR.SNR_prio[bindx]))
- logf(1.0 + NR.SNR_prio[bindx]); // looks like we had a similar one before, Frank?? ?
}
if (frame_speech < (0.15 * (VAD_high - VAD_low + 1.0)))// then we have noise!!!!
{
NR2.VAD_duration = 0;
if (NR2.VAD_delay > 0)
{
NR2.VAD_delay--;
}
else
{
Board_RedLed(LED_STATE_OFF); //Noise!
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++) //update the noise estimate in dpi
{
NR.Nest[bindx][0] = NR.Nest[bindx][0] * nr_params.beta + (1.0 - nr_params.beta) * NR2.X[bindx][0];
}
}
}
else
{
NR2.VAD_duration++;
if (NR2.VAD_duration > 1)
{
NR2.VAD_delay=nr_params.vad_delay;
Board_RedLed(LED_STATE_ON);//Speech!
}
}
// 4 calculate v = SNRprio(n, bin[i]) / (SNRprio(n, bin[i]) + 1) * SNRpost(n, bin[i]) (eq. 12 of Schmitt et al. 2002, eq. 9 of Romanin et al. 2009)
// and calculate the HK's
for(int bindx = VAD_low; bindx < VAD_high; bindx++)// maybe we should limit this to the signal containing bins (filtering!!)
{
float32_t v = NR.SNR_prio[bindx] * NR.SNR_post[bindx] / (1.0 + NR.SNR_prio[bindx]);
NR2.Hk[bindx] = 1.0 / NR.SNR_post[bindx] * sqrtf((0.7212 * v + v * v));
NR.Hk_old[bindx] = NR.SNR_post[bindx] * NR2.Hk[bindx] * NR2.Hk[bindx]; //
if(!(ts.dsp_active & DSP_NR_ENABLE)) // if NR is not enabled (but notch is enabled !)
{
NR2.Hk[bindx] = 1.0;
}
}
#ifdef OLD_LONG_TONE_DETECTION
if((ts.dsp_active & DSP_NOTCH_ENABLE))
{
// long tone attenuation = automatic notch filter
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
if(NR2.long_tone[bindx][0] > (float32_t)nr_params.long_tone_thresh)
{
NR2.long_tone_gain[bindx] = NR2.long_tone_gain[bindx] * 0.99;
if(bindx != 0)
{
NR2.long_tone_gain[bindx - 1] = NR2.long_tone_gain[bindx - 1] * 0.9995;
if(NR2.long_tone_gain[bindx - 1] < 0.2)
{
NR2.long_tone_gain[bindx - 1] = 0.2;
}
}
else
if(bindx != (nr_params.NR_FFT_L / 2 - 1))
{
NR2.long_tone_gain[bindx + 1] = NR2.long_tone_gain[bindx + 1] * 0.9995;
if(NR2.long_tone_gain[bindx + 1] < 0.2)
{
NR2.long_tone_gain[bindx + 1] = 0.2;
}
}
if(NR2.long_tone_gain[bindx] < 0.05)
{
NR2.long_tone_gain[bindx] = 0.05;
}
}
else
{
NR2.long_tone_gain[bindx] *= 1.01;
if(bindx != 0)
{
NR2.long_tone_gain[bindx - 1] = NR2.long_tone_gain[bindx - 1] * 1.0005;
if(NR2.long_tone_gain[bindx - 1] > 1.0)
{
NR2.long_tone_gain[bindx - 1] = 1.0;
}
}
else
if(bindx != (nr_params.NR_FFT_L / 2 - 1))
{
NR2.long_tone_gain[bindx + 1] = NR2.long_tone_gain[bindx + 1] * 1.0005;
if(NR2.long_tone_gain[bindx + 1] > 1.0)
{
NR2.long_tone_gain[bindx + 1] = 1.0;
}
}
if(NR2.long_tone_gain[bindx] > 1.0)
{
NR2.long_tone_gain[bindx] = 1.0;
}
}
}
}
#else
if((ts.dsp_active & DSP_NOTCH_ENABLE))
{
NR2.notch_change = false;
// long tone attenuation = automatic notch filter
// first version, only one notch implemented - finds the largest persisting signal and notches it with an IIR
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
// if the (strongly time smoothed) signal in a bin exceeds the threshold,
// increase the counter for that bin
if(NR2.long_tone[bindx][0] > (float32_t)nr_params.long_tone_thresh)
{
NR2.long_tone_counter[bindx]++;
}
// if it does not exceed the threshold, decrement its counter
else
{
NR2.long_tone_counter[bindx]--;
}
// care for low counter values
if(NR2.long_tone_counter[bindx] < 1)
{
NR2.long_tone_counter[bindx] = 0;
}
// care for high counter values
else if (NR2.long_tone_counter[bindx] > 200)
{
NR2.long_tone_counter[bindx] = 200;
}
}
// before we look for new notches, we have to care for existing notches
if(NR2.notch1_active == true)
{ // Is notch1 till notchworthy ?
if(NR2.long_tone_counter[NR2.notch1_bin] > 100)
{
}
else
{
// if a notch is no longer notchworthy, switch it off
// and do not switch it on again for at least one second --> set counter to 0
NR2.long_tone_counter[NR2.notch1_bin] = 0;
NR2.notch1_active = false;
NR2.notch_change = true;
}
}
// set max long tone to zero for maximum search
NR2.long_tone_max = 0.0;
NR2.max_bin = -99; // -99 is the indication for reset value
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
// look for new notches
// if a tone persists strong for at least one second = 100 frames
// its a notchworthy tone
if(NR2.long_tone_counter[bindx] > 100)
{
if(NR2.long_tone[bindx][0] > NR2.long_tone_max)
// find out, if this is the loudest long tone
{
NR2.long_tone_max = NR2.long_tone[bindx][0];
NR2.max_bin = bindx;
}
}
}
if(NR2.max_bin != -99)
{ // yes, we found a notchworthy bin
NR2.notch1_active = true;
NR2.long_tone_counter[NR2.max_bin] = 200; // hysteresis ! This notch will stay at least one second
// was this bin already notched last round?
if(NR2.notch1_bin != NR2.max_bin)
{
NR2.notch1_bin = NR2.max_bin;
NR2.notch_change = true; // indicate a change
}
else
{
}
}
// this activates (and deactivates) the autonotch(es)
if(NR2.notch_change)
{
AudioNr_ActivateAutoNotch(NR2.notch1_bin, NR2.notch1_active);
}
} // END NOTCH_ENABLE
#endif
if(nr_params.gain_smooth_enable)
{
// we hear considerable distortion in the end result
// this can be healed significantly by frequency smoothing the gain values
// this is a trial for smoothing among the gain values
//remark: if we smooth the gains and really modify the HK's like here, the noise reduction algorithm might directly "fight" against this!
// might be better to keep the HK's internaly and smooth a copy of the gains which are than working on the signal.
for(int bindx = 1; bindx < (nr_params.NR_FFT_L / 2) - 1; bindx++)
{
NR2.Hk[bindx] = nr_params.gain_smooth_alpha * NR2.Hk[bindx - 1] + (1.0 - 2.0 * nr_params.gain_smooth_alpha) * NR2.Hk[bindx] + nr_params.gain_smooth_alpha * NR2.Hk[bindx + 1];
}
NR2.Hk[0] = (1.0 - nr_params.gain_smooth_alpha) * NR2.Hk[0] + nr_params.gain_smooth_alpha * NR2.Hk[1];
NR2.Hk[(nr_params.NR_FFT_L / 2) - 1] = (1.0 - nr_params.gain_smooth_alpha) * NR2.Hk[(nr_params.NR_FFT_L / 2) - 1] + nr_params.gain_smooth_alpha * NR2.Hk[(nr_params.NR_FFT_L / 2) - 2];
}
} //end of "if nr_params.first_time == 3"
// FINAL SPECTRAL WEIGHTING: Multiply current FFT results with NR_FFT_buffer for 64 bins with the 64 bin-specific gain factors
// only do this for the bins inside the filter passband
// if you do this for all the bins, you will get distorted audio: plopping !
// for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++) // plopping !!!!
for(int bindx = VAD_low; bindx < VAD_high; bindx++) // no plopping
{
NR.FFT_buffer[bindx * 2] = NR.FFT_buffer [bindx * 2] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // real part
NR.FFT_buffer[bindx * 2 + 1] = NR.FFT_buffer [bindx * 2 + 1] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // imag part
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // real part conjugate symmetric
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // imag part conjugate symmetric
}
#endif
/*****************************************************************
* NOISE REDUCTION CODE ENDS HERE
*****************************************************************/
// very interesting!
// if I leave the FFT_buffer as is and just multiply the 19 bins below with 0.1, the audio
// is distorted a little bit !
// To me, this is an indicator of a problem with windowing . . .
// OR: smooth the bin gains by frequency, because the problem could be that one bin has gain 1.0 and
// the adjacent bin has gain 0.1 --> a 20dB difference!
#if 0
for(int bindx = 1; bindx < 20; bindx++)
// bins 2 to 29 attenuated
// set real values to 0.1 of their original value
{
NR.FFT_buffer[bindx * 2] *= 0.1;
// NR_FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] *= 0.1; //NR_iFFT_buffer[idx] * 0.1;
NR.FFT_buffer[bindx * 2 + 1] *= 0.1; //NR_iFFT_buffer[idx] * 0.1;
// NR_FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] *= 0.1; //NR_iFFT_buffer[idx] * 0.1;
}
#endif
#ifdef NR_NOTCHTEST // this is a test of a smoother notch filter
// centre bin to be notched
NR.FFT_buffer[ bin_c * 2] *= bin1_att; // real
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_c * 2 - 2] *= bin1_att; // imaginary
NR.FFT_buffer[ bin_c * 2 + 1] *= bin1_att; // real conjugate symmetric
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_c * 2 - 1] *= bin1_att; // imaginary conjugate symmetric
// centre_bin + 1 to be notched
NR.FFT_buffer[ bin_p1 * 2] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p1 * 2 - 2] *= bin2_att;
NR.FFT_buffer[ bin_p1 * 2 + 1] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p1 * 2 - 1] *= bin2_att;
// centre_bin - 1 to be notched
NR.FFT_buffer[ bin_m1 * 2] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m1 * 2 - 2] *= bin2_att;
NR.FFT_buffer[ bin_m1 * 2 + 1] *= bin2_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m1 * 2 - 1] *= bin2_att;
// centre_bin + 2 to be notched
NR.FFT_buffer[ bin_p2 * 2] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p2 * 2 - 2] *= bin3_att;
NR.FFT_buffer[ bin_p2 * 2 + 1] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p2 * 2 - 1] *= bin3_att;
// centre_bin - 2 to be notched
NR.FFT_buffer[ bin_m2 * 2] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m2 * 2 - 2] *= bin3_att;
NR.FFT_buffer[ bin_m2 * 2 + 1] *= bin3_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m2 * 2 - 1] *= bin3_att;
// centre_bin + 3 to be notched
NR.FFT_buffer[ bin_p3 * 2] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p3 * 2 - 2] *= bin4_att;
NR.FFT_buffer[ bin_p3 * 2 + 1] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p3 * 2 - 1] *= bin4_att;
// centre_bin - 3 to be notched
NR.FFT_buffer[ bin_m3 * 2] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m3 * 2 - 2] *= bin4_att;
NR.FFT_buffer[ bin_m3 * 2 + 1] *= bin4_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m3 * 2 - 1] *= bin4_att;
// centre_bin + 4 to be notched
NR.FFT_buffer[ bin_p4 * 2] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p4 * 2 - 2] *= bin5_att;
NR.FFT_buffer[ bin_p4 * 2 + 1] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_p4 * 2 - 1] *= bin5_att;
// centre_bin - 4 to be notched
NR.FFT_buffer[ bin_m4 * 2] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m4 * 2 - 2] *= bin5_att;
NR.FFT_buffer[ bin_m4 * 2 + 1] *= bin5_att;
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bin_m4 * 2 - 1] *= bin5_att;
#endif
// Window on exit!
if(nr_params.fft_256_enable)
{
arm_cfft_f32(&arm_cfft_sR_f32_len256, NR.FFT_buffer, 1, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_von_Hann_256[idx];
}
}
else
{
arm_cfft_f32(&arm_cfft_sR_f32_len128, NR.FFT_buffer, 1, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_van_hann[idx];
}
}
// do the overlap & add
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{ // take real part of first half of current iFFT result and add to 2nd half of last iFFT_result
// NR_output_audio_buffer[i + k * (nr_params.NR_FFT_L / 2)] = NR_FFT_buffer[i * 2] + NR_last_iFFT_result[i];
in_buffer[i + k * (nr_params.NR_FFT_L / 2)] = NR.FFT_buffer[i * 2] + NR.last_iFFT_result[i];
// FIXME: take out scaling !
// in_buffer[i + k * (nr_params.NR_FFT_L / 2)] *= 0.3;
}
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.last_iFFT_result[i] = NR.FFT_buffer[nr_params.NR_FFT_L + i * 2];
}
// end of "for" loop which repeats the FFT_iFFT_chain two times !!!
}
// IIR biquad notch filter with four independent notches
// arm_biquad_cascade_df1_f32 (&NR_notch_biquad, in_buffer, in_buffer, nr_params.NR_FFT_L);
}
#endif
void spectral_noise_reduction_3 (float* in_buffer)
{
////////////////////////////////////////////////////////////////////////////////////////
// Frank DD4WH & Michael DL2FW, November 2017
// NOISE REDUCTION BASED ON SPECTRAL SUBTRACTION
// following Romanin et al. 2009 on the basis of Ephraim & Malah 1984 and Hu et al. 2001
// detailed technical description of the implemented algorithm
// can be found in our WIKI
// https://github.com/df8oe/UHSDR/wiki/Noise-reduction
//
// half-overlapping input buffers (= overlap 50%)
// Hann window on 128 or 256 samples
// FFT128 - inverse FFT128 or FFT256 / iFFT256
// overlap-add
const float32_t width = FilterInfo[ts.filters_p->id].width;
const float32_t offset = ts.filters_p->offset;
float32_t NR_sample_rate = nr_params.NR_decimation_active? 6000.0: 12000.0;
static uint8_t NR_init_counter = 0;
uint8_t VAD_low=0;
uint8_t VAD_high=63;
float32_t lf_freq = (offset - width/2) / (NR_sample_rate / nr_params.NR_FFT_L); // bin BW is 93.75Hz [12000Hz / 128 bins]
float32_t uf_freq = (offset + width/2) / (NR_sample_rate / nr_params.NR_FFT_L);
//const float32_t tinc = 0.00533333; // frame time 5.3333ms
//const float32_t tax=0.071; // noise output smoothing time constant - absolut value in seconds
//const float32_t tap=0.152; // speech prob smoothing time constant - absolut value in seconds
const float32_t psthr=0.99; // threshold for smoothed speech probability [0.99]
const float32_t pnsaf=0.01; // noise probability safety value [0.01]
//const float32_t asnr=15; // active SNR in dB
const float32_t psini=0.5; // initial speech probability [0.5]
const float32_t pspri=0.5; // prior speech probability [0.5]
// for 12ksps and FFT128
//NR2.ax = 0.9276; //expf(-tinc / tax);
//NR2.ap = 0.9655; //expf(-tinc / tap);
// for 6ksps and FFT256
NR2.ax = 0.7405;
NR2.ap = 0.8691;
//NR2.xih1 = 31.62; //powf(10, (float32_t)NR2.asnr / 10.0);
NR2.xih1 = pow10f((float32_t)NR2.asnr / 10.0);
NR2.xih1r = 1.0 / (1.0 + NR2.xih1) - 1.0;
NR2.pfac= (1.0 / pspri - 1.0) * (1.0 + NR2.xih1);
NR2.snr_prio_min = 0.001; //powf(10, - (float32_t)NR2.snr_prio_min_int / 10.0); //range should be down to -30dB min
NR2.power_threshold = (float32_t)(NR2.power_threshold_int)/100.0;
static float32_t pslp[NR_FFT_SIZE];
static float32_t xt[NR_FFT_SIZE];
float32_t xtr;
float32_t ph1y[NR_FFT_SIZE];
//ax and ap adjustment according to FFT-size and decimation
// for 12KS and 128'er FFT we are doing well with the above values
// This table shows the tinc values in ms for the calculation of the coefficients ax and ap
//
// FFT Size: | 128 256
// ______|________________________
// Samplerate: 12KS | 5.333 10.666
// |
// 6KS |10.666 21.333
/*if (nr_params.fft_256_enable && nr_params.NR_decimation_enable)
{
NR2.ax = 0.7405;
NR2.ap = 0.8691;
}
else
if (nr_params.fft_256_enable || nr_params.NR_decimation_enable)
{
NR2.ax = 0.8605;
NR2.ap = 0.9322;
} */
if(nr_params.first_time == 1)
{ // TODO: properly initialize all the variables
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR.last_sample_buffer_L[bindx] = 0.0;
NR2.Hk[bindx] = 1.0;
//xu[bindx] = 1.0; //has to be replaced by other variable
NR.Hk_old[bindx] = 1.0; // old gain or xu in development mode
NR.Nest[bindx][0] = 0.0;
NR.Nest[bindx][1] = 1.0;
pslp[bindx] = 0.5;
// NR2.long_tone_gain[bindx] = 1.0;
}
nr_params.first_time = 2; // we need to do some more a bit later down
}
for(int k = 0; k < nr_params.NR_FFT_LOOP_NO; k++)
{
// NR_FFT_buffer is 256 floats big
// interleaved r, i, r, i . . .
// fill first half of FFT_buffer with last events audio samples
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.FFT_buffer[i * 2] = NR.last_sample_buffer_L[i]; // real
NR.FFT_buffer[i * 2 + 1] = 0.0; // imaginary
}
// copy recent samples to last_sample_buffer for next time!
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.last_sample_buffer_L [i] = in_buffer[i + k * (nr_params.NR_FFT_L / 2)];
}
// now fill recent audio samples into second half of FFT_buffer
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.FFT_buffer[nr_params.NR_FFT_L + i * 2] = in_buffer[i+ k * (nr_params.NR_FFT_L / 2)]; // real
NR.FFT_buffer[nr_params.NR_FFT_L + i * 2 + 1] = 0.0;
}
/////////////////////////////////7
// WINDOWING
// if(nr_params.fft_256_enable)
{
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_von_Hann_256[idx];
}
arm_cfft_f32(&arm_cfft_sR_f32_len256, NR.FFT_buffer, 0, 1);
}
/*else
{
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_van_hann[idx];
}
arm_cfft_f32(&arm_cfft_sR_f32_len128, NR.FFT_buffer, 0, 1);
}*/
// NR_FFT
// calculation is performed in-place the FFT_buffer [re, im, re, im, re, im . . .]
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
//here we need squared magnitude
NR2.X[bindx][0] = (NR.FFT_buffer[bindx * 2] * NR.FFT_buffer[bindx * 2] + NR.FFT_buffer[bindx * 2 + 1] * NR.FFT_buffer[bindx * 2 + 1]);
}
if(nr_params.first_time == 2)
{
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)
{
NR.Nest[bindx][0] = NR.Nest[bindx][0] + 0.05* NR2.X[bindx][0];// we do it 20 times to average over 20 frames for app. 100ms only on NR_on/bandswitch/modeswitch,...
xt[bindx] = psini * NR.Nest[bindx][0];
}
NR_init_counter++;
if (NR_init_counter > 19)//average over 20 frames for app. 100ms
{
NR_init_counter = 0;
nr_params.first_time = 3; // now we did all the necessary initialization to actually start the noise reduction
}
}
if (nr_params.first_time == 3)
{
//new noise estimate MMSE based!!!
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)// 1. Step of NR - calculate the SNR's
{
ph1y[bindx] = 1.0 / (1.0 + NR2.pfac * expf(NR2.xih1r * NR2.X[bindx][0]/xt[bindx]));
pslp[bindx] = NR2.ap * pslp[bindx] + (1.0 - NR2.ap) * ph1y[bindx];
//ph1y[bindx] = fmin(ph1y[bindx], 1.0 - pnsaf * (pslp[bindx] > psthr)); //?????
if (pslp[bindx] > psthr)
{
ph1y[bindx] = 1.0 - pnsaf;
}
else
{
ph1y[bindx] = fmin(ph1y[bindx] , 1.0);
}
xtr = (1.0 - ph1y[bindx]) * NR2.X[bindx][0] + ph1y[bindx] * xt[bindx];
xt[bindx] = NR2.ax * xt[bindx] + (1.0 - NR2.ax) * xtr;
}
for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++)// 1. Step of NR - calculate the SNR's
{
NR.SNR_post[bindx] = fmax(fmin(NR2.X[bindx][0] / xt[bindx],1000.0), NR2.snr_prio_min); // limited to +30 /-15 dB, might be still too much of reduction, let's try it?
NR.SNR_prio[bindx] = fmax(nr_params.alpha * NR.Hk_old[bindx] + (1.0 - nr_params.alpha) * fmax(NR.SNR_post[bindx] - 1.0, 0.0), 0.0);
}
VAD_low = (int)lf_freq;
VAD_high = (int)uf_freq;
if(VAD_low == VAD_high)
{
VAD_high++;
}
if(VAD_low < 1)
{
VAD_low = 1;
}
else
if(VAD_low > nr_params.NR_FFT_L / 2 - 2)
{
VAD_low = nr_params.NR_FFT_L / 2 - 2;
}
if(VAD_high < 1)
{
VAD_high = 1;
}
else
if(VAD_high > nr_params.NR_FFT_L / 2)
{
VAD_high = nr_params.NR_FFT_L / 2;
}
// 4 calculate v = SNRprio(n, bin[i]) / (SNRprio(n, bin[i]) + 1) * SNRpost(n, bin[i]) (eq. 12 of Schmitt et al. 2002, eq. 9 of Romanin et al. 2009)
// and calculate the HK's
for(int bindx = VAD_low; bindx < VAD_high; bindx++)// maybe we should limit this to the signal containing bins (filtering!!)
{
float32_t v = NR.SNR_prio[bindx] * NR.SNR_post[bindx] / (1.0 + NR.SNR_prio[bindx]);
NR2.Hk[bindx] = fmax(1.0 / NR.SNR_post[bindx] * sqrtf((0.7212 * v + v * v)),0.001); //limit HK's to 0.001'
NR.Hk_old[bindx] = NR.SNR_post[bindx] * NR2.Hk[bindx] * NR2.Hk[bindx]; //
/*if(!(ts.dsp_active & DSP_NR_ENABLE)) // if NR is not enabled (but notch is enabled !)
{
NR2.Hk[bindx] = 1.0;
} */
}
// musical noise "artefact" reduction by dynamic averaging - depending on SNR ratio
NR2.pre_power = 0.0;
NR2.post_power = 0.0;
for(int bindx = VAD_low; bindx < VAD_high; bindx++)
{
NR2.pre_power += NR2.X[bindx][0];
NR2.post_power += NR2.Hk[bindx] * NR2.Hk[bindx] * NR2.X[bindx][0];
}
NR2.power_ratio = NR2.post_power / NR2.pre_power;
if (NR2.power_ratio > NR2.power_threshold)
{
NR2.power_ratio = 1.0;
NR2.NN = 1;
}
else
{
NR2.NN = 1 + 2 * (int)(0.5 + NR2.width * (1.0 - NR2.power_ratio / NR2.power_threshold));
}
for(int bindx = VAD_low + NR2.NN/2; bindx < VAD_high - NR2.NN/2; bindx++)
{
NR.Nest[bindx][0] = 0.0;
for(int m = bindx - NR2.NN/2; m <= bindx + NR2.NN/2;m++)
{
NR.Nest[bindx][0] += NR2.Hk[m];
}
NR.Nest[bindx][0] /= (float32_t)NR2.NN;
}
// and now the edges - only going NN steps forward and taking the average
// lower edge
for(int bindx = VAD_low; bindx < VAD_low + NR2.NN/2; bindx++)
{
NR.Nest[bindx][0] = 0.0;
for(int m = bindx; m < (bindx + NR2.NN);m++)
{
NR.Nest[bindx][0] += NR2.Hk[m];
}
NR.Nest[bindx][0] /= (float32_t)NR2.NN;
}
// upper edge - only going NN steps backward and taking the average
for(int bindx = VAD_high - NR2.NN; bindx < VAD_high; bindx++)
{
NR.Nest[bindx][0] = 0.0;
for(int m = bindx; m > (bindx - NR2.NN); m--)
{
NR.Nest[bindx][0] += NR2.Hk[m];
}
NR.Nest[bindx][0] /= (float32_t)NR2.NN;
}
// end of edge treatment
for(int bindx = VAD_low + NR2.NN/2; bindx < VAD_high - NR2.NN/2; bindx++)
{
NR2.Hk[bindx] = NR.Nest[bindx][0];
}
// end of musical noise reduction
} //end of "if nr_params.first_time == 3"
// FINAL SPECTRAL WEIGHTING: Multiply current FFT results with NR_FFT_buffer for 64 bins with the 64 bin-specific gain factors
// only do this for the bins inside the filter passband
// if you do this for all the bins, you will get distorted audio: plopping !
// for(int bindx = 0; bindx < nr_params.NR_FFT_L / 2; bindx++) // plopping !!!!
for(int bindx = VAD_low; bindx < VAD_high; bindx++) // no plopping
{
NR.FFT_buffer[bindx * 2] = NR.FFT_buffer [bindx * 2] * NR2.Hk[bindx]; // real part
NR.FFT_buffer[bindx * 2 + 1] = NR.FFT_buffer [bindx * 2 + 1] * NR2.Hk[bindx]; // imag part
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] * NR2.Hk[bindx]; // real part conjugate symmetric
NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] * NR2.Hk[bindx]; // imag part conjugate symmetric
// NR.FFT_buffer[bindx * 2] = NR.FFT_buffer [bindx * 2] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // real part
// NR.FFT_buffer[bindx * 2 + 1] = NR.FFT_buffer [bindx * 2 + 1] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // imag part
// NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 2] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // real part conjugate symmetric
// NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] = NR.FFT_buffer[nr_params.NR_FFT_L * 2 - bindx * 2 - 1] * NR2.Hk[bindx] * NR2.long_tone_gain[bindx]; // imag part conjugate symmetric
}
/*****************************************************************
* NOISE REDUCTION CODE ENDS HERE
*****************************************************************/
// NR_iFFT
// & Window on exit!
// if(nr_params.fft_256_enable)
{
arm_cfft_f32(&arm_cfft_sR_f32_len256, NR.FFT_buffer, 1, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_von_Hann_256[idx];
}
}
/*else
{
arm_cfft_f32(&arm_cfft_sR_f32_len128, NR.FFT_buffer, 1, 1);
for (int idx = 0; idx < nr_params.NR_FFT_L; idx++)
{
NR.FFT_buffer[idx * 2] *= SQRT_van_hann[idx];
}
}*/
// do the overlap & add
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{ // take real part of first half of current iFFT result and add to 2nd half of last iFFT_result
// NR_output_audio_buffer[i + k * (nr_params.NR_FFT_L / 2)] = NR_FFT_buffer[i * 2] + NR_last_iFFT_result[i];
in_buffer[i + k * (nr_params.NR_FFT_L / 2)] = NR.FFT_buffer[i * 2] + NR.last_iFFT_result[i];
}
for(int i = 0; i < nr_params.NR_FFT_L / 2; i++)
{
NR.last_iFFT_result[i] = NR.FFT_buffer[nr_params.NR_FFT_L + i * 2];
}
// end of "for" loop which repeats the FFT_iFFT_chain two times !!!
}
// IIR biquad notch filter with four independent notches
// arm_biquad_cascade_df1_f32 (&NR_notch_biquad, in_buffer, in_buffer, nr_params.NR_FFT_L);
}
//alt noise blanking is trying to localize some impulse noise within the samples and after that
//trying to replace corrupted samples by linear predicted samples.
//therefore, first we calculate the lpc coefficients which represent the actual status of the
//speech or sound generating "instrument" (in case of speech this is an estimation of the current
//filter-function of the voice generating tract behind our lips :-) )
//after finding this function we inverse filter the actual samples by this function
//so we are eliminating the speech, but not the noise. Then we do a matched filtering an thereby detecting impulses
//After that we threshold the remaining samples by some
//level and so detecting impulse noise's positions within the current frame - if one (or more) impulses are there.
//finally some area around the impulse position will be replaced by predicted samples from both sides (forward and
//backward prediction)
//hopefully we have enough processor power left....
void alt_noise_blanking(float* insamp,int Nsam, int order, float* E ) //Nsam = 128
{
#ifdef debug_alternate_NR
const float32_t NR_test_samp[128] = { 853.472351,629.066223,864.270813,1012.3078,738.378113,
446.268219,635.763123,1062.71118,955.245667,22.6679211,-1130.45386,-1737.12817,
-1728.79114,-1594.82227,-1545.75671,-1208.91003,-252.898315,993.880493,1820.26538,
1915.65186,1597.90259,1248.58838,809.456909,28.6509247,-961.62677,-1604.66443,-1499.18225,
-824.882935,-85.1342163,432.899261,782.52063,1029.38452,1040.57166,692.128662,138.820541,
-286.785767,-420.356415,-384.165161,-348.958527,-308.304718,-171.111633,4.52698851, //last value:4.52698851,
-5.53196001,-368.999939,-1031.19165,-1766.01074,-2290.01587,-2293.98853,-1514.0238,
23.0157223,1797.16394,3018.3894,3231.77148,2702.38745,2085.92676,1685.99255,1145.43176,
-31.9259377,-1722.42847,-3112.2937,-3453.61426,-2790.31763,-1812.12769,-1028.70874,
-1812 ,897.985779,2375.50903,3409.33472,3332.44238,2293.16602,1067.26196,183.806381,
-548.479553,-1549.47034,-2692.18213,-3288.44702,-2873.70239,-1761.34033,-636.71936,
250.664383,1198.7804,2336.43726,3121.80615,2848.64355,1556.67969,110.084801,-724.328186,
-1013.82141,-1265.38879,-1506.06091,-1177.04529,-35.6577721,1209.823,1520.28088,679.406555,
-514.541626,-1245.55945,-1508.29407,-1707.93408,-1736.12427,-965.137085,752.618347,2518.7168,
3185.57031,2563.83838,1472.3927,613.243835,-172.269989,-1311.97058,-2534.06421,-2982.73169,
-2282.05859,-1025.64673,12.714426,809.696228,1828.12854,2977.01709,3388.77612,2460.82178,
751.800781,-567.183105,-1026.46143,-1190.80762,-1635.05701,-2060.84619,-1785.74683,-841.740173,
-62.468441
};
const float32_t NR_test_sinus_samp[128] = {
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302,
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302,
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302,
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302,
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302,
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302,
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302,
0, 765.3668647302, 1414.2135623731, 1847.7590650226, 2000, 1847.7590650226, 1414.2135623731, 765.3668647302,
0, -765.3668647302, -1414.2135623731, -1847.7590650226, -2000, -1847.7590650226, -1414.2135623731, -765.3668647302
};
#endif
#define boundary_blank 14 // for first trials very large!!!!
#define impulse_length 7 // has to be odd!!!! 7 / 3 should be enough
#define PL 3 // has to be (impulse_length-1)/2 !!!!
#define order 10 // lpc's order
arm_fir_instance_f32 LPC;
float32_t lpcs[order+1]; // we reserve one more than "order" because of a leading "1"
float32_t reverse_lpcs[order+1]; //this takes the reversed order lpc coefficients
float32_t firStateF32[NR_FFT_SIZE + order];
float32_t tempsamp[NR_FFT_SIZE];
float32_t sigma2; //taking the variance of the inpo
float32_t lpc_power;
float32_t impulse_threshold;
int impulse_positions[5]; //we allow a maximum of 5 impulses per frame
int search_pos=0;
int impulse_count=0;
//static float32_t last_frame_end[order+PL]; //this takes the last samples from the previous frame to do the prediction within the boundaries
#ifdef debug_alternate_NR
static int frame_count=0; //only used for the distortion insertion - can alter be deleted
int dist_level=0;//only used for the distortion insertion - can alter be deleted
int nr_setting = 0;
#endif
float32_t R[11]; // takes the autocorrelation results
float32_t k,alfa;
float32_t any[order+1]; //some internal buffers for the levinson durben algorithm
float32_t Rfw[impulse_length+order]; // takes the forward predicted audio restauration
float32_t Rbw[impulse_length+order]; // takes the backward predicted audio restauration
float32_t Wfw[impulse_length],Wbw[impulse_length]; // taking linear windows for the combination of fwd and bwd
float32_t s;
static float32_t working_buffer[NR_FFT_SIZE + 2 * order + 2 * PL]; //we need 128 + 26 floats to work on -
//necessary to watch for impulses as close to the frame boundaries as possible
#ifdef debug_alternate_NR // generate test frames to test the noise blanker function
// using the RIT (0..55) to select the test frame
// 00 = noise blanker active on orig. audio; threshold factor=3
// 01 = frame of vocal "a" undistorted
// 02 .. 05 = frame of vocal "a" with different impulse distortion levels
// 06 .. 09 = frame of vocal "a" with different impulse distortion levels
// noise blanker operating!!
// 5 / 9 is the biggest "click" and it is slightly noticeable in the restored audio (9)
// 10 = noise blanker active on orig. audio threshold factor=3
// 11 = sinusoidal signal undistorted
// 12 ..15 = sinusoidal signal with different impulse distortion levels
// 16 ..19 = sinusoidal signal with different impulse distortion levels
// noise blanker operating!!
// 20 ..50 noise blanker active on orig. audio; threshold factor varying between 3 and 0.26
nr_setting = (int)ts.rit_value;
//*********************************from here just debug impulse / signal generation
if ((nr_setting > 0) && (nr_setting < 10)) // we use the vocal "a" frame
{
for (int i=0; i<128;i++) // not using vocal "a" but the original signal
{insamp[i]=NR_test_samp[i];}
if ((frame_count > 19) && (nr_setting > 1)) // insert a distorting pulse
{
dist_level=nr_setting;
if (dist_level > 5) dist_level=dist_level-4; // distortion level is 1...5
insamp[4]=insamp[4] + dist_level*3000; // overlaying a short distortion pulse +/-
insamp[5]=insamp[5] - dist_level*1000;
}
}
if ((nr_setting > 10) && (nr_setting < 20)) // we use the sinus frame
{
for (int i=0; i<128;i++)
insamp[i]=NR_test_sinus_samp[i];
if ((frame_count > 19) && (nr_setting > 11)) // insert a distorting pulse
{
dist_level=nr_setting-10;
if (dist_level > 5) dist_level=dist_level-4;
insamp[24]=insamp[24] + dist_level*1000; // overlaying a short distortion pulse +/-
insamp[25]=insamp[25] + dist_level*500;
insamp[26]=insamp[26] - dist_level*200; // overlaying a short distortion pulse +/-
insamp[27]=insamp[27] - dist_level*100;
}
}
frame_count++;
if (frame_count > 20) frame_count=0;
#endif
//*****************************end of debug impulse generation
memcpy(&working_buffer[2*PL + 2*order],insamp,NR_FFT_SIZE * sizeof(float32_t));// copy incomming samples to the end of our working bufer
// start of test timing zone
for (int i=0; i<impulse_length; i++) // generating 2 Windows for the combination of the 2 predictors
{ // will be a constant window later!
Wbw[i]=1.0*i/(impulse_length-1);
Wfw[impulse_length-i-1]=Wbw[i];
}
// calculate the autocorrelation of insamp (moving by max. of #order# samples)
for(int i=0; i < (order+1); i++)
{
// arm_dot_prod_f32(&insamp[0],&insamp[i],Nsam-i,&R[i]); // R is carrying the crosscorrelations
arm_dot_prod_f32(&working_buffer[order+PL+0],&working_buffer[order+PL+i],Nsam-i,&R[i]); // R is carrying the crosscorrelations
}
// end of autocorrelation
//alternative levinson durben algorithm to calculate the lpc coefficients from the crosscorrelation
R[0] = R[0] * (1.0 + 1.0e-9);
lpcs[0] = 1; //set lpc 0 to 1
for (int i=1; i < order+1; i++)
lpcs[i]=0; // fill rest of array with zeros - could be done by memfill
alfa = R[0];
for (int m = 1; m <= order; m++)
{
s = 0.0;
for (int u = 1; u < m; u++)
s = s + lpcs[u] * R[m-u];
k = -(R[m] + s) / alfa;
for (int v = 1;v < m; v++)
any[v] = lpcs[v] + k * lpcs[m-v];
for (int w = 1; w < m; w++)
lpcs[w] = any[w];
lpcs[m] = k;
alfa = alfa * (1 - k * k);
}
// end of levinson durben algorithm
for (int o = 0; o < order+1; o++ ) //store the reverse order coefficients separately
reverse_lpcs[order-o]=lpcs[o]; // for the matched impulse filter
arm_fir_init_f32(&LPC,order+1,&reverse_lpcs[0],&firStateF32[0],NR_FFT_SIZE); // we are using the same function as used in freedv
//arm_fir_f32(&LPC,insamp,tempsamp,Nsam); //do the inverse filtering to eliminate voice and enhance the impulses
arm_fir_f32(&LPC,&working_buffer[order+PL],tempsamp,Nsam); //do the inverse filtering to eliminate voice and enhance the impulses
arm_fir_init_f32(&LPC,order+1,&lpcs[0],&firStateF32[0],NR_FFT_SIZE); // we are using the same function as used in freedv
arm_fir_f32(&LPC,tempsamp,tempsamp,Nsam); // do a matched filtering to detect an impulse in our now voiceless signal
arm_var_f32(tempsamp,NR_FFT_SIZE,&sigma2); //calculate sigma2 of the original signal ? or tempsignal
arm_power_f32(lpcs,order,&lpc_power); // calculate the sum of the squares (the "power") of the lpc's
// impulse_threshold = (float32_t)ts.nb_setting * 0.5 * sqrtf(sigma2 * lpc_power); //set a detection level (3 is not really a final setting)
impulse_threshold = (float32_t)(16 - ts.dsp.nb_setting) * 0.5 * sqrtf(sigma2 * lpc_power); //set a detection level (3 is not really a final setting)
//if ((nr_setting > 20) && (nr_setting <51))
// impulse_threshold = impulse_threshold / (0.9 + (nr_setting-20.0)/10); //scaling the threshold by 1 ... 0.26
search_pos = order+PL; // lower boundary problem has been solved! - so here we start from 1 or 0?
//search_pos = 1;
impulse_count=0;
do { //going through the filtered samples to find an impulse larger than the threshold
if ((tempsamp[search_pos] > impulse_threshold)||(tempsamp[search_pos] < (-impulse_threshold)))
{
impulse_positions[impulse_count]=search_pos - order; // save the impulse positions and correct it by the filter delay
impulse_count++;
search_pos+=PL; // set search_pos a bit away, cause we are already repairing this area later
// and the next impulse should not be that close
}
search_pos++;
//} while ((search_pos < NR_FFT_SIZE-boundary_blank) && (impulse_count < 5));// avoid upper boundary
} while ((search_pos < NR_FFT_SIZE) && (impulse_count < 5));
// from here: reconstruction of the impulse-distorted audio part:
// first we form the forward and backward prediction transfer functions from the lpcs
// that is easy, as they are just the negated coefficients without the leading "1"
// we can do this in place of the lpcs, as they are not used here anymore and being recalculated in the next frame!
arm_negate_f32(&lpcs[1],&lpcs[1],order);
arm_negate_f32(&reverse_lpcs[0],&reverse_lpcs[0],order);
for (int j=0; j<impulse_count; j++)
{
for (int k = 0; k<order; k++) // we have to copy some samples from the original signal as
{ // basis for the reconstructions - could be done by memcopy
//if ((impulse_positions[j]-PL-order+k) < 0)// this solves the prediction problem at the left boundary
//{
// Rfw[k]=last_frame_end[impulse_positions[j]+k];//take the sample from the last frame
//}
//else
//{
//Rfw[k]=insamp[impulse_positions[j]-PL-order+k];//take the sample from this frame as we are away from the boundary
Rfw[k]=working_buffer[impulse_positions[j]+k];//take the sample from this frame as we are away from the boundary
//}
//Rbw[impulse_length+k]=insamp[impulse_positions[j]+PL+k+1];
Rbw[impulse_length+k]=working_buffer[order+PL+impulse_positions[j]+PL+k+1];
} //bis hier alles ok
for (int i = 0; i < impulse_length; i++) //now we calculate the forward and backward predictions
{
arm_dot_prod_f32(&reverse_lpcs[0],&Rfw[i],order,&Rfw[i+order]);
arm_dot_prod_f32(&lpcs[1],&Rbw[impulse_length-i],order,&Rbw[impulse_length-i-1]);
}
arm_mult_f32(&Wfw[0],&Rfw[order],&Rfw[order],impulse_length); // do the windowing, or better: weighing
arm_mult_f32(&Wbw[0],&Rbw[0],&Rbw[0],impulse_length);
#ifdef debug_alternate_NR
// in debug mode do the restoration only in some cases
if (((ts.rit_value > 0) && (ts.rit_value < 6))||((ts.rit_value > 10) && (ts.rit_value < 16)))
{
// just let the distortion pass at setting 1...5 and 11...15
// arm_add_f32(&Rfw[order],&Rbw[0],&insamp[impulse_positions[j]-PL],impulse_length);
}
else
{
//finally add the two weighted predictions and insert them into the original signal - thereby eliminating the distortion
//arm_add_f32(&Rfw[order],&Rbw[0],&insamp[impulse_positions[j]-PL],impulse_length);
arm_add_f32(&Rfw[order],&Rbw[0],&working_buffer[order+PL+impulse_positions[j]-PL],impulse_length);
}
#else
//finally add the two weighted predictions and insert them into the original signal - thereby eliminating the distortion
//arm_add_f32(&Rfw[order],&Rbw[0],&insamp[impulse_positions[j]-PL],impulse_length);
arm_add_f32(&Rfw[order],&Rbw[0],&working_buffer[order+impulse_positions[j]],impulse_length);
#endif
}
//for (int p=0; p<(order+PL); p++)
//{
// last_frame_end[p]=insamp[NR_FFT_SIZE-1-order-PL+p];// store 13 samples from the current frame to use at the next frame
//}
//end of test timing zone
memcpy(insamp,&working_buffer[order+PL],NR_FFT_SIZE * sizeof(float32_t));// copy the samples of the current frame back to the insamp-buffer for output
memcpy(working_buffer,&working_buffer[NR_FFT_SIZE],(2*order + 2*PL) * sizeof(float32_t)); // copy
}
#endif