Phaser/allpass filter code

Algorithm development and general DSP issues

Moderator: frank

livingston
Posts: 131
Joined: Sun Nov 15, 2009 3:37 pm
Location: New Orleans, LA US

Phaser/allpass filter code

Post by livingston »

I've read this but I must admit I'm not at the point where I can translate these diagrams into the proper opcodes.

How would I write a single allpass stage in code?
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

The FV-1 has special instructions for this to make it quick to do. Make the following assumptions:

"IN" is in the accumulator already, may have come from a filter, delay, ADC, etc.
"DELAY" is the name of the delay block in the all-pass.
"K" = 0.37, why? Because I like that number for examples.

Code: Select all

rda DELAY#, 0.37 ; Read end of DELAY, multiply by 0.37 and add to acc
wrap DELAY, 0.37 ; Write acc to delay, multiply acc by -0.37 and add the 'last read' value from delay memory saved in the LR register. OUT is in the acc.
Note the LR register is automatically updated every time a value is read from memory so it holds DELAY# when we execute the wrap instruction.

So there you have it, a 2-tick APD.
Frank Thomson
Experimental Noize
livingston
Posts: 131
Joined: Sun Nov 15, 2009 3:37 pm
Location: New Orleans, LA US

Post by livingston »

Can I equate K to a register, or is it like my earlier question where it needs to be a specific coefficient? If I can't, how do I create a varying K value for a phaser?
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

In this case K is fixed, you need to use mulx to multiply by a register. An all pass can still be done but it will take 6 or 7 instructions to do it, I haven't written code to do this but it should be:

read end of delay (rda),
multiply by reg (mulx),
add input (rdax or rda depending if from register or memory),
write to head of delay (wra),
multiply by reg (mulx),
multiply by -1 (sof),
read end of delay(rda)
Frank Thomson
Experimental Noize
livingston
Posts: 131
Joined: Sun Nov 15, 2009 3:37 pm
Location: New Orleans, LA US

Post by livingston »

I hacked up what I thought a 4-stage phaser might look like using your suggestions. It uses the envelope to vary the phasing.

Code: Select all

mem	delay	205	
mem	delay2	205
mem	delay3	205
mem	delay4	205
mem	delay5	205
mem	delay6	205
mem	delay7	205
mem	delay8	205
equ	ap1out	reg1
equ	ap2out	reg2
equ	ap3out	reg3
equ	ap4out	reg4
equ	ap5out	reg5
equ	env	reg6
equ	envfil	reg7

rdax	adcr,1
absa
RDFX	envfil,0.001	;use LPF opcode for filtering
WRLX	envfil,-1		;infinite shelf LPF (could have used WRAX avgfil,1)
sof	1.5,0		;get bigger envelope
wrax	env,0		

rda 	DELAY#, 	1 	; Read end of DELAY, 
mulx 	env
rdax	adcr,	1
wra	delay, 	1	;write to head of delay (wra), 
mulx	env		;multiply by reg (mulx), 
sof	-1,	0	;multiply by -1 (sof), 
rda 	DELAY#, 	1 	;read end of delay(rda)
wrax	ap1out,	0

rda 	DELAY2#, 1 	; Read end of DELAY, 
mulx 	env
rdax	ap1out,	1
wra	delay2, 	1	;write to head of delay (wra), 
mulx	env		;multiply by reg (mulx), 
sof	-1,	0	;multiply by -1 (sof), 
rda 	DELAY2#, 1 	;read end of delay(rda)
wrax	ap2out,	0

rda 	DELAY3#, 1 	; Read end of DELAY, 
mulx 	env
rdax	ap2out,	1
wra	delay3, 	1	;write to head of delay (wra), 
mulx	env		;multiply by reg (mulx), 
sof	-1,	0	;multiply by -1 (sof), 
rda 	DELAY3#, 1 	;read end of delay(rda)
wrax	ap3out,	0

rda 	DELAY4#, 1 	; Read end of DELAY, 
mulx 	env
rdax	ap3out,	1
wra	delay4, 	1	;write to head of delay (wra), 
mulx	env		;multiply by reg (mulx), 
sof	-1,	0	;multiply by -1 (sof), 
rda 	DELAY4#, 1 	;read end of delay(rda)
wrax	ap4out,	1

mulx	pot1
rdax	adcr,	1

wrax	dacr,	0
So far it doesn't work, but it involved a lot of guessing so I didn't really expect it to. I can't remember now why I made the delays 205 samples, and I'm guessing this is one thing that isn't right, but I don't really know how to figure them.

Any suggestions on where I'm going wrong?
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

