Adds stereo for analog.

This commit is contained in:
pober
2026-01-19 14:52:53 +01:00
committed by pstruebi
parent fd69b30c98
commit c0d8a5f13d
2 changed files with 96 additions and 30 deletions

View File

@@ -395,6 +395,14 @@ else:
with st.container(border=True):
st.subheader("Radio 1")
# Stereo mode toggle for analog
stereo_enabled = st.checkbox(
"🎧 Stereo Mode",
value=bool(saved_settings.get('analog_stereo_mode', False)),
help="Enable stereo streaming for analog inputs. When enabled, ch1 becomes left channel and ch2 becomes right channel in a single stereo stream. Radio 2 will be disabled in stereo mode.",
disabled=is_streaming
)
# Always-enabled checkbox for Radio 1
st.checkbox(
"Radio 1 always enabled",
@@ -501,38 +509,69 @@ else:
if not is_streaming:
if analog_names:
default_r1_idx = 0
input_device1 = st.selectbox(
"Input Device (Radio 1)",
analog_names,
index=default_r1_idx,
)
if stereo_enabled:
# In stereo mode, only show ch1 and automatically select it
if 'ch1' in analog_names:
input_device1 = 'ch1'
st.selectbox(
"Input Device (Radio 1) - Stereo",
['ch1 + ch2 (Stereo: Left+Right channels)'],
index=0,
help="Stereo mode: Captures both ch1 (left) and ch2 (right) as a single stereo stream"
)
st.info("🎧 Stereo mode enabled - both ch1 and ch2 will be captured as left/right channels")
else:
st.error("ch1 not available for stereo mode")
input_device1 = None
else:
# Mono mode: show all available channels
default_r1_idx = 0
input_device1 = st.selectbox(
"Input Device (Radio 1)",
analog_names,
index=default_r1_idx,
)
else:
input_device1 = None
else:
input_device1 = saved_settings.get('input_device')
st.selectbox(
"Input Device (Radio 1)",
[input_device1 or "No device selected"],
index=0,
disabled=True,
help="Stop the stream to change the input device."
)
if stereo_enabled:
st.selectbox(
"Input Device (Radio 1) - Stereo",
[f"{input_device1 or 'ch1'} (Left+Right channels)" if input_device1 else "No device selected"],
index=0,
disabled=True,
help="Stop the stream to change the input device."
)
else:
st.selectbox(
"Input Device (Radio 1)",
[input_device1 or "No device selected"],
index=0,
disabled=True,
help="Stop the stream to change the input device."
)
# --- Radio 2 controls ---
with st.container(border=True):
st.subheader("Radio 2")
# If the backend reports that the secondary radio is currently streaming,
# initialize the checkbox to checked so the UI reflects the active state
# when the frontend is loaded.
radio2_enabled_default = secondary_is_streaming
radio2_enabled = st.checkbox(
"Enable Radio 2",
value=radio2_enabled_default,
help="Activate a second analog radio with its own quality and timing settings."
)
# Disable Radio 2 in stereo mode
if stereo_enabled:
st.info("🎧 Radio 2 is automatically disabled in stereo mode")
radio2_enabled = False
else:
# If the backend reports that the secondary radio is currently streaming,
# initialize the checkbox to checked so the UI reflects the active state
# when the frontend is loaded.
radio2_enabled_default = secondary_is_streaming
radio2_enabled = st.checkbox(
"Enable Radio 2",
value=radio2_enabled_default,
help="Activate a second analog radio with its own quality and timing settings."
)
if radio2_enabled:
if radio2_enabled and not stereo_enabled:
# Use analog-specific defaults for Radio 2
default_name_r2 = "Analog_Radio_2"
default_program_info_r2 = "Analog Radio Broadcast"
@@ -644,6 +683,7 @@ else:
'immediate_rendering': immediate_rendering1,
'presentation_delay_ms': presentation_delay_ms1,
'qos_preset': qos_preset1,
'stereo_mode': stereo_enabled, # Add stereo mode setting
}
if audio_mode == "Network - Dante":
@@ -1258,6 +1298,11 @@ if start_stream:
if not cfg or not cfg.get('input_device'):
return None
q = QUALITY_MAP[cfg['quality']]
# Determine if this is stereo mode (only applicable for analog)
stereo_mode = cfg.get('stereo_mode', False)
channels = 2 if stereo_mode else 1
return auracast_config.AuracastConfigGroup(
auracast_sampling_rate_hz=q['rate'],
octets_per_frame=q['octets'],
@@ -1274,10 +1319,11 @@ if start_stream:
program_info=cfg['program_info'],
language=cfg['language'],
audio_source=f"device:{cfg['input_device']}",
input_format=f"int16le,{q['rate']},1",
input_format=f"int16le,{q['rate']},{channels}",
iso_que_len=1,
sampling_frequency=q['rate'],
octets_per_frame=q['octets'],
num_bis=channels, # 1=mono, 2=stereo - this determines the behavior
)
],
)

