// fetch + OfflineAudioContext + bucketize. See ADR-002 / research 02. // Uses a low-sample-rate mono context to keep memory bounded. import { bucketize } from '../utils/bucketize'; const SAMPLE_RATE = 22_050; const DEFAULT_BUCKETS = 1800; type WebkitWindow = typeof window & { webkitOfflineAudioContext?: typeof OfflineAudioContext; }; function getOfflineCtor(): typeof OfflineAudioContext { const w = window as WebkitWindow; const Ctor = w.OfflineAudioContext ?? w.webkitOfflineAudioContext; if (!Ctor) throw new Error('OfflineAudioContext is not supported'); return Ctor; } export async function decodePeaks( src: string, buckets: number = DEFAULT_BUCKETS, signal?: AbortSignal, ): Promise { const response = await fetch(src, { signal, credentials: 'same-origin' }); if (!response.ok) { throw new Error(`Failed to fetch audio for peaks (${response.status})`); } const arr = await response.arrayBuffer(); const Ctor = getOfflineCtor(); // Length must be ≥ 1; the actual length doesn't matter for decodeAudioData. const ctx = new Ctor(1, 1, SAMPLE_RATE); const audio = await ctx.decodeAudioData(arr); const channel = audio.getChannelData(0); return bucketize(channel, buckets); }