diff --git a/src/web/server.py b/src/web/server.py index 18a7c15..2c509f1 100644 --- a/src/web/server.py +++ b/src/web/server.py @@ -2490,12 +2490,40 @@ iface eth0 inet static try: import psutil return { - "cpu_percent": psutil.cpu_percent(interval=None), + "cpu_percent": psutil.cpu_percent(interval=0.2), "sample_rate": SAMPLE_RATE if hasattr(self, 'deps') and self.deps.pipeline else 48000, } except Exception: return {"cpu_percent": 0, "sample_rate": 48000} + def _calc_nam_cpu(self, nam, pipeline) -> float: + """Estimate NAM engine CPU load as a percentage of real-time. + + Uses the engine's own per-block inference timing. When the NAM + block is bypassed or the engine isn't loaded, returns 0. + """ + if not nam or not nam.is_loaded: + return 0.0 + # Check if the NAM block is bypassed in the pipeline chain + if pipeline and hasattr(pipeline, '_chain'): + for entry in pipeline._chain: + if entry.get("fx_type") and entry["fx_type"].value in ("nam_amp", "nam"): + if entry.get("bypass") or not entry.get("enabled", True): + return 0.0 + break + avg_ms = nam.avg_inference_ms + if avg_ms <= 0: + return 0.0 + try: + period = self.deps.audio_system.config.latency_profile["period"] + rate = self.deps.audio_system.config.latency_profile["rate"] + callback_ms = (period / rate) * 1000.0 + if callback_ms <= 0: + return 0.0 + return round(min(100.0, (avg_ms / callback_ms) * 100.0), 1) + except Exception: + return 0.0 + # ── State gathering ────────────────────────────────────────────── def _gather_channel_state(self, channel: str) -> dict[str, Any]: @@ -2616,6 +2644,7 @@ iface eth0 inet static ), # System stats "cpu_percent": self._gather_system_stats().get("cpu_percent", 0), + "nam_cpu": self._calc_nam_cpu(nam, pl), "sample_rate": SAMPLE_RATE, "wifi": self._gather_wifi_state(), "bluetooth": self._gather_bt_state(), diff --git a/src/web/ui-v2-dist/index.html b/src/web/ui-v2-dist/index.html index 3f2db24..4063459 100644 --- a/src/web/ui-v2-dist/index.html +++ b/src/web/ui-v2-dist/index.html @@ -174,6 +174,7 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki