Problems Plotting Filter Response

Status
Not open for further replies.

craiglindley

Well-known member
First let me say that I've just started to work with the Audio library today so I'm probably doing something stupid.

I am trying to do a basic frequency response plot of the AudioFilterStateVariable low pass filter. My idea was to sweep the frequency of sine wave oscillator connected to the input of the filter and use the AudioAnalyzeRMS object to pull back the filtered values so they can be plotted vs the input frequency. My sketch is as follows:

Code:
// Attempt to plot response of low pass filter

#include <Audio.h>

AudioSynthWaveformSine   osc;
AudioFilterStateVariable filter;
AudioAnalyzeRMS          detector;
AudioOutputI2S           i2s1;
AudioConnection          patchCord1(osc, 0, filter, 0);
AudioConnection          patchCord2(filter, 0, i2s1, 0);
AudioConnection          patchCord3(filter, 0, i2s1, 1);
AudioConnection          patchCord4(filter, 0, detector, 0);
AudioControlSGTL5000     audioShield;

void setup(void) {

  Serial.begin(115200);
  while (!Serial);
  delay(3000);

  AudioMemory(2);

  audioShield.enable();
  audioShield.volume(1.0);

  osc.amplitude(1.0);

  // Set filter cutoff frequency
  filter.frequency(1000);

  Serial.println("Begin Sweep");

  for (int f = 10; f < 20000; f += 10) {
    // Set osc frequency
    osc.frequency(f);

    // Let osc settle
    delay(1);

    // Wait for detector data to be available
    while (! detector.available()) {
      delay(1);
    }

    // Read the detected value
    float ampl = detector.read();

    Serial.printf("f = %d, ampl = %f\n", f, ampl);
  }

  Serial.println("End Sweep");
}

void loop() {

}

Unfortunately the detector never has data available for me to plot.

What am I doing wrong and is there a better way to accomplish this ?

Thanks
 
Increase AudioMemory to at least 6. The state variable filter appears to need up to 5 buffers.

Pete
 
Last edited:
Hi, I don't have an Audio Shield on hand right now, so I had to alter your code a bit, and I increased the memory, as el_supremo suggested. It works.

I also changed the settling time after the frequency change to be dependent on the frequency, because a static 1ms is not enough for one full oscillation period with low freq's.

Code:
// Attempt to plot response of low pass filter

#include <Audio.h>

AudioSynthWaveformSine   osc;
AudioFilterStateVariable filter;
AudioAnalyzeRMS          detector;
AudioOutputI2S           i2s1;
AudioConnection          patchCord1(osc, 0, filter, 0);
AudioConnection          patchCord2(filter, 0, i2s1, 0);
AudioConnection          patchCord3(filter, 0, i2s1, 1);
AudioConnection          patchCord4(filter, 0, detector, 0);

void setup(void) {

  Serial.begin(115200);
  while (!Serial);
  Serial.println("start");
  delay(3000);

  AudioMemory(10);


  osc.amplitude(1.0);

  // Set filter cutoff frequency
  filter.frequency(1000);

  Serial.println("Begin Sweep");

  for (int f = 10; f <= 20000; f += 10) {
    // Set osc frequency
    osc.frequency(f);

    // Let osc settle
    delay(10000/f+10);

    // Wait for detector data to be available
    while (! detector.available()) {
      delay(1);
    }

    // Read the detected value
    float ampl = detector.read();

    Serial.printf("f = %d, ampl = %f\n", f, ampl);
  }

  Serial.println("End Sweep");
}

void loop() {

}
 
Thanks for the tips, I increased the memory as well and it worked for me. I hadn't thought about the settling time however, another great tip
 
Did you end up with any nice frequency response plots? Kinda curious to see them. I spent a lot of time testing by comparing numbers against a reference program on my PC, and with live signals using a signal generator and oscilloscope... but never actually plotted any response curves.
 
I didn't actually plot the filter data I just looked at the amplitude in dB vs frequency in the serial monitor and could see the filters were working as I expected.

Thanks for all your help Paul
 
Paul, as a side note, I was wondering if you have ever come across my book, "Digital Audio with Java" which I wrote years ago? I asked because you are doing a lot of stuff in the Audio Library that I did in Java way back when. Thanks for publishing your library because it stirred my interest in digital audio once again. In fact I am working on a project using your audio shield and your library that I will talk more about when I get things finished up. Who knows maybe it will result in more sales for pjrc.

Cheers
 
I was reluctant to "test the Audio Lib with the Audio Lib" but the AudioAnalyzePeak object is pretty much exact.

There are still some glitches :confused:
Give it a go with the Arduino Serial Plotter. 500 points exactly fills the plotter window. Frequency axis is logarithmic.
Code:
#include <Audio.h>

AudioSynthWaveformSine		sine1;
AudioFilterBiquad			biquad1;
AudioAnalyzePeak			peak1;
AudioOutputI2S 				i2s1;
AudioConnection				patchCord1(sine1, 0, biquad1, 0);
AudioConnection				patchCord2(biquad1, 0, i2s1, 0);
AudioConnection				patchCord3(biquad1, 0, i2s1, 1);
AudioConnection				patchCord4(biquad1, 0, peak1, 0);

