diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-18 01:40:18 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-18 01:40:18 +0200 |
| commit | 011f51051c9750706978d6c89ce84dd15560c335 (patch) | |
| tree | 2e7b23ebebb798934d6cb6aeace855f31699b633 /apps/web/src/services | |
| parent | c46d28891fee62ca2d3132da876af6411a41d9da (diff) | |
| download | opencut-011f51051c9750706978d6c89ce84dd15560c335.tar.gz | |
feat: clip volume line and timeline waveform improvements
Diffstat (limited to 'apps/web/src/services')
| -rw-r--r-- | apps/web/src/services/waveform-cache/service.ts | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/apps/web/src/services/waveform-cache/service.ts b/apps/web/src/services/waveform-cache/service.ts new file mode 100644 index 00000000..3b7d34f3 --- /dev/null +++ b/apps/web/src/services/waveform-cache/service.ts @@ -0,0 +1,87 @@ +"use client"; + +import { createAudioContext } from "@/lib/media/audio"; +import { + buildSourceWaveformSummary, + type SourceWaveformSummary, +} from "@/lib/media/waveform-summary"; + +interface GetSourceWaveformSummaryArgs { + sourceKey: string; + audioBuffer?: AudioBuffer; + sourceFile?: File; + audioUrl?: string; +} + +export class WaveformCache { + private summaries = new Map<string, Promise<SourceWaveformSummary>>(); + + getSourceSummary({ + sourceKey, + audioBuffer, + sourceFile, + audioUrl, + }: GetSourceWaveformSummaryArgs): Promise<SourceWaveformSummary> { + const existing = this.summaries.get(sourceKey); + if (existing) { + return existing; + } + + const promise = this.buildSummary({ + sourceKey, + audioBuffer, + sourceFile, + audioUrl, + }).catch((error) => { + this.summaries.delete(sourceKey); + throw error; + }); + + this.summaries.set(sourceKey, promise); + return promise; + } + + clearSource({ sourceKey }: { sourceKey: string }): void { + this.summaries.delete(sourceKey); + } + + clearAll(): void { + this.summaries.clear(); + } + + private async buildSummary({ + sourceKey, + audioBuffer, + sourceFile, + audioUrl, + }: GetSourceWaveformSummaryArgs): Promise<SourceWaveformSummary> { + if (audioBuffer) { + return buildSourceWaveformSummary({ sourceKey, buffer: audioBuffer }); + } + + let arrayBuffer: ArrayBuffer | null = null; + if (sourceFile) { + arrayBuffer = await sourceFile.arrayBuffer(); + } else if (audioUrl) { + const response = await fetch(audioUrl); + if (!response.ok) { + throw new Error(`Failed to fetch waveform source: ${response.status}`); + } + arrayBuffer = await response.arrayBuffer(); + } + + if (!arrayBuffer) { + throw new Error(`No waveform source available for ${sourceKey}`); + } + + const audioContext = createAudioContext(); + try { + const buffer = await audioContext.decodeAudioData(arrayBuffer.slice(0)); + return buildSourceWaveformSummary({ sourceKey, buffer }); + } finally { + void audioContext.close(); + } + } +} + +export const waveformCache = new WaveformCache(); |
