freq follow thing on spectrum

This commit is contained in:
Boof2015
2026-04-08 19:07:29 -04:00
parent fe29ac5a86
commit adf8c28796
8 changed files with 727 additions and 22 deletions
+3 -1
View File
@@ -3,6 +3,7 @@ import type { SpectrogramClarityMode, SpectrogramScaleMode } from './spectrogram
import type { VUMeterMode, VUMeterOrientation } from './vumeter'
import type { LUFSMeterMode } from './lufsmeter'
import { DEFAULT_WAVEFORM_MODE, type WaveformMode } from './waveform'
import { DEFAULT_SPECTRUM_PEAK_INFO_MODE, type SpectrumPeakInfoMode } from './spectrum'
export interface ScopeSettings {
spectrum: {
@@ -15,6 +16,7 @@ export interface ScopeSettings {
smoothing: number
fillGradient: boolean
showSideLine: boolean
peakInfoMode: SpectrumPeakInfoMode
}
oscilloscope: {
pitchLock: boolean
@@ -60,7 +62,7 @@ export interface ScopeSettings {
}
export const DEFAULT_SCOPE_SETTINGS: ScopeSettings = {
spectrum: { fftSize: 2048, tiltDbPerOctave: 2.0, heatmap: false, heatmapTiltDbPerOctave: 2.0, heatmapSmoothing: 0.5, showGrid: true, smoothing: 0.9, fillGradient: true, showSideLine: false },
spectrum: { fftSize: 2048, tiltDbPerOctave: 2.0, heatmap: false, heatmapTiltDbPerOctave: 2.0, heatmapSmoothing: 0.5, showGrid: true, smoothing: 0.9, fillGradient: true, showSideLine: false, peakInfoMode: DEFAULT_SPECTRUM_PEAK_INFO_MODE },
oscilloscope: { pitchLock: true, underfillEnabled: false, showGrid: true, lineWidth: 2 },
vectorscope: { mode: 'lissajous', multiband: false, showGrid: true, persistence: 0.10, lineWidth: 1.5 },
spectrogram: { fftSize: 2048, scrollSpeed: 2, clarityMode: 'sharper', scaleMode: 'log', colorScheme: 'heat' },
+58
View File
@@ -8,6 +8,34 @@ export const MIN_SPECTRUM_HEATMAP_TILT_DB_PER_OCTAVE = -2.0
export const MAX_SPECTRUM_HEATMAP_TILT_DB_PER_OCTAVE = 8.0
export const SPECTRUM_HEATMAP_TILT_STEP = 0.1
export type SpectrumPeakInfoMode = 'off' | 'on' | 'following'
export interface SpectrumPitchInfo {
note: string
octave: number
cents: number
}
export interface SpectrumPeakInfo {
db: number
frequencyHz: number
normalizedX: number
normalizedY: number
key: string
}
const NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] as const
export const DEFAULT_SPECTRUM_PEAK_INFO_MODE: SpectrumPeakInfoMode = 'off'
export function isSpectrumPeakInfoMode(value: unknown): value is SpectrumPeakInfoMode {
return value === 'off' || value === 'on' || value === 'following'
}
export function normalizeSpectrumPeakInfoMode(value: unknown): SpectrumPeakInfoMode {
return isSpectrumPeakInfoMode(value) ? value : DEFAULT_SPECTRUM_PEAK_INFO_MODE
}
export function clampSpectrumTiltDbPerOctave(value: unknown): number {
const numeric = Number(value)
if (!Number.isFinite(numeric)) {
@@ -35,3 +63,33 @@ export function clampSpectrumHeatmapTiltDbPerOctave(value: unknown): number {
Math.max(MIN_SPECTRUM_HEATMAP_TILT_DB_PER_OCTAVE, rounded)
)
}
export function resolveSpectrumPitchInfo(frequencyHz: number): SpectrumPitchInfo | null {
if (!Number.isFinite(frequencyHz) || frequencyHz <= 0) {
return null
}
const midiNote = 69 + (12 * Math.log2(frequencyHz / 440))
const nearestMidiNote = Math.round(midiNote)
const cents = Math.round((midiNote - nearestMidiNote) * 100)
const noteIndex = ((nearestMidiNote % 12) + 12) % 12
const octave = Math.floor(nearestMidiNote / 12) - 1
return {
note: NOTE_NAMES[noteIndex],
octave,
cents,
}
}
export function formatSpectrumPitchInfo(pitchInfo: SpectrumPitchInfo | null): string {
if (!pitchInfo) {
return '--'
}
const centsLabel = pitchInfo.cents > 0
? `+${pitchInfo.cents}c`
: `${pitchInfo.cents}c`
return `${pitchInfo.note}${pitchInfo.octave} ${centsLabel}`
}