Expander

Hi,

I have searched a bit but could not find any Expander plugin written in Nyquist.

An expander has been adressed in some discussion (some dating back as far as 2010) but it looks like no Expander has been made.

Let me introduce myself, as far as it is necessary for this thread: I am novice, both to compressors/expanders and Nyquist. I’ve just created my first simple plugin for personal use (under Steve’s tutelage). What I am trying to do? Quite a lot of recent music is heavily compressed and for my own listening pleasure I would like to expand them to some degree but do that in a somewhat automated way using data coming from diverse analyses (like RMS and peak info).

What I have found on this forum:

Soft Clipping Limiter from back in 2011 - originally called Peak Limiter / Expander, finally got rid of the expander. But the final version of the Peak Processor looks promising except that it clips anything above 1.0 (which it should not, as far as I’m concerned).

Square Roots and If-Thens from 2010 - it’s soft-knee expander seemed to shift some audio data but didn’t quite work as expected.

In September 2017 Steve explained the use of the (gate…) function to create an expander in the thread Nyquist code with the opposite of the Compressor effect but there was no follow-up.

Then there was some talk about using Dynamic Mirror as an expander but as far I can tell it mirrors some reference track only: Envelope follower / ducker from October 2017.

The oldest Nyquist plugin wasn’t even found on this Audacity forum but apparently its predecessor: Nonlinear Compressor/Limiter/Expander from 2008 by Igor Chernenko.

To me Steve’s Peak Processor is probably the best ticket so far.

;nyquist plug-in
;version 1
;type process
;categories "http://lv2plug.in/ns/lv2core/#DynamicsPlugin"
;name "Peak Processor..."
;action "processing peaks..."
;info "Peak Limiter / Expander.\nBy Steve Daulton (www.easyspacepro.com). Released under GPL v2.\n"

;; Version 1.1
;; peakprocess.ny by Steve Daulton, January 2011
;; Released under terms of the GNU General Public License version 2
;; http://www.gnu.org/copyleft/gpl.html

;control thresh "Threshold" real "linear" 0.5 0 1.0
;control ratio "Ratio [Compress]" real "[Expand]" 0 -20 20

(setq ratio (float ratio))

(defun compress (s-in val)
   (let ((pos (s-max 0 s-in))
         (neg (mult -1 (s-min 0 s-in))))
   (sim
      (mult (/ val)(sum -1 (s-exp (mult val pos))))
      (mult (/ -1 val)(sum -1 (snd-exp (mult val neg)))))))

(defun expansion (s-in val)
   (let ((pos (s-max 0 s-in))
         (neg (mult -1 (s-min 0 s-in)))
         (val (mult -1 val)))
   (sim
      (mult (/ val)(s-log (sum 1 (mult val pos))))
      (mult (/ -1 val)(s-log (sum 1 (mult val neg)))))))


(defun process (s-in)
  (let* ((nthresh (* -1 thresh))
      (tops (s-max s-in thresh))
      (mids (s-max (s-min s-in thresh) nthresh))
      (bottoms (s-min s-in nthresh)))
    (if (< ratio 0)
      (sim mids 
        (compress (sum (mult -1 thresh) tops) ratio)
        (compress (sum thresh bottoms) ratio))
    (if (> ratio 0)
    (progn
      ;; Prevent inf and nan sample values
      (setq tops (s-min tops (sum thresh -0.0000001 (/ ratio))))
      (setq bottoms (s-max bottoms (mult -1 (sum thresh -0.0000001 (/ ratio)))))
      (clip 
        (sim mids
          (expansion (sum (mult -1 thresh) tops) ratio)
          (expansion (sum thresh bottoms) ratio))1.0))
      (format nil "Ratio at zero.\nNothing to do.")))))

