MultiDelay effect

tcpoint

Well-known member
I've been porting the MultiDelay example from the Daisy Examples. It's kind of turned into a mix of the Petal and Patch examples. Not 100% happy with it, yet. I will probably tweak the approach, a bit. I can't help myself. FYI, I'm only developing on the Linux platform, should be easy to compile on Windows and Mac but I'm not going to provide any support for those.

Here's a brief description of what I've got so far.

# Description
Three delay bundled in one. Set their delay times independently. Feedback is controlled globally.
The three delays are mixed with the dry signal.
Set the dry/wet amount of this final mix with the dry/mix ctrl.

# Control

| Control | Description | Comment |
| --- | --- | --- |
| Ctrls 1 - 2 - 3| Delay Time | Time for delays 1 through 3 |
| Ctrl 4 | Feedback | Feedback for delays 1 through 3 |
| Ctrl 5 | Dry/Wet Mix | Set the dry/wet amount for the outputs |
| Foot Switch 1 | Passthru | Illuminated when set to passthru |

As you can see, I still have one more Ctrl (Pot) available), all 4 switches and Foot Switch 2.

After I tweak it a bit more and clean up the code, I'll post it the github repository that started for Terrarium examples. I can also make the binaries available. If anybody wants to help out (either with code or suggestions, feel free to let me know). How many have a working Terrarium and have figured out how to program it?
 
Last edited:
Got my Terrarium PCB yesterday (took 21 days to get to Québec City, Canada...)

I'm very interested in your development. I use all 3 main OS but Linux is only for my servers at the moment but I don't think it will be the main problem (the OS).

Right now I haven't see a simple (or even complicated) tutorial on how to simply start from the beginning to the end.

Found it !


Now I'm wondering if we should agreed on some naming conventions and ways to help centralized/share those projects.

Maybe we should start on new thread on conventions? (I'm not the guy who can take lead on this, I'm ok to follow recommendations) ;)
 
The programs tend to be fairly short. A lot of the complicated details are hidden in libDaisy and daisySP. The MultiDelay example is only 150 lines (including comments and lots of newlines). I'll post the link to the github repo, tomorrow. I want people to try out the code and give feedback. I'll include a bin file in project.
 
# Description
Three delay bundled in one. Set their delay times independently. Feedback is controlled globally.
The three delays are mixed with the dry signal.
Set the dry/wet amount of this final mix with the dry/mix ctrl.

# Control

| Control | Description | Comment |
| --- | --- | --- |
| Ctrls 1 - 2 - 3| Delay Time | Time for delays 1 through 3 |
| Ctrl 4 | Feedback | Feedback for delays 1 through 3 |
| Ctrl 5 | Dry/Wet Mix | Set the dry/wet amount for the outputs |
| Foot Switch 1 | Passthru | Illuminated when set to passthru |

As you can see, I still have one more Ctrl (Pot) available), all 4 switches and Foot Switch 2.
My first reflex without having tested it yet would be to try to make feedback more independent and not global.

