diff --git a/.env.example b/.env.example index 697351b..00989e1 100644 --- a/.env.example +++ b/.env.example @@ -37,10 +37,19 @@ SIP_DOMAIN=sip.plusnet.de SIP_PASS= SIP_PROXY=voice01.sip.plusnet.de -# --- Ansage (lokal; piper braucht Modelle unter ~/piper-voices) --- +# --- Ansage --- +# piper = lokal, natuerlich, Modelle unter ~/piper-voices +# say = lokal, macOS-Bordmittel (Rueckfall wenn Piper fehlt) +# mistral = Voxtral-API (Cloud) - nur fuer die einmalige Erzeugung der +# ansage.wav, die danach lokal fuer jeden Anruf wiederverwendet +# wird. Keine Live-Anrufdaten gehen dabei nach aussen. ANSAGE_TTS=piper ANSAGE_STIMME=de_DE-thorsten_emotional-medium ANSAGE_EMOTION=0 +# Nur fuer ANSAGE_TTS=mistral (Stimmen: telefon/ansage_bauen.sh --list-voices +# gibt es nicht direkt her - siehe MISTRAL_TTS_MODEL/Skill-Doku fuer die Liste) +MISTRAL_KEY= +MISTRAL_TTS_MODEL=voxtral-mini-tts-latest # --- Deck-Triage-Board (Nextcloud) --- # Hauptschalter: ohne 1 entsteht kein Board, egal was unten steht. diff --git a/telefon/ansage.txt b/telefon/ansage.txt index 5a9bb35..4795a8a 100644 --- a/telefon/ansage.txt +++ b/telefon/ansage.txt @@ -1 +1 @@ -Hallo, bitte hinterlassen Sie kurz Ihr Anliegen. Wir rufen Sie umgehend zurück. +Guten Tag, hier ist die Demo Praxis. Alle Kollegen befinden sich im Gespräch. Bitte hinterlassen Sie ihr Anliegen nach dem Signal. Wir rufen Sie zurück. diff --git a/telefon/ansage_bauen.sh b/telefon/ansage_bauen.sh index 776b061..a2c2572 100755 --- a/telefon/ansage_bauen.sh +++ b/telefon/ansage_bauen.sh @@ -2,17 +2,21 @@ # Erzeugt die Telefonansage aus telefon/ansage.txt. # Ergebnis: telefon/ansage.wav in Telefonqualitaet (8 kHz mono, PCM16). # -# Zwei Sprachsynthesen, beide lokal: -# piper (Standard, natuerlicher) - Modelle unter ~/piper-voices -# say (macOS-Bordmittel, Rueckfall wenn Piper fehlt) +# Drei Sprachsynthesen: +# piper (Standard, natuerlicher, lokal) - Modelle unter ~/piper-voices +# say (macOS-Bordmittel, lokal, Rueckfall wenn Piper fehlt) +# mistral (Voxtral-API, Cloud - nur fuer diesen einmaligen Erzeugungslauf; +# die fertige ansage.wav wird danach lokal fuer jeden Anruf +# wiederverwendet, es geht dabei keine Patientendaten raus) # # Steuerung ueber Umgebungsvariablen bzw. .env: -# ANSAGE_TTS=piper|say -# ANSAGE_STIMME= +# ANSAGE_TTS=piper|say|mistral +# ANSAGE_STIMME= # ANSAGE_EMOTION= # ANSAGE_TEMPO=<1.0 = normal, groesser = langsamer> +# MISTRAL_KEY=, MISTRAL_TTS_MODEL (optional) set -euo pipefail cd "$(dirname "$0")/.." # .env laden, aber bereits gesetzte Umgebungsvariablen behalten Vorrang - @@ -61,6 +65,34 @@ case "$TTS" in ffmpeg -v error -y -i "$TMP/sprache.aiff" -ar 8000 -ac 1 "$TMP/sprache.wav" BESCHREIBUNG="say/$SAY_STIMME" ;; + mistral) + MISTRAL_STIMME="${ANSAGE_STIMME:-en_paul_neutral}" + MISTRAL_MODELL="${MISTRAL_TTS_MODEL:-voxtral-mini-tts-latest}" + [ -n "${MISTRAL_KEY:-}" ] || { echo "MISTRAL_KEY fehlt (ANSAGE_TTS=mistral)" >&2; exit 1; } + TEXT="$(tr '\n' ' ' < telefon/ansage.txt)" + PAYLOAD="$(TTS_MODEL="$MISTRAL_MODELL" TTS_VOICE="$MISTRAL_STIMME" python3 -c ' +import json, os, sys +print(json.dumps({ + "model": os.environ["TTS_MODEL"], + "input": sys.argv[1], + "voice": os.environ["TTS_VOICE"], + "response_format": "mp3", +})) +' "$TEXT")" + RESP="$(curl -sf -X POST "https://api.mistral.ai/v1/audio/speech" \ + -H "Authorization: Bearer $MISTRAL_KEY" -H "Content-Type: application/json" \ + -d "$PAYLOAD")" || { echo "Voxtral-API-Aufruf fehlgeschlagen" >&2; exit 1; } + python3 - "$TMP/sprache.mp3" "$RESP" <<'PYEOF' +import json, base64, sys +ziel, roh = sys.argv[1], sys.argv[2] +b64 = json.loads(roh).get("audio_data") +if not b64: + print("Keine Audiodaten in der Voxtral-Antwort", file=sys.stderr); sys.exit(1) +open(ziel, "wb").write(base64.b64decode(b64)) +PYEOF + ffmpeg -v error -y -i "$TMP/sprache.mp3" -ar 8000 -ac 1 "$TMP/sprache.wav" + BESCHREIBUNG="mistral-voxtral/$MISTRAL_STIMME" + ;; *) echo "Unbekanntes TTS-Backend: $TTS" >&2; exit 1 ;; esac