You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
5.3 KiB
180 lines
5.3 KiB
/*
|
|
* AudioEffectNoiseGate_F32
|
|
*
|
|
* Created: Max Huster, Feb 2021
|
|
* Purpose: This module mutes the Audio completly, when it's below a given threshold.
|
|
*
|
|
* This processes a single stream fo audio data (ie, it is mono)
|
|
*
|
|
* MIT License. use at your own risk.
|
|
*/
|
|
|
|
#ifndef _AudioEffectNoiseGate_F32_h
|
|
#define _AudioEffectNoiseGate_F32_h
|
|
|
|
#include <arm_math.h> //ARM DSP extensions. for speed!
|
|
#include <AudioStream_F32.h>
|
|
|
|
#ifdef NOISEGATE_EXTENDEDINFO
|
|
const float minLinear = pow10f(-60 / 20.0f);
|
|
#endif
|
|
|
|
class AudioEffectNoiseGate_F32 : public AudioStream_F32
|
|
{
|
|
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
|
|
//GUI: shortName:NoiseGate
|
|
public:
|
|
//constructor
|
|
AudioEffectNoiseGate_F32(void) : AudioStream_F32(1, inputQueueArray_f32){};
|
|
AudioEffectNoiseGate_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32){};
|
|
|
|
//here's the method that does all the work
|
|
void update(void)
|
|
{
|
|
|
|
//Serial.println("AudioEffectNoiseGate_F32: updating."); //for debugging.
|
|
audio_block_f32_t *block;
|
|
block = AudioStream_F32::receiveWritable_f32();
|
|
if (!block)
|
|
return;
|
|
// create a new audio block for the gain
|
|
audio_block_f32_t *gainBlock = AudioStream_F32::allocate_f32();
|
|
// calculate the desired gain
|
|
calcGain(block, gainBlock);
|
|
// smooth the "blocky" gain block
|
|
calcSmoothedGain(gainBlock);
|
|
#ifdef NOISEGATE_EXTENDEDINFO
|
|
|
|
float32_t min;
|
|
float32_t max;
|
|
uint32_t index;
|
|
arm_min_f32(gainBlock->data, gainBlock->length, &min, &index);
|
|
arm_max_f32(gainBlock->data, gainBlock->length, &max, &index);
|
|
|
|
_inBetween = min > minLinear && max < (1 - minLinear);
|
|
#endif
|
|
// multiply it to the input singal
|
|
arm_mult_f32(gainBlock->data, block->data, block->data, block->length);
|
|
// release gainBlock
|
|
AudioStream_F32::release(gainBlock);
|
|
|
|
//transmit the block and be done
|
|
AudioStream_F32::transmit(block);
|
|
AudioStream_F32::release(block);
|
|
}
|
|
|
|
void setThreshold(float dbfs)
|
|
{
|
|
// convert dbFS to linear value to comapre against later
|
|
linearThreshold = pow10f(dbfs / 20.0f);
|
|
}
|
|
|
|
void setOpeningTime(float timeInSeconds)
|
|
{
|
|
openingTimeConst = expf(-1.0f / (timeInSeconds * AUDIO_SAMPLE_RATE));
|
|
}
|
|
|
|
void setClosingTime(float timeInSeconds)
|
|
{
|
|
closingTimeConst = expf(-1.0f / (timeInSeconds * AUDIO_SAMPLE_RATE));
|
|
}
|
|
|
|
void setHoldTime(float timeInSeconds)
|
|
{
|
|
holdTimeNumSamples = timeInSeconds * AUDIO_SAMPLE_RATE;
|
|
}
|
|
|
|
#ifdef NOISEGATE_EXTENDEDINFO
|
|
bool infoIsOpeningOrClosing()
|
|
{
|
|
return _inBetween;
|
|
}
|
|
|
|
#endif
|
|
bool infoIsOpen()
|
|
{
|
|
return _isOpenDisplay;
|
|
}
|
|
|
|
private:
|
|
float32_t linearThreshold;
|
|
float32_t prev_gain_dB = 0;
|
|
float32_t openingTimeConst, closingTimeConst;
|
|
float lastGainBlockValue = 0;
|
|
int32_t counter, holdTimeNumSamples = 0;
|
|
audio_block_f32_t *inputQueueArray_f32[1]; //memory pointer for the input to this module
|
|
bool falling = false;
|
|
|
|
bool _isOpen = false;
|
|
bool _isOpenDisplay = false;
|
|
|
|
#ifdef NOISEGATE_EXTENDEDINFO
|
|
bool _inBetween = false;
|
|
#endif
|
|
void calcGain(audio_block_f32_t *input, audio_block_f32_t *gainBlock)
|
|
{
|
|
_isOpen = false;
|
|
for (int i = 0; i < input->length; i++)
|
|
{
|
|
// take absolute value and compare it to the set threshold
|
|
bool isAboveThres = abs(input->data[i]) > linearThreshold;
|
|
_isOpen |= isAboveThres;
|
|
// if above the threshold set volume to 1 otherwise to 0, we did not account for holdtime
|
|
gainBlock->data[i] = isAboveThres ? 1 : 0;
|
|
|
|
// if we are falling and are above the threshold, the level is not falling
|
|
if (falling & isAboveThres)
|
|
{
|
|
falling = false;
|
|
}
|
|
// if we have a falling signal
|
|
if (falling || lastGainBlockValue > gainBlock->data[i])
|
|
{
|
|
// check whether the hold time is not reached
|
|
if (counter < holdTimeNumSamples)
|
|
{
|
|
// signal is (still) falling
|
|
falling = true;
|
|
counter++;
|
|
gainBlock->data[i] = 1.0f;
|
|
}
|
|
// otherwise the signal is already muted due to the line: "gainBlock->data[i] = isAboveThres ? 1 : 0;"
|
|
}
|
|
// note the last gain value, so we can compare it if the signal is falling in the next sample
|
|
lastGainBlockValue = gainBlock->data[i];
|
|
}
|
|
// note the display value
|
|
_isOpenDisplay = _isOpen;
|
|
};
|
|
|
|
//this method applies the "opening" and "closing" constants to smooth the
|
|
//target gain level through time.
|
|
void calcSmoothedGain(audio_block_f32_t *gain_block)
|
|
{
|
|
float32_t gain;
|
|
float32_t one_minus_opening_const = 1.0f - openingTimeConst;
|
|
float32_t one_minus_closing_const = 1.0f - closingTimeConst;
|
|
for (int i = 0; i < gain_block->length; i++)
|
|
{
|
|
gain = gain_block->data[i];
|
|
|
|
//smooth the gain using the opening or closing constants
|
|
if (gain < prev_gain_dB)
|
|
{ //are we in the opening phase?
|
|
gain_block->data[i] = openingTimeConst * prev_gain_dB + one_minus_opening_const * gain;
|
|
}
|
|
else
|
|
{ //or, we're in the closing phase
|
|
gain_block->data[i] = closingTimeConst * prev_gain_dB + one_minus_closing_const * gain;
|
|
}
|
|
|
|
//save value for the next time through this loop
|
|
prev_gain_dB = gain_block->data[i];
|
|
}
|
|
|
|
//return
|
|
return; //the output here is gain_block
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|