Fixes bug in setting ADC gain; Gain slider in db; Makes gain slider work while streaming.

This commit is contained in:
Pbopbo
2026-04-08 11:41:23 +02:00
parent 291d75b137
commit c3a36dc252
3 changed files with 73 additions and 32 deletions

View File

@@ -112,4 +112,5 @@ class AuracastConfigGroup(AuracastGlobalConfig):
bigs: List[AuracastBigConfig] = [ bigs: List[AuracastBigConfig] = [
AuracastBigConfigDeu(), AuracastBigConfigDeu(),
] ]
analog_gain: int = 50 # ADC gain level for analog mode (10-60%) analog_gain_db_left: float = 0.0 # ADC gain level for analog mode left channel (-12 to 18 dB)
analog_gain_db_right: float = 0.0 # ADC gain level for analog mode right channel (-12 to 18 dB)

View File

@@ -198,25 +198,34 @@ else:
start_stream, stop_stream = render_stream_controls(is_streaming, "Start Auracast", "Stop Auracast", running_mode, secondary_is_streaming) start_stream, stop_stream = render_stream_controls(is_streaming, "Start Auracast", "Stop Auracast", running_mode, secondary_is_streaming)
# Analog gain control (only for Analog mode, placed below start button) # Analog gain control (only for Analog mode, placed below start button)
analog_gain_value = 50 # default (ALSA 10-60 range) analog_gain_db = 0 # default (dB)
software_boost_db = 0 # default software_boost_db = 0 # default
if audio_mode == "Analog": if audio_mode == "Analog":
saved_analog_gain = saved_settings.get('analog_gain', 50) if '_analog_gain_db' not in st.session_state:
# Convert persisted ALSA value (10-60) to display value (0-100) saved_analog_gain_db = saved_settings.get('analog_gain_db_left', 0)
saved_display = int(round((saved_analog_gain - 10) * 100 / 50)) st.session_state['_analog_gain_db'] = max(-12, min(18, int(saved_analog_gain_db)))
saved_display = max(0, min(100, saved_display)) analog_gain_db = st.slider(
analog_gain_display = st.slider(
"Analog Input Gain", "Analog Input Gain",
min_value=0, min_value=-12,
max_value=100, max_value=18,
value=saved_display, key='_analog_gain_db',
step=5, step=1,
disabled=is_streaming, format="%d dB",
format="%d%%", help="ADC gain level for analog inputs (-12 to 18 dB). Default is 0 dB."
help="ADC gain level for both analog inputs. Default is 80%."
) )
# Map display value (0-100) back to ALSA range (10-60) # Apply gain live while streaming whenever the slider value changes
analog_gain_value = int(round(10 + analog_gain_display * 50 / 100)) if is_streaming:
prev_gain = st.session_state.get('_prev_analog_gain_db')
if prev_gain != analog_gain_db:
try:
requests.post(
f"{BACKEND_URL}/adc_gain",
json={"gain_db_left": analog_gain_db, "gain_db_right": analog_gain_db},
timeout=1,
)
except Exception:
pass
st.session_state['_prev_analog_gain_db'] = analog_gain_db
saved_boost = saved_settings.get('software_boost_db', 0) saved_boost = saved_settings.get('software_boost_db', 0)
software_boost_db = st.slider( software_boost_db = st.slider(
"Boost", "Boost",
@@ -684,7 +693,8 @@ else:
'immediate_rendering': immediate_rendering2, 'immediate_rendering': immediate_rendering2,
'presentation_delay_ms': presentation_delay_ms2, 'presentation_delay_ms': presentation_delay_ms2,
'qos_preset': qos_preset2, 'qos_preset': qos_preset2,
'analog_gain': analog_gain_value, 'analog_gain_db_left': analog_gain_db,
'analog_gain_db_right': analog_gain_db,
'software_boost_db': software_boost_db, 'software_boost_db': software_boost_db,
} }
@@ -701,7 +711,8 @@ else:
'presentation_delay_ms': presentation_delay_ms1, 'presentation_delay_ms': presentation_delay_ms1,
'qos_preset': qos_preset1, 'qos_preset': qos_preset1,
'stereo_mode': stereo_enabled, 'stereo_mode': stereo_enabled,
'analog_gain': analog_gain_value, 'analog_gain_db_left': analog_gain_db,
'analog_gain_db_right': analog_gain_db,
'software_boost_db': software_boost_db, 'software_boost_db': software_boost_db,
} }
@@ -1585,7 +1596,8 @@ if start_stream:
immediate_rendering=bool(cfg['immediate_rendering']), immediate_rendering=bool(cfg['immediate_rendering']),
presentation_delay_us=int(cfg['presentation_delay_ms'] * 1000), presentation_delay_us=int(cfg['presentation_delay_ms'] * 1000),
qos_config=QOS_PRESET_MAP[cfg['qos_preset']], qos_config=QOS_PRESET_MAP[cfg['qos_preset']],
analog_gain=cfg.get('analog_gain', 50), analog_gain_db_left=cfg.get('analog_gain_db_left', 0.0),
analog_gain_db_right=cfg.get('analog_gain_db_right', 0.0),
bigs=[ bigs=[
auracast_config.AuracastBigConfig( auracast_config.AuracastBigConfig(
id=cfg.get('id', 123456), id=cfg.get('id', 123456),

View File

@@ -242,16 +242,18 @@ async def _init_i2c_on_startup() -> None:
log.warning("Exception running i2cget (%s): %s", " ".join(read_cmd), e, exc_info=True) log.warning("Exception running i2cget (%s): %s", " ".join(read_cmd), e, exc_info=True)
async def _set_adc_level(gain_percent: int = 50) -> None: async def _set_adc_level(gain_db_left: float = 0.0, gain_db_right: float = 0.0) -> None:
"""Set ADC mixer level. """Set ADC mixer gain in dB for left and right channels independently.
Runs: amixer -c 1 set 'ADC' x% Runs: amixer -c 2 sset ADC {gain_db_left}dB,{gain_db_right}dB
Args: Args:
gain_percent: Gain level 10-60, default 50 (above 60% causes clipping/silence) gain_db_left: Left channel gain in dB (-12 to 18), default 0
gain_db_right: Right channel gain in dB (-12 to 18), default 0
""" """
gain_percent = max(10, min(60, gain_percent)) gain_db_left = max(-12.0, min(18.0, gain_db_left))
cmd = ["amixer", "-c", "1", "set", "ADC", f"{gain_percent}%"] gain_db_right = max(-12.0, min(18.0, gain_db_right))
cmd = ["amixer", "-c", "2", "sset", "ADC", "--", f"{int(gain_db_left)}dB,{int(gain_db_right)}dB"]
try: try:
proc = await asyncio.create_subprocess_exec( proc = await asyncio.create_subprocess_exec(
*cmd, *cmd,
@@ -347,8 +349,9 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup,
if input_device_name in ('ch1', 'ch2'): if input_device_name in ('ch1', 'ch2'):
audio_mode_persist = 'Analog' audio_mode_persist = 'Analog'
# Set ADC gain level for analog mode # Set ADC gain level for analog mode
analog_gain = getattr(conf, 'analog_gain', 50) analog_gain_db_left = getattr(conf, 'analog_gain_db_left', 0.0)
await _set_adc_level(analog_gain) analog_gain_db_right = getattr(conf, 'analog_gain_db_right', 0.0)
await _set_adc_level(analog_gain_db_left, analog_gain_db_right)
elif input_device_name in dante_channels: elif input_device_name in dante_channels:
audio_mode_persist = 'Network - Dante' audio_mode_persist = 'Network - Dante'
else: else:
@@ -493,7 +496,8 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup,
'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, 'analog_stereo_mode': getattr(conf.bigs[0], 'analog_stereo_mode', False) if conf.bigs else False,
'analog_gain': getattr(conf, 'analog_gain', 50), 'analog_gain_db_left': getattr(conf, 'analog_gain_db_left', 0.0),
'analog_gain_db_right': getattr(conf, 'analog_gain_db_right', 0.0),
'software_boost_db': getattr(conf.bigs[0], 'input_gain_db', 0.0) if conf.bigs else 0.0, 'software_boost_db': getattr(conf.bigs[0], 'input_gain_db', 0.0) if conf.bigs else 0.0,
'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],
@@ -557,6 +561,28 @@ async def stop_audio():
log.error("Exception in /stop_audio: %s", traceback.format_exc()) log.error("Exception in /stop_audio: %s", traceback.format_exc())
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.post("/adc_gain")
async def set_adc_gain(payload: dict):
"""Set ADC gain in dB for left and right channels without restarting the stream.
Body: {"gain_db_left": float, "gain_db_right": float}
"""
try:
gain_db_left = float(payload.get("gain_db_left", 0.0))
gain_db_right = float(payload.get("gain_db_right", 0.0))
await _set_adc_level(gain_db_left, gain_db_right)
# Persist the new values so they survive a restart
for load_fn, save_fn in [(load_stream_settings, save_stream_settings), (load_stream_settings2, save_stream_settings2)]:
s = load_fn() or {}
if s:
s['analog_gain_db_left'] = gain_db_left
s['analog_gain_db_right'] = gain_db_right
save_fn(s)
return {"status": "ok", "gain_db_left": gain_db_left, "gain_db_right": gain_db_right}
except Exception as e:
log.error("Exception in /adc_gain: %s", traceback.format_exc())
raise HTTPException(status_code=500, detail=str(e))
@app.post("/stream_lc3") @app.post("/stream_lc3")
async def send_audio(audio_data: dict[str, str]): async def send_audio(audio_data: dict[str, str]):
"""Sends a block of pre-coded LC3 audio via the worker.""" """Sends a block of pre-coded LC3 audio via the worker."""
@@ -762,7 +788,8 @@ async def _autostart_from_settings():
immediate_rendering=immediate_rendering, immediate_rendering=immediate_rendering,
assisted_listening_stream=assisted_listening_stream, assisted_listening_stream=assisted_listening_stream,
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,
analog_gain=settings.get('analog_gain', 50), analog_gain_db_left=settings.get('analog_gain_db_left', 0.0),
analog_gain_db_right=settings.get('analog_gain_db_right', 0.0),
bigs=bigs, bigs=bigs,
) )
# Set num_bis for stereo mode if needed # Set num_bis for stereo mode if needed
@@ -917,7 +944,8 @@ async def _autostart_from_settings():
immediate_rendering=immediate_rendering, immediate_rendering=immediate_rendering,
assisted_listening_stream=assisted_listening_stream, assisted_listening_stream=assisted_listening_stream,
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,
analog_gain=settings.get('analog_gain', 50), analog_gain_db_left=settings.get('analog_gain_db_left', 0.0),
analog_gain_db_right=settings.get('analog_gain_db_right', 0.0),
bigs=bigs, bigs=bigs,
) )
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"])
@@ -954,8 +982,8 @@ async def _startup_autostart_event():
# Hydrate settings cache once to avoid disk I/O during /status # Hydrate settings cache once to avoid disk I/O during /status
_init_settings_cache_from_disk() _init_settings_cache_from_disk()
await _init_i2c_on_startup() await _init_i2c_on_startup()
# Ensure ADC mixer level is set at startup (default 50%) # Ensure ADC mixer level is set at startup (default 0 dB)
await _set_adc_level(50) await _set_adc_level(0.0, 0.0)
refresh_pw_cache() refresh_pw_cache()
log.info("[STARTUP] Scheduling autostart task") log.info("[STARTUP] Scheduling autostart task")
asyncio.create_task(_autostart_from_settings()) asyncio.create_task(_autostart_from_settings())