Files
prism/src/plugin-ui/main.tsx
T
2026-07-10 21:53:47 -04:00

223 lines
6.6 KiB
TypeScript

import { StrictMode, type JSX } from 'react'
import { createRoot } from 'react-dom/client'
import '../renderer/styles/globals.css'
import './styles.css'
import ScopeApp from './ScopeApp'
import SpectrumScope from './SpectrumScope'
import OscilloscopeScope from './OscilloscopeScope'
import VUMeterScope from './VUMeterScope'
import LUFSMeterScope from './LUFSMeterScope'
import VectorscopeScope from './VectorscopeScope'
import SpectrogramScope from './SpectrogramScope'
import WaveformScope from './WaveformScope'
import { BridgeSpectrumAnalyzer } from './BridgeSpectrumAnalyzer'
import { BridgeOscilloscopeAnalyzer } from './BridgeOscilloscopeAnalyzer'
import { BridgeVUMeterAnalyzer } from './BridgeVUMeterAnalyzer'
import { BridgeLUFSMeterAnalyzer } from './BridgeLUFSMeterAnalyzer'
import { BridgeVectorscopeAnalyzer } from './BridgeVectorscopeAnalyzer'
import { BridgeSpectrogramAnalyzer } from './BridgeSpectrogramAnalyzer'
import { BridgeWaveformAnalyzer } from './BridgeWaveformAnalyzer'
import { PluginWebViewDataSource } from './PluginWebViewDataSource'
import { connectOscilloscopeBridge, connectSpectrumBridge, connectVUMeterBridge, connectLUFSMeterBridge, connectVectorscopeBridge, connectSpectrogramBridge, connectWaveformBridge } from './juceBridge'
// The C++ plugin tells us which scope it is via JUCE initialisation data.
// JUCE stores each value as an array (e.g. prismScope = ["oscilloscope"]).
function getScopeKind(): string {
const raw = (window as unknown as {
__JUCE__?: { initialisationData?: { prismScope?: unknown } }
}).__JUCE__?.initialisationData?.prismScope
const value = Array.isArray(raw) ? raw[0] : raw
return typeof value === 'string' ? value : 'spectrum'
}
const dataSource = new PluginWebViewDataSource()
function buildApp(): JSX.Element {
if (getScopeKind() === 'waveform') {
const analyzer = new BridgeWaveformAnalyzer()
connectWaveformBridge({
onFrame: (frame) => {
analyzer.pushFrame(frame.summaries, frame.stereo)
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
})
return (
<ScopeApp
kind="waveform"
renderScope={(settings, theme) => (
<WaveformScope
settings={settings}
theme={theme.waveform}
dataSource={dataSource}
nativeAnalyzer={analyzer}
/>
)}
/>
)
}
if (getScopeKind() === 'spectrogram') {
const analyzer = new BridgeSpectrogramAnalyzer()
connectSpectrogramBridge({
onFrame: (frame) => {
analyzer.pushFrame(frame.display, frame.heat, frame.columnCount, frame.rowCount)
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
getRowCount: () => analyzer.getExpectedRowCount(),
})
return (
<ScopeApp
kind="spectrogram"
renderScope={(settings, theme) => (
<SpectrogramScope
settings={settings}
theme={theme.spectrogram}
dataSource={dataSource}
nativeAnalyzer={analyzer}
/>
)}
/>
)
}
if (getScopeKind() === 'vectorscope') {
const analyzer = new BridgeVectorscopeAnalyzer()
connectVectorscopeBridge({
onFrame: (frame) => {
if (frame.multiband && frame.data) {
analyzer.setMultiband(frame.data, frame.count)
} else if (frame.x && frame.y) {
analyzer.setStandard(frame.x, frame.y, frame.count)
}
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
})
return (
<ScopeApp
kind="vectorscope"
renderScope={(settings, theme) => (
<VectorscopeScope
settings={settings}
theme={theme.vectorscope}
dataSource={dataSource}
nativeAnalyzer={analyzer}
/>
)}
/>
)
}
if (getScopeKind() === 'lufsmeter') {
const analyzer = new BridgeLUFSMeterAnalyzer()
connectLUFSMeterBridge({
onFrame: (frame) => {
analyzer.setSnapshot(frame)
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
})
return (
<ScopeApp
kind="lufsmeter"
renderScope={(settings, theme) => (
<LUFSMeterScope
settings={settings}
theme={theme.lufsmeter}
dataSource={dataSource}
nativeAnalyzer={analyzer}
/>
)}
/>
)
}
if (getScopeKind() === 'vumeter') {
const analyzer = new BridgeVUMeterAnalyzer()
connectVUMeterBridge({
onFrame: (frame) => {
analyzer.setSnapshot(frame)
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
})
return (
<ScopeApp
kind="vumeter"
renderScope={(settings, theme) => (
<VUMeterScope
settings={settings}
theme={theme.vumeter}
dataSource={dataSource}
nativeAnalyzer={analyzer}
/>
)}
/>
)
}
if (getScopeKind() === 'oscilloscope') {
const analyzer = new BridgeOscilloscopeAnalyzer()
connectOscilloscopeBridge({
onFrame: (frame) => {
analyzer.setSamples(frame.samples, frame.detectedPitch)
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
})
return (
<ScopeApp
kind="oscilloscope"
renderScope={(settings, theme) => (
<OscilloscopeScope
settings={settings}
theme={theme.oscilloscope}
dataSource={dataSource}
nativeAnalyzer={analyzer}
/>
)}
/>
)
}
const analyzer = new BridgeSpectrumAnalyzer(2048)
connectSpectrumBridge({
onFrame: (frame) => {
analyzer.setMagnitudes(frame.magnitudes, frame.side, frame.channelMax)
dataSource.setSampleRate(frame.sampleRate)
dataSource.setPlaying(true)
},
})
return (
<ScopeApp
kind="spectrum"
renderScope={(settings, theme) => (
<SpectrumScope
settings={settings}
theme={theme.spectrum}
dataSource={dataSource}
nativeAnalyzer={analyzer}
/>
)}
/>
)
}
const rootElement = document.getElementById('root')
if (!rootElement) {
throw new Error('Missing #root element')
}
try {
createRoot(rootElement).render(<StrictMode>{buildApp()}</StrictMode>)
document.documentElement.classList.add('prism-plugin-mounted')
} catch (error) {
const reporter = (window as unknown as {
__PRISM_PLUGIN_ERROR__?: (message: string) => void
}).__PRISM_PLUGIN_ERROR__
reporter?.(error instanceof Error ? error.message : String(error))
throw error
}