Allow multichannel audio sources as direct CSS and value source input

Add resolve_audio_source() that accepts both MultichannelAudioSource
(defaults to mono mix) and MonoAudioSource. Update CSS and brightness
value source dropdowns to show all audio sources with type badges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 20:41:42 +03:00
parent a5d855f469
commit f96cd5f367
6 changed files with 63 additions and 28 deletions

View File

@@ -156,6 +156,8 @@ class AudioValueStream(ValueStream):
mode: str = "rms",
sensitivity: float = 1.0,
smoothing: float = 0.3,
min_value: float = 0.0,
max_value: float = 1.0,
audio_capture_manager: Optional["AudioCaptureManager"] = None,
audio_source_store: Optional["AudioSourceStore"] = None,
):
@@ -163,6 +165,8 @@ class AudioValueStream(ValueStream):
self._mode = mode
self._sensitivity = sensitivity
self._smoothing = smoothing
self._min = min_value
self._max = max_value
self._audio_capture_manager = audio_capture_manager
self._audio_source_store = audio_source_store
@@ -178,11 +182,11 @@ class AudioValueStream(ValueStream):
self._resolve_audio_source()
def _resolve_audio_source(self) -> None:
"""Resolve mono audio source to device index / channel."""
"""Resolve audio source (mono or multichannel) to device index / channel."""
if self._audio_source_id and self._audio_source_store:
try:
device_index, is_loopback, channel = (
self._audio_source_store.resolve_mono_source(self._audio_source_id)
self._audio_source_store.resolve_audio_source(self._audio_source_id)
)
self._audio_device_index = device_index
self._audio_loopback = is_loopback
@@ -210,7 +214,7 @@ class AudioValueStream(ValueStream):
def get_value(self) -> float:
if self._audio_stream is None:
return 0.0
return self._min
analysis = self._audio_stream.get_latest_analysis()
if analysis is None:
@@ -222,7 +226,10 @@ class AudioValueStream(ValueStream):
# Temporal smoothing
smoothed = self._smoothing * self._prev_value + (1.0 - self._smoothing) * raw
self._prev_value = smoothed
return max(0.0, min(1.0, smoothed))
# Map to [min, max]
mapped = self._min + smoothed * (self._max - self._min)
return max(0.0, min(1.0, mapped))
def _extract_raw(self, analysis) -> float:
"""Extract raw scalar from audio analysis based on mode."""
@@ -265,6 +272,8 @@ class AudioValueStream(ValueStream):
self._mode = source.mode
self._sensitivity = source.sensitivity
self._smoothing = source.smoothing
self._min = source.min_value
self._max = source.max_value
# If audio source changed, re-resolve and swap capture stream
if source.audio_source_id != old_source_id:
@@ -598,6 +607,8 @@ class ValueStreamManager:
mode=source.mode,
sensitivity=source.sensitivity,
smoothing=source.smoothing,
min_value=source.min_value,
max_value=source.max_value,
audio_capture_manager=self._audio_capture_manager,
audio_source_store=self._audio_source_store,
)