chkhld/jsfx

Compressors have initial moment of "ducking"/overreacting

frescoalfredo opened this issue · 13 comments

Bus Comp, Track Comp, and Consolidator all seem to behave the same way in this regard.

If I start playback "in the middle of" some audio file with a level that triggers compression, for the first (approximately) half second or so, the audio "ducks" as though it's overreacting to the signal. After that the compressor sounds normal; it's only that initial "ducking" that sounds wrong. (To really notice this behavior, be sure to set the threshold low enough that significant compression happens.)

By comparison if I start playback prior to an audio event that would trigger the compression (starting playback on silence, or at a lower level audio item that doesn't trigger the compressor), the compressors behave nicely once the compression is triggered by louder audio.

So they work well most times, but that behavior could be problematic for rendering (and also it can be very distracting when starting/stopping playback while adjusting the compressor's settings).

I should also mention: I can "smash" other compressors by lowering their threshold a lot, and they don't exhibit this sort of behavior. The reaction of the other compressors (ReaComp for example) would sound consistent; starting/stopping playback, or starting playback in the middle of a loud audio item, makes no initial overreaction.

Please try the following:

  • Play back an audio item that will definitely trigger compression
  • Stop playback while it's compressing
  • Move the play cursor into an area of silence, or an audio item that will definitely not trigger the compression
  • Play back a few seconds of that silence (or below-threshold audio item)
  • Stop the playback after a few seconds
  • Move the play cursor into an audio item that will definitely trigger compression
  • Press play

In such a scenario, do you still get the "ducking"? If there's not, then it could be that the buffered envelope states maybe aren't reset between transport changes.

In that test situation as you describe, the "ducking" still happens. It's really obvious in Bus Comp since the "ducking" seems to slowly dissipate (for about a second).

The only way I can stop it is to reduce the release time to a short amount such as 20ms in Track Comp.

I've been able to observe what you mention, but I can't reliably reproduce it or pin it on one of the compressors. It just happens at some point, and then I click around but can't get it to happen again, until it maybe happens again on its own at some point later on. (I've only noticed this behaviour about 5 times over the last couple of days.)

If it happens on playback changes, i.e. moving the play cursor around in the project or stopping/pausing/playing, then I assume Reaper informs the plugin and calls its init section to cause an internal reset. If this reset reverts some variable to its default state, that could potentially create a problem.

The thing is, apart from setting an address for the noise buffer and initializing the envelopes that follow the signal to determine how much saturation to apply, nothing else is reset in that init section. It's all just function definitions and constants that never change, like 0.5π or 1/√2 and so on. So I don't see how that could create an audible fade. Clicks from sudden jumps of the saturation amount would make more sense.

I've attached the bus comp to this message, it has a line added that explicitly instructs Reaper NOT to do that full init reset when the playback state changes. I couldn't reproduce the problem over the past few hours, so I don't know if this actually does anything. Please let me know either way.
bus_comp.jsfx.zip

If that doesn't solve anything, then could you maybe put together a preset that definitely causes this problem, and attach it to your reply? Would also be good to know which of the plugins it's for, and what kind of signal you're running into it at roughly which peak/RMS level (before it enters the compressor).

That seems to have fixed it! I tried it with various source materials and settings, and never noticed a gradual swelling of the volume after playback start when in the middle of an audio item that triggers compression. It also sounds very nice. :) I noticed no weirdness when using the plugin.

If the new line is "ext_noinit = 1;", then I've seen that in other JS plugins including the popular ReEQ (the only one I can recall at the moment), so I'm guessing it's safe to use. I added the same line to track_comp and it works there too. I'm not sure how it affects the intended sound of these plugins but at least it seems to stop this issue.

I haven't seen that particular code in many plugins, but I see other references to things being "initialized", so perhaps it's a matter of how things are set to initialize (and as long as they do it somehow). That's about all I can say about this though. I wish I could help with any technical details of this, but I'm not a programmer at all.

I have a suggestion for track_comp: the attack time setting is very useful between 0 and 2 ms (for getting "squashed" sounds which also have a bit of bite), but it's difficult to dial in within that range. If that range were easy for fine-grained adjustment, that would be nice. I think I can figure out how to do that in the code at least, but also it might be nice for the plugin in general if you don't mind my saying so.

With further testing: I notice the threshold of bus_comp and track_comp seems a bit misleading. Maybe it's due to the knee being very curved/gradual, which I'm ok with. But I find myself dialing in the threshold a lot lower than I normally would based on what I see for peaks on the "stock" JS input meter. If there were an indicator of when compression is happening and how much, that would be great.

