A Spin on the Dattorro Plate Reverb

Algorithm development and general DSP issues

Moderator: frank

Post Reply
mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

A Spin on the Dattorro Plate Reverb

Post by mdroberts1243 » Mon Sep 13, 2010 5:07 am

I wanted to code a reverb from scratch, a fairly complex reverb that would pretty much consume all the FV-1 resources, and hopefully learn about tuning the reverb in the process. The Dattorro paper seemed a great starting point as it was a well-documented plate reverb that is touted as sounding fairly decent. There's a great thread on gearslutz that got me keen to do this.

This version follows (or attempts to) the block diagram in the paper exactly. I have another version that tries to go further (as the paper suggests) and uses both LFOs with SIN, COS, and COMPA to get eight different modulations, one for each delay in the 'tank'.

I have corrected a serious error in the damping low-pass filter, but I still suspect errors because of a strong sibilance in the reverb output... appreciate some additional eyes on this! UPDATE... found another error... the 'simple' low-pass for input bandwidth limiting didn't work the way I thought... much more pleasing now.

Code: Select all

; Plate Reverb -- derived from Jon Dattorro paper "Effect Design"
; - Supposedly good sounding with minimal required resources
; - available at: https://ccrma.stanford.edu/~dattorro/EffectDesignPart1.pdf
;

;pot0=reverb level
;pot1=reverb time
;pot2 = hf loss in tank (turn up for MORE damping)

; fixed parameters from the paper
equ	decay_diffusion_1	0.70	;default parameters from Dattorro paper
equ	input_diffusion_1	0.75
equ	input_diffusion_2	0.625

; k1 for freqs: 
equ	k1_1kHz	0.82552
equ	k1_2kHz	0.68148
equ	k1_4kHz	0.46441
equ	bandwidth	1-k1_2kHz	; coefficient for input low-pass

equ	excursion	16	; peak excursion for tap modulation

; no idea what a suitable pre-delay would be...  could go as high as 7957 samples
mem	predelay		3802	; 3802=116ms predelay at 32kHz

; allpass names are formed from the Dattorro paper node numbers in Figure 1
; all the memory sizes have been scaled up by 1.1010x to account for difference in sampling
; original paper specified sample rate of 29761 Hz, we have 32768

mem	ap13_14		156	; coeff is input_diffusion_1
mem	ap19_20		117	; coeff is input_diffusion_1
mem	ap15_16		417	; coeff is input_diffusion_2
mem	ap21_22		305	; coeff is input_diffusion_2

mem	ap23_24		 740+excursion	; coeff is decay_diffusion_1
mem	ap46_48		1000+excursion	; coeff is decay_diffusion_1

mem	del24_30	4903
mem	del48_54	4643
mem	ap31_33		1982	; coeff is decay_diffusion_2 (derived from pot1)
mem	ap55_59		2924	; coeff is decay_diffusion_2
mem	del33_39	4096
mem	del59_63	3483

equ	krl			reg0	; coeff for reverb level (from pot0)
equ	decay			reg1	; coeff for reverb time (from pot1)
equ	decay_diffusion_2		reg2	; related to coeff for reverb time (from delay)
equ	damping			reg3	; coeff for high-frequency decay within the tank (from pot2)
equ	one_minus_dmpg		reg4
equ	lp_inp			reg5	; 'bandwidth' low-pass at input
equ	lp30_31			reg6	; tank low-pass set by 'damping'
equ	lp54_55			reg7	; tank low-pass set by 'damping'
equ	mono			reg8	; mono input signal
equ	diffuse_in		reg9	; output of input diffusers
equ	temp			reg10	; temp for expanded allpass calculations
equ	temp2			reg11	; another temp value for allpass

;
; code starts here!

; now generate a pair of LFOs to modulate the APs in the loop:

skp	run,2
wlds	SIN0,27,excursion	; paper calls for 1-2Hz, 25=1Hz, 50=2Hz
wlds	SIN1,41,excursion

;now derive control coefficients from pots:

rdax	pot0,1		; control reverb attenuation level
mulx	pot0		; square it
wrax	krl,0		; reverb level, write for later use

rdax	pot1,1
wrax	decay,1			; reverb time

