Cocell
Cocell

Reputation: 159

Peak Filter has clicks and pops

OSStatus MusicPlayerCallback ( 
                       void *                     inRefCon, 
                       AudioUnitRenderActionFlags *   ioActionFlags, 
                       const AudioTimeStamp *         inTimeStamp, 
                       UInt32                     inBusNumber, 
                       UInt32                     inNumberFrames, 
                       AudioBufferList *            ioData) { 


MusicPlaybackState *musicPlaybackState = (MusicPlaybackState*) inRefCon; 

double sampleinp; 

double A,omega,sn,cs,alpha,Bandwidth; 

double dbGain; 

double   a0,a1,a2,b0,b1,b2; 

dbGain = 1.0; 

A=pow(10.0,dbGain/40.0); 

Bandwidth = 2.0; 

omega=2 * M_PI * 800.0/44100.0; 

sn=sin(omega); 

cs=cos(omega); 

alpha=sn*sinh(((M_LN2/2)*Bandwidth*omega)/sn); 


//Peak Filter Biquad 

b0 =1.0 + alpha * A; 

b1 = (-2.0 * cs); 

b2 = 1.0 - alpha * A; 

a0 = 1.0 + (alpha /A); 

a1 = -2.0 * cs; 

a2 = 1.0 - (alpha /A); 



double b0a0, b1a0, b2a0, a1a0, a2a0; 

double static x1; 

double static x2; 

double static y1; 

double static y2; 


b0a0=b0/a0;   

b1a0=b1/a0; 

b2a0=b2/a0; 

a1a0=a1/a0; 

a2a0=a2/a0; 


for (int i = 0 ; i < ioData->mNumberBuffers; i++){ 


  AudioBuffer buffer = ioData->mBuffers[i]; 
  AudioSampleType *outSample = buffer.mData; 

  for (int j = 0; j < inNumberFrames*2; j++){ 

     sampleinp = *musicPlaybackState->samplePtr++; 

     outSample[j] =  b0a0 * sampleinp + 
     b1a0 * x1 + 
     b2a0 * x2 - 
     a1a0 * y1 - 
     a2a0 * y2; 


     x2=x1; 
     x1=sampleinp; 

     y2=y1; 
     y1=outSample[j]; 

  }} 





return noErr; 
} 

Having Clicks/pop problems. Someone PLEASE HELP... I don't know what I'm doing wrong. This is in Xcode using C in Objective-C. I tried making the Coeff Global and Static but no luck. The Audio file using is a .caf I tried .wav but still no good....

Thanks, sorry for the general cry for help. I'm new to this site.. I'm trying to add an Peak filter in my app but everytime I use a slider or just leave the gain at 1 I get pops and clicks. It seems as everthing is there and working properly as far as holding the previous samples etc. I also get some type of phase when changing the frequency or the bandwidth. I'm so confused been studying dsp for a few months now, I think it's something with Objective-C and a lil user error. It seem to go away when changing the sample to a SInt32 but the left channel disappears when changing freq.

Dsp.h

typedef struct { 

  void* audioData; 

   UInt32 audioDataByteCount; 

   SInt16 *samplePtr; 

} MusicPlaybackState; 

Upvotes: 5

Views: 890

Answers (4)

Eryk Sun
Eryk Sun

Reputation: 34260

As per hotpaw2's answer, here's the plot of your filter's response:

from pylab import *
import scipy.signal as signal

def biquad_peak(omega, gain_db, bandwidth):
    sn = sin(omega)
    cs = cos(omega)
    alpha = sn * sinh(log(2) / 2 * bandwidth * omega / sn)
    gain_sqrt = 10.0 ** (gain_db / 40.0)

    # feed-forward coefficients
    b0 = 1.0 + alpha * gain_sqrt
    b1 = -2.0 * cs
    b2 = 1.0 - alpha * gain_sqrt
    # feedback coefficients
    a0 = 1.0 + (alpha / gain_sqrt)
    a1 = -2.0 * cs
    a2 = 1.0 - (alpha / gain_sqrt)
    # normalize by a0
    B = array([b0, b1, b2]) / a0
    A = array([a0, a1, a2]) / a0
    return B, A

