Thursday, October 29, 2009

Phaser modulation

It appears my math for the modulation function on the all-pass filter is not the best model for a JFET phaser. I spent a good couple hours with my guitar trying different settings on this phaser. There are certainly some nice sounds available, but it simply did not behave as expected.

I sat down and computed the bilinear transform myself, to better understand the sources I was reading, as well as doing my own analysis on small-signal resistance characteristic of a JFET. After 3 pages of algebra, I came to this conclusion:

R = 250/(1.0114 - sqrt(Vm));

This constrains the range of resistance to 250 to 22k-ohms. The constants 1.0114 and 250 include effects of parameters derived from 2N5458 JFET datasheet, also taking into account the parallel 22k resistor found in the MXR phase90 circuit.

You may notice that when put into the 1/2*pi*R*C equation for center frequency, we have an approximately linear frequency sweep up until the last bit. That explains some of the nature of the analog phaser. To give it a little bit of an exponential feel, we could do this:
R = 250/(lfo^2 + .0114);
or
R = 250/(0.5*(lfo + lfo^2) + .0114); ...to naturalize it a little bit. These are less accurate approximations, but are also less CPU intensive calculations than sqrt().

The final equation for "gain" in the difference equation (gl and gr in rakarrack code) is this:

g = [2*fs*C - (1.0114 - sqrt(Vm))/250 ] / [2*fs*C - (1.0114 - sqrt(Vm))/250 ]

Where fs is sampling frequency and C is value of capacitor (.05 uF).

The difference equation is this:
y[n] = g*(x[n] + y[n-1]) - x[n-1] where g is as noted above

Now a real JFET doesn't have a perfectly linear resistance characteristic, so we can introduce an input-dependent term that changes the "resistance" with signal amplitude:

Vm = lfo * (1-d*Vin^2) where constant "d" is amount of distortion to apply.

Vin is the input signal (smps[i]).

I think a realistic value for "d" is about 0.04 in this application. This can be a parameter that is possible to adjust from 0 to an insane number like .5 or .8 where there is some definite intermodulation available. This is not a rigorous account for the distortion added by the JFET, but I'm hoping it's a good enough approximation for acceptable sounding results while keeping CPU requirement at a minimum. The point is to add a small amount of harmonics at each phase stage to accentuate the "swoosh" heard from an analog phaser.

I see need to employ a look-up table for the LFO, so the phaser doesn't need to be interpolating it all the time.

Tuesday, October 27, 2009

Analog Phaser first pass -- Code

Here's something that seems to work reasonably well. It's modeled after the MXR Phase180 type circuit. Typical JFET phaser. I have not modeled the nonlinear effects yet, as I'm contemplating a CPU-friendly way to do this. The technically correct way would make this a cycle hog as it requires the math functions are applied to every sample in every phase stage.

Here's the code:
Phaser.C
Phaser.h

Download and replace your existing Phaser.C and Phaser.h files in your working Rakarrack source directory and

$ su
# make
# make install

or for those with sudo user:
sudo make
sudo make install

Please notice that L/R crossing is unused in this version, and I have also disabled panning. I need to figure out a way to implement these that does not negatively effect the intensity of the phasing effect.

Here is a more detailed description of what this model attempts to accomplish.

The basic operation is derived from a bilinear transformation of a FET all-pass stage similar to what is found in the MXR phase180 stompbox, and the feedback network matches this circuit topology, but with more extreme settings available. The Rakarrack Phaser (imported from Paul Octavian) employs positive and negative feedback, where a setting of 64 is zero feedback. This feature has been maintained in the analog model. Some more complex analog phasers implement this kind of flexibility.

The modulation characteristic employs math to approximate the transfer function between modulation voltage at the JFET gate to small-signal resistance seen between the source and drain.

In its present form I have not included any input-dependent modeling to approximate the effect of distortion introduced by the JFET's, nor effects of random variance between JFET's that normally cause each stage to vary somewhat from the ideal case when JFET's are meticulously matched.

I may end up modeling the old Ross Phaser tri/sine modulation waveform. It's pretty interesting --- it has the sine wave shape for the negative half of the cycle and the triangle wave shape for the positive half cycle. It was an OTA-based phaser, so the application of this to my phaser model will be entirely unique, but this phaser model isn't supposed to sound like any specific analog phaser. It's just modeled according to typical analog circuit blocks. Just the fact that the number of phase stages can be modified with a click of a mouse makes it unique.

Here are the features that make it most like an Analog Phaser:
a) Wet/Dry mix performs the phase cancellation, so that 50% wet/dry results in the deepest filtering.
b)Turning more to wet generates that classic vibrato sound when the speed is turned up, so it can create a little bit of a pitch shift.
c) Modulation sweep has that effect of dwelling at the low end of the sweep, and then creates a fast "swoosh" when it "comes up for air".

All in all I think this could be a nicely flavored addition to Rakarrack when I am done twiddling and tweaking.