; decay_diffusion_2 = decay + 0.15 but must range between 0.25 to 0.5
sof	1.0,-0.35		; check to see if we will go higher than ceiling (0.35 + 0.15)
skp	neg,1			;
clr				; set ceiling to 0.35 if we are still positive (after restoration below)
sof	1.0,0.35			; restore ACC (could combine with below)
sof	1.0,-0.10		; check to see if we will be below floor (0.10 + 0.15)
skp	gez,1
clr				; clr ACC will set floor to 0.25 after restoration below
sof	1.0,0.25			; restore ACC (+ 0.10) and add 0.15
wrax	decay_diffusion_2,0

rdax	pot2,1			; control high freq loss in the tank (low pass filter)
wrax	damping,1		; low-pass coefficient
sof	-1,0.999			; make '1-damping' control from 1- pot2
wrax	one_minus_dmpg,0	; other low pass damping coefficient

; sum inputs to mono, with 0.5x input gain adjustment
rdax	adcl,0.75	;TESTING... boosted from 0.5
rdax	adcr,0.75
wrax	mono,1		; leave signal in ACC

; do the pre-delay
wra	predelay,0

; input low-pass 
rda	predelay#,bandwidth
rdax	lp_inp,1-bandwidth
wrax	lp_inp,1

; now do input all passes:

rda	ap13_14#,-input_diffusion_1
wrap	ap13_14,input_diffusion_1
rda	ap19_20#,-input_diffusion_1
wrap	ap19_20,input_diffusion_1
rda	ap15_16#,-input_diffusion_2
wrap	ap15_16,input_diffusion_2
rda	ap21_22#,-input_diffusion_2
wrap	ap21_22,input_diffusion_2
wrax	diffuse_in,0

;
;allpassed input in place, now process the tank (two sides), with filtering

; left side of Figure 1 tank
rda	del59_63#,1	; input from right-side delay out

mulx	decay
rdax	diffuse_in,1

;modulated tap on this AP
wrax	temp,0
cho  	rda,sin0,sin|reg|compc,ap23_24#-excursion-1
cho 	rda,sin0,sin,ap23_24#-excursion
wrax	temp2,decay_diffusion_1	; store, apply coeff (note flipped sign for this AP)
rdax	temp,1			; add input
wra	ap23_24,-decay_diffusion_1	; write to head of delay
rdax	temp2,1				; add modulated tail 

wra	del24_30,0	;delay
rda	del24_30#,1

; simple low-pass with variable control
mulx	one_minus_dmpg
wrax	temp,0
rdax	lp30_31,1	
mulx	damping		;     damping derived from pot
rdax	temp,1
wrax	lp30_31,1

mulx	decay		; apply decay
wrax	temp,0		; save for applying a bit later...

; another allpass, but WRAP replaced to use variable coefficient
rda	ap31_33#,-1
mulx	decay_diffusion_2	; mult with 'negative' coefficient from pot
rdax	temp,1		; add input from temp register
wra	ap31_33,1	; store to delay
mulx	decay_diffusion_2	; apply coeff
rda	ap31_33#,1

wra	del33_39,0	; delay
rda	del33_39#,1

; right side of Figure 1 tank, delay output already in ACC
mulx	decay
rdax	diffuse_in,1

; modulated tap on this AP
wrax	temp,0
cho 	rda,sin0,cos|reg|compc,ap46_48#-excursion-1
cho 	rda,sin0,cos,ap46_48#-excursion
wrax	temp2,decay_diffusion_1	; store, apply coeff (note flipped sign for this AP)
rdax	temp,1			; add input
wra	ap46_48,-decay_diffusion_1
rdax	temp2,1				; add modulated tail

wra	del48_54,0	;delay
rda	del48_54#,1

; simple low-pass with variable control
mulx	one_minus_dmpg
wrax	temp,0
rdax	lp54_55,1	
mulx	damping		;     damping derived from pot
rdax	temp,1
wrax	lp54_55,1

mulx	decay		; apply decay

