Using custom library to process an input audio signal on an ESP32 leads to muffled sound and nothig else

Hello,
wahEffectUsingCustomLibrary.zip (7.7 KB)

I am working on a semester project with 3 others from my university. We are wanting to create a multi effects pedal for an electric guitar as we are specializing in DSP. For this project we have chosen to use an ESP32 Wroom coupled with the Sparkfun WM8960 Audio Codec breakout board. We have successfully managed to create the three main effects we were working on (wah, distortion and delay) on their own as well as implement them into one collective main file.

This however creates a rather long and somewhat cluttered main file, so we are trying to create a custom library where we can place the function definitions, and then just call them through a header file. As a test we tried to create a header file for the wah-effect as it is the smallest function out of the three, and thus we thought it would be the easiest one to implement through this. This effect in all its simplicity adjusts the center frequency of a very narrow bandpass filter (created using a digital state variable filter structure).

But as as the title suggests while the code compiles and uploads just fine, when sending in audio all that is output is a rather muffled version of the input, there is no adjustment of the center frequency as far as we can hear or see using oscilloscope. The surrounding code around where the function is called is more or less the same. Since this is the first time we are creating our own library it is very possible that we are doing something wrong. We followed this guide. We have placed the .h and .cpp files in their own sub-folder under \user\Documents\Arduino\libraries.

I don't know if there is anyone who can see what we might be doing wrong, but any help is greatly appreciated. I have attached a working version of our code, a version using the custom library and both the cpp and h file.

Kind regards,
Joans

AI dribble is never very helpful. Neither is this; it just takes up screen space. Reported in the expectation that it'll be deleted & account banned.

Mod edit:
AI generated dribble deleted.

OK, to clarify, did you get a satisfactory output before you started restructuring the code? If so, can you create a minimal version that demonstrates the problem, and post an also minimal version that demonstrates the working approach without restructuring it? Remove anything that's not required to demonstrate the problem.

Also please post the code here between code brackets instead of offering a ZIP file; many people post from phones/tablets and they're not going to root around in ZIP files - neither will most people on desktops (including me). Please remove all barriers and put people in the best position to help you.

When it comes to muffled sound, it's necessary to verify that the circuit makes sense and that it doesn't contribute to the problem. You mention the audio breakout board (this one?) - are you using it with the onboard class D 1W power amplifier and if so, what kind of speaker(s)/voicecoils do you use? Or do you have it hooked up to a different kind of output - if so, which?

Can you confirm that application examples as offered by Sparkfun do produce the desired/expected sound quality?

Hello rsmls,

Sorry for the late reply. I have inserted the various examples of the code in this reply. As for the breakout board, yes that is the correct one, and no we are not using the power amplifier on the board itself, as it is connected to a guitar amplifier with a 6.3mm jack. The examples from the Sparkfun WM8960 library do give the desired result, as do our own code when not calling the function through a header file.

We have created a short video demonstrating the issue, this can be found here: https://www.youtube.com/watch?v=EMAZWze6yJU

Here is the minimal versions of the code, sadly they cannot be made any shorter since there is a lot of initialization for the codec, and creating an example where random values in the range of an int16_t are generated seems kind of moot, since the problem is not output vs. no output, but rather output vs. wrongly altered output.

Minimal working example:

#include <Wire.h>
#include <math.h>
#include <SparkFun_WM8960_Arduino_Library.h>

// Click here to get the library: http://librarymanager/All#SparkFun_WM8960

WM8960 codec;

// Include I2S driver
#include <driver/i2s.h>

// Connections to I2S
#define I2S_WS 25
#define I2S_SD 17
#define I2S_SDO 4
#define I2S_SCK 16
// Use I2S Processor 0
#define I2S_PORT I2S_NUM_0