(multichan-expand #'process s)

Or the gate function:

(gate gatefollow lookahead risetime falltime  floor threshold)

Or as it is defined in the Github repository:

(defun gate (sound lookahead risetime falltime floor threshold)
    (cond ((< lookahead risetime)
           (break "lookahead must be greater than risetime in GATE function"))
          ((or (< risetime 0) (< falltime 0) (< floor 0))
           (break "risetime, falltime, and floor must all be positive in GATE function"))
          (t
           (let ((s
              (snd-gate (seq (cue sound) (abs-env (s-rest lookahead)))
                    lookahead risetime falltime floor threshold)))
             (snd-xform s (snd-srate s) (snd-t0 sound) 
            (+ (snd-t0 sound) lookahead) MAX-STOP-TIME 1.0)))))

Probably Igor’s expander is worth a look too. Has anyone else some other suggestions as to where this expander idea has been discussed?

Yes, Steve’s Dynamic-Mirror plugin will expand the dynamic range when used as a envelope-follower




Alternatively there is a free real-time* expander plugin called couture, (Windows only).

(* Dynamic-Mirror plugin cannot be adjusted in real-time, and does not have different rise & fall times).

Thanks, Trebor, but I was looking for a Nyquist expander, so couture (and others that I have used) would probably not work as I want to integrate it in a Nyquist plugin (unless you can call it from a Nyquist plugin and reuse its processed audio on a multiple track selection).

As for the Dynamic Mirror - it doesn’t seem to work on multiple tracks, it mirrored the envelope of the first selected track to the other tracks. (Or I am doing it wrong!?)

I think Steve’s original Peak Processor plugin minus the clip function does provide the simple expander I was looking for:

;nyquist plug-in
;version 3
;type process
;name "Peak Processor..."
;action "processing peaks..."
;release 1.1.1
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"
;info "Peak Limiter / Expander.\nBy Steve Daulton (www.easyspacepro.com). Released under GPL v2.\n"

;; Version 1.1
;; peakprocess.ny by Steve Daulton, January 2011
;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
;; Version 1.1.1
;; peakprocess.ny by HBB Think Tank, September 2019, removing the clip function, version updated from 1 to 3
;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

;control thresh "Threshold" real "linear" 0.5 0 1.0
;control ratio "Ratio [Compress]" real "[Expand]" 0 -20 20

(setq ratio (float ratio))

(defun compress (s-in val)
   (let ((pos (s-max 0 s-in))
         (neg (mult -1 (s-min 0 s-in))))
   (sim
      (mult (/ val)(sum -1 (s-exp (mult val pos))))
      (mult (/ -1 val)(sum -1 (snd-exp (mult val neg)))))))

(defun expansion (s-in val)
   (let ((pos (s-max 0 s-in))
         (neg (mult -1 (s-min 0 s-in)))
         (val (mult -1 val)))
   (sim
      (mult (/ val)(s-log (sum 1 (mult val pos))))
      (mult (/ -1 val)(s-log (sum 1 (mult val neg)))))))


(defun process (s-in)
  (let* ((nthresh (* -1 thresh))
      (tops (s-max s-in thresh))
      (mids (s-max (s-min s-in thresh) nthresh))
      (bottoms (s-min s-in nthresh)))
    (if (< ratio 0)
      (sim mids 
        (compress (sum (mult -1 thresh) tops) ratio)
        (compress (sum thresh bottoms) ratio))
    (if (> ratio 0)
    (progn
      ;; Prevent inf and nan sample values
      (setq tops (s-min tops (sum thresh -0.0000001 (/ ratio))))
      (setq bottoms (s-max bottoms (mult -1 (sum thresh -0.0000001 (/ ratio)))))
      (sim mids
          (expansion (sum (mult -1 thresh) tops) ratio)
          (expansion (sum thresh bottoms) ratio)))
      (format nil "Ratio at zero.\nNothing to do.")))))

(multichan-expand #'process s)

And here updated to version 4 syntax:

;nyquist plug-in
;version 4
;type process
;name "Peak Processor..."
;action "processing peaks..."
;release 1.1.2
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"
;info "Peak Limiter / Expander.\nBy Steve Daulton (www.easyspacepro.com). Released under GPL v2.\n"

;; Version 1.1
;; peakprocess.ny by Steve Daulton, January 2011
;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
;; Version 1.1.2
;; peakprocess.ny by HBB Think Tank, September 2019, removing the clip function, version updated from 1 to 4
;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

;control thresh "Threshold" float "linear" 0.5 0 1.0
;control ratio "Ratio" float "between -20 and +20" 0 -20 20
;control text "Compress on negative ratio, Expand on positiv ratio"

(defun compress (sig val)
   (let ((pos (s-max 0 sig))
         (neg (mult -1 (s-min 0 sig))))
   (sim
      (mult (/ val)(sum -1 (s-exp (mult val pos))))
      (mult (/ -1 val)(sum -1 (snd-exp (mult val neg)))))))

(defun expansion (sig val)
   (let ((pos (s-max 0 sig))
         (neg (mult -1 (s-min 0 sig)))
         (val (mult -1 val)))
   (sim
      (mult (/ val)(s-log (sum 1 (mult val pos))))
      (mult (/ -1 val)(s-log (sum 1 (mult val neg)))))))


(defun process (sig)
  (let* ((nthresh (* -1 thresh))
      (tops (s-max sig thresh))
      (mids (s-max (s-min sig thresh) nthresh))
      (bottoms (s-min sig nthresh)))
    (cond
      ((< ratio 0)
        (sim mids 
          (compress (sum (mult -1 thresh) tops) ratio)
          (compress (sum thresh bottoms) ratio)))
      ((> ratio 0)
        (progn
        ;; Prevent inf and nan sample values
        (setq tops (s-min tops (sum thresh -0.0000001 (/ ratio))))
        (setq bottoms (s-max bottoms (mult -1 (sum thresh -0.0000001 (/ ratio)))))
        (sim mids
            (expansion (sum (mult -1 thresh) tops) ratio)
            (expansion (sum thresh bottoms) ratio))))
      ((= ratio 0)
        (format nil "Ratio at zero.~%~
        Nothing to do.")))))

(multichan-expand #'process *track*)

The only part not working is the final part showing the nothing to do info. But it didn’t work for me even in the original code v1.1 from Steve.

To expand with Dynamic-mirror you need to adjust the the “control range max”* so it only affects (attenuates) the quiet parts. My suggestion is to set “control range max” to (approximately) half the RMS volume of the waveform you’re expanding. Must be set to “follow peak”, & I suggest a “time resolution” of 0.01 sec.

[* “control range min” should always be 0 if you are expanding ].

There’s more than one version of Dynamic-mirror.
IIRC the latest one does not require you to create a duplicate track/waveform :
if you only select one track it acts on itself.


Peak Processor could be set to expand the dynamic range, but it will change the harmonic content, (i.e. distort). whereas dynamic mirror & couture don’t do that as they operate over a longer timescale.

But there’s the kicker - I would like to expand a multi-track selection and each track on its own. But I’ll look at the code, maybe I can create one that allows multiple tracks that alway follows its own peaks.

Should be “follows its own envelope
Dynamic Mirror does not respond quickly enough to act on individual peaks,
which is a good thing as that minimizes harmonic-distortion.

Well, I have continued with my own project, let’s call it the DR Processor.

Unfortunately it doesn’t do anything right now, yet the Nyquist Prompt does not return anything. Is there a way to debug step by step?

Audacity has a Clip Fix plug-in that might “restore” some peaks. Izotope Ozone (not free) can expand, and I’m sure you can find VST expander plug-ins (which may or may-not work with Audacity.) …It’s rarely used in audio production except for a noisgate which is a special kind of downward expansion.

GoldWave (a $50 USD commercial “competitor” to Audacity) has a compressor/expander effect with variable ratio, threshold, attack, and release. There is a fully-functional free trial, and if try it be aware that their definitions of “compression” and “expansion” are sometimes messed-up. For example there is a “boost loud parts” preset and it works but it shows “compression”.

What I am trying to do? Quite a lot of recent music is heavily compressed and for my own listening pleasure I would like to expand them to some degree but do that in a somewhat automated way using data coming from diverse analyses (like RMS and peak info).

You may be able to make an improvement but it’s an “effect”. You can’t reverse or remove the compression… In theory you can reverse compression if you know the settings/parameters, but in practice you don’t.

Plus…

  • Limiting (and clipping) can’t be (accurately) reversed because it’s impossible to know the original waveform height or shape.
  • Compression/limiting may have been applied during mixing and again during mastering (with different-unknown settings).
  • Multiband compression may have been used (even more unknown parameters).
  • The individual tracks may have been compressed/limited before mixing.

…Back in the analog days I made an expander with an NE570 chip. It “worked” and it was a fun novelty but it was impractical because the expansion is fixed at 2:1 across the full dynamic range and that’s WAY too much. With what I know now it would be “simple” to set a threshold and limit the range but at the time I didn’t have the electronics knowledge. And, I haven’t played-around with expansion since.

Unfortunately it doesn’t do anything right now, yet the Nyquist Prompt does not return anything. Is there a way to debug step by step?

I’ve never used Nyquist (or done any audio programming) but I have done programming on-and-off in various languages over the years. The biggest trick is to write and test your code in small sections. The biggest mistake beginners make is trying to write the whole program at once.

If you are a beginner that means writing one or two lines of code at a time, or whatever minimum that “makes sense”. This isn’t always easy because you usually can’t just start at the top and work down. The code has to “make sense” and it has to “do something useful” that can be tested. Sometimes adding one line of code won’t work at all, especially with compiled languages. (Nyquist isn’t compiled so it doesn’t have to “make sense” to a compiler but the syntax still has to be correct.)

Another helpful thing is to display messages about what the program is doing (something like “now amplifying”, etc.) or display variables or the results of calculations, etc. I don’t know how to do that in Nyquist, and obviously you can’t display a result every time you manipulate a sample but hopefully you can use those concepts and maybe someone familiar with Nyquist can give some suggestions to help you see (or hear) what your program is doing or isn’t doing.

Just in-general, the two most important concepts in programming are conditional execution (if statements, etc.) and loops (doing something over-and-over, usually until some condition is reached). Those are the things that make programming “worthwhile”. i.e. With audio, you are usually looping to process every sample in the file (or every selected sample) until you reach the end.

Thanks, Doug, for taking the time.

To begin with I specifically asked for Expanders written in Nyquist. I know there are many various plugins and I have used quite a few but for my own selfish reasons I was looking for a Nyquist one.

Seconds, I know you can not recreate what has been lost. Again, I have my own selfish reasons for what I am trying to achieve.

Mostly these selfish reasons are a good excuse to try and learn some Nyquist programming. The fact that it is scripted and not compiled make it so appealing. Again, these are my reasons. Maybe as a consequence I could create something generally useful and I could improve upon an existing plugin, maybe not. We all need to start somewhere…

As for your last part: I used to train in C# for web-based applications, but nowadays I am “reduced” to the Access SQL queries. Nyquist is a re-entry point into some simple programming for which I would like to have some “step-by-step” debugger.

Chris’s compressor is a Nyquist plugin which will expand if you dial-in a negative value for the compression-ratio.

One technique that I frequently use is to add print statements at key points. For example, if I have a function, then print to the debug window a message when the function is called, and list the parameters. For example:

(defun my-test-function (sig arg)
  (format t "My test function: ~a   ~a~%" sig arg)
  ;rest of function code
  )

;call the function
(my-test-function *track* 42)

In the above example, if track is a mono sound, the debug window should show something like:

My test function: #<Sound: #23782948>   42

or for a stereo track:

My test function: #(#<Sound: #23782948> #<Sound: #237827a0>)   42