Sunday, October 18, 2009

Analog phaser off to a good start

Indeed, my implementation of the phaser had problems with my treatment of the loop.

Now I got a good phasing effect happening. Now that's out of the way I need to perfect the modulation end of things and model some of the distortion inherent to analog JFET phasers. Any who read, be looking for some code soon.

Thursday, October 15, 2009

Phaser Matlab file

I did some more research and have this analog phaser model pretty well tuned in Matlab (actually FreeMat). Probably is fine in Octave, too.

This is a simple script file, not really a function. It performs a frequency sweep on the filter and averages the output sort of reminiscent of the analog bench test signal generator police siren mode with an external trigger to trace the envelope outline. It's one way to get the job done.

I am hoping that porting the DSP phaser block to C++ will solve my phaser woes. This and the reverse delay will have to wait until my next free block of time to sit down and do some coding.

See below:

SR = 48000;
DUR = 4;
t = (1:1:DUR*SR);
tt = t/SR;
fmax = 12000;
fq = tt*fmax/DUR;
x = sin(2*pi*tt.*(1+fmax*tt/DUR));
%x = sin(2*pi*tt.*8000);
PER = length(x);

f3 = 2000;
C = 0.1e-6;
R = 1/(2*pi*C*f3)
b = 1/(2*SR*R*C);
att = (1 - b)/(b + 1)
IN =1;
output = x;
sta = 4;
CHARGE=0*(1:sta);
delta=CHARGE;
x1=delta;
i=1;
feedback = 0;
fb = 0;

for (k=1:PER);

j=0;

x(i) = x(i) + feedback;
b = 1/(2*SR*(.9*R + .1*R/(1 - x(i)*x(i)) )*C);
att = (1 - b)/(b + 1);

for stages = 1:sta
j = j+1;

CHARGE(j) = -x1(j) + att*(x(i) + CHARGE(j));
x1(j)=x(i);
x(i) = CHARGE(j);

end
feedback = x(i)*fb;
i=i+1;
end

output = .5*x + .5*output;
dbput = 20*log10(abs(output));

dcharge=0;
ddelta=0;
j=0;
att = .02;
for i=1:PER
j=j+1;
ddelta = dbput(j) - dcharge; %Low CPU interpolation
dcharge = dcharge + att*ddelta;
dbput(j) = dcharge;
end

%plot(tt,output,'r-',tt,x,'g-');
plot(fq,dbput,'r-');
CHARGE
max(output)

Tuesday, October 13, 2009

Analog Phaser Frustration

I have been attempting to model an analog phaser for rakarrack based upon the existing phaser (from ZynAddSubfx), since the only necessary changes lie within a few lines of code at the core of phaser.c.

I have modeled the algorithm in FreeMat (Matlab work-alike) and produced plots that agree with theory.

My inexperience with C and C++ must be causing me some problems in the way I am dealing with parameters...such as type casting int to float etc....or maybe I am just making a dumb "off by 1" mistake in the loop.

Argghhh. I have decided to take on a reverse delay feature request from the rakarrack forum. This seems a simple enough hack, starting with echo.c...and maybe port the functionality to MusicDelay.c.

If anyone reads this blog, here is the section of code modified. It is yet untested, but if the relevant parts are copy/pasted into echo.c, and make sure the extra variables are declared in echo.h, then I anticipate this will produce the reverse delay. I hope to compile and test this later this week. I have a bunch of meat to butcher, so my next couple of evenings are pretty well tied up with the joy of hunting season.

Comments are welcome:

/*
* Effect output
*/
void
Echo::out (REALTYPE * smpsl, REALTYPE * smpsr)
{
int i;
int reverse = 1; //Notice this is hard-coded to reverse delay mode. This variable declaration will needed to be deleted from here, declared in echo.h, and controlled from the GUI.
REALTYPE l, r, ldl, rdl;

for (i = 0; i < PERIOD; i++)
{

ldl = ldelay[kl];
rdl = rdelay[kr];

l = ldl * (1.0 - lrcross) + rdl * lrcross;
r = rdl * (1.0 - lrcross) + ldl * lrcross;
ldl = l;
rdl = r;


ldl = smpsl[i] * panning - ldl * fb;
rdl = smpsr[i] * (1.0 - panning) - rdl * fb;

if(reverse) {
efxoutl[i] = ldelay[rvkl] * 2.0;
efxoutr[i] = rdelay[rvkr] * 2.0;
}
else {
efxoutl[i] = ldl * 2.0;
efxoutr[i] = rdl * 2.0;
}


//LowPass Filter
ldelay[kl] = ldl = ldl * hidamp + oldl * (1.0 - hidamp);
rdelay[kr] = rdl = rdl * hidamp + oldr * (1.0 - hidamp);
oldl = ldl;
oldr = rdl;

if (++kl >= dl)
kl = 0;
if (++kr >= dr)
kr = 0;

rvkl = dl - kl;
rvkr = dr - kr;
};