Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ebur128_true_peak returns 2.12815e-314, incorrect measures overall #126

Open
bleachcult opened this issue Feb 5, 2023 · 14 comments
Open

Comments

@bleachcult
Copy link

I think I've hit the wall with this. Most of the time I get true peak as 2.12815e-314.
Overall, I'm getting incorrect measures - but not consistently. I'm comparing results against another commercial loudness meter from Izotope. I'm loading audio with libsndfile and passing on a std::vector with number of channels and correct samplerate.

For example, here's my output from libebur128
Integrated loudness: -21.942
Short term loudness: -28.8716
True peak level: 2.12815e-314
Dynamic range: 5.80092

By contrast, in Izotope RX I am getting the following (image attached):
Integrated loudness: -29.0
Short term loudness: -25.1
True peak level: -1.6
Dynamic range: 13.7

Screen Shot 2023-02-05 at 3 50 52 pm

I know that it's a very dynamic signal but LRA readings from libebur128 suggest otherwise.

I'm initializing ebur128 like this:
ebur128_state* state = ebur128_init(channels, sampleRate, EBUR128_MODE_LRA | EBUR128_MODE_TRUE_PEAK | EBUR128_MODE_S | EBUR128_MODE_I);

And calculating true peak like this:
ebur128_true_peak(state, channels, &peakLevel);

I suspect that because it's having trouble calculating true peak, all the other readings are off as well.
I would very much appreciate any help.

@audionuma
Copy link
Contributor

Hello,
As you don't show the full code, we can just guess. Are you trying to pass a std::vector to one of the add_frames_ functions ? This won't work properly, you have to use the underlying array of the std::vector.

@bleachcult
Copy link
Author

bleachcult commented Feb 5, 2023

Hello and thank you for your reply. Here's the complete function:

void calculateLoudnessMetrics(const std::vector<float>& audioSignal, int channels, int sampleRate,
                              double& integratedLoudness, double& shortTermLoudness, double& peakLevel, double& dynamicRange) {
  // Initialize ebur128 state
    ebur128_state* state = ebur128_init(channels, sampleRate, EBUR128_MODE_LRA | EBUR128_MODE_TRUE_PEAK | EBUR128_MODE_S | EBUR128_MODE_I);

  // Add audio samples to ebur128 state
  ebur128_add_frames_float(state, audioSignal.data(), (size_t)audioSignal.size());

  // Calculate integrated loudness
  ebur128_loudness_global(state, &integratedLoudness);

  // Calculate short term loudness
  ebur128_loudness_shortterm(state, &shortTermLoudness);
    

  // Calculate peak level
  ebur128_true_peak(state, channels, &peakLevel);

  // Calculate dynamic range
  ebur128_loudness_range(state, &dynamicRange);

  // Clean up ebur128 state
  ebur128_destroy(&state);
}

@bmatherly
Copy link
Contributor

ebur128_add_frames_float(state, audioSignal.data(), (size_t)audioSignal.size());

Should that be:
audioSignal.size() / channels
?

* @param frames number of frames. Not number of samples!

@bleachcult
Copy link
Author

Since I am testing it with a mono signal it didn't make a difference to the readings, but yes - good point, thanks!
Still getting True Peak as 2.12815e-314 and all the other values are off.

@bmatherly
Copy link
Contributor

A couple of other questions/comments:

  • Are your audio samples interleaved and in the range -1.0 - 1.0?
  • what is the return value of ebur128_add_frames_float()?
  • what is the return value of each ebur128_loudness_*() function?
  • ebur128_true_peak() is being called with an invalid value. It should be called once for each audio index (0 - (channels-1));
  • Keep in mind that loudness shortterm is a windowed measurement. It makes more sense to call it periodically (10 times per second, etc) and display the value as a meter. Calling it once after all the samples are processed might not make sense.

@audionuma
Copy link
Contributor

Have you tried to use c pointers instead of c++ references for the double args in your code ?
Something like:

