So lets take this line by line....
This instructs our compiler to include the Daisy Petal interface functions. This allows us to utilize the pre-written core functions of the Daisy Petal. We could use the daisy_seed.h header file instead, but the Petal offers us some shortcuts that will make life easier. Eventually we will likely adapt this into our own custom fit set of routines, but for now the Terrarium PCB is designed to use the standard Petal functions (with a couple exceptions).
This allows us to use all of the functions in the "daisy" namespace without having to implicitly specify the namespace each time. It's basically a shortcut to make code cleaner / easier to read. For example, without this directive the next line would read
daisy::DaisyPedal hw;"
.
This creates an object that allows us access to the DaisyPetal hardware.
C++:
void AudioCallback(float *in, float *out, size_t size)
{
hw.DebounceControls();
hw.UpdateAnalogControls();
for(size_t i = 0; i < size; i += 2)
{
out[i] = in[i];
out[i + 1] = in[i + 1];
}
}
The AudioCallback function will be executed for each block of audio that is to be processed. *in is a pointer to the input buffer, *out is a pointer to the output buffer, and size is the length of the block (48 samples by default).
When toggle switches / footswitches are switched the contacts tend to "bounce" briefly, creating many false triggers. We generally don't have to worry about this much with analog circuits, but in the digital world the switches need to be low-pass filtered. This function takes care of that.
C++:
hw.UpdateAnalogControls();
This reads the current state of analog controls (pots, switches, etc) so we can process them when needed.
C++:
for(size_t i = 0; i < size; i += 2)
{
out[i] = in[i];
out[i + 1] = in[i + 1];
}
This is where your actual effect processing will take place. The code inside the "for" loop will execute once for each sample (48 times per block by default). The Daisy audio is interleaved, so we can access the left and right channels simultaneously for each sample.
in[i]
is the left channel input.
in[i+1]
is the right channel input.
out[i]
is the left channel output
out[i+1]
is the right channel output.
The line
out[i] = in[i];
passes the left input directly to the left output with no other processing.
Similarly
out[i+1] = in[i+1];
passes the right input directly to the right output with no processing.
We could increase the left channel amplitude by multiplying:
out[i] = in[i] * 2.0f;
(gain of 2)
Or could attenuate the left channel by dividing (or multiplying by a decimal less than 1)
out[i] = in[i] * 0.5f;
(attenuate by 1/2)
The
f after a decimal number tells the compiler that this should be treated as a float (floating point) data type. Without the
f suffix the compiler could round your decimal value up or down to the nearest whole integer, or in some cases throw an exception (error) and just not compile at all.
C++:
int main(void)
{
hw.Init();
hw.StartAdc();
hw.StartAudio(AudioCallback);
for(;;) {}
}
The main() function is the entry point for most C++ programs. This is where program execution begins.
This initializes the Daisy hardware.
This starts the Analog to Digital conversion so we can access the analog audio inputs
C++:
hw.StartAudio(AudioCallback);
This instructs the Seed to begin processing audio input by sending blocks to our AudioCallback function above.
If all of this doesn't make sense initially, don't panic... Most of this is boilerplate code that you will copy/paste into every new program. It'll all come together once we start writing an actual algorithm that
does something.