Help Building a Plugin

I am looking for some technical programming assistance building a compressor plugin based on a transfer function of mine. I’m not really sure where to turn for help with this, but this seemed like a good first try.

I think this effect could be pretty interesting. The general realm is that of a variable-gain-response or automatic-gain-control compressor. It could also be interpreted as a soft clipping limiter with maximum softness. In effect, it is actually a soft-knee brickwall limiter. If anyone is interested in helping me, please let me know.

Before we start reinventing the wheel, have you tried Chris’s Dynamic Compressor?

Chris is the world class master of manipulating show volume and variations without seeming to do so. One simple change to one of the settings and I can get a very close match to the expensive audio processors in a Los Angeles FM station.

Chris uses all the tricks you posted about plus look-ahead so the compressor is never surprised by variations in the show.

The effect package downloads as an XML file and you can open it in TextEdit or NotePad and read what he did.


Thanks for your replies. I have looked at Chris’s compressor; it’s an admirable piece of work. I found it while doing research into software or hardware that might already perform the function I have in mind. Nothing has fit the bill so far.

The closest hardware type seems to be the “variable mu” compressor using remote-cutoff tubes to create a soft knee response. In a weird way, diode overdrive effects units are also related in that they create near-instantaneous soft clipping of each individual wave crest/trough.

The closest software approach seems to be waveshaping. Because of the involvement of summing infinite series in my concept, interpolated lookup tables appear to be a possible solution.

The heart of the idea is the transfer function:
I refer to this as the attenuation factor.

The process works like so: for the input signal to be processed, it processes each sample individually. Say the input signal sample has amplitude .5. The total final output for this input sample is the sum of a series. For the sake of illustration, say the increment value between the individual amplitudes of the sample (as determined by bit depth, for instance) is .00001 instead of infinitesimal. Starting at level 0, each increment of the input signal sample’s value is divided by the attenuation factor. So, .00001/((1/(1-.00001))^(REDUCTION EXPONENT)) is calculated and the resulting value is stored. Next, (.00002-.00001)/((1/(1-.00002))^(REDUCTION EXPONENT)) is calculated and the resulting value is stored. And so on, up until (.5-.49999)/((1/(1-.5))^(REDUCTION EXPONENT)). Then all the stored values are summed, and we have the output value for that sample.

The REDUCTION EXPONENT is simply a separate number to be chosen by the user. The larger it is, the more the process squashes the input signal.
As it turns out, for an input signal strength of 1, a reduction exponent of 1 results in an output value of .5. A reduction exponent of 2 results in an output of .333…, etc., so that the output value is a predictable 1/(REDUCTION EXPONENT+1).

As can be seen, the attack and release times for this process are necessarily fixed at 0 microseconds. The “threshold” and “ratio” controls are fundamentally united into one single control, which is in fact the process just illustrated; the lower the threshold, necessarily the higher the compression ratio, and vice versa.

I have tried all kinds of trigonometric functions and all variety of polynomials and other tricks in the transfer function that don’t involve infinite summing. The process just described gives by far the best-looking resulting attenuation curves. Moving from low ratios to high, the effect goes from a harmonic-reducing “rounding” of the waveform to the more traditional odd-harmonic-generating squaring of waveforms. I really want to see this in action, since as far as I can tell it has the potential to have an unprecedented and characteristic effect on the sound of incoming signals.

You can have hours of fun using Nyquist to create these type of effects.
Essentially the effect that you are wanting to produce is that of a “non-linear amplifier”.

