Page 1 of 2

Phaser/allpass filter code

Posted: Tue Dec 22, 2009 1:16 am
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?

Posted: Tue Dec 22, 2009 10:29 am
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.

Posted: Tue Dec 22, 2009 3:15 pm
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?

Posted: Tue Dec 22, 2009 4:42 pm
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)

Posted: Fri Jan 01, 2010 2:07 am
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?

Posted: Fri Jan 01, 2010 11:29 am
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.

Posted: Sat Sep 11, 2010 2:09 pm
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

Posted: Sat Sep 11, 2010 4:21 pm
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

Posted: Sat Sep 11, 2010 9:05 pm
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

Posted: Sun Sep 12, 2010 12:15 am
by frank
That looks good, think that will work.

Posted: Sun Sep 12, 2010 10:08 am
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,

Posted: Sun Sep 12, 2010 8:54 pm
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.

Posted: Mon Sep 13, 2010 4:54 am
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.

Posted: Mon Sep 13, 2010 6:10 am
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!

Posted: Mon Sep 13, 2010 10:44 am
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.