From 0c3d1bc71086b4b908055373878fae5664339ecd Mon Sep 17 00:00:00 2001 From: Joaquin Madrid Belando Date: Sat, 14 Mar 2026 17:28:29 +0100 Subject: [PATCH] Add broadcast queue: prevent simultaneous announcements causing audio micro-cuts. All announcements (AMBE + TTS) now queue sequentially with 1.5s gap between them. --- bridge_master.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/bridge_master.py b/bridge_master.py index 262e53a..e6e9855 100644 --- a/bridge_master.py +++ b/bridge_master.py @@ -907,6 +907,52 @@ _tts_tasks = {} _FRAME_INTERVAL = 0.054 +_broadcast_queue = [] +_broadcast_active = False +_BROADCAST_GAP = 1.5 + +def _enqueue_broadcast(_type, _targets, _pkts_by_ts, _source_id, _dst_id, _tg, _num, _label): + global _broadcast_queue, _broadcast_active + _broadcast_queue.append({ + 'type': _type, + 'targets': _targets, + 'pkts_by_ts': _pkts_by_ts, + 'source_id': _source_id, + 'dst_id': _dst_id, + 'tg': _tg, + 'num': _num, + 'label': _label + }) + _pos = len(_broadcast_queue) + if _broadcast_active: + logger.info('(%s) Enqueued broadcast (position %s in queue)', _label, _pos) + else: + _start_next_broadcast() + +def _start_next_broadcast(): + global _broadcast_queue, _broadcast_active + if not _broadcast_queue: + _broadcast_active = False + return + _broadcast_active = True + _item = _broadcast_queue.pop(0) + _type = _item['type'] + _label = _item['label'] + logger.info('(%s) Starting broadcast from queue (%s remaining)', _label, len(_broadcast_queue)) + if _type == 'ann': + reactor.callLater(0.5, _announcementSendBroadcast, _item['targets'], _item['pkts_by_ts'], 0, _item['source_id'], _item['dst_id'], _item['tg'], 0, _item['num']) + elif _type == 'tts': + reactor.callLater(0.5, _ttsSendBroadcast, _item['targets'], _item['pkts_by_ts'], 0, _item['source_id'], _item['dst_id'], _item['tg'], 0, _item['num']) + +def _broadcast_finished(): + global _broadcast_active + if _broadcast_queue: + logger.info('(QUEUE) Broadcast finished, next in %.1fs (%s queued)', _BROADCAST_GAP, len(_broadcast_queue)) + reactor.callLater(_BROADCAST_GAP, _start_next_broadcast) + else: + _broadcast_active = False + logger.info('(QUEUE) Broadcast finished, queue empty') + _RECORDING_MAX_FRAMES = 2750 _recording_state = { 'active': False, @@ -1017,6 +1063,7 @@ def _announcementSendBroadcast(_targets, _pkts_by_ts, _pkt_idx, _source_id, _dst pass _announcement_running[_ann_num] = False logger.info('(%s) Broadcast complete: %s packets sent to %s targets', _label, _total_pkts, len(_targets)) + _broadcast_finished() return _now = time() @@ -1172,7 +1219,7 @@ def scheduledAnnouncement(_ann_num=1): logger.info('(%s) Broadcasting %s packets to %s targets (TS1:%s TS2:%s): %s', _label, len(_pkts_by_ts[1]), len(_targets), _ts1_count, _ts2_count, _sys_names) _announcement_running[_ann_num] = True - reactor.callLater(1.0, _announcementSendBroadcast, _targets, _pkts_by_ts, 0, _source_id, _dst_id, _tg, 0, _ann_num) + _enqueue_broadcast('ann', _targets, _pkts_by_ts, _source_id, _dst_id, _tg, _ann_num, _label) def _checkVoiceConfigReload(): @@ -1388,7 +1435,7 @@ def _ttsConversionDone(_ambe_path, _tts_num, _file, _tg, _lang, _mode, _label): _sys_names += ', ... +{}'.format(len(_targets) - 8) logger.info('(%s) Broadcasting %s packets to %s targets (TS1:%s TS2:%s): %s', _label, len(_pkts_by_ts[1]), len(_targets), _ts1_count, _ts2_count, _sys_names) - reactor.callLater(1.0, _ttsSendBroadcast, _targets, _pkts_by_ts, 0, _source_id, _dst_id, _tg, 0, _tts_num) + _enqueue_broadcast('tts', _targets, _pkts_by_ts, _source_id, _dst_id, _tg, _tts_num, _label) def _ttsConversionError(failure, _tts_num, _label): @@ -1412,6 +1459,7 @@ def _ttsSendBroadcast(_targets, _pkts_by_ts, _pkt_idx, _source_id, _dst_id, _tg, pass _tts_running[_tts_num] = False logger.info('(%s) Broadcast complete: %s packets sent to %s targets', _label, _total_pkts, len(_targets)) + _broadcast_finished() return _now = time()