// Define input buffer length
#define bufferLen 64 //Define the buffer length
int16_t lBuffer[bufferLen]; //Create the buffer that is used to store the input data
int16_t TXBuffer[2 * bufferLen], RXBuffer[2 * bufferLen]; //Create the receive and the transmit buffers
float Fc = 500; //Initialise the centre frequency to be 500Hz
int16_t Q = 15; // Use a quality factor of 15
float Q1 = 1.0 / Q;  // Create a reciprocal of the quality factor, to be used in the filter coeficients
int32_t Fs = 44100; //Create a variable for the smapling frequency
float  F1 = 2 * sin((3.14 * Fc) / Fs); //Create the first filter coefficinet
float q = 1 - F1 * Q1; //Create the second filter coefficient
//Initialise the delay coefficients
float x_delay_1 = 0; 
float y_delay_1 = 0;
float y_delay_2 = 0;
bool increasing = true; //Create the boolean for whether the centre frequency should be increasing or decreasing.

//Setup standard from the I2S Passthrough exmaple from the SparkFun_WM8960_Arduino_Library. 
void setup() {
  Serial.begin(115200);
  Wire.begin();
  if (codec.begin() == false)
  //Begin communication over I2C
  {
    Serial.println("The device did not respond. Please check wiring.");
    while (1)
      ;  // Freeze
  }

  Serial.println("Device is connected properly.");
  codec_setup();
  // Set up I2S
  i2s_install();
  i2s_setpin();
  i2s_start(I2S_PORT);
}

int16_t autoWah(int16_t x) {
  x = x >> 1; 				//Bit shifting the input to avoid overflow
  float y; 					//Function output definition
  y = F1 * x; 				//Calculation of the first index in the input buffer
  y = y - F1 * x_delay_1; 	//Calculation of the delayed index in the input buffer, at index 0 the value of x_delay_1 will be 0
  y = (y - (F1 * F1 - q - 1) * y_delay_1); 		//Calculation of the first delayed output. At index 0, y_delay_1 will be 0.
  y = y - q * y_delay_2; 	//Calculation of the second delayed output. At index 0 and 1 this will be 0. 

	//Update variable values
  x_delay_1 = x; 			//The current input is set as the singularly delayed input, as the current x-value will represent the delayed input at the next index.  
  y_delay_2 = y_delay_1; 	//The double delayed output is set as the singularly delayed output, as y_delay_1 will represent the double delayed output as the next index.
  y_delay_1 = y; 			//The output is set as the single delayed output, as y will represent the single delayed output at the next index. 

  return y; 				//The function returns a processed signal that is dependant upon a current input, a singularly delayed input, a singularly delayed output and a double delayed output. 
}

void loop() {
  // Get I2S data and place in data buffer
  size_t bytesIn = 0; //From SparkFun_WM8960_Arduino_Library
  size_t bytesOut = 0; //From SparkFun_WM8960_Arduino_Library
  esp_err_t result; //
  result = i2s_read(I2S_PORT, &RXBuffer, bufferLen * 4, &bytesIn, portMAX_DELAY);

  if (result == ESP_OK) {
	  //Checks the values of the input, and corrects them if they exceed the thresholds defined in the 'if else'-statement.
    for (int i = 0; i < bufferLen; i++) {
      if (25000 < RXBuffer[i * 2]) {
        lBuffer[i] = (RXBuffer[i * 2] - 32767);
      } else if (RXBuffer[i * 2] < -25000) {
        lBuffer[i] = (RXBuffer[i * 2] + 32768);
      } else {
        lBuffer[i] = (RXBuffer[i * 2]);
      }
    }

    F1 = 2 * sin((3.14 * Fc) / Fs); //Updates the F1 filter coeficient.
    q = 1 - F1 * Q1; //Updates the q filter coefficient.


    for (int i = 0; i < bufferLen; i++) {
        TXBuffer[i * 2] = autoWah(lBuffer[i]); //Places the processed signal in every other index in the buffer, as the codec reads the buffer indexes as Left-Right iterations, and we are only using the left output. 
    }
	
	//Checks the conditions for the centre frequency Fc, and either increase or decreases it depending on the state of the 'increasing'-bool. 
    if (Fc == 1500) {
      increasing = false;
    } else if (Fc == 500) {
      increasing = true;
    }
    if (increasing == true) {
      Fc = Fc + 2;
    } else {
      Fc = Fc - 2;
    }
	
    result = i2s_write(I2S_PORT, &TXBuffer, bufferLen * 4, &bytesOut, portMAX_DELAY); //Sends the TXBuffer out through the codec. 
  } else {
    Serial.println("TX ERR. Check connections and variable names"); //Prints if there could not be established a connection to the codec. 
  }
}