A quick look seems OK (will take a closer look Monday when back in the office), try making it just one AP and seeing if it works than add in the LP filter, then the additional stages, etc. We need to isolate where it fails.
Frank Thomson
Experimental Noize
mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 »

frank wrote:In this case K is fixed, you need to use mulx to multiply by a register. An all pass can still be done but it will take 6 or 7 instructions to do it, I haven't written code to do this but it should be:

read end of delay (rda),
multiply by reg (mulx),
add input (rdax or rda depending if from register or memory),
write to head of delay (wra),
multiply by reg (mulx),
multiply by -1 (sof),
read end of delay(rda)
I just had to do a similar thing, where the allpass coefficient was in a register, derived from a pot. Input is in the ACC, so I didn't think you needed to add the input, it is done in the first rda. I came up with this (hopefully it is right):

rda delay#, 1 ; allpass with WRAP replaced to use variable coefficient
mulx coefficient
wra delay, -1 ; store to delay and then negate ACC
mulx coefficient ; apply 'negative' coefficient
rda delay#, 1 ; add in the end entry in the delay
-mark
My blog: http://tubenexus.com
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

mdroberts1243 wrote:
I just had to do a similar thing, where the allpass coefficient was in a register, derived from a pot. Input is in the ACC, so I didn't think you needed to add the input, it is done in the first rda. I came up with this (hopefully it is right):

rda delay#, 1 ; allpass with WRAP replaced to use variable coefficient
mulx coefficient
wra delay, -1 ; store to delay and then negate ACC
mulx coefficient ; apply 'negative' coefficient
rda delay#, 1 ; add in the end entry in the delay
Not quite correct, input to all-pass is: input + (delayout * K)

If input is in ACC then you are doing: (input + delayout) * K

Not the same result
Frank Thomson
Experimental Noize
mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 »

frank wrote:
mdroberts1243 wrote:
I just had to do a similar thing, where the allpass coefficient was in a register, derived from a pot. Input is in the ACC, so I didn't think you needed to add the input, it is done in the first rda. I came up with this (hopefully it is right):

rda delay#, 1 ; allpass with WRAP replaced to use variable coefficient
mulx coefficient
wra delay, -1 ; store to delay and then negate ACC
mulx coefficient ; apply 'negative' coefficient
rda delay#, 1 ; add in the end entry in the delay
Not quite correct, input to all-pass is: input + (delayout * K)

If input is in ACC then you are doing: (input + delayout) * K

Not the same result
Thanks Frank!

Had to go over it a few times to understand... ended up storing the input to a temp register, clearing the ACC and adding a line:

wrax temp, 0
rda delay#, 1 ; allpass with WRAP replaced to use variable coefficient
mulx coefficient
rdax temp,1 ; add input *NEW*
wra delay, -1 ; store to delay and then negate ACC
mulx coefficient ; apply 'negative' coefficient
rda delay#, 1 ; add in the end entry in the delay
-mark
My blog: http://tubenexus.com
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

That looks good, think that will work.
Frank Thomson
Experimental Noize
mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 »

frank wrote:That looks good, think that will work.
I think it works, but I went and tried to add chorusing on the delay taps, screwing everything up, which requires me to re-think the flow again.

I don't understand the 'post' processing chorus stuff that is done in a lot of the reverb example programmes... is that explained anywhere? I'm trying to implement the modulated output taps directly in the allpass as shown in a lot of reverb flow-diagrams instead.

Thanks,
-mark
My blog: http://tubenexus.com
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

Can you post or link directly to an example so I can see the code you are talking about? Lots of reasons to do things in a reverb program.
Frank Thomson
Experimental Noize
mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 »

frank wrote:Can you post or link directly to an example so I can see the code you are talking about? Lots of reasons to do things in a reverb program.
Hi Frank,
Thanks. I've been coding up the Dattorro plate reverb as an exercise (in frustration) :wink:
I will start a separate thread... the result might be interesting for others.
-mark
My blog: http://tubenexus.com
mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 »

frank wrote:Can you post or link directly to an example so I can see the code you are talking about? Lots of reasons to do things in a reverb program.
I think the plate reverb examples on spinsemi use a block of chorus statements at the end of the code to affect each delay. I don't understand that... It seems to just juggle some samples in the interior of the delay. If this is somehow equivalent to modulating the read from the end of the delay/AP then it would be a lot more efficient to implement the Dattorro diagram in the FV-1!
-mark
My blog: http://tubenexus.com
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

A quick look at the code makes me believe the chorusing of values in the all-pass' is to stop any repetitive noise from happening. With these kind of structures you can get a repetitive sound at a particular frequency, by slightly shifting the sound with a chorus type effect you can stop that from happening.
Frank Thomson
Experimental Noize
Post Reply