import type { PhaseMapSpec } from '../phasemap' import type { RenderResult } from '../types' import { LinePlot } from './LinePlot' type Props = { sigmaMs: number setSigmaMs: (v: number) => void map: PhaseMapSpec setMap: (m: PhaseMapSpec) => void fmax: number result: RenderResult | null } const SIGMA_LOG_MIN = Math.log10(0.3) const SIGMA_LOG_MAX = Math.log10(50) const sig3 = (x: number) => Number(x.toPrecision(3)) const MAP_COLOR = '#d9641e' const THRESHOLD_MS = 1 /** Window width, phase map choice and parameters, and the plots that describe the resulting filter. */ export function AnalysisPanel({ sigmaMs, setSigmaMs, map, setMap, fmax, result }: Props) { const sigmaF = 1000 / (2 * Math.PI * sigmaMs) const setKind = (kind: PhaseMapSpec['kind']) => { if (kind === map.kind) return if (kind === 'random') setMap({ kind, smoothBands: 8, seed: 1, strength: 1 }) else if (kind === 'delay') setMap({ kind, delayMs: sig3(sigmaMs / 4) }) else setMap({ kind, delayAtNyquistMs: sig3(sigmaMs / 4) }) } return (

Filter bank and phase map

The sound is split into overlapping Gaussian bands of width sigma in time (and 1 / (2 pi sigma) in frequency). Sound B is made by rotating the phase of each band by phi(f) and adding the bands back together, which leaves every band's envelope unchanged. For a fixed map this is an all-pass filter; its group delay, minus the slope of the phase, is what smears transients. Peaks of group delay beyond about {THRESHOLD_MS} ms are reported to be audible on impulsive sounds. Because the map is sampled at the band centers, it must change by less than about a radian from one band to the next; the magnitude plot shows when it does not. For a pure delay this means delays shorter than sigma, so the achievable group delay grows with the window width.

{map.kind === 'random' && ( <> seed {map.seed} )} {map.kind === 'delay' && ( )} {map.kind === 'chirp' && ( )}
{result && result.maxMagDeviationDb > 1 && (
The magnitude response deviates by up to {result.maxMagDeviationDb.toFixed(1)} dB, so B is not just a phase change of A. The map changes too quickly across neighbouring bands for this window: increase sigma, increase the smoothing, or reduce the delay.
)} {result && (
Phase map phi(f), wrapped to (-180°, 180°]
(v * 180) / Math.PI), color: MAP_COLOR, dots: result.map.f.length <= 400, width: 1 }]} xRange={[0, fmax]} yRange={[-190, 190]} xLabel="frequency (Hz)" yLabel="degrees" zeroLine />
Magnitude response of the equivalent filter (should stay near 0 dB)
Group delay of the equivalent filter (largest {result.maxGroupDelayMs.toFixed(2)} ms)
)}
) }