I also don't seem to notice any difference in the hard/soft knee setting of track_comp (no matter what settings I use and on various audio sources). Maybe I'm missing something about the way it works, but I figured I'd mention that in case something's not quite right about the knee shape control.

Thanks!

That's great news!

Yes, that was the new line. I've never seen that line before, but to be honest, I never knew that instruction insisted, and I never looked for it in other plugins. I just went to the JSFX documentation and randomly read information on the init section, and sure enough, there I found it.

I still don't know why reinitializing something that doesn't really change anything would cause such behaviour, but I'm glad if you say it's gone! Then I can put this into the other dynamics processors as well and update them.

I love ReEQ, but for some reason it occasionally gives me DC offset. I'll just randomly see spike at 0 Hz in the spectrum analyzer, and only when I disable ReEQ will that spike go away. So something in there isn't quite as reliable as I'd wish it to be. But then I haven't updated mine in a while, I'll try that later.

The upside of JSFX is that you can just add a line and have a slider appear in a window. Then you add another line and you convert that slider's value to something else. Then you add another line that takes the converted value and uses it to process an audio signal. Super efficient, super fast, super simple.

The downside is that by re-using the slider JSFX automatically creates with that single line of code, there aren't (to my knowledge) any fancy options to affect HOW it behaves. Minimum value, maximum value, default value, precision, that's it. (In this case, "precision" refers to post-comma floating-point numbers.)

Take frequencies, for example. With a [20,20.000] Hz frequency slider, the center frequency should be around 500-1000 Hz so that it makes sense for a human to operate. Look at ReEQ and ReaEQ, without checking to confirm I can tell you that their spectrum analyzers will center around the 500-1.000 Hz range.

But JSFX sliders are always linear, this means that on a scale from 20 to 20.000, the value 10.000 is ~ at the center of the slider, the 5.000 mark is at 1/4 of the slider's width, and 1.000 (which is 1/20 of 20.000) is at 1/20 of the slider's width. So you only get that 5% on the left of the slider to scroll through all the important frequencies up to 1.000 Hz.

If I were to abstract that value, and make the slider's range go from 1 to 100, for example, then I could "fix it in the code" by normalizing the slider's linear [1,100] range down to a linear [0,1] range, and then accellerating/decellerating it as I please. So then I could map the slider's center 50 value to stand for 1.000 Hz in the frequency domain.

That however makes the frequency slider hard to operate. Shoot it at me, no thinking: on a scale of [1,100] that reflects the [20,20.000] Hertz range - which value do you need to get 1.250 Hz? ... Huh? ... And if the slider for the Sidechain High-Pass Cutoff on e.g. the Track Comp ranges in [0,100], then how would you know where the 100 Hz you're looking for are? ... Any guesses? ... You see? :)

So I prefer to have relatable real-world values on the plugins, that actually mean something to me while I operate them. Unfortunately, in the world of JSFX this comes at the cost of useful scaling. ... That is, unless I started making entirely custom GUIs for those things, THEN of course I could do and scale whatever I want. But in all honesty, that's just too much trouble for unpaid work. :)

When dealing with something like a bass or piano track, there can be long phases where the instrument rings out and the signal goes very quiet - but it's still "operative". With short release times, this would result in constant "breathing", where the release opens up again because the signal is very quiet, and brings back all that background noise that was squashed down just a few seconds ago. Not good. So I'll definitely keep the long 1.5s release time in Track Comp.

But hey - this is JSFX. You can click the "Edit..." button at the top of the plugin, and you can customize it to your liking. If you want to narrow down the attack or release slider ranges, just go ahead and do so. I won't feel insulted or sad, promise. :)

Look for the lines at the top of the code that look like this, then change them as you please:
slider5: compAttack=10<0.001, 100, 0.01> Attack [ms]

The slider5 part is just an internal name for Reaper.
The "compAttack" is the name of a variable in the code that will be assigned the value of the slider.
(I pick relatively sensible names because I get confused otherwise, and in the hope that it helps someone reading the code figure out WTH I'm doing. :) So you should be able to identify the sliders by that name alone.)
The number immediately after the = sign is the default value.
The first number in <> brackets is the minimum value.
The second number in <> brackets is the maximum value.
The last number in <> brackets is the smallest amount of possible change between values, i.e. the "slider precision".
And at the end of it all, you'll find the Label that's displayed on the GUI.

And finally, I agree that the sliders of things like e.g. the Attack time are not sensitive enough at very small settings. But you can hold down a keyboard modifier and move the slider while you hold that key pressed, and Reaper will start making much more precise adjustments to that slider. This is probably a simpler (and more realistic) way to get more precision into those lower milliseconds than waiting for me to put together custom GUIs. ;)

