[CM] Chaining effects together
Jeremy Shaw
jeremy.shaw@lindows.com
Tue, 16 Jul 2002 12:01:05 -0700
Hello,
Does anyone have a good scheme for chaining filters (and effects)
together?
Here is some pseudo-code that won't work, but shows what I am
trying to figure out how to do:
(with-sound () (delay (:delay-time 1.0 :regen 0.9 :mix .5)
(low-pass-filter (:fc 500 :lfo-amt 200 :lfo-freq 0.3)
(saw 0 1 200 0.5)
(saw 1 1 400 0.5)
(saw 2 1 800 0.5)
(saw 3 1 400 0.5)
(saw 4 1 200 0.5))))
I started implementing a scheme, where "saw" writes its output to
*reverb*, and then delay reads from *reverb* and writes to *output*.
My current implementation has several problems:
(1) saw+delay works, saw+filter works, but saw+filter+delay fails
because filter is writing to *output* while delay is reading from
*reverb*.
(2) If I want just a plain saw wave, I have to use a filter that does
nothing but copy *reverb* to *output*.
(3) Makes real-time (aka with-dac) impractical.
I think I can address (1) and (2), but I am wondering if there is a
better way...
Thanks!
Jeremy Shaw.
Here the above mentioned scheme.
;;; -*- syntax: common-lisp; base: 10; mode: lisp -*-
(in-package :clm)
;; WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
;; WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
;;
;; This code exhibits a scheme for chaining effects together that DOES NOT WORK.
;;
;; WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
;; WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
#|
;; Play the saw wave, then play it with delay added (this works)
(with-sound ()
(mix (with-sound (:reverb unity :revfile "plain.rev" :output "plain")
(good-saw 0 1 200 0.3)))
(mix (with-sound (:reverb e-delay :reverb-data (:delay-time 0.5) :output "delay")
(good-saw 1.5 0.2 200 0.3))))
;; This does not work, it tries to play a saw with filtering and delay.
;; The delay expects to read its input from *reverb* but the filter is writing its output to *output*
;;
(with-sound (:reverb e-delay :reverb-data (:delay-time 0.5) :output "delay")
(with-sound (:reverb ch-low-pass :reverb-data (:fc 400 :qc 0.3) :revfile "filter.rev" :output "filter")
(good-saw 1 1 200 0.05)))
|#
;; Generate a band limited sawtooth-wave
;;
;; Based on ideas presented in "Alias-Free Digital Synthesis of Classic Analog Waveforms"
;; by Tim Stiltin
;; http://www-ccrma.stanford.edu/~stilti/papers/Welcome.html
;;
;; NOTES: This still needs work. At the very least, I think I need to adjust the DC-offset
(definstrument good-saw (start-time duration frequency amplitude
&optional (a0 1.0) (b1 -0.98) (amp-env '(0 1 99 1 100 0)))
(multiple-value-bind (beg end) (times->samples start-time duration)
(let ((s (make-sum-of-cosines :cosines (floor (/ *srate* (* 2 frequency)))
:frequency frequency))
(f-lowpass (make-one-pole :a0 a0
:b1 b1))
(amp (make-env :envelope amp-env
:scaler amplitude
:duration duration)))
(run
(loop for i from beg below end do
(outa i (* (env amp) (one-pole f-lowpass (sum-of-cosines s))) *reverb*))))))
;; A 2 pole low-pass filter
;; Sometimes called the Chamberlain filter a State Variable Filter ??
;;
;; "Filters, Delays, Modulations and Demodulations: A tutorial"
;; by Dutilleux, Pierre
;; http://www.iua.upf.es/dafx98/papers/
;;
;; Also see: http://www.cen.uiuc.edu/~ece320/handouts/chamberlin.ps
;;
;; NOTE: This filter does not noramilze the output and is generally way to loud
(definstrument ch-low-pass (start-time duration &key (fc 500) (qc 1.0))
(multiple-value-bind (beg end) (times->samples start-time duration)
(let* ((f (* 2 (sin (/ (* 3.1415926535 fc) *srate*))))
(b1 (* -1 (- 2 (* f qc) (* f f))))
(b2 (- 1 (* f qc)))
(a0 (+ 1 b1 b2))
(f1 (make-two-pole a0 b1 b2))
(f2 (make-two-pole a0 b1 b2))
(f3 (make-two-pole a0 b1 b2))
(f4 (make-two-pole a0 b1 b2)))
(run
(progn
(loop for i from beg below end do
(outa i (two-pole f1 (two-pole f2 (two-pole f3 (two-pole f4 (ina i *reverb*))))))))))))
(definstrument e-delay (start-time duration &key (delay-time 0.5) (decay-time 10) (regen 0.8))
(multiple-value-bind (beg end) (times->samples start-time duration)
(let ((d (make-delay (* delay-time 44100)))
(b (make-env :envelope '(0 1 75 1 100 0)
:scaler 1.0
:duration decay-time)))
(run
(progn
(loop for i from beg below end do
(outa i (+ (ina i *reverb*) (delay d (* regen (+ (tap d) (ina i *reverb*)))))))
(loop for i from end to (+ end (* decay-time *srate*)) do
(outa i (* (env b) (delay d (* regen (tap d)))))))))))
;; Read from *reverb* stream and send it to the *output* stream unmodified.
;;
;; Unity is probably not the best name, but I can't remember the name I really want.
(definstrument unity (start-time duration)
(multiple-value-bind (beg end) (times->samples start-time duration)
(run
(loop for i from beg below end do
(outa i (ina i *reverb*))))))