change spectrum db to dbFS

This commit is contained in:
Boof2015
2026-07-10 21:53:47 -04:00
parent 7a1a799266
commit bddebefd54
17 changed files with 585 additions and 93 deletions
+23 -1
View File
@@ -20,15 +20,17 @@ export class BridgeSpectrumAnalyzer implements SpectrumNativeAnalyzer {
private sampleRate = 48000
private magnitudes: Float32Array
private sideMagnitudes: Float32Array
private channelMaxMagnitudes: Float32Array
constructor(fftSize = 2048) {
this.fftSize = fftSize
this.magnitudes = new Float32Array(fftSize / 2).fill(FFT_SILENCE_DB)
this.sideMagnitudes = new Float32Array(fftSize / 2).fill(FFT_SILENCE_DB)
this.channelMaxMagnitudes = new Float32Array(fftSize / 2).fill(FFT_SILENCE_DB)
}
/** Called by the bridge whenever the host emits a new frame. */
setMagnitudes(magnitudes: Float32Array, side?: Float32Array): void {
setMagnitudes(magnitudes: Float32Array, side?: Float32Array, channelMax?: Float32Array): void {
if (magnitudes.length !== this.magnitudes.length) {
this.magnitudes = new Float32Array(magnitudes.length)
}
@@ -40,6 +42,12 @@ export class BridgeSpectrumAnalyzer implements SpectrumNativeAnalyzer {
}
this.sideMagnitudes.set(side)
}
const resolvedChannelMax = channelMax && channelMax.length > 0 ? channelMax : magnitudes
if (resolvedChannelMax.length !== this.channelMaxMagnitudes.length) {
this.channelMaxMagnitudes = new Float32Array(resolvedChannelMax.length)
}
this.channelMaxMagnitudes.set(resolvedChannelMax)
}
isAvailable(): boolean {
@@ -51,6 +59,7 @@ export class BridgeSpectrumAnalyzer implements SpectrumNativeAnalyzer {
this.fftSize = size
this.magnitudes = new Float32Array(size / 2).fill(FFT_SILENCE_DB)
this.sideMagnitudes = new Float32Array(size / 2).fill(FFT_SILENCE_DB)
this.channelMaxMagnitudes = new Float32Array(size / 2).fill(FFT_SILENCE_DB)
}
}
@@ -91,6 +100,14 @@ export class BridgeSpectrumAnalyzer implements SpectrumNativeAnalyzer {
return count
}
fillChannelMaxMagnitudes(output: Float32Array): number {
const count = Math.min(output.length, this.channelMaxMagnitudes.length)
if (count > 0) {
output.set(this.channelMaxMagnitudes.subarray(0, count), 0)
}
return count
}
getMagnitudes(): Float32Array {
return this.magnitudes
}
@@ -103,6 +120,10 @@ export class BridgeSpectrumAnalyzer implements SpectrumNativeAnalyzer {
return this.sideMagnitudes
}
getChannelMaxMagnitudes(): Float32Array {
return this.channelMaxMagnitudes
}
process(_audioData: Float32Array): Float32Array {
return this.magnitudes
}
@@ -114,5 +135,6 @@ export class BridgeSpectrumAnalyzer implements SpectrumNativeAnalyzer {
reset(): void {
this.magnitudes.fill(FFT_SILENCE_DB)
this.sideMagnitudes.fill(FFT_SILENCE_DB)
this.channelMaxMagnitudes.fill(FFT_SILENCE_DB)
}
}
+2 -2
View File
@@ -9,7 +9,7 @@ import { spectrumSettingsToOptions } from './spectrumOptions'
import { getScopeCanvasTransformStyle } from '../renderer/scopeCanvasTransform'
import { applyPluginScopeCanvasLayout } from './scopeCanvasLayout'
import {
formatSpectrumPeakDb,
formatSpectrumPeakDbfs,
formatSpectrumPeakFrequency,
resolveFollowingPeakOverlayStyle,
type CanvasResizeState,
@@ -137,7 +137,7 @@ export default function SpectrumScope({
className={['scope-module__peak-info', peakMode === 'following' ? 'is-following' : 'is-corner'].join(' ')}
style={overlayStyle}
>
<span className="scope-module__peak-info-value">{formatSpectrumPeakDb(peak.db)}</span>
<span className="scope-module__peak-info-value">{formatSpectrumPeakDbfs(peak.dbfs)}</span>
<span className="scope-module__peak-info-separator">/</span>
<span className="scope-module__peak-info-value">{formatSpectrumPeakFrequency(peak.frequencyHz)}</span>
<span className="scope-module__peak-info-separator">/</span>
+15 -5
View File
@@ -16,12 +16,15 @@ export interface SpectrumFrame {
magnitudes: Float32Array
/** Side magnitudes in dB (same length); empty if unavailable. */
side: Float32Array
/** Smoothed max(L, R) magnitudes in dBFS (same length). */
channelMax: Float32Array
}
interface SpectrumFramePayload {
sampleRate?: number
magnitudes?: string
side?: string
channelMax?: string
}
type JuceBackend = {
@@ -100,14 +103,19 @@ export function base64ToFloat32Array(b64: string): Float32Array {
return new Float32Array(bytes.buffer, 0, byteLength >> 2)
}
function decodeFrame(payload: unknown): SpectrumFrame | null {
export function decodeSpectrumFrame(payload: unknown): SpectrumFrame | null {
if (typeof payload !== 'object' || payload === null) return null
const { sampleRate, magnitudes, side } = payload as SpectrumFramePayload
const { sampleRate, magnitudes, side, channelMax } = payload as SpectrumFramePayload
if (typeof magnitudes !== 'string' || magnitudes.length === 0) return null
const decodedMagnitudes = base64ToFloat32Array(magnitudes)
const decodedChannelMax = typeof channelMax === 'string'
? base64ToFloat32Array(channelMax)
: new Float32Array(0)
return {
sampleRate: typeof sampleRate === 'number' && sampleRate > 0 ? sampleRate : 48000,
magnitudes: base64ToFloat32Array(magnitudes),
magnitudes: decodedMagnitudes,
side: typeof side === 'string' ? base64ToFloat32Array(side) : new Float32Array(0),
channelMax: decodedChannelMax.length > 0 ? decodedChannelMax : decodedMagnitudes,
}
}
@@ -128,6 +136,7 @@ export function connectSpectrumBridge(handlers: SpectrumBridgeHandlers): () => v
const sampleRate = 48000
const mid = new Float32Array(binCount)
const side = new Float32Array(binCount).fill(-100)
const channelMax = new Float32Array(binCount)
let phase = 0
const tick = (): void => {
if (disposed) return
@@ -138,8 +147,9 @@ export function connectSpectrumBridge(handlers: SpectrumBridgeHandlers): () => v
const peak2 = Math.exp(-Math.pow((t - 0.5) * 18, 2)) * 50
mid[i] = -100 + peak1 + peak2 + Math.random() * 6
side[i] = -100 + peak2 * 0.4 + Math.random() * 4
channelMax[i] = mid[i]
}
handlers.onFrame({ sampleRate, magnitudes: mid, side })
handlers.onFrame({ sampleRate, magnitudes: mid, side, channelMax })
mockRaf = requestAnimationFrame(tick)
}
mockRaf = requestAnimationFrame(tick)
@@ -149,7 +159,7 @@ export function connectSpectrumBridge(handlers: SpectrumBridgeHandlers): () => v
if (disposed) return
if (backend) {
listenerId = backend.addEventListener('spectrumFrame', (payload) => {
const frame = decodeFrame(payload)
const frame = decodeSpectrumFrame(payload)
if (frame) handlers.onFrame(frame)
})
handlers.onConnected?.(false)
+1 -1
View File
@@ -185,7 +185,7 @@ function buildApp(): JSX.Element {
const analyzer = new BridgeSpectrumAnalyzer(2048)
connectSpectrumBridge({
onFrame: (frame) => {
analyzer.setMagnitudes(frame.magnitudes, frame.side)
analyzer.setMagnitudes(frame.magnitudes, frame.side, frame.channelMax)
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
+2 -2
View File
@@ -28,11 +28,11 @@ function clampNumber(value: number, min: number, max: number): number {
return Math.min(max, Math.max(min, value))
}
export function formatSpectrumPeakDb(value: number): string {
export function formatSpectrumPeakDbfs(value: number): string {
if (!Number.isFinite(value)) {
return '--'
}
return `${value >= 0 ? '+' : ''}${value.toFixed(2)}dB`
return `${value >= 0 ? '+' : ''}${value.toFixed(2)}dBFS`
}
export function formatSpectrumPeakFrequency(value: number): string {