High frequency response of the State Variable (Chamberlin) Filter

Status
Not open for further replies.

Ardaudio

Member
I just started to explore the combination Teensy and Audio board and I'm stuck with a problem.

This is the diagram of the processing with the source code.
diagram.png
View attachment experiment_2_0a.ino

This is frequency response diagram that shows in red the frequency response of the Teensy Audio shield then output is connected directly to the input: i2s1 connected directly to i2s2 (aka DPS bypass). In blue the frequency response of the audio card I used for the measurements.
frequency linear.jpg

This below is the actual response of the filters. The blue line in the LPF output, the green line the HPF and the red line is the DSP bypass.

What worries me is the loss of high frequencies when the filter is introduced. The cut off frequency is set at 100Hz and the high frequency loss kicks in a 4kHz. At 10kHz is around -2dB and 15kHz -3.7dB.

For a digital filter is not a great frequency response. Did you notice anything similar before? Am I doing something wrong?
filters.jpg

P.S. I enabled IMPROVE_HIGH_FREQUENCY_ACCURACY and IMPROVE_EXPONENTIAL_ACCURACY in filter_variable.cpp but it didn't make any visible difference.
 
It gets weirder and weirder. I tried with another project that doesn't involve filters:
mixers diagram.png
View attachment experiment_2_0e_mixer.ino

The resulting frequency response is this:
mixers freq.png
In red the frequency response with i2s1 connected directly to i2s2. In green the frequency response of the project.

Please note that the only thing that I changed between the two frequency response measurements is the piece of software generated by GUItool.

Am I the only one that experienced odd and unpredictable frequency response at high frequencies?

(for the frequency measurement I used HOLMImpulse. Free download)
 
That is very strange. Might be a bug in the filter code?

How are you doing these tests? What hardware & software do I need to recreate the same test and get those plots?
 
Hi Paul,

The tests were very straight forward. I connected:

Audio card output (Edirol FA-101, output 3) to both channel of the line input of the audio board.
Audio board line output to audio card input (input 2).

The software that I used to measure the frequency response is HOLMImpulse. It is a free download.

The source code is available in the first post.

I agree. It is very strange. Please note that in this diagram:
filters.jpg
The only difference between the red line and the green line is the software. Nothing changed regarding hardware or connections.
 
It doesn't look like there is an overwhelming interest for this issue but just in case, I think I managed to find the exact cut off frequency of the attenuation. The -3dB point looks located a 11kHz. It is suspiciously close to 44.1Khz/4 but it might be just a coincidence.
 
Ardaudio said:
It is suspiciously close to 44.1Khz/4 but it might be just a coincidence.

That almost rings a bell.

I have faint memories of the Chamberlin SVF being unstable above fs/4. I just leafed through Hal's book, but I'm not finding it right now.

There's a link in the source file to a Music DSP thread:
http://www.musicdsp.org/showArchiveComment.php?ArchiveID=92

Where there's a line that seems to indicate that the 2x oversampling moves that stability limit to fs/2...there's also a commenter implying that the 2x oversampling is pretty crude, just recalculating with the same input sample a second time.

But am I reading your second post correctly; are you seeing this HF falloff with only a mixer (no filters at all) in the audio chain, but not seeing the falloff if input leads directly to output?
 
(...)I have faint memories of the Chamberlin SVF being unstable above fs/4

If this were the case, it would be a little bit of a disaster for me. With the biquad filter unable to go below 400Hz and the Chamberlin cutting above 11kHz, I'd have to abandon the Teensy+audio idea. I planned to use it professionally for a couple of products but I can't do it without filters.

But am I reading your second post correctly; are you seeing this HF falloff with only a mixer (no filters at all) in the audio chain, but not seeing the falloff if input leads directly to output?

Yes, you are reading the second post correctly. Frankly, when I saw the frequency response, my jaw dropped.

As you said, if I connect the input block directly to the output block, everything is as expected: apart from a gentle roll-off at the lower frequencies, the frequency response is linear. When I connected the mixers the high frequencies were attenuated.

It is so weird that I don't know what to think. Can it be a piece of code that remained active after I removed the filter and I designed the new diagram?

I double checked that the only thing that changed was the software. I didn't touch any cable, controls on the audio card or options in the software I used for measuring the frequency response.

On a general note, I am really surprised nobody noticed this before. Frequency response and distortion are the first things that usually people check in an audio system. I find hard to believe the nobody checked the actual frequency response during the development of the filters. Perhaps it was checked but I'm doing something wrong? I'm confused.
 
Can it be a piece of code that remained active after I removed the filter and I designed the new diagram?

If there's a chance that you changed the program, but it didn't upload, then the previous one would still be active. It's very unlikely that if you changed it, and the upload was complete, that there's somehow "old code" hanging around.

