From 21b25d303e87c9a7155fc9ceaa8529028344df40 Mon Sep 17 00:00:00 2001 From: IV3JDV Date: Fri, 14 Aug 2026 13:33:50 +0200 Subject: [PATCH] feat: fix CallerID dinamico su PBX e aggiunto AGC audio bidirezionale --- core.py | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/core.py b/core.py index aaf757a..414008e 100644 --- a/core.py +++ b/core.py @@ -199,7 +199,6 @@ class SessionManager: sess = self.sessioni[call_id] if sess.ptt_attivo and self.openbridge: self.openbridge.chiudi_flusso_chiamata(sess) - # Rilascia la porta del pool associata a questa chiamata self.vocoder.rilascia_porta(f"SIP_{call_id}") del self.sessioni[call_id] @@ -245,8 +244,8 @@ class SessionManager: f"Max-Forwards: 70\r\nFrom: {sess.dialog_from}\r\nTo: {sess.dialog_to}\r\n" f"Call-ID: {sess.call_id}\r\nCSeq: {sess.local_cseq} INVITE\r\n" f"Contact: \r\n" - f"Remote-Party-ID: \"{testo_display}\" ;party=called;screen=yes;privacy=off\r\n" - f"P-Asserted-Identity: \"{testo_display}\" \r\n" + f"Remote-Party-ID: \"{testo_display}\" ;party=called;screen=yes;privacy=off\r\n" + f"P-Asserted-Identity: \"{testo_display}\" \r\n" f"Content-Type: application/sdp\r\nContent-Length: {len(sdp)}\r\n\r\n{sdp}") self.sip_transport.sendto(sip_reinvite.encode('utf-8'), sess.sip_client_addr) @@ -256,7 +255,7 @@ class SessionManager: for sess in ascoltatori: if sess.ultimo_src_id != src_id: sess.ultimo_src_id = src_id - self.notifica_display(sess, f"RX: {src_id}") + self.notifica_display(sess, f"CID:{src_id}") asyncio.create_task(self.processa_audio_dmr(ambe_frames, ascoltatori)) async def processa_audio_dmr(self, ambe_27_bytes, ascoltatori): @@ -264,7 +263,6 @@ class SessionManager: tg_arrivo = ascoltatori[0].tg_destinazione try: - # 1. Decodifichiamo prima tutti e 3 i frame del superframe DMR frames_pcm = [] for i in range(0, len(ambe_27_bytes), 9): frame_9 = ambe_27_bytes[i:i+9] @@ -279,10 +277,20 @@ class SessionManager: pcm_data = await self.vocoder.converti_ambe_in_pcm(f"TG_{tg_arrivo}", frame_7) if pcm_data and len(pcm_data) >= 2: if len(pcm_data) % 2 != 0: pcm_data = pcm_data[:-1] + + # --- INIZIO AGC DMR -> SIP --- + 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 --- + pcma_data = audioop.lin2alaw(pcm_data, 2) frames_pcm.append(pcma_data) - # 2. Spediamo i pacchetti RTP cadenzandoli a ritmo di 19ms (sgranatura) for idx, pcma_data in enumerate(frames_pcm): for sess in ascoltatori: header = bytearray(12) @@ -298,7 +306,6 @@ class SessionManager: sess.rtp_seq = (sess.rtp_seq + 1) % 65536 sess.rtp_timestamp = (sess.rtp_timestamp + 160) % 4294967296 - # Attesa di ~19ms tra un frame audio e il successivo (tranne l'ultimo) if idx < len(frames_pcm) - 1: await asyncio.sleep(0.019) @@ -307,7 +314,26 @@ class SessionManager: async def processa_audio_rtp(self, sess, rtp_payload): if not sess.ptt_attivo: return try: - # *** PASSIAMO IL CALL ID PER IL POOL *** + # --- INIZIO AGC SIP -> DMR --- + # Decodifichiamo G.711a in PCM lineare temporaneamente per calcolare il volume + 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) + + # Ricodifichiamo in formato PCMA/A-Law prima di passarlo al vocoder + rtp_payload = audioop.lin2alaw(pcm_lineare, 2) + # --- FINE AGC --- + ambe_bytes = await asyncio.wait_for( self.vocoder.converti_rtp_in_ambe(f"SIP_{sess.call_id}", rtp_payload), timeout=0.1 )