Loudness (not amplitude) control

Eric_Singer's icon

Are there any patches/objects out there that attempt to do (psychoacoustic) loudness control, as opposed to [*~] amplitude control?

Thanks,
Eric

KrisW's icon

[average~] :) u can calculate the differences between rms and normal peak to provide dedicated level control i guess . ive never done that but i saw the compressors logic where "you are" controlling it automatically with reversed ratio - because you want to affect quiet signals not peaks

ps:if there are some objects.patches.externals i would also like to know

Peter Castine's icon

What Eric's asking for is far more complex than average~. can serve as an intro/primer into the topic.

I suppose one way to patch a loudness control would be with a pfft~ and lookup tables for each bin. Tedious but could be effective. Should I sketch out the idea in any more detail?

Sorry, I don't know of anything off-the-rack that will do it.

KrisW's icon

could you sketch it Peter ????? would be fantastic , i love math :D

mzed's icon

There's a loudness output in analyzer~, downloadable at the CNMAT site. (There's also a standalone loudness object.) You'd have to compensate for the delay inherent in the analysis, but that should get you started.

mzed's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Something like:

Roman Thilenius's icon

i shall put this here - upon request by someone else - but this is where it belongs.

loudness is always subjective, but there are defacto standards (yet not norms) for measuring what we call "loudness units", ITU BS 1770 / 1771 and EBU R128.

this can be implemented roughly like so:

- two times a shelving filter which raises the HF at 10^3Hz for +4db

- a highpass, default or butterworth topology, to lower the LF content at 10^2Hz to get about the curve you can see in the picture

- now you measure RMS with 400ms

- for short term, you will print the value every 30ms
- for long term you will print the value every 400 ms, after smoothing them across 400ms
- for programme (total LUFS of your song) i am not sure, but it should be fine to take either other of the above.

- for all of them you have to insert 2 noisegates before you print. the first one will be fixed at -70dbFS and the other one will be dynamic at -10db relative to ("under") the interpolated last 4 values already measured.
(i do this in data rate, which you dont see that in the picture, beause it might be wrong and could mislead you. the difference between the two methods, however, is quite small (~ +- 0.15db) in 99% of the cases.)

- in the case of dolby pro logic II (and "higher") matrixes the surround channels should be multiplied with √2


there is also a dozen critics and suggestions such as one should better use ISO 226 curves in addition (which itself is also considered outdated and waiting for a follow-up), add binarual corrections depending on the setup, take into account how hearing changes with spectrally complex material and how transients and directions can change perception, use modern KI techniques, have more women, blacks and asians in the subject groups, and so on and so forth.

so you can basically also just do what you want unless your mastering target is broadcast or streaming. :)

filtering and RMS in BS 1770-1 LKFS measurement, roughly, 110-style, no money-back guarantee.

trummerschlunk's icon

This is a stereo ITU BS 1770 / 1771 EBU R128 implementation in FAUST from my project master_me, which seems to work fine as faustgen~ in max. Curious how exactly you would implement it in max though...

// -*-Faust-*-
// double precision -double needed!

import("stdfaust.lib");

// init values
Nch = 2; //number of channels

// main
process = _,_ : lk2 : ba.db2linear;

// +++++++++++++++++++++++++ LUFS METER +++++++++++++++++++++++++

lk2 = par(i,2,kfilter : zi) :> 4.342944819 * log(max(1e-12)) : -(0.691) with {
  // maximum assumed sample rate is 192k
  maxSR = 192000;
  sump(n) = ba.slidingSump(n, Tg*maxSR)/n;
  envelope(period, x) = x * x :  sump(rint(period * ma.SR));
  //Tg = 0.4; // 3 second window for 'short-term' measurement
  Tg = 3;
  zi = envelope(Tg); // mean square: average power = energy/Tg = integral of squared signal / Tg

  kfilter = fi.itu_r_bs_1770_4_kfilter;
};

Roman Thilenius's icon

biquad~ and average~ in rms mode. the rest is arithmetics.