Skip to content

Commit f904d9b

Browse files
vanceingallsclaude
andcommitted
fix(studio-server): decode the same audio stream the peak map probes
The channel probe read stream a:0 while the decode left stream choice to ffmpeg, which picks a later stereo stream over a mono first one; every frame was then read at the wrong width, doubling the bins and taking the other stream's level. Both commands now select a:0, and the cache key moves to peaks-v2 so maps built from the wrong stream are rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 05737b7 commit f904d9b

2 files changed

Lines changed: 41 additions & 2 deletions

File tree

‎packages/studio-server/src/helpers/peakMap.test.ts‎

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe("buildPeakMapCacheKey", () => {
6666
expect(a).toBe(buildPeakMapCacheKey("a/talk.mp4", { size: 10, mtimeMs: 1 }));
6767
expect(a).not.toBe(buildPeakMapCacheKey("a/talk.mp4", { size: 11, mtimeMs: 1 }));
6868
expect(a).not.toBe(buildPeakMapCacheKey("a/talk.mp4", { size: 10, mtimeMs: 2 }));
69-
expect(a).toMatch(/^peaks-v1_a_talk\.mp4_10-1\.json$/);
69+
expect(a).toMatch(/^peaks-v2_a_talk\.mp4_10-1\.json$/);
7070
});
7171
});
7272

@@ -109,4 +109,41 @@ describe("decodePeakMap", () => {
109109
}
110110
},
111111
);
112+
113+
it.skipIf(!ffmpeg)(
114+
"decodes the probed first audio stream when a louder stereo stream follows it",
115+
async () => {
116+
const dir = mkdtempSync(join(tmpdir(), "hf-peaks-"));
117+
try {
118+
const file = join(dir, "multi.mkv");
119+
execFileSync(ffmpeg ?? "ffmpeg", [
120+
"-v",
121+
"error",
122+
"-f",
123+
"lavfi",
124+
"-i",
125+
"aevalsrc=0.1*sin(2*PI*440*t):s=48000:d=1",
126+
"-f",
127+
"lavfi",
128+
"-i",
129+
"aevalsrc=0.9*sin(2*PI*440*t)|0.9*sin(2*PI*440*t):s=48000:d=1",
130+
"-map",
131+
"0:a",
132+
"-map",
133+
"1:a",
134+
"-c:a",
135+
"pcm_f32le",
136+
"-disposition:a",
137+
"0",
138+
file,
139+
]);
140+
const peaks = await decodePeakMap(file);
141+
expect(peaks.length).toBeGreaterThanOrEqual(19);
142+
expect(peaks.length).toBeLessThanOrEqual(21);
143+
expect(Math.max(...peaks)).toBeCloseTo(0.1, 2);
144+
} finally {
145+
rmSync(dir, { recursive: true, force: true });
146+
}
147+
},
148+
);
112149
});

‎packages/studio-server/src/helpers/peakMap.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
55
export const PEAK_BIN_SECONDS = 0.05;
66
const PEAK_SAMPLE_RATE = 48_000;
77
const execFileAsync = promisify(execFile);
8-
const PEAK_MAP_CACHE_VERSION = "peaks-v1";
8+
const PEAK_MAP_CACHE_VERSION = "peaks-v2";
99

1010
/**
1111
* Running max-abs per bin over interleaved f32le at the source's own channel
@@ -92,6 +92,8 @@ export async function decodePeakMap(mediaPath: string): Promise<number[]> {
9292
"error",
9393
"-i",
9494
mediaPath,
95+
"-map",
96+
"0:a:0",
9597
"-vn",
9698
"-ar",
9799
String(PEAK_SAMPLE_RATE),

0 commit comments

Comments
 (0)