//The remaining code below is standard to the SparkFun_WM8960_Arduino_Library I2S-Passthrough example, and has not been modified

void codec_setup() {
  // General setup needed
  codec.enableVREF();
  codec.enableVMID();
  // Setup signal flow to the ADC
  codec.enableLMIC();
  codec.enableRMIC();
  // Connect from INPUT1 to "n" (aka inverting) inputs of PGAs.
  codec.connectLMN1();
  codec.connectRMN1();
  // Disable mutes on PGA inputs (aka INTPUT1)
  codec.disableLINMUTE();
  codec.disableRINMUTE();
  // Set pga volumes
  codec.setLINVOLDB(0.00);
  //Valid options are - 17.25dB to + 30dB(0.75dB steps)
  codec.setRINVOLDB(0.00);  //
  //Valid options are - 17.25dB to + 30dB(0.75dB steps)
  // Set input boosts to get inputs 1 to the boost mixers
  codec.setLMICBOOST(WM8960_MIC_BOOST_GAIN_0DB);
  codec.setRMICBOOST(WM8960_MIC_BOOST_GAIN_0DB);
  // Connect from MIC inputs (aka pga output) to boost mixers
  codec.connectLMIC2B();
  codec.connectRMIC2B();

  // Enable boost mixers
  codec.enableAINL();
  codec.enableAINR();

  // Disconnect LB2LO (booster to output mixer (analog bypass)
  // For this example, we are going to pass audio throught the ADC and DAC
  codec.disableLB2LO();
  codec.disableRB2RO();
  // Connect from DAC outputs to output mixer
  codec.enableLD2LO();
  codec.enableRD2RO();

  // Set gainstage between booster mixer and output mixer
  // For this loopback example, we are going to keep these as low as they go
  codec.setLB2LOVOL(WM8960_OUTPUT_MIXER_GAIN_NEG_21DB);
  codec.setRB2ROVOL(WM8960_OUTPUT_MIXER_GAIN_NEG_21DB);
  // Enable output mixers
  codec.enableLOMIX();
  codec.enableROMIX();

  // CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d
  // freq at 705.6kHz
  codec.enablePLL();  // Needed for class-d amp clock
  codec.setPLLPRESCALE(WM8960_PLLPRESCALE_DIV_2);
  codec.setSMD(WM8960_PLL_MODE_FRACTIONAL);
  codec.setCLKSEL(WM8960_CLKSEL_PLL);
  codec.setSYSCLKDIV(WM8960_SYSCLK_DIV_BY_2);
  codec.setBCLKDIV(4);
  codec.setDCLKDIV(WM8960_DCLKDIV_16);
  codec.setPLLN(7);
  codec.setPLLK(0x86, 0xC2, 0x26);  // PLLK=86C226h
  //codec.setADCDIV(0); // Default is 000 (what we need for 44.1KHz)
  //codec.setDACDIV(0); // Default is 000 (what we need for 44.1KHz)
  codec.setWL(WM8960_WL_16BIT);
  codec.enablePeripheralMode();
  //codec.enableMasterMode();
  //codec.setALRCGPIO(); // Note, should not be changed while ADC is enabled.
  // Enable ADCs and DACs
  codec.enableAdcLeft();
  codec.enableAdcRight();
  codec.enableDacLeft();
  codec.enableDacRight();
  codec.disableDacMute();
  //codec.enableLoopBack(); // Loopback sends ADC data directly into DAC
  codec.disableLoopBack();
  // Default is "soft mute" on, so we must disable mute to make channels active
  codec.disableDacMute();
  codec.enableHeadphones();
  codec.enableOUT3MIX();  // Provides VMID as buffer for headphone ground
  Serial.println("Volume set to +0dB");
  codec.setHeadphoneVolumeDB(0.00);
  Serial.println("Codec Setup complete. Listen to left/right INPUT1 on Headphone outputs.");
}

