mirror of
https://github.com/Boof2015/prism.git
synced 2026-08-16 16:21:11 +02:00
63ef5ffa0f
Add cross platform VSTs
119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
import type { SpectrumNativeAnalyzer } from '../renderer/audio/native'
|
|
|
|
const FFT_SILENCE_DB = -100
|
|
|
|
/**
|
|
* A drop-in `SpectrumNativeAnalyzer` for the plugin webview.
|
|
*
|
|
* In the Electron app, `SpectrumAnalyzer` pushes raw samples into the N-API DSP
|
|
* addon and reads magnitudes back. There is no N-API addon inside a webview, so
|
|
* here the DSP runs in the C++ plugin instead: it computes magnitudes off the
|
|
* realtime thread and pushes them over the JUCE bridge. This shim simply caches
|
|
* the latest pushed magnitudes and serves them through the same interface
|
|
* `SpectrumAnalyzer` already consumes — so the visualizer needs no changes.
|
|
*
|
|
* `pushSamples` / `pushStereoSamples` are intentional no-ops: audio never flows
|
|
* through the webview.
|
|
*/
|
|
export class BridgeSpectrumAnalyzer implements SpectrumNativeAnalyzer {
|
|
private fftSize = 2048
|
|
private sampleRate = 48000
|
|
private magnitudes: Float32Array
|
|
private sideMagnitudes: 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)
|
|
}
|
|
|
|
/** Called by the bridge whenever the host emits a new frame. */
|
|
setMagnitudes(magnitudes: Float32Array, side?: Float32Array): void {
|
|
if (magnitudes.length !== this.magnitudes.length) {
|
|
this.magnitudes = new Float32Array(magnitudes.length)
|
|
}
|
|
this.magnitudes.set(magnitudes)
|
|
|
|
if (side && side.length > 0) {
|
|
if (side.length !== this.sideMagnitudes.length) {
|
|
this.sideMagnitudes = new Float32Array(side.length)
|
|
}
|
|
this.sideMagnitudes.set(side)
|
|
}
|
|
}
|
|
|
|
isAvailable(): boolean {
|
|
return true
|
|
}
|
|
|
|
setFFTSize(size: number): void {
|
|
if (size > 0 && size !== this.fftSize) {
|
|
this.fftSize = size
|
|
this.magnitudes = new Float32Array(size / 2).fill(FFT_SILENCE_DB)
|
|
this.sideMagnitudes = new Float32Array(size / 2).fill(FFT_SILENCE_DB)
|
|
}
|
|
}
|
|
|
|
getFFTSize(): number {
|
|
return this.fftSize
|
|
}
|
|
|
|
setSampleRate(sampleRate: number): void {
|
|
this.sampleRate = sampleRate
|
|
}
|
|
|
|
// Smoothing is applied in the C++ DSP; nothing to do on this side.
|
|
setSmoothing(_smoothing: number): void {}
|
|
|
|
pushSamples(_audioData: Float32Array): void {}
|
|
|
|
pushStereoSamples(_leftChannel: Float32Array, _rightChannel: Float32Array): void {}
|
|
|
|
fillMagnitudes(output: Float32Array): number {
|
|
const count = Math.min(output.length, this.magnitudes.length)
|
|
if (count > 0) {
|
|
output.set(this.magnitudes.subarray(0, count), 0)
|
|
}
|
|
return count
|
|
}
|
|
|
|
// The C++ side sends already-smoothed magnitudes; serve them for the raw
|
|
// request too (the heatmap path re-smooths from this in the visualizer).
|
|
fillRawMagnitudes(output: Float32Array): number {
|
|
return this.fillMagnitudes(output)
|
|
}
|
|
|
|
fillSideMagnitudes(output: Float32Array): number {
|
|
const count = Math.min(output.length, this.sideMagnitudes.length)
|
|
if (count > 0) {
|
|
output.set(this.sideMagnitudes.subarray(0, count), 0)
|
|
}
|
|
return count
|
|
}
|
|
|
|
getMagnitudes(): Float32Array {
|
|
return this.magnitudes
|
|
}
|
|
|
|
getRawMagnitudes(): Float32Array {
|
|
return this.magnitudes
|
|
}
|
|
|
|
getSideMagnitudes(): Float32Array {
|
|
return this.sideMagnitudes
|
|
}
|
|
|
|
process(_audioData: Float32Array): Float32Array {
|
|
return this.magnitudes
|
|
}
|
|
|
|
binToFrequency(bin: number): number {
|
|
return (bin * this.sampleRate) / this.fftSize
|
|
}
|
|
|
|
reset(): void {
|
|
this.magnitudes.fill(FFT_SILENCE_DB)
|
|
this.sideMagnitudes.fill(FFT_SILENCE_DB)
|
|
}
|
|
}
|