Files
SnapOtter/apps/api/src/routes/tools/normalize-audio.ts
T
SnapOtterandGitHub 1548d475ca 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.
2026-06-16 14:46:23 +08:00

39 lines
1.3 KiB
TypeScript

import { extname } from "node:path";
import type { FastifyInstance } from "fastify";
import { z } from "zod";
import { audioOutputFor, runMediaTool } from "../../lib/media-tool.js";
import { createToolRoute } from "../tool-factory.js";
const settingsSchema = z.object({});
export function registerNormalizeAudio(app: FastifyInstance) {
createToolRoute(app, {
toolId: "normalize-audio",
settingsSchema,
process: async () => {
throw new Error("normalize-audio is v2-only");
},
processV2: async (ctx) => {
const origExt = extname(ctx.inputs[0].filename) || ".mp3";
const out = audioOutputFor(origExt);
const base = ctx.inputs[0].filename.replace(/\.[^.]+$/, "");
const outName = `${base}_normalized${out.ext}`;
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 };
},
});
}