void i2s_install() {
  // Set up I2S Processor configuration
  const i2s_driver_config_t i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX),
    .sample_rate = 44100,
    .bits_per_sample = i2s_bits_per_sample_t(16),
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,  //JDN
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_MSB),
    .intr_alloc_flags = 0,
    .dma_buf_count = 8,
    .dma_buf_len = bufferLen,
    .use_apll = false,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0,
    .mclk_multiple = i2s_mclk_multiple_t(I2S_MCLK_MULTIPLE_DEFAULT),
    .bits_per_chan = i2s_bits_per_chan_t(I2S_BITS_PER_CHAN_DEFAULT)
  };
  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
}

void i2s_setpin() {
  // Set I2S pin configuration
  const i2s_pin_config_t pin_config = {
    .mck_io_num = I2S_PIN_NO_CHANGE,
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = I2S_SDO,
    .data_in_num = I2S_SD

  };

  i2s_set_pin(I2S_PORT, &pin_config);
}

This code produces the desired output/effect where we have a center frequency that increases and decreases automatically.

Minimal example showing the problem:

#include <Wire.h>
#include <math.h>
#include <SparkFun_WM8960_Arduino_Library.h>
#include <wahHeader.h>
// Click here to get the library: http://librarymanager/All#SparkFun_WM8960

WM8960 codec;

//This is a version of our code, where we are using a custom library that we ourselves created. In this library is a singular function, the 'autoWah'-function.
//This was done as an experiment to see if we could create a custom library for our project, to save on space in our main file. 
//The only difference between this file and the working one is that we are not defining the function in this, but rather calling it from the header file, 
// with the necessary variables. The surrounding code around the last 'for'-loop in 'void loop' should be more or less identical to the working code. 



// Include I2S driver
#include <driver/i2s.h>

// Connections to I2S
#define I2S_WS 25
#define I2S_SD 17
#define I2S_SDO 4
#define I2S_SCK 16
// Use I2S Processor 0
#define I2S_PORT I2S_NUM_0

// Define input buffer length
#define bufferLen 64 //Define the buffer length
int16_t lBuffer[bufferLen]; //Create the buffer that is used to store the input data
int16_t TXBuffer[2 * bufferLen], RXBuffer[2 * bufferLen]; //Create the receive and the transmit buffers
float Fc = 500; //Initialise the centre frequency to be 500Hz
int16_t Q = 15; // Use a quality factor of 15
float Q1 = 1.0 / Q;  // Create a reciprocal of the quality factor, to be used in the filter coeficients
int32_t Fs = 44100; //Create a variable for the smapling frequency
float  F1 = 2 * sin((3.14 * Fc) / Fs); //Create the first filter coefficinet
float q = 1 - F1 * Q1; //Create the second filter coefficient
//Initialise the delay coefficients
float x_delay_1 = 0; 
float y_delay_1 = 0;
float y_delay_2 = 0;
bool increasing = true; //Create the boolean for whether the centre frequency should be increasing or decreasing. 

//Setup standard from the I2S Passthrough exmaple from the SparkFun_WM8960_Arduino_Library.

