1个回答

怎么能把不同声音大小的mp3音频统一成一个音量?

汇帮办公软件
23个点赞 👍

如果不造轮,

直接用 FFmpegEBU R128(双遍校准):

单行命令

ffmpeg -i input.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11:dual_mono=false:print_format=summary -ar 48000 -ac 2 -b:a 192k -y pass1.mp3 && ffmpeg -i input.mp3 -af "loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-16:measured_LRA=11:measured_TP=-2:measured_thresh=-26:offset=0.0:linear=true:print_format=summary" -ar 48000 -ac 2 -b:a 192k -y output.mp3


ffmpeg -i input.mp3 -af loudnorm=I=-16:TP=-1.5:LRA=11:dual_mono=false:print_format=summary -ar 48000 -ac 2 -b:a 192k -y pass1.mp3 && ffmpeg -i input.mp3 -af "loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-16:measured_LRA=11:measured_TP=-2:measured_thresh=-26:offset=0.0:linear=true:print_format=summary" -ar 48000 -ac 2 -b:a 192k -y output.mp3



自己造轮子?

C:\00OO>.\mp3_normalizer.exe .\_SAM-HU_HP8OUCE_.mp3 .\samhui_halfpd8ounce-20.mp3 -20

Samplerate=48000 Hz, Channels=2

Current RMS=-9.47 dBFS, Peak=0.00 dBFS

Target RMS=-20.00 dBFS, Applied gain=-10.53 dB (lin=0.297472)

Done.


C:\00OO>.\mp3_normalizer.exe .\_SAM-HU_HP8OUCE_.mp3  .\samhui_halfpd8ounce-20.mp3  -20
Samplerate=48000 Hz, Channels=2
Current RMS=-9.47 dBFS, Peak=0.00 dBFS
Target RMS=-20.00 dBFS, Applied gain=-10.53 dB (lin=0.297472)
Done.


版权授权: WTFPL

// LICENSING:WTFPL
//
// mp3_normalizer.cpp
// Usage:
// mp3_normalizer.exe input.mp3 output.mp3  -18   // target RMS dBFS (default -18)
//


#include <mpg123.h>
#include <lame/lame.h>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstdint>
#include <vector>
#include <string>
#include <iostream>

static void die(const char* msg) { std::fprintf(stderr, "Error: %s\n", msg); std::exit(1); }

struct Stats {
    long samplerate = 0;
    int channels = 0;
    uint64_t samples = 0;       // total samples across channels
    long double sumsq = 0.0L;   // sum of squares of normalized samples [-1,1]
    long double peak = 0.0L;    // max abs in [-1,1]
};

static void configure_formats(mpg123_handle* mh) {
    mpg123_param(mh, MPG123_FORCE_RATE, 0, 0.0);     // keep original
    mpg123_format_none(mh);
    const long* rates = nullptr; size_t count = 0;    // <-- const
    mpg123_rates(&rates, &count);
    for (size_t i = 0; i < count; ++i) {
        mpg123_format(mh, rates[i],
                      MPG123_MONO | MPG123_STEREO,
                      MPG123_ENC_SIGNED_16);
    }
}

static void pass_stats(mpg123_handle* mh, Stats& st) {
    const size_t BUF_SAMPLES = 1152 * 16;
    std::vector<short> buf(BUF_SAMPLES * 2);
    size_t done = 0;
    int ret = MPG123_OK;
    bool got_fmt = false;

    while (true) {
        ret = mpg123_read(mh, reinterpret_cast<unsigned char*>(buf.data()),
                          buf.size() * sizeof(short), &done);

        if (ret == MPG123_NEW_FORMAT) {
            long rate = 0; int ch = 0; int enc = 0;
            mpg123_getformat(mh, &rate, &ch, &enc);
            st.samplerate = rate; st.channels = ch;
            got_fmt = true;
            continue;
        }

        if (ret == MPG123_OK || (ret == MPG123_DONE && done > 0)) {
            if (!got_fmt) {
                long rate = 0; int ch = 0; int enc = 0;
                mpg123_getformat(mh, &rate, &ch, &enc);
                st.samplerate = rate; st.channels = ch;
                got_fmt = true;
            }
            size_t ns = done / sizeof(short);
            st.samples += ns;
            for (size_t i = 0; i < ns; ++i) {
                long double x = static_cast<long double>(buf[i]) / 32768.0L;
                st.sumsq += x * x;
                long double ax = fabsl(x);
                if (ax > st.peak) st.peak = ax;
            }
            if (ret == MPG123_DONE) break;
            continue;
        }

        if (ret == MPG123_DONE) break;

        die(mpg123_plain_strerror(mpg123_errcode(mh)));
    }

    if (st.samples == 0) die("empty or unreadable audio");
}

static long double rms_dbfs(const Stats& st) {
    long double mean_sq = st.sumsq / (st.samples > 0 ? (long double)st.samples : 1.0L);
    long double rms = sqrtl(mean_sq);
    if (rms <= 0.0L) return -120.0L;
    return 20.0L * log10l(rms);
}

static void rewind_and_reconfig(mpg123_handle*& mh, const std::string& path) {
    mpg123_close(mh);
    if (mpg123_open(mh, path.c_str()) != MPG123_OK) die("mpg123 reopen failed");
    configure_formats(mh);
}

