- Muchas notas - Fran Acién

20250806 - Arduino Audio Tools and Faust

Recently I discovered Arduino Audio Tools that is a super cool library that unify the audio into arduino Streams. Basically buffers that can execute write and read.

Also it implements the drivers for I2S and Faust, that is exactly what I use. And works like a charm. Here is an example of a broken autotune, that doesnt work perfectly:

#define USE_MEMORY_MANAGER

#include <Arduino.h>
#include "AudioTools.h"
#include "AudioControlSGTL5000.h"
#include "AudioTools/AudioLibs/AudioRealFFT.h"
#include "AudioTools/AudioLibs/AudioFaust.h"
#include "Pitcher.h"

// I2S_INPUT -> SPLITTER -> FFT
//                       -> PitchShift -> I2S_OUTPUT

#define I2S_BCLK   3   // Bit clock
#define I2S_LRC    6   // Word select / LR clock
#define I2S_DOUT   4   // Data out
#define I2S_DIN    5   // Data in (not used here)
#define I2S_MCLK   0   // Master clock (optional)

#define SAMPLE_RATE 44100
#define BIT_DEPTH   16

AudioControlSGTL5000 audio;

// Create i2S stream as audio sink and source
I2SStream i2s_stream;

MultiOutput splitter;

AudioRealFFT fft; // or AudioKissFFT
FaustStream<mydsp> faust(i2s_stream);

StreamCopy copier(splitter, i2s_stream);    // (To, From)

// Audio info: sample rate, channels, bit depth
AudioInfo info(SAMPLE_RATE, 2, BIT_DEPTH);

// display fft result
void fftResult(AudioFFTBase &fft){
    float diff;
    auto result = fft.result();
    if (result.magnitude>100){
        Serial.print(result.frequency);
        Serial.print(" ");
        Serial.print(result.magnitude);  
        Serial.print(" => ");
        Serial.print(result.frequencyAsNote(diff));
        Serial.print( " diff: ");
        Serial.println(diff);

        faust.setLabelValue("shift_semitones", - diff / 10);
    }
}

void setup() {
  Serial.begin(115200);

  // I2S config
  auto cfg_i2s_in = i2s_stream.defaultConfig(RXTX_MODE);
  cfg_i2s_in.sample_rate = SAMPLE_RATE;
  cfg_i2s_in.bits_per_sample = BIT_DEPTH;
  cfg_i2s_in.channels = 2;
  cfg_i2s_in.pin_bck = I2S_BCLK;
  cfg_i2s_in.pin_ws = I2S_LRC;
  cfg_i2s_in.pin_data = I2S_DOUT;
  cfg_i2s_in.pin_data_rx = I2S_DIN;
  cfg_i2s_in.pin_mck = I2S_MCLK;
  cfg_i2s_in.use_apll = true;

  i2s_stream.begin(cfg_i2s_in);

  // Setup Faust
  auto faust_cfg = faust.defaultConfig();
  faust.begin(faust_cfg);
  faust.setLabelValue("shift_semitones", 1);

  // setup multi output
  splitter.add(faust);
  splitter.add(fft);

  if(!audio.enable()){
    Serial.println("Something wrong happened");
  }
  audio.volume(0.5);

  // Setup FFT
  auto tcfg = fft.defaultConfig();
  tcfg.length = 8192;
  tcfg.channels = 2;
  tcfg.sample_rate = SAMPLE_RATE;
  tcfg.bits_per_sample = BIT_DEPTH;
  tcfg.callback = &fftResult;
  fft.begin(tcfg);
}

void loop() {
  // Generate and write audio frames to I2S
  copier.copy();
}

Fixing the script

The error is strange. Seems that fft gives strange readings when applying modifications to the Faust. It seams that the output affects the input, buuuuut checking the code, there is no possibility of this.

  • Most probably there are not enought resources to perform a FFT while modifying values of Faust
  • Or…. I am callying modifyin Faust while being in a callback, which can affect the FFT
  • Soooo…. The solution would be to divide the logic in two cores, and one modify the other