void setup() {
  Serial.begin(115200);
  Serial.println("Example 8 - I2S Passthough");
  Wire.begin();
  if (codec.begin() == false)
  //Begin communication over I2C
  {
    Serial.println("The device did not respond. Please check wiring.");
    while (1)
      ;  // Freeze
  }

  Serial.println("Device is connected properly.");
  codec_setup();
  // Set up I2S
  i2s_install();
  i2s_setpin();
  i2s_start(I2S_PORT);
}


void loop() {
  // Get I2S data and place in data buffer
  size_t bytesIn = 0;
  size_t bytesOut = 0;
  esp_err_t result;
  static int lll = 0;
  result = i2s_read(I2S_PORT, &RXBuffer, bufferLen * 4, &bytesIn, portMAX_DELAY);

  if (result == ESP_OK) {
	  //Checks the values of the input, and corrects them if they exceed the thresholds defined in the 'if else'-statement.
    for (int i = 0; i < bufferLen; i++) {
      if (25000 < RXBuffer[i * 2]) {
        lBuffer[i] = (RXBuffer[i * 2] - 32767);
      } else if (RXBuffer[i * 2] < -25000) {
        lBuffer[i] = (RXBuffer[i * 2] + 32768);
      } else {
        lBuffer[i] = (RXBuffer[i * 2]);
      }
    }

    F1 = 2 * sin((3.14 * Fc) / Fs); //Updates the F1 filter coeficient.
    q = 1 - F1 * Q1; //Updates the q filter coefficient.

    for (int i = 0; i < bufferLen; i++) {
        TXBuffer[i * 2] = autoWah(lBuffer[i], F1, q, x_delay_1, y_delay_1, y_delay_2); //Places the processed signal in every other index in the buffer, as the codec reads the buffer indexes as Left-Right iterations, and we are only using the left output.
      }
    
//Checks the conditions for the centre frequency Fc, and either increase or decreases it depending on the state of the 'increasing'-bool.
    if (Fc == 1500) {
      increasing = false;
    } else if (Fc == 500) {
      increasing = true;
    }
    if (increasing == true) {
      Fc = Fc + 2;
    } else {
      Fc = Fc - 2;
    }

    result = i2s_write(I2S_PORT, &TXBuffer, bufferLen * 4, &bytesOut, portMAX_DELAY); //Sends the TXBuffer out through the codec.
  } else {
    Serial.println("TX ERR. Check connections and variable names");
  }
}

