From 01cb7ba10556daffaa6d99cd155335014198618b Mon Sep 17 00:00:00 2001 From: Max Huster Date: Sun, 7 Feb 2021 19:42:57 +0100 Subject: [PATCH 1/3] add NoiseGate effect --- AudioEffectNoiseGate_F32.h | 180 +++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 AudioEffectNoiseGate_F32.h diff --git a/AudioEffectNoiseGate_F32.h b/AudioEffectNoiseGate_F32.h new file mode 100644 index 0000000..b3e595d --- /dev/null +++ b/AudioEffectNoiseGate_F32.h @@ -0,0 +1,180 @@ +/* + * 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 DSP extensions. for speed! +#include + +#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 From 60243a15a5076088d27dd46506b5c415d61355d0 Mon Sep 17 00:00:00 2001 From: Max Huster Date: Sun, 7 Feb 2021 19:52:46 +0100 Subject: [PATCH 2/3] add example code --- examples/NoiseGate/main.cpp | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/NoiseGate/main.cpp diff --git a/examples/NoiseGate/main.cpp b/examples/NoiseGate/main.cpp new file mode 100644 index 0000000..14f60a3 --- /dev/null +++ b/examples/NoiseGate/main.cpp @@ -0,0 +1,60 @@ +#include +// the next line can be skipped if more perfomance is needed +#define NOISEGATE_EXTENDEDINFO +#include +#include + +AudioInputI2S_F32 i2sAudioIn1; +AudioOutputI2S_F32 i2sAudioOut1; +AudioEffectNoiseGate_F32 noiseGate; + +AudioConnection_F32 patchCordL1(i2sAudioIn1, 0, noiseGate, 0); +AudioConnection_F32 patchCordL2(noiseGate, 0, i2sAudioOut1, 0); +AudioConnection_F32 patchCordR1(i2sAudioIn1, 1, i2sAudioOut1, 1); + + + +#define LED_RED 0 +#define LED_YELLOW 1 +#define LED_GREEN 2 + +//The setup function is called once when the system starts up +void setup(void) +{ + //Start the USB serial link (to enable debugging) + Serial.begin(115200); + delay(500); + Serial.println("Setup starting..."); + + //Allocate dynamically shuffled memory for the audio subsystem + AudioMemory(20); + AudioMemory_F32(20); + + //Put your own setup code here + pinMode(LED_RED, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_YELLOW, OUTPUT); + + // setup the noise gate. it is probaly a good idea to filter the incoming signal @20 Hz, maybe this colud be added in the future + noiseGate.setOpeningTime(0.02f); + noiseGate.setClosingTime(0.05f); + noiseGate.setHoldTime(0.1); + noiseGate.setThreshold(-40); + + //End of setup + Serial.println("Setup complete."); +}; + +//After setup(), the loop function loops forever. +//Note that the audio modules are called in the background. +//They do not need to be serviced by the loop() function. +float lastVal = 0; +void loop(void) +{ + + bool thresTrigger = noiseGate.infoIsOpen(); + digitalWrite(LED_RED, !thresTrigger); + digitalWrite(LED_GREEN, thresTrigger); + digitalWrite(LED_YELLOW, noiseGate.infoIsOpeningOrClosing()); + +}; \ No newline at end of file From 0dc8d9bd0822e511b3728bbe8f0f5929c11bbfcc Mon Sep 17 00:00:00 2001 From: max-huster <40296208+max-huster@users.noreply.github.com> Date: Fri, 26 Feb 2021 12:27:00 +0100 Subject: [PATCH 3/3] rename example.cpp to example.ino --- examples/NoiseGate/{main.cpp => main.ino} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename examples/NoiseGate/{main.cpp => main.ino} (99%) diff --git a/examples/NoiseGate/main.cpp b/examples/NoiseGate/main.ino similarity index 99% rename from examples/NoiseGate/main.cpp rename to examples/NoiseGate/main.ino index 14f60a3..e9b67fe 100644 --- a/examples/NoiseGate/main.cpp +++ b/examples/NoiseGate/main.ino @@ -57,4 +57,4 @@ void loop(void) digitalWrite(LED_GREEN, thresTrigger); digitalWrite(LED_YELLOW, noiseGate.infoIsOpeningOrClosing()); -}; \ No newline at end of file +};