summaryrefslogtreecommitdiff
path: root/apps/web/src/services/waveform-cache/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/services/waveform-cache/service.ts')
-rw-r--r--apps/web/src/services/waveform-cache/service.ts87
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();