This example shows how to use tools from Audio System Toolbox (TM) to measure loudness, loudness range, and true-peak value. It also shows how to normalize audio to meet the EBU R 128 standard compliance.
Volume normalization was traditionally performed by looking at peak signal measurements. However, this method had the drawback that excessively compressed audio could pass a signal-level threshold but still be very loud to hear. The result was a loudness war, where recordings tended to be louder than before and inconsistent across genres.
The modern solution to the loudness war is to measure the perceived loudness in combination with a true-peak level measurement. International standards like ITU BS.1770-4, EBU R 128, and ATSC A/85 have been developed to standardize loudness measurements based on the power of the audio signal. Many countries have already passed legislations for compliance with broadcast standards on loudness levels.
In this example, you measure loudness and supplementary parameters for both offline (file-based) and live (streaming) audio signals. You also see ways to normalize audio to be compliant with target levels.
Audio System Toolbox (TM) enables you to measure loudness and associated parameters according to the EBU R 128 standard. This standard defines the following measures of loudness:
Momentary loudness: Uses a sliding window of length 400 ms.
Short-term loudness: Uses a sliding window of length 3 s.
Integrated loudness: Aggregate loudness from start till end.
Loudness range: Quantifies variation of loudness on a macroscopic timescale.
True-peak value: Peak sample level of interpolated signal.
For a more detailed description of these parameters, refer to the documentation for EBU R 128 standard.
For cases where you already have the recorded audio samples, you can use the integratedLoudness function to measure loudness. It returns the integrated loudness, in units of LUFS, and loudness range, in units of LU, of the complete audio file.
[x, fs] = audioread('RockGuitar-16-44p1-stereo-72secs.wav'); [loudness, LRA] = integratedLoudness(x, fs); fprintf('Loudness before normalization: %.1f LUFS\n', loudness);
Loudness before normalization: -8.2 LUFS
EBU R 128 defines the target loudness level to be -23 LUFS. The loudness of the audio file is clearly above this level. A simple level reduction operation can be used to normalize the loudness.
target = -23; gaindB = target - loudness; gain = 10^(gaindB/20); xn = x.*gain; audiowrite('RockGuitar_normalized.wav', xn, fs);
The loudness of the new audio file is at the target level.
[x, fs] = audioread('RockGuitar_normalized.wav'); loudness = integratedLoudness(x, fs); fprintf('Loudness after normalization: %.1f LUFS\n', loudness);
Loudness after normalization: -23.0 LUFS
For streaming audio, EBU R 128 defines momentary and short-term loudness. You can use the loudnessMeter System object to measure momentary loudness, short-term loudness, integrated loudness, loudness range, and true-peak value of a live audio signal.
First, stream the audio signal to your sound card and measure its loudness using
visualize method of
loudnessMeter opens a user interface (UI) that displays all the loudness-related measurements as the simulation progresses.
reader = dsp.AudioFileReader('RockGuitar-16-44p1-stereo-72secs.wav', ... 'SamplesPerFrame', 1024); fs = reader.SampleRate; inputLoudness = loudnessMeter('SampleRate', fs); player = audioDeviceWriter('SampleRate', fs); runningMax = dsp.MovingMaximum('SpecifyWindowLength', false); visualize(inputLoudness); while ~isDone(reader) audioIn = reader(); [loudness,~,~,~,tp] = inputLoudness(audioIn); maxTP = runningMax(tp); player(audioIn); end fprintf('Maximum true-peak value before normalization: %.1f dBTP\n', ... maxTP(end)); release(reader); release(player);
Maximum true-peak value before normalization: -0.3 dBTP
As you can see on the UI, the loudness of the audio stream is clearly above the -23 LUFS threshold. Its maximum true-peak level of -0.3 dBTP is also above the threshold of -1 dBTP specified by EBU R 128. Normalizing the loudness of a live audio stream is trickier than normalizing the loudness of a file. One way to help get the loudness value close to a target threshold is to use an Automatic Gain Controller (AGC). In the following code, you use the audioexample.AGC System object to normalize the power of an audio signal to -23 dB. The AGC estimates the audio signal's power by looking at the previous 400 ms, which is the window size used to calculate momentary loudness. There are two loudness meters used in this example - one for the input to AGC and one for the output from AGC. The UIs for the two loudness meters may launch at the same location on your screen, so you will have to move one to the side to compare the measured loudness before and after AGC.
outputLoudness = loudnessMeter('SampleRate', fs); gainController = audioexample.AGC('DesiredOutputPower', -23, ... 'AveragingLength', 0.4*fs, 'MaxPowerGain', 20); reset(inputLoudness); % Reuse the same loudness meter from before reset(runningMax); visualize(inputLoudness); visualize(outputLoudness); while ~isDone(reader) audioIn = reader(); loudnessBeforeNorm = inputLoudness(audioIn); [audioOut, gain] = gainController(audioIn); [loudnessAfterNorm,~,~,~,tp] = outputLoudness(audioOut); maxTP = runningMax(tp); player(audioOut); end fprintf('Maximum true-peak value after normalization: %.1f dBTP\n', ... maxTP(end)); release(reader); release(player);
Maximum true-peak value after normalization: -3.7 dBTP
Using AGC not only brought the loudness of the audio close to the target of -23 LUFS, it also got the maximum true-peak value below the allowed -1 dBTP. In some cases, the maximum true-peak value remains above -1 dBTP although the loudness is at or below -23 LUFS. For such scenarios, you can pass the audio through a limiter.