|
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Suggestions, comments and bug reports are welcome. Please
- ;;; address email to: nando@ccrma.stanford.edu
- ;;;
- ;;; see the file COPYING for license info.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Dynamic multichannel three-dimentional signal locator
- ;;; (wow that sound good! :-)
- ;;;
- ;;; by Fernando Lopez Lezcano
- ;;; CCRMA, Stanford University
- ;;; nando@ccrma.stanford.edu
- ;;;
- ;;; Thanks to Juan Pampin for help in the initial coding of the new version
- ;;; and for prodding me to finish it. To Joseph L. Anderson and Marcelo Perticone
- ;;; for insights into the Ambisonics coding and decoding process.
- ;;; http://www.york.ac.uk/inst/mustech/3d_audio/ambison.htm for more details...
-
- ;;; CHANGES:
- ;;; 01/28/2012: third order ambisonics support (Nando).
- ;;; 04/18/2011: various small changes from lint.scm.
- ;;; 04/26/2010: add delay hack to remove artifacts in delay output, fix other bugs (Nando)
- ;;; added proper doppler src conversion thanks to Bill's code in dsp.scm
- ;;; merged in code for higher order ambisonics (up to 2nd order h/v)
- ;;; 06/28/2009: remove class/method stuff for s7 (Bill)
- ;;; 01/08/2007: make a few functions local etc (Bill)
- ;;; 07/05/2006: translate to scheme, use move-sound generator (Bill)
- ;;; 04/29/2002: fixed reverb envelopes for no reverb under clisp
- ;;; 01/14/2001: added multichannel reverb output with local and global control
- ;;; in the reverberator (the hrtf code is currently not merged into
- ;;; this version). Reverb-amount can now be an envelope. Ambisonics
- ;;; now outputs signal to the reverb stream.
- ;;; 02/05/2000: . don't compile as part of the clm package, import symbols
- ;;; . switched over to clm-2, otherwise convolve HAS to operate
- ;;; on a file, we want convolve to process the output of the
- ;;; doppler delay line (for the hrtf code)
- ;;; 02/03/2000: started working on hrtf's
- ;;; 01/15/2000: rewrote transform-path code to account for 3d paths
- ;;; 01/13/2000: . changed order of b-format output file to W:X:Y:Z from X:Y:Z:W
- ;;; . plot method would bomb with paths that had constant velocity,
- ;;; fixed norm function
- ;;; . added make-literal-path and friends to enable to create
- ;;; paths with user supplied coordinates
- ;;; . decoded-ambisonics was rotating sounds in the wrong direction
- ;;; . in change-direction: only check for change if both points are
- ;;; different (intersect-inside-radius can create a redundant point)
- ;;; 11/28/1999: decoded-ambisonics is now working for N channels
- ;;; includes reverberation send
- ;;; 11/27/1999: set minimum segment distance for rendering, otherwise for long
- ;;; lines the amplitude envelope does not reflect power curve.
- ;;; 11/26/1999: added code to check for intersection with inner radius
- ;;; fixed nearest-point to handle trivial cases
-
- ;;; 01/21/2001: fix envelope generated for mono reverberation stream.
- ;;; change input and output to use frames and mixers
- ;;; fix supersonic movement warning code
- ;;; > add warnings when object goes outside of area covered by speakers
- ;;; > fix one common vertice case of 3 speaker group transitions
- ;;; > redo the old code for multiple images (reflections in a rectangular room)
- ;;; a bit of a pain, would have to add z (ceiling and floor reflections)
- ;;; would be better to find general purpose code for non-rectangular rooms
- ;;; > we really need a good N-channel reverb [fixed with freeverb]
- ;;; > change reverb to be multichannel, add local and global reverb
- ;;; 11/24/1999: should use a waveguide reverb like pph@ccrma project
- ;;; would be a good idea to inject the signal through a center
- ;;; injection point that moves inside the virtual cube, more like
- ;;; a physical model of what actually happens in a room
- ;;; | add ambisonics back-end
- ;;; 11/24/1999: code to b-format sort of working
- ;;; how to deal with the inner space and 0:0:0?
- ;;; decoded format not working if we incorporate distance att
- ;;; formulas are wrong...
- ;;; > add hrtf back-end
- ;;; > extract a supath from an existing path
- ;;; > recode the transformation functions
- ;;; > add arcs of circles and other basic geometric paths
- ;;; make it so that you can concatenate them...
- ;;; | 11/25/1999 fix the "diagonal case" (sounds go through the head of the listener)
-
- (provide 'snd-dlocsig.scm)
-
- #|
- (define* (envelope-interp x e base) ;e is list of x y breakpoint pairs, interpolate at x returning y
- ;; "(envelope-interp x e (base 1.0)) -> value of e at x; base controls connecting segment type: (envelope-interp .3 '(0 0 .5 1 1 0) -> .6"
- (cond ((null? e) 0.0) ;no data -- return 0.0
- ((or (<= x (car e)) ;we're sitting on x val (or if < we blew it)
- (null? (cddr e))) ;or we're at the end of the list
- (cadr e)) ;so return current y value
- ((> (caddr e) x) ;x <= next env x axis value
- (if (or (= (cadr e) (cadddr e))
- (and base (= base 0.0)))
- (cadr e) ;y1=y0, so just return y0 (avoid endless calculations below)
- (if (or (not base) (= base 1.0))
- (+ (cadr e) ;y0+(x-x0)*(y1-y0)/(x1-x0)
- (* (- x (car e))
- (/ (- (cadddr e) (cadr e))
- (- (caddr e) (car e)))))
- (+ (cadr e) ; this does not exactly match xramp-channel
- (* (/ (- (cadddr e) (cadr e))
- (- base 1.0))
- (- (expt base (/ (- x (car e))
- (- (caddr e) (car e))))
- 1.0))))))
- (else (envelope-interp x (cddr e) base)))) ;go on looking for x segment
- |#
-
- (define x-norm
- (let ((documentation "(x-norm e xmax) changes 'e' x axis values so that they run to 'xmax'"))
- (lambda (e xmax)
- (let x-norm-1 ((e e)
- (scl (/ xmax (e (- (length e) 2))))
- (new-e ()))
- (if (null? e)
- (reverse! new-e)
- (x-norm-1 (cddr e) scl (cons (cadr e) (cons (* scl (car e)) new-e))))))))
-
-
- ;;;;;;;;;;;;;;;;;;;;;
- ;;; Global Parameters
-
- ;;; Define the base in which all angles are expressed
- (define dlocsig-one-turn 360)
-
- (define one-turn-is
- (let ((documentation "(one-turn-is unit) sets dlocsig's angle unit (degrees=360, the default or radians=2*pi)"))
- (lambda (unit)
- (set! dlocsig-one-turn unit))))
-
- (define angles-in-degree
- (let ((documentation "(angles-in-degree) sets dlocsig's unit to degrees (the default)"))
- (lambda ()
- (one-turn-is 360))))
-
- (define angles-in-radians
- (let ((documentation "(angles-in-radians) sets dlocsig's unit to radians (default is degrees)"))
- (lambda ()
- (one-turn-is (* 2 pi)))))
-
- (define angles-in-turns
- (let ((documentation "(angles-in-turns) sets dlocsig's angle unit to turns"))
- (lambda ()
- (one-turn-is 1))))
-
- ;; speed of sound in air, in meters per second under normal conditions
- (define dlocsig-speed-of-sound 344)
-
- (define distances-in-meters
- (let ((documentation "(distances-in-meters) sets dlocsig's distances in meters (the default)"))
- (lambda ()
- (set! dlocsig-speed-of-sound 344))))
-
- (define distances-in-feet
- (let ((documentation "(distances-in-feet) sets dlocsig's distances in feet (default is meters)"))
- (lambda ()
- (set! dlocsig-speed-of-sound 1128))))
-
- ;; default for whether to use two or three-dimensional speaker configurations
- (define dlocsig-3d #f)
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Speaker Configuration
-
- (define* (make-group (id 0) (size 0) vertices speakers matrix)
- (list 'group id size vertices speakers matrix))
-
- (define group-id (dilambda (lambda (a) (a 1)) (lambda (a b) (set! (a 1) b))))
- (define group-size (dilambda (lambda (a) (a 2)) (lambda (a b) (set! (a 2) b))))
- (define group-vertices (dilambda (lambda (a) (a 3)) (lambda (a b) (set! (a 3) b))))
- (define group-speakers (dilambda (lambda (a) (a 4)) (lambda (a b) (set! (a 4) b))))
- (define group-matrix (dilambda (lambda (a) (a 5)) (lambda (a b) (set! (a 5) b))))
-
-
- (define* (make-speaker-config number dimension coords groups delays omap)
- (list 'speaker-config number dimension coords groups delays omap))
-
- (define speaker-config-number (dilambda (lambda (a) (a 1)) (lambda (a b) (set! (a 1) b))))
- (define speaker-config-dimension (dilambda (lambda (a) (a 2)) (lambda (a b) (set! (a 2) b))))
- (define speaker-config-coords (dilambda (lambda (a) (a 3)) (lambda (a b) (set! (a 3) b))))
- (define speaker-config-groups (dilambda (lambda (a) (a 4)) (lambda (a b) (set! (a 4) b))))
- (define speaker-config-delays (dilambda (lambda (a) (a 5)) (lambda (a b) (set! (a 5) b))))
- (define speaker-config-map (dilambda (lambda (a) (a 6)) (lambda (a b) (set! (a 6) b))))
-
-
- ;;; Create a speaker configuration structure based on a list of speakers
- ;;;
- ;;; speakers: list of angles of speakers with respect to 0
- ;;; delays: list of delays for each speaker, zero if nil
- ;;; distances: list relative speaker distances,
- ;;; (instead of delays)
- ;;; omap: mapping of speakers to output channels
- ;;; content should be output channel number, zero based
-
- (define cis
- (let ((documentation "(cis a) returns e^(ia)"))
- (lambda (a)
- (exp (* 0.0+1.0i a)))))
-
- (define third
- (let ((documentation "(third lst) returns the 3rd element of 'lst'"))
- (lambda (a)
- (and (>= (length a) 3) (a 2)))))
-
- (define fourth
- (let ((documentation "(fourth lst) returns the 4th element of 'lst'"))
- (lambda (a)
- (and (>= (length a) 4) (a 3)))))
-
- (define* (last lst (n 1))
- (and (pair? lst)
- (list-tail lst (- (length lst) n))))
-
- (define* (arrange-speakers (speakers ())
- (groups ())
- (delays ())
- (distances ())
- (channel-map ()))
- ;; sanity checking of configuration
-
- (define (has-duplicates? lst)
- (and (not (null? lst))
- (or (member (car lst) (cdr lst))
- (has-duplicates? (cdr lst)))))
-
- (define (invert3x3 mat) ; invert a 3x3 matrix using cofactors
- (let ((m (make-float-vector '(3 3))))
- (do ((i 0 (+ i 1)))
- ((= i 3))
- (do ((j 0 (+ j 1)))
- ((= j 3))
- (set! (m i j) (mat i j))))
- (set! (mat 0 0) (- (* (m 1 1) (m 2 2)) (* (m 1 2) (m 2 1))))
- (set! (mat 0 1) (- (* (m 0 2) (m 2 1)) (* (m 0 1) (m 2 2))))
- (set! (mat 0 2) (- (* (m 0 1) (m 1 2)) (* (m 0 2) (m 1 1))))
- (set! (mat 1 0) (- (* (m 1 2) (m 2 0)) (* (m 1 0) (m 2 2))))
- (set! (mat 1 1) (- (* (m 0 0) (m 2 2)) (* (m 0 2) (m 2 0))))
- (set! (mat 1 2) (- (* (m 0 2) (m 1 0)) (* (m 0 0) (m 1 2))))
- (set! (mat 2 0) (- (* (m 1 0) (m 2 1)) (* (m 1 1) (m 2 0))))
- (set! (mat 2 1) (- (* (m 0 1) (m 2 0)) (* (m 0 0) (m 2 1))))
- (set! (mat 2 2) (- (* (m 0 0) (m 1 1)) (* (m 0 1) (m 1 0))))
-
- (let ((det (+ (* (m 0 0) (mat 0 0))
- (* (m 0 1) (mat 1 0))
- (* (m 0 2) (mat 2 0)))))
- (and (> (abs det) 1e-06)
- (do ((invdet (/ 1.0 det))
- (row 0 (+ 1 row)))
- ((= row 3) mat)
- (do ((col 0 (+ 1 col)))
- ((= col 3))
- (set! (mat row col) (* (mat row col) invdet))))))))
-
- (define (invert2x2 mat) ; invert a 2x2 matrix
- (let ((m (make-float-vector '(2 2)))
- (det (- (* (mat 0 0) (mat 1 1))
- (* (mat 1 0) (mat 0 1)))))
- (and (> (abs det) 1e-06)
- (begin
- (set! (m 0 0) (/ (mat 1 1) det))
- (set! (m 1 1) (/ (mat 0 0) det))
- (set! (m 0 1) (- (/ (mat 0 1) det)))
- (set! (m 1 0) (- (/ (mat 1 0) det)))
- m))))
-
- (if (null? speakers)
- (error 'mus-error "a speaker configuration must have at least one speaker!~%"))
-
- (if (pair? groups)
- (let ((first-len (length (car groups))))
- (for-each
- (lambda (group)
- (if (not (= (length group) first-len))
- (error 'mus-error "all groups must be of the same length! (~A)~%" first-len)))
- groups))
-
- ;; if the speakers are defined with only azimuth angles (no elevation)
- (if (not (pair? (car speakers)))
- ;; then create the groups ourselves because it is a 2d configuration;
- ;; we could create the 3d configuration groups but the algorithm for
- ;; doing that in the generic case is not trivial
-
- (let ((len (length speakers)))
- (if (= len 1)
- (set! groups (list (list 0)))
- (do ((i 0 (+ i 1))
- (j 1 (+ j 1)))
- ((= i len)
- (set! groups (reverse groups)))
- (set! groups (cons (list i (modulo j len)) groups)))))))
-
- (if (null? groups)
- (error 'mus-error "no groups specified, speakers must be arranged in groups~%"))
-
- (when (pair? delays)
- (if (pair? distances)
- (error 'mus-error "please specify delays or distances but not both~%"))
-
- (if (> (length speakers) (length delays))
- (error 'mus-error "all speaker delays have to be specified, only ~A supplied [~A]~%" (length delays) delays)
- (if (< (length speakers) (length delays))
- (error 'mus-error "more speaker delays than speakers, ~A supplied instead of ~A [~A]~%" (length delays) (length speakers) delays)))
-
- (for-each
- (lambda (dly)
- (if (< dly 0.0) (error 'mus-error "delays must be all positive, ~A is negative~%" dly)))
- delays))
-
- (when (pair? distances)
- (if (> (length speakers) (length distances))
- (error 'mus-error "all speaker distances have to be specified, only ~A supplied [~A]~%" (length distances) distances)
- (if (< (length speakers) (length distances))
- (error 'mus-error "more speaker distances than speakers, ~A supplied instead of ~A [~A]~%" (length distances) (length speakers) distances)))
-
- (for-each
- (lambda (dly)
- (if (< dly 0.0) (error 'mus-error "distances must be all positive, ~A is negative~%" dly)))
- distances))
-
- (if (pair? channel-map)
- (if (> (length speakers) (length channel-map))
- (error 'mus-error "must map all speakers to output channels, only ~A mapped [~A]~%" (length channel-map) channel-map)
- (if (< (length speakers) (length channel-map))
- (error 'mus-error "trying to map more channels than there are speakers, ~A supplied instead of ~A [~A]~%"
- (length channel-map) (length speakers) channel-map))))
-
- ;; collect unit vectors describing the speaker positions
- (let* ((coords
- (let ((val ()))
- (for-each
- (lambda (s) ; speakers
- (let* ((evec (let ((e (if (pair? s) (or (cadr s) 0.0) 0.0)))
- (cis (/ (* e 2 pi) dlocsig-one-turn))))
- (avec (let ((a (if (pair? s) (car s) s)))
- (cis (/ (* a 2 pi) dlocsig-one-turn))))
- (dxy (real-part evec))
- (z (imag-part evec))
- (x (* dxy (imag-part avec)))
- (y (* dxy (real-part avec)))
- (mag (sqrt (+ (* x x) (* y y) (* z z)))))
- (set! val (cons (list (/ x mag) (/ y mag) (/ z mag)) val))))
- speakers)
- (reverse val)))
-
- ;; find delay times from specified distances or delays
- (times (let ((min-dist (if (pair? distances) ; minimum distance
- (apply min distances)
- 0.0))
- (v (make-float-vector (length speakers))))
- (do ((i 0 (+ i 1)))
- ((= i (length speakers)))
- (set! (v i) (let ((distance (and (pair? distances) (distances i)))
- (dly (and (pair? delays) (delays i))))
- (or dly
- (and (number? distance)
- (/ (- distance min-dist) dlocsig-speed-of-sound))
- 0.0))))
- v))
-
- ;; create the group structures
- (groups (let ((vals ())
- (id 0))
- (for-each
- (lambda (group)
- (let* ((size (length group))
- (vertices (map coords group))
- (matrix (if (= size 3)
- (do ((m (make-float-vector '(3 3)))
- (i 0 (+ i 1)))
- ((= i 3)
- (invert3x3 m))
- (do ((j 0 (+ j 1)))
- ((= j 3))
- (set! (m i j) ((vertices i) j))))
- (and (= size 2)
- (do ((m (make-float-vector '(2 2)))
- (i 0 (+ i 1)))
- ((= i 2)
- (invert2x2 m))
- (do ((j 0 (+ j 1)))
- ((= j 2))
- (set! (m i j) ((vertices i) j))))))))
- (set! vals (cons (make-group :id id
- :size size
- :speakers group
- :vertices vertices
- :matrix matrix)
- vals))
- (set! id (+ 1 id))))
- groups)
- (reverse vals))))
-
- ;; check validity of map entries
- (if channel-map
- (let ((entries (length channel-map)))
- (for-each
- (lambda (entry)
- (if (>= entry entries)
- (error 'mus-error "channel ~A in map ~A is out of range (max=~A)~%" entry channel-map entries)))
- channel-map)
- (if (has-duplicates? channel-map)
- (error 'mus-error "there are duplicate channels in channel-map ~A~%" channel-map))))
-
- ;; create the speaker configuration structure
-
- (make-speaker-config :number (length speakers)
- :dimension (group-size (car groups))
- :coords coords
- :groups groups
- :delays times
- :omap (do ((v (make-vector (length speakers)))
- (chan 0 (+ 1 chan)))
- ((= chan (length speakers)) v)
- (set! (v chan) (or (and (pair? channel-map) (channel-map chan))
- chan))))))
-
- ;;; Default speaker configurations
-
- (define dlocsig-speaker-configs
- ;; by default up to eight channels, 2-d and 3-d configurations
- (list
- (list
- ;;
- ;; 2-D speaker configurations
- ;; no channels: impossible
- #f
- ;; mono
- (arrange-speakers :speakers '(0))
- ;; stereo
- (arrange-speakers :speakers '(-60 60))
- ;; three channels
- (arrange-speakers :speakers '(-45 45 180))
- ;; four channels
- (arrange-speakers :speakers '(-45 45 135 225))
- ;; five channels (5.1 arrangement)
- (arrange-speakers :speakers '(-45 0 45 135 -135))
- ;; six channels
- (arrange-speakers :speakers '(-60 0 60 120 180 240))
- ;; seven channels
- (arrange-speakers :speakers '(-45 0 45 100 140 -140 -100))
- ;; eight speakers
- (arrange-speakers :speakers '(-22.5 22.5 67.5 112.5 157.5 202.5 247.5 292.5)))
- ;;
- ;; 3-D speaker configurations
- ;;
- (list
- ;; no channels: impossible
- #f
- ;; mono
- #f
- ;; stereo
- #f
- ;; three channels
- #f
- ;; four channels 3d
- (arrange-speakers :speakers '((-60 0) (60 0) (180 0)
- (0 90))
- :groups '((0 1 3) (1 2 3) (2 0 3)
- ;; floor
- (0 1 2)))
- ;; five channels 3d
- (arrange-speakers :speakers '((-45 0) (45 0) (135 0) (-135 0)
- (0 90))
- :groups '((0 1 4) (1 2 4) (2 3 4) (3 0 4)
- ;; floor
- (0 1 2) (2 3 0)))
- ;; six channels 3d
- (arrange-speakers :speakers '((-45 0) (45 0) (135 0) (-135 0)
- (-90 60) (90 60))
- :groups '((0 1 4) (1 4 5) (1 2 5) (2 3 5) (3 4 5) (3 0 4)
- ;; floor
- (0 1 2) (2 3 0)))
- ;; seven channels 3d
- (arrange-speakers :speakers '((-45 0) (45 0) (135 0) (-135 0)
- (-60 60) (60 60) (180 60))
- :groups '((0 1 4) (1 4 5) (1 2 5) (2 6 5) (2 3 6) (3 4 6) (3 0 4) (4 5 6)
- ;; floor
- (0 1 2) (2 3 0)))
- ;; eight speakers 3d
- (arrange-speakers :speakers '((-45 -10) (45 -10) (135 -10) (225 -10)
- (-45 45) (45 45) (135 45) (225 45))
- :groups '((0 4 5) (0 5 1) (5 1 2) (2 6 5) (6 7 2) (2 3 7)
- (3 7 4) (3 0 4)
- ;; ceiling
- (4 7 6) (6 5 4)
- ;; floor
- (0 1 2) (2 3 0))))))
-
-
- ;;; Set a particular speaker configuration
-
- (define set-speaker-configuration
- (let ((documentation "(set-speaker-configuration config (configs dlocsig-speaker-configs)) sets a dlocsig speaker configuration"))
- (lambda* (config (configs dlocsig-speaker-configs))
- (let ((lst ((if (< (speaker-config-dimension config) 3) car cadr) configs))
- (num (speaker-config-number config)))
- (set! (lst num) config)))))
-
-
- ;;; Get the speaker configuration for a given number of output channels
-
- (define get-speaker-configuration
- (let ((documentation "(get-speaker-configuration channels (3d dlocsig-3d) (configs dlocsig-speaker-configs)) returns a dlocsig speaker configuration"))
- (lambda* (channels (3d dlocsig-3d) (configs dlocsig-speaker-configs))
- (let ((config (((if 3d cadr car) configs) channels)))
- (if (null? config)
- (error 'mus-error "no speaker configuration exists for ~A ~A output channel~A~%~%"
- (if 3d "tridimensional" "bidimensional")
- channels (if (= channels 1) "s" "")))
- config))))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Dlocsig unit generator
- ;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;;; global dlocsig parameters
-
- (define dlocsig-path ())
- (define dlocsig-scaler 1.0)
- (define dlocsig-direct-power 1.5)
- (define dlocsig-inside-direct-power 1.5)
- (define dlocsig-reverb-power 0.5)
- (define dlocsig-inside-reverb-power 0.5)
- (define dlocsig-initial-delay #f)
- (define dlocsig-unity-gain-distance #f)
- (define dlocsig-reverb-amount 0.04)
- (define dlocsig-inside-radius 1.0)
- (define dlocsig-minimum-segment-length 1.0)
-
- ;; render using:
-
- (define amplitude-panning 1)
- (define ambisonics 2)
- (define decoded-ambisonics 3)
- ;(define stereo-hrtf 4)
-
- ; for backwards compatibility
- (define b-format-ambisonics ambisonics)
-
- ; a reasonable default
-
- (define dlocsig-render-using amplitude-panning)
-
- ;; ambisonics horizontal and vertical order for encoding
- ;; the default is first order b-format WXYZ
-
- (define dlocsig-ambisonics-h-order 1)
- (define dlocsig-ambisonics-v-order 1)
-
- ;; globals for ambisonics
-
- (define point707 (cos (/ (* pi 2) 8.0)))
- (define dlocsig-ambisonics-scaler point707)
- (define dlocsig-ambisonics-ho-rev-scaler 0.05)
- ;; for 3rd order FuMa
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Get number of channels needed by ambisonics
-
- (define* (ambisonics-channels
- (h-order dlocsig-ambisonics-h-order)
- (v-order dlocsig-ambisonics-v-order))
- (if (< h-order 0)
- 0 ;; error: we need at least horizontal order 1!
- (let ((count 0))
- (if (>= h-order 1)
- ;; W X Y
- (set! count (+ count 3)))
- (if (>= v-order 1)
- ;; Z
- (set! count (+ count 1)))
- (if (>= v-order 2)
- ;; R S T
- (set! count (+ count 3)))
- (if (>= h-order 2)
- ;; U V
- (set! count (+ count 2)))
- (if (>= v-order 3)
- ;; K L M N O
- (set! count (+ count 5)))
- (if (>= h-order 3)
- ;; P Q
- (set! count (+ count 2)))
- count)))
-
-
- ;;;;;;;;;
- ;;; Paths
- ;;;;;;;;;
-
- ;;; Generic path class
-
- ;;; path is a list (type rx ry rz rv rt tx ty tz tt ...)
-
- (define path-rx (dilambda (lambda (p) (p 1)) (lambda (p val) (set! (p 1) val))))
- (define path-ry (dilambda (lambda (p) (p 2)) (lambda (p val) (set! (p 2) val))))
- (define path-rz (dilambda (lambda (p) (p 3)) (lambda (p val) (set! (p 3) val))))
- (define path-rv (dilambda (lambda (p) (p 4)) (lambda (p val) (set! (p 4) val))))
- (define path-rt (dilambda (lambda (p) (p 5)) (lambda (p val) (set! (p 5) val))))
- (define path-tx (dilambda (lambda (p) (p 6)) (lambda (p val) (set! (p 6) val))))
- (define path-ty (dilambda (lambda (p) (p 7)) (lambda (p val) (set! (p 7) val))))
- (define path-tz (dilambda (lambda (p) (p 8)) (lambda (p val) (set! (p 8) val))))
- (define path-tt (dilambda (lambda (p) (p 9)) (lambda (p val) (set! (p 9) val))))
-
- ;(define (make-path) (list 'path () () () () () () () () ()))
- #|
- (define (describe path)
- (format #f
- (if (memq (car path) '(bezier-path open-bezier-path))
- (values
- "<bezier-path>:~% rx: ~A~% ry: ~A~% rz: ~A~% rv: ~A~% rt: ~A~% tx: ~A~% ty: ~A~% tz: ~A~% tt: ~A~% ~
- x: ~A~% y: ~A~% z: ~A~% v: ~A~% bx: ~A~% by: ~A~% bz: ~A~% error: ~A~% curvature: ~A~%"
- (path-rx path) (path-ry path) (path-rz path) (path-rv path) (path-rt path) (path-tx path)
- (path-ty path) (path-tz path) (path-tt path) (bezier-x path) (bezier-y path)
- (bezier-z path) (bezier-v path) (bezier-bx path) (bezier-by path) (bezier-bz path)
- (bezier-error path) (bezier-curvature path))
- (values
- "<path>:~% rx: ~A~% ry: ~A~% rz: ~A~% rv: ~A~% rt: ~A~% tx: ~A~% ty: ~A~% tz: ~A~% tt: ~A~%"
- (path-rx path) (path-ry path) (path-rz path) (path-rv path) (path-rt path) (path-tx path)
- (path-ty path) (path-tz path) (path-tt path)))))
- |#
-
- ;;; Inquiries into the state of the path
-
- (define (not-rendered path)
- (null? (path-rx path)))
-
- (define (not-transformed path)
- (null? (path-tx path)))
-
- ;;; Reset any transformations on the originally rendered path
-
- (define (reset-transformation path)
- (set! (path-tt path) ())
- (set! (path-tx path) ())
- (set! (path-ty path) ())
- (set! (path-tz path) ())
- path)
-
- ;;; Reset the rendered path (and any transformations)
-
- (define (reset-rendering path)
- (set! (path-rt path) ())
- (set! (path-rv path) ())
- (set! (path-rx path) ())
- (set! (path-ry path) ())
- (set! (path-rz path) ())
- (reset-transformation path))
-
- ;;; Return the best possible set of coordinates
-
- (define list??
- (let ((documentation "list?? returns a if it is a list"))
- (lambda (a)
- (and (pair? a) a))))
-
- (define (path-x path)
- (or (list?? (path-tx path))
- (list?? (path-rx path))
- (path-rx (render-path path))))
-
- (define (path-y path)
- (or (list?? (path-ty path))
- (list?? (path-ry path))
- (path-ry (render-path path))))
-
- (define (path-z path)
- (or (list?? (path-tz path))
- (list?? (path-rz path))
- (path-rz (render-path path))))
-
- (define (path-time path)
- (or (list?? (path-tt path))
- (list?? (path-rt path))
- (path-rt (render-path path))))
-
-
- ;;;;;;;;;;;;;;;;
- ;;; Bezier paths
- ;;;;;;;;;;;;;;;;
-
- ;;; Parse a path as two or three-dimensional paths
-
- (define path-3d #f)
-
- ;;; Path class for bezier rendered paths
-
- ;;; bezier-path is path + path 3d polar x y z v bx by bz error curvature
-
-
- (define bezier-path (dilambda (lambda (p) (p 10)) (lambda (p val) (set! (p 10) val))))
- (define bezier-3d (dilambda (lambda (p) (p 11)) (lambda (p val) (set! (p 11) val))))
- (define bezier-polar (dilambda (lambda (p) (p 12)) (lambda (p val) (set! (p 12) val))))
- (define bezier-x (dilambda (lambda (p) (p 13)) (lambda (p val) (set! (p 13) val))))
- (define bezier-y (dilambda (lambda (p) (p 14)) (lambda (p val) (set! (p 14) val))))
- (define bezier-z (dilambda (lambda (p) (p 15)) (lambda (p val) (set! (p 15) val))))
- (define bezier-v (dilambda (lambda (p) (p 16)) (lambda (p val) (set! (p 16) val))))
- (define bezier-bx (dilambda (lambda (p) (p 17)) (lambda (p val) (set! (p 17) val))))
- (define bezier-by (dilambda (lambda (p) (p 18)) (lambda (p val) (set! (p 18) val))))
- (define bezier-bz (dilambda (lambda (p) (p 19)) (lambda (p val) (set! (p 19) val))))
- (define bezier-error (dilambda (lambda (p) (p 20)) (lambda (p val) (set! (p 20) val))))
- (define bezier-curvature (dilambda (lambda (p) (p 21)) (lambda (p val) (set! (p 21) val))))
-
- (define* (make-bezier-path (path ()) (3d #t) polar (error 0.01) curvature)
- (list 'bezier-path () () () () () () () () () path 3d polar () () () () () () () error curvature))
-
-
- ;;; Path class for open bezier paths
-
- (define initial-direction (dilambda (lambda (p) (p 22)) (lambda (p val) (set! (p 22) val))))
- (define final-direction (dilambda (lambda (p) (p 23)) (lambda (p val) (set! (p 23) val))))
-
- (define* (make-open-bezier-path (path ()) (3d #t) polar (error 0.01) curvature
- (initial-direction '(0.0 0.0 0.0)) (final-direction '(0.0 0.0 0.0)))
- (list 'open-bezier-path () () () () () () () () () path 3d polar () () () () () () () error curvature initial-direction final-direction))
-
-
-
- ;;;
- ;;; Generic defining function (for open, closed, polar and cartesian paths)
- ;;;
-
- (define make-path
- (let ()
-
- (define (make-path-error-checks path closed initial-direction final-direction)
- ;; some sanity checks
- (if (null? path)
- (error 'mus-error "Can't define a path with no points in it~%"))
- (when closed
- (if initial-direction
- (error 'mus-error "Can't specify initial direction ~A for a closed path ~A~%" initial-direction path))
- (if final-direction
- (error 'mus-error "Can't specify final direction ~A for a closed path ~A~%" final-direction path))
- (let ((closed? (if (pair? (car path))
- (let ((start (car path))
- (end (car (last path))))
- (and (= (car start) (car end))
- (= (cadr start) (cadr end))
- (or (not path-3d)
- (= (third start) (third end)))))
- (let ((end (last path (if path-3d 3 2))))
- (and (= (car path) (car end))
- (= (cadr path) (cadr end))
- (or (not path-3d)
- (= (third path) (third end))))))))
- (if (not closed?)
- (error 'mus-error "Closed path ~A is not closed~%" path)))))
-
- (lambda* (path
- (3d path-3d)
- polar
- closed
- curvature
- (error 0.01) ; this name ("error") is a bad idea -- it means we can't call the real error function (this is Scheme, not CL)
- ;; only for open paths
- initial-direction
- final-direction)
-
- (make-path-error-checks path closed initial-direction final-direction) ; the error check uses path-3d -- was 3d intended?
-
- ;; create the path structure
- (if closed
- (make-bezier-path
- :path path
- :3d 3d
- :polar polar
- :curvature curvature
- :error error)
- (make-open-bezier-path
- :path path
- :3d 3d
- :polar polar
- :curvature curvature
- :error error
- :initial-direction initial-direction
- :final-direction final-direction)))))
-
-
- ;;; Some convenient abbreviations
-
- (define* (make-polar-path path
- (3d path-3d)
- closed
- curvature
- (error 0.01)
- ;; only for open paths
- initial-direction
- final-direction)
- (if closed
- (make-path :path path
- :3d 3d
- :polar #t
- :closed closed
- :curvature curvature
- :error error)
- (make-path :path path
- :3d 3d
- :polar #t
- :closed closed
- :curvature curvature
- :error error
- :initial-direction initial-direction
- :final-direction final-direction)))
-
- (define* (make-closed-path path
- (3d path-3d)
- polar
- curvature
- (error 0.01))
- (make-path :path path
- :3d 3d
- :polar polar
- :closed #t
- :curvature curvature
- :error error))
-
-
-
- ;;;
- ;;; Parse a path and transform it into cartesian coordinates
- ;;;
-
- (define (not-parsed path)
- (null? (bezier-x path)))
-
-
- ;;; Parse a set of 2d or 3d points into the separate coordinates
-
- (define parse-cartesian-coordinates
- (let ((documentation "(parse-cartesian-coordinates points 3d) parses a set of 2d or 3d points into the separate coordinates"))
- (lambda (points 3d)
- (if (pair? (car points))
- ;; decode a list of lists into x:y:z:v components
- ;; 3d -> t [default]
- ;; '((x0 y0 z0 v0) (x1 y1 z1 v1)...(xn yn zn vn))
- ;; '((x0 y0 z0) (x1 y1 z1)...(xn yn zn))
- ;; '((x0 y0) (x1 y1)...(xn yn))
- ;; v: relative velocity
- ;; x, y, z: coordinates of source [missing z's assumed 0.0]
- ;; 3d -> nil
- ;; '((x0 y0 v0) (x1 y1 v1)...(xn yn vn))
- ;; '((x0 y0) (x1 y1)...(xn yn))
- ;; v: relative velocity
- ;; x, y, z: coordinates of source [missing z's assumed 0.0]
- (let ((v ())
- (x ())
- (y ())
- (z ()))
- (for-each
- (lambda (p)
- (set! x (cons (car p) x))
- (set! y (cons (cadr p) y))
- (set! z (cons (if 3d (or (third p) 0.0) 0.0) z))
- (set! v (cons ((if 3d fourth third) p) v)))
- points)
- (list (reverse x) (reverse y) (reverse z) (reverse v)))
-
- ;; decode a plain list
- (let ((px ())
- (py ())
- (len (length points)))
- (if 3d
- ;; it's a three dimensional list
- ;; '(x0 y0 z0 x1 y1 z1 ... xn yn zn)
- ;; x, y, z: coordinates of source
- (do ((pz ())
- (i 0 (+ i 3)))
- ((>= i len)
- (list (reverse px) (reverse py) (reverse pz) (make-list (length px) #f)))
- (set! px (cons (points i) px))
- (set! py (cons (points (+ i 1)) py))
- (set! pz (cons (points (+ i 2)) pz)))
- ;; it's a two dimensional list
- ;; '(x0 y0 x1 y1 ... xn yn)
- ;; x, y, z: coordinates of source [missing z's assumed 0.0]
- (do ((i 0 (+ i 2)))
- ((>= i len)
- (list (reverse px) (reverse py) (make-list (length px) 0.0) (make-list (length px) #f)))
- (set! px (cons (points i) px))
- (set! py (cons (points (+ i 1)) py)))))))))
-
- ;;; Parse a set of 2d or 3d polar points into the separate coordinates
-
- (define parse-polar-coordinates
- (let ((documentation "(parse-polar-coordinates points 3d) parses a polar path"))
- (lambda (points 3d)
- (let ((x ())
- (y ()))
- (if (pair? (car points))
- ;; decode a list of lists of d:a:e:v into x:y:z:v components
- ;; 3d --> t [default]
- ;; '((d0 a0 e0 v0) (d1 a1 e1 v1)...(dn an en vn))
- ;; '((d0 a0 e0) (d1 a1 e1)...(dn an en))
- ;; '((d0 a0) (d1 a1)...(dn an))
- ;; 3d --> nil
- ;; '((d0 a0 v0) (d1 a1 v1)...(dn an vn))
- ;; '((d0 a0) (d1 a1)...(dn an))
- ;; v: velocity
- ;; d: distance
- ;; a: azimut angle
- ;; e: elevarion angle [missing elevations assumed 0.0]
- (let ((z ())
- (v ()))
- (for-each
- (lambda (p)
- (let ((d (car p))
- (evec (let ((e (if (and 3d (pair? (cddr p))) (caddr p) 0.0)))
- (cis (/ (* e 2 pi) dlocsig-one-turn)))))
- (let ((dxy (* d (real-part evec)))
- (avec (let ((a (cadr p)))
- (cis (/ (* a 2 pi) dlocsig-one-turn)))))
- (set! x (cons (* dxy (imag-part avec)) x))
- (set! y (cons (* dxy (real-part avec)) y)))
- (set! z (cons (* d (imag-part evec)) z))
- (set! v (cons ((if 3d fourth third) p) v))))
- points)
- (list (reverse x) (reverse y) (reverse z) (reverse v)))
-
- ;; decode a list of d:a:e components
- (let ((len (length points)))
- (if 3d
- ;; decode a three dimensional list
- ;; '(d0 a0 e0 d1 a1 e1 ... dn an en)
- ;; d: distance
- ;; a: azimut angle
- ;; e: elevarion angle [missing elevations assumed 0.0]
- (do ((z ())
- (i 0 (+ i 3)))
- ((>= i len)
- (list (reverse x) (reverse y) (reverse z) (make-list (length x) #f)))
- (let* ((d (points i))
- (evec (let ((e (points (+ i 2))))
- (cis (/ (* e 2 pi) dlocsig-one-turn))))
- (dxy (* d (real-part evec)))
- (avec (let ((a (points (+ i 1))))
- (cis (/ (* a 2 pi) dlocsig-one-turn)))))
- (set! x (cons (* dxy (imag-part avec)) x))
- (set! y (cons (* dxy (real-part avec)) y))
- (set! z (cons (* d (imag-part evec)) z))))
-
- ;; decode a two dimensional list
- ;; '(d0 a0 d1 a1 ... dn an)
- ;; d: distance
- ;; a: azimut angle
- ;; e: elevarion angle [missing elevations assumed 0.0]
- (do ((i 0 (+ i 2)))
- ((>= i len)
- (list (reverse x) (reverse y) (make-list (length x) 0.0) (make-list (length x) #f)))
- (let ((d (points i))
- (avec (let ((a (points (+ i 1))))
- (cis (/ (* a 2 pi) dlocsig-one-turn)))))
- (set! x (cons (* d (imag-part avec)) x))
- (set! y (cons (* d (real-part avec)) y)))))))))))
-
-
- (define (xparse-path xpath)
- (let ((polar (bezier-polar xpath))
- (points (bezier-path xpath))
- (3d (bezier-3d xpath)))
- (let ((vals ((if polar parse-polar-coordinates parse-cartesian-coordinates) points 3d)))
- (set! (bezier-x xpath) (car vals))
- (set! (bezier-y xpath) (cadr vals))
- (set! (bezier-z xpath) (caddr vals))
- (set! (bezier-v xpath) (cadddr vals))))
- (for-each
- (lambda (v)
- (if (and (real? v)
- (< v 0))
- (error 'mus-error "velocities for path ~A must be all positive~%" (bezier-path xpath))))
- (bezier-v xpath))
- (reset-fit xpath))
-
-
- ;;;
- ;;; Bezier curve fitting auxiliary functions
- ;;;
-
- ;;; Pythagoras
-
- (define distance
- (let ((documentation "(distance x y z) returns the euclidean distance of (x y z) from the origin"))
- (lambda (x y z)
- (sqrt (+ (* x x) (* y y) (* z z))))))
-
- ;;; Nearest point in a line
-
- (define (nearest-point x0 y0 z0 x1 y1 z1 px py pz)
-
- (define (vcos a0 b0 c0 a1 b1 c1)
- (/ (+ (* a0 a1) (* b0 b1) (* c0 c1))
- (* (distance a0 b0 c0) (distance a1 b1 c1))))
-
- (define (same a0 b0 c0 a1 b1 c1)
- (and (= a0 a1) (= b0 b1) (= c0 c1)))
-
- (cond ((same x0 y0 z0 px py pz) (list x0 y0 z0))
- ((same x1 y1 z1 px py pz) (list x1 y1 z1))
- ((same x0 y0 z0 x1 y1 z1) (list x0 y0 z0))
- (else (let* ((xm0 (- x1 x0))
- (ym0 (- y1 y0))
- (zm0 (- z1 z0))
- (ratio (let ((p (let ((xm1 (- px x0))
- (ym1 (- py y0))
- (zm1 (- pz z0)))
- (* (distance xm1 ym1 zm1)
- (vcos xm0 ym0 zm0 xm1 ym1 zm1)))))
- (/ p (distance xm0 ym0 zm0)))))
- (list (+ x0 (* xm0 ratio))
- (+ y0 (* ym0 ratio))
- (+ z0 (* zm0 ratio)))))))
-
- ;;; Bezier curve fitting auxilliary functions
-
- (define path-ak-even #f)
- (define path-ak-odd #f)
- (define path-maxcoeff 8)
-
- (define (make-a-even)
- (let ((g (let ((path-gtab #f))
- (lambda (m)
- (if (not path-gtab)
- (begin
- (set! path-gtab (make-vector path-maxcoeff))
- (set! (path-gtab 0) 1)
- (set! (path-gtab 1) -4)
- (do ((i 2 (+ i 1)))
- ((= i path-maxcoeff))
- (set! (path-gtab i) (- (* -4 (path-gtab (- i 1)))
- (path-gtab (- i 2)))))))
- (path-gtab m)))))
-
- (set! path-ak-even (make-vector (- path-maxcoeff 1)))
- (do ((m 1 (+ 1 m)))
- ((= m path-maxcoeff))
- (set! (path-ak-even (- m 1)) (make-vector m))
- (do ((k 1 (+ k 1)))
- ((> k m))
- (set! ((path-ak-even (- m 1)) (- k 1)) (* 1.0 (/ (- (g (- m k))) (g m))))))))
-
- (define (make-a-odd)
- (let ((f (let ((path-ftab #f))
- (lambda (m)
- (if (not path-ftab)
- (begin
- (set! path-ftab (make-vector path-maxcoeff))
- (set! (path-ftab 0) 1)
- (set! (path-ftab 1) -3)
- (do ((i 2 (+ i 1)))
- ((= i path-maxcoeff))
- (set! (path-ftab i) (- (* -4 (path-ftab (- i 1)))
- (path-ftab (- i 2)))))))
- (path-ftab m)))))
-
- (set! path-ak-odd (make-vector (- path-maxcoeff 1)))
- (do ((m 1 (+ 1 m)))
- ((= m path-maxcoeff))
- (set! (path-ak-odd (- m 1)) (make-vector m))
- (do ((k 1 (+ k 1)))
- ((> k m))
- (set! ((path-ak-odd (- m 1)) (- k 1)) (* 1.0 (/ (- (f (- m k))) (f m))))))))
-
- ;;; Calculate bezier difference vectors for the given path
- ;;; (path-x (make-path '((-10 10)(0 5)(10 10))))
-
- (define (fit path)
- (let ((n (- (length (bezier-x path )) 1)))
- (cond ((not (eq? (car path) 'open-bezier-path))
- (let ((m (/ (- n (if (odd? n) 3 4)) 2))
- ;; data points P(i)
- (p (vector (apply vector (bezier-x path))
- (apply vector (bezier-y path))
- (apply vector (bezier-z path))))
- ;; control points D(i)
- (d (let ((maker (lambda () (make-vector n 0.0))))
- (vector (maker) (maker) (maker)))))
-
- (define (a-1 k n)
- (if (odd? (min (+ (* path-maxcoeff 2) 1) n))
- (begin
- (if (not path-ak-odd) (make-a-odd))
- ((path-ak-odd (/ (- n 3) 2)) (- k 1)))
- (begin
- (if (not path-ak-even) (make-a-even))
- ((path-ak-even (/ (- n 4) 2)) (- k 1)))))
-
- (define (xvector-ref z j i)
- (z j (if (> i (- n 1))
- (- i n)
- (if (< i 0)
- (+ i n) i))))
-
- (do ((i 0 (+ i 1)))
- ((= i n))
- (do ((k 1 (+ k 1)))
- ((> k m))
- (do ((a 0 (+ 1 a)))
- ((> a 2))
- (set! (d a i)
- (+ (d a i)
- (* (a-1 k n)
- (- (xvector-ref p a (+ i k))
- (xvector-ref p a (- i k)))))))))
- (if (bezier-curvature path)
- (do ((i 0 (+ i 1)))
- ((= i n))
- (set! (d 0 i) (* (d 0 i) (bezier-curvature path)))
- (set! (d 1 i) (* (d 1 i) (bezier-curvature path)))
- (set! (d 2 i) (* (d 2 i) (bezier-curvature path)))))
- (list (- n 1) p d)))
- (else
- (let ((m (- n 1))
- ;; data points P(i)
- (p (vector (apply vector (bezier-x path))
- (apply vector (bezier-y path))
- (apply vector (bezier-z path))))
- ;; control points D(i)
- (d (vector (make-vector (+ n 1) 0.0)
- (make-vector (+ n 1) 0.0)
- (make-vector (+ n 1) 0.0))))
-
- (define (ac k n)
- (let ((un (min n path-maxcoeff)))
- (if (not path-ak-even) (make-a-even))
- ((path-ak-even (- un 2)) (- k 1))))
-
- (define (ref z j i)
- (cond ((> i n) (z j (- i n)))
- ((< i 0) (z j (+ i n)))
- ((= i n) (- (z j n) (d j n)))
- ((= i 0) (+ (z j 0) (d j 0)))
- (else (z j i))))
-
- ;; forced initial direction
- (if (initial-direction path)
- (begin
- (set! (d 0 0) (car (initial-direction path)))
- (set! (d 1 0) (cadr (initial-direction path)))
- (set! (d 2 0) (third (initial-direction path))))
- (begin
- (set! (d 0 0) 0.0)
- (set! (d 1 0) 0.0)
- (set! (d 2 0) 0.0)))
-
- ;; forced final direction
- (if (final-direction path)
- (begin
- (set! (d 0 n) (car (final-direction path)))
- (set! (d 1 n) (cadr (final-direction path)))
- (set! (d 2 n) (caddr (final-direction path))))
- (begin
- (set! (d 0 n) 0.0)
- (set! (d 1 n) 0.0)
- (set! (d 2 n) 0.0)))
-
- ;; calculate fit
- (do ((i 1 (+ i 1)))
- ((= i n))
- (do ((k 1 (+ k 1)))
- ((> k (min m (- path-maxcoeff 1))))
- (let ((d0 (d 0 i))
- (d1 (d 1 i))
- (d2 (d 2 i)))
- (set! (d 0 i) (+ d0 (* (ac k n)
- (- (ref p 0 (+ i k))
- (ref p 0 (- i k))))))
- (set! (d 1 i) (+ d1 (* (ac k n)
- (- (ref p 1 (+ i k))
- (ref p 1 (- i k))))))
- (set! (d 2 i) (+ d2 (* (ac k n)
- (- (ref p 2 (+ i k))
- (ref p 2 (- i k)))))))))
- (list n p d))))))
-
- ;;; Calculate bezier control points for the given open path
-
- (define (not-fitted path)
- (null? (bezier-bx path)))
-
- (define (reset-fit path)
- (set! (bezier-bx path) ())
- (set! (bezier-by path) ())
- (set! (bezier-bz path) ())
- (reset-rendering path))
-
- (define (fit-path path)
- (cond ((eq? (car path) 'open-bezier-path)
- (if (not-parsed path)
- (xparse-path path))
-
- (let ((points (length (bezier-x path))))
- (cond ((> points 2)
- (let ((vals (fit path)))
- (let ((n (car vals))
- (p (cadr vals))
- (d (caddr vals)))
- (let ((c (bezier-curvature path))
- (cs (make-vector n)))
- ;; setup the curvatures array
- (cond ((memq c '(#f ())) ; no curvature specified, default is 1.0
- (do ((i 0 (+ i 1)))
- ((= i n))
- (set! (cs i) (list 1.0 1.0))))
- ((number? c) ; same curvature for all segments
- (do ((i 0 (+ i 1)))
- ((= i n))
- (set! (cs i) (list c c))))
- ((and (pair? c) (= n (length c))) ; list of curvatures
- (let ((i 0))
- (for-each
- (lambda (ci)
- (set! (cs i) (if (pair? ci)
- (if (= (length ci) 2)
- ci
- (error 'mus-error "curvature sublist must have two elements ~A~%" ci))
- (list ci ci)))
- (set! i (+ i 1)))
- c)))
- (else (error 'mus-error "bad curvature argument ~A to path, need ~A elements~%" c n)))
-
- ;; calculate control points
- (do ((xc ())
- (yc ())
- (zc ())
- (i 0 (+ i 1)))
- ((= i n)
- (set! (bezier-bx path) (reverse xc))
- (set! (bezier-by path) (reverse yc))
- (set! (bezier-bz path) (reverse zc)))
-
- (set! xc (cons (list (p 0 i)
- (+ (p 0 i) (* (d 0 i) (car (cs i))))
- (- (p 0 (+ i 1)) (* (d 0 (+ i 1)) (cadr (cs i))))
- (p 0 (+ i 1))) xc))
- (set! yc (cons (list (p 1 i)
- (+ (p 1 i) (* (d 1 i) (car (cs i))))
- (- (p 1 (+ i 1)) (* (d 1 (+ i 1)) (cadr (cs i))))
- (p 1 (+ i 1))) yc))
- (set! zc (cons (list (p 2 i)
- (+ (p 2 i) (* (d 2 i) (car (cs i))))
- (- (p 2 (+ i 1)) (* (d 2 (+ i 1)) (cadr (cs i))))
- (p 2 (+ i 1))) zc)))))))
-
- ((= points 2)
- ;; just a line, stays a line
- (let ((x1 (car (bezier-x path)))
- (x2 (cadr (bezier-x path)))
- (y1 (car (bezier-y path)))
- (y2 (cadr (bezier-y path)))
- (z1 (car (bezier-z path)))
- (z2 (cadr (bezier-z path))))
- (set! (bezier-bx path) (list (list x1 x1 x2 x2)))
- (set! (bezier-by path) (list (list y1 y1 y2 y2)))
- (set! (bezier-bz path) (list (list z1 z1 z2 z2)))))
- ((= points 1)
- ;; just one point, bezier won't do much here
- (set! (bezier-bx path) ())
- (set! (bezier-by path) ())
- (set! (bezier-bz path) ())))
-
- (reset-rendering path)))
-
- (else
- (if (not-parsed path)
- (xparse-path path))
-
- (if (> (length (bezier-x path)) 4)
- (let ((vals (fit path)))
- (do ((n (car vals))
- (p (cadr vals))
- (d (caddr vals))
- ;; enough points, fit path
- (xc ())
- (yc ())
- (zc ())
- (i 0 (+ i 1)))
- ((= i n)
- (set! (bezier-bx path) (append (reverse xc) (list (list (p 0 n)
- (+ (p 0 n) (d 0 n))
- (- (p 0 0) (d 0 0))
- (p 0 0)))))
- (set! (bezier-by path) (append (reverse yc) (list (list (p 1 n)
- (+ (p 1 n) (d 1 n))
- (- (p 1 0) (d 1 0))
- (p 1 0)))))
- (set! (bezier-bz path) (append (reverse zc) (list (list (p 2 n)
- (+ (p 2 n) (d 2 n))
- (- (p 2 0) (d 2 0))
- (p 2 0))))))
- (set! xc (cons (list (p 0 i)
- (+ (p 0 i) (d 0 i))
- (- (p 0 (+ i 1)) (d 0 (+ i 1)))
- (p 0 (+ i 1))) xc))
- (set! yc (cons (list (p 1 i)
- (+ (p 1 i) (d 1 i))
- (- (p 1 (+ i 1)) (d 1 (+ i 1)))
- (p 1 (+ i 1))) yc))
- (set! zc (cons (list (p 2 i)
- (+ (p 2 i) (d 2 i))
- (- (p 2 (+ i 1)) (d 2 (+ i 1)))
- (p 2 (+ i 1))) zc))))
-
- ;; not enough points to fit a closed path
- (do ((xc ())
- (yc ())
- (zc ())
- (len (min (length (bezier-x path)) (length (bezier-y path)) (length (bezier-z path))))
- (i 0 (+ i 1)))
- ((>= i len)
- (format *stderr* "[fit-path:closed-path] not enough points to do bezier fit (~A points)" len)
- (set! (bezier-bx path) (reverse xc))
- (set! (bezier-by path) (reverse yc))
- (set! (bezier-bz path) (reverse zc)))
- (let ((x1 ((bezier-x path) i))
- (x2 ((bezier-x path) (+ i 1)))
- (y1 ((bezier-y path) i))
- (y2 ((bezier-y path) (+ i 1)))
- (z1 ((bezier-z path) i))
- (z2 ((bezier-z path) (+ i 1))))
- (set! xc (cons (list x1 x1 x2 x2) xc))
- (set! yc (cons (list y1 y1 y2 y2) yc))
- (set! zc (cons (list z1 z1 z2 z2) zc)))))
- (reset-rendering path))))
-
-
-
- ;;;;;;;;;;;;;;;;;
- ;;; Literal paths
- ;;;;;;;;;;;;;;;;;
-
-
- (define literal-points (dilambda (lambda (p) (p 10)) (lambda (p val) (set! (p 10) val))))
- (define literal-3d (dilambda (lambda (p) (p 11)) (lambda (p val) (set! (p 11) val))))
- (define literal-polar (dilambda (lambda (p) (p 12)) (lambda (p val) (set! (p 12) val))))
-
- ;;; Generic literal path creation function
- (define* (make-literal-path (points ()) (3d path-3d) polar)
- (list 'literal-path () () () () () () () () () points 3d polar))
-
- ;;; Specific polar literal path creation function
- (define* (make-literal-polar-path (points ()) (3d path-3d))
- (make-literal-path points 3d #t))
-
-
- ;;;;;;;;;;;
- ;;; Spirals
- ;;;;;;;;;;;
-
- (define spiral-start-angle (dilambda (lambda (p) (p 13)) (lambda (p val) (set! (p 13) val))))
- (define spiral-total-angle (dilambda (lambda (p) (p 14)) (lambda (p val) (set! (p 14) val))))
- (define spiral-step-angle (dilambda (lambda (p) (p 15)) (lambda (p val) (set! (p 15) val))))
- (define spiral-turns (dilambda (lambda (p) (p 16)) (lambda (p val) (set! (p 16) val))))
- (define spiral-distance (dilambda (lambda (p) (p 17)) (lambda (p val) (set! (p 17) val))))
- (define spiral-height (dilambda (lambda (p) (p 18)) (lambda (p val) (set! (p 18) val))))
- (define spiral-velocity (dilambda (lambda (p) (p 19)) (lambda (p val) (set! (p 19) val))))
-
- (define* (make-spiral-path (start-angle 0.0)
- total-angle
- step-angle
- (turns ())
- (distance '(0 10 1 10))
- (height '(0 0 1 0))
- (velocity '(0 1 1 1)))
- (if (and total-angle (pair? turns))
- (error 'mus-error "can't specify total-angle [~A] and turns [~A] at the same time for the spiral path~%" total-angle turns))
-
- (list 'spiral-path () () () () () () () () () () path-3d #f
- start-angle total-angle
- (or step-angle (/ dlocsig-one-turn 100))
- turns distance height velocity))
-
-
-
- ;;; Transform a Bezier control point fit to a linear segment approximation
-
- (define (bezier-render path)
- (if (not-fitted path)
- (fit-path path))
- (let ((xrx ()) (xry ()) (xrz ()) (xrv ()))
-
- (define (berny xl yl zl xh yh zh ul u uh c err)
- ;; Create a linear segment rendering of a bezier segment
-
- (define (bezier-point u c)
- ;; Evaluate a point at parameter u in bezier segment
- (let ((u1 (- 1 u))
- (cr (vector (make-vector 3 0.0) (make-vector 3 0.0) (make-vector 3 0.0))))
- (do ((j 0 (+ j 1)))
- ((= j 3))
- (set! (cr 0 j) (+ (* u1 (c 0 j)) (* u (c 0 (+ j 1)))))
- (set! (cr 1 j) (+ (* u1 (c 1 j)) (* u (c 1 (+ j 1)))))
- (set! (cr 2 j) (+ (* u1 (c 2 j)) (* u (c 2 (+ j 1))))))
- (do ((i 1 (- i 1)))
- ((< i 0))
- (do ((j 0 (+ j 1)))
- ((> j i))
- (set! (cr 0 j) (+ (* u1 (cr 0 j)) (* u (cr 0 (+ j 1)))))
- (set! (cr 1 j) (+ (* u1 (cr 1 j)) (* u (cr 1 (+ j 1)))))
- (set! (cr 2 j) (+ (* u1 (cr 2 j)) (* u (cr 2 (+ j 1)))))))
- (list (cr 0 0)
- (cr 1 0)
- (cr 2 0))))
-
- (let ((vals (bezier-point u c)))
- (let ((x (car vals))
- (y (cadr vals))
- (z (caddr vals)))
- (let ((val1 (nearest-point xl yl zl xh yh zh x y z)))
- (let ((xn (car val1))
- (yn (cadr val1))
- (zn (caddr val1)))
- (if (<= (distance (- xn x) (- yn y) (- zn z)) err)
- (list () () ())
- (let ((val2 (berny xl yl zl x y z ul (/ (+ ul u) 2) u c err))
- (val3 (berny x y z xh yh zh u (/ (+ u uh) 2) uh c err)))
- (let ((xi (car val2))
- (yi (cadr val2))
- (zi (caddr val2))
- (xj (car val3))
- (yj (cadr val3))
- (zj (caddr val3)))
- (list (append xi (cons x xj))
- (append yi (cons y yj))
- (append zi (cons z zj)))))))))))
-
- ;; Create linear segment approximations of the bezier segments
- ;; make sure there are initial and final velocity values
- (if (not (pair? (bezier-v path)))
- (set! (bezier-v path) (list 1 1))
- (if (not (car (bezier-v path)))
- (begin
- (set! ((bezier-v path) 0) 1)
- (set! ((bezier-v path) (- (length (bezier-v path)) 1)) 1))))
-
- ;; only one point means no movement, static source
- (if (= (length (bezier-x path)) 1)
- (begin
- (set! (path-rx path) (bezier-x path))
- (set! (path-ry path) (bezier-y path))
- (set! (path-rz path) (bezier-z path))
- (set! (path-rt path) (list 0.0))
- (reset-transformation path)) ; after?
- (begin
- (let ((len (length (bezier-bx path))))
- ;(path-x (make-path '((-10 10)(0 5)(10 10))))
- ;; render the path only if it has at least two points
- (do ((i 0 (+ i 1)))
- ((= i len))
- (let ((x-bz ((bezier-bx path) i))
- (y-bz ((bezier-by path) i))
- (z-bz ((bezier-bz path) i)))
- (let ((vi-bz ((bezier-v path) i))
- (vf-bz ((bezier-v path) (+ i 1)))
- (xi-bz (car x-bz))
- (xf-bz (x-bz (- (length x-bz) 1)))
- (yi-bz (car y-bz))
- (yf-bz (y-bz (- (length y-bz) 1)))
- (zi-bz (car z-bz))
- (zf-bz (z-bz (- (length z-bz) 1))))
- (let ((vals (berny xi-bz yi-bz zi-bz xf-bz yf-bz zf-bz 0.0 0.5 1.0
- (vector (apply vector x-bz)
- (apply vector y-bz)
- (apply vector z-bz))
- (bezier-error path))))
- ;; approximate the bezier curve with linear segments
- (set! xry (append xry (cons yi-bz (cadr vals))))
- (set! xrz (append xrz (cons zi-bz (caddr vals))))
- (let ((xs (car vals)))
- (set! xrx (append xrx (cons xi-bz xs)))
- ;; accumulate intermediate unknown velocities as nils
- (set! xrv (append xrv (cons vi-bz (make-list (length xs) #f))))
- (if (= i (- len 1))
- (begin
- ;; add the last point
- (set! xrx (append xrx (list xf-bz)))
- (set! xry (append xry (list yf-bz)))
- (set! xrz (append xrz (list zf-bz)))
- (set! xrv (append xrv (list vf-bz)))))))))))
-
- ;; calculate times for each velocity segment
- (let ((ti 0)
- (times (list 0)))
- (let ((len (- (length xrx) 1))
- (xseg (list (xrx 0)))
- (yseg (list (xry 0)))
- (zseg (list (xrz 0)))
- (vseg (list (xrv 0)))
- (vi (xrv 0)))
- (do ((i 0 (+ i 1)))
- ((= i len))
- (let ((x (xrx (+ i 1)))
- (y (xry (+ i 1)))
- (z (xrz (+ i 1)))
- (v (xrv (+ i 1))))
- (set! xseg (append xseg (list x)))
- (set! yseg (append yseg (list y)))
- (set! zseg (append zseg (list z)))
- (set! vseg (append vseg (list v)))
-
- (when v
- (let ((dseg (list))
- (sum 0.0)
- (len (- (length xseg) 1)))
- (do ((i 0 (+ i 1)))
- ((= i len))
- (let ((xsi (xseg i))
- (ysi (yseg i))
- (zsi (zseg i))
- (xsf (xseg (+ i 1)))
- (ysf (yseg (+ i 1)))
- (zsf (zseg (+ i 1))))
-
- (set! sum (+ sum (distance (- xsf xsi) (- ysf ysi) (- zsf zsi))))
- (set! dseg (cons sum dseg))))
-
- (let ((df (car dseg)))
- (set! dseg (reverse dseg))
- (let ((tseg ()))
- (let ((a (/ (* (- v vi) (+ v vi)) df 4)))
- (if (= vi 0.0) (set! vi 1))
- (for-each
- (lambda (d)
- (let ((seg (+ ti (if (= v vi)
- (/ d vi)
- (/ (- (sqrt (+ (* vi vi) (* 4 a d))) vi) (* 2 a))))))
- (set! tseg (cons seg tseg))))
- dseg))
- (set! ti (car tseg))
- (set! tseg (reverse tseg))
-
- (set! times (append times tseg))
- (set! xseg (list x))
- (set! yseg (list y))
- (set! zseg (list z))
- (set! vseg (list v))
- (set! vi v))))))))
-
- (set! (path-rx path) xrx)
- (set! (path-ry path) xry)
- (set! (path-rz path) xrz)
- (set! (path-rt path)
- (let ((tf (times (- (length times) 1)))
- (val ()))
- (for-each
- (lambda (ti)
- (set! val (cons (/ ti tf) val)))
- times)
- (reverse val)))
- (reset-transformation path))))))
-
- ;; (set! p (make-path '((-10 10 0 0) (0 5 0 1) (10 10 0 0)) :error 0.01))
- ;; (set! p (make-path '((-10 10 0 1) (-7 7 0 0.9) (0 5 0 0) (7 7 0 0.2) (10 10 0 1)) :error 0.001))
- ;; (with-sound(:channels 4 :play #f) (sinewave 0 2 880 0.5 :path p))
-
-
- (define (literal-render path)
-
- ;; Render a user-defined literal path from the data points
-
- ;; decode the points into coordinates
- (let ((points (literal-points path))
- (3d (literal-3d path))
- (polar (literal-polar path)))
- (let ((vals ((if polar parse-polar-coordinates parse-cartesian-coordinates) points 3d)))
- (set! (path-rx path) (car vals))
- (set! (path-ry path) (cadr vals))
- (set! (path-rz path) (caddr vals))
- (set! (path-rv path) (cadddr vals)))
-
- ;; make sure there are initial and final velocity values
- (if (not (car (path-rv path)))
- (begin
- (set! ((path-rv path) 0) 1)
- (set! ((path-rv path) (- (length (path-rv path)) 1)) 1)))
-
- ;; only one point means no movement, static source
- (if (= (length (path-rx path)) 1)
- (begin
- (set! (path-rt path) (list 0.0))
- (reset-transformation path))
- (let ((rx (path-rx path))
- (ry (path-ry path))
- (rz (path-rz path))
- (rv (path-rv path)))
- (let ((xseg (list (car rx)))
- (yseg (list (car ry)))
- (zseg (list (car rz)))
- (vseg (list (car rv)))
- (vi (car rv))
- (len (length rx))
- (ti 0))
- (let ((times (list ti)))
- (do ((i 1 (+ i 1)))
- ((= i len))
- (let ((x (rx i))
- (y (ry i))
- (z (rz i))
- (v (rv i)))
- (set! xseg (append xseg (list x)))
- (set! yseg (append yseg (list y)))
- (set! zseg (append zseg (list z)))
- (set! vseg (append vseg (list v)))
-
- (when (number? v)
- (let ((sofar 0.0)
- (dseg ())
- (len (- (length xseg) 1)))
- (do ((i 0 (+ i 1)))
- ((= i len))
- (let ((xsi (xseg i))
- (ysi (yseg i))
- (zsi (zseg i))
- (xsf (xseg (+ i 1)))
- (ysf (yseg (+ i 1)))
- (zsf (zseg (+ i 1))))
- (set! sofar (+ sofar (distance (- xsf xsi) (- ysf ysi) (- zsf zsi))))
- (set! dseg (cons sofar dseg))))
- (let ((df (car dseg)))
- (set! dseg (reverse dseg))
- (let ((tseg ()))
- (let ((a (/ (* (- v vi) (+ v vi)) df 4)))
- (for-each
- (lambda (d)
- (let ((seg (+ ti (if (= v vi)
- (/ d vi)
- (/ (- (sqrt (+ (* vi vi) (* 4 a d))) vi) (* 2 a))))))
- (set! tseg (cons seg tseg))))
- dseg))
- (set! ti (car tseg))
- (set! tseg (reverse tseg))
- (set! times (append times tseg))
- (set! xseg (list x))
- (set! yseg (list y))
- (set! zseg (list z))
- (set! vseg (list v))
- (set! vi v)))))))
-
- (set! (path-rt path) (let ((val ())
- (tf (times (- (length times) 1))))
- (for-each
- (lambda (ti)
- (set! val (cons (/ ti tf) val)))
- times)
- (reverse val)))
- (reset-transformation path)))))))
-
- (define (spiral-render path)
- ;; Render a spiral path from the object data
-
- (let* ((start (/ (* (spiral-start-angle path) 2 pi) dlocsig-one-turn))
- (total (if (spiral-total-angle path)
- (/ (* (spiral-total-angle path) 2 pi) dlocsig-one-turn)
- (if (spiral-turns path)
- (* (spiral-turns path) 2 pi)
- (error 'mus-error "a spiral-path needs either a total-angle or turns, none specified~%"))))
- (step (let ((steps (abs (/ (* total dlocsig-one-turn) (* (spiral-step-angle path) 2 pi)))))
- (/ total (ceiling steps)
- (if (< (spiral-step-angle path) 0) -1 1))))
- (xdistance (x-norm (spiral-distance path) total))
- (height (x-norm (spiral-height path) total)))
- (let ((x ())
- (y ())
- (z ())
- (len (+ 1 (round (abs (/ total step))))))
- (do ((i 0 (+ i 1))
- (angle start (+ angle step)))
- ((>= i len))
- (let ((xy (cis angle))
- (d (envelope-interp angle xdistance)))
- (set! x (cons (* d (imag-part xy)) x))
- (set! y (cons (* d (real-part xy)) y))
- (set! z (cons (envelope-interp angle height) z))))
-
- (set! x (reverse x))
- (set! y (reverse y))
- (set! z (reverse z))
-
- (let ((dp ())
- (len (- (length x) 1))
- (sofar 0.0))
- (do ((i 0 (+ i 1)))
- ((>= i len))
- (let ((xi (x i))
- (xf (x (+ i 1)))
- (yi (y i))
- (yf (y (+ i 1)))
- (zi (z i))
- (zf (z (+ i 1))))
- (set! sofar (+ sofar (distance (- xf xi) (- yf yi) (- zf zi))))
- (set! dp (cons sofar dp))))
- (let ()
- (set! dp (reverse dp))
- (let ((tp ())
- (td 0)
- (len (- (length dp) 1)))
- (do ((i 0 (+ i 1)))
- ((>= i len))
- (let* ((di (dp i))
- (df (dp (+ i 1)))
- (vp (x-norm (spiral-velocity path) df)))
- (let ((vi (envelope-interp di vp))
- (vf (envelope-interp df vp)))
- (set! tp (cons td tp))
- (set! td (+ td (/ (- df di) (+ vi vf) 2))))))
- (let ((tf (car tp)))
- (set! tp (reverse tp))
- (set! (path-rx path) x)
- (set! (path-ry path) y)
- (set! (path-rz path) z)
- (let ((val ()))
- (for-each
- (lambda (ti)
- (set! val (cons (/ ti tf) val)))
- tp)
- (set! (path-rt path) (reverse val))))))))
-
- (reset-transformation path)))
-
-
- (define (render-path path)
- ((case (car path)
- ((bezier-path open-bezier-path) bezier-render)
- ((literal-path) literal-render)
- (else spiral-render))
- path))
-
-
-
-
- ;;;;;;;;;;;;;;;;;;;
- ;;; Transformations
- ;;;;;;;;;;;;;;;;;;;
-
- ;;; Transform a rendered path using scaling, translation and rotation
-
- ;;; Transform a path (scaling + translation + rotation)
-
- (define* (transform-path path
- scaling
- translation
- rotation
- rotation-center
- (rotation-axis '(0.0 0.0 1.0)))
-
- ;; Derive a rotation matrix from an axis vector and an angle
-
- (define (rotation-matrix x y z angle)
- ;; translated from C routine by David Eberly
- ;; (http://www.magic-software.com/)
-
- (define (normalize a b c)
- (let ((mag (distance a b c)))
- (list (/ a mag) (/ b mag) (/ c mag))))
-
- (let ((rotate (vector (vector 0.0 0.0 0.0) (vector 0.0 0.0 0.0) (vector 0.0 0.0 0.0)))
- (I (vector (vector 1.0 0.0 0.0) (vector 0.0 1.0 0.0) (vector 0.0 0.0 1.0)))
- (A (let ((vals (normalize x y z)))
- (let ((dx (car vals))
- (dy (cadr vals))
- (dz (caddr vals)))
- (vector (vector 0.0 dz (- dy)) (vector (- dz) 0.0 dx) (vector dy (- dx) 0.0)))))
- (AA (vector (vector 0.0 0.0 0.0) (vector 0.0 0.0 0.0) (vector 0.0 0.0 0.0)))
- (sn (sin (- angle)))
- (omcs (- 1 (cos angle)))) ; (cos (- angle)) == (cos angle)
-
- (do ((row 0 (+ 1 row)))
- ((= row 3))
- (do ((col 0 (+ 1 col)))
- ((= col 3))
- (set! (AA row col) 0.0)
- (do ((mid 0 (+ 1 mid)))
- ((= mid 3))
- (set! (AA row col)
- (+ (AA row col)
- (* (A row mid)
- (A mid col)))))))
-
- ;; rotation matrix is I+sin(angle)*A+[1-cos(angle)]*A*A
- (do ((row 0 (+ 1 row)))
- ((= row 3))
- (do ((col 0 (+ 1 col)))
- ((= col 3))
- (set! (rotate row col)
- (+ (I row col)
- (* sn (A row col))
- (* omcs (AA row col))))))
- rotate))
-
-
- (if (not-rendered path)
- (render-path path))
- (if (or scaling translation rotation)
- ;; there's at least one transformation to execute
- (let* ((rotation (and (number? rotation)
- (/ (* 2 pi rotation) dlocsig-one-turn)))
- (matrix (and rotation (rotation-matrix (car rotation-axis)
- (cadr rotation-axis)
- (third rotation-axis)
- rotation)))
- (xc (path-x path))
- (yc (path-y path))
- (zc (path-z path)))
- (if (and rotation-center (not (= (length rotation-center) 3)))
- (error 'mus-error "rotation center has to have all three coordinates~%"))
- (if (and rotation-axis (not (= (length rotation-axis) 3)))
- (error 'mus-error "rotation axis has to have all three coordinates~%"))
- (do ((xtr ())
- (ytr ())
- (ztr ())
- (len (length xc))
- (i 0 (+ i 1)))
- ((= i len)
- (set! (path-tx path) (reverse xtr))
- (set! (path-ty path) (reverse ytr))
- (set! (path-tz path) (reverse ztr)))
-
- (let ((x (xc i))
- (y (yc i))
- (z (zc i)))
- (let ((xw x)
- (yw y)
- (zw z))
- (when rotation
- ;; rotating around non-triple zero? translate first
- (when rotation-center
- (set! xw (- xw (car rotation-center)))
- (set! yw (- yw (cadr rotation-center)))
- (set! zw (- zw (third rotation-center))))
- ;; rotation
- (let ((xr (+ (* (matrix 0 0) xw)
- (* (matrix 1 0) yw)
- (* (matrix 2 0) zw)))
- (yr (+ (* (matrix 0 1) xw)
- (* (matrix 1 1) yw)
- (* (matrix 2 1) zw)))
- (zr (+ (* (matrix 0 2) xw)
- (* (matrix 1 2) yw)
- (* (matrix 2 2) zw))))
- (set! xw xr)
- (set! yw yr)
- (set! zw zr))
- ;; rotating around non-triple zero? untranslate
- (when rotation-center
- (set! xw (+ xw (car rotation-center)))
- (set! yw (+ yw (cadr rotation-center)))
- (set! zw (+ zw (third rotation-center)))))
-
- ;; scaling
- (when scaling
- (set! xw (* xw (car scaling)))
- (if (cadr scaling)
- (set! yw (* yw (cadr scaling))))
- (if (third scaling)
- (set! zw (* zw (third scaling)))))
-
- ;; translating
- (when translation
- (set! xw (+ xw (car translation)))
- (if (cadr translation)
- (set! yw (+ yw (cadr translation))))
- (if (third translation)
- (set! zw (+ zw (third translation)))))
-
- ;; collect the points
- (set! xtr (cons xw xtr))
- (set! ytr (cons yw ytr))
- (set! ztr (cons zw ztr))))))
-
- (begin
- ;; if there's no transformation just copy the rendered path
- (set! (path-tt path) (copy (path-rt path)))
- (set! (path-tx path) (copy (path-rx path)))
- (set! (path-ty path) (copy (path-ry path)))
- (set! (path-tz path) (copy (path-rz path)))))
- path)
-
- ;;; Scale a path
-
- (define (scale-path path scaling)
- (transform-path path :scaling scaling))
-
- ;;; Translate a path
-
- (define (translate-path path translation)
- (transform-path path :translation translation))
-
- ;;; Rotate a path
-
- (define rotate-path
- (let ((documentation "rotate-path is a dlocsig function that rotates a dlocsig path"))
- (lambda* (path rotation rotation-center (rotation-axis '(0.0 0.0 1.0)))
- (transform-path path
- :rotation rotation
- :rotation-center rotation-center
- :rotation-axis rotation-axis))))
-
- ;;; Mirror a path around an axis
-
- (define* (mirror-path path (axis 'y) (around 0))
- (if (not-transformed path)
- (transform-path path))
- (let ((val ()))
- (if (eq? axis 'y)
- (begin
- (for-each
- (lambda (x)
- (set! val (cons (- around x) val)))
- (path-tx path))
- (set! (path-tx path) (reverse val)))
- (begin
- (for-each
- (lambda (y)
- (set! val (cons (- around y) val)))
- (path-ty path))
- (set! (path-ty path) (reverse val)))))
- path)
-
- ;;; Change the times of the rendered envelope so that the velocity is constant
-
- (define constant-velocity
- (let ((documentation "constant-velocity is a dlocsig function that changes the times of the rendered envelope so that the velocity is constant"))
- (lambda (path)
- (if (not (path-rx path))
- (render-path path))
- (reset-transformation path)
- (let* ((xcoords (path-x path))
- (ycoords (path-y path))
- (zcoords (path-z path))
- (tcoords (path-time path))
- (total-distance
- (do ((sum 0.0)
- (len (length xcoords))
- (i 0 (+ i 1)))
- ((= i len) sum)
- (let ((x1 (xcoords i))
- (x2 (xcoords (+ i 1)))
- (y1 (ycoords i))
- (y2 (ycoords (+ i 1)))
- (z1 (zcoords i))
- (z2 (zcoords (+ i 1))))
- (set! sum (+ sum (distance (- x2 x1) (- y2 y1) (- z2 z1)))))))
- (start-time (car tcoords))
- (velocity (/ total-distance (- (tcoords (- (length tcoords) 1)) start-time)))
- (now ()))
- (do ((dist 0.0)
- (len (length xcoords))
- (i 0 (+ i 1)))
- ((= i len))
- (let ((xp (xcoords i))
- (x (xcoords (+ i 1)))
- (yp (ycoords i))
- (y (ycoords (+ i 1)))
- (zp (zcoords i))
- (z (zcoords (+ i 1))))
- (set! dist (+ dist (distance (- x xp) (- y yp) (- z zp))))
- (set! now (cons (/ dist velocity) now))))
- (set! now (reverse now))
- (set! (path-rt path) (cons start-time now))
- (set! (path-tx path) (copy (path-rx path)))
- (set! (path-ty path) (copy (path-ry path)))
- (set! (path-tz path) (copy (path-rz path))))
- path)))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Create a new dlocsig structure
-
- (define* (make-dlocsig start-time
- duration
- (path dlocsig-path)
- (scaler dlocsig-scaler)
- (direct-power dlocsig-direct-power)
- (inside-direct-power dlocsig-inside-direct-power)
- (reverb-power dlocsig-reverb-power)
- (inside-reverb-power dlocsig-inside-reverb-power)
- (reverb-amount dlocsig-reverb-amount)
- (initial-delay dlocsig-initial-delay)
- (unity-gain-dist dlocsig-unity-gain-distance)
- (inside-radius dlocsig-inside-radius)
- (minimum-segment-length dlocsig-minimum-segment-length)
- (render-using dlocsig-render-using)
- (ambisonics-h-order dlocsig-ambisonics-h-order)
- (ambisonics-v-order dlocsig-ambisonics-v-order)
- out-channels
- rev-channels)
-
- (if (null? start-time)
- (error 'mus-error "a start time is required in make-dlocsig~%"))
- (if (null? duration)
- (error 'mus-error "a duration has to be specified in make-dlocsig~%"))
-
- ;; check to see if we have the right number of channels for b-format ambisonics
- (if (= render-using ambisonics)
- (begin
- (if (or (> ambisonics-h-order 3)
- (> ambisonics-v-order 3))
- (error 'mus-error "ambisonics encoding is currently limited to third order components~%"))
- (let ((channels (ambisonics-channels ambisonics-h-order ambisonics-v-order)))
- (if (< (or out-channels (mus-channels *output*)) channels)
- (error 'mus-error "ambisonics number of channels is wrong, dlocsig needs ~A output channels for h:~A, v:~A order (current number is ~A)~%"
- channels ambisonics-h-order ambisonics-v-order (or out-channels (mus-channels *output*)))))))
-
- (if (not out-channels)
- (if *output*
- (set! out-channels (channels *output*))
- (begin
- (format () "warning: no *output*? Will set out-channels to 2~%")
- (set! out-channels 2))))
- (if (not rev-channels)
- (set! rev-channels (if *reverb* (channels *reverb*) 0)))
-
- (let (;; speaker configuration for current number of channels
- (speakers (and (not (= render-using ambisonics))
- (get-speaker-configuration out-channels)))
- ;; coordinates of rendered path
- (xpoints (path-x path))
- (ypoints (path-y path))
- (zpoints (path-z path))
- (tpoints (path-time path))
-
- ;; array of gains -- envelopes
- (channel-gains (make-vector out-channels ()))
- (channel-rev-gains (make-vector out-channels ()))
-
- ;; speaker output delays
- (max-out-delay 0.0)
- (out-delays (make-vector out-channels))
-
- ;; speed of sound expressed in terms of path time coordinates
- (dly ())
- (doppler ())
- (prev-time #f)
- (prev-dist #f)
- (first-dist #f)
- (last-dist #f)
- (min-dist #f)
- (max-dist #f)
- (delay-hack 1)
- ;; channel offsets in output stream for ambisonics
- ;; (depends on horizontal and vertical order, default is h=1,v=1)
- (z-offset #f)
- (r-offset #f)
- (s-offset #f)
- (t-offset #f)
- (u-offset #f)
- (v-offset #f)
- (k-offset #f)
- (l-offset #f)
- (m-offset #f)
- (n-offset #f)
- (o-offset #f)
- (p-offset #f)
- (q-offset #f))
-
- (when (= render-using ambisonics)
- ;; calculate output channel offsets for ambisonics rendering
- (let ((offset 3))
- ;; the default is at least a horizontal order of 1
- (if (>= ambisonics-v-order 1)
- (begin
- ;; add Z
- (set! z-offset offset)
- (set! offset (+ offset 1))))
- (if (>= ambisonics-v-order 2)
- (begin
- ;; add R S T
- (set! r-offset offset)
- (set! s-offset (+ offset 1))
- (set! t-offset (+ offset 2))
- (set! offset (+ offset 3))))
- (if (>= ambisonics-h-order 2)
- (begin
- ;; add U V
- (set! u-offset offset)
- (set! v-offset (+ offset 1))
- (set! offset (+ offset 2))))
- (if (>= ambisonics-v-order 3)
- (begin
- ;; add K L M N O
- (set! k-offset offset)
- (set! l-offset (+ offset 1))
- (set! m-offset (+ offset 2))
- (set! n-offset (+ offset 3))
- (set! o-offset (+ offset 4))
- (set! offset (+ offset 5))))
- (if (>= ambisonics-h-order 3)
- (begin
- ;; add P Q
- (set! p-offset offset)
- (set! q-offset (+ offset 1)))
- (set! offset (+ offset 2)))))
-
- (define (dist->samples d) (round (* d (/ *clm-srate* dlocsig-speed-of-sound))))
- ;; (define (dist->seconds d) (/ d dlocsig-speed-of-sound))
- (define (time->samples time) (round (* time *clm-srate*)))
-
- ;; calculate speaker gains for group
- (define (find-gains x y z group)
- (let ((zero-coord 1.0e-10)
- (zero-gain 1.0e-10)
- (size (group-size group))
- (mat (group-matrix group))) ; returns float-vector
- (if (and (< (abs x) zero-coord)
- (< (abs y) zero-coord)
- (< (abs z) zero-coord))
- (list #t (list 1.0 1.0 1.0))
- (case size
- ((3)
- (let* ((gain-a (+ (* (mat 0 0) x)
- (* (mat 1 0) y)
- (* (mat 2 0) z)))
- (gain-b (+ (* (mat 0 1) x)
- (* (mat 1 1) y)
- (* (mat 2 1) z)))
- (gain-c (+ (* (mat 0 2) x)
- (* (mat 1 2) y)
- (* (mat 2 2) z)))
- (mag (distance gain-a gain-b gain-c)))
- ;; truncate to zero roundoff errors
- (if (< (abs gain-a) zero-gain)
- (set! gain-a 0.0))
- (if (< (abs gain-b) zero-gain)
- (set! gain-b 0.0))
- (if (< (abs gain-c) zero-gain)
- (set! gain-c 0.0))
- (list (and (>= gain-a 0) (>= gain-b 0) (>= gain-c 0))
- (list (/ gain-a mag) (/ gain-b mag) (/ gain-c mag)))))
-
- ((2)
- (let* ((gain-a (+ (* (mat 0 0) x)
- (* (mat 1 0) y)))
- (gain-b (+ (* (mat 0 1) x)
- (* (mat 1 1) y)))
- (mag (sqrt (+ (* gain-a gain-a)
- (* gain-b gain-b)))))
- ;; truncate to zero roundoff errors
- (if (< (abs gain-a) zero-gain)
- (set! gain-a 0.0))
- (if (< (abs gain-b) zero-gain)
- (set! gain-b 0.0))
- (list (and (>= gain-a 0) (>= gain-b 0))
- (list (/ gain-a mag) (/ gain-b mag)))))
-
- ((1)
- (list #t (list 1.0)))))))
-
-
- ;; Render a trajectory breakpoint through amplitude panning
- (define famplitude-panning
- (let ((speed-limit (/ (* dlocsig-speed-of-sound (- (car (last tpoints)) (car tpoints))) duration))
- (prev-group #f)
- (prev-x #f)
- (prev-y #f)
- (prev-z #f))
-
- (define (equalp-intersection L1 L2)
- (if (null? L2)
- L2
- (let loop1 ((L1 L1)
- (result ()))
- (cond ((null? L1)
- (reverse! result))
- ((member (car L1) L2)
- (loop1 (cdr L1)
- (cons (car L1)
- result)))
- (else (loop1 (cdr L1)
- result))))))
-
- (define (transition-point-3 vert-a vert-b xa ya za xb yb zb)
- (define (cross v1 v2)
- (list (- (* (cadr v1) (third v2))
- (* (third v1) (cadr v2)))
- (- (* (third v1) (car v2))
- (* (car v1) (third v2)))
- (- (* (car v1) (cadr v2))
- (* (cadr v1) (car v2)))))
- (define (dot v1 v2)
- (+ (* (car v1) (car v2))
- (* (cadr v1) (cadr v2))
- (* (third v1) (third v2))))
- (define (sub v1 v2)
- (list (- (car v1) (car v2))
- (- (cadr v1) (cadr v2))
- (- (third v1) (third v2))))
- (define (add v1 v2)
- (list (+ (car v1) (car v2))
- (+ (cadr v1) (cadr v2))
- (+ (third v1) (third v2))))
- (define (scale v1 c)
- (list (* (car v1) c)
- (* (cadr v1) c)
- (* (third v1) c)))
-
- (let* ((tolerance 1.0e-6)
- (line-b (list xa ya za))
- (line-m (sub (list xb yb zb) line-b))
- (normal (cross vert-a vert-b))
- (denominator (dot normal line-m)))
- (and (> (abs denominator) tolerance)
- (add line-b (scale line-m (/ (- (dot normal line-b)) denominator))))))
-
- ;; calculate transition point between two adjacent two-speaker groups
- ;; original line intersection code from Graphic Gems III
- (define (transition-point-2 vert xa ya xb yb)
- (let ((Ax (car vert))
- (Bx (- xa xb))
- (Ay (cadr vert))
- (By (- ya yb)))
- (let ((d (- (* By (- xa)) (* Bx (- ya))))
- (f (- (* Ay Bx) (* Ax By))))
- (and (not (= f 0))
- (list (/ (* d Ax) f)
- (/ (* d Ay) f))))))
-
- ;; find the speaker group that contains a point
- (define (find-group x y z)
- (call-with-exit
- (lambda (return)
- (for-each
- (lambda (group)
- (let ((vals (find-gains x y z group)))
- (let ((inside (car vals))
- (gains (cadr vals)))
- (if inside
- (return (list group gains))))))
- (speaker-config-groups speakers))
- (list #f #f))))
-
- ;; push zero gains on all channels
- (define (push-zero-gains time)
- (do ((len (speaker-config-number speakers))
- (i 0 (+ i 1)))
- ((= i len))
- (set! (channel-gains i) (cons time (channel-gains i)))
- (set! (channel-gains i) (cons 0.0 (channel-gains i))))
- (do ((len rev-channels)
- (i 0 (+ i 1)))
- ((= i len))
- (set! (channel-rev-gains i) (cons time (channel-rev-gains i)))
- (set! (channel-rev-gains i) (cons 0.0 (channel-rev-gains i)))))
-
- ;; push gain and time into envelopes
- (define (push-gains group gains dist time)
-
- (define* (position val lst (pos 0))
- (call-with-exit
- (lambda (return)
- (and (not (null? lst))
- (if (= val (car lst))
- (return pos)
- (position val (cdr lst) (+ 1 pos)))))))
-
- (let ((outputs (make-vector out-channels 0.0))
- (rev-outputs (make-vector rev-channels 0.0))
- ;; attenuation with distance of reverberated signal
- (ratt (if (>= dist inside-radius)
- (expt dist (- reverb-power))
- (- 1.0 (expt (/ dist inside-radius) (/ inside-reverb-power))))))
- (let (;; attenuation with distance of direct signal
- (att (if (>= dist inside-radius)
- (expt dist (- direct-power))
- (- 1.0 (expt (/ dist inside-radius) (/ inside-direct-power))))))
- (if (>= dist inside-radius)
- ;; outside the inner sphere, signal is sent to group
- (do ((len (length gains))
- (i 0 (+ i 1)))
- ((= i len))
- (let ((speaker ((group-speakers group) i))
- (gain (gains i)))
- (set! (outputs speaker) (* gain att))
- (if (and (> rev-channels 1)
- (< speaker (length rev-outputs)))
- (set! (rev-outputs speaker) (* gain ratt)))))
-
- (let ((gain 0.0)
- (len (speaker-config-number speakers)))
- (do ((speaker 0 (+ 1 speaker)))
- ((= speaker len))
- ;; inside the inner sphere, signal is sent to all speakers
- (let ((found (position speaker (group-speakers group))))
- (if found
- ;; speaker belongs to group, add to existing gain
- (begin
- (set! gain (gains found))
- (set! (outputs speaker) (+ gain (* (- 1.0 gain) att)))
- (if (> rev-channels 1) (set! (rev-outputs speaker) (+ gain (* (- 1.0 gain) ratt)))))
- ;; speaker outside of group
- (begin
- (set! (outputs speaker) att)
- (if (> rev-channels 1) (set! (rev-outputs speaker) ratt)))))))))
-
- ;; push all channel gains into envelopes
- (let ((len (speaker-config-number speakers)))
- (do ((i 0 (+ i 1)))
- ((= i len))
- (if (or (null? (channel-gains i))
- (> time (cadr (channel-gains i))))
- (begin
- (set! (channel-gains i) (cons time (channel-gains i)))
- (set! (channel-gains i) (cons (outputs i) (channel-gains i)))))))
-
- (if (> rev-channels 1)
- (do ((i 0 (+ i 1)))
- ((= i rev-channels))
- (if (or (null? (channel-rev-gains i))
- (> time (cadr (channel-rev-gains i))))
- (begin
- (set! (channel-rev-gains i) (cons time (channel-rev-gains i)))
- (set! (channel-rev-gains i) (cons (rev-outputs i) (channel-rev-gains i)))))))
-
- ;; push reverb gain into envelope for mono reverb
- (if (and (= rev-channels 1)
- (or (null? (channel-rev-gains 0))
- (> time (cadr (channel-rev-gains 0)))))
- (begin
- (set! (channel-rev-gains 0) (cons time (channel-rev-gains 0)))
- (set! (channel-rev-gains 0) (cons ratt (channel-rev-gains 0)))))))
-
- (lambda (x y z dist time)
-
- ;; output gains for current point
- (if prev-group
- (let ((vals (find-gains x y z prev-group)))
- (let ((inside (car vals))
- (gains (cadr vals)))
- ;; check that the source is not moving faster than sound
- (if (not (= time prev-time))
- (let ((speed (/ (- dist prev-dist) (- time prev-time))))
- (if (> speed speed-limit)
- (format () "warning: supersonic radial movement at [~F,~F,~F, ~F], speed=~F~%" x y z time speed))))
- (if inside
- ;; still in the same group
- (begin
- (push-gains prev-group gains dist time)
- (set! prev-x x)
- (set! prev-y y)
- (set! prev-z z))
- ;; left the group
- (let ((vals (find-group x y z)))
- (let ((group (car vals))
- (gains (cadr vals)))
- (if (not group)
- (begin
- ;; current point is outside all defined groups
- ;; we should send a warning at this point...
- (push-zero-gains time)
- (set! prev-group #f))
- (begin
- ;; we have to interpolate a new point that lies on the shared
- ;; edge of the adjacent groups so that the speakers opposite
- ;; the edge have zero gain when the trajectory switches groups
- (let ((edge (equalp-intersection (group-vertices group)
- (group-vertices prev-group))))
- (cond ((= (length edge) 2)
- ;; the groups have two shared points (ie: share an edge)
- ;; this must be a three speaker groups transition
- (let ((pint (transition-point-3 (car edge) (cadr edge) x y z prev-x prev-y prev-z)))
- (when pint
- (let* ((xi (car pint))
- (yi (cadr pint))
- (zi (third pint))
- (vals (find-gains xi yi zi prev-group)))
- (let ((di (distance xi yi zi))
- (ti (+ prev-time (max .00001 (* (/ (distance (- xi prev-x)
- (- yi prev-y)
- (- zi prev-z))
- (distance (- x prev-x)
- (- y prev-y)
- (- z prev-z)))
- (- time prev-time)))))
- ;; see if we are inside the previous group
- ;; we can be on either side due to roundoff errors
- (inside (car vals))
- (gains (cadr vals)))
- (if inside
- (push-gains prev-group gains di ti)
- (let ((val1 (find-gains xi yi zi group)))
- (let ((inside (car val1))
- (gains (cadr val1)))
- (if inside
- (push-gains group gains di ti)
- ;; how did we get here?
- (error 'mus-error "Outside of both adjacent groups [~A:~A:~A @~A]~%~%" xi yi zi ti))))))))))
-
- ((and (pair? edge)
- (null? (cdr edge))
- (= (group-size group) 2))
- ;; two two-speaker groups share one point
- ;; z coordinates are silently ignored
- (let ((pint (transition-point-2 (car edge) x y prev-x prev-y)))
- (when pint
- (let* ((xi (car pint))
- (yi (cadr pint))
- (vals (find-gains xi yi 0.0 prev-group)))
- (let ((di (distance xi yi 0.0))
- (ti (+ prev-time (max .00001 (* (/ (distance (- xi prev-x)
- (- yi prev-y)
- 0.0)
- (distance (- x prev-x)
- (- y prev-y)
- 0.0))
- (- time prev-time)))))
- ;; see if we are inside the previous group
- ;; we can be on either side due to roundoff errors
- (inside (car vals))
- (gains (cadr vals)))
- (if inside
- (push-gains prev-group gains di ti)
- (let ((val1 (find-gains xi yi 0.0 group)))
- (let ((inside (car val1))
- (gains (cadr val1)))
- (if inside
- (push-gains group gains di ti)
- ;; how did we get here?
- (format () "Outside of both adjacent groups [~A:~A @~A]~%~%" xi yi ti))))))))))
-
- ((and (pair? edge)
- (null? (cdr edge)))
- ;; groups share only one point... for now a warning
- ;; we should calculate two additional interpolated
- ;; points as the trajectory must be crossing a third
- ;; group
- (for-each
- (lambda (int-group)
- (if (and (member (car edge) (group-vertices int-group))
- (not (equal? int-group group))
- (not (equal? int-group prev-group)))
- (format () "e1=~A; e2=~A~%~%"
- (equalp-intersection (group-vertices int-group)
- (group-vertices prev-group))
- (equalp-intersection (group-vertices int-group)
- (group-vertices group)))))
-
- (speaker-config-groups speakers))
- (format () "warning: crossing between groups with only one point in common~% prev=~A~% curr=~A~%" prev-group group))
-
- ;; groups don't share points... how did we get here?
- ((null? edge)
- (format () "warning: crossing between groups with no common points, ~A~A to ~A~A~%"
- (group-id prev-group) (group-speakers prev-group)
- (group-id group) (group-speakers group)))))
-
- ;; finally push gains for current group
- (push-gains group gains dist time)
- (set! prev-group group)
- (set! prev-x x)
- (set! prev-y y)
- (set! prev-z z))))))))
- ;; first time around
- (let ((vals (find-group x y z)))
- (let ((group (car vals))
- (gains (cadr vals)))
- (if group
- (begin
- (push-gains group gains dist time)
- (set! prev-group group)
- (set! prev-x x)
- (set! prev-y y)
- (set! prev-z z))
- (begin
- (push-zero-gains time)
- (set! prev-group #f)))))))))
-
- ;; Render a trajectory breakpoint for ambisonics b-format coding
- ;; http://www.york.ac.uk/inst/mustech/3d_audio/ambis2.htm
- ;;
- ;; Ambisonics b-format has four discrete channels encoded as follows:
- ;; W 0.707107 0.707107
- ;; X cos(A)cos(E) x
- ;; Y sin(A)cos(E) y
- ;; R 1.5sin(E)sin(E)-0.5 1.5zz-0.5
- ;; S cos(A)sin(2E) 2zx
- ;; T sin(A)sin(2E) 2yz
- ;; U cos(2A)cos(E)cos(E) xx-yy
- ;; V sin(2A)cos(E)cos(E) 2xy
- ;; K z(2.5zz-1.5)
- ;; L K1(x(5zz-1)
- ;; K1=sqrt(21*45/(224*8))
- ;; M K1(y(5zz-1))
- ;; N K2(Uz)
- ;; K2=sqrt(3)*3/2
- ;; O K2(Vz)
- ;; P x(xx-3yy)
- ;; Q y(3xx-yy)
- ;;
- ;; where:
- ;; A: counter-clockwise angle of rotation from the front center
- ;; E: the angle of elevation above the horizontal plane
- ;;
- ;; in our coordinate system (normalizing the vectors):
- ;; xy: (* dist (cos E))
- ;; (cos A): (/ y xy)
- ;; (sin A): (/ -x xy)
- ;; (cos E): (/ xy dist)
- ;; (sin E): (/ z dist)
- ;; so:
- ;; W: (* signal 0.707)
- ;; X: (* signal (/ y dist))
- ;; Y: (* signal (/ -x dist))
- ;; Z: (* signal (/ z dist))
- ;;
- ;; R: (* signal (- (* 1.5 z z 1/dist 1/dist) 0.5))
- ;; S: (* signal 2 z (- x) 1/dist 1/dist)
- ;; T: (* signal 2 z y 1/dist 1/dist)
- ;; U: (* signal (- (* x x 1/dist 1/dist) (* y y 1/dist 1/dist)))
- ;; V: (* signal 2 (- x) y 1/dist 1/dist)
- ;;
- ;; K: (* signal z (- (* 2.5 z z 1/dist 1/dist) 1.5))
- ;; L: (* signal K1 x 1/dist (- (* 5 z z 1/dist 1/dist) 1))
- ;; M: (* signal K1 y 1/dist (- (* 5 z z 1/dist 1/dist) 1))
- ;; N: (* signal K2 U z 1/dist)
- ;; O: (* signal K2 V z 1/dist)
- ;; P: (* signal x 1/dist (- (* x x 1/dist 1/dist) (* 3 y y 1/dist 1/dist)))
- ;; Q: (* signal y 1/dist (- (* 3 x x 1/dist 1/dist) (* y y 1/dist 1/dist)))
- ;;
- ;; see also: http://wiki.xiph.org/index.php/Ambisonics
- ;; for mixed order systems
- ;;
- (define render-ambisonics
- (let ((w-offset 0)
- (x-offset 1)
- (y-offset 2)
- (ambisonics-k1 (sqrt 135/256)) ;(/ (* 21 45) 224 8)
- (ambisonics-k2 (* (sqrt 3) 3 0.5)))
- (lambda (x y z dist time)
- (let ((att (if (> dist inside-radius)
- (expt (/ inside-radius dist) direct-power)
- (expt (/ dist inside-radius) (/ inside-direct-power))))
- (ratt (if (> dist inside-radius)
- (expt (/ inside-radius dist) reverb-power)
- (expt (/ dist inside-radius) (/ inside-reverb-power))))
- (u 0)
- (v 0)
- (lm 0)
- (no 0))
- ;; output encoding gains for point
- ;; W: 0.707
- (set! (channel-gains w-offset) (cons time (channel-gains w-offset)))
- (let ((attW (if (> dist inside-radius)
- (* point707 att)
- (- 1 (* (- 1 point707) (expt (/ dist inside-radius) direct-power))))))
- (set! (channel-gains w-offset) (cons attW (channel-gains w-offset))))
- ;; X: (* (cos A) (cos E))
- (set! (channel-gains x-offset) (cons time (channel-gains x-offset)))
- (set! (channel-gains x-offset) (cons (* (if (zero? dist) 0 (/ y dist)) att) (channel-gains x-offset)))
- ;; Y: (* (sin A) (cos E))
- (set! (channel-gains y-offset) (cons time (channel-gains y-offset)))
- (set! (channel-gains y-offset) (cons (* (if (zero? dist) 0 (/ (- x) dist)) att) (channel-gains y-offset)))
- (when (>= ambisonics-v-order 1)
- ;; Z: (sin E)
- (set! (channel-gains z-offset) (cons time (channel-gains z-offset)))
- (set! (channel-gains z-offset) (cons (* (if (zero? dist) 0 (/ z dist)) att) (channel-gains z-offset))))
- (when (>= ambisonics-v-order 2)
- ;; R
- (set! (channel-gains r-offset) (cons time (channel-gains r-offset)))
- (set! (channel-gains r-offset) (cons (* (if (zero? dist) 0 (- (* 1.5 z z (if (zero? dist) 1 (/ 1.0 dist dist))) 0.5)) att)
- (channel-gains r-offset)))
- ;; S
- (set! (channel-gains s-offset) (cons time (channel-gains s-offset)))
- (set! (channel-gains s-offset) (cons (* (if (zero? dist) 0 2) z (- x) (if (zero? dist) 1 (/ 1.0 dist dist)) att)
- (channel-gains s-offset)))
- ;; T
- (set! (channel-gains t-offset) (cons time (channel-gains t-offset)))
- (set! (channel-gains t-offset) (cons (* (if (zero? dist) 0 2) z y (if (zero? dist) 1 (/ 1.0 dist dist)) att)
- (channel-gains t-offset))))
- (when (>= ambisonics-h-order 2)
- (set! u (* (if (zero? dist) 0 1) (- (* x x (if (zero? dist) 1 (/ 1.0 dist dist)))
- (* y y (if (zero? dist) 1 (/ 1.0 dist dist)))) att))
- (set! v (* (if (zero? dist) 0 2) (- x) y (if (zero? dist) 1 (/ 1.0 dist dist)) att))
- ;; U
- (set! (channel-gains u-offset) (cons time (channel-gains u-offset)))
- (set! (channel-gains u-offset) (cons u (channel-gains u-offset)))
- ;; V
- (set! (channel-gains v-offset) (cons time (channel-gains v-offset)))
- (set! (channel-gains v-offset) (cons v (channel-gains v-offset))))
-
- (when (>= ambisonics-v-order 3)
- (set! lm (* ambisonics-k1 (- (* 5 z z (if (zero? dist) 1 (/ 1.0 dist dist))) 1) att))
- (set! no (* ambisonics-k2 z (if (zero? dist) 1 (/ dist)) att))
- ;; K
- (set! (channel-gains k-offset) (cons time (channel-gains k-offset)))
- (set! (channel-gains k-offset) (cons (* (if (zero? dist) 0 1) (- (* 2.5 z z (if (zero? dist) 1 (/ 1.0 dist dist))) 1.5) att) (channel-gains k-offset)))
- ;; L
- (set! (channel-gains l-offset) (cons time (channel-gains l-offset)))
- (set! (channel-gains l-offset) (cons (* (if (zero? dist) 0 (/ x dist)) lm) (channel-gains l-offset)))
- ;; M
- (set! (channel-gains m-offset) (cons time (channel-gains m-offset)))
- (set! (channel-gains m-offset) (cons (* (if (zero? dist) 0 (/ y dist)) lm) (channel-gains m-offset)))
- ;; N
- (set! (channel-gains n-offset) (cons time (channel-gains n-offset)))
- (set! (channel-gains n-offset) (cons (* (if (zero? dist) 0 no) u) (channel-gains n-offset)))
- ;; O
- (set! (channel-gains o-offset) (cons time (channel-gains o-offset)))
- (set! (channel-gains o-offset) (cons (* (if (zero? dist) 0 no) v) (channel-gains o-offset))))
-
- (when (>= ambisonics-h-order 3)
- ;; P
- (set! (channel-gains p-offset) (cons time (channel-gains p-offset)))
- (set! (channel-gains p-offset) (let ((dist-p (- (* x x (if (zero? dist) 1 (/ 1.0 dist dist)))
- (* 3 y y (if (zero? dist) 1 (/ 1.0 dist dist))))))
- (cons (* (if (zero? dist) 0 (/ att dist)) x dist-p)
- (channel-gains p-offset))))
- ;; Q
- (set! (channel-gains q-offset) (cons time (channel-gains q-offset)))
- (set! (channel-gains q-offset) (let ((dist-q (- (* 3 x x (if (zero? dist) 1 (/ 1.0 dist dist)))
- (* y y (if (zero? dist) 1 (/ 1.0 dist dist))))))
- (cons (* (if (zero? dist) 0 (/ att dist)) y dist-q)
- (channel-gains q-offset)))))
- ;; push reverb gain into envelope
- (when (= rev-channels 1)
- ;; mono reverb output
- (set! (channel-rev-gains 0) (cons time (channel-rev-gains 0)))
- (set! (channel-rev-gains 0) (cons (if (>= dist inside-radius)
- (expt dist (- reverb-power))
- (- 1.0 (expt (/ dist inside-radius) (/ inside-reverb-power))))
- (channel-rev-gains 0))))
- (when (> rev-channels 1)
- ;; multichannel reverb, send ambisonics components
- ;; W: 0.707
- (set! (channel-rev-gains w-offset) (cons time (channel-rev-gains w-offset)))
- (let ((rattW (if (> dist inside-radius)
- (* point707 ratt)
- (- 1 (* (- 1 point707) (expt (/ dist inside-radius) reverb-power))))))
- (set! (channel-rev-gains w-offset) (cons rattW (channel-rev-gains w-offset))))
- ;; X: (* (cos A)(cos E))
- (set! (channel-rev-gains x-offset) (cons time (channel-rev-gains x-offset)))
- (set! (channel-rev-gains x-offset) (cons (* (if (zero? dist) 0 1) y (if (zero? dist) 1 (/ dist)) ratt)
- (channel-rev-gains x-offset)))
- ;; Y: (* (sin A)(cos E))
- (set! (channel-rev-gains y-offset) (cons time (channel-rev-gains y-offset)))
- (set! (channel-rev-gains y-offset) (cons (* (if (zero? dist) 0 1) (- x) (if (zero? dist) 1 (/ dist)) ratt)
- (channel-rev-gains y-offset)))
- (when (>= ambisonics-v-order 1)
- ;; Z: (sin E)
- (set! (channel-rev-gains z-offset) (cons time (channel-rev-gains z-offset)))
- (set! (channel-rev-gains z-offset) (cons (* (if (zero? dist) 0 1) z (if (zero? dist) 1 (/ dist)) ratt)
- (channel-rev-gains z-offset))))
- (when (>= ambisonics-v-order 2)
- ;; R
- (set! (channel-rev-gains r-offset) (cons time (channel-rev-gains r-offset)))
- (set! (channel-rev-gains r-offset) (cons (* (if (zero? dist) 0 (- (* 1.5 z z (if (zero? dist) 1 (/ 1.0 dist dist))) 0.5))
- dlocsig-ambisonics-ho-rev-scaler ratt)
- (channel-rev-gains r-offset)))
- ;; S
- (set! (channel-rev-gains s-offset) (cons time (channel-rev-gains s-offset)))
- (set! (channel-rev-gains s-offset) (cons (* (if (zero? dist) 0 2) z (- x) (if (zero? dist) 1 (/ 1.0 dist dist))
- dlocsig-ambisonics-ho-rev-scaler ratt)
- (channel-rev-gains s-offset)))
- ;; T
- (set! (channel-rev-gains t-offset) (cons time (channel-rev-gains t-offset)))
- (set! (channel-rev-gains t-offset) (cons (* (if (zero? dist) 0 2) z y (if (zero? dist) 1 (/ 1.0 dist dist))
- dlocsig-ambisonics-ho-rev-scaler ratt)
- (channel-rev-gains t-offset))))
- (when (>= ambisonics-h-order 2)
- ;; U
- (set! (channel-rev-gains u-offset) (cons time (channel-rev-gains u-offset)))
- (set! (channel-rev-gains u-offset) (let ((dist-u (* (if (zero? dist) 0
- (- (* x x (if (zero? dist) 1 (/ 1.0 dist dist)))
- (* y y (if (zero? dist) 1 (/ 1.0 dist dist)))))
- dlocsig-ambisonics-ho-rev-scaler ratt)))
- (cons dist-u (channel-rev-gains u-offset))))
- ;; V
- (set! (channel-rev-gains v-offset) (cons time (channel-rev-gains v-offset)))
- (set! (channel-rev-gains v-offset) (let ((dist-v (* (if (zero? dist) 0 2)
- (- x) y
- (if (zero? dist) 1 (/ 1.0 dist dist))
- dlocsig-ambisonics-ho-rev-scaler ratt)))
- (cons dist-v (channel-rev-gains v-offset)))))
-
- (when (>= ambisonics-v-order 3)
- (set! lm (* ambisonics-k1 (- (* 5 z z (if (zero? dist) 1 (/ 1.0 dist dist))) 1)
- dlocsig-ambisonics-ho-rev-scaler ratt))
- (set! no (* ambisonics-k2 z (if (zero? dist) 1 (/ dist)) ratt))
- ;; K
- (set! (channel-rev-gains k-offset) (cons time (channel-rev-gains k-offset)))
- (set! (channel-rev-gains k-offset) (let ((dist-k (* (if (zero? dist) 0 1)
- (- (* 2.5 z z (if (zero? dist) 1 (/ 1.0 dist dist))) 1.5)
- dlocsig-ambisonics-ho-rev-scaler ratt)))
- (cons dist-k (channel-rev-gains k-offset))))
- ;; L
- (set! (channel-rev-gains l-offset) (cons time (channel-rev-gains l-offset)))
- (set! (channel-rev-gains l-offset) (cons (* (if (zero? dist) 0 (/ x dist)) lm)
- (channel-rev-gains l-offset)))
- ;; M
- (set! (channel-rev-gains m-offset) (cons time (channel-rev-gains m-offset)))
- (set! (channel-rev-gains m-offset) (cons (* (if (zero? dist) 0 (/ y dist)) lm)
- (channel-rev-gains m-offset)))
- ;; N
- (set! (channel-rev-gains n-offset) (cons time (channel-rev-gains n-offset)))
- (set! (channel-rev-gains n-offset) (cons (* (if (zero? dist) 0 no) u)
- (channel-rev-gains n-offset)))
- ;; O
- (set! (channel-rev-gains o-offset) (cons time (channel-rev-gains o-offset)))
- (set! (channel-rev-gains o-offset) (cons (* (if (zero? dist) 0 no) v)
- (channel-rev-gains o-offset))))
- (when (>= ambisonics-h-order 3)
- ;; P
- (set! (channel-rev-gains p-offset) (cons time (channel-rev-gains p-offset)))
- (set! (channel-rev-gains p-offset) (let ((dist-p (* (if (zero? dist) 0 (/ ratt dist))
- dlocsig-ambisonics-ho-rev-scaler x
- (- (* x x (if (zero? dist) 1 (/ 1.0 dist dist)))
- (* 3 y y (if (zero? dist) 1 (/ 1.0 dist dist)))))))
- (cons dist-p (channel-rev-gains p-offset))))
- ;; Q
- (set! (channel-rev-gains q-offset) (cons time (channel-rev-gains q-offset)))
- (set! (channel-rev-gains q-offset) (let ((dist-q (* (if (zero? dist) 0 (/ ratt dist))
- dlocsig-ambisonics-ho-rev-scaler y
- (- (* 3 x x (if (zero? dist) 1 (/ 1.0 dist dist)))
- (* y y (if (zero? dist) 1 (/ 1.0 dist dist)))))))
- (cons dist-q (channel-rev-gains q-offset)))))
- )))))
-
- ;; Render a trajectory breakpoint to a room for decoded ambisonics
- ;;
- ;; for a given speaker located in 3d space in polar coordinates:
- ;; az: azimut angle, increments clockwise
- ;; el: elevation angle
- ;;
- ;; S: (+ W (* X (cos az) (cos el))
- ;; (* Y (sin az) (cos el))
- ;; (* Z (sin el)))
- ;;
- (define (fdecoded-ambisonics x y z dist time)
- (let ((att (if (> dist inside-radius)
- (expt (/ inside-radius dist) direct-power)
- (expt (/ dist inside-radius) (/ inside-direct-power))))
- (ratt (if (> dist inside-radius)
- (expt (/ inside-radius dist) reverb-power)
- (expt (/ dist inside-radius) (/ inside-reverb-power)))))
- (let ((attW (if (> dist inside-radius)
- (* point707 att)
- (- 1 (* (- 1 point707) (expt (/ dist inside-radius) direct-power)))))
- (rattW (if (> dist inside-radius)
- (* point707 ratt)
- (- 1 (* (- 1 point707) (expt (/ dist inside-radius) reverb-power))))))
- ;; output decoded gains for point
- (let ((len (speaker-config-number speakers))
- (spkrs (speaker-config-coords speakers)))
- (do ((i 0 (+ i 1)))
- ((= i len))
- (let ((signal (let ((s (spkrs i)))
- (* dlocsig-ambisonics-scaler
- (+
- ;; W
- (* attW point707)
- ;; (* X (cos az) (cos el))
- (* att (if (= dist 0) 0 (/ y dist)) (cadr s))
- ;; (* Y (sin az) (cos el))
- (* att (if (= dist 0) 0 (/ x dist)) (car s))
- ;; (* Z (sin el)
- (* att (if (= dist 0) 0 (/ z dist)) (third s)))))))
- (set! (channel-gains i) (cons time (channel-gains i)))
- (set! (channel-gains i) (cons signal (channel-gains i))))))
-
- ;; push reverb gain into envelope
- (if (= rev-channels 1)
- (begin
- ;; mono reverberation
- (set! (channel-rev-gains 0) (cons time (channel-rev-gains 0)))
- (set! (channel-rev-gains 0) (cons (if (>= dist inside-radius)
- (expt dist (- reverb-power))
- (- 1.0 (expt (/ dist inside-radius) (/ inside-reverb-power))))
- (channel-rev-gains 0))))
- ;; multichannel reverb
- (do ((i 0 (+ i 1)))
- ((= i rev-channels))
- (let ((signal (let ((s ((speaker-config-coords speakers) i)))
- (* dlocsig-ambisonics-scaler
- (+
- ;; W
- (* rattW point707)
- ;; (* X (cos az) (cos el))
- (* ratt (if (zero? dist) 0 (/ y dist)) (cadr s))
- ;; (* Y (sin az) (cos el))
- (* ratt (if (zero? dist) 0 (/ x dist)) (car s))
- ;; (* Z (sin el)
- (* ratt (if (zero? dist) 0 (/ z dist)) (third s)))))))
- (set! (channel-rev-gains i) (cons time (channel-rev-gains i)))
- (set! (channel-rev-gains i) (cons signal (channel-rev-gains i)))))))))
-
- ;; Loop through all virtual rooms for one breakpoint in the trajectory
- (define (walk-all-rooms x y z time)
- (let ((dist (distance x y z)))
- ;; remember first and last distances
- (if (not first-dist) ; set to #f (far) above
- (set! first-dist dist))
- (set! last-dist dist)
- ;; remember maximum and minimum distances
- (if (or (not (real? min-dist)) (< dist min-dist))
- (set! min-dist dist))
- (if (or (not (real? max-dist)) (> dist max-dist))
- (set! max-dist dist))
- ;; push delay for current point (for doppler)
- (if (or (null? dly)
- (> time (cadr dly)))
- (begin
- (set! dly (cons (dist->samples dist)
- (cons time dly)))
- ;; doppler should be easy, yeah right. We use "relativistic" correction
- ;; as the sound object can be travelling close to the speed of sound.
- ;; http://www.mathpages.com/rr/s2-04/2-04.htm,
- ;; va = 0 (stationary listener)
- ;; ve = moving object
- ;; va = (* ve (/ 1 (+ 1 (/ ve c))) (sqrt (- 1 (* (/ ve c) (/ ve c)))))
- (if prev-time
- (let ((ratio (/ (- dist prev-dist)
- (* duration (- time prev-time) dlocsig-speed-of-sound))))
- (set! doppler (cons (* (/ 1.0 (+ 1 ratio)) (sqrt (- 1 (* ratio ratio))))
- (cons (/ (+ prev-time time) 2) doppler)))))))
-
- ;; do the rendering of the point
- (cond ((= render-using amplitude-panning)
- ;; amplitude panning
- (famplitude-panning x y z dist time))
- ((= render-using ambisonics)
- ;; ambisonics b format
- (render-ambisonics x y z dist time))
- ((= render-using decoded-ambisonics)
- ;; ambisonics decoded
- (fdecoded-ambisonics x y z dist time)))
-
- (let ((room 1))
- ;; remember current time and distance for next point
- (set! prev-time time)
- (set! prev-dist dist)
- ;; return number of rooms processed (?)
- room)))
-
- ;; Check to see if a segment changes radial direction:
- ;; a change in radial direction implies a change in
- ;; doppler shift that has to be reflected as a new
- ;; point in the rendered envelopes
- (define (change-direction xa ya za ta xb yb zb tb)
- (walk-all-rooms xa ya za ta)
- (unless (and (= xa xb)
- (= ya yb)
- (= za zb)
- (= ta tb))
- (let ((vals (nearest-point xa ya za xb yb zb 0 0 0)))
- (let ((xi (car vals))
- (yi (cadr vals))
- (zi (caddr vals)))
- (if (and (if (< xa xb) (<= xa xi xb) (<= xb xi xa))
- (if (< ya yb) (<= ya yi yb) (<= yb yi ya))
- (if (< za zb) (<= za zi zb) (<= zb zi za)))
- (walk-all-rooms xi yi zi
- (+ tb (* (- ta tb)
- (/ (distance (- xb xi) (- yb yi) (- zb zi))
- (distance (- xb xa) (- yb ya) (- zb za)))))))))))
-
- ;; Check to see if a segment intersects the inner sphere:
- ;; points inside are rendered differently so we need to
- ;; create additional envelope points in the boundaries
- (define (intersects-inside-radius xa ya za ta xb yb zb tb)
- (let* ((mag (distance (- xb xa) (- yb ya) (- zb za)))
- (vx (/ (- xb xa) mag))
- (vy (/ (- yb ya) mag))
- (vz (/ (- zb za) mag))
- (bsq (+ (* xa vx) (* ya vy) (* za vz)))
- (disc (let ((u (- (+ (* xa xa) (* ya ya) (* za za))
- (* inside-radius inside-radius))))
- (- (* bsq bsq) u)))
- (hit (>= disc 0.0)))
- (if (not hit)
- (change-direction xa ya za ta xb yb zb tb)
- ;; ray defined by two points hits sphere
- (let ((root (sqrt disc)))
- (let ((rin (- (+ bsq root)))
- (rout (- root bsq))
- (xi #f) (yi #f) (zi #f) (ti #f) (xo #f) (yo #f) (zo #f) (to #f))
- (if (> mag rin 0) ;(and (> rin 0) (< rin mag))
- ;; intersects entering sphere
- (begin
- (set! xi (+ xa (* vx rin)))
- (set! yi (+ ya (* vy rin)))
- (set! zi (+ za (* vz rin)))
- (set! ti (+ tb (* (- ta tb)
- (/ (distance (- xb xi) (- yb yi) (- zb zi))
- (distance (- xb xa) (- yb ya) (- zb za))))))))
- (if (and (> rout 0) (< (abs rout) mag))
- ;; intersects leaving sphere
- (begin
- (set! xo (+ xa (* vx rout)))
- (set! yo (+ ya (* vy rout)))
- (set! zo (+ za (* vz rout)))
- (set! to (+ tb (* (- ta tb)
- (/ (distance (- xb xo) (- yb yo) (- zb zo))
- (distance (- xb xa) (- yb ya) (- zb za))))))))
- (if xi
- (begin
- (change-direction xa ya za ta xi yi zi ti)
- (if xo
- (begin
- (change-direction xi yi zi ti xo yo zo to)
- (change-direction xo yo zo to xb yb zb tb))
- (change-direction xi yi zi ti xb yb zb tb)))
- (if xo
- (begin
- (change-direction xa ya za ta xo yo zo to)
- (change-direction xo yo zo to xb yb zb tb))
- (change-direction xa ya za ta xb yb zb tb))))))))
-
- ;; Recursively split segment if longer than minimum rendering distance:
- ;; otherwise long line segments that have changes in distance render
- ;; the amplitude envelope as a linear function that does not reflect
- ;; the chosen power function (1/d^n)
- (define (fminimum-segment-length xa ya za ta xb yb zb tb)
- (let ((dist (distance (- xb xa) (- yb ya) (- zb za))))
- (if (< dist minimum-segment-length)
- (intersects-inside-radius xa ya za ta xb yb zb tb)
- ;; interpolate a new point half way thorugh the segment
- (let* ((xi (/ (+ xa xb) 2))
- (yi (/ (+ ya yb) 2))
- (zi (/ (+ za zb) 2))
- (ti (+ tb (* (- ta tb)
- (/ (distance (- xb xi) (- yb yi) (- zb zi))
- (distance (- xb xa) (- yb ya) (- zb za)))))))
- (fminimum-segment-length xa ya za ta xi yi zi ti)
- (fminimum-segment-length xi yi zi ti xb yb zb tb)))))
-
- ;; returns the new duration of a sound after using an envelope for time-varying sampling-rate conversion
- ;; (from Bill's dsp.scm)
- (define (src-duration e)
- (let ((len (- (length e) 2)))
- (do ((all-x (- (e len) (e 0))) ; last x - first x
- (dur 0.0)
- (i 0 (+ i 2)))
- ((>= i len) dur)
- (let ((area (let ((x0 (e i))
- (x1 (e (+ i 2)))
- (y0 (e (+ i 1))) ; 1/x x points
- (y1 (e (+ i 3))))
- (if (< (abs (real-part (- y0 y1))) .0001)
- (/ (- x1 x0) (* y0 all-x))
- (/ (* (- (log y1) (log y0))
- (- x1 x0))
- (* (- y1 y0) all-x))))))
- (set! dur (+ dur (abs (real-part area))))))))
-
- ;; Loop for each pair of points in the position envelope and render them
- (if (and (pair? xpoints)
- (null? (cdr xpoints)))
- ;; static source (we should check if this is inside the inner radius?)
- (walk-all-rooms (car xpoints) (car ypoints) (car zpoints) (car tpoints))
-
- ;; moving source
- (do ((len (- (min (length xpoints) (length ypoints) (length zpoints) (length tpoints)) 1))
- (i 0 (+ i 1)))
- ((>= i len))
- (let ((xa (xpoints i))
- (ya (ypoints i))
- (za (zpoints i))
- (ta (tpoints i))
- (xb (xpoints (+ i 1)))
- (yb (ypoints (+ i 1)))
- (zb (zpoints (+ i 1)))
- (tb (tpoints (+ i 1))))
- (fminimum-segment-length xa ya za ta xb yb zb tb)
- (if (= i len)
- (walk-all-rooms xb yb zb tb)))))
-
- ;; create delay lines for output channels that need them
- (if speakers
- (let ((delays (speaker-config-delays speakers)))
- (do ((len (length delays))
- (channel 0 (+ 1 channel)))
- ((= channel len))
- (let ((delayo (delays channel)))
- (set! (out-delays channel) (and (not (= delayo 0.0))
- (make-delay (time->samples delayo))))
- (set! max-out-delay (max max-out-delay delayo))))))
-
- ;; end of the run according to the duration of the note
- ;; (set! end (time->samples duration))
- ;; start and end of the loop in samples
- (let (;; delay from the minimum distance to the listener
- (min-delay (dist->samples min-dist))
- ;; duration of sound at listener's position after doppler src
- ;; this does not work quite right but the error leads to a longer
- ;; run with zeroed samples at the end so it should be fine
- (real-dur (* duration (if (null? doppler) 1.0 (src-duration (reverse doppler)))))
- ;; minimum distance for unity gain calculation
- (min-dist-unity (max min-dist inside-radius))
- (run-beg (time->samples start-time)))
- (let ((run-end (floor (- (+ (time->samples (+ start-time (max duration real-dur)))
- (dist->samples last-dist)
- (time->samples max-out-delay))
- (if initial-delay 0.0 min-delay))))
- ;; sample at which signal first arrives to the listener
- (start (+ run-beg (dist->samples (- first-dist (if initial-delay 0.0 min-dist)))))
- ;; unity-gain gain scalers
- (unity-gain (* scaler
- (if (number? unity-gain-dist)
- (expt unity-gain-dist direct-power)
- (if unity-gain-dist
- 1.0
- (expt min-dist-unity direct-power)))))
- (unity-rev-gain (* scaler
- (if (number? unity-gain-dist)
- (expt unity-gain-dist reverb-power)
- (if unity-gain-dist ; defaults to #f above
- 1.0
- (expt min-dist-unity reverb-power)))))
- ;; unity-gain ambisonics gain scalers
- (amb-unity-gain (* scaler
- (if (number? unity-gain-dist)
- (expt unity-gain-dist direct-power)
- (if unity-gain-dist
- 1.0
- (expt min-dist-unity direct-power)))))
- (amb-unity-rev-gain (* scaler
- (if (number? unity-gain-dist)
- (expt unity-gain-dist reverb-power)
- (if unity-gain-dist ; defaults to #f above
- 1.0
- (expt min-dist-unity reverb-power))))))
-
- ;; XXX hack!! this should be intercepted in the calling code, no 0 duration please...
- (if (<= real-dur 0.0)
- (begin
- (format () ";;; error: resetting real duration to 0.1 (was ~A)~%" real-dur)
- (set! real-dur 0.1)))
-
- (let ((gen (make-move-sound
- (list
- ;; :start
- start
- ;; :end
- (time->samples (+ start-time (max duration real-dur)))
- ;; :out-channels
- (if speakers (speaker-config-number speakers) out-channels)
- ;; :rev-channels
- rev-channels
- ;; :path
- (make-delay delay-hack :max-size (max 1 (+ (ceiling (dist->samples max-dist)) delay-hack)))
- ;; :delay
- (make-env (reverse dly)
- :offset (if initial-delay 0.0 (- min-delay))
- :duration real-dur)
- ;; :rev
- (make-env (if (number? reverb-amount) ; as opposed to an envelope I guess
- (list 0 reverb-amount 1 reverb-amount)
- reverb-amount)
- :duration real-dur)
- ;; :out-delays
- out-delays
- ;; :gains
- (do ((v (make-vector out-channels))
- (i 0 (+ i 1)))
- ((= i out-channels) v)
- (set! (v i) (make-env (reverse (channel-gains i))
- :scaler (if (= render-using ambisonics) amb-unity-gain unity-gain)
- :duration real-dur)))
- ;; :rev-gains
- (and (> rev-channels 0)
- (do ((v (make-vector rev-channels))
- (i 0 (+ i 1)))
- ((= i rev-channels) v)
- (set! (v i) (make-env (reverse (channel-rev-gains i))
- :scaler (if (= render-using ambisonics) amb-unity-rev-gain unity-rev-gain)
- :duration real-dur))))
- ;; :out-map
- (if speakers
- (speaker-config-map speakers)
- (do ((v (make-vector out-channels))
- (i 0 (+ i 1)))
- ((= i out-channels) v)
- (set! (v i) i))))
- *output*
- *reverb*)))
- (list gen
- ;; return start and end samples for the run loop
- run-beg
- run-end))))))
-
- ;; (with-sound(:channels 6 :play #f :statistics #t) (sinewave 0 10 440 0.5 :path (make-path '((-10 10) (0.5 0.5) (10 10)) :error 0.001)))
- ;;
- ;; (with-sound(:statistics #t :channels 4 :reverb-channels 4 :reverb freeverb :decay-time 3)
- ;; (move 0 "/usr/ccrma/snd/nando/sounds/kitchen/bowl/small-medium-large-1.snd"
- ;; :paths (list (make-spiral-path :start-angle 0 :turns 2.5)
- ;; (make-spiral-path :start-angle 180 :turns 3.5))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Run macro to localize samples
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (define dlocsig move-sound)
-
-
- #|
-
- ;; (define hi (make-path '((-10 10) (0.5 0.5) (10 10)) :3d #f :error 0.001))
- ;; (make-dlocsig 0 1.0 :out-channels 2 :rev-channels 0 :path (make-path '((-10 10) (0.5 0.5) (10 10)) :3d #f))
-
- (if (provided? 'snd)
- (require snd-ws.scm)
- (require sndlib-ws.scm))
-
- (define* (sinewave start-time duration freq amp
- (amp-env '(0 1 1 1))
- (path (make-path :path '(-10 10 0 5 10 10))))
- (let* ((vals (make-dlocsig :start-time start-time
- :duration duration
- :path path))
- (dloc (car vals))
- (beg (cadr vals))
- (end (caddr vals)))
- (let ((osc (make-oscil :frequency freq))
- (aenv (make-env :envelope amp-env :scaler amp :duration duration)))
- (do ((i beg (+ i 1)))
- ((= i end))
- (dlocsig dloc i (* (env aenv) (oscil osc)))))))
-
- (with-sound (:channels 2) (sinewave 0 1.0 440 .5 :path (make-path '((-10 10) (0.5 0.5) (10 10)) :3d #f)))
-
- |#
-
|