Add 3.8 changelog and fix RTA octave selection

This commit is contained in:
Mikei386
2026-07-21 20:13:04 +02:00
parent 54f0f7a450
commit e318647d3b
6 changed files with 78 additions and 11 deletions
+15
View File
@@ -44,4 +44,19 @@ assert.equal(extension.bpo, '1_12');
assert.equal(extension.freqRange, 'lf'); assert.equal(extension.freqRange, 'lf');
assert.equal(extension.order, 8); assert.equal(extension.order, 8);
const configSource = fs.readFileSync(new URL('../www/core/config.js', import.meta.url), 'utf8');
const selectionSource = configSource.match(/function applyRtaBpoSelection[\s\S]*?\n\}/)?.[0];
assert.ok(selectionSource, 'RTA BPO selection policy must remain testable');
const selectionContext = vm.createContext({
String,
CONFIG: { RTA_BPO_MODE: '1_3', RTA_BAR_LAYOUT: 'rtw' },
});
vm.runInContext(selectionSource, selectionContext);
assert.equal(selectionContext.applyRtaBpoSelection('1_6'), '1_6');
assert.equal(selectionContext.CONFIG.RTA_BPO_MODE, '1_6');
assert.equal(selectionContext.CONFIG.RTA_BAR_LAYOUT, 'iec');
selectionContext.CONFIG.RTA_BAR_LAYOUT = 'rtw';
assert.equal(selectionContext.applyRtaBpoSelection('1_3'), '1_3');
assert.equal(selectionContext.CONFIG.RTA_BAR_LAYOUT, 'rtw');
console.log('RTA profile regression tests passed'); console.log('RTA profile regression tests passed');
+13
View File
@@ -663,6 +663,9 @@ fn apply_global_to_rta(
) -> PhoenixRtaConfig { ) -> PhoenixRtaConfig {
config.fft_size = global.fft_size; config.fft_size = global.fft_size;
config.bpo = global.rta_bpo_mode.clone(); config.bpo = global.rta_bpo_mode.clone();
if config.bpo != "1_3" && config.layout == "rtw" {
config.layout = "iec".to_string();
}
config.input_offset_db_l = global.input_offset_db_l; config.input_offset_db_l = global.input_offset_db_l;
config.input_offset_db_r = global.input_offset_db_r; config.input_offset_db_r = global.input_offset_db_r;
config.mono_input = global.mono_input; config.mono_input = global.mono_input;
@@ -744,4 +747,14 @@ mod tests {
assert_eq!(config.freq_range, "lf"); assert_eq!(config.freq_range, "lf");
assert_eq!(config.integration, "medium"); assert_eq!(config.integration, "medium");
} }
#[test]
fn non_third_octave_global_selection_leaves_locked_rtw_profile() {
let mut global = PhoenixGlobalConfig::default();
global.rta_bpo_mode = "1_12".to_string();
let config = apply_global_to_rta(PhoenixRtaConfig::default(), &global);
assert_eq!(config.layout, "iec");
assert_eq!(config.bpo, "1_12");
assert_eq!(config.engine, "iir");
}
} }
+16 -1
View File
@@ -281,6 +281,17 @@ const CONFIG = {
SOFTWARE_PRESET: 'default', SOFTWARE_PRESET: 'default',
}; };
const CONFIG_DEFAULTS = JSON.parse(JSON.stringify(CONFIG)); const CONFIG_DEFAULTS = JSON.parse(JSON.stringify(CONFIG));
function applyRtaBpoSelection(value) {
const mode = ['1_3', '1_6', '1_12'].includes(String(value)) ? String(value) : '1_3';
CONFIG.RTA_BPO_MODE = mode;
// RTW remains the original 31-band third-octave reference profile. Finer
// resolutions are Phoenix extensions and switch visibly to the IEC profile.
if (mode !== '1_3' && CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_BAR_LAYOUT = 'iec';
}
return mode;
}
const SOFTWARE_PRESET_IDS = Object.freeze(['default', 'claus', 'michael']); const SOFTWARE_PRESET_IDS = Object.freeze(['default', 'claus', 'michael']);
const LAYOUT_PRESET_IDS = Object.freeze(['1', '2', '3', '4']); const LAYOUT_PRESET_IDS = Object.freeze(['1', '2', '3', '4']);
const LAYOUT_PRESET_KEYS = Object.freeze([ const LAYOUT_PRESET_KEYS = Object.freeze([
@@ -778,6 +789,9 @@ function loadConfig(opts = {}) {
if (typeof saved.PPM_DIN_DECAY_DB_PER_S === 'number') CONFIG.PPM_DIN_DECAY_DB_PER_S = saved.PPM_DIN_DECAY_DB_PER_S; if (typeof saved.PPM_DIN_DECAY_DB_PER_S === 'number') CONFIG.PPM_DIN_DECAY_DB_PER_S = saved.PPM_DIN_DECAY_DB_PER_S;
if (typeof saved.PPM_DIN_HOLD_MS === 'number') CONFIG.PPM_DIN_HOLD_MS = saved.PPM_DIN_HOLD_MS; if (typeof saved.PPM_DIN_HOLD_MS === 'number') CONFIG.PPM_DIN_HOLD_MS = saved.PPM_DIN_HOLD_MS;
if (CONFIG.RTA_BAR_LAYOUT === 'classic') CONFIG.RTA_BAR_LAYOUT = 'rtw'; if (CONFIG.RTA_BAR_LAYOUT === 'classic') CONFIG.RTA_BAR_LAYOUT = 'rtw';
if (CONFIG.RTA_BAR_LAYOUT === 'rtw' && CONFIG.RTA_BPO_MODE !== '1_3') {
CONFIG.RTA_BAR_LAYOUT = 'iec';
}
if (CONFIG.RTA_BAR_LAYOUT === 'rtw') { if (CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_ENGINE = 'iir';
CONFIG.RTA_BPO_MODE = '1_3'; CONFIG.RTA_BPO_MODE = '1_3';
@@ -1108,7 +1122,7 @@ function applyPhoenixGlobalConfig(payload = {}) {
} }
if ('rtaBpoMode' in payload) { if ('rtaBpoMode' in payload) {
const mode = String(payload.rtaBpoMode || '').trim(); const mode = String(payload.rtaBpoMode || '').trim();
CONFIG.RTA_BPO_MODE = (mode === '1_3' || mode === '1_6' || mode === '1_12') ? mode : '1_6'; applyRtaBpoSelection(mode);
} }
if (CONFIG.RTA_BAR_LAYOUT === 'rtw') { if (CONFIG.RTA_BAR_LAYOUT === 'rtw') {
CONFIG.RTA_ENGINE = 'iir'; CONFIG.RTA_ENGINE = 'iir';
@@ -1278,6 +1292,7 @@ export {
saveConfig, saveConfig,
buildPhoenixGlobalConfigPayload, buildPhoenixGlobalConfigPayload,
applyPhoenixGlobalConfig, applyPhoenixGlobalConfig,
applyRtaBpoSelection,
applyBroadcastStandard, applyBroadcastStandard,
applySoftwarePreset, applySoftwarePreset,
loadSoftwarePreset, loadSoftwarePreset,
+27 -3
View File
@@ -310,7 +310,7 @@
<option value="1_6">1/6 Oktave</option> <option value="1_6">1/6 Oktave</option>
<option value="1_12">1/12 Oktave</option> <option value="1_12">1/12 Oktave</option>
</select> </select>
<small>Filterauflösung Anzahl Bänder</small> <small>RTW verwendet fest 1/3; 1/6 und 1/12 wechseln automatisch zum IEC-/Phoenix-Profil.</small>
</div> </div>
<div class="opt"><label>IIR Ordnung</label> <div class="opt"><label>IIR Ordnung</label>
<input id="opt_rtaOrder" type="number" min="2" max="8" step="2" style="width:110px"> <input id="opt_rtaOrder" type="number" min="2" max="8" step="2" style="width:110px">
@@ -1245,8 +1245,32 @@
</p> </p>
<details style="margin:20px 0 0 5px"> <details style="margin:20px 0 0 5px">
<summary>Version / Änderungen</summary> <summary>Version / Änderungen</summary>
<div class="opt-group"> <div class="opt-group">
<details style="margin-left:12px" open> <details style="margin-left:12px" open>
<summary>3.8 (Phoenix 1.0)</summary>
<div class="opt-group">
<div class="opt">
<p style="margin:0; color:#8fd3d4;">Veröffentlichung: 21.07.2026</p>
<p style="margin:4px 0 0 12px; color:#8fd3d4;">Änderungen:</p>
<ul style="margin:2px 0 0 24px; color:#b2c7d9;">
<li><strong>Analysegrundlage:</strong> Die tiefgehende Code- und DSP-Prüfung, durch die die nachfolgenden Fehler und Verbesserungsmöglichkeiten gefunden werden konnten, wurde durch den Umstieg auf das LLM-Modell GPT-5.6 SOL ermöglicht.</li>
<li><strong>Echtzeit-Datenweg:</strong> WebSocket-Verarbeitung auf „latest value wins“ umgestellt. Messwerte, Spektrogramm sowie Goniometer/Waveform verwenden getrennte, begrenzte Datenwege; alte Frames können keine anwachsende Anzeigeverzögerung mehr bilden.</li>
<li><strong>Browserlast:</strong> Single-Slot-Puffer und Sequenzprüfung für Mess-, Spektrogramm- und Visualisierungsdaten ergänzt. Große Rohsamplefelder werden nicht mehr in jedem JSON-Messpaket wiederholt.</li>
<li><strong>Spektrogramm:</strong> Langzeitstillstand und stotterndes Nachholen beseitigt. Inkrementelles Spaltenzeichnen, Worker-ACK, Watchdog/Neustart und begrenztes Überspringen veralteter Spalten ergänzt. 0,5×, 1×, 2×, 4× und 6× besitzen nun eine feste, FFT- und DPI-unabhängige Zeitbasis.</li>
<li><strong>DIN-/EBU-PPM:</strong> Blockunabhängige Quasi-Peak-Detektoren mit bandbegrenzter Interpolation, korrekter Attack- und Rücklaufballistik sowie automatischen Tonburst-, Frequenzgang- und Polaritätstests implementiert. Eine zweite Browser-Anstiegsballistik wurde entfernt.</li>
<li><strong>RTW-RTA:</strong> RTW-Profil verbindlich auf die IIR-Dritteloktav-Filterbank festgelegt. Die Filterbank verwendet vollständige Butterworth-Bandpässe sechster Ordnung aus unterschiedlichen SOS-Sektionen, interne f64-Rechnung sowie exakte IEC-Basis-10-Mitten und -Bandkanten bei vertrauten Nominalbeschriftungen.</li>
<li><strong>RTA-Bewertung und Integration:</strong> A-, C- und Z-Bewertung sampleweise vor der Filterbank ergänzt. Fast, Medium, Slow, Impulse, Average und Peak rechnen im Leistungsbereich und unabhängig von der ALSA-Periodengröße.</li>
<li><strong>RTA-Auflösung:</strong> Umschaltung zwischen 1/3, 1/6 und 1/12 repariert. 1/6 und 1/12 wechseln sichtbar in das IEC-/Phoenix-Erweiterungsprofil; das originale RTW-Profil bleibt korrekt auf 1/3 verriegelt.</li>
<li><strong>Hardwareparameter:</strong> Tatsächlich von ALSA ausgehandelte Samplerate, Perioden- und Puffergröße werden nun durch DSP, Aufnahme, Status und Browser verwendet; die feste 48-kHz-Annahme wurde entfernt.</li>
<li><strong>Goniometer:</strong> Nur neue XY-Samplepaare werden mit einer periodengrößenunabhängigen mittleren Rate von 60 Hz übertragen. Punktbegrenzung wirkt bereits auf den Transport; begrenzte und wiederverwendbare Spurpuffer reduzieren Speicher- und Zeichenlast.</li>
<li><strong>Goniometer-Persistenz:</strong> Reproduzierbare Fast-, Medium- und Slow-Profile sowie ein freies Phoenix-Fade ergänzt. Künstliche Bézier-Verformung der Messspur entfernt; AGC und Silence-Gate arbeiten zeit- beziehungsweise fensterbasiert.</li>
<li><strong>Korrelation:</strong> Kontinuierliche DSP-Messung aus L², R² und L·R mit wählbaren 1,0/2,5 Sekunden ergänzt. Bildratenabhängige Doppelglättung entfernt; Negative-Peak-Memory, Marker und manueller Reset hinzugefügt.</li>
<li><strong>Qualitätssicherung:</strong> Automatische Regressionstests für PPM, RTA, A/C/Z, mehrere Sampleraten, Korrelationssignale, Goniometertaktung, Binärprotokolle sowie Spektrogramm-Zeitbasis und Langlauf ergänzt.</li>
</ul>
</div>
</div>
</details>
<details style="margin-left:12px" open>
<summary>3.3.5 (Phoenix 1.0)</summary> <summary>3.3.5 (Phoenix 1.0)</summary>
<div class="opt-group"> <div class="opt-group">
<div class="opt"> <div class="opt">
+3 -2
View File
@@ -20,7 +20,7 @@ try {
} }
} catch(_) {} } catch(_) {}
import { CONFIG, loadConfig, saveConfig, loadLayoutPreset } from './core/config.js'; import { CONFIG, applyRtaBpoSelection, loadConfig, saveConfig, loadLayoutPreset } from './core/config.js';
import * as utils from './core/utils.js'; import * as utils from './core/utils.js';
import { meterFacade, registerMeter } from './core/registry.js'; import { meterFacade, registerMeter } from './core/registry.js';
import { initAudio, audioLost, reloadAudio } from './core/audio.js'; import { initAudio, audioLost, reloadAudio } from './core/audio.js';
@@ -2152,7 +2152,7 @@ function handlePeakHistScrollSelection(value){
function handleRtaBpoSelection(value){ function handleRtaBpoSelection(value){
const mode = normalizeRtaBpo(value); const mode = normalizeRtaBpo(value);
CONFIG.RTA_BPO_MODE = mode; applyRtaBpoSelection(mode);
saveConfig(); saveConfig();
if (rtaBpoSel) { if (rtaBpoSel) {
rtaBpoSel.value = mode; rtaBpoSel.value = mode;
@@ -2162,6 +2162,7 @@ function handleRtaBpoSelection(value){
select.value = mode; select.value = mode;
} }
try { env.audio.updateRtaConfig?.(); } catch (_) {} try { env.audio.updateRtaConfig?.(); } catch (_) {}
try { env.audio.updatePhoenixGlobalConfig?.(); } catch (_) {}
} }
function getNearestWaveWindow(value){ function getNearestWaveWindow(value){
+4 -5
View File
@@ -1,7 +1,7 @@
// ui/options.js — bindet das Options-Panel an CONFIG (lesen/schreiben), Presets, Export/Import // ui/options.js — bindet das Options-Panel an CONFIG (lesen/schreiben), Presets, Export/Import
// Erwartet vorhandenes DOM aus index.html. Nutzt localStorage-Key 'analyzer_config'. // Erwartet vorhandenes DOM aus index.html. Nutzt localStorage-Key 'analyzer_config'.
import { CONFIG, saveConfig, loadConfig, applyAlignmentProfile, applySoftwarePreset, loadSoftwarePreset, saveSoftwarePreset, loadLayoutPreset, saveLayoutPreset, exportSoftwarePreset, importSoftwarePreset, updateVuReference, resetConfigToDefaults, updateInputOffsetGain } from '../core/config.js'; import { CONFIG, saveConfig, loadConfig, applyAlignmentProfile, applyRtaBpoSelection, applySoftwarePreset, loadSoftwarePreset, saveSoftwarePreset, loadLayoutPreset, saveLayoutPreset, exportSoftwarePreset, importSoftwarePreset, updateVuReference, resetConfigToDefaults, updateInputOffsetGain } from '../core/config.js';
import { clamp, clampPow2 } from '../core/utils.js'; import { clamp, clampPow2 } from '../core/utils.js';
// DOM helper // DOM helper
@@ -1075,10 +1075,9 @@ function wireHandlers(env) {
return CONFIG.RTA_ENGINE; return CONFIG.RTA_ENGINE;
}); });
h('opt_rtaBpo', v => { h('opt_rtaBpo', v => {
const allowed = new Set(['1_3', '1_6', '1_12']); applyRtaBpoSelection(v);
CONFIG.RTA_BPO_MODE = CONFIG.RTA_BAR_LAYOUT === 'rtw' const layoutControl = E('opt_rtaLayout');
? '1_3' if (layoutControl) layoutControl.value = CONFIG.RTA_BAR_LAYOUT;
: (allowed.has(v) ? v : '1_6');
notifyPhoenixGlobalConfig(); notifyPhoenixGlobalConfig();
return CONFIG.RTA_BPO_MODE; return CONFIG.RTA_BPO_MODE;
}); });