Skip to content

Commit 9a10a23

Browse files
authored
Noise gate (#50)
Noise gate with quite a few tweakable settings. I set the defaults to what work best for me/my setup. - Fast attack of 1ms - Hold and release both of 50ms - Fade time of ~100-200ms - And then threshold somewhere in the middle
1 parent be0fb67 commit 9a10a23

File tree

3 files changed

+197
-1
lines changed

3 files changed

+197
-1
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#include "noise_gate_module.h"
2+
#include <q/fx/envelope.hpp>
3+
4+
using namespace bkshepherd;
5+
6+
// The threshold knob/parameter will generate a float between 0 and 1 which gets
7+
// multiplied by this value to be used as the threshold against the audio input
8+
// signal
9+
const float maxThreshold = 0.2;
10+
11+
// These are placeholder values that will get overwritten with the attack and release parameters at initialization
12+
cycfi::q::ar_envelope_follower env_follower(48000.0, 0.002f, 0.020f);
13+
14+
static const int s_paramCount = 5;
15+
static const ParameterMetaData s_metaData[s_paramCount] = {{
16+
name : "Threshold",
17+
valueType : ParameterValueType::Float,
18+
valueBinCount : 0,
19+
defaultValue : {.float_value = 0.5f},
20+
knobMapping : 0,
21+
midiCCMapping : -1
22+
},
23+
{
24+
name : "Atk [ms]",
25+
valueType : ParameterValueType::Float,
26+
valueBinCount : 0,
27+
defaultValue : {.float_value = 1.0f},
28+
knobMapping : 1,
29+
midiCCMapping : -1,
30+
minValue : static_cast<int>(1),
31+
maxValue : static_cast<int>(20)
32+
},
33+
{
34+
name : "Rel [ms]",
35+
valueType : ParameterValueType::Float,
36+
valueBinCount : 0,
37+
defaultValue : {.float_value = 50.0f},
38+
knobMapping : 2,
39+
midiCCMapping : -1,
40+
minValue : static_cast<int>(1),
41+
maxValue : static_cast<int>(500)
42+
},
43+
{
44+
name : "Hold [ms]",
45+
valueType : ParameterValueType::Float,
46+
valueBinCount : 0,
47+
defaultValue : {.float_value = 50.0f},
48+
knobMapping : 3,
49+
midiCCMapping : -1,
50+
minValue : static_cast<int>(0),
51+
maxValue : static_cast<int>(1000)
52+
},
53+
{
54+
name : "Fade [ms]",
55+
valueType : ParameterValueType::Float,
56+
valueBinCount : 0,
57+
defaultValue : {.float_value = 200.0f},
58+
knobMapping : 4,
59+
midiCCMapping : -1,
60+
minValue : static_cast<int>(0),
61+
maxValue : static_cast<int>(500)
62+
}};
63+
64+
// Default Constructor
65+
NoiseGateModule::NoiseGateModule() : BaseEffectModule() {
66+
// Set the name of the effect
67+
m_name = "Noise Gate";
68+
69+
// Setup the meta data reference for this Effect
70+
m_paramMetaData = s_metaData;
71+
72+
// Initialize Parameters for this Effect
73+
this->InitParams(s_paramCount);
74+
}
75+
76+
// Destructor
77+
NoiseGateModule::~NoiseGateModule() {
78+
// No Code Needed
79+
}
80+
81+
void NoiseGateModule::Init(float sample_rate) {
82+
BaseEffectModule::Init(sample_rate);
83+
84+
env_follower.config(GetParameterAsFloat(1) / 1000.f, GetParameterAsFloat(2) / 1000.f, sample_rate);
85+
}
86+
87+
void NoiseGateModule::ParameterChanged(int parameter_id) {
88+
switch (parameter_id) {
89+
case 1:
90+
env_follower.attack(GetParameterAsFloat(1) / 1000.f, GetSampleRate());
91+
break;
92+
case 2:
93+
env_follower.release(GetParameterAsFloat(2) / 1000.f, GetSampleRate());
94+
break;
95+
}
96+
}
97+
98+
void NoiseGateModule::ProcessMono(float in) {
99+
// Update envelope follower with input signal
100+
const float raw_env_level = env_follower(std::abs(in));
101+
102+
// Run a smoothing filter on the envelope to prevent crackling due to rapid adjustments of the envelope state
103+
const float currentTimeInSeconds = static_cast<float>(daisy::System::GetNow()) / 1000.f;
104+
const float smoothed_env_level = m_smoothingFilter(raw_env_level, currentTimeInSeconds);
105+
106+
if (smoothed_env_level > GetParameterAsFloat(0) * maxThreshold) {
107+
// Signal is above the threshold, open the gate and reset the timer
108+
m_gateOpen = true;
109+
m_holdTimer = 0.0f;
110+
m_currentGain = 1.0f; // Fully open
111+
} else if (m_gateOpen) {
112+
// Signal is below the threshold but within hold time
113+
114+
const float dt = currentTimeInSeconds - m_prevTimeSeconds;
115+
m_holdTimer += dt;
116+
117+
if (m_holdTimer >= (GetParameterAsFloat(3) / 1000.0f)) {
118+
// Gate is closing: start fading out
119+
const float fadeOutStep = dt / (GetParameterAsFloat(4) / 1000.0f);
120+
m_currentGain -= fadeOutStep;
121+
if (m_currentGain <= 0.0f) {
122+
m_currentGain = 0.0f;
123+
m_gateOpen = false;
124+
}
125+
}
126+
}
127+
128+
m_prevTimeSeconds = currentTimeInSeconds;
129+
130+
// Apply noise gate using the smoothed envelope value and hold time, and
131+
// also the current gain value (used for fade)
132+
const float out = m_gateOpen ? in * m_currentGain : 0.0f;
133+
134+
m_audioLeft = out;
135+
m_audioRight = m_audioLeft;
136+
}
137+
138+
void NoiseGateModule::ProcessStereo(float inL, float inR) {
139+
// Calculate the mono effect
140+
ProcessMono(inL);
141+
}
142+
143+
float NoiseGateModule::GetBrightnessForLED(int led_id) const {
144+
float value = BaseEffectModule::GetBrightnessForLED(led_id);
145+
146+
if (led_id == 1) {
147+
return m_gateOpen ? 1.0f : 0.0f;
148+
}
149+
150+
return value;
151+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#pragma once
2+
#ifndef NOISEGATE_MODULE_H
3+
#define NOISEGATE_MODULE_H
4+
5+
#include <stdint.h>
6+
7+
#include "../Util/1efilter.hpp"
8+
#include "base_effect_module.h"
9+
10+
#ifdef __cplusplus
11+
12+
/** @file noisegate_module.h */
13+
14+
namespace bkshepherd {
15+
16+
class NoiseGateModule : public BaseEffectModule {
17+
public:
18+
NoiseGateModule();
19+
~NoiseGateModule();
20+
21+
void Init(float sample_rate) override;
22+
void ParameterChanged(int parameter_id) override;
23+
void ProcessMono(float in) override;
24+
void ProcessStereo(float inL, float inR) override;
25+
float GetBrightnessForLED(int led_id) const override;
26+
27+
private:
28+
// inputs:
29+
// estimated frequency: overwritten by timestamps at runtime and not used
30+
// cutoff freq
31+
// beta: 0.0f disables it entirely, but used for scaling cutoff frequency
32+
// derivative cutoff freq: used when beta is > 0
33+
one_euro_filter<float, float> m_smoothingFilter{48000, 0.5f, 0.05f, 1.0f};
34+
35+
float m_holdTimer; // Timer tracking hold duration
36+
bool m_gateOpen; // Is the gate currently open?
37+
float m_prevTimeSeconds;
38+
39+
float m_currentGain;
40+
};
41+
} // namespace bkshepherd
42+
#endif
43+
#endif

Software/GuitarPedal/guitar_pedal.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "Effect-Modules/looper_module.h"
1212
#include "Effect-Modules/metro_module.h"
1313
#include "Effect-Modules/multi_delay_module.h"
14+
#include "Effect-Modules/noise_gate_module.h"
1415
#include "Effect-Modules/overdrive_module.h"
1516
#include "Effect-Modules/peq_module.h"
1617
#include "Effect-Modules/pitch_shifter_module.h"
@@ -568,7 +569,7 @@ int main(void) {
568569
crossFaderTransitionTimeInSamples = hardware.GetNumberOfSamplesForTime(crossFaderTransitionTimeInSeconds);
569570

570571
// Init the Effects Modules
571-
availableEffectsCount = 14;
572+
availableEffectsCount = 15;
572573
availableEffects = new BaseEffectModule *[availableEffectsCount];
573574
availableEffects[0] = new ModulatedTremoloModule();
574575
availableEffects[1] = new OverdriveModule();
@@ -584,6 +585,7 @@ int main(void) {
584585
availableEffects[11] = new LooperModule();
585586
availableEffects[12] = new GraphicEQModule();
586587
availableEffects[13] = new ParametricEQModule();
588+
availableEffects[14] = new NoiseGateModule();
587589

588590
for (int i = 0; i < availableEffectsCount; i++) {
589591
availableEffects[i]->Init(sample_rate);

0 commit comments

Comments
 (0)