Files
Vance IngallsandClaude Opus 5.5 9244c47328 feat(studio): normalize loudness and duck under voice, with a limiter report (4/8) (#4816)
* feat(studio-server): share loudness measurement with an absolute target and video support

normalize-audio moves its measure/plan code into @hyperframes/studio-server/loudness,
gains an absolute mode (--lufs, default -16, clamped at +12 dB and -1.5 dBTP) and
accepts <video> clips with sound. Adds the @hyperframes/core/audible-video subpath.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(studio-server): add a measure-only loudness normalize endpoint

POST /projects/:id/loudness/normalize measures a clip's played window (audio or
video) and answers the -16 LUFS data-volume plan; Studio writes the attribute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: register the loudness and audible-video subpaths in their descriptors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format the loudness and audible-video subpath descriptors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(studio): add Normalize loudness and Duck under voice to the clip menu

Normalize measures the clip on studio-server and writes data-volume as one undoable
edit; Duck toggles a carve against every overlapping voice (audio or video with
sound) at the default strength. Both live in clipMenuAudioItems with one hook-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(studio): tighten carveOutput docs to the package comment share

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(producer): report how far the true-peak limiter lowered the export

The AAC true-peak pass now returns its attenuation; the render logs "Audio lowered
by X.X dB to stay under -1 dBTP" and sets RenderJob.audioLoweredDb, which Studio
servers carry in the complete SSE event and the /renders record. Also exports
useAudioMetersVisible from @hyperframes/studio for hosts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(studio-server): scan HTML start tags without a backtracking regex

The start-tag regex backtracked quadratically on '<' followed by many spaces
(CodeQL js/polynomial-redos). A linear character scan reads the same name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(studio): refuse to normalize a volume-automated clip and honour save outcomes

A volume lane owns the clip's gain in preview and render, so writing
data-volume changed nothing while the toast claimed success; Normalize now
refuses with a clear message. Normalize and Duck also check each quiet-save
outcome, stop at the first failed or refused write and report it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(core): drop the duplicate audible-video subpath descriptor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 03:51:34 +00:00

35 lines
1.4 KiB
TypeScript

import { defineConfig } from "tsup";
export default defineConfig({
entry: {
index: "src/index.ts",
"helpers/screenshotClip": "src/helpers/screenshotClip.ts",
"helpers/mediaCodecMap": "src/helpers/mediaCodecMap.ts",
"helpers/proxyTranscoder": "src/helpers/proxyTranscoder.ts",
"helpers/loudness": "src/helpers/loudness.ts",
"helpers/mediaProxyPreview": "src/helpers/mediaProxyPreview.ts",
"helpers/manualEditsRenderScript": "src/helpers/manualEditsRenderScript.ts",
"helpers/studioMotionRenderScript": "src/helpers/studioMotionRenderScript.ts",
"helpers/draftMarkers": "src/helpers/draftMarkers.ts",
"helpers/finiteMutation": "src/helpers/finiteMutation.ts",
"helpers/sourceMutation": "src/helpers/sourceMutation.ts",
"helpers/safePath": "src/helpers/safePath.ts",
"helpers/hfIdPersist": "src/helpers/hfIdPersist.ts",
"history/index": "src/history/index.ts",
},
format: ["esm"],
outDir: "dist",
target: "node22",
platform: "node",
bundle: true,
// Split shared chunks so every entry (index + helper subpaths) imports ONE
// copy of stateful modules — proxyTranscoder's in-flight dedupe, transcode
// semaphore, and negative cache must be process-global, not per-entry.
// With splitting off, each entry inlined its own copy and a pre-warm from
// media-proxy-preview couldn't dedupe against a route's proxy-transcoder.
splitting: true,
sourcemap: true,
clean: true,
dts: true,
});