void setup(void) {
	Serial.begin(115200);
	while (!Serial);
	//Serial.println("start");
	delay(500);
	AudioMemory(30);
	sine1.amplitude(0.5); //6dB headroom
	
	//configure filter:
	biquad1.setLowShelf(0, 1000, -6, 8);
	
	double fStart = 20;	//min freq.
	double fEnd = 20000;	//max freq.
	int numPoints = 500;	//how many data points
	//sweep:
	for (int i = 0; i<numPoints; i++) {
		double freq = fStart * pow( nthRoot( (fEnd/fStart) ,numPoints-1) ,i);
		sine1.frequency(freq);
		delayMicroseconds(int(2E6/freq));	//wait some signal periods
		while (!peak1.available()) delay(10);
		double dB = 20*log10(peak1.read()*2.0);
		// Serial.println(freq);
		Serial.println(dB);
	}
}

void loop() {}

double nthRoot(double val, double n) {
	return pow(val, 1.0/n);
}
filterresponseplot.png
 
Paul, as a side note, I was wondering if you have ever come across my book, "Digital Audio with Java" which I wrote years ago?

Haven't seen it, until now. ;)

Does it have code samples? Are they usable in MIT license open source?


There are still some glitches :confused:

I'm looking at this now. The glitch is at 11028.41 Hz, which is almost exactly 1/4th of our 44117.647 Hz sample rate.

Here's what I believe is probably happening:

peaks.jpg

We're looking only at samples in the middle phase of the waveform. For fairly high frequencies that very close to a multiple of the sample frequency, even if we collect samples over 100 periods, we tend to keep sampling within the same phase angles and miss the peaks at 90 and 270 degrees.

I'm not sure if there's any good solution, other than massively upsampling and applying a really good (expensive) filter, and then analyzing that data. Maybe a future version of the peak detect object should offer than as an optional setting?
 
Of course, the proper fix is probably to use AudioAnalyzeRMS instead of AudioAnalyzePeak. But then at low frequencies you'll see interesting artifacts....

rms.png
 
IMHO it's pretty low priority. I'll do some tests with waveforms other than sine and see if it gets any worse.

Edit:
But then at low frequencies you'll see interesting artifacts....
Yeah that's why I switched to peak. Maybe add a note in the GUI to use the peak object when expecting low freq's?
 
Last edited:
I switched to a high shelf filter with negative gain and slope = 1 to make the frequency response monotonically decreasing, that way the harmonic spectrum of the non-sine signals won't affect the response plot too much.

sine.png

sawtooth.png

sawtooth_reverse.png

square.png

triangle.png

Code:
#include <Audio.h>

AudioSynthWaveform			waveform1;
AudioFilterBiquad			biquad1;
AudioAnalyzePeak			peak1;
AudioOutputI2S 				i2s1;
AudioConnection				patchCord1(waveform1, 0, biquad1, 0);
AudioConnection				patchCord2(biquad1, 0, i2s1, 0);
AudioConnection				patchCord3(biquad1, 0, i2s1, 1);
AudioConnection				patchCord4(biquad1, 0, peak1, 0);

void setup(void) {
	Serial.begin(115200);
	while (!Serial);
	//Serial.println("start");
	delay(500);
	AudioMemory(30);
	
	double fStart = 20;	//min freq.
	double fEnd = 20000;	//max freq.
	int numPoints = 500;	//how many data points

	waveform1.begin(0.5, fStart, WAVEFORM_TRIANGLE); //6dB headroom
	biquad1.setHighShelf(0, 1000, -6, 1);
	//sweep:
	for (int i = 0; i<numPoints; i++) {
		double freq = fStart * pow( nthRoot( (fEnd/fStart) ,numPoints-1) ,i);
		waveform1.frequency(freq);
		delayMicroseconds(int(2E6/freq));	//wait some signal periods
		while (!peak1.available()) delay(10);
		double dB = 20*log10(peak1.read()*2.0);
		// Serial.println(freq);
		Serial.println(dB);
	}
}

void loop() {}

double nthRoot(double val, double n) {
	return pow(val, 1.0/n);
}

Edit:
Maybe, once its matured, the upsampling that's currently in the making with the PT8211 DAC can be used in the peak object.
 
Last edited:
I posted this question on the Columbia DSP Music mail list. One very promising-looking reply came from Wen Xue :

I suggest the cubic spline interpolator. It expresses the underlying function as piecewise trinomial so that the maxima/minima can be computed by solving binomial equations. It is also known to be close to the ideal sync interpolation alias-wise.

Xue

Not sure I fully understand everything he means, or if cubic spline interpolation and then an algorithm for the min/max of the polynomial is something we can do 44117 times per second, but it sounds pretty interesting. ;)
 
Status
Not open for further replies.
Back
Top