For gated loudness of a segment, the EBU R128 spec requires a 50% window overlap. Maybe I'm missing something obvious - but I'm not seeing any indication of overlap in the code. Am I wrong?

The code badly needs some comments, I know. The lib uses a circular buffer of 400ms (or 3s if mode S is enabled). All 200ms a new gating block is calculated, except at the beginning, where 400ms are needed. This behaviour is controlled by the variable "needed_frames", which is initialized to samplerate*2/5 (line 168) and set to samplerate/5 for all following blocks (line 407).

I did a test with some short samples, logging the number of calculated blocks:

And in the example of a 600ms input, it should be calculating the loudness for 0-400 ms and then 200-600 ms. Because in the code it looks like it might be doing 0-400ms and then 400-600ms. But again I might just be following along wrong - the #define macros made it hard to debug and follow along

Ah, right, then. Well, then it would be helpful to add modes for retrieving momentary and short-term loudness, gated. Or maybe I'll just stick to using a fork that does what I need. Because using BS.1770 for normalization instead of track or album analysis is probably beyond its design scope anyway.

PPS You don't have to redo the whole calculation each time. The absolute gating threshold does not change and must only be compared once per block. The relative gating threshold must be updated and re-applied after every new block. You may hit a drawback of using plain ANSI C, though. C++ would allow comfortable storing of block energies of virtually infinite number in dynamically growing memory structures like vectors or lists. It's not trivial to match the latter in performance/maintainability by hand coding in plain C. But when you've come so far, I'm sure you are going to find a solution.

I currently do something like this already. All block energies above the absolute threshold are stored in a singly linked list. When ebur128_loudness_global is called, this list is traversed twice, first for the relative threshold and a second time for the gated loudness. And yes, I miss std::list.

And in the example of a 600ms input, it should be calculating the loudness for 0-400 ms and then 200-600 ms. Because in the code it looks like it might be doing 0-400ms and then 400-600ms. But again I might just be following along wrong - the #define macros made it hard to debug and follow along

ebur128_calc_gating_block in line 390 is always called with samplerate*2/5 (400ms) as a parameter. So it will always analyse the last 400ms for each new block. A bit difficult are lines 256-275: I must check if the last 400ms "wrap around" the circular buffer, and handle that case separately.

Ah, right, then. Well, then it would be helpful to add modes for retrieving momentary and short-term loudness, gated. Or maybe I'll just stick to using a fork that does what I need. Because using BS.1770 for normalization instead of track or album analysis is probably beyond its design scope anyway.

How does gated short-term or momentary loudness work? Isn't gating only used for long audio segments where very quiet or silent parts could distort the result?

It could be interesting to plot short-term and integrated loudness and see how they influence each other.

It I understand you correctly something like that was already posted in that other R128 thread but without short-term history view: http://www.nugenaudio.com/images/VisLM-H.pngIt can also output CSV with all kinds of data, but it's commercial

[edit] actually very commercial, I wasn't aware of it's price until now

And in the example of a 600ms input, it should be calculating the loudness for 0-400 ms and then 200-600 ms. Because in the code it looks like it might be doing 0-400ms and then 400-600ms. But again I might just be following along wrong - the #define macros made it hard to debug and follow along

ebur128_calc_gating_block in line 390 is always called with samplerate*2/5 (400ms) as a parameter. So it will always analyse the last 400ms for each new block. A bit difficult are lines 256-275: I must check if the last 400ms "wrap around" the circular buffer, and handle that case separately.The first block differs from the others. This is probably because the filter states must get filled.

I looked at the code again and it's definitely not doing any sort of window overlap. It's treating each 200ms block of audio separately, except for the first block which is done as 400ms. We should get this fixed. Unfortunately, the IIR filter code is writing the filtered data back into the audio_data buffer. In order to do overlapped windows, you'd need to keep the audio intact as each sample will be used twice. One simple solution would be to do the sum-of-squares inside the filter function, so that you don't need to store the filtered audio.

Just to be clear, this is essentially what needs to be done for overlap. For the sake of clarity, let's use a 1000Hz sampling rate.

It is doing window overlap. Otherwise, the block energies from the test in post 81 would be different, and not all 0.654412.

Oh ok I see what it's doing now. It's doing the windowing in ebur128_calc_gating_block instead of ebur128_add_frames_/ebur128_filter_. Sorry for making such a stink, just wanted to be sure that the library was working correctly. I had gotten concerned when it was running the filter on 200ms blocks but now I see that it's doing the sum-of-squares on 400ms segments. Sorry for the alarm

Library:- Removed the function ebur128_set_channel_map. Use ebur128_set_channel instead.- Fixed the samplerate bug (the library would not work correctly with a samplerate that was not divisible by 5).- ebur128_*_multiple functions skip NULL pointers.

Scanners:- Removed the Python tagging script. Tagging is now directly integrated into the scanners. I'm using TagLib for that. Usage example:

