Spring reverb

Algorithm development and general DSP issues

Moderator: frank

donstavely
Posts: 53
Joined: Thu Jan 07, 2010 2:29 pm
Location: Windsor, Colorado

Spring reverb

Post by donstavely »

Hello all. I haven't been doing FV-1 stuff for a while, but my son asked me if I could do a spring reverb. I was surprised at how little info there was on the forum. Learning more about it, I see why now. It is totally unlike other reverb algorithms, and it is hard to do. Being a retired electrical engineer, I decided to wade through what technical papers I could find on the subject, and then see what I could do on the FV-1. Here is a short tutorial if anyone is still interested:

Spring reverb has unique sound due to the way sound travels down the spring itself. High frequencies travel slower than low frequencies, a characteristic called "dispersion". The classic "boing" of a tapped spring results from the impulse being dispersed into a "chirp".

Simulating the dispersion of the spring requires a "spectral delay filter". It can be implemented with a very large number of unit delay allpass filters. The allpass provides a frequency-dependent phase shift. Put a lot of them in series and the phase shift adds up to a delay.

We need many milliseconds of delay of the high frequencies for the effect to be noticeable - 8ms at 32KHz sample rate is 256 allpasses! All is not lost, though. If we bandwidth-limit the signal, we can "stretch" the allpasses by going from unit delays to longer delays. Not hundreds of samples like conventional "reflection emulating" reverbs - more like 3 or 4 samples. At 4, now we are talking 64 allpasses. The FV-1 does one allpass in two instructions, so this would totally fill the program memory. We need to compromise further to get anything else done, but we are at least in the ballpark.

Add a couple of long simple delays in front of the allpass string, some filtering, and feedback around the whole thing. This is as close as we are going to get to a spring reverb within the confines of the FV-1.

I have coded it up and it indeed sounds "springy". I don't have a real spring reverb to compare it too, though. This has just been an intellectual exercise for me - no intent to develop a product.

I hesitate to share code because I don't want to field questions about what each line of code does or how to change it. I think that the information I have provided here should be enough for any FV-1 jockeys out there to get started.

Good luck,
Don
Don Stavely
Iconnu
Posts: 10
Joined: Thu Apr 17, 2014 7:49 pm

Post by Iconnu »

Do you have an audio clip? I'm curious to hear it.

Iconnu
Digital Larry
Posts: 338
Joined: Mon Nov 12, 2012 1:12 pm
Contact:

Post by Digital Larry »

The "Chirp" block in SpinCAD Designer recent builds was an idea taken from a paper I read about DSP emulation of spring reverb. I did some experiments to see if I could get a spring sound. I got as far as something that sounded pretty wild on percussive material but does not like sustained tones at all.
donstavely
Posts: 53
Joined: Thu Jan 07, 2010 2:29 pm
Location: Windsor, Colorado

Spring reverb

Post by donstavely »

I said I was hesitant to post the code, but what the hell:

Code: Select all