void codec_setup() {
  // General setup needed
  codec.enableVREF();
  codec.enableVMID();
  // Setup signal flow to the ADC
  codec.enableLMIC();
  codec.enableRMIC();
  // Connect from INPUT1 to "n" (aka inverting) inputs of PGAs.
  codec.connectLMN1();
  codec.connectRMN1();
  // Disable mutes on PGA inputs (aka INTPUT1)
  codec.disableLINMUTE();
  codec.disableRINMUTE();
  // Set pga volumes
  codec.setLINVOLDB(0.00);
  //Valid options are - 17.25dB to + 30dB(0.75dB steps)
  codec.setRINVOLDB(0.00);  //
  //Valid options are - 17.25dB to + 30dB(0.75dB steps)
  // Set input boosts to get inputs 1 to the boost mixers
  codec.setLMICBOOST(WM8960_MIC_BOOST_GAIN_0DB);
  codec.setRMICBOOST(WM8960_MIC_BOOST_GAIN_0DB);
  // Connect from MIC inputs (aka pga output) to boost mixers
  codec.connectLMIC2B();
  codec.connectRMIC2B();

  // Enable boost mixers
  codec.enableAINL();
  codec.enableAINR();

  // Disconnect LB2LO (booster to output mixer (analog bypass)
  // For this example, we are going to pass audio throught the ADC and DAC
  codec.disableLB2LO();
  codec.disableRB2RO();
  // Connect from DAC outputs to output mixer
  codec.enableLD2LO();
  codec.enableRD2RO();

  // Set gainstage between booster mixer and output mixer
  // For this loopback example, we are going to keep these as low as they go
  codec.setLB2LOVOL(WM8960_OUTPUT_MIXER_GAIN_NEG_21DB);
  codec.setRB2ROVOL(WM8960_OUTPUT_MIXER_GAIN_NEG_21DB);
  // Enable output mixers
  codec.enableLOMIX();
  codec.enableROMIX();

  // CLOCK STUFF, These settings will get you 44.1KHz sample rate, and class-d
  // freq at 705.6kHz
  codec.enablePLL();  // Needed for class-d amp clock
  codec.setPLLPRESCALE(WM8960_PLLPRESCALE_DIV_2);
  codec.setSMD(WM8960_PLL_MODE_FRACTIONAL);
  codec.setCLKSEL(WM8960_CLKSEL_PLL);
  codec.setSYSCLKDIV(WM8960_SYSCLK_DIV_BY_2);
  codec.setBCLKDIV(4);
  codec.setDCLKDIV(WM8960_DCLKDIV_16);
  codec.setPLLN(7);
  codec.setPLLK(0x86, 0xC2, 0x26);  // PLLK=86C226h
  //codec.setADCDIV(0); // Default is 000 (what we need for 44.1KHz)
  //codec.setDACDIV(0); // Default is 000 (what we need for 44.1KHz)
  codec.setWL(WM8960_WL_16BIT);
  codec.enablePeripheralMode();
  //codec.enableMasterMode();
  //codec.setALRCGPIO(); // Note, should not be changed while ADC is enabled.
  // Enable ADCs and DACs
  codec.enableAdcLeft();
  codec.enableAdcRight();
  codec.enableDacLeft();
  codec.enableDacRight();
  codec.disableDacMute();
  //codec.enableLoopBack(); // Loopback sends ADC data directly into DAC
  codec.disableLoopBack();
  // Default is "soft mute" on, so we must disable mute to make channels active
  codec.disableDacMute();
  codec.enableHeadphones();
  codec.enableOUT3MIX();  // Provides VMID as buffer for headphone ground
  Serial.println("Volume set to +0dB");
  codec.setHeadphoneVolumeDB(0.00);
  Serial.println("Codec Setup complete. Listen to left/right INPUT1 on Headphone outputs.");
}

void i2s_install() {
  // Set up I2S Processor configuration
  const i2s_driver_config_t i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX),
    .sample_rate = 44100,
    .bits_per_sample = i2s_bits_per_sample_t(16),
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,  //JDN
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_MSB),
    .intr_alloc_flags = 0,
    .dma_buf_count = 8,
    .dma_buf_len = bufferLen,
    .use_apll = false,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0,
    .mclk_multiple = i2s_mclk_multiple_t(I2S_MCLK_MULTIPLE_DEFAULT),
    .bits_per_chan = i2s_bits_per_chan_t(I2S_BITS_PER_CHAN_DEFAULT)
  };
  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
}

void i2s_setpin() {
  // Set I2S pin configuration
  const i2s_pin_config_t pin_config = {
    .mck_io_num = I2S_PIN_NO_CHANGE,
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = I2S_SDO,
    .data_in_num = I2S_SD

  };

  i2s_set_pin(I2S_PORT, &pin_config);
}

This code does not behave as expected and rather than oscillating the center frequency there is an oscillating amplitude which is a new problem that has just arisen.

Cpp file:

#include "wahHeader.h"


