Zum Hauptinhalt springen

Qwen3-TTS-12Hz-1.7B-CustomVoice

Im Text-to-Speech-Guide findest du lauffähige Beispiele für die neun Stimmen, die Ausgabeformate, das Sprechtempo und das Aufteilen langer Texte.

Beschreibung

„Qwen3-TTS-12Hz-1.7B-CustomVoice" ist ein Text-to-Speech-Modell von Alibaba mit 1,7 Milliarden Parametern. Du schickst Text hin und bekommst eine Audiodatei zurück. Es ist das Gegenstück zu Whisper-Large-V3-Turbo, das den umgekehrten Weg geht und aus Audio Text macht.

Das Modell bedient die Route /v1/audio/speech und bringt neun eingebaute Stimmen für dreizehn Sprachen und Dialekte mit. Die Sprachausgabe entsteht schneller als in Echtzeit: Für eine Sekunde Audio braucht das Modell auf unserer Hardware etwa eine fünftel Sekunde, und mit Streaming kommt das erste Audio in weniger als 150 ms.

Das Modell eignet sich für:

  • Sprachausgabe in dreizehn Sprachen und Dialekten
  • Neun verschiedene eingebaute Stimmen, pro Anfrage wählbar
  • Fünf Ausgabeformate, darunter komprimierte für die direkte Auslieferung an den Browser
  • Ein Sprechtempo zwischen 0,25 und 4,0
  • Streaming, entweder als rohe Audio-Chunks oder als speech.audio.delta-Events im OpenAI-Format, sodass das Audio beginnt, bevor die Generierung abgeschlossen ist

Diese Einschränkungen gelten:

  • Voice-Cloning mit eigenem Referenz-Audio ist auf dieser Route nicht verfügbar
  • response_format="aac" wird nicht unterstützt, obwohl die OpenAI-API es vorsieht
  • Der Parameter language wird geprüft, ein gültiger Wert ändert in der Praxis aber wenig, siehe Gültige Werte für den Parameter language
  • voice ist Pflicht. Eine Anfrage ohne diesen Parameter antwortet mit HTTP 400
  • Ungültige Parameterwerte kommen als HTTP 400 zurück. Manche Antworten nennen den Parameter, andere bleiben allgemein, siehe Fehlerantworten
  • Streaming braucht response_format="wav" oder "pcm". Jedes andere Format zusammen mit stream antwortet mit HTTP 400, siehe Streaming
  • Das Modell hat keine Chat-Route. /v1/chat/completions steht dafür nicht zur Verfügung
  • Es gibt keine Auszeichnungssprache. [angry], <laugh> und SSML-Tags werden vorgelesen oder verschluckt, siehe Sprechweise über instructions steuern
  • Deutscher Text braucht eine Vorbereitung, bevor du ihn schickst, und kurze deutsche Zeilen sind unzuverlässig, siehe Text für Deutsch aufbereiten
  • seed wird angenommen, liefert aber kein reproduzierbares Ergebnis, siehe Reproduzierbarkeit

Verfügbare Stimmen

Alle neun funktionieren auf dieser Route. Es gibt keine getrennte Liste für „Custom Voices", die du erst freischalten müsstest.

aiden, dylan, eric, ono_anna, ryan, serena, sohee, uncle_fu, vivian

Jede Stimme spricht jede unterstützte Sprache, und alle neun kamen in unseren Tests auf Deutsch und Englisch verständlich zurück. Wähl also nach dem Klang, nicht nach der Muttersprache der Stimme. Gemessene mittlere Tonhöhe auf englischem Text:

StimmeMutterspracheTonhöhe
aidenEnglisch147 Hz
dylanChinesisch (Peking)164 Hz
ryanEnglisch171 Hz
ericChinesisch (Sichuan)177 Hz
uncle_fuChinesisch218 Hz
soheeKoreanisch220 Hz
serenaChinesisch246 Hz
vivianChinesisch250 Hz
ono_annaJapanisch264 Hz