omega = 2 * pi * 800.0 / 44100.0
gain_db = 1.0
bandwidth = 2.0

B, A = biquad_peak(omega, gain_db, bandwidth)
w, H = signal.freqz(B, A)
f = w / pi * 22050.0
plot(f, abs(H), 'r')
gain = 10.0 ** (gain_db / 20.0)
print  "Gain:", gain
plot(f, gain*ones(len(f)), 'b--'); grid()

biquad peak filter response

The peak gain is set to 1.1220184543 (i.e. 1 dB). You can see how the filter causes most of the audible range to have a gain greater than 1.

Edit 2: If this is for an adjustable EQ, then it's up to the user to set the gain that avoids distortion. Plus I doubt the extreme problem you describe would be caused by a mild gain of 1 dB over a narrow band for a typical audio track. I think instead it's because your audio has interleaved stereo data. Each of these channels needs to be filtered separately. I've taken a crack at modifying your nested loop to accomplish this:

a0 = 1.0 + alpha / A; 
a1 = -2.0 * cs / a0;     
a2 = (1.0 - alpha / A) / a0; 
b0 = (1.0 + alpha * A) / a0; 
b1 = -2.0 * cs / a0; 
b2 = (1.0 - alpha * A) / a0;

double static x11, x12, x21, x22;
double static y11, y12, y21, y22;
double x0, y0;

for (int i = 0; i < ioData->mNumberBuffers; i++) {  

    AudioBuffer buffer = ioData->mBuffers[i]; 
    AudioSampleType *outSample = buffer.mData; 

    for (int j = 0; j < inNumberFrames*2; j++) { 

        /* x0 is in the range of SInt16: -32768 to 32767 */

        x0 = *musicPlaybackState->samplePtr++;

        y0 = b0 * x0 + 
             b1 * x11 + 
             b2 * x12 - 
             a1 * y11 - 
             a2 * y12; 

        outSample[j] = fmax(fmin(y0, 32767.0), -32768.0); 

        x12 = x11; 
        x11 = x0;
        y12 = y11;
        y11 = y0
        j++;          

        x0 = *musicPlaybackState->samplePtr++;

        y0 =  b0 * x0 + 
              b1 * x21 + 
              b2 * x22 - 
              a1 * y21 - 
              a2 * y22; 

        outSample[j] = fmax(fmin(y0, 32767.0), -32768.0); 

        x22 = x21; 
        x21 = x0;          
        y22 = y21; 
        y21 = y0; 
    }
}

Upvotes: 4

Paul R
Paul R

Reputation: 212979

In addition to the excellent answers that have already been given, I'll just add a general word of advice - you should factor out and decouple your filter routine from the callback (i.e. make it a separate function with no dependencies on your iOS code). That way you can put it in a test harness and more easily debug it (in a non-real-time context), separate from any iOS-specific issues that might complicate matters.

Upvotes: 1

justin
justin

Reputation: 104698

one important issue is that your filter state must be stored within the refcon state (at least, that is the obvious best place in typical cases).

without that, your filter is rebuilt and initialized at every invocation of the callback which means the state will not be preserved correctly and you'll have glitches at the boundaries of every callback other than the first.

Upvotes: 2

hotpaw2
hotpaw2

Reputation: 70703

Looks like you're using RBJ's biquad cookbook recipe.

A biquad peaking filter with an average gain of 1.0 has a gain above 1.0 at certain frequencies. If you don't want clipping, you either have to use a gain of less than 1.0, or use an attenuator or AGC on the input so that the peak frequency boost never gets to a level that will clip.

It's a problem without an AGC, since some customers expect a peaking or bass boost filter to make things louder, and you have to attenuate the average level to actually lower to completely prevent clipping at the peaking frequency.

Upvotes: 1

Related Questions