diff --git a/AGENTS.md b/AGENTS.md index 288707f..e652763 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,5 +7,6 @@ ## Application - this is a bluetooth Auracast transmitter application +- if you add a new parameter for a stream make sure it is saved to the settings.json so it is persisted - it consists of multicast_frontend.py and multicast_server.py mainly which connect to each other via a rest api - +- after you implemented something the user will mainly test it and you should call the update_and_run_server_and_frontend.sh script if the server and frontend were already running. diff --git a/src/auracast/auracast_config.py b/src/auracast/auracast_config.py index 41114ee..0b0daf8 100644 --- a/src/auracast/auracast_config.py +++ b/src/auracast/auracast_config.py @@ -55,6 +55,7 @@ class AuracastBigConfig(BaseModel): precode_wav: bool = False iso_que_len: int = 64 num_bis: int = 1 # 1 = mono (FRONT_LEFT), 2 = stereo (FRONT_LEFT + FRONT_RIGHT) + input_gain_db: float = 0.0 # Software gain boost in dB applied before LC3 encoding (0 = off, max 20) class AuracastBigConfigDeu(AuracastBigConfig): id: int = 12 diff --git a/src/auracast/multicast.py b/src/auracast/multicast.py index c4caefd..71fa5fe 100644 --- a/src/auracast/multicast.py +++ b/src/auracast/multicast.py @@ -805,6 +805,13 @@ class Streamer(): big['encoder'] = encoders[0] big['precoded'] = False + # Pre-compute software gain multiplier from dB config (0 dB = 1.0 = no change) + gain_db = getattr(big_config[i], 'input_gain_db', 0.0) + gain_db = max(0.0, min(20.0, float(gain_db))) + big['_gain_linear'] = 10.0 ** (gain_db / 20.0) if gain_db > 0 else 0.0 + if big['_gain_linear'] > 0.0: + logging.info("Software gain for BIG %d: +%.1f dB (linear %.3f)", i, gain_db, big['_gain_linear']) + logging.info("Streaming audio...") bigs = self.bigs self.is_streaming = True @@ -852,6 +859,14 @@ class Streamer(): stream_finished[i] = True continue + # Apply software gain boost if configured (> 0 dB) + gain_lin = big.get('_gain_linear', 0.0) + if gain_lin > 0.0: + pcm_arr = np.frombuffer(pcm_frame, dtype=np.int16).astype(np.float32) + pcm_arr *= gain_lin + np.clip(pcm_arr, -32768, 32767, out=pcm_arr) + pcm_frame = pcm_arr.astype(np.int16).tobytes() + # Measure LC3 encoding time t1 = time.perf_counter() num_bis = big.get('num_bis', 1) diff --git a/src/auracast/server/multicast_frontend.py b/src/auracast/server/multicast_frontend.py index 4bd5529..c28b59e 100644 --- a/src/auracast/server/multicast_frontend.py +++ b/src/auracast/server/multicast_frontend.py @@ -198,6 +198,7 @@ else: # Analog gain control (only for Analog mode, placed below start button) analog_gain_value = 50 # default +software_boost_db = 0 # default if audio_mode == "Analog": saved_analog_gain = saved_settings.get('analog_gain', 50) analog_gain_value = st.slider( @@ -209,6 +210,16 @@ if audio_mode == "Analog": disabled=is_streaming, help="ADC gain level for both analog inputs (10-60%). Default is 50%." ) + saved_boost = saved_settings.get('software_boost_db', 0) + software_boost_db = st.slider( + "Software Boost (dB)", + min_value=0, + max_value=20, + value=min(int(saved_boost), 20), + step=1, + disabled=is_streaming, + help="Digital gain boost applied before encoding (0-20 dB). Use this when the line-level signal is too quiet even at max ADC gain. Higher values may cause clipping on loud signals." + ) # Placeholder for validation errors (will be filled in later) validation_error_placeholder = st.empty() @@ -617,6 +628,7 @@ else: 'presentation_delay_ms': presentation_delay_ms2, 'qos_preset': qos_preset2, 'analog_gain': analog_gain_value, + 'software_boost_db': software_boost_db, } radio1_cfg = { @@ -633,6 +645,7 @@ else: 'qos_preset': qos_preset1, 'stereo_mode': stereo_enabled, 'analog_gain': analog_gain_value, + 'software_boost_db': software_boost_db, } if audio_mode == "Network - Dante": @@ -1529,6 +1542,7 @@ if start_stream: sampling_frequency=q['rate'], octets_per_frame=q['octets'], num_bis=channels, # 1=mono, 2=stereo - this determines the behavior + input_gain_db=float(cfg.get('software_boost_db', 0)), ) ], ) diff --git a/src/auracast/server/multicast_server.py b/src/auracast/server/multicast_server.py index f3bac0f..82b0915 100644 --- a/src/auracast/server/multicast_server.py +++ b/src/auracast/server/multicast_server.py @@ -493,6 +493,7 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup, '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_gain': getattr(conf, 'analog_gain', 50), + '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), '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], @@ -736,6 +737,7 @@ async def _autostart_from_settings(): iso_que_len=1, sampling_frequency=rate, octets_per_frame=octets, + input_gain_db=float(settings.get('software_boost_db', 0)), ) ] conf = auracast_config.AuracastConfigGroup( @@ -890,6 +892,7 @@ async def _autostart_from_settings(): iso_que_len=1, sampling_frequency=rate, octets_per_frame=octets, + input_gain_db=float(settings.get('software_boost_db', 0)), ) ] conf = auracast_config.AuracastConfigGroup(