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
CI / test (push) Has been cancelled
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user