From 4a6e7900ce0d4256b1e1909d26335e8088894d7d Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 17 Jun 2026 17:53:36 -0400 Subject: [PATCH] fix: NAM CPU display now responds to bypass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPU label in the UI was showing psutil.cpu_percent(interval=None) which returns total system CPU — barely moves and doesn't change when the NAM model is bypassed. Fix: - Added _calc_nam_cpu() that computes NAM engine CPU from the engine's own per-block inference timing (avg_inference_ms) vs the JACK callback interval. When the NAM block is bypassed in the chain, returns 0. - Added 'nam_cpu' field to /api/state response - Status bar shows NAM CPU % next to VU meters (drops to 0 on bypass) - Settings CPU row shows NAM CPU with system CPU in dim text beside it - Changed psutil.cpu_percent(interval=None) → interval=0.2 for a real system CPU reading --- src/web/server.py | 31 ++++++++++++++++++++++++++++++- src/web/ui-v2-dist/index.html | 11 ++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) 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
80%
I
O
+ 0% @@ -392,7 +393,7 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
🔓 DiscoverableOn
System
-
🔧 CPU0%
+
🔧 CPU0%
🌡 Temp
⏱ Uptime
💾 Disk
@@ -478,7 +479,7 @@ const API = { }; // ═══ State ═══ -let state={channel:'guitar',connected:false,bypass:false,master_volume:0.8,blocks:[],routing_mode:'4cm',tuner_enabled:false,cpu_percent:0}; +let state={channel:'guitar',connected:false,bypass:false,master_volume:0.8,blocks:[],routing_mode:'4cm',tuner_enabled:false,cpu_percent:0,nam_cpu:0}; let currentChannel='guitar'; let channels=['guitar','bass','keys','vocals','backing_tracks']; let presets=[]; @@ -656,7 +657,11 @@ async function loadState(){ const fs=document.getElementById('footswitch'); if(s.bypass){fs.className='footswitch off';fs.textContent='MUTE';} else{fs.className='footswitch on';fs.textContent='BYpass';} - document.getElementById('cpuLabel').textContent=(s.cpu_percent!=null?s.cpu_percent:'—')+'%'; + document.getElementById('cpuLabel').textContent=(s.nam_cpu!=null?s.nam_cpu:'—')+'%'; + const ncEl=document.getElementById('namCpuLabel'); + if(ncEl)ncEl.textContent=(s.nam_cpu!=null?s.nam_cpu:'—')+'%'; + const scEl=document.getElementById('sysCpuLabel'); + if(scEl)scEl.textContent='sys '+(s.cpu_percent!=null?s.cpu_percent:'—')+'%'; const vi=Math.min(100,Math.round((s.input_level||0)*300)); const vo=Math.min(100,Math.round((s.output_level||0)*300)); document.getElementById('vuIn').style.width=vi+'%';