Actually, there's one other place that old code might persist - in the codec chip on the audio board. The codec has a pile of features inside, including filters - check the datasheet for details. If those filters got set, they might persist, even when loading new programs. Similarly, most audio DACs perform significant oversampling, which is in effect another filter - but the SGTL5000 datasheet doesn't ever use the word oversampling. It sounds too good to not be using it.

Try a patch where the left input leads straight to the left output, but the right channel has some different blocks inserted - a mixer, a multiplier. Then you can compare HF behavior, perhaps nailing it down to specific blocks. I don't have a ready explanation as to why the mixer would alter the frequency response. You might also check to see if the headphone output is any different - be warned, it's DC coupled.

If you want filters that don't do funny things at fs/4 and fs/2, you might consider an analog solution...or a higher-res DSP solution that puts fs/4 above the audible band.

Frequency response and distortion are the first things that usually people check in an audio system. I find hard to believe the nobody checked the actual frequency response during the development of the filters.

It's a $15 board that is far superior to the 8-bit mangling I'd been doing before. I'm not looking this gift horse in the mouth - my ears tell me it's good enough.
 
If there's a chance that you changed the program, but it didn't upload, then the previous one would still be active. It's very unlikely that if you changed it, and the upload was complete, that there's somehow "old code" hanging around.

I entirely agree. The hypothesis of old code hanging around is pretty unlikely.

Everything is possible but I'm pretty confident I uploaded the program before the test. I repeated the test to be sure.

Try a patch where the left input leads straight to the left output, but the right channel has some different blocks inserted - a mixer, a multiplier. Then you can compare HF behavior, perhaps nailing it down to specific blocks. I don't have a ready explanation as to why the mixer would alter the frequency response. You might also check to see if the headphone output is any different - be warned, it's DC coupled.

It's a good suggestion - thanks! I'll see if I can narrow it down to a set of simple cases that point in a clear direction.

I will also re-test the first example just to be on the safe side.

If you want filters that don't do funny things at fs/4 and fs/2, you might consider an analog solution...or a higher-res DSP solution that puts fs/4 above the audible band.

Thanks for the suggestion B.Jacquot but I already use analogue solutions. I would like to be sure that the issues I'm experiencing are due to hardware limitations and not software bugs before throwing in the bin the idea of using Teensy+audio. I get your point though.

It's a $15 board that is far superior to the 8-bit mangling I'd been doing before. I'm not looking this gift horse in the mouth - my ears tell me it's good enough.

Fair enough. If it sounds good to your ears and everything works as it should for your applications, it's a good thing.

My observation was due to the fact that I usually try to get objective measures when I work on something. It doesn't mean that my way of operating is better in absolute terms. It's just the way I work and I was taught. My apologies if my comment sounded judgemental and/or disrespectful.
 
I run further tests:

== filters =================

I re-run the same tests of first post. I uploaded multiple times with a frequency response test after every upload. I also tested the frequency before and after a board reboot (power off/on). The result was always the same.

I also tried with the headphone output. The frequency response is the same of the line output.

== mixer ===================

This is a little bit more complicated and weird. This is the code I used.

Code:
/*
* f_response_1_0
*
*
*
*/
 
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=87,167
AudioMixer4              mixer1;         //xy=300,239
AudioMixer4              mixer2;         //xy=506,302
AudioOutputI2S           i2s2;           //xy=530,169
AudioConnection          patchCord1(i2s1, 0, mixer1, 0);
AudioConnection          patchCord2(i2s1, 0, i2s2, 0);
AudioConnection          patchCord3(i2s1, 1, mixer1, 1);
AudioConnection          patchCord4(mixer1, 0, i2s2, 1);
AudioConnection          patchCord5(mixer1, 0, mixer2, 0);
AudioConnection          patchCord6(mixer1, 0, mixer2, 1);
AudioControlSGTL5000     sgtl5000;       //xy=86,41
// GUItool: end automatically generated code



/***********************************************
***** setup() **********************************
***********************************************/
void setup()
{
  /* 
  * Audio interface/objects initialization
  */
  AudioMemory(100);
  sgtl5000.enable();         // Enable the audio shield
  sgtl5000.inputSelect( AUDIO_INPUT_LINEIN );  // enable line-in
  sgtl5000.volume(0);        // disable headphone out
  sgtl5000.muteHeadphone();  // disable headphone out
  sgtl5000.micGain(0);       // minimum gain for mic in
  sgtl5000.unmuteLineout();  // enable line out
  sgtl5000.lineInLevel(5);   // 3.13V p-p
  sgtl5000.lineOutLevel(29); // 3.16V p-p
  sgtl5000.dacVolume (1); // set the pre-dac attenuation (1=no attenuation)
  sgtl5000.audioProcessorDisable();
  /*----------------------------------------*/


  mixer1.gain(0, 0.5);
  mixer1.gain(1, 0.5);
  mixer1.gain(2, 0.0);
  mixer1.gain(3, 0.0);

  mixer2.gain(0, 0.1);
  mixer2.gain(1, 0.1);
  mixer2.gain(2, 0.1);
  mixer2.gain(3, 0.1);
  
}


