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

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *