1
$\begingroup$

I am working on speech synthesis with LPC and I originally implemented a pre-emphasis filter:

+(void)processBuffer:(Buffer *)buffer {
    float alpha = -0.9375f;
    for (int i = 1; i < buffer.size; i++) {
        buffer.samples[i] += buffer.samples[i - 1] * alpha;
    }
}

And found that my output audio was distorting, so I adding some code to scale the values, and so now it looks like this:

+(void)processBuffer:(Buffer *)buffer {
    float preEnergy = [self energyFor:buffer];

    float alpha = -0.9375f;
    for (int i = 1; i < buffer.size; i++) {
        buffer.samples[i] += buffer.samples[i - 1] * alpha;
    }

    [self scaleBuffer:buffer preEnergy:preEnergy postEnergy:[self energyFor:buffer]];
}

+(void)scaleBuffer:(Buffer *)buffer preEnergy:(double)preEnergy postEnergy:(double)postEnergy {
    float scale = sqrt(preEnergy / postEnergy);

    for (int i = 0; i < buffer.size; i++) {
        buffer.samples[i] *= scale;
    }
}

+(float)energyFor:(Buffer *)buffer {
    double sum = 0.0;

    for (int i = 0; i < buffer.size; i++) {
        sum += buffer.samples[i] * buffer.samples[i];
    }
    return sum; 
}

I am finding that the speech I am generating still doesn't sound very good, and so I am just wondering if my implementation for this filter is ok? Does anyone see anything I am doing wrong or does this look ok?

$\endgroup$

2 Answers 2

2
$\begingroup$

The most glaring problem is that buffer.samples[0] is never filtered because the for loop goes from 1 to size-1. This is an inherent problem in the implementation of FIR filters in software where you are dealing with buffers of data rather than an infinite stream. In the more general case, where your filter has N states you would need to store N-1 states so that they can be used on the next invocation of the filter. Since your filter only has two taps and the implementation is rather more simple than a general solution would be.

// state is a member variable of the class initialized to zero.
float alpha = -0.9375f;
buffer.samples[0] += state * alpha;
for (int i = 1; i < buffer.size; i++) {
    buffer.samples[i] += buffer.samples[i - 1] * alpha;
}
state = buffer.samples[buffer.size-1]; // store for the next time.

Scaling

Your filter has close to 2x gain in the passband that might be causing you problems. I suspect samples need to stay in the range of -1 to 1. Imagine samples was a stream of [...,1,-1,1,-1,...] (this happens to be a sine wave at the sampleRate/2). Your filter output is going to be [...,-1+1*-0.98,1+-1*-0.98,...] which is approximately [...,-2,2,-2,2,...]

The strategy of scaling each consecutive buffer by the filter gain for that buffer is a nice idea but it has the unfortunate property of producing a different scaling factor every time you call it. This would likely introduce a transient between each buffer.

It is better to analyze the filter and choose a constant at design time to scale by.

In your case the would be 1/2.

// state is a member variable of the class initialized to zero.
float alpha = -0.9375f;
buffer.samples[0] += state * alpha;
buffer.samples[0] *= 0.5;
for (int i = 1; i < buffer.size; i++) {
    buffer.samples[i] += buffer.samples[i - 1] * alpha;
    buffer.samples[i] *= 0.5;
}
state = buffer.samples[buffer.size-1]; // store for the next time.

Of course if you wanted the gain then disregard this part.

$\endgroup$
2
  • $\begingroup$ I am actually processing an entire signal with this, so buffer.samples[0] literally is only for sample #0 in the entire signal. $\endgroup$
    – patrick
    Commented Sep 13, 2015 at 2:32
  • $\begingroup$ Okay. That was not at all obvious from your question and is an important detail. $\endgroup$
    – jaket
    Commented Sep 13, 2015 at 2:45
1
$\begingroup$

Turns out the problem I was having had to do with the fact that I had implemented an IIR filter rather than an FIR.

double unmodifiedPreviousSample = buffer.samples[0];
double tempSample;
for (int i = 1; i < buffer.size; i++) {
    tempSample = buffer.samples[i];
    buffer.samples[i] += (alpha * unmodifiedPreviousSample);
    unmodifiedPreviousSample = tempSample;
}

Once I made this change this, all the low frequencies in the signal were removed and it sounds awesome.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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