/rsound/filter.rkt

https://github.com/jbclements/RSound · Racket · 223 lines · 162 code · 24 blank · 37 comment · 29 complexity · 85c41b710358ab39d4545370324cd703 MD5 · raw file

  1. #lang racket/base
  2. (require (only-in racket pi)
  3. racket/match
  4. "rsound.rkt"
  5. "network.rkt"
  6. "filter-typed.rkt"
  7. "reverb.rkt"
  8. racket/flonum)
  9. (provide fir-filter
  10. iir-filter
  11. dynamic-lti-signal
  12. lpf-sig
  13. lpf/dynamic
  14. (all-from-out "reverb.rkt"))
  15. (define i (sqrt -1))
  16. (define twopi (* 2 pi))
  17. ;; poly : a transfer function
  18. ;; coefficients : a list of coefficients to use in a transfer function
  19. ;; roots / zeros : places where the transfer function is zero
  20. ;; poles : places where the transfer function is infinite
  21. ;; FIR filters
  22. ;; fir-filter : (listof (list/c delay amplitude)) -> 1/1/network
  23. ;; filter the input signal using the delay values and amplitudes given for an FIR filter
  24. (define (fir-filter params)
  25. (match params
  26. [`((,delays ,amplitudes) ...)
  27. (unless (andmap (lambda (d) (and (exact-integer? d) (<= 0 d))) delays)
  28. (raise-type-error 'fir-filter "exact integer delays greater than zero" 0 params))
  29. (unless (andmap real? amplitudes)
  30. (raise-type-error 'fir-filter "real number amplitudes" 0 params))
  31. ;; enough to hold delayed and current, rounded up to next power of 2:
  32. [define max-delay (up-to-power-of-two (+ 1 (apply max delays)))]
  33. ;; set up buffer to delay the signal
  34. [define delay-buf (make-vector max-delay 0.0)]
  35. [define next-idx 0]
  36. (define (wraparound-add1 idx)
  37. (define next (add1 idx))
  38. (cond [(<= max-delay next) 0]
  39. [else next]))
  40. (lambda (this-val)
  41. (vector-set! delay-buf next-idx this-val)
  42. (define result
  43. (for/fold ([sum 0])
  44. ([d (in-list delays)]
  45. [a (in-list amplitudes)])
  46. (+ sum (* a (vector-ref delay-buf (modulo (- next-idx d) max-delay))))))
  47. (set! next-idx (wraparound-add1 next-idx))
  48. result)]
  49. [other (raise-type-error 'fir-filter "(listof (list number number))" 0 params)]))
  50. ;; IIR filters
  51. ;; iir-filter : (listof (list/c delay amplitude)) -> 1/1/networkt
  52. ;; filter the input signal using the delay values and amplitudes given for an IIR filter
  53. ;; the only difference here is that we put the final result in the delay line, rather than
  54. ;; the input signal.
  55. (define (iir-filter params)
  56. (match params
  57. [`((,delays ,amplitudes) ...)
  58. (unless (andmap (lambda (d) (and (exact-integer? d) (< 0 d))) delays)
  59. (raise-type-error 'iir-filter "exact integer delays greater than zero" 0 params))
  60. (unless (andmap real? amplitudes)
  61. (raise-type-error 'iir-filter "real number amplitudes" 0 params))
  62. (let* ([max-delay (up-to-power-of-two (+ 1 (apply max delays)))]
  63. ;; set up buffer to delay the signal
  64. [delay-buf (make-vector max-delay 0.0)]
  65. [next-idx 0])
  66. (lambda (this-val)
  67. ;; could be a lot faster:
  68. (let* ([new-val (for/fold ([sum this-val])
  69. ([d (in-list delays)]
  70. [a (in-list amplitudes)])
  71. ;; FIXME: get rid of this modulo:
  72. (+ sum (* a (vector-ref delay-buf
  73. (modulo (- next-idx d) max-delay)))))])
  74. (begin0
  75. new-val
  76. (vector-set! delay-buf next-idx new-val)
  77. (set! next-idx (modulo (add1 next-idx) max-delay))))))]
  78. [other (raise-type-error 'iir-filter "(listof (list number number))" 0 params)]))
  79. ;; lti-filter : rsound (listof (list/c number? number?)) (listof (list/c number? number?)) -> rsound
  80. ;; given coefficients for an FIR and an IIR filter, apply
  81. ;; the given filter to the sound.
  82. #;(define (lti-filter gain fir-coefficients iir-coefficients)
  83. (unless (real? gain)
  84. (raise-type-error 'lti-filter "real number" 0 gain fir-coefficients
  85. iir-coefficients))
  86. (unless (and (list? fir-coefficients)
  87. (andmap (lambda (x) (and (list? x)
  88. (= (length x) 2)
  89. (nonnegative-integer? (car x))
  90. (real? (cadr x))))
  91. fir-coefficients))
  92. (raise-type-error 'lti-filter "list of delays and coefficients" 1
  93. snd fir-coefficients iir-coefficients))
  94. (unless (and (list? iir-coefficients)
  95. (andmap (lambda (x) (and (list? x)
  96. (= (length x) 2)
  97. (nonnegative-integer? (first x))
  98. (real? (second x))))
  99. iir-coefficients))
  100. (raise-type-error 'lti-filter "list of delays and coefficients" 2
  101. snd fir-coefficients iir-coefficients))
  102. ;; must normalize, include gain...
  103. (define the-fir (fir-filter fir-coefficients))
  104. (define the-iir (iir-filter iir-coefficients))
  105. (signals->rsound (rs-frames snd)
  106. (the-iir (the-fir (rsound->signal/left snd)))
  107. (the-iir (the-fir (rsound->signal/right snd)))))
  108. (define filter-param-update-interval 32)
  109. ;; we want to be able to change the filter dynamically...
  110. ;; (nat nat -> 2/1/network)
  111. ;; accepts a tap-length, produces
  112. ;; a network with two inputs: the parameter signal and the input
  113. ;; signal. The parameter signal must produce a vector of three
  114. ;; things: the input tap vector, the output tap vector, and the gain.
  115. ;; sadly, there's going to be some inevitable checking of vector
  116. ;; bounds here.
  117. ;; NB: requires input & output taps to be of the same length. This
  118. ;; is pretty normal.
  119. ;; this whole thing could be *way* more optimized.
  120. (define (dynamic-lti-signal tap-len)
  121. (unless (< 0 tap-len)
  122. (raise-argument-error 'dynamic-lti-signal "number greater than zero" 0 tap-len))
  123. (define saved-input-buf (make-flvector tap-len))
  124. (define saved-output-buf (make-flvector tap-len))
  125. (define (wraparound idx)
  126. (cond [(<= tap-len idx) 0]
  127. [else idx]))
  128. (define next-idx 0)
  129. (lambda (fir-terms iir-terms gain this-val)
  130. ;; don't want to do this check every time...
  131. #;(unless (and (flvector? fir-terms)
  132. (= (flvector-length fir-terms)
  133. input-tap-len))
  134. (error 'dynamic-lti-signal
  135. "expected vector of length ~s for fir-terms, got vector of length ~s"
  136. input-tap-len (flvector-length fir-terms)))
  137. ;; don't want to do this check every time....
  138. #;(unless (and (flvector? iir-terms)
  139. (= (flvector-length iir-terms)
  140. output-tap-len))
  141. (error 'dynamic-lti-signal
  142. "expected vector of length ~s for iir-terms, got vector of length ~s"
  143. output-tap-len (flvector-length iir-terms)))
  144. (define fir-sum
  145. (for/fold ([sum 0.0])
  146. ([i (in-range tap-len)])
  147. (fl+ sum
  148. (fl* (flvector-ref fir-terms i)
  149. (flvector-ref saved-input-buf
  150. (modulo (- next-idx i 1) tap-len))))))
  151. (define iir-sum
  152. (for/fold ([sum 0.0])
  153. ([i (in-range tap-len)])
  154. (fl+ sum
  155. (fl* (flvector-ref iir-terms i)
  156. (flvector-ref saved-output-buf
  157. (modulo (- next-idx i 1) tap-len))))))
  158. (define next-val (fl* gain (exact->inexact this-val)))
  159. (flvector-set! saved-input-buf next-idx next-val)
  160. (define output-val (fl+ next-val (fl+ fir-sum iir-sum)))
  161. (flvector-set! saved-output-buf next-idx output-val)
  162. (set! next-idx (wraparound (add1 next-idx)))
  163. output-val))
  164. (define max-scale-val 3.0)
  165. (define min-scale-val 0.00)
  166. (define perceptible-interval 0.01)
  167. (define coefficient-table (make-vector (inexact->exact
  168. (floor
  169. (/ (- max-scale-val
  170. min-scale-val)
  171. perceptible-interval)))
  172. #f))
  173. ;; A network that maps scale values into fir/iir vectors
  174. (define lpf-sig
  175. (lambda (theta)
  176. (when (not (<= min-scale-val theta max-scale-val))
  177. (error 'dynamic-lpf "scale value ~s not between ~s and ~s"
  178. theta
  179. min-scale-val
  180. max-scale-val))
  181. (define table-index (inexact->exact
  182. (round
  183. (/ (- theta min-scale-val)
  184. perceptible-interval))))
  185. (define results
  186. (match (vector-ref coefficient-table table-index)
  187. [#f (define result-vec (lpf-tap-vectors theta))
  188. (vector-set! coefficient-table table-index result-vec)
  189. result-vec]
  190. [other other]))
  191. (values (vector-ref results 0)
  192. (vector-ref results 1)
  193. (vector-ref results 2))))
  194. ;; dynamic low-pass filter: the first argument is a signal that controls
  195. ;; the filter cutoff, the second is the signal being filtered.
  196. (define lpf/dynamic
  197. (network (lpf-control audio-sig)
  198. [(fir-terms iir-terms gain) <= lpf-sig lpf-control]
  199. [out <= (dynamic-lti-signal 4) fir-terms iir-terms gain audio-sig]))