2
$\begingroup$

I am using Naudio open source library and I am trying to do some simple filtering. The problem is that I hear some "clicks", not too loud. The library offers me the possibility to use at least two buffers, so the computing time doesn't introduce a delay between them. Because in the most of the time I am dealing with a stereo signal, I have split it in two arrays and I compute each other independently. I would like to know if is there something special that I have to do with a filter when I used it on a buffer. I have first used a low pass biquad filter like the one below:

       //generate coeff
       //sincerely, I don't know what's up with q
       //I have taken into consideration some values
       //to see if the noise disappears   
       double w0 = 2 * Math.PI * cutoffFrequency / _sampleRate;
       double cosw0 = Math.Cos(w0);
       double alpha = Math.Sin(w0) / (2 * q);
       _b0 = (1 - cosw0) / 2;
       _b1 = 1 - cosw0;
       _b2 = (1 - cosw0) / 2;
       _a0 = 1 + alpha;
       _a1 = -2 * cosw0;
       _a2 = 1 - alpha; 
       for (int i = 2; i < length; i++)  
       {
            output[i] = (float)((_b0 / _a0) * input[i] + (_b1 / _a0) * input[i - 1] + (_b2 / _a0) * input[i - 2]- (_a1 / _a0) * output[i - 1] - (_a2 / _a0) * output[i - 2]);
       }
       output[1] = (float)(
                (_b0 / _a0) * input[1] + (_b1 / _a0) * input[0] + (_b2 / _a0) * input[0]
                - (_a1 / _a0) * output[0] - (_a2 / _a0) * output[0]);
        output[0] = (float)(
                (_b0 / _a0) * input[0] + (_b1 / _a0) * 0 + (_b2 / _a0) * 0
                - (_a1 / _a0) * 0 - (_a2 / _a0) * 0);

I thought that all my problems came from the first two samples (output 0:1), I've tried all combinations: output[-1]=0,output[-1]=output[0], but nothing worked. What values output[i-1], output[i-2] should have when "i" is 0 or 1?

I have encountered the same noise (clicks) when I used a LowPass Windowed-Sinc Filter, just like this:

//calculate coeff

        int i;

        int m = length;
        double PI = Math.PI;
        length=101;
        for (i = 0; i < length; i++)
        {
            if (i - m / 2 == 0)
            {
                _h[i] = 2 * PI * _cutOffFrecv;
            }
            else
            { 
                //!=0
                _h[i] = Math.Sin(2 * PI * _cutOffFrecv * (i - m / 2)) / (i - m / 2);
            }
            _h[i] = _h[i] * (0.54 - 0.46 * Math.Cos(2 * PI * i / m));
        }
        //normalize the low-pass filter kernel for unity gain at DC
        double s = 0;
        for (i = 0; i < m; i++)
        {
            s = s + _h[i];
        }
        for (i = 0; i < m; i++)
        {
            _h[i] = _h[i] / s;
        }
        //convolve the input & kernel
        //_kernelSize=101
        //most often length is 6615 or 6614 for each channel
        //in these examples I compute only one channel
        for (j = 0; j < length; j++)
        {
            output[j]=0;
            for (i = 0; i < _kernelSize; i++)
            {
                if (j >= i)
                {
                    output[j] =(float)(output[j]+ _h[i] * input[j - i]);
                }
            }
        }

The problem surely is not from splitting the signal, or from combining channels, because I have tested this without any filter and everything is ok. I have also tried to simulate some delays created by a processing algorithm (but without changing the signal) and nothing went wrong. I am very sure that the problem comes from filtering. Everything I wrote is used on a buffer.

$\endgroup$
1
$\begingroup$

The context in which you use these functions is not clear, but it seems to me that your problem is "edge effects".

When you are evaluating the convolution or the biquad, you need to access samples which are outside the current buffer. Your two implementations evaluate these samples as zero. This is incorrect. For example, for the biquad, everytime you process a block of audio, you need to store the last 2 values of the input[] and output[]; and reuse them in place of input[-1], input[-2], output[-1], output[-2]. Even if the data you process comes in small chunks, you must process it as if it came in one single stream; so the state variables of your filters must not be reset to zero at the boundaries of each buffer.

$\endgroup$
  • $\begingroup$ It didn't work. I've posted a detailed explanation in an answer section. $\endgroup$ – Victor Mar 20 '13 at 14:56
0
$\begingroup$

Regarding the biquad filter, I've saved the previous values as you said, but nothing has changed. Here's the code:

    public void DoFiltering(float[] input, float[] output, int length, bool rightChannel)
    {


        for (int i = 2; i < length; i++)
        {
            output[i] = (float)(
                (_b0 / _a0) * input[i] + (_b1 / _a0) * input[i - 1] + (_b2 / _a0) * input[i - 2]
                - (_a1 / _a0) * output[i - 1] - (_a2 / _a0) * output[i - 2]);
        }

        if (rightChannel)
        {
            //output[-1] = _outR_Channel[0]
            //output[-2]= _outL_Channel[1]
            //the same thing for left channel
            //and for input
            output[1] = (float)(
                (_b0 / _a0) * input[1] + (_b1 / _a0) * input[0] + (_b2 / _a0) * _inR_Channel[0]
                - (_a1 / _a0) * output[0] - (_a2 / _a0) * _outR_Channel[0]);
            output[0] = (float)(
                (_b0 / _a0) * input[0] + (_b1 / _a0) * _inR_Channel[0] + (_b2 / _a0) * _inR_Channel[1]
                - (_a1 / _a0) * _outR_Channel[0] - (_a2 / _a0) * _outR_Channel[1]);
            //save the last two values in order to use them in the next step
            _outR_Channel[0] = output[length - 1];
            _outR_Channel[1] = output[length - 2];
            _inR_Channel[0] = input[length - 1];
            _inR_Channel[1] = input[length - 2];
            // (output[length-2],output[length-1]) <=> (output[-2],output[-1]) <=> (_output[1],_output[0])
        }
        else
        {

            output[1] = (float)(
                (_b0 / _a0) * input[1] + (_b1 / _a0) * input[0] + (_b2 / _a0) * _inL_Channel[0]
                - (_a1 / _a0) * output[0] - (_a2 / _a0) * _outL_Channel[0]);
            output[0] = (float)(
                (_b0 / _a0) * input[0] + (_b1 / _a0) * _inL_Channel[0] + (_b2 / _a0) * _inL_Channel[1]
                - (_a1 / _a0) * _outL_Channel[0] - (_a2 / _a0) * _outL_Channel[1]);

            _outL_Channel[0] = output[length - 1];
            _outL_Channel[1] = output[length - 2];
            _inL_Channel[0] = input[length - 1];
            _inL_Channel[1] = input[length - 2];
        }

I've checked the indexes several times to see if I'am not making any mistake. I am sure that the split channel procedure is correct because I have tested it independently and without any filter. I would like to know how I should change the window-sinc filter. Supposing that I have an input of "n" elements, shifting it with "k" positions to the left and padding it with previous input values ( "k" values), would it be ok? What about the output? in this case, I will obtain n+k output array, but I need only n values. It would be ok if I would take samples from k to k+n-1 ? I'am also thinking to take into consideration the next values and the input should be: [k previous values] [current data - n length] [k next values]. And for the output I would have the same structure, but different values. That would give a headache, because I have to do another double buffering, or triple buffering technique. I would like to point out the fact that Naudio gives me the chance to change the buffer length. In fact, I can change a latency time which directly affects the buffer length. If I increase it, let's say 400 or 500 ms, the clicks are not so often.

Do you have any idea about how I should resolve this problem? I am quite sure that it should be a solution.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.