Old 03-16-2024, 05:32 AM   #1
C. Yoder
Human being with feelings
 
Join Date: Jun 2011
Location: United States
Posts: 6
Default Questions about reaper.CalculateNormalization

Hi all,

I'm looking for some help in using reaper.CalculateNormalization in a script. My goal is to read the LUFS value from the first item in a selection, and use that returned LUFS value as the normalizeTarget argument in reaper.CalculateNormalization to match the subsequent items' loudness to the first item

However I'm running into the problem of the subsequent items' loudness ending up being about 3db quieter than the target, even when applying to the exact same item, so I think I'm doing something wrong. In the past I've done this manually using the SWS Loudness extension, analyzing all items, then selecting all but the first and using the "Normalize.." option to manually enter the target, but I'm trying to automate this.

I've attached a picture below showing the comparison. Item 1 on each track is reference, purple items are all the exact same item, and the grey item is the one with different volume. SWS normalize results in a value of +0.26dB, but the script getting the LUFS value using reaper.NF_AnalyzeTakeLoudness_IntegratedOnly, then using reaper.CalculateNormalization to set to that target LUFS value results in -3.55dB, and -3.18dB on the exact same item, so obviously I've screwed something up since doing this to the same item should result in no change.

Can someone help point out my mistake? Here's my code:

Code:
count_items = reaper.CountSelectedMediaItems(0) 

selected_items = {}
for i = 0, reaper.CountSelectedMediaItems(0)-1 do
	selected_items[i+1] = reaper.GetSelectedMediaItem(0, i)
end

if count_items > 0 then -- if any items are selected

    for index, it in ipairs(selected_items) do

      item = it
      take = reaper.GetActiveTake(item)
      itemname = reaper.GetTakeName(take)
      src = reaper.GetMediaItemTake_Source(take)
      sourceoffset = reaper.GetMediaItemTakeInfo_Value(take, "D_STARTOFFS")
      item_length = reaper.GetMediaItemInfo_Value(item, "D_LENGTH")

      source_end = sourceoffset + item_length
      source_start = sourceoffset 

      if(index == 1)then 
        _, volume_ref = reaper.NF_AnalyzeTakeLoudness_IntegratedOnly(take) -- get LUFS value of first item
      else
	volume_correction = reaper.CalculateNormalization(src, 0, volume_ref, source_start, source_end) -- normalize subsequent items to first LUFS value

        reaper.SetMediaItemTakeInfo_Value(take, "D_VOL", volume_correction) --apply volume correction to take
      end
    end
 end
Attached Images
File Type: png LUFS comparison.png (67.7 KB, 16 views)
C. Yoder is offline   Reply With Quote
Old 03-16-2024, 05:46 AM   #2
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,749
Default

My guess would be the extension function is not calculating stereo loudness on these stereo items. A loudness difference from expectations of 3dB is usually caused by a mono vs stereo calculation.

You don't need to use the extension function, though. You could do something like this instead (this is not real code):

Code:
baseline_correction = reaper.CalculateNormalization(reference_item, lufs_i, target_of_zero_lu)
correction = reaper.CalculateNormalization(other_item, lufs_i, target_of_zero_lu))
reaper.SetMediaItemTakeInfo_Value(other_item, "D_VOL", correction/baseline_correction)
In other words, if the baseline reference item needs a correction of X to get to 0 LU, and the other item needs a correction of Y to get to 0 LU, that's the same as saying the other item needs a correction of X/Y to get to the same LU as the reference item.
schwa is offline   Reply With Quote
Old 03-16-2024, 05:50 AM   #3
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Quote:
Originally Posted by C. Yoder View Post
However I'm running into the problem of the subsequent items' loudness ending up being about 3db quieter than the target
A difference of 3 dB almost certainly hints to a mono vs. stereo issue.
Are you working on mono takes?

Please see the signal flow here, SWS loudness for takes is measured directly on the takes itself, while Reaper's loudness calculation is measured in a track context (I think), and since Reaper's tracks are always stereo this would result in the 3 dB difference.

Does this make sense?

edit:
Ninja'ed by schwa.

Last edited by nofish; 03-16-2024 at 06:04 AM.
nofish is offline   Reply With Quote
Old 03-16-2024, 06:51 AM   #4
80icio
Human being with feelings
 
Join Date: Mar 2016
Location: Italy
Posts: 322
Default

Code:
reaper.SetMediaItemTakeInfo_Value(take, "D_VOL", volume_correction)
"D_VOL" is an absolute value and not a DB value

