Hello,
I’m building a noise gate that eventually will end up as a 500 series module.
I have a breadboard with the following components:
- Seed
- LCD screen
- 1 pot to adjust the LCD contrast
- 2 pots to adjust the noise gate threshold and knee settings
- 2 3.5mm audio jacks for audio in/out
The Pots and LCD screen work i.e. I can adjust the pots and see their values update on the LCD screen.
But I cannot get the audio in/out to work. It looks like the audio callback function is not getting called. I can confirm this as the controls function, within the audio callback function never gets called and the I var never increments. Any help would be appreciated.
Thanks!
My code follows…
// noise gate using seed platform
// include libs
#include "daisy_seed.h"
// Use the daisy namespace to prevent having to type
// daisy:: before all libdaisy functions
using namespace daisy;
// Declare a DaisySeed object called hardware
DaisySeed hardware;
// create a var and pin defs for the LCD
LcdHD44780 lcd;
LcdHD44780::Config lcd_config;
// number matches the light blue pin numbers on the seed pin out diagram
#define PIN_LCD_RS 21 // LCD pin 4
#define PIN_LCD_EN 22 // LCD pin 6
#define PIN_LCD_D4 23 // LCD: pin 11
#define PIN_LCD_D5 24 // LCD: pin 12
#define PIN_LCD_D6 25 // LCD: pin 13
#define PIN_LCD_D7 28 // LCD: pin 14
// LCD: pin 5 r/w goes to ground
// vars for the noise gate
float thresholdNg, attackNg, decayNg, kneeNg;
//Helper functions
void Controls(); // knob and lcd function
void GetNoiseGateSample(float &outl, float &outr, float inl, float inr); // noise gate function
float clip(float n, float lower, float upper); // clip/clamp function
// vars for the knobs
float knob1Value, knob2Value;
// var used to convert float to char for the LCD
char buff[16]; // var to hold our char
// vars for debug
int i = 0;
// audio call back func
void AudioCallback(AudioHandle::InterleavingInputBuffer in, AudioHandle::InterleavingOutputBuffer out, size_t size)
{
float outl, outr, inl, inr; // vars for the audio in/out ports
Controls(); // call func to handle input knobs & output LCD
//audio
for(size_t i = 0; i < size; i += 2)
{
// get the audio in
inl = in[i]; // left
inr = in[i + 1]; // right
// call the noise gate function
GetNoiseGateSample(outl, outr, inl, inr);
// send audio out
out[i] = outl; // left out
out[i + 1] = outr; // right out
}
i++; // debug to confirm this func is getting called
}
int main(void)
{
// Configure and Initialize the Daisy Seed
// These are separate to allow reconfiguration of any of the internal
// components before initialization.
hardware.Configure();
hardware.Init();
// Knob vars & init
AdcChannelConfig adcConfig[2]; // config the ADC, the # = total number of knobs to monitor
adcConfig[0].InitSingle(hardware.GetPin(15)); // knob 1 is blue pin 15 - threshold
adcConfig[1].InitSingle(hardware.GetPin(16)); // knob 2 is blue pin 16 - knee
hardware.adc.Init(adcConfig, 2); // the num represents the total # of knobs
hardware.adc.Start(); // start reading values from the knobs
// set initial noise gate parameters
thresholdNg = 0.5f; // max app it's .5
attackNg = 1.0f; // max app its 1
decayNg = 20.0f; // max app its 20
kneeNg = .75f; // max app its .75
// LCD vars & init
lcd_config.cursor_on = true;
lcd_config.cursor_blink = false;
lcd_config.rs = hardware.GetPin(PIN_LCD_RS);
lcd_config.en = hardware.GetPin(PIN_LCD_EN);
lcd_config.d4 = hardware.GetPin(PIN_LCD_D4);
lcd_config.d5 = hardware.GetPin(PIN_LCD_D5);
lcd_config.d6 = hardware.GetPin(PIN_LCD_D6);
lcd_config.d7 = hardware.GetPin(PIN_LCD_D7);
lcd.Init(lcd_config);
// start callback
hardware.StartAudio(AudioCallback);
while(1) {
// for debugging, confirm the controls function is working properly
//Controls(); // call func to handle input knobs & output LCD
//System::Delay(500);
}
}
void Controls()
{
lcd.Clear(); // clear the LCD
// get the knob values
knob1Value = hardware.adc.GetFloat(0);
knob2Value = hardware.adc.GetFloat(1);
// convert the float to a char
sprintf(buff, "T" FLT_FMT3 " K" FLT_FMT3 "\n", FLT_VAR3(knob1Value),FLT_VAR3(knob2Value));
// update the LCD
lcd.SetCursor(0, 0);
lcd.Print(buff);
lcd.SetCursor(1, 0);
lcd.PrintInt(i);
}
void GetNoiseGateSample(float &outl, float &outr, float inl, float inr)
{
// FROM MAX NOISE GATE DOCUMENTATION AND THE MAX Dynamics processing patch example
/*
Similar to a compressor with an envelope follower and a threshold, but instead of reducing the gain of the audio energy
that exceeds the threshold, a gate ateenuates that fall below the threshold. Instead of a ratio, the gate has a knee which
is proportional of the threshold within which the audio is attenuated rather than cut altogether, with defaul settings:
attack = 1, decay = 20, threshold = .5, knee = .75...
- sound above 0.5 are left alone (i.e. gain = 1, no change)
- sound below 0.5 and above 0.375 (the threshold * the knee) are scaled between 0 and 1
- sound below 0.375 are gated out (i.e. g = 0, no sound)
gate formula g = (e-(t*k)) / (t-(t*k))
where:
g = gain 0 to 1
e = envelope signal (TODO: what is an envelope signal?)
t = threshold
k = knee
*/
// 1. attack param X 44.1 (sample rate?)
//float attackl = attackNg * sample_rate; // TO-DO, only need this 1x move to global?
//float attackr = attackNg * sample_rate;
// 2. decay param X 44.1 (sample rate?)
//float decayl = decayNg * sample_rate; // TO-DO, only need this 1x move to global?
//float decayr = decayNg * sample_rate;
// 3. convert incoming signal to abs value
float inlAbs = fabsf(inl); // convert inl float to a single precision version
float inrAbs = fabsf(inr);
// 4. smooth incoming signal: incoming signal (3), ramp up attack param (1), ramp down decay param (2)
float rampSmoothl = inlAbs; // TODO - how achieve this with dasiySP?
float rampSmoothr = inrAbs; // TODO - how achieve this with dasiySP?
// 5. convert threshold param into a signal (doesn't seem to change the value)
// TODO
// 6. convert knee param into a signal (doesn't seem to change the value)
// TODO
// 7. multiply threshold (5) X knee (6)
float thresXknee = thresholdNg * kneeNg;
// 8. subtract threshold (5) - (threshold (5) X knee (6)) (7)
float thresDelta = thresholdNg - (thresXknee);
// 9. subtract rampsmooth (4) - (threshold (5) X knee (6))
float rampSmoothDeltal = rampSmoothl - thresXknee; // TODO - how achieve this with dasiySP?
float rampSmoothDeltar = rampSmoothr - thresXknee; // TODO - how achieve this with dasiySP?
// 10. divide rampsmooth (9) / threshold result (8)
float preclampl = rampSmoothDeltal / thresDelta;
float preclampr = rampSmoothDeltar / thresDelta;
// 11. clamp result of (10) range 0 to 1
// TODO - is there a clamp function
float gainl = clip(preclampl,0.0f,1.0f);
float gainr = clip(preclampr,0.0f,1.0f);
// 11b. new - smooth the distortion when the waveform crosses the knee
auto sq = [] (float x) { return x * x; };
gainl = 1 - sq (1 - gainl);
gainr = 1 - sq (1 - gainr);
// 12. out signal = camp (11) * incoming signal
outl = gainl * inl;
outr = gainr * inr;
}
float clip(float n, float lower, float upper)
{
return std::max(lower, std::min(n, upper));
}