10 band Equalizer

Apr 1, 2013 at 1:49 AM
I'm trying to create a 10 band EQ (frequency based; beginning at 32hz, ending at 16khz). I found BiQuadFilter.PeakingEQ, but I cannot figure out what the "q" parameter is supposed to be.

Is it the peak width? If so, what is the best way to implement? Overlapping peaks?

Is PeakingEQ what I should be using or is there a better option?
Apr 2, 2013 at 8:23 PM
I figured out what "Q" is: http://www.homemadehitshow.com/EQTips.htm.

The BiQuadFilter class is private and seems to be... unused and forgotten. I decided to just create my own class to handle the peaking EQ. If anyone is interested (and I remember), I will post the code here.

If I remember to do it...
Coordinator
Apr 4, 2013 at 7:27 AM
Hi, the BiQuadFilter isn't really part of the official NAUdio API. I'd like to rework it at some point and make it a bit easier to use. You are right - q controls the peak width, but I don't know what the "standard" way to overlap bands in a 10 band eq is.
May 7, 2013 at 9:41 PM
Hi Robert,

Did you create a 10-band EQ and have it work well with the nAudio library? I wouldn't mind having a copy if it is possible so I can use it with my program...

Paul
May 10, 2013 at 6:34 PM
Currently working on a generic (reusable by everyone) solution.
May 15, 2013 at 7:33 AM
Edited May 15, 2013 at 9:16 AM
Hi Robert,

I have just completed a WPF prototype 10 band equaliser using yuvalnv's code on Google code as a starting point. The one thing I haven't quite got working yet is when you drag the thumb on a slider to keep the audio stream going. At the moment I have to pause the play, set the new values then start the play again. It's an inconvenience I would like to overcome.

Thinking one has to buffer the stream.

Jim.

PS - Mark, would you have any thoughts or suggestions that would be helpful ? Thanks.
May 15, 2013 at 1:38 PM
Hi James,

I was able to modify the equalizer settings (BassTreble only using the Audacity effect as a baseline) while audio was playing. I was using the NAudio library and the bufferedWaveProvider coupled with the CircularBuffer to ensure continuous playback. The waveout device would then read from the last provider to playback the modified buffers.

Paul
May 15, 2013 at 10:09 PM
Thanks Paul, that looks like the way to tackle it.

Will be some modification to the way the WaveStream is handled. I'm glad that we have Mark's mp3 streaming demo !

Jim
May 15, 2013 at 10:15 PM
Hi James,

Is there a way I could take a peek at the code. I am tasked to make a 27-band equalizer (similar to Audacity) and looking at a 10-band may help me along the way. We can take this offline if you wish, but don't know if there is a way to ping you without publishing my email address.

Paul
Coordinator
May 15, 2013 at 10:17 PM
If you are making effects for NAudio, then I strongly recommend using ISampleProvider as your base class rather than WaveStream. It will make your life much simpler. I'm hoping to get some example effects into a future version of NAudio to show what I mean.
May 15, 2013 at 11:38 PM
Edited May 15, 2013 at 11:40 PM
Thanks Mark for the tip.

Also I will take this opportunity to thank you for a great piece of work :)

brunp - I'm happy to post some code up and how I tackled it a bit later. The codes in a bit of a mess at the moment as I try to get it all working ! 27 Band wow ! What frequency range are you wanting to cover with that ?
I thought 20-20,000Hz as standard but I have had to get somebody with younger ears to confirm some of my tests ! I can't hear that range now !

Jim
May 22, 2013 at 7:02 PM
My calculations are working without causing any sound gaps, pops, or crackles; but I cannot hear the changes.

16hz through 500khz at 5db with the high frequencies at -5db should sound like the music is being muffled.

1000hz - 16000hz at 5db with low frequencies at -5db should sound tinny.

FilterSampleProvider:
  public class FilterSampleProvider : ISampleProvider
  {
    private readonly BiQuadFilter[] _sampleFilters;
    private readonly WaveFormat _waveFormat;
    private readonly ISampleProvider _sampleProvider;

    public WaveFormat WaveFormat { get { return _waveFormat; } }

    public FilterSampleProvider(ISampleProvider source, BiQuadFilter[] filters)
    {
      _sampleFilters = filters;
      _sampleProvider = source;
      _waveFormat = _sampleProvider.WaveFormat;
    }

    public int Read(float[] buffer, int offset, int sampleCount)
    {
      var read = _sampleProvider.Read(buffer, offset, sampleCount);

      var filterBuffer = new float[buffer.Length];

      //cascade the filters
      for (var j = _sampleFilters.Length; j-- > 0; )
      {
        if (j == _sampleFilters.Length - 1)
        {
          filterBuffer = _sampleFilters[j].ProcessSample(buffer, offset, sampleCount);

          continue;
        }

        filterBuffer = _sampleFilters[j].ProcessSample(filterBuffer, offset, sampleCount);
      }

      buffer = filterBuffer;

      return read;
    }
  }
I pre-calculate the coefficients when the dbGain or Q changes.

BiQuadFilter.ProcessSample:
    public float[] ProcessSample(float[] buffer, int offset, int sampleCount)
    {
      //skip processing if gain is 0 or we are recalculating due to a Gain or Q change
      if (_dbGain != 0f && !_calcInProgress)
      {
        //if Gain or Q changes during this process, ReCalculate will wait. Prevents odd results.
        _transformInProgress = true;

        var current = new float[buffer.Length];

        for (int i = 0; i < sampleCount; i += 2)
        {
          if (_counter == _blockAlign)
          {
            _counter = 0;

            Reset();
          }

          //Left channel
          var left = (_biQuadLeft.A0 * buffer[offset + i] + _biQuadLeft.A1 * _biQuadLeft.X1 + _biQuadLeft.A2
                          * _biQuadLeft.X2 - _biQuadLeft.A3 * _biQuadLeft.Y1 - _biQuadLeft.A4 * _biQuadLeft.Y1);

          _biQuadLeft.X2 = _biQuadLeft.X1;
          _biQuadLeft.X1 = buffer[offset + i];

          _biQuadLeft.Y2 = _biQuadLeft.Y1;
          _biQuadLeft.Y1 = double.IsNaN(left) ? 0 : left;

          current[offset + i] = (float)_biQuadLeft.Y1;

          //Right channel
          var right = (_biQuadRight.A0 * buffer[offset + i + 1] + _biQuadRight.A1 * _biQuadRight.X1 + _biQuadRight.A2
                            * _biQuadRight.X2 - _biQuadRight.A3 * _biQuadRight.Y1 - _biQuadRight.A4 * _biQuadRight.Y1);

          _biQuadRight.X2 = _biQuadRight.X1;
          _biQuadRight.X1 = buffer[offset + i + 1];

          _biQuadRight.Y2 = _biQuadRight.Y1;
          _biQuadRight.Y1 = double.IsNaN(right) ? 0 : right;

          current[offset + i + 1] = (float)_biQuadRight.Y1;

          _counter++;
        }

        _transformInProgress = false;

        return current;
      }

      return buffer;
    }

    private void Reset()
    {
      _biQuadLeft.X1 = 0;
      _biQuadLeft.X2 = 0;
      _biQuadLeft.Y1 = 0;
      _biQuadLeft.Y2 = 0;

      _biQuadRight.X1 = 0;
      _biQuadRight.X2 = 0;
      _biQuadRight.Y1 = 0;
      _biQuadRight.Y2 = 0;
    }
Coordinator
May 30, 2013 at 10:40 AM
you can't reassign buffer in the Read method. You must write into the buffer you were passed
Aug 16, 2013 at 8:30 AM
Hi Robert,

Have you done the 10 band Equalizer yet? Could I have a looked at the codes?

I think that 10 PeakingEQ biquad filters (time domain) may not get the effect as FFT filter does.

Have you got some results now, could you give some advice?

Many Thanks