/***********************************************
***** loop *************************************
***********************************************/
void loop()
{
  int n = 0;
  
  //  n = 0  
  //  n = 1  
  //  n = 2
  
  switch( n )
  {
    case 0:
      mixer2.gain(0, 0.0);
      mixer2.gain(1, 0.0);
      mixer2.gain(2, 0.0);
      mixer2.gain(3, 0.0);
      break;
    case 1:
      mixer2.gain(0, 0.5);
      mixer2.gain(1, 0.5);
      mixer2.gain(2, 0.0);
      mixer2.gain(3, 0.0);
      break;
    case 2:
      mixer1.gain(0, 0.5);
      mixer1.gain(1, 0.5);
      mixer1.gain(2, 0.5);
      mixer1.gain(3, 0.5);
      break;
  }
}


// EOF

I uploaded the software and measured the frequency response. The frequency response was the same of the frequency diagram in the second post. I re-run the frequency analysis 3 times - same result.

Then I rebooted the board and the frequency response changed. It became linear. Again, measured 3 times - no changes.

I changed a comment to force a re-compile and uploaded again. The frequency response stayed linear.

I re-uploaded the filters software of the first post, measured the frequency, the result was the same as in the previous filter tests.


This is the interesting part: I compiled/uploaded the mixer test (i.e. the software of the second post) and measured the frequency. The high-frequency cut returned back. The frequency plot was the same of the second post.

I rebooted the board and the frequency response kept cutting the high frequency.

I rebooted the board again and the frequency response returned linear.

In all the tests above I didn't plug/unplug any connector. I only used keyboard and mouse.
 
A couple of quick questions:

When you say "reboot," exactly what steps are you taking?

Also, when the you see the HF droop, is it on both channels? If the channel that's direct input->output experiences it, it would seem to exonerate the mixer.

If that's true, a guess would be that the codec is either coming up in an unpredictable state and not being fully initialized, or something in it is being overwritten.
 
As you said, if I connect the input block directly to the output block, everything is as expected: apart from a gentle roll-off at the lower frequencies, the frequency response is linear. When I connected the mixers the high frequencies were attenuated.

It is so weird that I don't know what to think.

This is *really* weird indeed.

On a general note, I am really surprised nobody noticed this before. Frequency response and distortion are the first things that usually people check in an audio system. I find hard to believe the nobody checked the actual frequency response during the development of the filters.

When I wrote this filter code, I did quite a lot of testing with a function generator to the line-in and the filter to the line-out, viewing with my oscilloscope. I also did several tests with specific input signals and compared numerical output to a prototype of the code on my PC using double precision floats.

Of course, I also did quite a bit of listening, but losing the top octave is the sort of thing that could go unnoticed in casual listening. That's why I did most of the testing with a signal generator and oscilloscope.

Perhaps it was checked but I'm doing something wrong? I'm confused.

Might be a subtle bug in the code, or the SGTL5000, or the testing methodology. Very hard to guess.

Realistically, I probably won't be able to really dig into this until mid-June. It definitely is saved on my list of reproducible problems to investigate.
 
When you say "reboot," exactly what steps are you taking?
Sorry, it wasnt 'very clear in my post. I meant that I powered off the board for a couple of seconds and I switched on the power again afterwards.

Also, when the you see the HF droop, is it on both channels? If the channel that's direct input->output experiences it, it would seem to exonerate the mixer.
Good point. The HF drop was only on the right channel (i.e. the channel with the mixer). The left channel had the output connected directly to the input and there wasn't any HF drop.
 
This is *really* weird indeed.
I couldn't agree more Paul :)

Might be a subtle bug in the code, or the SGTL5000, or the testing methodology. Very hard to guess.
If you have any suggestion regarding further tests please let me know. I'd be very happy to help.

Realistically, I probably won't be able to really dig into this until mid-June. It definitely is saved on my list of reproducible problems to investigate.
I understand the situation and I appreciate your help on this. If you need any info, please post here or do feel free to contact me with a PM.

If I had to give a priority to the problems, I would give a higher priory to the high-frequency cut of the filter but you know better than anybody else what is the best way to move forward.
 
Sorry, it wasnt 'very clear in my post. I meant that I powered off the board for a couple of seconds and I switched on the power again afterwards.

OK, that's reasonable. My concern was that there's more than meets the eye going on when you press the button on the teensy itself - it'll invoke the bootloader and reflash the board if you've got the loader app running.

Good point. The HF drop was only on the right channel (i.e. the channel with the mixer). The left channel had the output connected directly to the input and there wasn't any HF drop.

That implicates the mixer, rather than the codec.

Can you try two more things:

  1. #1: swap the channels in the previous patch - put the passthru on the other side.
  2. #2: Try a patch that uses a multiply object instead of the mixers.
 
Status
Not open for further replies.
Back
Top