diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
| commit | 3e89d2998588f8e35c524e523ba2ee853f0f6475 (patch) | |
| tree | 57221f96c1259b3b07e3ddf8c1adefc502b9d8eb /apps/web/src/lib/media/waveform-summary.ts | |
| parent | 729d10592f4e802f91c555c309ff3ba4b119f843 (diff) | |
| download | opencut-3e89d2998588f8e35c524e523ba2ee853f0f6475.tar.gz | |
refactor: restructure files to their domains, new preview overlay system, and dep graph
Diffstat (limited to 'apps/web/src/lib/media/waveform-summary.ts')
| -rw-r--r-- | apps/web/src/lib/media/waveform-summary.ts | 233 |
1 files changed, 0 insertions, 233 deletions
diff --git a/apps/web/src/lib/media/waveform-summary.ts b/apps/web/src/lib/media/waveform-summary.ts deleted file mode 100644 index 522d6e04..00000000 --- a/apps/web/src/lib/media/waveform-summary.ts +++ /dev/null @@ -1,233 +0,0 @@ -"use client"; - -import { getSourceTimeAtClipTime } from "@/lib/retime"; -import type { RetimeConfig } from "@/lib/timeline"; - -const RMS_ANALYSIS_WINDOW_SECONDS = 0.02; -const DEFAULT_SOURCE_WAVEFORM_BUCKET_SIZE = 128; - -function computePeakBuckets({ - buffer, - buckets, -}: { - buffer: AudioBuffer; - buckets: SampleBucket[]; -}): number[] { - const channels = buffer.numberOfChannels; - const channelData: Float32Array[] = Array.from({ length: channels }, (_, c) => - buffer.getChannelData(c), - ); - - return buckets.map(({ bucketStart, bucketEnd }) => { - let peak = 0; - for (let c = 0; c < channels; c++) { - const data = channelData[c]; - for (let j = bucketStart; j < bucketEnd; j++) { - const abs = Math.abs(data[j] ?? 0); - if (abs > peak) { - peak = abs; - } - } - } - return peak; - }); -} - -export interface SampleBucket { - bucketStart: number; - bucketEnd: number; -} - -export interface SourceWaveformSummary { - sourceKey: string; - sampleRate: number; - totalSamples: number; - bucketSize: number; - amplitudes: Float32Array; -} - -export function buildWaveformSourceKey({ - kind, - id, -}: { - kind: "media" | "library"; - id: string; -}): string { - return `${kind}:${id}`; -} - -export function buildSourceWaveformSummary({ - sourceKey, - buffer, - bucketSize = DEFAULT_SOURCE_WAVEFORM_BUCKET_SIZE, -}: { - sourceKey: string; - buffer: AudioBuffer; - bucketSize?: number; -}): SourceWaveformSummary { - const safeBucketSize = Math.max(1, Math.floor(bucketSize)); - const bucketCount = Math.max(1, Math.ceil(buffer.length / safeBucketSize)); - const amplitudes = computePeakBuckets({ - buffer, - buckets: Array.from({ length: bucketCount }, (_, bucketIndex) => { - const bucketStart = bucketIndex * safeBucketSize; - const bucketEnd = Math.min(buffer.length, bucketStart + safeBucketSize); - return { bucketStart, bucketEnd }; - }), - }); - - return { - sourceKey, - sampleRate: buffer.sampleRate, - totalSamples: buffer.length, - bucketSize: safeBucketSize, - amplitudes: Float32Array.from(amplitudes), - }; -} - -export function buildWaveformSampleBuckets({ - clipLeftPx, - clipRightPx, - barCount, - pixelsPerSecond, - clipDurationSec, - sourceStartSec, - retime, - sampleRate, - maxSampleExclusive, - barStepPx, -}: { - clipLeftPx: number; - clipRightPx: number; - barCount: number; - pixelsPerSecond: number; - clipDurationSec: number; - sourceStartSec: number; - retime?: RetimeConfig; - sampleRate: number; - maxSampleExclusive: number; - barStepPx: number; -}): SampleBucket[] { - return Array.from({ length: barCount }, (_, index) => { - const bucketLeftPx = clipLeftPx + index * barStepPx; - const bucketRightPx = Math.min(clipRightPx, bucketLeftPx + barStepPx); - const clipStartSec = Math.max( - 0, - Math.min(clipDurationSec, bucketLeftPx / pixelsPerSecond), - ); - const clipEndSec = Math.max( - clipStartSec, - Math.min(clipDurationSec, bucketRightPx / pixelsPerSecond), - ); - const sourceBucketStartSec = - sourceStartSec + - getSourceTimeAtClipTime({ - clipTime: clipStartSec, - retime, - }); - const sourceBucketEndSec = - sourceStartSec + - getSourceTimeAtClipTime({ - clipTime: clipEndSec, - retime, - }); - - return { - bucketStart: Math.max(0, Math.floor(sourceBucketStartSec * sampleRate)), - bucketEnd: Math.min( - maxSampleExclusive, - Math.max(0, Math.ceil(sourceBucketEndSec * sampleRate)), - ), - }; - }); -} - -export function sampleSourceWaveformSummary({ - summary, - buckets, -}: { - summary: SourceWaveformSummary; - buckets: SampleBucket[]; -}): number[] { - return buckets.map(({ bucketStart, bucketEnd }) => { - if (bucketEnd <= bucketStart) { - return 0; - } - - const startIndex = Math.max( - 0, - Math.floor(bucketStart / summary.bucketSize), - ); - const endIndex = Math.min( - summary.amplitudes.length, - Math.max(startIndex + 1, Math.ceil(bucketEnd / summary.bucketSize)), - ); - - let maxAmplitude = 0; - for (let i = startIndex; i < endIndex; i++) { - const amplitude = summary.amplitudes[i] ?? 0; - if (amplitude > maxAmplitude) { - maxAmplitude = amplitude; - } - } - - return maxAmplitude; - }); -} - -export function computeRmsBuckets({ - buffer, - buckets, -}: { - buffer: AudioBuffer; - buckets: SampleBucket[]; -}): number[] { - const channels = buffer.numberOfChannels; - const maxWindowLength = Math.max( - 1, - Math.floor(buffer.sampleRate * RMS_ANALYSIS_WINDOW_SECONDS), - ); - - const channelData: Float32Array[] = new Array(channels); - for (let c = 0; c < channels; c++) { - channelData[c] = buffer.getChannelData(c); - } - - const result = new Array<number>(buckets.length); - - for (let i = 0; i < buckets.length; i++) { - const { bucketStart, bucketEnd } = buckets[i]; - const bucketLength = bucketEnd - bucketStart; - if (bucketLength <= 0) { - result[i] = 0; - continue; - } - - const windowLength = Math.max(1, Math.min(bucketLength, maxWindowLength)); - let maxMeanSquare = 0; - - for (let winStart = bucketStart; winStart < bucketEnd; ) { - const winEnd = Math.min(winStart + windowLength, bucketEnd); - const n = winEnd - winStart; - if (n > 0) { - let sum = 0; - for (let c = 0; c < channels; c++) { - const data = channelData[c]; - for (let j = winStart; j < winEnd; j++) { - const v = data[j]; - sum += v * v; - } - } - const meanSquare = sum / (n * channels); - if (meanSquare > maxMeanSquare) { - maxMeanSquare = meanSquare; - } - } - winStart = winEnd; - } - - result[i] = Math.sqrt(maxMeanSquare); - } - - return result; -} |
