EXAMPLE Distortion

untamedfrontier

Well-known member
They published a distortion example for the pedal a few weeks ago and I messed around with it a bit last night.
The good news: It passes signal! Both hard & soft clip appear to be working. All 3 knobs work as well.
The bad news: The LEDs aren't lighting up, and I know the LED section of the code is a mess.
edit: LEDs now both light up and stay permanently lit up, not changing with effect state of either soft/hard clip footswitch.

Any suggestions would be lovely
EDIT 2: Check PedalPCB response below for update.
C++:
#include "daisysp.h"
#include "daisy_petal.h"
#include "terrarium.h"

using namespace daisysp;
using namespace daisy;
using namespace terrarium;

// Declare a local daisy_petal for hardware access
static DaisyPetal petal;

dsy_gpio led1;
dsy_gpio led2;

float hardClip(float in)
{
    in = in > 1.f ? 1.f : in;
    in = in < -1.f ? -1.f : in;
    return in;
}

float softClip(float in)
{
    if (in > 0)
        return 1 - expf(-in);
    return -1 + expf(in);
}

bool bypassHard, bypassSoft;
static void AudioCallback(float **in, float **out, size_t size)
{
    petal.UpdateAnalogControls();
    petal.DebounceControls();
   
    float Pregain = petal.knob[Terrarium::KNOB_1].Process() * 10 + 1.2;
    float Gain = petal.knob[Terrarium::KNOB_2].Process() * 100 + 1.2;
    float drywet = petal.knob[Terrarium::KNOB_3].Process();

    bypassSoft = petal.switches[Terrarium::FOOTSWITCH_1].RisingEdge() ? !bypassSoft : bypassSoft;
        dsy_gpio_toggle(&led1);  
    bypassHard = petal.switches[Terrarium::FOOTSWITCH_2].RisingEdge() ? !bypassHard : bypassHard;
        dsy_gpio_toggle(&led2);
       
    for(size_t i = 0; i < size; i ++)
    {  
        for (int chn = 0; chn < 2; chn++)
        {
            in[chn][i] *= Pregain;
            float wet = in[chn][i];

            if (!bypassSoft || !bypassHard){
                wet *= Gain;
            }
   
            if (!bypassSoft)
            {
                wet = softClip(wet);
            }
       
            if (!bypassHard)
            {
                wet = hardClip(wet);
            }
           
            out[chn][i] = wet * drywet + in[chn][i] * (1 - drywet);
        }
    }

}

int main(void)
{
    petal.Init();
   
    bypassHard = bypassSoft = false;
   
    // start callback
    petal.StartAdc();
    petal.StartAudio(AudioCallback);
    while(1)
    {
        //LED stuff
        petal.SetFootswitchLed((DaisyPetal::FootswitchLed)0, !bypassSoft);
        led1.pin = petal.seed.GetPin(22);
        led1.mode = DSY_GPIO_MODE_OUTPUT_PP;
        led1.pull = DSY_GPIO_NOPULL;
        dsy_gpio_init(&led1);

        petal.SetFootswitchLed((DaisyPetal::FootswitchLed)1, !bypassHard);
        led2.pin = petal.seed.GetPin(23);
        led2.mode = DSY_GPIO_MODE_OUTPUT_PP;
        led2.pull = DSY_GPIO_NOPULL;
        dsy_gpio_init(&led2);
        for (int i = 0; i < 8; i++)
       
        /* Pretty sure I don't need this, since it's specific to the petal encoder
        {
            petal.SetRingLed((DaisyPetal::RingLed)i, 1.f, 0.f, 0.f);
        }
        */
        petal.UpdateLeds();
        dsy_system_delay(6);
    }
}
 