//This function takes an input x, and processes it according to the following difference equation:
// y[n] = F1*x[n] - F1*x[n-1] - (F1*F1 - q - 1)*y[n-1] - q*y[n-2]
int16_t autoWah(int16_t x, float f1Scalar, float qScalar, float xDelay1, float yDelay1, float yDelay2) {
  x = x >> 1; 				//Bit shifting the input to avoid overflow
  float y; 					//Function output definition
  y = f1Scalar * x; 				//Calculation of the first index in the input buffer
  y = y - f1Scalar * xDelay1; 	//Calculation of the delayed index in the input buffer, at index 0 the value of x_delay_1 will be 0
  y = (y - (f1Scalar * f1Scalar - qScalar - 1) * yDelay1); 		//Calculation of the first delayed output. At index 0, y_delay_1 will be 0.
  y = y - qScalar * yDelay2; 	//Calculation of the second delayed output. At index 0 and 1 this will be 0. 

	//Update variable values
  xDelay1 = x; 			//The current input is set as the singularly delayed input, as the current x-value will represent the delayed input at the next index.  
  yDelay2 = yDelay1; 	//The double delayed output is set as the singularly delayed output, as y_delay_1 will represent the double delayed output as the next index.
  yDelay1 = y; 			//The output is set as the single delayed output, as y will represent the single delayed output at the next index. 

  return y; 				//The function returns a processed signal that is dependant upon a current input, a singularly delayed input, a singularly delayed output and a double delayed output. 
}

Here is the cpp for the library. The function is called using the exact same name, and with the exact same operations internal to the function, though with variable names having been changed.

Header file:

#ifndef WAHHEADER_H
#define WAHHEADER_H


#include <Arduino.h>

int16_t autoWah(int16_t x, float f1Scalar, float qScalar, float xDelay1, float yDelay1, float yDelay2);

 
 #endif

Replying to myself to say that we solved the issue. Our problem was with the delay variables.

float x_delay_1;
float y_delay_1;
float y_delay_2;

These were globally defined as floats at the start. The problem with that is that the delay coefficients in the function call (and in the function in general) are local to the function. We suspect that since these were global that they would not be defined for each sample, but rather for each buffer. This in theory removes the filter aspect of the state variable filter implementation, reducing it to a simple scaling dependent on the center frequency and thus creating this oscillation in the amplitude of the signal. The working header file now looks like this.

Cpp file:

#include "wahHeader.h"

//This function takes an input x, and processes it according to the following difference equation:
// y[n] = F1*x[n] - F1*x[n-1] - (F1*F1 - q - 1)*y[n-1] - q*y[n-2]
int16_t autoWah(int16_t x, float f1Scalar, float qScalar) {
  static float xDaly1; //The singular delay of the input
  static float yDelay1; // Singular delay of the output
  static float yDelay2; //Double delayed output
  
  
  x = x >> 1; 				//Bit shifting the input to avoid overflow
  float y; 					//Function output definition
  y = f1Scalar * x; 				//Calculation of the first index in the input buffer
  y = y - f1Scalar * xDelay1; 	//Calculation of the delayed index in the input buffer, at index 0 the value of x_delay_1 will be 0
  y = (y - (f1Scalar * f1Scalar - qScalar - 1) * yDelay1); 		//Calculation of the first delayed output. At index 0, y_delay_1 will be 0.
  y = y - qScalar * yDelay2; 	//Calculation of the second delayed output. At index 0 and 1 this will be 0. 

	//Update variable values
  xDelay1 = x; 			//The current input is set as the singularly delayed input, as the current x-value will represent the delayed input at the next index.  
  yDelay2 = yDelay1; 	//The double delayed output is set as the singularly delayed output, as y_delay_1 will represent the double delayed output as the next index.
  yDelay1 = y; 			//The output is set as the single delayed output, as y will represent the single delayed output at the next index. 

  return y; 				//The function returns a processed signal that is dependant upon a current input, a singularly delayed input, a singularly delayed output and a double delayed output. 
}

Header file:

#ifndef WAHHEADER_H
#define WAHHEADER_H

#include <Arduino.h>

int16_t autoWah(int16_t x, float f1Scalar, float qScalar);

 #endif

Hello rsmls,

We figured out what went wrong. I have created a separate reply outlining what went wrong and how it was solved. Sorry for taking up any of your time with such a small problem. Hope I have not caused too much of a distraction for you.

Kind regards,
Jonas

Not at all, glad to hear you've solved it! Good detective work; maybe it'll prove useful for future readers running into a similar issue.

Yeah, I hope so as well.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.