I'll check the Threshold and Knee settings you mention.
(The difference between hard and soft knee won't always be super obvious. And some compressors don't "lower a threshold onto the signal" but "boost the signal into a threshold", and then confusingly name that boost parameter "Threshold", so maybe it's just a difference between expectation/implementation.)

Nope, can't confirm problems with Threshold or Knee for now.

I set Track Comp up a certain way:

  • no Feedback
  • no RMS
  • no Stereo Link
  • no SC filter
  • no Auto Makeup
  • no Saturation.

This means it's doing nothing special, it just compresses.

In addition to that, I set its Attack and Release times to zero, by entering the values in the text boxes by hand and overriding the plugin's slider ranges. This now essentially turns it into a clipping plugin with immediate distortion onset.

I ran a -12 dBfs sine wave through it, and checked in the spectrum analyzer and an oscilloscope what came out of the Track Comp.

  • With a completely hard knee (slider value 0) I could bring the Threshold down to exactly -12 dBfs, and nothing happened. oscilloscope and spectrum analyzer showed nothing unusual.
  • The second I set the slider just .01 lower than that, I could see a deformation in the oscilloscope and harmonics appear in the spectrum analyzer. (This was obviously distortion from the super-fast Attack/Release times.)
  • Bringing the Threshold slider up again above -12 dBfs removed those nasty side-effects.

To me, this means the Threshold is working correctly.

The Soft Knee parameter works like this: the number of dB you dial in are split up, half those dB affect the signal below the Threshold, and half those dB affect the signal above the Threshold. So this means that with a Soft Knee of 12 dB dialed in, the distortion should not start immediately with the Threshold at -12 dBfs, but it should start gradually 6 dB earlier.

And well, that's what it did. With a Threshold above -6 dBfs (minus another 6 dB from the Knee) everything stayed clean, and with the Threshold below -6 dBfs (minus another 6 dB from the Knee) there were artifacts from the distortion.

If you have any more specific idea what shouldn't be working about the Threshold and Knee, then please do let me know. But, and I'm weirdly a bit sorry to say so, to me it seems as if everything is working the way it should. :)

Thanks for the explanation! Yes I already edited the attack slider range, that much I had some idea how to do. I'm using compressors for drums and electric bass guitar mostly, so I hadn't thought of something like piano.

ReEQ has been updated. It had some issues with the AGC (auto gain calculation) which would cause volume spikes, and that was taken care of a while ago in a prior update. Plus also there was some very low-level DC that I had noticed and reported (although it was around -150 dB, it might spike up a bit under some circumstances); the latest update takes care of that. There's an explanation about the even lower-level DC that remains in the plugin after this change (it's for denormal purposes), and also a line of code that can be edited to remove it entirely (which I did, and it seems to work fine).

https://forum.cockos.com/showthread.php?p=2443617#post2443617

The first post of the thread is where the latest version of ReEQ is (attached at the bottom of the post).

I don't mind that these plugins don't have a GUI other than possibly a gain reduction meter; I'd try transposing the graphics from the 1175 plugin, but the last time I tried that, it didn't work well lol. Maybe someday I'll understand more of this code.

I set track_comp as you described and sure enough, I noticed the threshold is behaving as intended. I'm not sure why I didn't notice this, but I'm guessing it's because I left "feedback" turned up, or possibly one of the other controls. Anyway I still can't notice a difference in the knee, at least for now. :) Maybe with some more use, I will. Either way it sounds really good. I'll definitely be using both these plugins. I do see a difference when I test it (similarly to how you did), so I know the knee control does do something, but I think its effect is a bit too subtle for me to notice.

Thanks for these plugins! The more good plugins that exist, the better. Also I use Linux, so JS plugins have been a huge help.

PS. I just tried bass_squeezer now! It's great!

Thought I'd teaser a little. ;)
Not done yet, will be a day or two before I can get this finished and into Track Comp and possibly others, methinks.

Apart from the (orange) Threshold marker, I'm currently probing 6 signal stages, displayed in L/R pairs:

  • raw input signal (green)
  • input signal after gain adjustment (red)
  • key signal after possible M/S conversion and stereo linking (yellow)
  • gain reduction (blue)
  • output signal post-compression before (manual) make-up gain (purple)
  • raw output signal after make-up gain, clipping and dry/wet (cyan)

I don't know how many of these will stay in, maybe the many pre/post signals are a bit much.

Is that something you can work with?
And are you particularly (dis-) interested in any of those signals?
Maybe something I haven't thought of?
Lemme know!

And yes, the colours will certainly change! :)

Screenshot 2021-06-07 at 06 45 53

