fix: narrow SHM cleanup in profile handler to client semaphore only; add DC-blocking HPF to pipeline input (60Hz hum fix)
CI / test (push) Has been cancelled

This commit is contained in:
2026-06-18 17:46:41 -04:00
parent 92c6bb9587
commit 4ad50e3588
2 changed files with 49 additions and 6 deletions
+42
View File
@@ -352,6 +352,15 @@ class AudioPipeline:
# load_preset() holds it only for the atomic swap (not during model I/O).
self._lock = threading.Lock()
# ── DC-blocking filter state ───────────────────────────────────────
# Single-pole high-pass at ~15Hz to remove DC offset and subsonic
# noise that can amplify into audible hum through NAM models.
# Filter: y[n] = alpha * (y[n-1] + x[n] - x[n-1])
# where alpha = 1 / (1 + 2*pi*fc/sr)
self._dc_prev_x: float = 0.0
self._dc_prev_y: float = 0.0
self._dc_alpha: float = 1.0 / (1.0 + 2.0 * np.pi * 15.0 / 48000.0)
logger.info("Audio pipeline initialized (block=%d, sr=%d)",
self._block_size, self._sample_rate)
@@ -646,6 +655,21 @@ class AudioPipeline:
)
buf = audio_in.copy()
# ── DC-blocking high-pass filter ────────────────────────────
# Removes DC offset and subsonic noise (~15Hz corner) that can
# amplify into audible hum through high-gain NAM models.
# First-order HPF: y[n] = alpha * (y[n-1] + x[n] - x[n-1])
alpha = self._dc_alpha
px = self._dc_prev_x
py = self._dc_prev_y
for i in range(len(buf)):
y = alpha * (py + buf[i] - px)
px = buf[i]
py = y
buf[i] = y
self._dc_prev_x = px
self._dc_prev_y = py
for idx, entry in enumerate(chain):
if entry["bypass"] or not entry["enabled"]:
continue
@@ -683,6 +707,23 @@ class AudioPipeline:
ch0 = audio_in[0, :].copy()
ch1 = audio_in[1, :].copy()
# ── DC-blocking high-pass filter on both channels ───────────
alpha = self._dc_alpha
px = self._dc_prev_x
py = self._dc_prev_y
for i in range(len(ch0)):
y = alpha * (py + ch0[i] - px)
px = ch0[i]
py = y
ch0[i] = y
for i in range(len(ch1)):
y = alpha * (py + ch1[i] - px)
px = ch1[i]
py = y
ch1[i] = y
self._dc_prev_x = px
self._dc_prev_y = py
# Update input VU level from the guitar input channel (ch0)
in_rms = np.sqrt(np.mean(ch0 ** 2) + _EPS)
self._input_level = (
@@ -3102,6 +3143,7 @@ class AudioPipeline:
self._block_size = block_size
self._sample_rate = sample_rate
self._vu_alpha = np.exp(-block_size / (0.05 * sample_rate))
self._dc_alpha = 1.0 / (1.0 + 2.0 * np.pi * 15.0 / sample_rate)
# Clear DSP state — effects will reinit with new block/sample rate
self._state.clear()
self._coeffs.clear()
+7 -6
View File
@@ -1768,14 +1768,15 @@ class WebServer:
# Clean stale client SHM before creating new JACK client.
# The old process's jack.Client __del__ can recreate the
# pi-multifx semaphore after stop_jack() cleaned it.
# IMPORTANT: Only target our client's semaphore, NOT the
# running JACK server's files (jack_sem.*_system, jack-shm-registry,
# jack_db-0/) — deleting those between start_jack() and
# jack_client.start() kills the JACK server socket and makes
# the client connection fail silently.
try:
from pathlib import Path
import shutil
for p in Path("/dev/shm").glob("jack*"):
if p.is_dir():
shutil.rmtree(p, ignore_errors=True)
else:
p.unlink(missing_ok=True)
for p in Path("/dev/shm").glob("jack_sem.*.default_pi-multifx*"):
p.unlink(missing_ok=True)
except Exception:
pass