A Spin on the Dattorro Plate Reverb
Posted: 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.
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