Skip to content

Fully Automatic Advanced Volume Normalization #101

@Substancelib

Description

@Substancelib

✨ 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:

  1. Analyze Audio: The source audio is first analyzed to measure its Integrated Loudness (LUFS-I) and True Peak (dBTP).

  2. 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.
  3. Calculate Gain: The necessary gain is calculated to bring the track to the target loudness using the formula: gain = targetLUFS - measuredLUFS.

  4. 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.

  5. Apply Gain: The final calculated gain is applied during the audio conversion process (for example, via an FFmpeg filter).

Image Image

💻 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;
    }
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions