如果不造轮,
单行命令
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;
}还没有人送礼物,鼓励一下作者吧


