mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-11-09 07:23:14 +00:00
- Implement comprehensive solo/mute export pipeline in audioEncoder.ts - Fix duplicate caption IDs by adding chunk-specific indices in handlers.ts - Prioritize native monitor selection by bounds in monitor_utils.cpp - Fix alignment for blur annotations extending beyond canvas boundaries - Resolve timeline selection desync and sidebar visibility for blur annotations - Fix memory leaks in audio waveform generation with AudioContext cleanup
51 lines
1.6 KiB
TypeScript
51 lines
1.6 KiB
TypeScript
/**
|
|
* Extracts waveform peaks from an audio file.
|
|
* We decode the audio file using the Web Audio API and calculate the max peaks for each sample.
|
|
*/
|
|
|
|
import { toFileUrl } from "@/components/video-editor/projectPersistence";
|
|
|
|
const waveformCache = new Map<string, number[]>();
|
|
|
|
export async function generateWaveform(audioPath: string, samples = 200): Promise<number[]> {
|
|
const cacheKey = `${audioPath}:${samples}`;
|
|
if (waveformCache.has(cacheKey)) {
|
|
return waveformCache.get(cacheKey)!;
|
|
}
|
|
|
|
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
|
|
try {
|
|
const response = await fetch(toFileUrl(audioPath));
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
|
|
// Use an offline audio context to decode the data
|
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
|
|
const channelData = audioBuffer.getChannelData(0); // Use the first channel
|
|
const blockSize = Math.floor(channelData.length / samples);
|
|
const peaks: number[] = [];
|
|
|
|
for (let i = 0; i < samples; i++) {
|
|
const start = i * blockSize;
|
|
let max = 0;
|
|
for (let j = 0; j < blockSize; j++) {
|
|
const value = Math.abs(channelData[start + j]);
|
|
if (value > max) max = value;
|
|
}
|
|
peaks.push(max);
|
|
}
|
|
|
|
waveformCache.set(cacheKey, peaks);
|
|
return peaks;
|
|
} catch (error) {
|
|
console.error('Failed to generate waveform:', error);
|
|
return new Array(samples).fill(0);
|
|
} finally {
|
|
try {
|
|
await audioContext.close();
|
|
} catch (e) {
|
|
// ignore close errors
|
|
}
|
|
}
|
|
}
|