Noise as a modulation source for phaser

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

I'm working on a more advanced version of the phaser I did for CM. For this version, I've given the modulation depth a much greater range.

When I set the modulation source to one of the LFOs and set the LFO wave to noise, dial up the feedback level (which is always less than one) and modulation depth, the phaser blows up. This doesn't happen with any of the other wave shapes. Since it's white noise, I guess the energy build up is much, much greater.

I'd hate to leave the noise waveform out, so I was looking for a place in my algorithm to put in some clipping to keep the feedback from getting out of hand. But nothing I do seems to solve the problem. Here's the algorithm I'm using:

First I modulate the phaser frequency with the modulation sources. This is kind of psuedo code:

Code: Select all

float fc = frequency * pow(2.0f, mod * modDepth);
Then I calculate the gain that's used in the allpass filters:

Code: Select all

float d = fc / halfSampleRate;
float g = (1.0f - d) / (1.0f + d);
I add the feedback to the input before running it through the allpass filters:

Code: Select all

input += feedback * feedbackLevel;
Then I run the allpasses.

I've clipped the feedback, but that doesn't seem to help. Or perhaps I'm not doing it right, or something. :oops:

Post

Have you considered putting a mild lowpass filter on the modulating noise? I have a hunch that would help.

Have you tried doing the computations in double precision? That would help determine whether it's a round-off problem or not.

How are you computing the coefficients for your allpass filters?

Post

MackTuesday wrote:Have you considered putting a mild lowpass filter on the modulating noise? I have a hunch that would help.
Actually, my LFO has a "Smooth" parameter which is just a lowpass filter for smoothing out the wave shapes. When I use noise and set the smooth frequency to 4khz or so, it does help.
Have you tried doing the computations in double precision? That would help determine whether it's a round-off problem or not.
I should try this (I should've tried it for the state variable filter, too).
How are you computing the coefficients for your allpass filters?
Basically, just the code you see in my above post:

Code: Select all

float g = (1.0f - d) / (1.0f + d); 
Then I run the input through a series of allpass filters using the above coefficient.

Post

I got the gain part, so maybe you're putting a couple of comb filters together, but what's the delay? If you have something like

Code: Select all

y[n] = g∙x[n] + x[n-k] - g∙y[n-k]
what's k?

Never mind, I have a weird idea: Clip y[n]-y[n-1]. In other words,

Code: Select all

w[n] = g∙x[n] + x[n-k] - g∙y[n-k]
y[n] = y[n-1] + clip(w[n] - y[n-1])
This idea is based on the fact that g can change so rapidly from sample to sample that it interacts badly with the filter history. So don't allow the filter to change too quickly. Heck, you can even output w[n], just don't *store* it if it's wonky.

Maybe you could do this:

Code: Select all

y[n] = y[n-k] + clip2(w[n]-y[n-k], x[n]-x[n-k])
where the clip2 function clips the first argument so its absolute value is no greater than the absolute value of the second argument:

Code: Select all

clip2(a,b) = min(|a|,|b|) * sign(a)
In other words, don't let y change any faster than x changes.
Last edited by MackTuesday on Wed Jun 10, 2009 12:38 am, edited 3 times in total.

Post

Maybe you could also try storing the history of g and using old values on old samples, as in

Code: Select all

y[n] = g[n]x[n] + x[n-k] - g[n-k]y[n-k]

Post

MackTuesday wrote:I got the gain part, so maybe you're putting a couple of comb filters together, but what's the delay? If you have something like

Code: Select all

y[n] = g∙x[n] + x[n-k] - g∙y[n-k]
what's k?

Never mind, I have a weird idea: Clip y[n]-y[n-1]. In other words,

Code: Select all

w[n] = g∙x[n] + x[n-k] - g∙y[n-k]
y[n] = y[n-1] + clip(w[n] - y[n-1])
This idea is based on the fact that g can change so rapidly from sample to sample that it interacts badly with the filter history. So don't allow the filter to change too quickly. Heck, you can even output w[n], just don't *store* it if it's wonky.

Maybe you could do this:

Code: Select all

y[n] = y[n-k] + clip2(w[n]-y[n-k], x[n]-x[n-k])
where the clip2 function clips the first argument so its absolute value is no greater than the absolute value of the second argument:

Code: Select all

|clip2(a,b)| <= |b|
In other words, don't let y change any faster than x changes.
That's an interesting suggestion. I will study it. Sorry for not posting more code in response to your earlier question. Here's the actual code for the allpass filters:

Code: Select all

leftInput += leftFeedback * feedbackLevel;
rightInput += rightFeedback * feedbackLevel;

for(int i = 0; i < StageModeValues[stageMode]; i++)
{
    temp = leftInput;
    leftInput = leftInput * -leftGain + leftBuffer[i];
    leftBuffer[i] = leftInput * leftGain + temp + DenormalOffset;

    temp = rightInput;
    rightInput = rightInput * -rightGain + rightBuffer[i];
    rightBuffer[i] = rightInput * rightGain + temp + DenormalOffset;
}

leftFeedback = leftInput;
rightFeedback = rightInput;
The StateModeValues array has the values for the number of stages, e.g. 4, 6, 8, etc... The leftBuffer and rightBuffer arrays just store the outputs of each of the cascaded allpass filters.

I will study your code and see how I can apply it to mine. Thanks. :)

Post

MackTuesday wrote:I got the gain part, so maybe you're putting a couple of comb filters together, but what's the delay?
The delay is just one sample.

Post

I had something else written here, but it was wrong.

No wait, it was right after all. You have

Code: Select all

temp = input;
input = input * -gain + buffer[i];
buffer[i] = input * gain + temp + denormal;
This is equivalent to

Code: Select all

a = x[n]
b = -g∙x[n] + y[n-1]
y[n] = gb + a + ε
which reduces to

Code: Select all

y[n] = -g²∙x[n] + x[n] + g∙y[n-1] + ε
whose response is

Code: Select all

Y(z)/X(z) = (1 - g²) / (1 - g/z)
If g is close enough to 1 for long enough, it'll give you a nice big juicy DC offset.

Post

Thanks for your input, Mack.

I didn't so much as solve the problem as avoid it by limiting the cutoff frequency of the LFO's smoothing filter to 1khz. That avoids any blowups.

Post

In the following I assume that your allpass filter follows the design from http://ccrma.stanford.edu/~jos/waveguid ... Combs.html
or
http://crca.ucsd.edu/~msp/techniques/la ... de150.html

Assuming a pole at g and a zero at 1/g, the response is

Code: Select all

H = (1/z - 1/g) / (1/z - g)
and the phase response is

Code: Select all

arg H = π - φ + 2∙arctan[(sin φ) / (g + cos φ)]
Setting this to π we get

Code: Select all

-φ + 2∙arctan[(sin φ) / (g + cos φ)] = 0
(sin φ) / (g + cos φ) = tan(φ / 2)
                      = (1 - cos φ) / (sin φ)
sin² φ = g + (1-g)∙cos φ - cos² φ
1 = g + (1-g)∙cos φ
(1-g)/(1-g) = 1 = cos φ
φ = 0
Indeed, substituting z = exp(i∙0) = 1 into the first expression gives H = -1, and graphing the phase response I've given shows that φ = 0 is the only solution. The inverse tangent has multiple solutions, but when you solve for φ you end up taking the tangent of both sides anyway, and the tangent of all of those multiple solutions is the same. So if you're able to get phase inversion near some frequency other than 0, there may be a problem with your implementation.

Post Reply

Return to “DSP and Plugin Development”