Code:
reaper.NF_AnalyzeTakeLoudness_IntegratedOnly(take)
it's a very slow function, you should follow SCHWA suggestion and see the difference between takes by using a reference level, like a classic - 23

you should add
Code:
reaper.UpdateArrange()
at the end of your code to update the arrange window and check the updated gain results
80icio is offline   Reply With Quote
Old 03-16-2024, 07:49 AM   #5
C. Yoder
Human being with feelings
 
Join Date: Jun 2011
Location: United States
Posts: 6
Default

Thank you all! That's very interesting to know about the mono vs stereo calculation. However, @schwa, that code worked perfectly to solve my problem, and feels a bit more clean since it's using the same calculation method. Again, much appreciated

@80icio - Thanks for pointing that out. The code above is actually stripped down for readability, and the full code has more of those niceties like undo points, update arrange, etc. but didn't want to clutter things for my question.
C. Yoder is offline   Reply With Quote
Old 03-16-2024, 01:52 PM   #6
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Quote:
Originally Posted by 80icio View Post
Code:
reaper.NF_AnalyzeTakeLoudness_IntegratedOnly(take)
it's a very slow function
Did you do any benchmarks/comparisons? (just curious )
nofish is offline   Reply With Quote
Old 03-16-2024, 02:58 PM   #7
80icio
Human being with feelings
 
Join Date: Mar 2016
Location: Italy
Posts: 322
Default

Quote:
Originally Posted by nofish View Post
Did you do any benchmarks/comparisons? (just curious )
this is 7 items (roughly 1 min length total)

reaper.NF_AnalyzeTakeLoudness_IntegratedOnly

2.1809449195862 sec

reaper.CalculateNormalization

0.056704998016357 sec

the difference is enormous.

reaper.NF_AnalyzeTakeLoudness_IntegratedOnly is an extension function,

You can clearly spot the speed difference if you compare these 2 actions as well

SWS/BR: Normalize loudness of selected items to -23 LUFS
VS.
Item properties: Normalize items (peak/RMS/LUFS)...
80icio is offline   Reply With Quote
Old 03-16-2024, 05:11 PM   #8
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Wow that's striking indeed, thanks.

I didn't code the original SWS loudness functions (that's Breeder), but I exposed them to ReaScript (NF_ = nofish), but I never compared them to the native ones which came to Reaper later on.
Nice that they are available now.
nofish is offline   Reply With Quote
Old 03-17-2024, 01:43 AM   #9
80icio
Human being with feelings
 
Join Date: Mar 2016
Location: Italy
Posts: 322
Default

Quote:
Originally Posted by nofish View Post
Wow that's striking indeed, thanks.

I didn't code the original SWS loudness functions (that's Breeder), but I exposed them to ReaScript (NF_ = nofish), but I never compared them to the native ones which came to Reaper later on.
Nice that they are available now.
I'd bet that function is slower due to the progress bar more than the analysis itself or the fact that is not a native one.

SWS/BR: Normalize loudness of selected items to -23 LUFS
Is the actual reason I started using REAPER.
No daws had anything like that, it saved me so much time on hundreds of mastering sessions.
That's why I deeply know the function.
Now I'm a full day Reaper user.
Thanks to you and Breeder
80icio is offline   Reply With Quote
Old 03-17-2024, 02:58 AM   #10
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

It spends 80% of the time doing GetAudioAccessorSamples reading 10ms or 200ms worth of audio. ~0.6ms each call a couple thousands of times quickly adds up. Changing it to read fewer, larger chunks could be worth a try.

EDIT: That was on a 4 mins Opus file (~800ms reading, ~200ms analyzing). It's negligible for WAV (~40ms reading, ~200ms analyzing). Unless it's resampled, then it's (~5000ms reading, ~200ms analyzing).

Reading fewer larger chunks or not doesn't impact speed in a noticeable way.

Does SWS loudness measurement really need to be done post-resampling to the project's sample rate?

Last edited by cfillion; 03-17-2024 at 03:27 AM.
cfillion is offline   Reply With Quote
Old 03-17-2024, 06:53 AM   #11
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Quote:
Originally Posted by cfillion View Post
Does SWS loudness measurement really need to be done post-resampling to the project's sample rate?
Hm good question.
I'm not as deep into the loudness stuff as I used to so I wouldn't know ad hoc.
Personally though I'd treat the ReaScript loudness functions as factually deprecated now with the native alternative.

(I looked once into deprecating them officially but didn't do in the end for a reason that eludes me currently lol. Maybe I have the started branch still on my dev machine, will check.)
nofish is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 01:37 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.