Zwei Dinge solltest du wissen, bevor du eine Stimme für Deutsch aussuchst. Die Stimmen mit einer anderen Muttersprache haben einen hörbaren Akzent, das ist Geschmackssache und kein Fehler. Und instructions wirkt praktisch nur auf englischem Text mit einer englischsprachigen Stimme, siehe Sprechweise über instructions steuern. Auf Deutsch ist die Stimme selbst also die einzige Stellschraube für den Charakter der Ausgabe.

voice ist hier Pflicht, obwohl die OpenAI-API den Parameter als optional vorsieht: Eine Anfrage ohne voice antwortet mit HTTP 400. Ein unbekannter Name führt ebenfalls zu HTTP 400, mit einem allgemeinen Got bad request., das die gültigen Werte nicht nennt. Prüf die Schreibweise also gegen die Liste der verfügbaren Stimmen und nicht gegen die Fehlermeldung.

Gültige Werte für den Parameter language

Auto, Beijing_Dialect, Chinese, English, French, German, Italian, Japanese, Korean, Portuguese, Russian, Sichuan_Dialect, Spanish

Das sind die dreizehn Sprachen und Dialekte, die das Modell spricht, und die einzigen Werte, die language annimmt. Alles andere, auch ein ISO-Code wie de, antwortet mit HTTP 400. Einen gültigen Wert mitzuschicken ist erlaubt, lohnt aber selten: In unseren Durchläufen kam deutscher Text mit und ohne Parameter gleich genau zurück. Lass ihn also weg, das ist der einfachere Standard.

Ausgabeformate

response_formatContent-TypeErste Bytes der AntwortGröße pro Sekunde Audio
wavaudio/wavRIFFWAVE48,0 kB
pcmaudio/pcmSamples ohne Header48,0 kB
flacaudio/flacfLaCetwa 23 kB
mp3audio/mpegmp3-Frame-Sync FF F37,0 kB
opusaudio/oggOggS4,6 kB

wav ist der Standard und die richtige Wahl, wenn du das Audio weiterverarbeitest. Für die Auslieferung an Browser oder Telefon ist opus bei Sprachqualität rund ein Neuntel so groß.

Die Größen sind absichtlich auf eine Sekunde Audio normiert. Die Generierung ist nicht deterministisch, zwei Durchläufe über denselben Text unterscheiden sich in der Länge um eine Sekunde oder mehr, und ein Vergleich der reinen Dateigrößen vergleicht dann die Längen und nicht die Formate. Normiert sind pcm und wav dasselbe: je 48,0 kB pro Sekunde, wobei wav einen Header von 44 Byte voranstellt.

Der Content-Type nennt das Format, das du angefordert hast. Ein Player, der den Header liest, dekodiert also den richtigen Container. Die Bytes in eine Datei mit der passenden Endung zu schreiben, funktioniert weiterhin und braucht keinen Header.

aac antwortet mit HTTP 400 und nennt die erlaubten Werte:

Input should be 'wav', 'pcm', 'flac', 'mp3' or 'opus'

Das Audio ist in jedem Format 24 kHz, 16 Bit, mono. Die Lautheit ist nicht normalisiert: Über unseren Testsatz lag der Spitzenpegel zwischen 0,23 und 0,89 der Vollaussteuerung, das sind rund 11 dB zwischen dem leisesten und dem lautesten Clip. Wenn du mehrere Clips hintereinander abspielst, schick sie vorher durch eine Lautheitsnormalisierung.

Erste Schritte

from openai import OpenAI

client = OpenAI(
base_url="https://llm.aihosting.mittwald.de/v1",
api_key="sk-your-api-key-here",
)

response = client.audio.speech.create(
model="Qwen3-TTS-12Hz-1.7B-CustomVoice",
voice="ryan",
input="Hallo und herzlich willkommen bei mittwald.",
response_format="mp3",
)

response.write_to_file("willkommen.mp3")

Empfohlene Parameter

ParameterWertWirkung
voiceeiner der neun NamenPflicht. Eine Anfrage ohne diesen Parameter schlägt fehl
languageweglassenWird geprüft, wirkt aber kaum, siehe Gültige Werte
response_formatwav oder opuswav zum Weiterverarbeiten, opus zum Ausliefern
speed1.0Zwischen 0,25 und 4,0, siehe Sprechtempo
instructionsfreier Text, höchstens 500 ZeichenOptional. Beschreibt, wie gesprochen werden soll, siehe Sprechweise steuern
seedeine ganze ZahlWird angenommen, reproduziert aber nicht, siehe Reproduzierbarkeit

Sprechtempo

speed skaliert die Dauer annähernd umgekehrt proportional. Derselbe deutsche Satz mit 99 Zeichen, fünf Durchläufe je Einstellung, über die API gemessen:

speedMittlere Dauer
0.512,0 s
1.06,1 s
2.03,2 s

Die Länge schwankt auch bei identischer Eingabe, weil die Generierung nicht deterministisch ist: Die fünf Durchläufe bei 1.0 lagen zwischen 5,2 s und 6,6 s. Nimm die Zahlen als Verlauf, nicht als exakte Werte.

speed wirkt auch auf gestreamtes Audio. Beim Streaming kamen bei 0.5 12,8 s und 16,5 s heraus, bei 2.0 2,9 s. Du musst die Abspielgeschwindigkeit auf deiner Seite also nicht nachkorrigieren.

Sprechweise über instructions steuern

Diese Route kennt keine Auszeichnungssprache. Tags in eckigen oder spitzen Klammern und SSML werden nicht ausgewertet. Sie werden entweder vorgelesen oder verschluckt, und was davon passiert, ist nicht vorhersagbar:

Du schickstDas Modell sagt, je zwei Durchläufe
[wütend] Die Lieferung kommt am Dienstagmorgen an.Für ihn, die Lieferung kommt am Dienstagmorgen an." / „Hey, wütend! Die Lieferung kommt am Dienstagmorgen an."
[angry] The delivery arrives on Tuesday morning.And the delivery arrives on Tuesday morning." / nur der Satz
<laugh> The delivery arrives on Tuesday morning.der Satz, dazu 1 bis 5 Sekunden Audio, das aus dem Tag entstanden ist; zwei frühere Durchläufe lieferten stattdessen Dauergeräusch
(excited) The delivery arrives on Tuesday morning!A delivery arrives on Tuesday morning." / nur der Satz, beide Male ohne Emotion
The delivery arrives … <break time="1s"/> Thank you.„The delivery arrives to thank you when number first to get, huh, thank you." / „The delivery arrives. Thank you. Thank you."
<speak><prosody rate="slow">…</prosody></speak>nur der Satz / „Persody Ray Slow, the delivery arrives on Tuesday morning."

Nimm die Auszeichnungen aus deinem Text heraus und schreib stattdessen in den Parameter instructions, wie die Zeile klingen soll. Ein normaler Satz reicht, höchstens 500 Zeichen. Längere Werte führen zu HTTP 400.

<laugh> ist der deutlichste Grund, Auszeichnungen herauszunehmen. Vier Durchläufe mit dem Tag vor demselben Satz ergaben 4,2 s bis 8,7 s Audio, wo derselbe Satz ohne Tag 3,1 s bis 3,8 s brauchte. Aus dem Tag wird also zusätzlicher Klang und nicht das Lachen, das du wolltest, und an einem früheren Tag kam bei zwei Durchläufen Dauergeräusch ohne den Satz heraus.

Beispiel: Vertonung einer Kundenseite, Abschnitt für Abschnitt

Eine Webagentur baut eine Landingpage für einen Kunden und will jeden Abschnitt vertont haben: Eine ruhige Produkteinleitung, einen freundlicheren Absatz zu den Funktionen und einen Call-to-Action mit etwas Druck. Eine Stimme über alle Abschnitte hält es als denselben Sprecher erkennbar, instructions ändert die Sprechweise, und opus hält die Dateien klein genug, um sie mit der Seite auszuliefern.

instructions ist in den OpenAI-SDKs ein regulärer Parameter und braucht deshalb kein extra_body. Lass language weg: Englischen Text liest das Modell von selbst als Englisch.

from openai import OpenAI

client = OpenAI(
base_url="https://llm.aihosting.mittwald.de/v1",
api_key="sk-your-api-key-here",
)

sections = [
(
"01-intro",
"Your hosting, fully managed. No servers to patch and no pager at three in the morning.",
"Speak calmly and clearly, like a product narrator.",
),
(
"02-feature",
"Every deploy ships in seconds, and any change is one click away from a rollback.",
"Speak in a cheerful, upbeat tone.",
),
(
"03-cta",
"Start your free trial today and put your first site online in ten minutes.",
"Speak fast and excitedly, like a short radio advert.",
),
]

for name, text, instructions in sections:
response = client.audio.speech.create(
model="Qwen3-TTS-12Hz-1.7B-CustomVoice",
voice="ryan",
input=text,
instructions=instructions,
response_format="opus",
)
response.write_to_file(f"{name}.opus")

Das ergab drei Dateien mit 28 kB, 23 kB und 21 kB, und die Sprechweise folgt den Anweisungen. Jede Zeile ist der Mittelwert aus zwei Durchläufen:

AbschnittAnweisungDauerSprechtempo
01-introruhiger Produktsprecher6,20 s13,9 Zeichen/s
02-featurefröhlich, gut gelaunt5,04 s15,9 Zeichen/s
03-ctaschnell und begeistert4,36 s17,0 Zeichen/s

Für die deutsche Fassung derselben Seite lass instructions weg und variier stattdessen voice und speed:

sections_de = [
("de-01-intro", "Dein Hosting, vollständig verwaltet. Keine Server zum Patchen und kein Pager um drei Uhr nachts.", "serena", 0.95),
("de-02-feature", "Jedes Deployment ist in Sekunden ausgerollt, und jede Änderung lässt sich mit einem Klick zurücknehmen.", "serena", 1.0),
("de-03-cta", "Starte heute deine kostenlose Testphase und bring deine erste Website in zehn Minuten online.", "vivian", 1.15),
]

for name, text, voice, speed in sections_de:
response = client.audio.speech.create(
model="Qwen3-TTS-12Hz-1.7B-CustomVoice",
voice=voice,
input=text,
speed=speed,
response_format="opus",
)
response.write_to_file(f"{name}.opus")

Gemessen kamen dabei 12,6, 14,8 und 15,7 Zeichen pro Sekunde heraus, also derselbe Aufbau im Tempo, ohne instructions.

Text für Deutsch aufbereiten

Deutsche Texte brauchen einen Durchgang, bevor du sie schickst. Gut geschriebene deutsche Prosa liest das Modell korrekt, aber drei Dinge aus ganz normalen Quelltexten kommen falsch heraus, und keines davon scheitert hörbar am Statuscode.

Zweitens: Schreib Abkürzungen und Zahlen aus. Die kurzen Formen, die ein CMS oder ein Sprachmodell liefert, werden falsch gelesen:

Du schickstDas Modell sagtSchreib stattdessen
Bestellung 45312„Bestellung 4FOR 5002"fünfundvierzigtausenddreihundertzwölf
1.299,99 Euro„1.29,999", „Euro" fällt wegeintausendzweihundertneunundneunzig Euro und neunundneunzig Cent
u. a. … z. B. … ggf. inkl.„O.A. … setz bei … die geben English"unter anderem … zum Beispiel … gegebenenfalls inklusive
GmbH & Co. KG„GmbH & Co, Kagi"GmbH und Co. KG

Datumsangaben wie 20.05.2026, Prozentwerte wie 19 % und Telefonnummern wie 05772 293-100 werden so gelesen, wie sie dastehen. Telefonnummern spricht das Modell Ziffer für Ziffer, das ist richtig, dauert aber: Rechne bei gleicher Zeichenzahl mit etwa der vierfachen Audiolänge gegenüber normaler Prosa.

Englisch ist davon nicht betroffen. Order 45312 shipped on May 20th, 2026. The total is 1,299.99 euros including 19 percent VAT. kommt praktisch wortwörtlich zurück.

Drittens: Englische Fachbegriffe mitten im deutschen Satz sind der schwächste Fall, den wir gefunden haben. Kubernetes-Cluster, Continuous Deployment und Zero-Downtime-Updates in einem deutschen Satz kamen teilweise verstümmelt zurück. Eigennamen sind auf dieselbe Weise unzuverlässig, und keine Schreibweise repariert das: Wir haben einen Markennamen normal, umgeschrieben, mit Bindestrich und mit Leerzeichen probiert, dazu instructions mit der Bitte um deutsche Aussprache, und 1 von 21 Versuchen kam richtig heraus. Ein Aussprachelexikon gibt es auf dieser Route nicht. Muss ein Name jedes Mal sitzen, lass ihn aus dem vertonten Text heraus oder schneide eine Aufnahme davon ein.

Reproduzierbarkeit

seed wird angenommen und liefert HTTP 200, aber nicht zweimal dasselbe Audio. Drei identische Anfragen mit seed: 42 ergaben drei verschiedene Dateien mit 6,64 s, 6,48 s und 6,32 s Länge. Alle drei waren einwandfrei verständlich, das ist also eine Grenze der Reproduzierbarkeit und kein Qualitätsproblem.

Wenn ein Clip über mehrere Deployments hinweg identisch bleiben soll, erzeug ihn einmal und leg die Datei ab.

Latenz

Auf unserer Hardware gemessen, Deutsch und Englisch, eine Anfrage zur Zeit:

Länge der EingabeWartezeit auf die ganze AntwortErzeugtes Audio
41 Zeichen619 ms (390 bis 877)2,5 s
100 Zeichen1343 ms (986 bis 1677)6,7 s
272 Zeichen3242 ms (2047 bis 4278)17,8 s

Jede Zeile ist der Median aus vier Durchläufen, die Spanne steht in Klammern, gepuffert, mp3. Das Verhältnis von Wartezeit zu Audiolänge fällt von 0,235 bei 41 Zeichen auf 0,170 bei 272. Der feste Anteil einer Anfrage wiegt bei kurzem Text also schwerer als bei langem. Rechne mit einem Fünftel der Abspieldauer, die du anforderst, und nimm die Streuung ernst: Derselbe Text mit 272 Zeichen brauchte in einem Durchlauf 2,0 s und in einem anderen 4,3 s.

Streaming drückt die Wartezeit auf das erste Audio unter 150 ms, siehe Streaming. Nutz es, sobald jemand darauf wartet.

Streaming

Setz stream auf true, dann bekommst du das Audio, während es noch entsteht. Das erste Audio kommt dann in weniger als 150 ms statt erst nach der kompletten Generierung. Für alles, worauf ein Mensch wartet, ist das die entscheidende Einstellung.

Streaming funktioniert nur mit response_format="wav" und "pcm". Jedes andere Format zusammen mit stream antwortet mit HTTP 400:

Streaming (stream=true, stream_format='audio', or stream_format='sse') requires response_format='pcm' or 'wav'. Got response_format='mp3'.

Gemessen am selben Satz mit 100 Zeichen wie in der Tabelle darüber, response_format="pcm":

ModusContent-TypeErstes AudioWas ankommt
gepuffert (Standard)audio/pcm870 msein Antwortkörper
Streaming mit "audio"audio/pcm91 ms97 Chunks, verteilt über 123 bis 999 ms
Streaming mit "sse"text/event-stream95 ms7 speech.audio.delta, 1 speech.audio.done