; another allpass, but WRAP replaced to use variable coefficient
wrax	temp,0		; save for applying a bit later...
rda	ap55_59#,-1
mulx	decay_diffusion_2	; mult with 'negative' coefficient from pot
rdax	temp,1		; add input from temp register
wra	ap55_59,1	; store to delay
mulx	decay_diffusion_2	; apply coeff
rda	ap55_59#,1

wra	del59_63,0	; write delay leaving ACC clear

;
;now gather outputs from loop delays (values scaled for different sampling freq):

rda	del48_54+292,0.6
rda	del48_54+3274,0.6
rda	ap55_59+2107,-0.6
rda	del59_63+2198,0.6
rda	del24_30+2192,-0.6
rda	ap31_33+205,-0.6
rda	del33_39+1174,-0.6
mulx	krl				;attenuate reverb by Pot0 setting
wrax	dacl,0

rda	del24_30+389,0.6
rda	del24_30+3993,0.6
rda	ap31_33+1352,-0.6
rda	del33_39+2943,0.6
rda	del48_54+2325,-0.6
rda	ap55_59+369,-0.6
rda	del59_63+133,-0.6
mulx	krl				;attenuate reverb by Pot0 setting
wrax	dacr,0

;
;  DONE
Last edited by mdroberts1243 on Mon Sep 13, 2010 6:15 pm, edited 2 times in total.
-mark
My blog: http://tubenexus.com

mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 » Mon Sep 13, 2010 1:07 pm

I found a serious error in how I'm implementing the low-pass for 'damping' in the tank... just need to figure out how to do it and I'll repost the source.
-mark
My blog: http://tubenexus.com

mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 » Mon Sep 13, 2010 2:59 pm

mdroberts1243 wrote:I found a serious error in how I'm implementing the low-pass for 'damping' in the tank... just need to figure out how to do it and I'll repost the source.
Fixed the two damping low-pass filters but still don't like the sibilance I'm hearing.
-mark
My blog: http://tubenexus.com

mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

Post by mdroberts1243 » Mon Sep 13, 2010 6:17 pm

mdroberts1243 wrote:
mdroberts1243 wrote:I found a serious error in how I'm implementing the low-pass for 'damping' in the tank... just need to figure out how to do it and I'll repost the source.
Fixed the two damping low-pass filters but still don't like the sibilance I'm hearing.
I think the latest version, with the fix to the input bandwidth low-pass filter nails it (finally).
-mark
My blog: http://tubenexus.com

mdroberts1243
Posts: 18
Joined: Tue Jul 22, 2008 3:54 pm
Location: Ottawa, Canada
Contact:

More modulated version of Dattorro Plate Reverb

Post by mdroberts1243 » Tue Sep 14, 2010 8:05 am

Here's a version of the Dattorro Plate Reverb that modulates all four of the allpasses in the 'tank' portion of the algorithm using slightly different LFO modes in each case.

The thing I don't like is that even if you turn the decay all the way down and the damping all the way up you get a really strong 'tail' that is fairly long... apparently from the way the outputs are derived from the tank delays and especially the allpasses, which take time to reduce even if you are applying no further input (I'm guessing).

Some of the VST plugins I've auditioned don't seem to have this 'artifact', but I can't see how they get rid of it... perhaps I've still got a problem somewhere, but I can't see where I've departed from the paper.

Code: Select all

; Plate Reverb -- derived from Jon Dattorro paper "Effect Design"
; - Supposedly good sounding with minimal required resources
; - available at: https://ccrma.stanford.edu/~dattorro/EffectDesignPart1.pdf
; - coded by mdroberts1243 'at' gmail.com

;pot0=reverb level
;pot1=reverb time (decay... all the way up is infinite sustain in the tank)
;pot2 = hf loss in the tank (damping... turn up for MORE damping)

; fixed parameters from the paper
equ	decay_diffusion_1	0.70	;default parameters from Dattorro paper
equ	input_diffusion_1	0.75
equ	input_diffusion_2	0.625

; k1 for freqs: 
equ	k1_1kHz	0.82552
equ	k1_2kHz	0.68148
equ	k1_4kHz	0.46441
equ	k1_12kHz 0.232205
equ	bandwidth	1-k1_2kHz	; coefficient for input low-pass

equ	excursion	8	; peak excursion for tap modulation