; SPRING REVERB			(c) 2016 Don Stavely
;				Please, not for commercial use!
; Description:
; Spring reverbs sound "boingy" because of dispersion in the spring - 
; higher frequencies travel slower than low frequencies.
; A "spectral delay filter", consisting of many (100's) of unit allpasses 
; will produce the desired "chirp" impulse response.   
; The number of APs can be reduced by "stretching" the allpass filters,  
; using delays larger than one.
; Using different chirp AP lengths spreads the eigentones (?)
; Reverb loop looks like std X-coupled AP-AP-DELAY loops, but inputs 
; and outputs moved so first echos straight out of delays, not APs.
;
;           +-------------------------------------+
;           |                                     |
; in---[+]- | --[  D1  ]--[lp]--+--[AP1a]--[AP1b]-+
;       |   |                   |
;       |   |                  [+]---------------->[CHIRP APs]--->
;       |   |                   |
; in----| -[+]--[  D2  ]--[lp]--+--[AP2a]--[AP2b]-+ 	
;       |                                         |
;       +-----------------------------------------+
;
; Delay, AP lengths scaled from GA reverb, modded close to Accutronics
; 2-spring delay lengths of 33ms and 41ms.
; Filtering inside the loop and sin/cos LFO smearing of the reverb APs
; also reduces ringing.
; Oh, and it has tremolo so my son can use it for surf rock.  You could 
; add tone and reverb time controls instead.
;
; Pot0 = Reverb Level
; Pot1 = Tremolo Rate
; pot2 = Tremolo Depth

; Declare constants

equ	LEN1	5	; length of chirp filters
equ	LEN2	6
equ	LEN3	6
equ	LEN4	7
equ	LEN5	7
equ	LEN6	8
equ	KAP	-0.6	; chirp allpass coefficient
equ	KLAP	0.6	; allpass coefficient
equ	KRT	0.85	; reverb time
equ	KRF	0.55	; reverb lpf freq
equ	KRS	-1	; reverb lpf shelf

; Memory declarations

mem	lap1a	404	;      	  GA/2 =	404
mem	lap1b	967	;		967
mem	d1	1445	; 41ms=1344	1244.5

mem	lap2a	608	;	  GA/2 =	508
mem	lap2b	893	;		893.5		
mem	d2	1013	; 33ms=1081	1143.5

mem	ap1	LEN1	; chirp allpasses
mem	ap2	LEN1
mem	ap3	LEN1
mem	ap4	LEN1
mem	ap5	LEN1
mem	ap6	LEN1
mem	ap7	LEN1
mem	ap8	LEN2
mem	ap9	LEN2
mem	ap10	LEN2
mem	ap11	LEN2
mem	ap12	LEN2
mem	ap13	LEN2
mem	ap14	LEN2
mem	ap15	LEN3
mem	ap16	LEN3
mem	ap17	LEN3
mem	ap18	LEN3
mem	ap19	LEN3
mem	ap20	LEN3
mem	ap21	LEN3
mem	ap22	LEN4
mem	ap23	LEN4
mem	ap24	LEN4
mem	ap25	LEN4
mem	ap26	LEN4
mem	ap27	LEN4
mem	ap28	LEN4
mem	ap29	LEN5
mem	ap30	LEN5
mem	ap31	LEN5
mem	ap32	LEN5
mem	ap33	LEN5
mem	ap34	LEN5
mem	ap35	LEN5
mem	ap36	LEN6
mem	ap37	LEN6
mem	ap38	LEN6
mem	ap39	LEN6
mem	ap40	LEN6
mem	ap41	LEN6
mem	ap42	LEN6

; Register equates

equ	mono	reg0
equ	lp1	reg1
equ	lp2	reg2
equ 	trem	reg3
equ	revout	reg4

; Initialize LFOs

skp	run, endinit
wlds	sin0,15,40	; to smear reverb
wlds	sin1,12,32767	; for tremolo
endinit:

; Control and get tremelo sinwave

rdax	pot1, 1		; rate
mulx	pot1		
sof	0.6, 0.1		; 7:1 range
wrax	sin1_rate, 0		
cho	RDAL, sin1
mulx	pot2		; depth
sof	0.5, 0.5		; 0 to 1
wrax	trem, 0		; save LFO value	

; Sum inputs to mono, apply tremolo

rdax	adcl, 0.5
rdax	adcr, 0.5
mulx	trem		; apply tremolo
wrax	mono, 0	

; Do reverb loops

rda	d1#, KRT	; get 1st delay output, scaled by RT
rdfx	lp1, KRF		; shelving lowpass inside loop
wrlx	lp1, KRS
rda	lap1a#, KLAP	; reverb allpasses
wrap	lap1a, -KLAP
rda	lap1b#, KLAP
wrap	lap1b, -KLAP
rdax	mono, 1		; add input
wra	d2, 0		; put in 2nd spring delay, clear

rda	d2#, KRT	; get 2nd delay output, saled by RT
rdfx	lp2, KRF		; shelving lowpass inside loop
wrlx	lp2, KRS
rda	lap2a#, KLAP	; reverb allpasses
wrap	lap2a, -KLAP
rda	lap2b#, KLAP
wrap	lap2b, -KLAP
rdax	mono, 1		; add input
wra	d1, 0		; put in 1st spring delay, clear

; Get reverb output, do chirp filter

rdax	lp1,1
rdax	lp2,1
wrax	revout, 1
rda	ap1#, KAP
wrap	ap1, -KAP
rda	ap2#, KAP
wrap	ap2, -KAP
rda	ap3#, KAP
wrap	ap3, -KAP
rda	ap4#, KAP
wrap	ap4, -KAP
rda	ap5#, KAP
wrap	ap5, -KAP
rda	ap6#, KAP
wrap	ap6, -KAP
rda	ap7#, KAP
wrap	ap7,-KAP
rda	ap8#, KAP
wrap	ap8, -KAP
rda	ap9#, KAP
wrap	ap9, -KAP
rda	ap10#, KAP
wrap	ap10, -KAP
rda	ap11#, KAP
wrap	ap11, -KAP
rda	ap12#, KAP
wrap	ap12, -KAP
rda	ap13#, KAP
wrap	ap13, -KAP
rda	ap14#, KAP
wrap	ap14, -KAP
rda	ap15#, KAP
wrap	ap15, -KAP
rda	ap16#, KAP
wrap	ap16, -KAP
rda	ap17#, KAP
wrap	ap17, -KAP
rda	ap18#, KAP
wrap	ap18, -KAP
rda	ap19#, KAP
wrap	ap19, -KAP
rda	ap20#, KAP
wrap	ap20, -KAP
rda	ap21#, KAP
wrap	ap21, -KAP
rda	ap22#, KAP
wrap	ap22, -KAP
rda	ap23#, KAP
wrap	ap23, -KAP
rda	ap24#, KAP
wrap	ap24, -KAP
rda	ap25#, KAP
wrap	ap25, -KAP
rda	ap26#, KAP
wrap	ap26, -KAP
rda	ap27#, KAP
wrap	ap27, -KAP
rda	ap28#, KAP
wrap	ap28, -KAP
rda	ap29#, KAP
wrap	ap29, -KAP
rda	ap30#, KAP
wrap	ap30, -KAP
rda	ap31#, KAP
wrap	ap31, -KAP
rda	ap32#, KAP
wrap	ap32, -KAP
rda	ap33#, KAP
wrap	ap33, -KAP
rda	ap34#, KAP
wrap	ap34, -KAP
rda	ap35#, KAP
wrap	ap35, -KAP
rda	ap36#, KAP
wrap	ap36, -KAP
rda	ap37#, KAP
wrap	ap37, -KAP
rda	ap38#, KAP
wrap	ap38, -KAP
rda	ap39#, KAP
wrap	ap39, -KAP
rda	ap40#, KAP
wrap	ap40, -KAP
rda	ap41#, KAP
wrap	ap41, -KAP
;rda	ap42#, KAP	; as many as can fit
;wrap	ap42, -KAP

; Chirp out in ACC - add dry, and output it
	
mulx	pot0		; reverb level
rdax	mono, 1		; add dry signal
wrax	dacl, 1	
wrax	dacr, 0		; output 

; Smooth reverb with LFO modulating APs

cho	rda,sin0,sin|reg|compc,lap1b+25
cho	rda,sin0,sin,lap1b+26
wra	lap1b+50,0
cho	rda,sin0,cos|reg|compc,lap2b+25
cho	rda,sin0,cos,lap2b+26
wra	lap2b+50,0

; End
Don Stavely
Iconnu
Posts: 10
Joined: Thu Apr 17, 2014 7:49 pm

Post by Iconnu »

Don,

that was very nice of you to share your code. Sounds very good. I saw that in the code you put a question mark after "eigentone". These are the various frequencies that cause resonance in a system. Mechanical systems can have an infinite amount of resonant frequencies. In the spring reverb each spring will have a natural frequency (which will have the largest magnitude) and then a bunch of others with diminishing magnitudes. Spreading these eigentones creates a more pleasant sound as opposed to having them all near each other thus creating a booming response when you play a note that is close to the group of resonant frequencies.


Its 2am I hope I'm making sense.

I.
direct
Posts: 8
Joined: Sun Mar 10, 2013 1:19 pm

Post by direct »

(Wrax revout, 1) only write to the register but never been called, what is the role?
PhilHaw
Posts: 14
Joined: Wed Feb 27, 2013 9:10 am
Location: Northern Ireland
Contact:

Post by PhilHaw »

Iconnu wrote:Don,

that was very nice of you to share your code. Sounds very good. I saw that in the code you put a question mark after "eigentone". These are the various frequencies that cause resonance in a system. Mechanical systems can have an infinite amount of resonant frequencies. In the spring reverb each spring will have a natural frequency (which will have the largest magnitude) and then a bunch of others with diminishing magnitudes. Spreading these eigentones creates a more pleasant sound as opposed to having them all near each other thus creating a booming response when you play a note that is close to the group of resonant frequencies.


Its 2am I hope I'm making sense.

I.
How did you get this to assemble? I can't!

Surely there are too many instructions for the FV-1?

Phil.
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

I just tried it and it assembled fine. Not too many instructions (128 exactly), mem and equ statements are assembler directives not instructions.
Frank Thomson
Experimental Noize
PhilHaw
Posts: 14
Joined: Wed Feb 27, 2013 9:10 am
Location: Northern Ireland
Contact:

Post by PhilHaw »

frank wrote:I just tried it and it assembled fine. Not too many instructions (128 exactly), mem and equ statements are assembler directives not instructions.
Hi Frank,

I get an error log that says:

<0000>[ Pass 1] [ 1015] Line: 250 "rdax   mono, 1       " - ERROR:Program Length Exceeds Limit -
<0001>[ Pass 1] [ 1031] Line: 250 "rdax   mono, 1       " - ERROR:FAILED On Pass - ONE


Spring Reverb (Don Stavely from Spinsemi Forum).spn [FAILED BUILD] Build Stopped..
2 Log Messages


and that was after commenting out a lot of the AP's.

Phil.
PhilHaw
Posts: 14
Joined: Wed Feb 27, 2013 9:10 am
Location: Northern Ireland
Contact:

Post by PhilHaw »

I just copied and pasted the code from Don's post above and get this error when I try to assemble it:

<0000>[ Pass 1] [ 1015] Line: 191 "wrap   ap15, -KAP " - ERROR:Program Length Exceeds Limit -
<0001>[ Pass 1] [ 1031] Line: 191 "wrap   ap15, -KAP " - ERROR:FAILED On Pass - ONE


Spring Reverb (Don Stavely Copied from Spinsemi Forum).spn [FAILED BUILD] Build Stopped..
2 Log Messages


Phil.
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

I just installed SpinAsm on a different computer and tried it and it assembled fine. What is your computer setup?
Frank Thomson
Experimental Noize
PhilHaw
Posts: 14
Joined: Wed Feb 27, 2013 9:10 am
Location: Northern Ireland
Contact:

Post by PhilHaw »

frank wrote:I just installed SpinAsm on a different computer and tried it and it assembled fine. What is your computer setup?
It's a Windows 10 OS with 16GB ram. All other SpinAsm programs I've tried assemble just fine.

Ice9 tells me he's getting the same error as me after copying Don's code from the post above. Are you copying and pasting this into SpinAsm as we are or are you using an older file that you previously saved onto your computer?
I'm just wondering in case Don changed the code in the post.

Thanks for your help anyway,

Phil.
Digital Larry
Posts: 338
Joined: Mon Nov 12, 2012 1:12 pm
Contact:

Post by Digital Larry »

Make sure you don't somehow have two copies of the code in the Spin IDE. I'm not 100% sure but I think I've seen that happen with copy-paste.

There was some other issue recently that was traced to locale settings (need to use comma for decimal point - or leading zero on a numeric value dropped, or something like that), so weird things sometimes happen that nobody can reproduce because our environment is not the same.
PhilHaw
Posts: 14
Joined: Wed Feb 27, 2013 9:10 am
Location: Northern Ireland
Contact:

Post by PhilHaw »

Ice9 has sent me a version from which he has removed the tremolo sections and that does assemble. I've compared the two side-by-side and I can't see any evidence that I've got two copies of Don's original code

I've come across the locale settings issue with some of my own apps but I don't think it's that. In the UK we use the period (.) as the decimal separator; in the rest of Europe they use the comma. Time for Brexit :lol:

Thanks again Frank,

Phil.
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

I am doing a copy/paste so it was the same as what you are doing.

The only big difference I can see is the locale setting, I am also on Windows 10 on both machines I've tried. Try changing to locale to US, both use the period decimal separator but there may be something in Windows that is handling things differently.

Actually try this leaving the locale to UK, take a new copy of the code, delete from line 190 down since the error says it starts at line 191. Assemble the code then click the view machine code icon (the m: with the underline) and see what it thinks the other code lines are. Something is being interpreted as a code line that isn't, could be comment lines with a particular character, directive lines, etc.
Frank Thomson
Experimental Noize
Post Reply