fix: voice announcements and TTS playback; add Audio/.gitkeep

- Use DefaultVoiceProvider when Audio dir exists (was gated by ANNOUNCEMENT_LANGUAGES)
- Create Audio dir at startup when voice config present
- ANNOUNCEMENT_LANGUAGES optional (ident only); each item has own LANGUAGE
- ident: fallback to per-system ANNOUNCEMENT_LANGUAGE when empty
- disconnected_voice: use system language directly
- Clarify ENABLED, ANNOUNCEMENT_LANGUAGES in adn-voice.example.yaml
- Add Audio/.gitkeep, update .gitignore to keep folder structure
pull/4/head
Rodrigo Pérez 7 months ago
parent 5e1cff3b06
commit 40a54b4520

3
.gitignore vendored

@ -34,7 +34,8 @@ json/*
!json/.gitkeep
docs/*
!docs/PARROT.md
Audio/
Audio/*
!Audio/.gitkeep
# -----------------------------------------------------------------------------
# Python

@ -23,7 +23,9 @@ Copy `adn-server.example.yaml` to `adn-server.yaml` and edit with your settings.
Voice features (announcements, TTS, recording) use a separate config file. Copy `adn-voice.example.yaml` to `adn-voice.yaml` and edit. If the file does not exist, voice features are disabled (no error). Changes are hot-reloaded every 15 seconds.
TTS (Text-to-Speech) requires **ffmpeg** installed on the system (see Requirements above). The pipeline is: `.txt` → gTTS → `.mp3` → ffmpeg → `.wav` → vocoder → `.ambe`
- **Each item** (ANNOUNCEMENTS, TTS_ANNOUNCEMENTS) has its own `LANGUAGE` and `ENABLED: true` to activate.
- **ANNOUNCEMENT_LANGUAGES** is optional (for voice ident only); announcements/TTS work without it.
- **TTS** requires ffmpeg + vocoder (TTS_VOCODER_CMD or TTS_AMBESERVER_HOST). Pipeline: `.txt` → gTTS → `.mp3` → ffmpeg → `.wav` → vocoder → `.ambe`
## Run

@ -3,10 +3,12 @@
#
# If adn-voice.yaml does not exist, voice features are disabled (no error).
# This file is re-checked every 15 s; changes take effect without restart.
#
# Each announcement/TTS has its own LANGUAGE. ENABLED: true activates that item.
VOICE:
# Languages available for voice announcements (comma-separated, e.g. "es_ES,en_GB").
# Leave empty to disable voice features.
# Optional: languages for voice ident (MASTER systems with VOICE_IDENT).
# If empty, ident uses per-system ANNOUNCEMENT_LANGUAGE. Not needed for announcements/TTS.
ANNOUNCEMENT_LANGUAGES: ""
# Base path for AMBE audio files (relative to project root).
@ -17,22 +19,23 @@ VOICE:
# Pre-recorded .ambe files played on a talkgroup at intervals or hourly.
# FILE: filename without .ambe, looked up in Audio/<LANGUAGE>/ondemand/
# MODE: "hourly" (once per hour) or "interval" (every INTERVAL seconds)
# ENABLED: true to activate; false to keep configured but inactive
# ---------------------------------------------------------------------------
ANNOUNCEMENTS: []
# Example (all variables used):
# ANNOUNCEMENTS:
# - ENABLED: false
# - ENABLED: true
# FILE: announcement
# TG: 2
# MODE: hourly
# INTERVAL: 3600
# LANGUAGE: en_GB
# - ENABLED: false
# - ENABLED: true
# FILE: announcement2
# TG: 2
# MODE: interval
# INTERVAL: 1800
# LANGUAGE: en_GB
# INTERVAL: 60
# LANGUAGE: es_ES
# ---------------------------------------------------------------------------
# Recording
@ -50,6 +53,7 @@ VOICE:
# TTS (Text-to-Speech) — array
# Converts .txt files to AMBE via: gTTS -> ffmpeg -> vocoder/AMBEServer
# FILE: filename without .txt, looked up in Audio/<LANGUAGE>/ondemand/
# ENABLED: true to activate; requires TTS_VOCODER_CMD or TTS_AMBESERVER_HOST
# ---------------------------------------------------------------------------
# External vocoder command ({wav} and {ambe} are replaced at runtime).
@ -70,15 +74,15 @@ VOICE:
# Example:
# TTS_ANNOUNCEMENTS:
# - ENABLED: false
# - ENABLED: true
# FILE: text1
# TG: 2
# MODE: hourly
# INTERVAL: 3600
# LANGUAGE: en_GB
# - ENABLED: false
# - ENABLED: true
# FILE: text2
# TG: 2
# MODE: interval
# INTERVAL: 3600
# LANGUAGE: en_GB
# INTERVAL: 60
# LANGUAGE: es_ES

@ -65,6 +65,13 @@ class IdentUseCases:
systems_cfg = self._config.get("SYSTEMS", {})
protocols = self._get_protocols()
ann_lang = (self._config.get("VOICE", {}).get("ANNOUNCEMENT_LANGUAGES") or "").strip()
if not ann_lang:
# Fallback: derive from MASTER systems with VOICE_IDENT (each has ANNOUNCEMENT_LANGUAGE)
langs = set()
for sys_cfg in systems_cfg.values():
if sys_cfg.get("MODE") == "MASTER" and sys_cfg.get("VOICE_IDENT"):
langs.add(sys_cfg.get("ANNOUNCEMENT_LANGUAGE", "en_GB"))
ann_lang = ",".join(sorted(langs)) if langs else ""
if not ann_lang:
return
words_by_lang = self._voice.get_ambe_words(ann_lang, self._audio_path)

@ -534,12 +534,9 @@ class VoiceUseCases:
protocol = self._get_protocols().get(system)
if not protocol or not getattr(protocol, "STATUS", None):
return
ann_lang = (self._config.get("VOICE", {}).get("ANNOUNCEMENT_LANGUAGES") or "").strip()
if not ann_lang:
return
words_by_lang = self.get_ambe_words(ann_lang, self._audio_path)
sys_cfg = self._config.get("SYSTEMS", {}).get(system, {})
_lang = sys_cfg.get("ANNOUNCEMENT_LANGUAGE", "en_GB")
words_by_lang = self.get_ambe_words(_lang, self._audio_path)
if _lang not in words_by_lang:
return
words = words_by_lang[_lang]

@ -234,10 +234,12 @@ def main() -> None:
recording_handler = RecordingHandler(config, project_root)
# Voice: AMBE words + pkt_gen (legacy readAMBE + mk_voice). Use Default when languages + audio path set.
# Voice: AMBE words + pkt_gen (legacy readAMBE + mk_voice). Use Default when Audio dir exists.
# ANNOUNCEMENT_LANGUAGES is only for voice ident; announcements/TTS use per-item LANGUAGE.
audio_path = os.path.join(project_root, config.get("VOICE", {}).get("AUDIO_PATH", "Audio"))
ann_lang = (config.get("VOICE", {}).get("ANNOUNCEMENT_LANGUAGES") or "").strip()
if ann_lang and os.path.isdir(audio_path):
if config.get("VOICE") and not os.path.isdir(audio_path):
os.makedirs(audio_path, exist_ok=True)
if os.path.isdir(audio_path):
voice_provider = DefaultVoiceProvider()
else:
voice_provider = StubVoiceProvider()

Loading…
Cancel
Save

Powered by TurnKey Linux.