Compare commits

...
4 Commits
Author SHA1 Message Date
michael ec3b6c3059 correlation: hold last value and ease to zero on silence
The XY correlation cube snapped to center the moment the audio dropped
below the silence threshold, because process() hard-set value to 0.0 in
the silent branch. Now the silent branch eases the displayed value back
toward zero using the configured response time, so the cube holds its
last reading and then settles instead of jumping. The active path is
unchanged, so all existing response-time and peak tests still pass.

- add `display` (f64) that tracks the live correlation while active
- silent branch: display += alpha * (0 - display)
- update configurable_silence_threshold test for the eased decay
- add correlation_holds_then_decays_to_zero_on_silence test
2026-09-04 12:44:28 +02:00
Mikei386 684c5fe3d3 Install Volumio integration during updates 2026-08-11 09:06:50 +02:00
Mikei386 07563c3b11 Prevent kiosk and getty tty1 race 2026-08-11 08:59:23 +02:00
Mikei386 48578c5919 Enable low-frequency RTW analyzer range 2026-08-06 20:53:30 +02:00
17 changed files with 201 additions and 53 deletions

No files matched your search

+7 -2
View File
@@ -2,8 +2,13 @@ Mini-Dokumentation: Analyzer Kiosk auf Raspberry Pi OS
1. Dienste deaktivieren
sudo systemctl disable --now lightdm.service
sudo systemctl disable --now getty@tty1.service
sudo systemctl reset-failed getty@tty1.service
sudo systemctl mask --now getty@tty1.service
Hinweis:
- tty1 ist exklusiv fuer Xorg/Chromium reserviert. Ein blosses `disable` reicht
nicht, weil systemd den Getty bei einem spaeteren Boot erneut erzeugen kann.
- Fuer einen lokalen Login-Prompt auf tty1 den Kiosk zuerst stoppen und Getty
anschliessend mit `sudo systemctl unmask --now getty@tty1.service` reaktivieren.
2. systemd neu laden
sudo systemctl daemon-reload
+19 -2
View File
@@ -124,8 +124,25 @@ zugehörige sudo-Regel werden einmalig als root installiert:
sudo bash /home/volumio/Phoenix/scripts/install_volumio_services.sh
```
Der Installer verändert keine Debian-/Analyzer-Units und startet laufende
Dienste nicht automatisch neu.
Der Installer überschreibt keine Debian-/Analyzer-Unitdateien und startet
laufende Dienste nicht automatisch neu. Er maskiert `getty@tty1.service`, da der
Kiosk-X-Server `tty1` exklusiv verwendet. Ohne die Maskierung können Getty und
Xorg beim Booten um dasselbe virtuelle Terminal konkurrieren und eine
Kiosk-Neustartschleife auslösen.
Nach dieser einmaligen Grundinstallation bringt der privilegierte
Volumio-Online-Updater bei jedem erfolgreichen Phoenix-Update auch die
mitgelieferten systemd-Units, den Update-Helfer und die sudo-Regel aus. Fehlen
diese Integrationsdateien in einem Update, wird es vor dem Austausch des
laufenden Projektbaums abgelehnt.
Um den lokalen Login-Prompt auf `tty1` wiederherzustellen, zuerst den Kiosk
stoppen und Getty anschließend wieder freigeben:
```bash
sudo systemctl stop analyzer-kiosk-volumio.service
sudo systemctl unmask --now getty@tty1.service
```
Wenn `toggle-ext` genutzt werden soll, sollte es also unter diesem Pfad liegen:
@@ -1,5 +1,7 @@
[Unit]
Description=Analyzer Kiosk Volumio (Raspberry Pi OS)
Conflicts=getty@tty1.service
After=systemd-user-sessions.service getty-pre.target
After=network-online.target
After=analyzer-web-volumio.service
Wants=analyzer-web-volumio.service
@@ -1,5 +1,7 @@
[Unit]
Description=Analyzer Kiosk (Raspberry Pi OS)
Conflicts=getty@tty1.service
After=systemd-user-sessions.service getty-pre.target
After=network-online.target
After=analyzer-web.service
Wants=analyzer-web.service
+5
View File
@@ -51,8 +51,13 @@ install -o root -g root -m 0440 \
"$SUDOERS_SOURCE" \
/etc/sudoers.d/phoenix-update-volumio
# tty1 is reserved for Xorg by analyzer-kiosk-volumio.service. Disabling the
# generated getty is insufficient because systemd may create/start it again on
# a later boot. Mask it so the kiosk and agetty can never race for the VT.
systemctl mask --now getty@tty1.service
systemctl daemon-reload
systemctl enable analyzer-web-volumio.service analyzer-kiosk-volumio.service
echo "Volumio integration installed"
echo "getty@tty1.service masked; run 'systemctl unmask getty@tty1.service' to restore the tty1 login console"
echo "Existing services were not restarted"
+58 -7
View File
@@ -209,6 +209,46 @@ build_volumio_candidate() {
echo "staged Volumio build completed"
}
validate_volumio_integration_candidate() {
local phoenix_dir="${1:-$STAGE_DIR}"
local required_path
local required_paths=(
"scripts/install_volumio_services.sh"
"scripts/restart_phoenix_services.sh"
"etc/systemd/system/analyzer-kiosk-volumio.service"
"etc/systemd/system/analyzer-web-volumio.service"
"etc/systemd/system/phoenix-update-volumio.service"
"etc/sudoers.d/phoenix-update-volumio"
)
for required_path in "${required_paths[@]}"; do
if [ ! -f "$phoenix_dir/$required_path" ]; then
echo "staged Volumio integration file missing: $phoenix_dir/$required_path" >&2
return 1
fi
done
bash -n \
"$phoenix_dir/scripts/install_volumio_services.sh" \
"$phoenix_dir/scripts/restart_phoenix_services.sh"
}
install_volumio_integration() {
local phoenix_dir="${1:-$PHOENIX_ROOT}"
local installer="$phoenix_dir/scripts/install_volumio_services.sh"
if [ "$(id -u)" -ne 0 ]; then
echo "Volumio integration update requires root" >&2
return 1
fi
if [ ! -f "$installer" ]; then
echo "Volumio integration installer missing: $installer" >&2
return 1
fi
echo "installing Volumio system integration from $phoenix_dir"
/bin/bash "$installer"
}
start_all_services() {
if is_volumio_install; then
restart_if_present analyzer-web-volumio.service
@@ -248,6 +288,12 @@ rollback_to_backup() {
return 1
fi
mv "$BACKUP_DIR" "$PHOENIX_ROOT"
if is_volumio_install && [ -f "$PHOENIX_ROOT/scripts/install_volumio_services.sh" ]; then
if ! install_volumio_integration "$PHOENIX_ROOT"; then
echo "backup files were restored but their Volumio integration could not be installed" >&2
return 1
fi
fi
if ! wait_for_candidate_health "$PHOENIX_ROOT" "${PHOENIX_ROLLBACK_HEALTH_ATTEMPTS:-120}" 1; then
echo "backup rollback restored files but Phoenix health did not recover" >&2
return 1
@@ -289,6 +335,7 @@ if [ ! -f "$STAGE_DIR/Cargo.toml" ] || [ ! -d "$STAGE_DIR/src" ] || [ ! -d "$STA
fi
if is_volumio_install; then
validate_volumio_integration_candidate "$STAGE_DIR"
build_volumio_candidate "$STAGE_DIR"
fi
@@ -303,6 +350,17 @@ mv "$STAGE_DIR" "$PHOENIX_ROOT"
health_attempts=45
health_delay=1
if is_volumio_install; then
if ! install_volumio_integration "$PHOENIX_ROOT"; then
if ! rollback_to_backup; then
echo "new Volumio integration failed and rollback failed" >&2
exit 1
fi
echo "new Volumio integration failed; rolled back to backup" >&2
exit 1
fi
fi
if ! wait_for_candidate_health "$PHOENIX_ROOT" "$health_attempts" "$health_delay"; then
if ! rollback_to_backup; then
echo "new Phoenix version failed health check and rollback failed" >&2
@@ -312,11 +370,4 @@ if ! wait_for_candidate_health "$PHOENIX_ROOT" "$health_attempts" "$health_delay
exit 1
fi
if is_volumio_install && [ "$(id -u)" -eq 0 ] && [ -f "$PHOENIX_ROOT/scripts/restart_phoenix_services.sh" ]; then
install -o root -g root -m 0755 \
"$PHOENIX_ROOT/scripts/restart_phoenix_services.sh" \
/usr/local/libexec/phoenix/restart_phoenix_services.sh
echo "Volumio privileged update helper refreshed"
fi
echo "=== $(date): restart_phoenix_services.sh OK ==="
+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 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(/<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');
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');
+7 -5
View File
@@ -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',
+17 -15
View File
@@ -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)]
+67 -8
View File
@@ -17,6 +17,7 @@ pub struct CorrelationMeter {
cross_power: f64,
gate_power: f64,
value: f32,
display: f64,
negative_peak: f32,
reset_token: u64,
}
@@ -40,6 +41,7 @@ impl CorrelationMeter {
cross_power: 0.0,
gate_power: 0.0,
value: 0.0,
display: 0.0,
negative_peak: 0.0,
reset_token,
};
@@ -93,18 +95,24 @@ impl CorrelationMeter {
* (0.5 * (l * l + r * r) - self.gate_power);
// Below the configured mono RMS threshold the correlation indication
// settles at its neutral position.
// eases back to its neutral position instead of snapping there.
const MIN_POWER: f64 = 1.0e-10;
let denominator = (self.power_l * self.power_r).sqrt();
self.value = if self.gate_power >= self.silence_threshold_power
let active = self.gate_power >= self.silence_threshold_power
&& self.power_l > MIN_POWER
&& self.power_r > MIN_POWER
&& denominator > 0.0
{
(self.cross_power / denominator).clamp(-1.0, 1.0) as f32
&& denominator > 0.0;
if active {
// Audio present: track the live correlation exactly as before.
self.display = (self.cross_power / denominator).clamp(-1.0, 1.0);
self.value = self.display as f32;
} else {
0.0
};
// Silent: hold the last value, then decay it back to zero using
// the configured response time, so the XY cube settles instead of
// jumping to center.
self.display += self.alpha * (0.0 - self.display);
self.value = self.display as f32;
}
if self.value < self.negative_peak {
self.negative_peak = self.value;
}
@@ -255,7 +263,26 @@ mod tests {
};
(sample, sample)
});
assert_eq!(meter.value(), 0.0);
// Raising the threshold above the signal level neutralizes the meter.
// With the eased display it decays toward zero rather than snapping.
assert!(
meter.value() < 0.2,
"meter did not neutralize: {}",
meter.value()
);
run_signal(&mut meter, 3, |index| {
let sample = if index & 1 == 0 {
amplitude
} else {
-amplitude
};
(sample, sample)
});
assert!(
meter.value().abs() < 0.05,
"meter did not settle to zero: {}",
meter.value()
);
}
#[test]
@@ -278,4 +305,36 @@ mod tests {
});
assert_eq!(meter.value(), 0.0);
}
#[test]
fn correlation_holds_then_decays_to_zero_on_silence() {
let sample_rate = 48_000;
let mut meter = CorrelationMeter::new(sample_rate, 1.0, -75.0, 0);
// Drive a strongly correlated signal so the meter settles near +1.
for index in 0..sample_rate as usize * 3 {
let sample = if index & 1 == 0 { 0.5 } else { -0.5 };
meter.process(sample, sample);
}
assert!(meter.value() > 0.9, "expected near +1, got {}", meter.value());
// Cut the audio: the value must hold (not snap to 0) right away...
for _ in 0..10 {
meter.process(0.0, 0.0);
}
assert!(
meter.value() > 0.5,
"value snapped to zero on silence: {}",
meter.value()
);
// ...and then decay back toward zero over the response time.
for _ in 0..sample_rate as usize * 4 {
meter.process(0.0, 0.0);
}
assert!(
meter.value().abs() < 0.05,
"value did not decay to zero: {}",
meter.value()
);
}
}
+4 -4
View File
@@ -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);
+1 -2
View File
@@ -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]
+1 -1
View File
@@ -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,
-2
View File
@@ -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));
+4 -1
View File
@@ -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,
+1 -3
View File
@@ -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);
+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_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;