Menu

What sample rate does Eq APO use when calculating biquad coefficients?

2020-05-10
2020-05-11
  • Marc Grissom

    Marc Grissom - 2020-05-10

    Some Background:
    I currently working on generating my own EQ plots. I seem to get the same results as EQ APO if I choose a really large value for sample rate in my calculations. But I don't know how correct that really is.

    So my question is what value for variable 'srate' (Sample rate) is EQ APO effectively using? It's not tied to the Windows setting is it?

    Refer to BiQuad.cpp for the biquad coefficient equations.

     
  • Peter Verbeek

    Peter Verbeek - 2020-05-10

    For calculating a plot a fixed sample rate must used but the value itself doesn't matter. This is what I've noticed when creating the graph window of the Peace equalizer interface. But when Equalizer APO is processing the audio (stream) it needs to take the sample rate for the specific device. This sample rate is usually 48 kHz of 96 kHz. In other words, the sample rate is tied to the Windows setting of that specific device. Any deviation and the Biquad filter calculations will fail.

     
  • Marc Grissom

    Marc Grissom - 2020-05-10

    That's interesting. In my own calculations, sample rate does actually have an effect if your F0 frequncy is close to your SampleRate/2. Take a look at my attachement. The 3 curves are peaking filters at 18k, Q=1, dB=6.

    I'm using the exact same equations for calculation the Biquad coefficients. But maybe I'm evaluating the transfer function differently?

     
    • Peter Verbeek

      Peter Verbeek - 2020-05-11

      You're right. When nearing the sample rate/2 the frequency curve does seem different (which is obviously a normal "behavour" according to the calculations). Sorry, I didn't spot that. As example I've attached 2 curves: 44100 and 96000 Hz. The peak filter on 2 kHz isn't affected but the one on 20 kHz is. The same as your curves, I guess, although it looks a bit different, less effect towards the 10 kHz. I'll give my calculation program code below.

      One thing has to be noted. Since the sample rate is high (even 44100), the actual difference can't be easily heard. We're talking about frequencies above 10 kHz which our ears having more difficulty with than lower ones. But for accuracy the graph should be according to the set sample rate.

      The following AutoIt program code is part of Peace. CalculateCoefficient is a function which needs an input of a filter type, a gain in dB, a frequency and a quality to calculate the coefficients for a BiQuad filter. For your information the filter types are the known ones and also Linkwitz-Riley and Butterworth filters which are built up from low/high pass filters. The second function called GainAtfrequency takes these coefficients to calculate the output gain at a given frequency. This function includes also the calculations for the Linkwitz-Riley and Butterworth filters. Both functions are similar to those of Equalizer APO except in earlier versions of Equalizer APO the Gain At Frequency function had a bug (a wrong sign). I think it's now corrected in Equalizer APO.

      Func CalculateCoefficient($FilterNo,$FilterType,$dBGain,$Frequency,$Quality)    ; calculate coefficients of transfer function of a filter
          Local $Gain,$Omega,$Cosine,$Alpha,$Beta,$a0,$a1,$a2,$b0,$b1,$b2,$Factors,$FirstOrder
      
          If $FilterType = 0 Or $FilterType = 4 Or $FilterType = 5 Or $FilterType = 8 Or $FilterType = 9 Or $FilterType = 14 Or $FilterType = 15 Then     ; peak, low/high shelf
      ;~      beneath calculation not needed, not sure why
      ;~      ; calculate center frequency of LS/HS 6/12 dB filters)
      ;~      If $FilterType = 10 Then
      ;~          $Frequency *= 10^(($dBGain/80)/($Quality/12))
      ;~      ElseIf $FilterType = 11 Then
      ;~          $Frequency /= 10^(($dBGain/80)/($Quality/12))
      ;~      EndIf
              $Gain = 10^($dBGain/40)
          Else
              $Gain = 10^($dBGain/20)
          EndIf
          $Omega = $2Pi*$Frequency/$SampleFrequency;
          $Cosine = cos($Omega);
          If $FilterType = 4 Or $FilterType = 5 Or $FilterType = 8 Or $FilterType = 9 Or $FilterType = 14 Or $FilterType = 15 Then                        ; low/high shelf
              ; set or calculate slope factor
              If $FilterType = 4 Or $FilterType = 5 Then
                  $Quality = 0.9                                                  ; quality taken from Equalizer APO for low/high shelf
              ElseIf $FilterType = 8 Or $FilterType = 9 Then
                  $Quality /= 12                                                  ; quality division taken from Equalizer APO for low/high shelf with center frequency
              ElseIf $FilterType = 14 Or $FilterType = 15 Then
                  $Quality = 1/((1/($Quality*$Quality)-2)/($Gain+1/$Gain)+1)      ; quality to slope, Equalizer APO 1.2.1 and higher
              EndIf
              $Alpha = sin($Omega)/2*sqrt(($Gain + 1/$Gain)*(1/$Quality-1)+2)
              $Beta = 2*sqrt($Gain)*$Alpha
          Else
              $Alpha = sin($Omega)/(2*$Quality)
          EndIf
          Switch ($FilterType)
              Case 0      ; peak
                  $b0 = 1+($Alpha*$Gain)
                  $b1 = -2*$Cosine
                  $b2 = 1-($Alpha*$Gain)
                  $a0 = 1+($Alpha/$Gain)
                  $a1 = -2*$Cosine
                  $a2 = 1-($Alpha/$Gain)
              Case 1      ; low pass
                  $b0 = (1-$Cosine)/2
                  $b1 = 1-$Cosine
                  $b2 = (1-$Cosine)/2
                  $a0 = 1+$Alpha;
                  $a1 = -2*$Cosine;
                  $a2 = 1-$Alpha;
              Case 2      ; high pass
                  $b0 = (1+$Cosine)/2
                  $b1 = -(1+$Cosine)
                  $b2 = (1+$Cosine) /2
                  $a0 = 1+$Alpha
                  $a1 = -2*$Cosine
                  $a2 = 1-$Alpha
              Case 3      ; band pass
                  $b0 = $Alpha
                  $b1 = 0;
                  $b2 = -$Alpha
                  $a0 = 1+$Alpha
                  $a1 = -2*$Cosine
                  $a2 = 1-$Alpha
              Case 4,8,14 ; low shelf
                  $b0 = $Gain*(($Gain+1)-($Gain-1)*$Cosine+$Beta)
                  $b1 = 2*$Gain*(($Gain-1)-($Gain+1)*$Cosine)
                  $b2 = $Gain*(($Gain+1)-($Gain-1)*$Cosine-$Beta)
                  $a0 = ($Gain+1)+($Gain-1)*$Cosine+$Beta
                  $a1 = -2*(($Gain-1)+($Gain+1)*$Cosine)
                  $a2 = ($Gain+1)+($Gain-1)*$Cosine-$Beta
              Case 5,9,15 ; high shelf
                  $b0 = $Gain*(($Gain+1)+($Gain-1)*$Cosine+$Beta)
                  $b1 = -2*$Gain*(($Gain-1)+($Gain+1)*$Cosine)
                  $b2 = $Gain*(($Gain+1)+($Gain-1)*$Cosine-$Beta)
                  $a0 = ($Gain+1)-($Gain-1)*$Cosine+$Beta
                  $a1 = 2*(($Gain-1)-($Gain+1)*$Cosine)
                  $a2 = ($Gain+1)-($Gain-1)*$Cosine-$Beta
              Case 6      ; notch
                  $b0 = 1
                  $b1 = -2*$Cosine
                  $b2 = 1
                  $a0 = 1+$Alpha
                  $a1 = -2*$Cosine
                  $a2 = 1-$Alpha
              Case 7      ; all pass
                  $b0 = 1-$Alpha
                  $b1 = -2*$Cosine
                  $b2 = 1+$Alpha
                  $a0 = 1+$Alpha
                  $a1 = -2*$Cosine
                  $a2 = 1-$Alpha
              Case 10,12  ; low pass Butterworth and Linkwitz-Riley
                  If $FilterType = 10 Or $FilterType = 11 Then
                      $Factors = Floor($Quality/2)
                      $FirstOrder = 0
                  Else
                      $Factors = Floor($Quality/2)
                      $FirstOrder = Mod($Factors,2)
                      $Factors = Floor($Factors/2)
                      $Quality /= 2
                  EndIf
                  If $FirstOrder = 1 Then
                      $Alpha = sin($Omega)/(2*0.5)
                      $b0 = (1-$Cosine)/2
                      $b1 = 1-$Cosine
                      $b2 = (1-$Cosine)/2
                      $a0 = 1+$Alpha;
                      $a1 = -2*$Cosine;
                      $a2 = 1-$Alpha;
                      $TFb0[$FilterNo][0] = $b0/$a0
                      $TFb1[$FilterNo][0] = $b1/$a0
                      $TFb2[$FilterNo][0] = $b2/$a0
                      $TFa1[$FilterNo][0] = $a1/$a0
                      $TFa2[$FilterNo][0] = $a2/$a0
                  EndIf
                  For $k = $Factors To 1 Step -1      ; $Quality = number of orders n
                      $Alpha = sin($Omega)/(2*CalculateQuality($k,$Quality))
                      $b0 = (1-$Cosine)/2
                      $b1 = 1-$Cosine
                      $b2 = (1-$Cosine)/2
                      $a0 = 1+$Alpha;
                      $a1 = -2*$Cosine;
                      $a2 = 1-$Alpha;
                      $TFb0[$FilterNo][$k+$FirstOrder-1] = $b0/$a0
                      $TFb1[$FilterNo][$k+$FirstOrder-1] = $b1/$a0
                      $TFb2[$FilterNo][$k+$FirstOrder-1] = $b2/$a0
                      $TFa1[$FilterNo][$k+$FirstOrder-1] = $a1/$a0
                      $TFa2[$FilterNo][$k+$FirstOrder-1] = $a2/$a0
                  Next
              Case 11,13  ; high pass Butterworth and Linkwitz-Riley
                  If $FilterType = 10 Or $FilterType = 11 Then    ; Butterworth
                      $Factors = Floor($Quality/2)
                      $FirstOrder = 0
                  Else
                      $Factors = Floor($Quality/2)
                      $FirstOrder = Mod($Factors,2)
                      $Factors = Floor($Factors/2)
                      $Quality /= 2
                  EndIf
                  If $FirstOrder = 1 Then
                      $Alpha = sin($Omega)/(2*0.5)
                      $b0 = (1+$Cosine)/2
                      $b1 = -(1+$Cosine)
                      $b2 = (1+$Cosine)/2
                      $a0 = 1+$Alpha
                      $a1 = -2*$Cosine
                      $a2 = 1-$Alpha
                      $TFb0[$FilterNo][0] = $b0/$a0
                      $TFb1[$FilterNo][0] = $b1/$a0
                      $TFb2[$FilterNo][0] = $b2/$a0
                      $TFa1[$FilterNo][0] = $a1/$a0
                      $TFa2[$FilterNo][0] = $a2/$a0
                  EndIf
                  For $k = $Factors To 1 Step -1      ; $Quality = number of orders n
                      $Alpha = sin($Omega)/(2*CalculateQuality($k,$Quality))
                      $b0 = (1+$Cosine)/2
                      $b1 = -(1+$Cosine)
                      $b2 = (1+$Cosine)/2
                      $a0 = 1+$Alpha
                      $a1 = -2*$Cosine
                      $a2 = 1-$Alpha
                      $TFb0[$FilterNo][$k+$FirstOrder-1] = $b0/$a0
                      $TFb1[$FilterNo][$k+$FirstOrder-1] = $b1/$a0
                      $TFb2[$FilterNo][$k+$FirstOrder-1] = $b2/$a0
                      $TFa1[$FilterNo][$k+$FirstOrder-1] = $a1/$a0
                      $TFa2[$FilterNo][$k+$FirstOrder-1] = $a2/$a0
                  Next
          EndSwitch
      
          If Not ($FilterType >= 10 And $FilterType <= 13) Then   ; no Butterworth and Linkwitz-Riley low/high pass filters
              ; normalize by a0
              $TFb0[$FilterNo][0] = $b0/$a0
              $TFb1[$FilterNo][0] = $b1/$a0
              $TFb2[$FilterNo][0] = $b2/$a0
              $TFa1[$FilterNo][0] = $a1/$a0
              $TFa2[$FilterNo][0] = $a2/$a0
          EndIf
      EndFunc
      
      Func GainAtFrequency($Speaker,$FilterNo,$Frequency)         ; calculate gain at a frequency
          Local $Phi = (sin($2Pi*$Frequency/(2*$SampleFrequency)))^2,$Numerator,$Denominator,$Gain,$CascadeAmount,$Factors,$FirstOrder,$Quality
      
          If $Equalizer[$Speaker][4][$FilterNo] >= 10 And $Equalizer[$Speaker][4][$FilterNo] <= 13 Then   ; Butterworth and Linkwitz-Riley low/high pass filters
              $Gain = 0
              $Quality = $Equalizer[$Speaker][3][$FilterNo]
              If $Equalizer[$Speaker][4][$FilterNo] = 10 Or $Equalizer[$Speaker][4][$FilterNo] = 11 Then
                  $CascadeAmount = 1
                  $Factors = Floor($Quality/2)
                  $FirstOrder = 0
              Else
                  $CascadeAmount = 2
                  $Factors = Floor($Quality/2)
                  $FirstOrder = Mod($Factors,2)
                  $Factors = Floor($Factors/2)
                  $Quality /= 2
              EndIf
              If $FirstOrder = 1 Then
                  $Numerator = ($TFb0[$FilterNo][0]+$TFb1[$FilterNo][0]+$TFb2[$FilterNo][0])^2-4*($TFb0[$FilterNo][0]*$TFb1[$FilterNo][0]+4*$TFb0[$FilterNo][0]*$TFb2[$FilterNo][0]+$TFb1[$FilterNo][0]*$TFb2[$FilterNo][0])*$Phi+16*$TFb0[$FilterNo][0]*$TFb2[$FilterNo][0]*$Phi*$Phi
                  $Denominator = (1+$TFa1[$FilterNo][0]+$TFa2[$FilterNo][0])^2-4*($TFa1[$FilterNo][0]+4*$TFa2[$FilterNo][0]+$TFa1[$FilterNo][0]*$TFa2[$FilterNo][0])*$Phi+16*$TFa2[$FilterNo][0]*$Phi*$Phi
                  $Gain += 10*Log10($Numerator/$Denominator)
              EndIf
              For $k = $Factors+$FirstOrder-1 To $FirstOrder Step -1      ; $Quality = number of filter orders n
                  For $Cascade = 1 To $CascadeAmount
                      $Numerator = ($TFb0[$FilterNo][$k]+$TFb1[$FilterNo][$k]+$TFb2[$FilterNo][$k])^2-4*($TFb0[$FilterNo][$k]*$TFb1[$FilterNo][$k]+4*$TFb0[$FilterNo][$k]*$TFb2[$FilterNo][$k]+$TFb1[$FilterNo][$k]*$TFb2[$FilterNo][$k])*$Phi+16*$TFb0[$FilterNo][$k]*$TFb2[$FilterNo][$k]*$Phi*$Phi
                      $Denominator = (1+$TFa1[$FilterNo][$k]+$TFa2[$FilterNo][$k])^2-4*($TFa1[$FilterNo][$k]+4*$TFa2[$FilterNo][$k]+$TFa1[$FilterNo][$k]*$TFa2[$FilterNo][$k])*$Phi+16*$TFa2[$FilterNo][$k]*$Phi*$Phi
                      $Gain += 10*Log10($Numerator/$Denominator)
                  Next
              Next
              Return $Gain
          Else
              $Numerator = ($TFb0[$FilterNo][0]+$TFb1[$FilterNo][0]+$TFb2[$FilterNo][0])^2-4*($TFb0[$FilterNo][0]*$TFb1[$FilterNo][0]+4*$TFb0[$FilterNo][0]*$TFb2[$FilterNo][0]+$TFb1[$FilterNo][0]*$TFb2[$FilterNo][0])*$Phi+16*$TFb0[$FilterNo][0]*$TFb2[$FilterNo][0]*$Phi*$Phi
              $Denominator = (1+$TFa1[$FilterNo][0]+$TFa2[$FilterNo][0])^2-4*($TFa1[$FilterNo][0]+4*$TFa2[$FilterNo][0]+$TFa1[$FilterNo][0]*$TFa2[$FilterNo][0])*$Phi+16*$TFa2[$FilterNo][0]*$Phi*$Phi
              Return 10*Log10($Numerator/$Denominator)
          EndIf
      EndFunc
      
       
      • Marc Grissom

        Marc Grissom - 2020-05-11

        I looked at the EQ APO plotter and it does respond to the Windows system sample rate in the same way. When I first glanced at it, I thought it wasn't.

        So does this mean the way our programmed EQ filters sound can be affected (albeit slightly) by our sample rate setting?

        I'm wondering if the difference between 44k and 192k will start to matter when you have multiple filters set above 8k interacting with each other. Yesterday I was doing just that when I realized my plots (at fs=44k) looked different at a glance from Peace and my RME ADI-2 (fancy DAC with built in EQ).

         
        • Peter Verbeek

          Peter Verbeek - 2020-05-11

          I wasn't aware of this so it's great to hear that Equalizer APO responds to the sample rate. But there's one major catch. When adding filters in the Configurator Editor or Peace, all these filters will be applied on all devices. But each device could have its own sample rate. Therefore the graph of analysis panel is probably based on the sample rate of the current selected device. I'm guessing here. Could also be a fixed one. This can be checked in the program code.

          Yes, the sample rate can influence a filter (as Juha is also pointing out), the most at a lower sample rate say 44100. Fortunately sample rates of devices are set much higher, 96 kHz or 192 kHz. Audio played of a lower sample rate is converted to this higher rate. In other words in general applying filters is okay then. And using a sample rate of 96 kHz for a plot is also okay. If your RME ADI-2 uses a sample rate of 44100 you'll see a noticable difference of course, especially with multiple high frequency filters.

           
          • Marc Grissom

            Marc Grissom - 2020-05-11

            I myself have always used a setting of 192k for my audio. So it's no issue for me. But whenever you see EQ presets posted online (like the Auto EQ project) no one ever mentions the sample rate. So I'm sure there are some other users getting slightly worse EQ results because they left the their sample rate setting as the default.

            The ADI-2 DAC will accept any frequncy you throw at it. Interestingly, the built-in plotter doesn't respond to any change in sample rate. I wonder if it's using a different filter method. I don't believe it's upsampling the audio prior to any DSP functions.

             
            • Juha

              Juha - 2020-05-11

              The ADI-2 DAC will accept any frequncy you throw at it. Interestingly, the built-in plotter doesn't respond to any change in sample rate. I wonder if it's using a different filter method. I don't believe it's upsampling the audio prior to any DSP functions.

              It might also be that ADI-2 uses s-domain filters for its plots so,... as those are continious time filters there aren't that N limit present. To find it out, you could measure responses of the ADI-2 internal filters (loop-back).

               

              Last edit: Juha 2020-05-11
              • Marc Grissom

                Marc Grissom - 2020-05-11

                I think the developers would actually be happy to anwer questions about their DSP functionality on the RME forum. So I'll probably try that later.

                 
    • Juha

      Juha - 2020-05-11

      Your plot looks normal.
      Certain type discrete time filters tends to 'skew' when fc closes N (Nyqvist) frequency (fs/2). RBJ-filters also may work differently with quality parameter Q than with BW. This is normal 'effect' but, also it can be improved a bit by using some tweaking methods. Unfortunately you need to go custom IIR filters then which are not CPU friendly in EqualizerAPO.

      There are few response matching methods you could try:
      Massberg https://books.google.fi/books?id=QddcxHLavrMC&pg=PA201&lpg=PA204#v=onepage&q&f=false
      Orfandis (check särkkä .../pub/ folder (https://users.aalto.fi/~ssarkka/pub/eq-design-demo.zip)
      Vicanek (most filter types) https://vicanek.de/articles/BiquadFits.pdf
      Särkkä https://users.aalto.fi/~ssarkka/pub/eq-article.pdf
      MZTi (LPF only) https://www.kvraudio.com/forum/viewtopic.php?t=360690
      Jackson https://ieeexplore.ieee.org/document/870677?tp=&arnumber=870677
      Mecklenbräuker https://www.sciencedirect.com/science/article/abs/pii/S0165168400001134
      Nelatury https://www.sciencedirect.com/science/article/abs/pii/S1051200406001424
      etc...

      EDIT: Here's a plot showing peak filter responses of commonly used methods (BLT, RBJq and RBJbw) against s-domain (analog) model (fs = 44100Hz).

       

      Last edit: Juha 2020-05-11
      • Marc Grissom

        Marc Grissom - 2020-05-11

        Thanks for sharing these links. But for my purposes, I need to use whatever methods EQ APO is using. Do you by chance know if there's other parametric EQ software out there using something other than the RBJ filters?

         
        • Juha

          Juha - 2020-05-11

          EqualizerAPO is based on RBJ filters but there's an option for custom IIR filters so you be able to code your own filters (though, its limited scripting language). Here's an example of LPF I did some time ago.

           
          • Marc Grissom

            Marc Grissom - 2020-05-11

            Thanks Juha. That should be quite helpful if I decide I want a custom filter later.

             

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.