summaryrefslogtreecommitdiff
path: root/lib/spatial.lsp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spatial.lsp')
-rw-r--r--lib/spatial.lsp506
1 files changed, 506 insertions, 0 deletions
diff --git a/lib/spatial.lsp b/lib/spatial.lsp
new file mode 100644
index 0000000..bef95da
--- /dev/null
+++ b/lib/spatial.lsp
@@ -0,0 +1,506 @@
+; SPATIAL.LSP
+; created by Adam Hartman and Roger B. Dannenberg
+; 2005
+; stereo manipulation and spatialization functions
+
+; EMPHIGH -- use four equalizer bands to emphasize
+; the higher frequencies in an input sound
+;
+(defun emphigh (base)
+ (eq-band
+ (eq-band
+ (eq-band
+ (eq-band base 31 -3 1)
+ 62 -3 1)
+ 8000 3 1)
+ 16000 3 1))
+
+; EMPLOW -- use four equalizer bands to emphasize
+; the lower frequencies in an input sound
+;
+(defun emplow (base)
+ (eq-band
+ (eq-band
+ (eq-band
+ (eq-band base 31 3 1)
+ 62 3 1)
+ 8000 -3 1)
+ 16000 -3 1))
+
+; LEFTIN -- apply low frequency emphasis to a sound
+(defun leftin (inl) (emplow inl))
+
+; RIGHTIN - apply high frequency emphasis and
+; a very slight delay to a sound
+;
+(defun rightin (inr) (seq (s-rest 0.02) (emphigh inr)))
+
+; STEREOIZE -- create a stereo sound from a monaural source
+;
+(defun stereoize (monoin)
+ (vector (leftin monoin) (rightin monoin)))
+
+; EXTRACTLEFT -- extract the left channel of a stereo sound
+(defun extractleft (inl) (aref inl 0))
+
+; EXTRACTRIGHT -- extract the right channel of a stereo sound
+(defun extractright (inr) (aref inr 1))
+
+
+; WSUM -- weighted sum of two monaural sounds
+;
+; inl: first monaural sound
+; inr: second monaural sound
+; amtl: multiplier for the first monaural sound
+; amtr: multiplier for the second monaural sound
+;
+(defun wsum (inl inr amtl amtr)
+ (sum (mult inl amtl) (mult inr amtr)))
+
+
+; SMIXER -- remix a stereo signal
+;
+; in: original stereo sound
+; lamtl: amount in new left channel from the original left channel
+; lamtr: amount in new left channel from the original right channel
+; ramtl: amount in new right channel from the original left channel
+; ramtr: amount in new right channel from the original right channel
+; Note: lamtl, lamtr, ramtl, ramtr should have values in the
+; range of -1 to 1 and may be static numbers or sounds
+;
+(defun smixer (in lamtl lamtr ramtl ramtr)
+ (let ((eleft (extractleft in)) (eright (extractright in)))
+ (vector (wsum eleft eright lamtl lamtr)
+ (wsum eleft eright ramtl ramtr))))
+
+
+; WIDEN -- widen the field of a stereo sound
+;
+; in: original stereo sound
+; amt: a value between 0 and 1 which represents a widening factor
+; 0 will leave the sound unchanged while 1 indicates the widest
+; possible stereo field
+; Note: amt may be a static number or a sound
+;
+(defun widen (in amt)
+ (let ((widenamt (mult -1 amt)))
+ (smixer in 1 widenamt widenamt 1)))
+
+; SPAN -- pan the virtual center channel of a stereo sound
+;
+; in: original stereo sound
+; amt: a value between 0 and 1 which represents the panning location
+; 0 pans the center channel all the way to the left while 1 pans
+; it all the way to the right
+; Note: amt may be a static number or a sound
+;
+(defun span (in amt)
+ (let ((leftc (sum 0.5 (mult -1 amt))) (rightc (sum -0.5 amt)))
+ (smixer in 0.5 leftc rightc 0.5)))
+
+; SWAPCHANNELS -- swap the two channels in a stereo sound
+(defun swapchannels (in) (vector (aref in 1) (aref in 0)))
+
+#| NOTE: there's nothing wrong with the code that is commented out here.
+These functions were in the original library, but I have commented them
+out because they are very simple and not very general. Perhaps they
+can be incorporated in an expanded form in a future version of Nyquist.
+For example, some general 3-D positioning with Doppler effects, etc.,
+and some more elaborate HRTF code would be very interesting. Feel free
+to give these a try. -RBD
+
+; IID -- position a monaural sound source by attenuating the volume
+; of the sound at each ear point based on the distance between the
+; two ear points and the distance of the sound source from the listener
+;
+; in: monaural source sound
+; dist: lateral distance of the sound source from the listener in meters
+; headwidth: width of the listener's head (i.e. the distance between
+; the two ears) in meters
+; rorl: a value of either 0 or 1 which represents whether the sound
+; source is to the left or to the right of the listener
+;
+(defun iid (in dist headWidth RorL)
+ (let ((nearmult (/ 1.0 (mult dist dist)))
+ (farmult (/ 1.0 (mult (sum dist headWidth)
+ (sum dist headWidth)))))
+ (if (eq rorl 0)
+ ; sound source is to the left of listener
+ (vector (mult in nearmult) (mult in farmult))
+ ; sound source is to the right of listener
+ (vector (mult in farmult) (mult in nearmult)))))
+
+; ITD -- position a monaural sound source by delaying the arrival
+; of the sound at each ear point based on the distance
+; between the two ear points and the distance of the sound
+; source from the listener
+; in: monaural source sound
+; dist: lateral distance of the sound source from the listener in meters
+; headwidth: width of the listener's head (i.e. the distance
+; between the two ears) in meters
+; rorl: a value of either 0 or 1 which represents whether the sound
+; source is to the left or to the right of the listener
+;
+(defun itd (in dist headWidth RorL)
+ (let ((neardel (mult 0.0029387 dist))
+ (fardel (mult 0.0029387 (sum dist headWidth))))
+ (if (eq rorl 0)
+ ; sound source is to the left of listener
+ (vector (seq (s-rest neardel) in ) (seq (s-rest fardel) in))
+ ; sound source is to the right of listener
+ (vector (seq (s-rest fardel) in) (seq (s-rest neardel) in)))))
+
+
+; CFSPATIALIZATION -- a spatialization effect based on a cross-feed network
+;
+(defun cfspatialization (in)
+ (let ((shadowLeft (lp (seq (s-rest 0.0004) (aref in 0)) 265))
+ (shadowRight (lp (seq (s-rest 0.0004) (aref in 1)) 265)))
+ (vector (sum (aref in 0) shadowRight) (sum (aref in 1) shadowLeft))))
+
+; CUSTBP -- a helper function that creates a custom bandpass filter
+; for use in the hrtfapprox function
+(defun custbp (in) (lp (sum (hp in 4980) (mult in 0.75)) 7900))
+
+; HRTFAPPROX -- a spatialization effect based on an approximated HRTF
+;
+(defun hrtfapprox (in)
+ (let ((filteredLeft (seq (s-rest 0.00025) (custbp (aref in 0))))
+ (filteredRight (seq (s-rest 0.00025) (custbp (aref in 1)))))
+ (vector (sum (aref in 0) (lp filteredRight 10200))
+ (sum (aref in 0) (lp filteredLeft 1020)))))
+|#
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dolby Pro-Logic encoding and a
+;; 2D Sound Positioning Scheme
+;;
+;; Dave Borel (dborel) with minor changes by
+;; Roger B. Dannenberg
+;;
+;; Features:
+;; -Dolby Pro-Logic panning
+;; -Doppler for moving sounds
+;; -Distance attenuation
+;; -Atmospheric damping of high frequencies
+;; -Progagation delay
+;; -Test programs
+
+(setf config 1) ;Distance between listener and speakers
+
+;---------------------------------------------------
+; Math Helpers
+;---------------------------------------------------
+
+;
+; Distance between two points
+;
+(defun dist (x0 y0 x1 y1)
+ (let* ( (rx (sum x1 (mult -1.0 x0)))
+ (ry (sum y1 (mult -1.0 y0))) )
+ (s-sqrt (sum (mult rx rx) (mult ry ry)))))
+
+;
+; Raise x to each sample of snd
+;
+(defun s-expt (x snd)
+ (s-exp (mult (s-log x) snd)))
+
+
+;;
+;; SPATIALIZATION HELPERS:
+;;
+
+;
+; Doppler effect
+;
+(defun pl-doppler (snd r)
+ (let* ( (v (mult -1 (slope r)))
+ (ratio (recip (sum 1 (mult v (recip 344.31)))))
+ (map (integrate ratio)) )
+
+ (sound-warp map snd) ))
+
+
+;
+; Distance-based low-pass filter
+; (see report)
+;
+(defun absorb (snd r-m)
+ (lp snd (mult 14763.67 (s-expt 0.97895 r-m))))
+
+
+;
+; Distance-based attenuation
+; (see report)
+;
+(defun atten (snd r)
+
+; (let* ( (log2-r (mult (s-log r) (recip (log 10.0))))
+; (db-ratio (mult 20 log2-r))
+; (ratio (db-to-linear db-ratio)) )
+;
+; (mult (clip ratio 1.0) snd)))
+ (mult snd (clip (recip (mult r r)) 1)))
+
+;
+; Top-level spatializer
+; sound source at (x,y)
+; speaker at (xs, ys)
+; assumes listener at (0,0)
+;
+; You could use this with
+; configurations other than
+; pro-logic (e.g. 6.1, 7.1, etc)
+;
+(defun stage (snd x y xs ys)
+ (let* ( (r (dist x y 0 0))
+ (rs (dist xs ys 0 0))
+ (x-hat (mult x (recip r)))
+ (y-hat (mult y (recip r)))
+ (xs-hat (mult xs (recip rs)))
+ (ys-hat (mult ys (recip rs)))
+
+ (dot (sum (mult x-hat xs-hat) (mult y-hat ys-hat)))
+ (overlap (mult 0.5 (sum dot (s-abs dot)))) )
+ (mult overlap snd)))
+
+
+;---------------------------------------------------
+; Speaker Mixing
+;---------------------------------------------------
+;
+; Dolby Pro-Logic Encoder
+;
+(defun prologic (left center right surround)
+ (let* ( (c (scale 0.5 center))
+ (s (highpass2 (lowpass2 (scale 0.5 surround) 7000) 100) )
+ (slfe (scale 0.25 (lp surround 100)))
+ (l (sim left center (mult -1.0 s) slfe))
+ (r (sim right center s slfe)) )
+ (vector l r)))
+
+
+;
+; Direct-to-speaker playback
+;
+(defun pl-left (snd) (let ((s (mult 0 snd))) (prologic snd s s s)))
+(defun pl-center (snd) (let ((s (mult 0 snd))) (prologic s snd s s)))
+(defun pl-right (snd) (let ((s (mult 0 snd))) (prologic s s snd s)))
+(defun pl-rear (snd) (let ((s (mult 0 snd))) (prologic s s s snd)))
+
+;
+; Pans a sound across the surround speakers
+; (no realistic imaging or attenuation)
+; Works like pan but y specifies depth
+(defun pl-pan2d (s-in x y)
+ (let ((snd (scale 0.5 s-in)))
+ (prologic (mult snd (sum 1.0 (mult -1.0 x)
+ (sum 1 (mult -1.0 y))) );left
+ (mult snd 0.0) ;center(null)
+ (mult snd x (sum 1.0 (mult -1.0 y) )) ;right
+ (mult snd y 0.5)))) ;rear
+
+;
+; Position a sound in the 2D soundstage
+; Includes spatialization effects
+;
+; (x,y) may be (flonum, flonum) or (behavior, behavior)
+;
+(defun pl-position (s-in x y config)
+ (let* ( (r-m (dist x y 0 0))
+ (r (mult r-m (recip config)))
+ (spd-snd (/ 344.31 config))
+ (offset (if (soundp r-m)
+ (/ (aref (snd-samples r 1) 0) spd-snd)
+ (/ r spd-snd)))
+ (snd (seq (s-rest offset)
+ (if (soundp r-m)
+ (atten (absorb (pl-doppler s-in r-m) r-m) r)
+ (atten (absorb s-in r-m) r)))) )
+
+ ; Two Notes:
+ ; 1.) The center channel is automatically imaged correctly
+ ; because sounds placed between the left-and-right channels
+ ; distribute linearly between the two channels.
+ ;
+ ; 2.) Although the below settings assume that all speakers are
+ ; equidistant from the listener, you can easily assume a
+ ; different layout by modifying the xs and ys values in
+ ; each channel's call to the stage function.
+ ;
+ (prologic (stage snd x y -.1913 -.4619) ;left
+ (scale 0.0 snd) ;center (null)
+ (stage snd x y .1913 -.4619) ;right
+ (stage snd x y 0.0 .5 )))) ;rear
+
+
+;---------------------------------------------------
+; Diagnostics
+;---------------------------------------------------
+
+;
+; Pro-Logic Channel Test Tones
+;
+(defun pl-test ()
+ (play (prologic
+ ( osc-note a3 )
+ (seq (s-rest 1.25) (osc-note b3))
+ (seq (s-rest 2.5 ) (osc-note c4))
+ (seq (s-rest 3.75) (osc-note d4)) )))
+
+
+;
+; Pan Test
+;
+(defun pan-test ()
+ (play (pl-pan2d
+ (seq
+ (s-rest .25) (osc a3 .75)
+ (s-rest .25) (osc b3 .75)
+ (s-rest .25) (osc c4 .75)
+ (s-rest .25) (osc d4 .75))
+
+ (pwl
+ 0 0
+ 0.99 0
+
+ 1 1
+ 2.99 1
+
+ 3 0
+ 4 0
+ 4 )
+
+ (pwl
+ 0 0
+ 1.99 0
+
+ 2 1
+ 4 1
+ 4 ))))
+
+
+;
+; Doppler test
+;
+(defun dop ()
+ (play (pl-doppler (osc c4 10) (pwl .25 0 .5 100 .75 100 1.0))))
+
+;
+; Attenuation test
+;
+(defun att ()
+ (play (atten (osc-note c4 4)
+ (pwl 0 2
+ 1 2
+ 2 100
+ 3 100
+ 4 2
+ 4 ))))
+
+
+;
+; Doppler positioning test (ambulance)
+;
+(defun ambulance ()
+ (play (scale 0.2
+ (pl-position
+
+ (stretch 16 (fmosc c4 (mult 1000 (lfo 2))))
+
+ (pwl
+ 0 -20
+ 8 20
+ 8 )
+
+ (pwl
+ 0 0
+ 8 )
+
+ config))))
+
+
+;
+; Position test
+;
+
+; Make a sound orbit the listener
+(defun orbit-x (r t times)
+ (let (k)
+ (seqrep (k times)
+ (pwl
+ 0.0 0.0
+ (/ t 4) r
+ (/ t 2) 0.0
+ (/ (* t 3) 4) (* -1 r)
+ t 0.0
+ t ) )))
+(defun orbit-y (r t times)
+ (let (k)
+ (seqrep (k times)
+ (pwl
+ 0.0 (* -1 r)
+ (/ t 4.0) 0.0
+ (/ t 2.0) r
+ (/ (* t 3.0)4.0) 0.0
+ t (* -1 r)
+ t ) )))
+(defun orbit (snd r t times)
+ (pl-position snd
+ (orbit-x r t times)
+ (orbit-y r t times)
+ config))
+
+
+; Play some tones
+(defun pos-1 ()
+ (play (pl-position
+ (seq
+ (s-rest .125) (osc a3 1.75)
+ (s-rest .25) (osc b3 1.75)
+ (s-rest .25) (osc c4 1.75)
+ (s-rest .25) (osc d4 1.75) (s-rest .125))
+
+ (pwl
+ 0 -5
+ 1 5
+ 2 5
+ 3 -5
+ 4 -5
+ 5 5
+ 6 5
+ 7 -5
+ 8 -5
+ 8 )
+
+ (pwl
+ 0 -5
+ 1 -5
+ 2 5
+ 3 5
+ 4 -5
+ 5 -5
+ 6 5
+ 7 5
+ 8 -5
+ 8 )
+
+ config)))
+
+(defun pos-2 ()
+ (play (seq
+ (orbit (seq
+ (s-rest .125) (osc a3 1.75)
+ (s-rest .25) (osc b3 1.75)
+ (s-rest .25) (osc c4 1.75)
+ (s-rest .25) (osc d4 1.75) (s-rest .125))
+
+ 5 8 1)
+ (orbit (seq
+ (s-rest .125) (osc a3 1.75)
+ (s-rest .25) (osc b3 1.75)
+ (s-rest .25) (osc c4 1.75)
+ (s-rest .25) (osc d4 1.75) (s-rest .125))
+
+ 5 8 1))))