Very nice!

I think a couple of the indicators aren't necessary and might help the plugin look less "busy" if they were removed. They could all be left in the plugin too (especially after seeing your updated picture with labels on each of the indicators, and how you removed the "stock" JS input/output meters on each side of the plugin to tidy up the look). But here are my thoughts about why a couple indicators might not be considered necessary.

Input (raw, before adjustment) indication shouldn't be necessary since the only input signal that matters is after it's been adjusted by the input gain control.

Similarly, output signal post-compression (but pre-makeup gain) indication shouldn't be needed; if a person has adjusted the output level by increasing or decreasing makeup gain, they should know it. Then again if the plugin has auto makeup gain (such as track_comp) and that feature is being used, it might be useful to see both sets of output indicators.

The other four indicators, I'd consider useful. Key/sidechain indication (after filtering, etc.) is actually something I've wanted in other compressors. Gain reduction is something people commonly want in compressors. Input versus output levels in general is also good to have.

If a "hybrid" sort of indicator were possible for input and output, that would be nice. I'm imagining the "raw" input indicator being green, and any additional gain (from the input gain control) being "stacked" on the right in a different color (darker green for instance). If the input gain is turned down, maybe there could be a thick green line representing the gain reduction "embedded" in the end of the indicator. (See the attached images.) If that kind of approach is feasible, then the input pre/post adjustment could be combined into one indicator. Likewise the output pre/post makeup gain could be combined into one indicator.

added

reduced

Something like that, anyway, if it's possible. Maybe that's not the best way to do it either (maybe there's a more intuitive way to show the gain reduction for instance). [edit] Looking at those pictures again, maybe the opposite approach would be more intuitive, lol.

I don't know if you've updated Reaper recently (to a development build in particular) but there's a new LUFS loudness meter plugin (JS), and it has some new functionality introduced in JSFX: the ability to hide/show sliders (click on the little icon on the bottom right). It's "slider_show()" function, in particular. That function might come in handy for you.

Screenshot 2021-06-07 at 09 29 05

I did remove the "pre" and "post" meters, they bothered me. Now it's only:

  • the raw input (green) because there is no more input meter on the left
  • the key (yellow) which reflects input gain, stereo linking, filtering, etc.
  • the gain reduction (orange) coming from the opposite direction
  • the output (blue) after volume adjustment, optional clipping, dry/wet mix, etc.
    I think it should work fine like this.

I could "stack" the input gain changes onto the input meter, but then A) the real input value is lost since there's no more JSFX input meter on the side, and B) the gain would also be in the key signal, which makes their difference less significant, they'd always be fairly close to each other.

I'm currently pondering what exactly the input gain should affect with an external sidechain, the input signal or the sidechain signal. Ah... it never ends. :)

That way of metering provides all the pertinent information while avoiding clutter. I like it.

I suppose since the threshold can be lowered enough (it goes to -60 dB), the "input gain" can be used strictly for key signal whether it's on the main input or sidechain. I'd suggest naming the control something other than "key" though since that's not as common a term as "detector input" or "detector level" (or even "sensitivity"). I generally don't use input gain on a compressor with this degree of versatility; I only use it on compressors which don't have a threshold control that I can lower.

I thought about your suggestion regarding renaming the Key meters, but I think their name is fitting as it is.

I disagree about "detector" being a more common name, since the "detector" is part of the circuit but not a signal as such.

Renaming it to "Sidechain" or "SC" was an option, since I use those terms with the plugin sliders. But firstly, the "sidechain" is just another name for the "detector path", and secondly the meters do not reflect the sidechain/detector input signal, but they also factor in the gain, the filtering and the stereo linking.

Calling the signal level that is actually used to calculate the compression from a "key" is pretty common, so I will leave it that way for now. If it turns out to be too confusing for people, I may reconsider. ;)

Level staging is a very important feature in the plugin world. If the signal going into a plugin is too hot, a dynamics threshold that only goes up to 0.0 dBfs may not be sufficient. (Many offer thresholds above 0.0 dBfs.) Having an input gain slider can help to bring the signal down into a level range useful with that specific plugin.

Also consider this: if you put together a signal FX chain with, maybe, a compressor, an EQ, a clipper, a send to a reverb, and so on - then all the signal levels after that first compressor depend on the output from that first compressor. If you load the FX chain on a track that is too loud or too quiet, you'll need a gain plugin before the compressor - or you'll need to mess with the compressor's threshold and compression settings, which in return will make it necessary to compensate that volume change in all the following plugins too.

So even if you find you don't really need it, I think having input/output volume in a plugin is an important feature, and I won't take them out.