Last edited:
The LED GPIO initialization needs to be moved outside of the "while' block in the main function.
You can remove all of the remaining code from the "while" block. (we don't use the SetFootswitchLed or UpdateLeds functions with the Terrarium).

Then in your AudioCallback function change this:
C++:
bypassSoft = petal.switches[Terrarium::FOOTSWITCH_1].RisingEdge() ? !bypassSoft : bypassSoft;
dsy_gpio_toggle(&led1);
bypassHard = petal.switches[Terrarium::FOOTSWITCH_2].RisingEdge() ? !bypassHard : bypassHard;
dsy_gpio_toggle(&led2);

to this:
C++:
if (petal.switches[Terrarium::FOOTSWITCH_1].RisingEdge())
{
        bypassSoft = !bypassSoft;
        dsy_gpio_toggle(&led1);
}

if (petal.switches[Terrarium::FOOTSWITCH_2].RisingEdge())
{
        bypassHard = !bypassHard;
        dsy_gpio_toggle(&led2);
}




Here's the complete code, give this a shot:

C++:
#include "daisysp.h"
#include "daisy_petal.h"
#include "terrarium.h"

using namespace daisysp;
using namespace daisy;
using namespace terrarium;

// Declare a local daisy_petal for hardware access
static DaisyPetal petal;

dsy_gpio led1;
dsy_gpio led2;

float hardClip(float in)
{
    in = in > 1.f ? 1.f : in;
    in = in < -1.f ? -1.f : in;
    return in;
}

float softClip(float in)
{
    if (in > 0)
        return 1 - expf(-in);
    return -1 + expf(in);
}

bool bypassHard, bypassSoft;
static void AudioCallback(float **in, float **out, size_t size)
{
    petal.UpdateAnalogControls();
    petal.DebounceControls();

    float Pregain = petal.knob[Terrarium::KNOB_1].Process() * 10 + 1.2;
    float Gain = petal.knob[Terrarium::KNOB_2].Process() * 100 + 1.2;
    float drywet = petal.knob[Terrarium::KNOB_3].Process();

    if (petal.switches[Terrarium::FOOTSWITCH_1].RisingEdge())
    {
        bypassSoft = !bypassSoft;
        dsy_gpio_toggle(&led1);
    }

    if (petal.switches[Terrarium::FOOTSWITCH_2].RisingEdge())
    {
        bypassHard = !bypassHard;
        dsy_gpio_toggle(&led2);
    }

  
    for(size_t i = 0; i < size; i ++)
    {
        for (int chn = 0; chn < 2; chn++)
        {
            in[chn][i] *= Pregain;
            float wet = in[chn][i];

            if (!bypassSoft || !bypassHard)
            {
                wet *= Gain;
            }

            if (!bypassSoft)
            {
                wet = softClip(wet);
            }
  
            if (!bypassHard)
            {
                wet = hardClip(wet);
            }
      
            out[chn][i] = wet * drywet + in[chn][i] * (1 - drywet);
        }
    }

}

int main(void)
{
    petal.Init();

    bypassHard = bypassSoft = true;

    led1.pin = petal.seed.GetPin(22);
    led1.mode = DSY_GPIO_MODE_OUTPUT_PP;
    led1.pull = DSY_GPIO_NOPULL;
    dsy_gpio_init(&led1);

    led2.pin = petal.seed.GetPin(23);
    led2.mode = DSY_GPIO_MODE_OUTPUT_PP;
    led2.pull = DSY_GPIO_NOPULL;
    dsy_gpio_init(&led2);

    // start callback
    petal.StartAdc();
    petal.StartAudio(AudioCallback);

    while(1) {}
}
 
Last edited by a moderator:
This is so helpful! I feel like I've just leveled up. Just tested it out and now the LEDs are opposite (lit up when bypassed, not lit up when effect engaged), but hopefully I can start to figure that one out since the essentially all the legwork is already done
 
I've updated the code to correct the LEDs by starting up with both clipping modes bypassed:

C++:
bypassHard = bypassSoft = true;

Alternatively you could have just turned on each LED after the gpio_init in main(), but I figured bypass at power-on was a more standard approach.
 
I promise I'll get working github links at some point, but wanted to share this as people who actually know how to code might be able to add fun updates.
This is Version 1.3.1, I've changed the LED control so it mimics the @sonic_explorer Rhythm Delay example, and in the same vein I also pulled the Tone control to work with the Hard Clip side (footswitch 2). I also didn't bother to change most of the comments ?

C++:
//Ported distortion petal example to terrarium. Version 1.3.1
// V1.3.x is adding tone control from sonic_explorer's RhythmDelay
#include "daisysp.h"
#include "daisy_petal.h"
#include "terrarium.h"

using namespace daisysp;
using namespace daisy;
using namespace terrarium;

// Declare a local daisy_petal for hardware access
static DaisyPetal petal;

static Tone toneLP; // this is for a Tone object, Tone is just a low-pass filter
static ATone toneHP; // this is for a ATone object, ATone is just a high-pass filter
// static Balance bal; // this is for a Balance object, which will correct for volume drop from the filters
float tone_val =0.0f;

float hardClip(float in)
{
    in = in > 1.f ? 1.f : in;
    in = in < -1.f ? -1.f : in;
    return in;
}

float softClip(float in)
{
    if (in > 0)
        return 1 - expf(-in);
    return -1 + expf(in);
}

Parameter toneParam;

// These are the cutoff freqs for the high and low pass filters
float tone_freqHP;
float tone_freqLP;

// Use the LED object
Led led1, led2;

bool bypassHard, bypassSoft;

static void AudioCallback(float **in, float **out, size_t size)
{
    petal.UpdateAnalogControls();
    petal.DebounceControls();
    toneHP.SetFreq(tone_freqHP);
    toneLP.SetFreq(tone_freqLP);
    led1.Update();
    led2.Update();

    // use a pot for a tone control - toneParam sweeps from -1 to 1
    tone_val = toneParam.Process();
    if (tone_val<0.0f){ // left half of pot HP off, LP on
    tone_freqHP = 0;
        tone_freqLP = 5000.0f*(powf(10,2*tone_val))+100.f;//This is a more complex function just to make the taper nice and smooth, the filter turned on too fast when linear
    }
    else{// right half of pot HP on, LP off
    tone_freqHP = 5000.0f*powf(10,2.f*tone_val-2);//This is a more complex function just to make the taper nice and smooth, the filter turned on too fast when linear
    tone_freqLP = 1000000.0f;// just something very high so the filter is not killing any actual guitar sound
    }

    float Pregain = petal.knob[Terrarium::KNOB_1].Process() * 10 + 1.2;
    float Gain = petal.knob[Terrarium::KNOB_2].Process() * 100 + 1.2;
    float drywet = petal.knob[Terrarium::KNOB_3].Process();

    if (petal.switches[Terrarium::FOOTSWITCH_1].RisingEdge())
    {
        bypassSoft = !bypassSoft;
        led1.Set(bypassSoft ? 0.0f : 1.0f);
    }

    if (petal.switches[Terrarium::FOOTSWITCH_2].RisingEdge())
    {
        bypassHard = !bypassHard;
        led2.Set(bypassHard ? 0.0f : 1.0f);
    }

  
    for(size_t i = 0; i < size; i ++)
    {
        for (int chn = 0; chn < 2; chn++)
        {
            in[chn][i] *= Pregain;
            float wet = in[chn][i];

            if (!bypassSoft || !bypassHard)
            {
                wet *= Gain;
            }

            if (!bypassSoft)
            {
                wet = softClip(wet);
            }
  
            if (!bypassHard)
            {
                wet = hardClip(wet);
                wet = toneHP.Process(wet);
                wet = toneLP.Process(wet);
            }

            
    
      
            out[chn][i] = wet * drywet + in[chn][i] * (1 - drywet);
        }
    }

}

void InitLeds(void)
{
    //Initialize the leds - these are using LED objects
    led1.Init(petal.seed.GetPin(Terrarium::LED_1),false);
    led2.Init(petal.seed.GetPin(Terrarium::LED_2),false);
    // The 'Terrarium::LED_1' (and similar for the knobs) references the terrarium.h which defines which GPIO pins
    //     are associated with which knobs, switches, & LEDs

}

void InitTone(float samplerate)
{
   // Initialize the Tone object
    toneHP.Init(samplerate);
    toneLP.Init(samplerate);
    toneParam.Init(petal.knob[Terrarium::KNOB_4], -1.0f, 1.0f, Parameter::LINEAR); // This knob value will be converted later in ProcessControls to a frequency for the High/Low Pass filter
}

int main(void)
{
    float samplerate;
    petal.Init();
    samplerate = petal.AudioSampleRate();

    bypassHard = bypassSoft = true;

    // Initialize the leds
    InitLeds();

    // Initialize the Tone object
    InitTone(samplerate);

    // start callback
    petal.StartAdc();
    petal.StartAudio(AudioCallback);

    while(1) {}
}
 
Awesome, glad to see someone using parts of my code already!

I have a few questions/comments about the effect:
- What is the point of the pregain? It is applied without any of the footswitches being pressed and the 'knob_value+1.2' makes it apply a factor a 1.2 even when the knob is all the way down. When the pedal is 'on' (either footswitch is pressed) the gain value is applied, so then the pregain seems kind of redundant. I know it is in the DaisyExample code....but I don't get it...maybe I am missing something obvious though.
- With the gain going up to 100 this effect gets super loud. There may be a way you can use a Balance object to even out the output volume with the input volume (or the input volume+some amount if you want the distorted signal louder).
 
Yeah, I totally agree about the pregain, I didn't really give any thought about it, as it was the example code. I think I was just hoping to get something working on my own :) Maybe swapping out pregain for softclip gain on knob 1 and hardclip gain on knob 2 would be the more useful application. I also combed over the docs and found nothing about softClip or hardClip at all, so I'm kinda baffled as to what I can change there either.

As far as balance object, the first time I had seen that was in your RhythmDelay example, and wasn't exactly sure how to implement it (mostly because I still don't know what almost everything does).
 
Yea, I agree it is good to build these things in steps - and getting something working first is a big morale boost. It took me a pretty long time to even get the multidelay converted into the rhythm_delay and way more Daisy program flashes than I am willing to admit (even to myself).

Maybe you don't even need the balance object, maybe dividing the wet signal by 10 before you mix it back with the dry signal would help even out the volume differences a bit.
 
I'm glad you did and posted it though. It would drive me nuts not knowing if it was bad code or a bad pedal. At least now I can't blame the pedal.
 
actually maybe a Terrarium? I see terrarium.h included as well as terrarium constants.
Terrarium with seed, this example was ported from the petal examples, but does need to be updated to compile properly with the more recent libdaisy and DaisySP updates

Maybe someday I'll learn how to use github properly
 
Back
Top