feat: dataset builder
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
a1e9ce86a0
commit
ca9f3bbb83
+19
-5
@@ -2,27 +2,41 @@ import type { Audio } from "../core/types.ts";
|
||||
|
||||
const PEAK_TARGET = Math.pow(10, -1 / 20);
|
||||
|
||||
/** Sum stems (with per-stem gain in dB), then peak-normalise to -1 dBFS. */
|
||||
export function mixStems(stems: { audio: Audio; gainDb: number }[]): Audio {
|
||||
/**
|
||||
* Sum stems (with per-stem gain in dB), then peak-normalise to -1 dBFS.
|
||||
* Also returns each input stem as it contributed to the mix (gain and normalisation applied, padded to the
|
||||
* mix's channel count and length), so that sum(scaled) === mix, and the applied normalisation factor.
|
||||
*/
|
||||
export function mixStemsScaled(stems: { audio: Audio; gainDb: number }[]): { mix: Audio; scaled: Audio[]; scale: number } {
|
||||
if (stems.length === 0) throw new Error("mixStems: no stems");
|
||||
const sr = stems[0]!.audio.sampleRate;
|
||||
const nch = Math.max(...stems.map((s) => s.audio.channels.length));
|
||||
const n = Math.max(...stems.map((s) => s.audio.channels[0]?.length ?? 0));
|
||||
const out = Array.from({ length: nch }, () => new Float32Array(n));
|
||||
const scaled: Audio[] = [];
|
||||
for (const { audio, gainDb } of stems) {
|
||||
if (audio.sampleRate !== sr) throw new Error("mixStems: sample rate mismatch");
|
||||
const g = Math.pow(10, gainDb / 20);
|
||||
const own = Array.from({ length: nch }, () => new Float32Array(n));
|
||||
for (let c = 0; c < nch; c++) {
|
||||
const src = audio.channels[c] ?? audio.channels[0]!;
|
||||
const dst = out[c]!;
|
||||
for (let i = 0; i < src.length; i++) dst[i]! += src[i]! * g;
|
||||
const o = own[c]!;
|
||||
for (let i = 0; i < src.length; i++) { const v = src[i]! * g; dst[i]! += v; o[i] = v; }
|
||||
}
|
||||
scaled.push({ sampleRate: sr, channels: own });
|
||||
}
|
||||
let peak = 0;
|
||||
for (const ch of out) for (let i = 0; i < n; i++) peak = Math.max(peak, Math.abs(ch[i]!));
|
||||
const k = peak > 0 ? PEAK_TARGET / peak : 1;
|
||||
if (peak > 0) {
|
||||
const k = PEAK_TARGET / peak;
|
||||
for (const ch of out) for (let i = 0; i < n; i++) ch[i]! *= k;
|
||||
for (const s of scaled) for (const ch of s.channels) for (let i = 0; i < n; i++) ch[i]! *= k;
|
||||
}
|
||||
return { sampleRate: sr, channels: out };
|
||||
return { mix: { sampleRate: sr, channels: out }, scaled, scale: k };
|
||||
}
|
||||
|
||||
/** Sum stems (with per-stem gain in dB), then peak-normalise to -1 dBFS. */
|
||||
export function mixStems(stems: { audio: Audio; gainDb: number }[]): Audio {
|
||||
return mixStemsScaled(stems).mix;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user