Nyquist is a simple but powerful scripting language that is built into Audacity. You can either run Nyquist commands from the “Nyquist Prompt” (in the Effect" menu), or create new plug-ins which will be listed in the Effect, Generate or Analyze menus.
There is an introduction to Nyquist programming here:
The Nyquist manual is here:
The Function index is here:

As Nyquist is based on the XLisp programming language, the XLisp manual is very useful (it explains many of the functions found in Nyquist in a lot more detail:

There are several similar effects to what you are asking about.
There’s one by Edgar called “Broadcast Limiter II (RFT-Limiter-II.ny)” on this page:
Attached is another one that takes a different approach to the same task. (747 Bytes)

Thanks Steve. The softclip.ny plugin is by far the closest Nyquist implementation I’ve seen to what I’m trying to do. Your linking to it has encouraged me to revisit it again.

While I’m here, I may ask: I don’t see “s-in,” which is used several times in softclip.ny, in the function index. What is that?

It’s just a variable. It could have been anything but I frequently use “s-in” (sound-in) as the name of the variable in a function that will hold the sound to be processed (the “input sound”).

What’s the eventual goal? I don’t remember hitting that anywhere. I’m suspicious of anyone who starts with algorithms and works out to the goal instead of the other way to. Chris’s success stems in no small part in that he started with a crystal clear goal first – listen to opera in the car – and then worked down to the algorithms.

Do you remember your first sentence?


This is Chris’s first sentence…

<<<I’ve written a program that makes it easier to listen to classical music, or other music that has a wide range of volumes, at low volumes or in high noise conditions (such as in your car)>>>

And he succeeded spectacularly well. Where are you going with this?


Thanks for your suspicion kozikowski. Needless to say, the problem preceded the formula. My interest in the relationship between the compression and the overdrive applied to a signal by variable-gain-response devices led me to the intuitive conclusion that the process as I eventually worked it out would result in a sound that is “totally ******* awesome.”

I have been paring away at the Softclip.ny file to remove all the inessentials; the heart of that plugin

(setq strength .5)
(defun pump (s strength)
(setq smash (mult -1 (s-abs s)))
(mult s (sum 1.0 (mult smash strength)))
(pump s strength)

is a variation on (if not essentially identical to) some formulas I came up with on the way to my final solution. The effect is not exactly the same as my final solution, but iterative application of the formula gives a reasonable approximation. In the interest of not beating my head against a brick wall, I may end up just accepting this compromise.

I understand. So the deliverable filename would be awesome.ny.

I designed something similar and delivered about a month ago.

Note the second post.


Thanks for all your help and encouragement. I’m now convinced that I can actually handle doing some stuff in Nyquist (and that I don’t have to reinvent the wheel to do it!). This boiled-down script is more or less exactly what I was looking for:

;nyquist plug-in
;version 1
;type process
;name “soft-knee brickwall limiter”

(defun limit (s) (mult s (diff 1.0 (mult 0.5 (s-abs s)))))
(limit s)

I’m impressed - the soft clipping sounds better than my original plug-in :slight_smile:
It does weird things on stereo files though.

For processing stereo files you could add this to the end:

(if (arrayp s)
   (vector (limit (aref s 0))(limit (aref s 1)))
   (limit s))

but you would also need to rename the variable “s” within the function so as to differentiate it from “s”.
For example:

;nyquist plug-in
;version 1
;type process
;name "soft-knee brickwall limiter"

(defun limit (s-in) 
   (mult s-in (diff 1.0 (mult 0.5 (s-abs s-in)))))

(if (arrayp s)
   (vector (limit (aref s 0))(limit (aref s 1)))
   (limit s))

A shorter, but somewhat more cryptic way of writing this would be:

(defun limit (s-in) 
   (mult s-in (diff 1.0 (mult 0.5 (s-abs s-in)))))

   (multichan-expand #'limit s)

(note the single quote after the #)

If this effect is used for “maximising” a recording, you will want to amplify the output to compensate for the reduction in peak gain.
Fortunately, this algorithm produces easily predictable attenuation (where the multiplier of (s-abs s) is greater than 0.5).

Here is a slight modification that allows the limiter threshold to be set in the range of 0 to -6 dB ( -6dB is the same as your code) and can also perform the necessary amplification to compensate for the peak attenuation.

;nyquist plug-in
;version 1
;type process
;name "soft-clip brickwall limiter"
;info "Usually best to Normalize the audio before using this effect."
;control thresh "Threshold" real "dB" -3 -6 0
;control mu "Apply Make-Up Gain?" choice "Yes,No" 0

; limit max and min values of thresh
(setq thresh (min (max thresh -6) 0))

; convertthresh to linear scale
(setq thresh (dB-to-linear thresh))

   ((make-up (if (= mu 0)(/ thresh)1)) ; set make up gain to inverse of thresh, or 1
   (thresh (- 1.0 thresh))) ; set thresh 
   (defun limit (s-in)   
     (mult s-in make-up (diff 1.0 (mult thresh (s-abs s-in)))))

  (multichan-expand #'limit s))

Wow, thanks Steve! I am going to have to spend some time to really understand some of the suggestions you made but they look like good ideas. My rudimentary scripting skills are going to have to do some growing here…

Since you’re interested in Nyquist programming it may be worth signing up to the Audacity-Nyquist mailing list.
It is a very low volume e-mail list that you send e-mails to and receive e-mails from other list subscribers. It’s not actually used very much, but there are occasionally interesting things come up and it provides another avenue for asking questions.