stream_format wählt das Transportformat. "audio" liefert eine Chunked-Antwort mit rohem Audio im angeforderten Format, und das willst du, wenn du in eine Datei schreibst oder an einen Player weitergibst. "sse" liefert das Event-Format von OpenAI: speech.audio.delta-Events mit dem Audio als Base64, abgeschlossen von einem speech.audio.done.

event: speech.audio.delta
data: {"type":"speech.audio.delta","audio":"UklGRv////9XQVZF…"}
from openai import OpenAI

client = OpenAI(
base_url="https://llm.aihosting.mittwald.de/v1",
api_key="sk-your-api-key-here",
)

with client.audio.speech.with_streaming_response.create(
model="Qwen3-TTS-12Hz-1.7B-CustomVoice",
voice="ryan",
input="Ein längerer Text, bei dem die Wiedergabe sofort starten soll.",
response_format="wav",
stream_format="audio",
extra_body={"stream": True},
) as response:
response.stream_to_file("ausgabe.wav")

Bei sehr langen Texten lohnt sich das Aufteilen trotzdem, denn dann kannst du die nächste Anfrage schon stellen, während der aktuelle Teil abspielt. Ein ausgearbeitetes Beispiel steht im Text-to-Speech-Guide.

Fehlerantworten

SituationStatusMeldung
input ist ein leerer String400Got bad request.
voice fehlt400Got bad request.
Unbekannte voice400Got bad request.
Unbekannte language400Got bad request.
instructions länger als 500 Zeichen400Got bad request.
response_format="aac" oder ein anderes unbekanntes Format400Input should be 'wav', 'pcm', 'flac', 'mp3' or 'opus'
speed außerhalb von 0,25 bis 4,0400Input should be less than or equal to 4
stream mit einem anderen Format als wav oder pcm400… requires response_format='pcm' or 'wav'
ref_audio, ref_text, speaker_embedding oder task_type in der Anfrage500Internal error., siehe Voice-Cloning
Unbekannter Modellname404Got unknown model name.

Abgewiesene Werte kommen in zwei Formen zurück. response_format, speed und die Streaming-Bedingung werden mit einer Meldung geprüft, die das Feld nennt:

{
"error": {
"message": "litellm.BadRequestError: Hosted_vllmException - 1 validation error:\n {'type': 'literal_error', 'loc': ('body', 'response_format'), 'msg': \"Input should be 'wav', 'pcm', 'flac', 'mp3' or 'opus'\"}",
"code": "400"
}
}

voice, language, instructions und ein leerer input kommen allgemein zurück, ohne Hinweis darauf, welcher Parameter falsch war:

{
"error": {
"message": "Got bad request.",
"code": "400"
}
}

Prüf voice und language also gegen die Listen auf dieser Seite, bevor du sie schickst: Bei diesen beiden sagt dir der Statuscode, dass etwas falsch ist, und nichts sagt dir, was.

Voice-Cloning

Eine Stimme aus eigenem Referenz-Audio zu klonen, gibt es hier nicht. Dieses Modell bringt die neun eingebauten Stimmen mit und keinen Speaker-Encoder, es fehlt also die Komponente, die aus einer Aufnahme deiner Stimme eine sprechbare Stimme machen würde. Anfragen mit ref_audio, ref_text, speaker_embedding oder task_type scheitern mit HTTP 500 und Internal error., statt ein unbrauchbares Ergebnis zu liefern.

Wenn du eine bestimmte Stimme brauchst, arbeite mit den neun vorhandenen: Wähl nach Tonhöhe wie unter Verfügbare Stimmen beschrieben, und form die Sprechweise über instructions und speed.

Lizenz und Nutzungsbedingungen

Alibaba veröffentlicht das Modell unter der Apache-2.0-Lizenz. Bevor du es nutzen kannst, musst du die Nutzungsbedingungen im mStudio annehmen. Siehe Nutzungsbedingungen.