Adds stereo for analog.
This commit is contained in:
@@ -395,6 +395,14 @@ else:
|
|||||||
with st.container(border=True):
|
with st.container(border=True):
|
||||||
st.subheader("Radio 1")
|
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
|
# Always-enabled checkbox for Radio 1
|
||||||
st.checkbox(
|
st.checkbox(
|
||||||
"Radio 1 always enabled",
|
"Radio 1 always enabled",
|
||||||
@@ -501,38 +509,69 @@ else:
|
|||||||
|
|
||||||
if not is_streaming:
|
if not is_streaming:
|
||||||
if analog_names:
|
if analog_names:
|
||||||
default_r1_idx = 0
|
if stereo_enabled:
|
||||||
input_device1 = st.selectbox(
|
# In stereo mode, only show ch1 and automatically select it
|
||||||
"Input Device (Radio 1)",
|
if 'ch1' in analog_names:
|
||||||
analog_names,
|
input_device1 = 'ch1'
|
||||||
index=default_r1_idx,
|
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:
|
else:
|
||||||
input_device1 = None
|
input_device1 = None
|
||||||
else:
|
else:
|
||||||
input_device1 = saved_settings.get('input_device')
|
input_device1 = saved_settings.get('input_device')
|
||||||
st.selectbox(
|
if stereo_enabled:
|
||||||
"Input Device (Radio 1)",
|
st.selectbox(
|
||||||
[input_device1 or "No device selected"],
|
"Input Device (Radio 1) - Stereo",
|
||||||
index=0,
|
[f"{input_device1 or 'ch1'} (Left+Right channels)" if input_device1 else "No device selected"],
|
||||||
disabled=True,
|
index=0,
|
||||||
help="Stop the stream to change the input device."
|
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 ---
|
# --- Radio 2 controls ---
|
||||||
with st.container(border=True):
|
with st.container(border=True):
|
||||||
st.subheader("Radio 2")
|
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
|
# Disable Radio 2 in stereo mode
|
||||||
# when the frontend is loaded.
|
if stereo_enabled:
|
||||||
radio2_enabled_default = secondary_is_streaming
|
st.info("🎧 Radio 2 is automatically disabled in stereo mode")
|
||||||
radio2_enabled = st.checkbox(
|
radio2_enabled = False
|
||||||
"Enable Radio 2",
|
else:
|
||||||
value=radio2_enabled_default,
|
# If the backend reports that the secondary radio is currently streaming,
|
||||||
help="Activate a second analog radio with its own quality and timing settings."
|
# 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
|
# Use analog-specific defaults for Radio 2
|
||||||
default_name_r2 = "Analog_Radio_2"
|
default_name_r2 = "Analog_Radio_2"
|
||||||
default_program_info_r2 = "Analog Radio Broadcast"
|
default_program_info_r2 = "Analog Radio Broadcast"
|
||||||
@@ -644,6 +683,7 @@ else:
|
|||||||
'immediate_rendering': immediate_rendering1,
|
'immediate_rendering': immediate_rendering1,
|
||||||
'presentation_delay_ms': presentation_delay_ms1,
|
'presentation_delay_ms': presentation_delay_ms1,
|
||||||
'qos_preset': qos_preset1,
|
'qos_preset': qos_preset1,
|
||||||
|
'stereo_mode': stereo_enabled, # Add stereo mode setting
|
||||||
}
|
}
|
||||||
|
|
||||||
if audio_mode == "Network - Dante":
|
if audio_mode == "Network - Dante":
|
||||||
@@ -1258,6 +1298,11 @@ if start_stream:
|
|||||||
if not cfg or not cfg.get('input_device'):
|
if not cfg or not cfg.get('input_device'):
|
||||||
return None
|
return None
|
||||||
q = QUALITY_MAP[cfg['quality']]
|
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(
|
return auracast_config.AuracastConfigGroup(
|
||||||
auracast_sampling_rate_hz=q['rate'],
|
auracast_sampling_rate_hz=q['rate'],
|
||||||
octets_per_frame=q['octets'],
|
octets_per_frame=q['octets'],
|
||||||
@@ -1274,10 +1319,11 @@ if start_stream:
|
|||||||
program_info=cfg['program_info'],
|
program_info=cfg['program_info'],
|
||||||
language=cfg['language'],
|
language=cfg['language'],
|
||||||
audio_source=f"device:{cfg['input_device']}",
|
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,
|
iso_que_len=1,
|
||||||
sampling_frequency=q['rate'],
|
sampling_frequency=q['rate'],
|
||||||
octets_per_frame=q['octets'],
|
octets_per_frame=q['octets'],
|
||||||
|
num_bis=channels, # 1=mono, 2=stereo - this determines the behavior
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -359,12 +359,25 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup,
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if sel in ('ch1', 'ch2'):
|
if sel in ('ch1', 'ch2'):
|
||||||
# Analog channels: mono at 48kHz
|
# Analog channels: check if this should be stereo based on num_bis
|
||||||
device_index = resolve_input_device_index(sel)
|
is_stereo = getattr(big, 'num_bis', 1) == 2
|
||||||
if device_index is None:
|
|
||||||
raise HTTPException(status_code=400, detail=f"Audio device '{sel}' not found.")
|
if is_stereo and sel == 'ch1':
|
||||||
big.audio_source = f'device:{device_index}'
|
# Stereo mode: use ALSA directly to capture both channels from hardware
|
||||||
big.input_format = f"int16le,{hardware_capture_rate},1"
|
# 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
|
continue
|
||||||
|
|
||||||
if sel and sel.isdigit():
|
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),
|
'qos_preset': _resolve_qos_preset_name(conf.qos_config),
|
||||||
'immediate_rendering': getattr(conf, 'immediate_rendering', False),
|
'immediate_rendering': getattr(conf, 'immediate_rendering', False),
|
||||||
'assisted_listening_stream': getattr(conf, 'assisted_listening_stream', 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),
|
'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_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],
|
'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,
|
presentation_delay_us=pres_delay if pres_delay is not None else 40000,
|
||||||
bigs=bigs,
|
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"])
|
conf.qos_config = QOS_PRESET_MAP.get(saved_qos_preset, QOS_PRESET_MAP["Fast"])
|
||||||
log.info("[AUTOSTART][PRIMARY] Scheduling demo init_radio in 2s")
|
log.info("[AUTOSTART][PRIMARY] Scheduling demo init_radio in 2s")
|
||||||
await asyncio.sleep(2)
|
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,
|
presentation_delay_us=pres_delay if pres_delay is not None else 40000,
|
||||||
bigs=bigs,
|
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"])
|
conf.qos_config = QOS_PRESET_MAP.get(saved_qos_preset, QOS_PRESET_MAP["Fast"])
|
||||||
log.info("[AUTOSTART][PRIMARY] Scheduling device init_radio in 2s")
|
log.info("[AUTOSTART][PRIMARY] Scheduling device init_radio in 2s")
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
|
|||||||
Reference in New Issue
Block a user