Improve TX/RX AGC

This commit is contained in:
2026-08-29 12:06:25 +02:00
parent 21b25d303e
commit 8b124751d5
2 changed files with 49 additions and 28 deletions
+46 -26
View File
@@ -62,6 +62,10 @@ class SessioneSIP:
self.stream_id = os.urandom(4)
self.ultimo_src_id = None
# Variabili Inerzia per l'AGC
self.agc_rx = 1.0
self.agc_tx = 1.0
self.dtmf_buffer = ""
self.dtmf_task = None
self.ultimo_rtp_timestamp_dtmf = None
@@ -159,7 +163,7 @@ class OpenBridgeClient(asyncio.DatagramProtocol):
class SessionManager:
def __init__(self):
self.vocoder = VocoderEngine()
self.vocoder = VocoderEngine(port_start=2470, port_end=2474)
self.openbridge = None
self.rtp_transport = None
self.sip_transport = None
@@ -278,20 +282,40 @@ class SessionManager:
if pcm_data and len(pcm_data) >= 2:
if len(pcm_data) % 2 != 0: pcm_data = pcm_data[:-1]
# --- INIZIO AGC DMR -> SIP ---
# --- AGC DEFINITIVO (Zero Sleep, Soft-Limiting) ---
livello_rms = audioop.rms(pcm_data, 2)
if livello_rms > 18000:
pcm_data = audioop.mul(pcm_data, 2, 0.4)
elif livello_rms > 12000:
pcm_data = audioop.mul(pcm_data, 2, 0.7)
elif livello_rms < 300:
pcm_data = audioop.mul(pcm_data, 2, 0.0)
# --- FINE AGC ---
picco_max = audioop.max(pcm_data, 2)
if livello_rms > 50:
target_rms = 4000.0
target_mult = target_rms / livello_rms
if target_mult > 2.0: target_mult = 2.0
elif target_mult < 0.3: target_mult = 0.3
for sess in ascoltatori:
if target_mult < sess.agc_rx:
sess.agc_rx = (sess.agc_rx * 0.90) + (target_mult * 0.10)
else:
sess.agc_rx = (sess.agc_rx * 0.99) + (target_mult * 0.01)
moltiplicatore = ascoltatori[0].agc_rx
# Soft-Brickwall: previene scatti improvvisi del moltiplicatore
if picco_max > 0 and (picco_max * moltiplicatore) > 30000:
moltiplicatore_sicuro = 30000 / picco_max
# Lasciamo scivolare l'inerzia verso il basso dolcemente, non di scatto
for sess in ascoltatori:
sess.agc_rx = (sess.agc_rx * 0.70) + (moltiplicatore_sicuro * 0.30)
moltiplicatore = sess.agc_rx
pcm_data = audioop.mul(pcm_data, 2, moltiplicatore)
pcma_data = audioop.lin2alaw(pcm_data, 2)
frames_pcm.append(pcma_data)
for idx, pcma_data in enumerate(frames_pcm):
# --- INVIO RTP A RAFFICA (Gestito dal Jitter Buffer del SIP) ---
for pcma_data in frames_pcm:
for sess in ascoltatori:
header = bytearray(12)
header[0] = 0x80; header[1] = 0x08
@@ -304,33 +328,29 @@ class SessionManager:
self.rtp_transport.sendto(rtp_packet, sess.rtp_client_addr)
sess.rtp_seq = (sess.rtp_seq + 1) % 65536
# Il timestamp matematico è tutto ciò che serve al telefono per temporizzare l'audio
sess.rtp_timestamp = (sess.rtp_timestamp + 160) % 4294967296
if idx < len(frames_pcm) - 1:
await asyncio.sleep(0.019)
except Exception: pass
async def processa_audio_rtp(self, sess, rtp_payload):
if not sess.ptt_attivo: return
try:
# --- INIZIO AGC SIP -> DMR ---
# Decodifichiamo G.711a in PCM lineare temporaneamente per calcolare il volume
# --- AGC DINAMICO MORBIDO SIP -> DMR ---
pcm_lineare = audioop.alaw2lin(rtp_payload, 2)
livello_rms = audioop.rms(pcm_lineare, 2)
if livello_rms > 18000:
pcm_lineare = audioop.mul(pcm_lineare, 2, 0.4)
elif livello_rms > 12000:
pcm_lineare = audioop.mul(pcm_lineare, 2, 0.7)
elif livello_rms > 500 and livello_rms < 3000:
# Piccolo booster se il microfono SIP è basso
pcm_lineare = audioop.mul(pcm_lineare, 2, 1.5)
elif livello_rms < 150:
# Noise gate
pcm_lineare = audioop.mul(pcm_lineare, 2, 0.0)
if livello_rms > 50:
target_rms = 4000.0
target_mult = target_rms / livello_rms
if target_mult > 2.5: target_mult = 2.5
elif target_mult < 0.5: target_mult = 0.5
# Inerzia per evitare scatti
sess.agc_tx = (sess.agc_tx * 0.90) + (target_mult * 0.10)
pcm_lineare = audioop.mul(pcm_lineare, 2, sess.agc_tx)
# Ricodifichiamo in formato PCMA/A-Law prima di passarlo al vocoder
rtp_payload = audioop.lin2alaw(pcm_lineare, 2)
# --- FINE AGC ---