import { useState } from 'react' import { formatSeconds } from '../plot' import type { RenderResult, Source } from '../types' import { SpectrogramPlot } from './SpectrogramPlot' import { WaveformPlot } from './WaveformPlot' type Props = { source: Source result: RenderResult stale: boolean playing: string | null play: (label: string, which: ('A' | 'B')[]) => void stop: () => void } const COLOR_A = '#1f5fa8' const COLOR_B = '#d9641e' const SPANS: { label: string; sec: number | null }[] = [ { label: 'full clip', sec: null }, { label: '500 ms', sec: 0.5 }, { label: '100 ms', sec: 0.1 }, { label: '20 ms', sec: 0.02 }, { label: '5 ms', sec: 0.005 }, ] const DB_RANGE = 70 /** Play A and B, and compare their waveforms and spectrograms. */ export function PlaybackPanel({ source, result, stale, playing, play, stop }: Props) { const [spanIdx, setSpanIdx] = useState(0) const [center, setCenter] = useState(0.5) const fs = source.fs const duration = source.samples.length / fs const span = SPANS[spanIdx].sec ?? duration const start = SPANS[spanIdx].sec ? Math.min(Math.max(0, center * duration - span / 2), Math.max(0, duration - span)) : 0 let yMax = 0 for (let i = 0; i < source.samples.length; i++) yMax = Math.max(yMax, Math.abs(source.samples[i])) for (let i = 0; i < result.y.length; i++) yMax = Math.max(yMax, Math.abs(result.y[i])) yMax = Number(yMax.toPrecision(2)) || 1 const busy = playing !== null return (

Listen and compare

A is the source; B has the phase map applied. Both are played with the same gain{stale ? ' (B is being re-rendered)' : ''}. The waveforms differ by {Math.round(100 * result.relDiff)}% of A's RMS, while the spectrograms below, computed at the analysis resolution, should look the same.

{playing ? `Playing ${playing}…` : ''}
{SPANS[spanIdx].sec !== null && ( )}
Spectrograms at the analysis resolution, {DB_RANGE} dB range, shared scale
) }