mirror of
https://github.com/Boof2015/prism.git
synced 2026-08-12 05:10:51 +02:00
add stereo to waveform scope, add side metering to spectrum
This commit is contained in:
@@ -47,6 +47,69 @@ test('routes chunks only to demanded scopes and prunes queues when demand is rem
|
||||
|
||||
assert.equal(router.flushPendingSpectrumSamples().length, 0)
|
||||
assert.equal(router.flushPendingWaveformSamples().length, 0)
|
||||
assert.equal(router.flushPendingWaveformStereoSamples().length, 0)
|
||||
})
|
||||
|
||||
test('spectrum keeps stereo chunks for the side overlay path and still exposes mono downmixes', () => {
|
||||
const router = new AudioRouter()
|
||||
const sessionId = router.beginSession(48000, 2, 'electron-system')
|
||||
router.setVisualizerConsumerDemand('test-consumer', { spectrum: true })
|
||||
|
||||
router.ingestChunk(createChunk(2), createChunk(4), {
|
||||
sessionId,
|
||||
channelCount: 2,
|
||||
sequence: 1,
|
||||
capturedAt: performance.now() - 5,
|
||||
})
|
||||
|
||||
const stereoChunks = router.flushPendingSpectrumStereoSamples()
|
||||
assert.equal(stereoChunks.length, 1)
|
||||
assert.deepEqual(Array.from(stereoChunks[0]?.left ?? []), [2, 2, 2, 2])
|
||||
assert.deepEqual(Array.from(stereoChunks[0]?.right ?? []), [4, 4, 4, 4])
|
||||
assert.equal(router.flushPendingSpectrumSamples().length, 0)
|
||||
|
||||
router.ingestChunk(createChunk(6), createChunk(10), {
|
||||
sessionId,
|
||||
channelCount: 2,
|
||||
sequence: 2,
|
||||
capturedAt: performance.now() - 5,
|
||||
})
|
||||
|
||||
const monoChunks = router.flushPendingSpectrumSamples()
|
||||
assert.equal(monoChunks.length, 1)
|
||||
assert.deepEqual(Array.from(monoChunks[0] ?? []), [8, 8, 8, 8])
|
||||
assert.equal(router.flushPendingSpectrumStereoSamples().length, 0)
|
||||
})
|
||||
|
||||
test('waveform keeps stereo chunks for stereo mode while mono flushes still expose the left channel', () => {
|
||||
const router = new AudioRouter()
|
||||
const sessionId = router.beginSession(48000, 2, 'electron-system')
|
||||
router.setVisualizerConsumerDemand('test-consumer', { waveform: true })
|
||||
|
||||
router.ingestChunk(createChunk(2), createChunk(4), {
|
||||
sessionId,
|
||||
channelCount: 2,
|
||||
sequence: 1,
|
||||
capturedAt: performance.now() - 5,
|
||||
})
|
||||
|
||||
const stereoChunks = router.flushPendingWaveformStereoSamples()
|
||||
assert.equal(stereoChunks.length, 1)
|
||||
assert.deepEqual(Array.from(stereoChunks[0]?.left ?? []), [2, 2, 2, 2])
|
||||
assert.deepEqual(Array.from(stereoChunks[0]?.right ?? []), [4, 4, 4, 4])
|
||||
assert.equal(router.flushPendingWaveformSamples().length, 0)
|
||||
|
||||
router.ingestChunk(createChunk(6), createChunk(10), {
|
||||
sessionId,
|
||||
channelCount: 2,
|
||||
sequence: 2,
|
||||
capturedAt: performance.now() - 5,
|
||||
})
|
||||
|
||||
const monoChunks = router.flushPendingWaveformSamples()
|
||||
assert.equal(monoChunks.length, 1)
|
||||
assert.deepEqual(Array.from(monoChunks[0] ?? []), [6, 6, 6, 6])
|
||||
assert.equal(router.flushPendingWaveformStereoSamples().length, 0)
|
||||
})
|
||||
|
||||
test('keeps the newest chunks when a fixed-capacity ring overflows', () => {
|
||||
|
||||
@@ -64,6 +64,7 @@ function createProfile(name: string): Profile {
|
||||
windowBounds: { x: 120, y: 40, width: 420, height: 240 },
|
||||
}
|
||||
profile.windowBounds = { x: 10, y: 20, width: 840, height: 180 }
|
||||
profile.scopeSettings.spectrum.showSideLine = true
|
||||
profile.scopeSettings.spectrogram.colorScheme = 'mono'
|
||||
return profile
|
||||
}
|
||||
@@ -82,6 +83,7 @@ test('profile file serialization excludes geometry and round-trips with local me
|
||||
const restored = profileFileToProfile(file, extractLocalProfileMetadata(profile))
|
||||
assert.deepEqual(restored.windowBounds, profile.windowBounds)
|
||||
assert.deepEqual(restored.scopePopouts.spectrum.windowBounds, profile.scopePopouts.spectrum.windowBounds)
|
||||
assert.equal(restored.scopeSettings.spectrum.showSideLine, true)
|
||||
assert.equal(restored.scopeSettings.spectrogram.colorScheme, 'mono')
|
||||
})
|
||||
|
||||
@@ -195,6 +197,7 @@ test('partial files normalize, unsupported versions fail, and import does not ch
|
||||
|
||||
const partialSnapshot = await harness.library.importProfileFromPath(partialPath)
|
||||
assert.equal(partialSnapshot.activeProfileId, 'profile_partial')
|
||||
assert.equal(partialSnapshot.profiles.profile_partial.scopeSettings.spectrum.showSideLine, false)
|
||||
assert.equal(partialSnapshot.profiles.profile_partial.scopeSettings.spectrogram.colorScheme, 'heat')
|
||||
assert.equal(partialSnapshot.profiles.profile_partial.scopePopouts.spectrogram.poppedOut, true)
|
||||
assert.equal(partialSnapshot.profiles.profile_partial.widthWeights.spectrum, 1)
|
||||
|
||||
@@ -9,17 +9,21 @@ import {
|
||||
import {
|
||||
createDefaultProfile,
|
||||
} from '../src/shared/profileState'
|
||||
import { createDefaultTheme, resolveTheme } from '../src/shared/themeState'
|
||||
import { usePerformanceStore } from '../src/renderer/stores/performanceStore'
|
||||
import {
|
||||
moveDockedScopeOrder,
|
||||
useSettingsStore,
|
||||
} from '../src/renderer/stores/settingsStore'
|
||||
import { scopeSettingsToOptions } from '../src/renderer/components/ScopeModule'
|
||||
import { scopeSummary } from '../src/renderer/components/ScopeSettingsSection'
|
||||
import {
|
||||
applyInputGainToStereoSamples,
|
||||
inputGainDbToLinear,
|
||||
} from '../src/renderer/audio/inputGain'
|
||||
import { SCOPE_KINDS, type ScopeKind } from '../src/types/scope'
|
||||
import type { ScopePopoutStateMap } from '../src/types/popout'
|
||||
import { ScopePopoutDataSource } from '../src/renderer/popouts/ScopePopoutDataSource'
|
||||
import {
|
||||
VUMeterBallistics,
|
||||
VU_INTEGRATION_WINDOW_MS,
|
||||
@@ -499,6 +503,67 @@ test('moveDockedScopeOrder swaps a middle docked scope with its adjacent docked
|
||||
])
|
||||
})
|
||||
|
||||
test('scopeSettingsToOptions wires spectrum side overlay settings into analyzer options', () => {
|
||||
const profile = createDefaultProfile('Default')
|
||||
profile.scopeSettings.spectrum.showSideLine = true
|
||||
const theme = resolveTheme(createDefaultTheme())
|
||||
|
||||
const options = scopeSettingsToOptions('spectrum', profile.scopeSettings.spectrum, theme.spectrum)
|
||||
|
||||
assert.equal(options.showSideLine, true)
|
||||
assert.equal(options.secondaryLineColor, theme.spectrum.secondary)
|
||||
assert.equal(options.lineColor, theme.spectrum.primary)
|
||||
})
|
||||
|
||||
test('scopeSettingsToOptions wires waveform stereo mode into analyzer options', () => {
|
||||
const profile = createDefaultProfile('Default')
|
||||
profile.scopeSettings.waveform.mode = 'stereo'
|
||||
profile.scopeSettings.waveform.multiband = true
|
||||
const theme = resolveTheme(createDefaultTheme())
|
||||
|
||||
const options = scopeSettingsToOptions('waveform', profile.scopeSettings.waveform, theme.waveform)
|
||||
|
||||
assert.equal(options.mode, 'stereo')
|
||||
assert.equal(options.multiband, true)
|
||||
assert.equal(options.lineColor, theme.waveform.primary)
|
||||
})
|
||||
|
||||
test('scopeSummary includes Stereo for waveform only when stereo mode is enabled', () => {
|
||||
const profile = createDefaultProfile('Default')
|
||||
profile.scopeSettings.waveform.gainDb = 6
|
||||
|
||||
assert.equal(scopeSummary('waveform', profile.scopeSettings.waveform), '+6 dB')
|
||||
|
||||
profile.scopeSettings.waveform.mode = 'stereo'
|
||||
assert.equal(scopeSummary('waveform', profile.scopeSettings.waveform), '+6 dB · Stereo')
|
||||
|
||||
profile.scopeSettings.waveform.multiband = true
|
||||
assert.equal(scopeSummary('waveform', profile.scopeSettings.waveform), '+6 dB · Stereo · RGB')
|
||||
})
|
||||
|
||||
test('ScopePopoutDataSource switches waveform batches between mono and stereo queues', () => {
|
||||
const dataSource = new ScopePopoutDataSource('waveform')
|
||||
const monoChunk = new Float32Array([0.1, 0.2, 0.3])
|
||||
const stereoLeft = new Float32Array([0.4, 0.5])
|
||||
const stereoRight = new Float32Array([0.6, 0.7])
|
||||
const nextMonoChunk = new Float32Array([0.8])
|
||||
|
||||
dataSource.pushAudioBatch([monoChunk])
|
||||
assert.equal(dataSource.getPendingWaveformSamples()[0], monoChunk)
|
||||
assert.equal(dataSource.getPendingWaveformStereoSamples().length, 0)
|
||||
|
||||
dataSource.pushAudioBatch([{ left: stereoLeft, right: stereoRight }])
|
||||
assert.equal(dataSource.getPendingWaveformSamples().length, 0)
|
||||
const stereoBatch = dataSource.getPendingWaveformStereoSamples()
|
||||
assert.equal(stereoBatch.length, 1)
|
||||
assert.equal(stereoBatch[0]?.left, stereoLeft)
|
||||
assert.equal(stereoBatch[0]?.right, stereoRight)
|
||||
|
||||
dataSource.pushAudioBatch([nextMonoChunk])
|
||||
assert.equal(dataSource.getPendingWaveformStereoSamples().length, 0)
|
||||
assert.equal(dataSource.getPendingWaveformSamples()[0], nextMonoChunk)
|
||||
})
|
||||
|
||||
test('applying a profile snapshot does not change the machine-local frame target', () => {
|
||||
const previousPerformanceState = usePerformanceStore.getState()
|
||||
const previousSettingsState = useSettingsStore.getState()
|
||||
|
||||
Reference in New Issue
Block a user