Worum es geht
Ein eigenständiges Plugin für Sprachsynthese, das auf Communication aufsetzt — wie SipBridge, und aus
demselben Grund: Communication liefert die Mechanik, das Vertical entscheidet, und hier entsteht der
Inhalt.
Ausdrücklich kein Teil von Communication. Ein TTS-Modell ist eine Meinung über Stimme, Sprache,
Lizenz und Modellgröße; die Plattform sollte keine davon erzwingen.
Die Trennung
| Wer |
Was |
| Communication |
Bytes im richtigen Takt in einen Anruf schieben (ICallAudioPlayback, eigenes Issue) |
| Dieses Plugin |
Aus Text Audio machen |
| SipBridge / andere Verticals |
Wissen, dass jetzt „Bitte PIN eingeben" dran ist |
Dieses Plugin sendet also nicht selbst in einen Anruf. Täte es das, müsste es die
Taktmaschinerie nachbauen, die Communication für den Konferenz-Downlink schon hat. Es liefert Bytes;
wer sie abspielt, entscheidet der Aufrufer.
Modellwahl: Piper, nicht Kokoro
Kokoro-82M-v1.0-ONNX war der erste Kandidat und fällt für uns aus: kein Deutsch. Der ONNX-Port
führt ausschließlich amerikanische und britische Stimmen; auch das Upstream-Modell kennt Deutsch
nicht (Spanisch, Französisch, Hindi, Italienisch, Portugiesisch, Japanisch, Chinesisch — kein
Deutsch).
Piper passt: MIT-Lizenz, ONNX Runtime, 35 Sprachen
einschließlich Deutsch, Modelle im zweistelligen MB-Bereich und schnell genug für Echtzeit auf CPU.
Deutsche Stimmen unter anderem de_DE-thorsten (in mehreren Qualitätsstufen), de_DE-kerstin,
de_DE-eva_k, de_DE-karlsson, de_DE-ramona, de_DE-pavoque.
Zwei Dinge vor der Festlegung prüfen: Die Stimmen haben eigene Lizenzen, unabhängig von Pipers
MIT — die Thorsten-Stimme ist CC0, andere sind CC-BY und verlangen eine Namensnennung, die dann
irgendwo stehen muss. Und die Aussprache deutscher Fachbegriffe und Eigennamen sollte man an echten
Ansagen hören, bevor man sich bindet.
Modelle gehören nicht ins Repository
Dasselbe Muster wie bei VideoConference und den MediaPipe-Assets: Die Modelldateien werden zur
Bauzeit geholt und liegen in einem gitignorierten Verzeichnis, nicht im Repo. Ein paar hundert MB
Stimmen haben in der Versionsgeschichte nichts verloren, und das Nachladen macht die Stimmenauswahl
zur Deployment-Entscheidung statt zur Compile-Zeit-Festlegung.
Vertragsskizze
public interface ITextToSpeech
{
Task<SynthesizedAudio> SynthesizeAsync(
string text,
SpeechVoice voice,
AudioFormat format,
CancellationToken cancellationToken = default);
IReadOnlyList<SpeechVoice> AvailableVoices { get; }
}
Das Zielformat gehört in den Aufruf, nicht dahinter: Ein Telefon-Leg will G.711 µ-law bei 8 kHz, ein
Browser etwas anderes. Einmal beim Synthetisieren umwandeln ist richtig; bei jedem Abspielen wäre es
Verschwendung.
Zwischenspeichern. Dieselbe Ansage immer wieder zu synthetisieren ist teuer und unnötig — die
Einwahl-Ansagen ändern sich nie. Schlüssel ist (Text, Stimme, Format). Damit wird der
Kaltstart-Aufwand einmalig und die Wiedergabe sofort.
TTS und STT sind nicht dasselbe Paket
TTS ist zustandslos: Text rein, Audio raus. STT und Realtime brauchen einen laufenden Audiostrom aus
einem Anruf und liefern fortlaufend Ergebnisse — andere Lebenszeiten, andere Fehlerfälle, andere
Rückdruck-Fragen. Sie gehören nicht in denselben Vertrag und sollten nicht miteinander ausgeliefert
werden müssen.
Für die Telefon-Einwahl wird ohnehin nur TTS gebraucht; sie läuft über DTMF.
Warum das nichts blockiert
Die Einwahl-Ansagen sind alle statisch. Ein Satz vorproduzierter G.711-Dateien reicht, sobald
Communication sie abspielen kann — SipBridge wird damit fertig, ohne dass dieses Plugin existiert.
Gebraucht wird es erst für dynamische Inhalte: den Raumnamen vorlesen, die Anzahl Wartender ansagen,
eine Fehlermeldung mit Kontext. Das ist die Verbesserung, nicht die Voraussetzung.
Offene Entscheidungen
- Ein Plugin für TTS und STT mit getrennten Verträgen, oder zwei Plugins?
- Läuft die Synthese im Host-Prozess (ONNX Runtime als Abhängigkeit des Plugins) oder als Sidecar?
Im Prozess ist einfacher; ein Sidecar hält native Abhängigkeiten aus dem Host heraus und überlebt
einen Plugin-Neustart.
- Wird die Stimme pro Workspace konfiguriert oder pro Aufruf gewählt? Vermutlich beides: eine
Voreinstellung am Workspace, überschreibbar am Aufruf.
Referenzen
- Abspiel-Mechanik: separates Communication-Issue
- Erster Konsument:
callora-sipbridge (DialInPrompt)
- Asset-Muster:
callora-videoconference, scripts/fetch-blur-assets.mjs
Worum es geht
Ein eigenständiges Plugin für Sprachsynthese, das auf Communication aufsetzt — wie SipBridge, und aus
demselben Grund: Communication liefert die Mechanik, das Vertical entscheidet, und hier entsteht der
Inhalt.
Ausdrücklich kein Teil von Communication. Ein TTS-Modell ist eine Meinung über Stimme, Sprache,
Lizenz und Modellgröße; die Plattform sollte keine davon erzwingen.
Die Trennung
ICallAudioPlayback, eigenes Issue)Dieses Plugin sendet also nicht selbst in einen Anruf. Täte es das, müsste es die
Taktmaschinerie nachbauen, die Communication für den Konferenz-Downlink schon hat. Es liefert Bytes;
wer sie abspielt, entscheidet der Aufrufer.
Modellwahl: Piper, nicht Kokoro
Kokoro-82M-v1.0-ONNX war der erste Kandidat und fällt für uns aus: kein Deutsch. Der ONNX-Port
führt ausschließlich amerikanische und britische Stimmen; auch das Upstream-Modell kennt Deutsch
nicht (Spanisch, Französisch, Hindi, Italienisch, Portugiesisch, Japanisch, Chinesisch — kein
Deutsch).
Piper passt: MIT-Lizenz, ONNX Runtime, 35 Sprachen
einschließlich Deutsch, Modelle im zweistelligen MB-Bereich und schnell genug für Echtzeit auf CPU.
Deutsche Stimmen unter anderem
de_DE-thorsten(in mehreren Qualitätsstufen),de_DE-kerstin,de_DE-eva_k,de_DE-karlsson,de_DE-ramona,de_DE-pavoque.Zwei Dinge vor der Festlegung prüfen: Die Stimmen haben eigene Lizenzen, unabhängig von Pipers
MIT — die Thorsten-Stimme ist CC0, andere sind CC-BY und verlangen eine Namensnennung, die dann
irgendwo stehen muss. Und die Aussprache deutscher Fachbegriffe und Eigennamen sollte man an echten
Ansagen hören, bevor man sich bindet.
Modelle gehören nicht ins Repository
Dasselbe Muster wie bei VideoConference und den MediaPipe-Assets: Die Modelldateien werden zur
Bauzeit geholt und liegen in einem gitignorierten Verzeichnis, nicht im Repo. Ein paar hundert MB
Stimmen haben in der Versionsgeschichte nichts verloren, und das Nachladen macht die Stimmenauswahl
zur Deployment-Entscheidung statt zur Compile-Zeit-Festlegung.
Vertragsskizze
Das Zielformat gehört in den Aufruf, nicht dahinter: Ein Telefon-Leg will G.711 µ-law bei 8 kHz, ein
Browser etwas anderes. Einmal beim Synthetisieren umwandeln ist richtig; bei jedem Abspielen wäre es
Verschwendung.
Zwischenspeichern. Dieselbe Ansage immer wieder zu synthetisieren ist teuer und unnötig — die
Einwahl-Ansagen ändern sich nie. Schlüssel ist (Text, Stimme, Format). Damit wird der
Kaltstart-Aufwand einmalig und die Wiedergabe sofort.
TTS und STT sind nicht dasselbe Paket
TTS ist zustandslos: Text rein, Audio raus. STT und Realtime brauchen einen laufenden Audiostrom aus
einem Anruf und liefern fortlaufend Ergebnisse — andere Lebenszeiten, andere Fehlerfälle, andere
Rückdruck-Fragen. Sie gehören nicht in denselben Vertrag und sollten nicht miteinander ausgeliefert
werden müssen.
Für die Telefon-Einwahl wird ohnehin nur TTS gebraucht; sie läuft über DTMF.
Warum das nichts blockiert
Die Einwahl-Ansagen sind alle statisch. Ein Satz vorproduzierter G.711-Dateien reicht, sobald
Communication sie abspielen kann — SipBridge wird damit fertig, ohne dass dieses Plugin existiert.
Gebraucht wird es erst für dynamische Inhalte: den Raumnamen vorlesen, die Anzahl Wartender ansagen,
eine Fehlermeldung mit Kontext. Das ist die Verbesserung, nicht die Voraussetzung.
Offene Entscheidungen
Im Prozess ist einfacher; ein Sidecar hält native Abhängigkeiten aus dem Host heraus und überlebt
einen Plugin-Neustart.
Voreinstellung am Workspace, überschreibbar am Aufruf.
Referenzen
callora-sipbridge(DialInPrompt)callora-videoconference,scripts/fetch-blur-assets.mjs