feat: 1-to-1 message render + web data-lake backend
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
let current: HTMLMediaElement | null = null;
|
||||
|
||||
export function claimPlayback(element: HTMLMediaElement) {
|
||||
if (current && current !== element) {
|
||||
current.pause();
|
||||
}
|
||||
current = element;
|
||||
}
|
||||
|
||||
export function releasePlayback(element: HTMLMediaElement) {
|
||||
if (current === element) {
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
const BARS = 48;
|
||||
const MIN_BAR = 0.08;
|
||||
|
||||
export async function computeWaveform(
|
||||
url: string,
|
||||
bars = BARS
|
||||
): Promise<number[]> {
|
||||
const response = await fetch(url);
|
||||
const buffer = await response.arrayBuffer();
|
||||
const ctx = new AudioContext();
|
||||
try {
|
||||
const audio = await ctx.decodeAudioData(buffer);
|
||||
const data = audio.getChannelData(0);
|
||||
const block = Math.max(1, Math.floor(data.length / bars));
|
||||
const peaks: number[] = [];
|
||||
for (let i = 0; i < bars; i++) {
|
||||
const start = i * block;
|
||||
let max = 0;
|
||||
for (let j = 0; j < block; j++) {
|
||||
const value = Math.abs(data[start + j] ?? 0);
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
peaks.push(max);
|
||||
}
|
||||
const norm = Math.max(...peaks, 0.0001);
|
||||
return peaks.map((peak) => Math.max(MIN_BAR, peak / norm));
|
||||
} finally {
|
||||
await ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
export function flatWaveform(bars = BARS): number[] {
|
||||
return Array.from({ length: bars }, () => 0.3);
|
||||
}
|
||||
Reference in New Issue
Block a user