; no idea what a suitable pre-delay would be...  could go as high as 7000+ samples
; 655=20ms, 3802=116ms, etc.
mem	predelay		655	; 3802=116ms predelay at 32kHz

; allpass names are formed from the Dattorro paper node numbers in Figure 1
; all the memory sizes have been scaled up by 1.1010x to account for difference in sampling
; original paper specified sample rate of 29761 Hz, we have 32768

mem	ap13_14		156	; coeff is input_diffusion_1
mem	ap19_20		117	; coeff is input_diffusion_1
mem	ap15_16		417	; coeff is input_diffusion_2
mem	ap21_22		305	; coeff is input_diffusion_2

mem	ap23_24		 740+excursion	; coeff is decay_diffusion_1
mem	ap46_48		1000+excursion	; coeff is decay_diffusion_1

mem	del24_30	4903
mem	del48_54	4643
mem	ap31_33		1982+excursion	; coeff is decay_diffusion_2 (derived from pot1)
mem	ap55_59		2924+excursion	; coeff is decay_diffusion_2
mem	del33_39	4096
mem	del59_63	3483

equ	krl			reg0	; coeff for reverb level (from pot0)
equ	decay			reg1	; coeff for reverb time (from pot1)
equ	decay_diffusion_2		reg2	; related to coeff for reverb time (from delay)
equ	damping			reg3	; coeff for high-frequency decay within the tank (from pot2)
equ	one_minus_dmpg		reg4
equ	lp_inp			reg5	; 'bandwidth' low-pass at input
equ	lp30_31			reg6	; tank low-pass set by 'damping'
equ	lp54_55			reg7	; tank low-pass set by 'damping'
equ	mono			reg8	; mono input signal
equ	diffuse_in		reg9	; output of input diffusers
equ	temp			reg10	; temp for expanded allpass calculations
equ	temp2			reg11	; another temp value for allpass

;
; code starts here!

; now generate a pair of LFOs to modulate the APs in the loop:

skp	run,2
wlds	SIN0,27,excursion	; paper calls for 1-2Hz, 25=1Hz, 50=2Hz
wlds	SIN1,23,excursion

;now derive control coefficients from pots:

rdax	pot0,1		; control reverb attenuation level
mulx	pot0		; square it
wrax	krl,0		; reverb level, write for later use

rdax	pot1,1
;mulx	pot1		; could square it if you like.
wrax	decay,1		; reverb time

; decay_diffusion_2 = decay + 0.15 but must range between 0.25 to 0.5
sof	1.0,-0.35		; check to see if we will go higher than ceiling (0.35 + 0.15)
skp	neg,1			;
clr				; set ceiling to 0.35 if we are still positive (after restoration below)
sof	1.0,0.35			; restore ACC (could combine with below)
sof	1.0,-0.10		; check to see if we will be below floor (0.10 + 0.15)
skp	gez,1
clr				; clr ACC will set floor to 0.25 after restoration below
sof	1.0,0.25			; restore ACC (+ 0.10) and add 0.15
wrax	decay_diffusion_2,0

rdax	pot2,1			; control high freq loss in the tank (low pass filter)
wrax	damping,-1		; low-pass coefficient
sof	1,0.9990234375		; make '1-damping' control from 1- pot2
wrax	one_minus_dmpg,0	; other low pass damping coefficient

; sum inputs to mono, with 0.5x input gain adjustment
rdax	adcl,0.5	;
rdax	adcr,0.5
wrax	mono,1		; leave signal in ACC

; do the pre-delay
wra	predelay,0

; input low-pass 
rda	predelay#,bandwidth
rdax	lp_inp,1-bandwidth
wrax	lp_inp,1

; now do input all passes:

rda	ap13_14#,-input_diffusion_1
wrap	ap13_14,input_diffusion_1
rda	ap19_20#,-input_diffusion_1
wrap	ap19_20,input_diffusion_1
rda	ap15_16#,-input_diffusion_2
wrap	ap15_16,input_diffusion_2
rda	ap21_22#,-input_diffusion_2
wrap	ap21_22,input_diffusion_2
wrax	diffuse_in,0

;
;allpassed input in place, now process the tank (two sides), with filtering
; - all the delays & ap-delays are modulated by LFOs... four different variations

; left side of Figure 1 tank
rda	del59_63#,1

mulx	decay
rdax	diffuse_in,1
wrax	temp,0

cho  	rda,sin0,sin|reg|compc,ap23_24#-excursion-1
cho 	rda,sin0,sin,ap23_24#-excursion
wrax	temp2,decay_diffusion_1	; store, apply coeff (note flipped sign for this AP)
rdax	temp,1			; add input
wra	ap23_24,-decay_diffusion_1	; write to head of delay
rdax	temp2,1				; add modulated tail 

wra	del24_30,0	;delay
rda	del24_30#,1

; simple low-pass with variable control
mulx	one_minus_dmpg
wrax	temp,0
rdax	lp30_31,1	
mulx	damping		;     damping derived from pot
rdax	temp,1
wrax	lp30_31,1

mulx	decay		; apply decay
wrax	temp,0		; save for applying a bit later...

; another allpass, but WRAP replaced to use variable coefficient
cho 	rda,sin1,cos|reg|compc,ap31_33#-excursion-1
cho 	rda,sin1,cos,ap31_33#-excursion
wrax	temp2,-1		; store temporarily, and negate
mulx	decay_diffusion_2	; mult with 'negative' coefficient from pot
rdax	temp,1			; add input from temp register
wra	ap31_33,1		; store to delay
mulx	decay_diffusion_2	; apply coeff
rdax	temp2,1			; add back in the modulated tail stored in temp

wra	del33_39,0	; delay
rda	del33_39#,1

; right side of Figure 1 tank, delay output already in ACC
mulx	decay
rdax	diffuse_in,1
wrax	temp,0

cho 	rda,sin0,cos|reg|compc,ap46_48#-excursion-1
cho 	rda,sin0,cos,ap46_48#-excursion
wrax	temp2,decay_diffusion_1	; store, apply coeff (note flipped sign for this AP)
rdax	temp,1			; add input
wra	ap46_48,-decay_diffusion_1
rdax	temp2,1				; add modulated tail

wra	del48_54,0	;delay
rda	del48_54#,1

; simple low-pass with variable control
mulx	one_minus_dmpg
wrax	temp,0
rdax	lp54_55,1	
mulx	damping		;     damping derived from pot
rdax	temp,1
wrax	lp54_55,1

mulx	decay		; apply decay
wrax	temp,0		; save for applying a bit later...

; another allpass, but WRAP replaced to use variable coefficient
cho  	rda,sin1,sin|reg|compc,ap55_59#-excursion-1
cho 	rda,sin1,sin,ap55_59#-excursion
wrax	temp2,-1		; store temporarily, and negate
mulx	decay_diffusion_2	; mult with 'negative' coefficient from pot
rdax	temp,1			; add input from temp register
wra	ap55_59,1		; store to delay
mulx	decay_diffusion_2	; apply coeff
rdax	temp2,1			; add back in the modulated tail stored in temp

wra	del59_63,0	; write delay leaving ACC clear

;
;now gather outputs from loop delays (values scaled for different sampling freq):

rda	del48_54+292,0.6
rda	del48_54+3274,0.6
rda	ap55_59+2107,-0.6
rda	del59_63+2198,0.6
rda	del24_30+2192,-0.6
rda	ap31_33+205,-0.6
rda	del33_39+1174,-0.6
mulx	krl				;attenuate reverb by Pot0 setting
wrax	dacl,0

rda	del24_30+389,0.6
rda	del24_30+3993,0.6
rda	ap31_33+1352,-0.6
rda	del33_39+2943,0.6
rda	del48_54+2325,-0.6
rda	ap55_59+369,-0.6
rda	del59_63+133,-0.6
mulx	krl				;attenuate reverb by Pot0 setting
wrax	dacr,0

;
;  DONE
-mark
My blog: http://tubenexus.com

MacroMachines
Posts: 71
Joined: Fri Dec 12, 2014 9:45 pm
Location: Detroit,MI
Contact:

Post by MacroMachines » Fri Jul 08, 2016 2:36 am

Decent reverb! I like it more with the excursion up to 256+
http://MacroMachines.net
Digital Control for your Analog Soul.

Post Reply