void calculateLoudnessMetrics(const std::vector<float>& audioSignal, int channels, int sampleRate,
                              double* integratedLoudnessPtr, double* shortTermLoudnessPtr, double* peakLevelPtr, double* dynamicRangePtr) {
  // Initialize ebur128 state
    ebur128_state* state = ebur128_init(channels, sampleRate, EBUR128_MODE_LRA | EBUR128_MODE_TRUE_PEAK | EBUR128_MODE_S | EBUR128_MODE_I);

  // Add audio samples to ebur128 state
  ebur128_add_frames_float(state, audioSignal.data(), (size_t)audioSignal.size());

  // Calculate integrated loudness
  ebur128_loudness_global(state, integratedLoudnessPtr);

  // Calculate short term loudness
  ebur128_loudness_shortterm(state, shortTermLoudnessPtr);
    

  // Calculate peak level
  ebur128_true_peak(state, channels, peakLevelPtr);

  // Calculate dynamic range
  ebur128_loudness_range(state, dynamicRangePtr);

  // Clean up ebur128 state
  ebur128_destroy(&state);

Does it change anything to the results obtained ?

@bleachcult
Copy link
Author

Thank you very much for your help Brian!

  • My audio samples are interleaved and in the range -1.0 to 1.0.
  • return value of ebur128_add_frames_float() is 0.
  • return values of all ebur128_loudness_*() functions are 0. They shouldn't be all zeros, right? If I understand correctly, I've assigned it to auto value and then print it.
  • ebur128_true_peak() - yes, I misunderstood this one previously. Fixed it, now I'm getting some values.
  • Again, I misunderstood the purpose of this one. Implemented it the way you recommended, very useful.

So now that I'm getting some values, the main problem still remains - my readings are quite off. True peak is way above its real value, dynamic range is way too low.

Integrated loudness: -21.942 (should be -29.0)
True peak level: 0.825834 (should be -1.6)
Dynamic range: 5.80092 (should be 13.7)

I also tried it with EBU test samples. Similar story. seq-3341-9-24bit.wav, for example:
Integrated loudness: -22.9947 (great! Should be -23.0)
True peak level: 0.1 (way off, should be -20.0)
Dynamic range: 1.35003e-13 (should be 3.8)

I must be doing something wrong... :/

The one that is crucial to get right for my purposes is dynamic range.

Once again, thank you very much for your help, I truly appreciate it.

@bleachcult
Copy link
Author

bleachcult commented Feb 5, 2023

@audionuma - Thank you very much! I have tried that. It changed something on some files, but not all.

My original example still returned identical results. It is a clean sound effects file, well formatted, well recorded and I have checked it thoroughly. TP too high and LRA way off, still.

On the EBU test sample seq-3341-9-24bit.wav, it's changed slightly:
Integrated loudness: -23.0461 (different, but still great!)
True peak level: 0.1 (same, way off, should be -20.0)
Dynamic range: 4.73654 (different, not that far off, but still should be 3.8.)

Here's how I load my audio files, in case I'm messing something up there:

std::vector<float> audio_signal;
int channels;
int samplerate;
                
                SndfileHandle wav_sf = SndfileHandle(&out_source[0]);

                if(wav_sf.frames() < 1){
                    std::cout << "Not much data in " << out_source << "\n" << std::endl;
                }
                else{
                    int frames = (int)wav_sf.frames();
                    channels = wav_sf.channels();
                    samplerate = wav_sf.samplerate();
                    audio_signal.resize(frames*channels);
                    wav_sf.read(&audio_signal[0], float(frames*channels));
                }

I pass audio_signal, channels and samplerate to my calculateLoudnessMetrics function.

Once again, thank you very, very much.

@bmatherly
Copy link
Contributor

I do not have any experience with Sndfile. But maybe you should mimic its use from the example program to make sure you are getting the right format. I see the example uses sf_readf_double(). I am not sure what format is returned by wav_sf.read().

https://github.com/jiixyj/libebur128/blob/master/test/minimal-example.c#L60

@bleachcult
Copy link
Author

Sndfile can feed the audio as float, double or short. I have tried loading the signal both as float and double. No change.
Also, I have tried minimal-example.c, ran some EBU test files and I am getting similar results.
Integrated loudness: -22.99 (OK, looks like -23.0)
True peak level: 0.1 (way off, should be -20.0)
Dynamic range: 0.0 (should be 3.8)

So, true peak and dynamic range are still off.

I am developing on an M1 Mac, ARM64, wonder if that could be the issue?

@jiixyj
Copy link
Owner

jiixyj commented Feb 7, 2023

The true peak level at least looks good! 0.1 corresponds to -20dB, and 0.825834 to -1.6dB.

@bleachcult
Copy link
Author

bleachcult commented Feb 7, 2023

Thanks jiixyj, that's great to hear.

I have applied 20 * log10 to my true peak readings and they are now spot on.
I am now exclusively testing with EBU Test files - integrated looks good, true peak is perfect. However, loudness range is still completely off. Not even close to readings produced by commercial R128 meters.

For example, here are the results for seq-3341-13-13-24bit.wav.wav
Integrated loudness: -25.5897 (Great, Izotope RX reports -25.4)
True peak level: -23 (Perfect, matches -23.0)
Dynamic range: 0 (Izotope RX reports LRA 16.9)

seq-3341-10-1-24bit.wav:
Integrated loudness: -23.216 (Great, Izotope RX reports -23.4)
True peak level: -23 (Perfect, matches -23.0)
Dynamic range: 1.76083 (Izotope RX reports LRA 12.0)

I have tried all examples in test.c too - same results...
Do I need to scale loudness range readings too?

@jiixyj
Copy link
Owner

jiixyj commented Feb 7, 2023

I have tried all examples in test.c too - same results... Do I need to scale loudness range readings too?

With that, do you mean the tests in https://github.com/jiixyj/libebur128/blob/master/test/tests.c#L322, and those tests printed "FAILED"? If so, that indeed points to some bug in the library...

@bleachcult
Copy link
Author

With that, do you mean the tests in https://github.com/jiixyj/libebur128/blob/master/test/tests.c#L322, and those tests printed "FAILED"? If so, that indeed points to some bug in the library...

Yes, I meant I tried running examples in tests.c and results were the same - meaning that true peak, integrated, short were pretty accurate, but loudness range was completely off. But no, they did NOT print "FAILED". I am in no way suggesting this could be a bug in the library. I believe the problem is caused by my implementation. I will run these same tests on my Windows machine to rule out any system specific causes.

I don't believe there's a problem with my wav files, since Izotope RX reports loudness very accurately - dynamic sounds are reported with LRA > 10, flat sounds LRA < 10. With libebur128 this is inconsistent. I have some very dynamic files with gunshots, metal rods being hit and door slams with some silent audio events in between and libebur128 reports LRA around 5, just like that of "Neighborhood ambience" or "Motel roomtone background" which are basically constant hums. By contrast, Izotope's meter reports LRA around 25 for the dynamic audio and 5 for ambiences. But occasionally, maybe half of the results are reasonably meaningful, not completely accurate but have fairly obvious clues when audio is dynamic or flat.

I wonder if there are any gating settings, or filter, or window size I could tweak?

Either way - I must say this - thank you for this library, it's really very cool. And thank you for your precious time. I still have hope I will figure it out eventually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants