Add audio channel selection (mono/left/right), show device LED count in target editor

Audio capture now produces per-channel FFT spectrum and RMS alongside
the existing mono mix. Each audio color strip source can select which
channel to visualize via a new "Channel" dropdown. This enables stereo
setups with separate left/right segments on the same LED strip.

Also shows the device LED count under the device selector in the target
editor for quick reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 15:05:15 +03:00
parent 9d593379b8
commit f15ff8fea0
13 changed files with 129 additions and 31 deletions

View File

@@ -68,6 +68,7 @@ class AudioColorStripStream(ColorStripStream):
self._auto_size = not source.led_count
self._led_count = source.led_count if source.led_count and source.led_count > 0 else 1
self._mirror = bool(getattr(source, "mirror", False))
self._audio_channel = getattr(source, "audio_channel", "mono") # mono | left | right
with self._colors_lock:
self._colors: Optional[np.ndarray] = None
@@ -193,6 +194,16 @@ class AudioColorStripStream(ColorStripStream):
elapsed = time.perf_counter() - loop_start
time.sleep(max(frame_time - elapsed, 0.001))
# ── Channel selection ─────────────────────────────────────────
def _pick_channel(self, analysis):
"""Return (spectrum, rms) for the configured audio channel."""
if self._audio_channel == "left":
return analysis.left_spectrum, analysis.left_rms
elif self._audio_channel == "right":
return analysis.right_spectrum, analysis.right_rms
return analysis.spectrum, analysis.rms
# ── Spectrum Analyzer ──────────────────────────────────────────
def _render_spectrum(self, buf: np.ndarray, n: int, analysis) -> None:
@@ -200,7 +211,7 @@ class AudioColorStripStream(ColorStripStream):
buf[:] = 0
return
spectrum = analysis.spectrum
spectrum, _ = self._pick_channel(analysis)
sensitivity = self._sensitivity
smoothing = self._smoothing
lut = self._palette_lut
@@ -249,7 +260,8 @@ class AudioColorStripStream(ColorStripStream):
buf[:] = 0
return
rms = analysis.rms * self._sensitivity
_, ch_rms = self._pick_channel(analysis)
rms = ch_rms * self._sensitivity
# Temporal smoothing on RMS
rms = self._smoothing * self._prev_rms + (1.0 - self._smoothing) * rms
self._prev_rms = rms