fix(normalize-audio): preserve source sample rate after loudnorm (#248)

ffmpeg's loudnorm filter runs internally at 192 kHz and emits 192 kHz
unless the chain resamples back, so normalize-audio produced 192 kHz
output (~4.3x larger files) regardless of the input rate. Append
aresample to restore the input's sample rate; runMediaTool now exposes
the input audio sample rate to its args callback.
This commit is contained in:
SnapOtter
2026-06-16 14:46:23 +08:00
committed by GitHub
parent 8f6312c521
commit 1548d475ca
2 changed files with 21 additions and 4 deletions
+9 -2
View File
@@ -128,7 +128,11 @@ export async function runFfmpegWithProgress(
export async function runMediaTool(
ctx: ToolProcessCtxV2,
outName: string,
argsFor: (inPath: string, outPath: string, info: { durationS: number | null }) => string[],
argsFor: (
inPath: string,
outPath: string,
info: { durationS: number | null; audioSampleRate: number | null },
) => string[],
opts: { timeoutMs?: number } = {},
): Promise<MediaRunResult> {
const dir = join(ctx.scratchDir, "media");
@@ -139,7 +143,10 @@ export async function runMediaTool(
const outPath = join(dir, outName);
await runFfmpegWithProgress(
ctx,
argsFor(inPath, outPath, { durationS: info.durationS }),
argsFor(inPath, outPath, {
durationS: info.durationS,
audioSampleRate: info.streams.find((s) => s.type === "audio")?.sampleRate ?? null,
}),
info.durationS,
opts,
);
+12 -2
View File
@@ -19,8 +19,18 @@ export function registerNormalizeAudio(app: FastifyInstance) {
const base = ctx.inputs[0].filename.replace(/\.[^.]+$/, "");
const outName = `${base}_normalized${out.ext}`;
const { outPath } = await runMediaTool(ctx, outName, (inPath, outPath) => {
return ["-i", inPath, "-af", "loudnorm=I=-16:TP=-1.5:LRA=11", ...out.encodeArgs, outPath];
const { outPath } = await runMediaTool(ctx, outName, (inPath, outPath, info) => {
// loudnorm runs internally at 192 kHz and emits at 192 kHz unless we
// resample back, so restore the source rate to avoid inflating the file.
const sr = info.audioSampleRate ?? 44100;
return [
"-i",
inPath,
"-af",
`loudnorm=I=-16:TP=-1.5:LRA=11,aresample=${sr}`,
...out.encodeArgs,
outPath,
];
});
return { scratchPath: outPath, filename: outName, contentType: out.contentType };
},