CTR 1-2-3 to delay time is perfect
Maybe make SW 1-2-3 independent feedback at 105 Down, 50% UP (I don't even know if it makes sense)
CTR 4-5-6 Dry mix Mix

Just thinking it will be possible to reassign such stuff makes me happy.

So many possibilities...
 
My first reflex without having tested it yet would be to try to make feedback more independent and not global.

CTR 1-2-3 to delay time is perfect
Maybe make SW 1-2-3 independent feedback at 105 Down, 50% UP (I don't even know if it makes sense)
CTR 4-5-6 Dry mix Mix

Just thinking it will be possible to reassign such stuff makes me happy.

So many possibilities...
And then there's modulation, pitch shifting, etc. Filtering to emulate analog delays. Tails. Turn delay taps on or off. The programming is fairly easy.
 
Last edited:
Added the binaries.
The Verb sounds great and I've just started playing around with the MultiDelay... really makes the mind wander with how many ways one could go with tweaking effects.

Now for a total noob question: Is it the the compiler that combines the C++ code and the various H file modules to create the binaries?

Follow up question: What compiler are you using to create the binaries?

Thanks!
 
I'm just using the gnu C++ compiler. I do all my development work on Linux. My day job is programming a Console Server (which runs embedded Linux). The programming, so far, has been easy for me. Still trying to learn more about dsp. I've done lots of easy dsp but wouldn't consider myself anything more than hack. I've been working on a overdrive / reverb effect. It sounds pretty good but wouldn't come close to finding a place on my pedal board. There's a lot of different directions to take the multidelay but I feel like an ass stuck between two bales of hay, can't figure out where to start.
 
I'm trying to "learn by example"...

Can you explain this line?

C++:
#define MAX_DELAY static_cast<size_t>(48000 * 1.f)

Mainly the "48000*1.f".

Is it in ms? 480 ms?

Suppose I want a 2 minutes max delay (yes I'm being extreme...)?
 
I'm trying to "learn by example"...

Can you explain this line?

C++:
#define MAX_DELAY static_cast<size_t>(48000 * 1.f)

Mainly the "48000*1.f".

Is it in ms? 480 ms?

Suppose I want a 2 minutes max delay (yes I'm being extreme...)?
Terrible code that that I copied over from the example. 48000 is the sample rate (number of samples per second). It shouldn't be hard coded. Change the 1.f to 2.0f for 2 seconds.
 
Terrible code that that I copied over from the example. 48000 is the sample rate (number of samples per second). It shouldn't be hard coded. Change the 1.f to 2.0f for 2 seconds.
Thanks for the clarification!

It will really be 120.0f then. It was not a typo to try 2 minutes delay.
:p
 
I do everything from the shell.
The command I use is:

make program-dfu

which translates to:

dfu-util -a 0 -s 0x80000000:leave -D $(TARGET_BIN) -d 0483:df11

where $(TARGET_BIN) is the name of your binary file.

BTW. I have a sample compressor almost finished. It's working but I need to figure out some good ranges for the controls. Maybe I'll post it tomorrow (with or without the good ranges). The range for the gain is between (1-40), which can get pretty loud.

(I got distracted with a couple of overdrives and a couple of fuzzes).
 
Can someone explain to me what line 72 does? In my version which I added a 4th delay I changed this from 3 to 4, but I did that only through guesswork, there was no real knowledge behind it

C++:
for(int i = 0; i < 3; i++)
 
That's a "for" loop. The code in the block directly below it is executed multiple times (as determined by the parameters in parentheses).

C++:
int i = 0
A variable of type "integer" named "i" is created, initialized with the value 0.

C++:
i < 3
The loop will continue to run while "i" is less than 3.

C++:
i++
The variable "i" is incremented by 1 on each iteration.
 
Can someone explain to me what line 72 does? In my version which I added a 4th delay I changed this from 3 to 4, but I did that only through guesswork, there was no real knowledge behind it

C++:
for(int i = 0; i < 3; i++)
And as you see later, there are "CASEs" so just changing the "i < 3" is not sufficient.

You need to assign a case to it. I think KNOB_6 is "available"...

<standard disclaimer> I may be wrong ! </> ;)
 
The good news is what I did worked, I'm just trying to understand that line in particular

Here's the code I used, if you don't like it, blame tcpoint because they did all the hard work:

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

#include <string>

#define MAX_DELAY static_cast<size_t>(48000 * 1.f)

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

DaisyPetal petal;

DelayLine<float, MAX_DELAY> DSY_SDRAM_BSS delayMems[3];
float feedback;
float wetdry = 0.5f;

struct Delay
{
    DelayLine<float, MAX_DELAY> *delay;
    float                        currentDelay;
    float                        delayTarget;

    float Process(float in)
    {
        //set delay times
        fonepole(currentDelay, delayTarget, .0002f);
        delay->SetDelay(currentDelay);
        float read = delay->Read();
        delay->Write((feedback * read) + in);
        return read;
    }
};

Delay     delays[4];
Parameter delayParams[4];
Parameter feedbackParam;
Parameter mixParam;

dsy_gpio led1;

// int   drywet;
bool  passThruOn;

void ProcessControls();

static void AudioCallback(float **in, float **out, size_t size)
{
    ProcessControls();

    for(size_t i = 0; i < size; i++)
    {
        float mix = 0;
        //update delayline with feedback
        for(int d = 0; d < 4; d++)
        {
            mix += delays[d].Process(in[0][i]);
        }

        if(passThruOn)
        {
            wetdry = 0;
        }
        mix       = (wetdry * mix) / 4.0f + (1.0f - wetdry) * in[0][i];
        out[0][i] = out[1][i] = mix;
    }
}

void InitDelays(float samplerate)
{
    for(int i = 0; i < 4; i++)
    {
        //Init delays
        delayMems[i].Init();
        delays[i].delay = &delayMems[i];
        //3 delay times
        int knob;
        switch(i) {
        case 0:
            knob = Terrarium::KNOB_1;
            break;
        case 1:
            knob = Terrarium::KNOB_2;
            break;
        case 2:
            knob = Terrarium::KNOB_3;
            break;
        case 3:
            knob = Terrarium::KNOB_4;
                break;
        }
        delayParams[i].Init(petal.knob[knob],
                       samplerate * .05,
                       MAX_DELAY,
                       Parameter::LOGARITHMIC);
    }
    feedbackParam.Init(petal.knob[Terrarium::KNOB_5], 0.0, 1.0, Parameter::LINEAR);
    mixParam.Init(petal.knob[Terrarium::KNOB_6], 0.0, 1.0, Parameter::LINEAR);
}

int main(void)
{
    float samplerate;
    petal.Init(); // Initialize hardware (daisy seed, and petal)
    samplerate = petal.AudioSampleRate();

    InitDelays(samplerate);

    passThruOn = false;

    petal.StartAdc();
    petal.StartAudio(AudioCallback);

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

    while(1)
    {
        // Update Pass thru
        dsy_gpio_write(&led1, passThruOn ? 0.0f : 1.0f);
        dsy_system_delay(6);
    }
}

void ProcessControls()
{
    petal.UpdateAnalogControls();
    petal.DebounceControls();

    //knobs
    for(int i = 0; i < 4; i++)
    {
        delays[i].delayTarget = delayParams[i].Process();
    }

    feedback    = feedbackParam.Process();
    wetdry = mixParam.Process();

    //footswitch
    if(petal.switches[Terrarium::FOOTSWITCH_1].RisingEdge())
    {
        passThruOn = !passThruOn;
    }
}
 
Back
Top