Enable low-frequency RTW analyzer range

This commit is contained in:
Mikei386
2026-08-06 20:53:30 +02:00
parent cbbeac5b0f
commit 48578c5919
10 changed files with 41 additions and 34 deletions
+5
View File
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
const html = fs.readFileSync(new URL('../www/index.html', import.meta.url), 'utf8'); const html = fs.readFileSync(new URL('../www/index.html', import.meta.url), 'utf8');
const main = fs.readFileSync(new URL('../www/main.js', import.meta.url), 'utf8'); const main = fs.readFileSync(new URL('../www/main.js', import.meta.url), 'utf8');
const options = fs.readFileSync(new URL('../www/ui/options.js', import.meta.url), 'utf8');
const realtime = fs.readFileSync(new URL('../www/views/realtime.js', import.meta.url), 'utf8'); const realtime = fs.readFileSync(new URL('../www/views/realtime.js', import.meta.url), 'utf8');
const styleSelect = html.match(/<select\b[^>]*\bid="styleSel"[^>]*>/)?.[0] || ''; const styleSelect = html.match(/<select\b[^>]*\bid="styleSel"[^>]*>/)?.[0] || '';
@@ -17,5 +18,9 @@ assert((realtime.match(/applyPeakHold\(state, integrated, CONFIG, range\)/g) ||
'every RTA engine must advance peak hold on the display clock'); 'every RTA engine must advance peak hold on the display clock');
assert(/useIirEngine[^]*?applyRealtimeBarBallistics\(state, displayBase, CONFIG, range\)/.test(realtime), assert(/useIirEngine[^]*?applyRealtimeBarBallistics\(state, displayBase, CONFIG, range\)/.test(realtime),
'native IIR bars must advance their visible release on the display clock'); 'native IIR bars must advance their visible release on the display clock');
assert(/h\('opt_rtaFreq', v => \{\s*CONFIG\.RTA_FREQ_RANGE = \(v === 'lf'\) \? 'lf' : 'norm'/.test(options),
'the low-frequency range must remain selectable in the RTW layout');
assert(!/RTA_FREQ_RANGE\s*=\s*CONFIG\.RTA_BAR_LAYOUT\s*===\s*'rtw'\s*\?\s*'norm'/.test(options),
'the RTW layout must not force the normal frequency range');
console.log('GUI contract regression tests passed'); console.log('GUI contract regression tests passed');
+7 -5
View File
@@ -31,9 +31,9 @@ const context = vm.createContext({
Number, Number,
normalizeCorrelationResponseSeconds: correlationContext.normalizeCorrelationResponseSeconds, normalizeCorrelationResponseSeconds: correlationContext.normalizeCorrelationResponseSeconds,
getRtwCenters(mode) { getRtwCenters(mode) {
if (mode === '1_12') return new Array(121).fill(0); if (mode === '1_12') return new Array(145).fill(0);
if (mode === '1_6') return new Array(61).fill(0); if (mode === '1_6') return new Array(73).fill(0);
return new Array(31).fill(0); return new Array(37).fill(0);
}, },
}); });
vm.runInContext(functionSource.replace('export function', 'function'), context); vm.runInContext(functionSource.replace('export function', 'function'), context);
@@ -56,7 +56,7 @@ const forced = context.buildRtaRuntimeConfig({
}); });
assert.equal(forced.engine, 'iir'); assert.equal(forced.engine, 'iir');
assert.equal(forced.bpo, '1_12'); assert.equal(forced.bpo, '1_12');
assert.equal(forced.freqRange, 'norm'); assert.equal(forced.freqRange, 'lf');
assert.equal(forced.order, 6); assert.equal(forced.order, 6);
assert.equal(forced.detector, 'peak'); assert.equal(forced.detector, 'peak');
assert.equal(forced.rtaPeakHoldMode, 'fall'); assert.equal(forced.rtaPeakHoldMode, 'fall');
@@ -64,7 +64,7 @@ assert.equal(forced.rtaPeakHoldSeconds, 4);
assert.equal(forced.rtaPeakDecayDbPerSecond, 20); assert.equal(forced.rtaPeakDecayDbPerSecond, 20);
assert.equal(forced.rtaPeakResetToken, 9); assert.equal(forced.rtaPeakResetToken, 9);
assert.equal(forced.tauFast, 0.125); assert.equal(forced.tauFast, 0.125);
assert.equal(forced.rtwCenters.length, 121); assert.equal(forced.rtwCenters.length, 145);
assert.equal(forced.correlationResponseS, 2.5); assert.equal(forced.correlationResponseS, 2.5);
assert.equal(forced.correlationSilenceThresholdRmsDbfs, -50); assert.equal(forced.correlationSilenceThresholdRmsDbfs, -50);
assert.equal(forced.correlationResetToken, 7); assert.equal(forced.correlationResetToken, 7);
@@ -99,6 +99,8 @@ assert.equal(selectionContext.applyRtaBpoSelection('1_3'), '1_3');
assert.equal(selectionContext.CONFIG.RTA_BAR_LAYOUT, 'rtw'); assert.equal(selectionContext.CONFIG.RTA_BAR_LAYOUT, 'rtw');
const twelfthCenters = getRtwCenters('1_12'); const twelfthCenters = getRtwCenters('1_12');
assert.equal(twelfthCenters[0], 5);
assert.equal(twelfthCenters.at(-1), 20000);
const staleTwelfthPacket = { const staleTwelfthPacket = {
engine: 'iir', engine: 'iir',
bpo: '1_12', bpo: '1_12',
+17 -15
View File
@@ -90,24 +90,26 @@ const BOX_MAX_DB: f32 = 9.0;
const NATIVE_RECORDER_DISCONTINUITY_BLEND_FRAMES: usize = 256; const NATIVE_RECORDER_DISCONTINUITY_BLEND_FRAMES: usize = 256;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
const RTW_CENTERS_1_6: &[f32] = &[ const RTW_CENTERS_1_6: &[f32] = &[
20.0, 22.4, 25.0, 28.0, 31.5, 35.5, 40.0, 45.0, 50.0, 56.0, 63.0, 70.8, 80.0, 90.0, 100.0, 5.0, 5.6, 6.3, 7.08, 8.0, 9.0, 10.0, 11.2, 12.5, 14.1, 16.0, 18.0, 20.0, 22.4, 25.0, 28.0,
112.0, 125.0, 141.0, 160.0, 180.0, 200.0, 224.0, 250.0, 282.0, 315.0, 355.0, 400.0, 450.0, 31.5, 35.5, 40.0, 45.0, 50.0, 56.0, 63.0, 70.8, 80.0, 90.0, 100.0, 112.0, 125.0, 141.0, 160.0,
500.0, 560.0, 630.0, 708.0, 800.0, 900.0, 1000.0, 1120.0, 1250.0, 1410.0, 1600.0, 1800.0, 180.0, 200.0, 224.0, 250.0, 282.0, 315.0, 355.0, 400.0, 450.0, 500.0, 560.0, 630.0, 708.0,
2000.0, 2240.0, 2500.0, 2820.0, 3150.0, 3550.0, 4000.0, 4500.0, 5000.0, 5600.0, 6300.0, 7080.0, 800.0, 900.0, 1000.0, 1120.0, 1250.0, 1410.0, 1600.0, 1800.0, 2000.0, 2240.0, 2500.0, 2820.0,
8000.0, 9000.0, 10000.0, 11200.0, 12500.0, 14100.0, 16000.0, 18000.0, 20000.0, 3150.0, 3550.0, 4000.0, 4500.0, 5000.0, 5600.0, 6300.0, 7080.0, 8000.0, 9000.0, 10000.0,
11200.0, 12500.0, 14100.0, 16000.0, 18000.0, 20000.0,
]; ];
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
const RTW_CENTERS_1_12: &[f32] = &[ const RTW_CENTERS_1_12: &[f32] = &[
20.0, 21.2, 22.4, 23.8, 25.0, 26.5, 28.0, 29.8, 31.5, 33.4, 35.5, 37.6, 40.0, 42.4, 45.0, 47.5, 5.0, 5.3, 5.6, 5.95, 6.3, 6.7, 7.1, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.6, 11.2, 11.8, 12.5,
50.0, 53.0, 56.0, 59.5, 63.0, 67.0, 71.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, 106.0, 112.0, 13.2, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.2, 22.4, 23.8, 25.0, 26.5, 28.0, 29.8, 31.5,
118.0, 125.0, 132.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0, 200.0, 212.0, 224.0, 238.0, 33.4, 35.5, 37.6, 40.0, 42.4, 45.0, 47.5, 50.0, 53.0, 56.0, 59.5, 63.0, 67.0, 71.0, 75.0, 80.0,
250.0, 265.0, 280.0, 298.0, 315.0, 334.0, 355.0, 376.0, 400.0, 424.0, 450.0, 475.0, 500.0, 85.0, 90.0, 95.0, 100.0, 106.0, 112.0, 118.0, 125.0, 132.0, 140.0, 150.0, 160.0, 170.0, 180.0,
530.0, 560.0, 595.0, 630.0, 670.0, 710.0, 750.0, 800.0, 850.0, 900.0, 950.0, 1000.0, 1060.0, 190.0, 200.0, 212.0, 224.0, 238.0, 250.0, 265.0, 280.0, 298.0, 315.0, 334.0, 355.0, 376.0,
1120.0, 1180.0, 1250.0, 1320.0, 1400.0, 1500.0, 1600.0, 1700.0, 1800.0, 1900.0, 2000.0, 2120.0, 400.0, 424.0, 450.0, 475.0, 500.0, 530.0, 560.0, 595.0, 630.0, 670.0, 710.0, 750.0, 800.0,
2240.0, 2380.0, 2500.0, 2650.0, 2800.0, 2980.0, 3150.0, 3340.0, 3550.0, 3760.0, 4000.0, 4240.0, 850.0, 900.0, 950.0, 1000.0, 1060.0, 1120.0, 1180.0, 1250.0, 1320.0, 1400.0, 1500.0, 1600.0,
4500.0, 4750.0, 5000.0, 5300.0, 5600.0, 5950.0, 6300.0, 6700.0, 7100.0, 7500.0, 8000.0, 8500.0, 1700.0, 1800.0, 1900.0, 2000.0, 2120.0, 2240.0, 2380.0, 2500.0, 2650.0, 2800.0, 2980.0, 3150.0,
9000.0, 9500.0, 10_000.0, 10_600.0, 11_200.0, 11_800.0, 12_500.0, 13_200.0, 14_000.0, 15_000.0, 3340.0, 3550.0, 3760.0, 4000.0, 4240.0, 4500.0, 4750.0, 5000.0, 5300.0, 5600.0, 5950.0, 6300.0,
16_000.0, 17_000.0, 18_000.0, 19_000.0, 20_000.0, 6700.0, 7100.0, 7500.0, 8000.0, 8500.0, 9000.0, 9500.0, 10_000.0, 10_600.0, 11_200.0, 11_800.0,
12_500.0, 13_200.0, 14_000.0, 15_000.0, 16_000.0, 17_000.0, 18_000.0, 19_000.0, 20_000.0,
]; ];
#[derive(Clone)] #[derive(Clone)]
+4 -4
View File
@@ -6,9 +6,9 @@
use std::collections::VecDeque; use std::collections::VecDeque;
pub const RTW_THIRD_OCTAVE_CENTERS: &[f32] = &[ pub const RTW_THIRD_OCTAVE_CENTERS: &[f32] = &[
20.0, 25.0, 31.5, 40.0, 50.0, 63.0, 80.0, 100.0, 125.0, 160.0, 200.0, 250.0, 315.0, 400.0, 5.0, 6.3, 8.0, 10.0, 12.5, 16.0, 20.0, 25.0, 31.5, 40.0, 50.0, 63.0, 80.0, 100.0, 125.0, 160.0,
500.0, 630.0, 800.0, 1_000.0, 1_250.0, 1_600.0, 2_000.0, 2_500.0, 3_150.0, 4_000.0, 5_000.0, 200.0, 250.0, 315.0, 400.0, 500.0, 630.0, 800.0, 1_000.0, 1_250.0, 1_600.0, 2_000.0, 2_500.0,
6_300.0, 8_000.0, 10_000.0, 12_500.0, 16_000.0, 20_000.0, 3_150.0, 4_000.0, 5_000.0, 6_300.0, 8_000.0, 10_000.0, 12_500.0, 16_000.0, 20_000.0,
]; ];
/// Maps a rounded preferred label to its exact IEC base-ten center frequency. /// Maps a rounded preferred label to its exact IEC base-ten center frequency.
@@ -448,7 +448,7 @@ mod tests {
#[test] #[test]
fn third_octave_butterworth_has_correct_center_and_edges() { fn third_octave_butterworth_has_correct_center_and_edges() {
assert_eq!(RTW_THIRD_OCTAVE_CENTERS.len(), 31); assert_eq!(RTW_THIRD_OCTAVE_CENTERS.len(), 37);
for &nominal_center in RTW_THIRD_OCTAVE_CENTERS { for &nominal_center in RTW_THIRD_OCTAVE_CENTERS {
let center = exact_fractional_octave_center(nominal_center, 3); let center = exact_fractional_octave_center(nominal_center, 3);
let (lower, upper) = fractional_octave_edges(center, 3); let (lower, upper) = fractional_octave_edges(center, 3);
+1 -2
View File
@@ -426,7 +426,6 @@ fn normalize_rta_config(mut config: PhoenixRtaConfig) -> PhoenixRtaConfig {
if config.layout == "rtw" { if config.layout == "rtw" {
config.engine = "iir".to_string(); config.engine = "iir".to_string();
config.order = 6; config.order = 6;
config.freq_range = "norm".to_string();
} }
// Fast and Slow are named standard responses, not free tuning controls. // Fast and Slow are named standard responses, not free tuning controls.
@@ -853,7 +852,7 @@ mod tests {
assert_eq!(config.filterbank, "legacy"); assert_eq!(config.filterbank, "legacy");
assert_eq!(config.bpo, "1_12"); assert_eq!(config.bpo, "1_12");
assert_eq!(config.order, 6); assert_eq!(config.order, 6);
assert_eq!(config.freq_range, "norm"); assert_eq!(config.freq_range, "lf");
} }
#[test] #[test]
+1 -1
View File
@@ -927,7 +927,7 @@ export function buildRtaRuntimeConfig(CONFIG = {}) {
lrFractionalDelayEnabled: !!CONFIG.LR_FRACTIONAL_DELAY_ENABLED, lrFractionalDelayEnabled: !!CONFIG.LR_FRACTIONAL_DELAY_ENABLED,
lrFractionalDelaySamples: Number.isFinite(CONFIG.LR_FRACTIONAL_DELAY_SAMPLES) ? CONFIG.LR_FRACTIONAL_DELAY_SAMPLES : 0, lrFractionalDelaySamples: Number.isFinite(CONFIG.LR_FRACTIONAL_DELAY_SAMPLES) ? CONFIG.LR_FRACTIONAL_DELAY_SAMPLES : 0,
bpo: runtimeBpoMode, bpo: runtimeBpoMode,
freqRange: rtwProfile ? 'norm' : (CONFIG.RTA_FREQ_RANGE || 'norm'), freqRange: CONFIG.RTA_FREQ_RANGE === 'lf' ? 'lf' : 'norm',
weighting: CONFIG.RTA_WEIGHTING || 'z', weighting: CONFIG.RTA_WEIGHTING || 'z',
order: rtwProfile ? 6 : (CONFIG.RTA_IIR_ORDER || 6), order: rtwProfile ? 6 : (CONFIG.RTA_IIR_ORDER || 6),
tauFast: CONFIG.RTA_IIR_TAU_FAST || 0.125, tauFast: CONFIG.RTA_IIR_TAU_FAST || 0.125,
-2
View File
@@ -821,7 +821,6 @@ function loadConfig(opts = {}) {
if (CONFIG.RTA_BAR_LAYOUT === 'rtw') { if (CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_ENGINE = 'iir';
CONFIG.RTA_IIR_ORDER = 6; CONFIG.RTA_IIR_ORDER = 6;
CONFIG.RTA_FREQ_RANGE = 'norm';
const hold = Number(CONFIG.RTA_PEAK_HOLD_SEC); const hold = Number(CONFIG.RTA_PEAK_HOLD_SEC);
CONFIG.RTA_PEAK_HOLD_SEC = hold >= 3.25 ? 4.0 : 2.5; CONFIG.RTA_PEAK_HOLD_SEC = hold >= 3.25 ? 4.0 : 2.5;
} }
@@ -1168,7 +1167,6 @@ function applyPhoenixGlobalConfig(payload = {}) {
if (CONFIG.RTA_BAR_LAYOUT === 'rtw') { if (CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_ENGINE = 'iir';
CONFIG.RTA_IIR_ORDER = 6; CONFIG.RTA_IIR_ORDER = 6;
CONFIG.RTA_FREQ_RANGE = 'norm';
} }
const spectroGamma = Number(payload.spectroGamma); const spectroGamma = Number(payload.spectroGamma);
if (Number.isFinite(spectroGamma)) CONFIG.SPECTRO_GAMMA = Math.max(0.3, Math.min(1.2, spectroGamma)); if (Number.isFinite(spectroGamma)) CONFIG.SPECTRO_GAMMA = Math.max(0.3, Math.min(1.2, spectroGamma));
+4 -1
View File
@@ -1,10 +1,11 @@
export const RTW_CENTER_MAP = { export const RTW_CENTER_MAP = {
'1_3': [ '1_3': [
20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 5, 6.3, 8, 10, 12.5, 16, 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160,
200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600,
2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000,
], ],
'1_6': [ '1_6': [
5, 5.6, 6.3, 7.08, 8, 9, 10, 11.2, 12.5, 14.1, 16, 18,
20, 22.4, 25, 28, 31.5, 35.5, 40, 45, 50, 56, 20, 22.4, 25, 28, 31.5, 35.5, 40, 45, 50, 56,
63, 70.8, 80, 90, 100, 112, 125, 141, 160, 180, 63, 70.8, 80, 90, 100, 112, 125, 141, 160, 180,
200, 224, 250, 282, 315, 355, 400, 450, 500, 560, 200, 224, 250, 282, 315, 355, 400, 450, 500, 560,
@@ -13,6 +14,8 @@ export const RTW_CENTER_MAP = {
6300, 7080, 8000, 9000, 10000, 11200, 12500, 14100, 16000, 18000, 20000, 6300, 7080, 8000, 9000, 10000, 11200, 12500, 14100, 16000, 18000, 20000,
], ],
'1_12': [ '1_12': [
5, 5.3, 5.6, 5.95, 6.3, 6.7, 7.1, 7.5, 8, 8.5, 9, 9.5,
10, 10.6, 11.2, 11.8, 12.5, 13.2, 14, 15, 16, 17, 18, 19,
20, 21.2, 22.4, 23.8, 25, 26.5, 28, 29.8, 31.5, 33.4, 20, 21.2, 22.4, 23.8, 25, 26.5, 28, 29.8, 31.5, 33.4,
35.5, 37.6, 40, 42.4, 45, 47.5, 50, 53, 56, 59.5, 35.5, 37.6, 40, 42.4, 45, 47.5, 50, 53, 56, 59.5,
63, 67, 71, 75, 80, 85, 90, 95, 100, 106, 63, 67, 71, 75, 80, 85, 90, 95, 100, 106,
+1 -3
View File
@@ -1002,7 +1002,7 @@ function wireHandlers(env) {
return val; return val;
}); });
h('opt_rtaFreq', v => { h('opt_rtaFreq', v => {
CONFIG.RTA_FREQ_RANGE = CONFIG.RTA_BAR_LAYOUT === 'rtw' ? 'norm' : ((v === 'lf') ? 'lf' : 'norm'); CONFIG.RTA_FREQ_RANGE = (v === 'lf') ? 'lf' : 'norm';
return CONFIG.RTA_FREQ_RANGE; return CONFIG.RTA_FREQ_RANGE;
}); });
h('opt_rtaDisplayGainFft', v => { CONFIG.RTA_DISPLAY_GAIN_FFT_DB = clamp(+v, -25, 25); }, 'val_rtaDisplayGainFft', v=>`${v} dB`); h('opt_rtaDisplayGainFft', v => { CONFIG.RTA_DISPLAY_GAIN_FFT_DB = clamp(+v, -25, 25); }, 'val_rtaDisplayGainFft', v=>`${v} dB`);
@@ -1013,11 +1013,9 @@ function wireHandlers(env) {
if (val === 'rtw') { if (val === 'rtw') {
CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_ENGINE = 'iir';
CONFIG.RTA_IIR_ORDER = 6; CONFIG.RTA_IIR_ORDER = 6;
CONFIG.RTA_FREQ_RANGE = 'norm';
const forced = { const forced = {
opt_rtaEngine: 'iir', opt_rtaEngine: 'iir',
opt_rtaOrder: '6', opt_rtaOrder: '6',
opt_rtaFreq: 'norm',
}; };
for (const [id, value] of Object.entries(forced)) { for (const [id, value] of Object.entries(forced)) {
const control = document.getElementById(id); const control = document.getElementById(id);
+1 -1
View File
@@ -11,7 +11,7 @@ const METER_PAD_BOTTOM = 5;
const METER_SLOT_SHRINK = 24; // reduziert nutzbare Höhe leicht, damit Slot niedriger wirkt const METER_SLOT_SHRINK = 24; // reduziert nutzbare Höhe leicht, damit Slot niedriger wirkt
const METER_EXTRA_BOTTOM_PAD = 5; const METER_EXTRA_BOTTOM_PAD = 5;
const FLOOR_DB = -120; const FLOOR_DB = -120;
const RTA_X_TICKS = [20, 31.5, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]; const RTA_X_TICKS = [5, 10, 20, 31.5, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000];
const DEFAULT_RTA_BAR_BASE_COLOR = MID_COLOR; const DEFAULT_RTA_BAR_BASE_COLOR = MID_COLOR;
const RTA_PEAK_COLOR = WARN_COLOR; const RTA_PEAK_COLOR = WARN_COLOR;
const EMBEDDED_RTA_HIDE_20HZ_WIDTH = 420; const EMBEDDED_RTA_HIDE_20HZ_WIDTH = 420;