first of all thanks for making and publishing such a useful peace of software! What a lucky day it was when I saw it compiling on FreeBSD without errors...

With the latest version I noticed that a script I wrote for normalizing didn't work any more. It turned out that the text output of r128-ffmpeg changed somewhat. You can see it when you pipe the output to e.g. hexdump. So in my case piping the output to bc for calculation causes a syntax error.

And a suggestion: As I said I use your software for calculation of normalization. When it turns out that an audiofile has to be made "louder" I measure the given headroom with sox - actualy another time costing scan. Maybe it would be useful if you add a switch that outputs the highest true-peak within a given file.

It turned out that the text output of r128-ffmpeg changed somewhat. You can see it when you pipe the output to e.g. hexdump. So in my case piping the output to bc for calculation causes a syntax error.

Yes, the output changed in the last version a bit... Currently it is not really meant for scripts as it writes everything to stderr. I will try to write something useful to stdout.

And a suggestion: As I said I use your software for calculation of normalization. When it turns out that an audiofile has to be made "louder" I measure the given headroom with sox - actualy another time costing scan. Maybe it would be useful if you add a switch that outputs the highest true-peak within a given file.

I will try to get true peak measurement done for the next version, thanks for the feedback.

Library:- removed the ebur128_start_new_segment and ebur128_loudness_segment functions. To calculate track/album values use one library state for each track, then calculate the album gain with ebur128_loudness_global_multiple.- documented ebur128.h with Doxygen.

Scanner:- fixed a crash with the FFmpeg scanner.- fixed recursive folder scanning (didn't work in 0.2.0).- implemented true peak scanning! Those values will NOT be used for the ReplayGain tags.- improved scanner output a lot. Hopefully it is now easier to parse by scripts.

The scanner can now calculate all three values recommended by the EBU: "Programme Loudness", "Loudness Range" and "True Peak Level".

And here is 0.2.2, with a single change:- added "--gate" option to specify the relative gate. The standard uses a gate of -8 dB, but will switch to -10 dB in the next revision. This option is for testing only and will be removed when the standard is updated!

And here is 0.2.2, with a single change:- added "--gate" option to specify the relative gate. The standard uses a gate of -8 dB, but will switch to -10 dB in the next revision. This option is for testing only and will be removed when the standard is updated!

Hey Raiden you are great!!! Not only are you on the cutting edge of the spec but also do it all well. FYI I've tested libebur128 with the test set provided by Qualis Audio http://www.qualisaudio.com/downloads.htmand ALL test have passed perfectly!!! (except test #2 which is ac-3 encoded 5.1 and I'm not sure if libebur128 can handle this)Just a couple of remarks:-It should be sufficient to indicate 1 decimal point in dB figures. For example, some test that should measure -23.0 LUFS according to Qualis, measure -22,97 LUFS in libebur. Of course it is the same but it just doesn't look so good And I think EBU Tech docs use 1 decimal point always. - Please make the True Peak measurement in dBTP!!! At first I thought the measurement was wrong (0,793..) but of course this is linear for -2.0 dBTP !! Good work!!!

On the other hand:Could someone guide me through the steps of how to build an executable for OSX from source code? is it complicated? Does it imply modifying the code? I have XCode (mainly because of some bundled apps) but I have no idea where to start Best regards!Carles

Hey Raiden you are great!!! Not only are you on the cutting edge of the spec but also do it all well. FYI I've tested libebur128 with the test set provided by Qualis Audio http://www.qualisaudio.com/downloads.htmand ALL test have passed perfectly!!! (except test #2 which is ac-3 encoded 5.1 and I'm not sure if libebur128 can handle this)

I'm glad that it works for you!I've had a quick look at LM2.wav, and it seems to be just a normal stereo 24-bit PCM wave file at 48000 Hz. They say in TechNote #2:

-It should be sufficient to indicate 1 decimal point in dB figures. For example, some test that should measure -23.0 LUFS according to Qualis, measure -22,97 LUFS in libebur. Of course it is the same but it just doesn't look so good And I think EBU Tech docs use 1 decimal point always.

You're right. I've been using two digit precision because it's a bit more helpful in debugging.

Could someone guide me through the steps of how to build an executable for OSX from source code? is it complicated? Does it imply modifying the code? I have XCode (mainly because of some bundled apps) but I have no idea where to start

I don't know much about OSX, but you'll probably need to use some kind of ports system like MacPorts.A command like "sudo port install libsndfile taglib cmake glib2" should install all needed dependencies to build the r128-sndfile scanner with the instructions in the README.

Hi!Thanks for the Macports suggestion, i will try it.File #2 is most probably a Dolby-E encoded 5.1 signal. Dolby-E is a broadcast codec that compresses and encodes 5.1 to a stereo bitstream. Decoding needs expensive hardware or software. I wouldn't care about this.Cheers