-
-
Notifications
You must be signed in to change notification settings - Fork 25
Description
✨ LUFS Normalization Improvements
This update introduces a new, more advanced loudness normalization system, inspired by services like YouTube Music. The primary goal is to provide a consistent and comfortable listening experience across all tracks by normalizing their perceived loudness, while also preventing audio clipping.
⚙️ How It Works
The normalization process follows these steps:
-
Analyze Audio: The source audio is first analyzed to measure its Integrated Loudness (LUFS-I) and True Peak (dBTP).
-
Select Target Mode: You can choose from several normalization modes:
OFF: No normalization is applied. The audio remains unchanged.YOUTUBE_STYLE: Targets -11.0 LUFS, matching the loudness standard used by YouTube Music for a modern, louder playback experience. This is the new default.AUTO: Targets -12.0 LUFS, a universal and safe default suitable for a wide range of content.MANUAL: Allows you to specify a custom LUFS target for full control.
-
Calculate Gain: The necessary gain is calculated to bring the track to the target loudness using the formula:
gain = targetLUFS - measuredLUFS. -
Prevent Clipping: A key improvement is the clip prevention mechanism. The gain is automatically limited to ensure the final audio has at least -1.0 dBTP of headroom. This prevents distortion, especially after the audio is re-encoded into a lossy format like AAC or MP3.
-
Apply Gain: The final calculated gain is applied during the audio conversion process (for example, via an FFmpeg filter).
💻 Code Example
Here is a simplified C++ example demonstrating the gain calculation logic:
enum class VolumeNormalizationMode {
OFF,
YOUTUBE_STYLE,
MANUAL,
AUTO
};
struct VolumeAnalysis {
double lufs_i; // Integrated loudness (LUFS)
double peak_db; // True peak (dBTP)
};
class VolumeAnalyzer {
public:
double calculateNormalizationGain(const VolumeAnalysis& analysis,
VolumeNormalizationMode mode,
double targetLufs)
{
if (mode == VolumeNormalizationMode::OFF) {
return 0.0;
}
double target;
switch (mode) {
case VolumeNormalizationMode::YOUTUBE_STYLE:
target = -11.0;
break;
case VolumeNormalizationMode::MANUAL:
target = targetLufs;
break;
case VolumeNormalizationMode::AUTO:
default:
target = -12.0;
break;
}
double gainDb = target - analysis.lufs_i;
// Clip prevention: ensure we have -1.0 dBTP of headroom
if ((analysis.peak_db + gainDb) > -1.0) {
gainDb = -1.0 - analysis.peak_db;
}
// Round to two decimal places
gainDb = std::round(gainDb * 100.0) / 100.0;
// Example FFmpeg command to apply the gain:
// ffmpeg -i input.wav -af "volume=<gainDb>dB" output.wav
// Replace <gainDb> with the value returned by calculateNormalizationGain
return gainDb;
}
};