From 48578c5919504d65dde5c72badee6dcf6147a282 Mon Sep 17 00:00:00 2001 From: Mikei386 <44135113+Mikei386@users.noreply.github.com> Date: Thu, 6 Aug 2026 20:53:30 +0200 Subject: [PATCH] Enable low-frequency RTW analyzer range --- scripts/test_gui_contracts.mjs | 5 +++++ scripts/test_rta_profile.mjs | 12 +++++++----- src/audio.rs | 32 +++++++++++++++++--------------- src/rta.rs | 8 ++++---- src/state.rs | 3 +-- www/core/audio.js | 2 +- www/core/config.js | 2 -- www/core/rtw_centers.js | 5 ++++- www/ui/options.js | 4 +--- www/views/realtime.js | 2 +- 10 files changed, 41 insertions(+), 34 deletions(-) diff --git a/scripts/test_gui_contracts.mjs b/scripts/test_gui_contracts.mjs index aa3f98e..72bc7e3 100644 --- a/scripts/test_gui_contracts.mjs +++ b/scripts/test_gui_contracts.mjs @@ -3,6 +3,7 @@ import assert from 'node:assert/strict'; 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 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 styleSelect = html.match(/]*\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'); assert(/useIirEngine[^]*?applyRealtimeBarBallistics\(state, displayBase, CONFIG, range\)/.test(realtime), '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'); diff --git a/scripts/test_rta_profile.mjs b/scripts/test_rta_profile.mjs index 5959388..a40405c 100644 --- a/scripts/test_rta_profile.mjs +++ b/scripts/test_rta_profile.mjs @@ -31,9 +31,9 @@ const context = vm.createContext({ Number, normalizeCorrelationResponseSeconds: correlationContext.normalizeCorrelationResponseSeconds, getRtwCenters(mode) { - if (mode === '1_12') return new Array(121).fill(0); - if (mode === '1_6') return new Array(61).fill(0); - return new Array(31).fill(0); + if (mode === '1_12') return new Array(145).fill(0); + if (mode === '1_6') return new Array(73).fill(0); + return new Array(37).fill(0); }, }); vm.runInContext(functionSource.replace('export function', 'function'), context); @@ -56,7 +56,7 @@ const forced = context.buildRtaRuntimeConfig({ }); assert.equal(forced.engine, 'iir'); assert.equal(forced.bpo, '1_12'); -assert.equal(forced.freqRange, 'norm'); +assert.equal(forced.freqRange, 'lf'); assert.equal(forced.order, 6); assert.equal(forced.detector, 'peak'); assert.equal(forced.rtaPeakHoldMode, 'fall'); @@ -64,7 +64,7 @@ assert.equal(forced.rtaPeakHoldSeconds, 4); assert.equal(forced.rtaPeakDecayDbPerSecond, 20); assert.equal(forced.rtaPeakResetToken, 9); 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.correlationSilenceThresholdRmsDbfs, -50); 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'); const twelfthCenters = getRtwCenters('1_12'); +assert.equal(twelfthCenters[0], 5); +assert.equal(twelfthCenters.at(-1), 20000); const staleTwelfthPacket = { engine: 'iir', bpo: '1_12', diff --git a/src/audio.rs b/src/audio.rs index cc59036..d8a4667 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -90,24 +90,26 @@ const BOX_MAX_DB: f32 = 9.0; const NATIVE_RECORDER_DISCONTINUITY_BLEND_FRAMES: usize = 256; #[cfg(target_os = "linux")] 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, - 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, - 500.0, 560.0, 630.0, 708.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, 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, + 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, + 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, + 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, + 800.0, 900.0, 1000.0, 1120.0, 1250.0, 1410.0, 1600.0, 1800.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, 8000.0, 9000.0, 10000.0, + 11200.0, 12500.0, 14100.0, 16000.0, 18000.0, 20000.0, ]; #[cfg(target_os = "linux")] 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, - 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, - 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, - 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, - 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, - 1120.0, 1180.0, 1250.0, 1320.0, 1400.0, 1500.0, 1600.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, 3340.0, 3550.0, 3760.0, 4000.0, 4240.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, - 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, + 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, + 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, + 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, + 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, + 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, + 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, + 850.0, 900.0, 950.0, 1000.0, 1060.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, 2240.0, 2380.0, 2500.0, 2650.0, 2800.0, 2980.0, 3150.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, + 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)] diff --git a/src/rta.rs b/src/rta.rs index 6fe012a..07e74b3 100644 --- a/src/rta.rs +++ b/src/rta.rs @@ -6,9 +6,9 @@ use std::collections::VecDeque; 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, - 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, - 6_300.0, 8_000.0, 10_000.0, 12_500.0, 16_000.0, 20_000.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, + 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, + 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. @@ -448,7 +448,7 @@ mod tests { #[test] 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 { let center = exact_fractional_octave_center(nominal_center, 3); let (lower, upper) = fractional_octave_edges(center, 3); diff --git a/src/state.rs b/src/state.rs index 4087a3f..5e00487 100644 --- a/src/state.rs +++ b/src/state.rs @@ -426,7 +426,6 @@ fn normalize_rta_config(mut config: PhoenixRtaConfig) -> PhoenixRtaConfig { if config.layout == "rtw" { config.engine = "iir".to_string(); config.order = 6; - config.freq_range = "norm".to_string(); } // 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.bpo, "1_12"); assert_eq!(config.order, 6); - assert_eq!(config.freq_range, "norm"); + assert_eq!(config.freq_range, "lf"); } #[test] diff --git a/www/core/audio.js b/www/core/audio.js index 8d1e0fc..45dd0a8 100644 --- a/www/core/audio.js +++ b/www/core/audio.js @@ -927,7 +927,7 @@ export function buildRtaRuntimeConfig(CONFIG = {}) { lrFractionalDelayEnabled: !!CONFIG.LR_FRACTIONAL_DELAY_ENABLED, lrFractionalDelaySamples: Number.isFinite(CONFIG.LR_FRACTIONAL_DELAY_SAMPLES) ? CONFIG.LR_FRACTIONAL_DELAY_SAMPLES : 0, bpo: runtimeBpoMode, - freqRange: rtwProfile ? 'norm' : (CONFIG.RTA_FREQ_RANGE || 'norm'), + freqRange: CONFIG.RTA_FREQ_RANGE === 'lf' ? 'lf' : 'norm', weighting: CONFIG.RTA_WEIGHTING || 'z', order: rtwProfile ? 6 : (CONFIG.RTA_IIR_ORDER || 6), tauFast: CONFIG.RTA_IIR_TAU_FAST || 0.125, diff --git a/www/core/config.js b/www/core/config.js index 7a9743a..8b7b312 100644 --- a/www/core/config.js +++ b/www/core/config.js @@ -821,7 +821,6 @@ function loadConfig(opts = {}) { if (CONFIG.RTA_BAR_LAYOUT === 'rtw') { CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_IIR_ORDER = 6; - CONFIG.RTA_FREQ_RANGE = 'norm'; const hold = Number(CONFIG.RTA_PEAK_HOLD_SEC); 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') { CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_IIR_ORDER = 6; - CONFIG.RTA_FREQ_RANGE = 'norm'; } const spectroGamma = Number(payload.spectroGamma); if (Number.isFinite(spectroGamma)) CONFIG.SPECTRO_GAMMA = Math.max(0.3, Math.min(1.2, spectroGamma)); diff --git a/www/core/rtw_centers.js b/www/core/rtw_centers.js index 753fef8..3723107 100644 --- a/www/core/rtw_centers.js +++ b/www/core/rtw_centers.js @@ -1,10 +1,11 @@ export const RTW_CENTER_MAP = { '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, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000, ], '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, 63, 70.8, 80, 90, 100, 112, 125, 141, 160, 180, 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, ], '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, 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, diff --git a/www/ui/options.js b/www/ui/options.js index 1312690..ebdaba3 100644 --- a/www/ui/options.js +++ b/www/ui/options.js @@ -1002,7 +1002,7 @@ function wireHandlers(env) { return val; }); 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; }); 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') { CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_IIR_ORDER = 6; - CONFIG.RTA_FREQ_RANGE = 'norm'; const forced = { opt_rtaEngine: 'iir', opt_rtaOrder: '6', - opt_rtaFreq: 'norm', }; for (const [id, value] of Object.entries(forced)) { const control = document.getElementById(id); diff --git a/www/views/realtime.js b/www/views/realtime.js index 46c2adb..d499a23 100644 --- a/www/views/realtime.js +++ b/www/views/realtime.js @@ -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_EXTRA_BOTTOM_PAD = 5; 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 RTA_PEAK_COLOR = WARN_COLOR; const EMBEDDED_RTA_HIDE_20HZ_WIDTH = 420;