[Audacity-nyquist] new version of Chris's Dynamic Compressor (fwd)

1 Message Forum Options Options
Embed this topic
Permalink
David R. Sky
[Audacity-nyquist] new version of Chris's Dynamic Compressor (fwd)
Reply Threaded MoreMore options
Print post
Permalink
---------- Forwarded message ----------
Date: Mon, 27 Oct 2008 23:31:55 -0500
From: Chris Capel <pdf23ds@...>
Reply-To: Discussion of developing Nyquist plug-ins for Audacity
     <audacity-nyquist@...>
To: Discussion of developing Nyquist plug-ins for Audacity
     <audacity-nyquist@...>
Subject: [Audacity-nyquist] new version of Chris's Dynamic Compressor

I have released a new version of my dynamic compressor plugin. This
version implements a slightly different algorithm that applies a more
variable amount of gain so it can handle a wider variety of input
levels (especially those that change loudness more dramatically) more
transparently. The source has comments explaining the algorithm in
more detail. (I abuse the word "paraboloid"!)

http://pdf23ds.net/software/dynamic-compressor/

This version has a couple of interesting features. One, it has much
better memory usage on large files than the last version, because I
was able to implement the algorithm in a fully lazy fashion thanks to
some help from Roger Dannenberg. Second is that it uses a small group
of methods to buffer the input sound to provide for arbitrarily long
lookahead without wasting memory. Here's the code I used to do that:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Sound buffer object
;;
;; The sound buffer takes a sound and a buffer size and returns any random
;; sample, keeping everything needed in memory. _set-buffer-pos_ is used to
;; tell it you're done with samples before that offset so it can discard the
;; earlier samples.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun make-snd-buffer (sound buffer-size)
   (list 0 ;position
         sound
         buffer-size
         nil ;; list of buffer arrays
         ))

(defun get-buffer-sample (buf samp-num)
   (let ((pos (first buf))
         (sound (second buf))
         (size (third buf))
         (bufs (fourth buf)))
     (let ((buffer-num (truncate (/ (- samp-num pos) size))))
       (while (>= buffer-num (length bufs))
         (setf (nth 3 buf)
               (nconc bufs (list (my-snd-fetch-array sound size))))
         (setf bufs (fourth buf)))
       (let* ((buf-vec (nth buffer-num bufs))
              (sample (aref buf-vec (rem (- samp-num pos) size))))
         (if (< sample -1000)
             nil
             sample)))))

(defun my-snd-fetch-array (snds size)
   (if (arrayp snds)
       (let (buffers)
         (dotimes (x (length snds))
           (push (snd-fetch-array (aref snds x) size size)
                 buffers))
         (dotimes (i (length (first buffers)))
           (setf (aref (first buffers) i)
                 (apply #'max (mapcar (lambda (x)
                                        (linear-to-db (abs (aref x i))))
                                      buffers))))
         (first buffers))
       (let ((val (snd-fetch snds)))
         (when (not (null val))
           (linear-to-db (abs val))))))

(defun set-buffer-pos (buf pos)
   "tell the buffer what position you're currently at, promising you won't need
   any samples before pos, so that the buffer can discard earlier samples."
   (let ((size (third buf)))
     (while (< (+ (first buf) size) pos)
       (setf (nth 3 buf) (rest (fourth buf))
             (nth 0 buf) (+ (first buf) size)))))

I'm sure my mail client will mess up all the spaces. You can download
the source from my website as well. MY-SND-FETCH-ARRAY implements
snd-fetch in a multi-channel fashion. It has a bit of code in there
that you might want to change depending on your application, where it
does (linear-to-db (abs x)) in two places.

The other part to this, the part that allows me to calculate just
about anything I want to with a sound object, is due to this magic bit
of code here:

;; create a new class with instance variables input iv1 iv2 iv3
(setf custom-class (send class :new '(input iv1 iv2 iv3)))

;; create the object initialization method, takes a sound as input
(send custom-class :answer :isnew '(snd)
  '((setf input snd)
   (setf iv1 nil iv2 nil iv3 nil)))

;; define method to obtain next sample
(send custom-class :answer :next '()
  '((setf iv1 (snd-fetch input)) ; read next sample of input
   iv1)) ; in this example, just pass input through to resulting sound

;; create a functional interface
;;
(defun custom-operation (snd)
  (snd-from-object (snd-t0 snd) (snd-srate snd) (send custom-class :new snd)))

You just fill in the :next message handler with whatever code to
return a float, and you've created a sound! How cool. Turns out this
*is* documented, slightly.

Chris Capel
--
"What is it like to be a bat? What is it like to bat a bee? What is it
like to be a bee being batted? What is it like to be a batted bee?"
-- The Mind's I (Hofstadter, Dennet)

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Audacity-nyquist mailing list
Audacity-nyquist@...
https://lists.sourceforge.net/lists/listinfo/audacity-nyquist


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
--
Mailing list: Audacity-users@...
To UNSUBSCRIBE, use the form at the bottom of this web page:
https://lists.sourceforge.net/lists/listinfo/audacity-users