static void pass_encode(mpg123_handle* mh, lame_t lame, FILE* fout,
                        long double lin_gain, int expected_channels, long expected_rate) {
    const size_t BUF_SAMPLES = 1152 * 16;
    std::vector<short> inbuf(BUF_SAMPLES * 2);
    std::vector<short> work(inbuf.size());
    std::vector<unsigned char> mp3buf(1 << 16);

    size_t done = 0;
    int ret = MPG123_OK;
    bool got_fmt = false;

    while (true) {
        ret = mpg123_read(mh, reinterpret_cast<unsigned char*>(inbuf.data()),
                          inbuf.size() * sizeof(short), &done);

        if (ret == MPG123_NEW_FORMAT) {
            long rate = 0; int ch = 0; int enc = 0;
            mpg123_getformat(mh, &rate, &ch, &enc);
            got_fmt = true;
            if (rate != expected_rate || ch != expected_channels) {
                die("Input changed samplerate/channels mid-stream; not supported.");
            }
            continue;
        }

        if (ret == MPG123_OK || (ret == MPG123_DONE && done > 0)) {
            if (!got_fmt) {
                long rate = 0; int ch = 0; int enc = 0;
                mpg123_getformat(mh, &rate, &ch, &enc);
                if (rate != expected_rate || ch != expected_channels) {
                    die("Unexpected samplerate/channels at start of pass_encode.");
                }
                got_fmt = true;
            }
            size_t ns = done / sizeof(short);

            for (size_t i = 0; i < ns; ++i) {
                long double x = (long double)inbuf[i] * lin_gain;
                if (x >  32767.0L) x =  32767.0L;
                if (x < -32768.0L) x = -32768.0L;
                work[i] = (short)llround(x);
            }

            int wrote = 0;
            if (expected_channels == 2) {
                wrote = lame_encode_buffer_interleaved(
                    lame, work.data(), (int)(ns / 2), mp3buf.data(), (int)mp3buf.size());
            } else {
                wrote = lame_encode_buffer(
                    lame, work.data(), nullptr, (int)ns, mp3buf.data(), (int)mp3buf.size());
            }
            if (wrote < 0) die("lame_encode_buffer failed");
            if (wrote > 0) fwrite(mp3buf.data(), 1, wrote, fout);

            if (ret == MPG123_DONE) break;
            continue;
        }

        if (ret == MPG123_DONE) break;

        die(mpg123_plain_strerror(mpg123_errcode(mh)));
    }

    int wrote = lame_encode_flush(lame, mp3buf.data(), (int)mp3buf.size());
    if (wrote < 0) die("lame_encode_flush failed");
    if (wrote > 0) fwrite(mp3buf.data(), 1, wrote, fout);
}

int main(int argc, char** argv) {
    if (argc < 3 || argc > 4) {
        std::fprintf(stderr, "Usage: %s <in.mp3> <out.mp3> [target_rms_dbfs default=-18]\n", argv[0]);
        return 1;
    }
    std::string inpath = argv[1];
    std::string outpath = argv[2];
    double target_db = (argc == 4) ? std::atof(argv[3]) : -18.0;

    if (mpg123_init() != MPG123_OK) die("mpg123_init");
    mpg123_handle* mh = mpg123_new(nullptr, nullptr);
    if (!mh) die("mpg123_new");

    if (mpg123_open(mh, inpath.c_str()) != MPG123_OK) die("mpg123_open");
    configure_formats(mh);

    Stats st;
    pass_stats(mh, st);
    long double cur_rms_db = rms_dbfs(st);
    long double cur_peak   = st.peak;

    long double gain_db  = (long double)target_db - cur_rms_db;
    long double lin_gain = powl(10.0L, gain_db / 20.0L);
    const long double max_peak = 0.99L;
    if (cur_peak > 0.0L) {
        long double max_lin = max_peak / cur_peak;
        if (lin_gain > max_lin) lin_gain = max_lin;
    }

    std::fprintf(stderr, "Samplerate=%ld Hz, Channels=%d\n", st.samplerate, st.channels);
    std::fprintf(stderr, "Current RMS=%.2Lf dBFS, Peak=%.2Lf dBFS\n",
                 cur_rms_db, (cur_peak > 0 ? 20.0L * log10l(cur_peak) : -120.0L));
    std::fprintf(stderr, "Target RMS=%.2f dBFS, Applied gain=%.2Lf dB (lin=%.6Lf)\n",
                 target_db, 20.0L * log10l(lin_gain), lin_gain);

    rewind_and_reconfig(mh, inpath);

    lame_t lame = lame_init();
    if (!lame) die("lame_init failed");
    lame_set_in_samplerate(lame, (int)st.samplerate);
    lame_set_num_channels(lame, st.channels);
    lame_set_quality(lame, 2);
    lame_set_brate(lame, 192);
    lame_set_mode(lame, st.channels == 1 ? MONO : JOINT_STEREO);
    if (lame_init_params(lame) < 0) die("lame_init_params failed");

    FILE* fout = std::fopen(outpath.c_str(), "wb");
    if (!fout) die("cannot open output");

    pass_encode(mh, lame, fout, lin_gain, st.channels, st.samplerate);

    std::fclose(fout);
    lame_close(lame);
    mpg123_close(mh);
    mpg123_delete(mh);
    mpg123_exit();

    std::fprintf(stderr, "Done.\n");
    return 0;
}

麦文学
自由评论 (0)
分享
Copyright © 2022 GreatFire.org