View File

@@ -359,12 +359,25 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup,
continue
if sel in ('ch1', 'ch2'):
# Analog channels: mono at 48kHz
device_index = resolve_input_device_index(sel)
if device_index is None:
raise HTTPException(status_code=400, detail=f"Audio device '{sel}' not found.")
big.audio_source = f'device:{device_index}'
big.input_format = f"int16le,{hardware_capture_rate},1"
# Analog channels: check if this should be stereo based on num_bis
is_stereo = getattr(big, 'num_bis', 1) == 2
if is_stereo and sel == 'ch1':
# Stereo mode: use ALSA directly to capture both channels from hardware
# ch1=left (channel 0), ch2=right (channel 1)
big.audio_source = 'alsa:hw:CARD=i2s,DEV=0'
big.input_format = f"int16le,{hardware_capture_rate},2"
log.info("Configured analog stereo input: using ALSA hw:CARD=i2s,DEV=0 with ch1=left, ch2=right")
elif is_stereo and sel == 'ch2':
# Skip ch2 in stereo mode as it's already captured as part of stereo pair
continue
else:
# Mono mode: individual channel capture
device_index = resolve_input_device_index(sel)
if device_index is None:
raise HTTPException(status_code=400, detail=f"Audio device '{sel}' not found.")
big.audio_source = f'device:{device_index}'
big.input_format = f"int16le,{hardware_capture_rate},1"
continue
if sel and sel.isdigit():
@@ -456,6 +469,7 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup,
'qos_preset': _resolve_qos_preset_name(conf.qos_config),
'immediate_rendering': getattr(conf, 'immediate_rendering', False),
'assisted_listening_stream': getattr(conf, 'assisted_listening_stream', False),
'analog_stereo_mode': getattr(conf.bigs[0], 'analog_stereo_mode', False) if conf.bigs else False,
'stream_password': (conf.bigs[0].code if conf.bigs and getattr(conf.bigs[0], 'code', None) else None),
'big_ids': [getattr(big, 'id', DEFAULT_BIG_ID) for big in conf.bigs],
'big_random_addresses': [getattr(big, 'random_address', DEFAULT_RANDOM_ADDRESS) for big in conf.bigs],
@@ -643,6 +657,9 @@ async def _autostart_from_settings():
presentation_delay_us=pres_delay if pres_delay is not None else 40000,
bigs=bigs,
)
# Set num_bis for stereo mode if needed
if conf.bigs and settings.get('analog_stereo_mode', False):
conf.bigs[0].num_bis = 2
conf.qos_config = QOS_PRESET_MAP.get(saved_qos_preset, QOS_PRESET_MAP["Fast"])
log.info("[AUTOSTART][PRIMARY] Scheduling demo init_radio in 2s")
await asyncio.sleep(2)
@@ -707,6 +724,9 @@ async def _autostart_from_settings():
presentation_delay_us=pres_delay if pres_delay is not None else 40000,
bigs=bigs,
)
# Set num_bis for stereo mode if needed
if conf.bigs and settings.get('analog_stereo_mode', False):
conf.bigs[0].num_bis = 2
conf.qos_config = QOS_PRESET_MAP.get(saved_qos_preset, QOS_PRESET_MAP["Fast"])
log.info("[AUTOSTART][PRIMARY] Scheduling device init_radio in 2s")
await asyncio.sleep(2)