Yesterday, a user was proposing an Arduino Sketch for a Guitar Effects Pedal which is mixing the original input with an effect. Setting up a simple effect is easy, but I have never thought about how to mix the effect with the original input. He was proposing the following process chain (which I think is just ingenious):
|-> AudioEffectStream --|
I2SStream -copy-> MultiOutput -| |-> OutputMixer -> I2SStream
|-----------------------|
It took me quite some time to figure out why this is not working: The OutputMixer requires consistent write sizes of all it’s input components and the AudioEffectStream seemed to be writing individual samples. Fortunately this can be easily fixed by adding a buffer into the chain:
|-> AudioEffectStream -> BufferedStream --|
... MultiOutput -| |-> OutputMixer ...
|-> BufferedStream -----------------------|
The BufferedStream makes sure that the OutputMixer gets fed with a consistent number of samples.
The corresponding sketch for a LyraT board look as follows:
#include "AudioTools.h"
#include "AudioTools/AudioLibs/AudioSTK.h"
#include "AudioTools/AudioLibs/AudioBoardStream.h"
AudioInfo info(44100, 2, 16);
AudioBoardStream i2s(LyratV43); // or use I2SStream
MultiOutput split;
OutputMixer<int16_t> mixer(i2s, 2); // output mixer with 2 outputs
BufferedStream effects_buffer(1024, mixer);
BufferedStream direct_buffer(1024, mixer);
AudioEffectStream effects(effects_buffer);
StreamCopy copier(split, i2s, 1024); // copy i2sStream
Fuzz fuzz(6.5, 500);
STKChorus chorus(6000);
PitchShift pitch(0.5);
Boost boost(1.0);
// Arduino Setup
void setup(void) {
Serial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
// setup multioutput
split.add(effects);
split.add(direct_buffer);
// setup effects
//effects.addEffect(fuzz);
//effects.addEffect(chorus);
//effects.addEffect(boost);
//effects.addEffect(pitch);
effects.begin(info);
// setup i2s
auto cfg = i2s.defaultConfig(RXTX_MODE);
cfg.copyFrom(info);
cfg.sd_active = false;
cfg.input_device = ADC_INPUT_LINE2;
cfg.buffer_size = 256;
cfg.buffer_count = 3;
i2s.begin(cfg);
i2s.setVolume(1.0);
// setup mixer
//mixer.setWeight(0, 1.0); // dynamically adjust weights
//mixer.setWeight(1, 1.0);
mixer.begin(1024);
}
// Arduino loop - copy data
void loop() {
copier.copy();
}
I was selecting the LyraT because there it is possible to define the aux input source properly.
It might be a good idea to replace the 1024 values that are scattered across the Sketch with one constant that is defined in the beginning. I have the gut feeling that it’s possible to decrease this value to optimize the RAM use.
I am still impressed that it is possible to write such a complex logic with only 50 lines!
0 Comments