Teilen über


Geringere Wartezeit bei der Sprachsynthese mit dem Speech SDK

In diesem Artikel werden die bewährten Methoden vorgestellt, um die Wartezeit der Sprachsynthese zu verringern und Ihren Endbenutzern die beste Leistung zu bieten.

Normalerweise messen wir die Wartezeit wie folgt anhand von first byte latency und finish latency:

Wartezeit BESCHREIBUNG SpeechSynthesisResult-Eigenschaftsschlüssel
first byte latency (Wartezeit für das erste Byte) Gibt die Zeitverzögerung zwischen dem Start des Synthesetasks und dem Empfang des ersten Audiodatenblocks an. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish latency (Abschlusswartezeit) Gibt die Zeitverzögerung zwischen dem Start des Synthesetasks und dem Empfang der gesamten synthetisierten Audiodaten an. SpeechServiceResponse_SynthesisFinishLatencyMs

Das Speech SDK fügt die Wartezeitdauern in die Properties-Auflistung von SpeechSynthesisResult ein. Der folgende Beispielcode zeigt diese Werte.

var result = await synthesizer.SpeakTextAsync(text);
Console.WriteLine($"first byte latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs)} ms");
Console.WriteLine($"finish latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs)} ms");
// you can also get the result id, and send to us when you need help for diagnosis
var resultId = result.ResultId;
Wartezeit BESCHREIBUNG SpeechSynthesisResult-Eigenschaftsschlüssel
first byte latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang des ersten Audioblocks an. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang der gesamten synthetisierten Audiodaten an. SpeechServiceResponse_SynthesisFinishLatencyMs

Das Speech SDK hat die Wartezeiten gemessen und sie in der Eigenschaftensammlung von SpeechSynthesisResult abgelegt. Sie erhalten sie über die folgenden Codes.

auto result = synthesizer->SpeakTextAsync(text).get();
auto firstByteLatency = std::stoi(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_SynthesisFirstByteLatencyMs));
auto finishedLatency = std::stoi(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_SynthesisFinishLatencyMs));
// you can also get the result id, and send to us when you need help for diagnosis
auto resultId = result->ResultId;
Wartezeit BESCHREIBUNG SpeechSynthesisResult-Eigenschaftsschlüssel
first byte latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang des ersten Audioblocks an. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang der gesamten synthetisierten Audiodaten an. SpeechServiceResponse_SynthesisFinishLatencyMs

Das Speech SDK hat die Wartezeiten gemessen und sie in der Eigenschaftensammlung von SpeechSynthesisResult abgelegt. Sie erhalten sie über die folgenden Codes.

SpeechSynthesisResult result = synthesizer.SpeakTextAsync(text).get();
System.out.println("first byte latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs) + " ms.");
System.out.println("finish latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs) + " ms.");
// you can also get the result id, and send to us when you need help for diagnosis
String resultId = result.getResultId();
Wartezeit BESCHREIBUNG SpeechSynthesisResult-Eigenschaftsschlüssel
first byte latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang des ersten Audioblocks an. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang der gesamten synthetisierten Audiodaten an. SpeechServiceResponse_SynthesisFinishLatencyMs

Das Speech SDK hat die Wartezeiten gemessen und sie in der Eigenschaftensammlung von SpeechSynthesisResult abgelegt. Sie erhalten sie über die folgenden Codes.

result = synthesizer.speak_text_async(text).get()
first_byte_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs))
finished_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs))
# you can also get the result id, and send to us when you need help for diagnosis
result_id = result.result_id
Wartezeit BESCHREIBUNG SPXSpeechSynthesisResult-Eigenschaftsschlüssel
first byte latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang des ersten Audioblocks an. SPXSpeechServiceResponseSynthesisFirstByteLatencyMs
finish latency Gibt die Zeitverzögerung zwischen dem Start der Synthese und dem Empfang der gesamten synthetisierten Audiodaten an. SPXSpeechServiceResponseSynthesisFinishLatencyMs

Das Speech SDK hat die Wartezeiten gemessen und sie in der Eigenschaftensammlung von SPXSpeechSynthesisResult abgelegt. Sie erhalten sie über die folgenden Codes.

SPXSpeechSynthesisResult *speechResult = [speechSynthesizer speakText:text];
int firstByteLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisFirstByteLatencyMs]];
int finishedLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisFinishLatencyMs]];
// you can also get the result id, and send to us when you need help for diagnosis
NSString *resultId = result.resultId;

Die Wartezeit für das erste Byte (first byte latency) ist in den meisten Fällen niedriger als die Abschlusswartezeit (finish latency). Die Wartezeit für das erste Byte ist unabhängig von der Textlänge, während die Abschlusswartezeit mit der Textlänge zunimmt.

Im Idealfall möchten wir die vom Benutzer erlebte Wartezeit (die Wartezeit, bevor der Benutzer den Ton hört) auf eine Netzwerkstreckenzeit plus die Wartezeit für den ersten Audioblock des Sprachsynthesediensts minimieren.

Streaming

Streaming ist entscheidend für die Verringerung der Wartezeit. Clientcode kann die Wiedergabe starten, wenn der erste Audioblock empfangen wird. In einem Dienstszenario können Sie die Audioblöcke sofort an Ihre Clients weiterleiten, anstatt auf die gesamten Audiodaten zu warten.

Sie können das PullAudioOutputStream, PushAudioOutputStream, Synthesizing-Ereignis und AudioDataStream des Speech SDK verwenden, um das Streaming zu aktivieren.

Nehmen wir AudioDataStream als Beispiel:

using (var synthesizer = new SpeechSynthesizer(config, null as AudioConfig))
{
    using (var result = await synthesizer.StartSpeakingTextAsync(text))
    {
        using (var audioDataStream = AudioDataStream.FromResult(result))
        {
            byte[] buffer = new byte[16000];
            uint filledSize = 0;
            while ((filledSize = audioDataStream.ReadData(buffer)) > 0)
            {
                Console.WriteLine($"{filledSize} bytes received.");
            }
        }
    }
}

Sie können das PullAudioOutputStream-, PushAudioOutputStream-, Synthesizing-Ereignis und AudioDataStream des Speech SDK verwenden, um das Streaming zu aktivieren.

Nehmen wir AudioDataStream als Beispiel:

auto synthesizer = SpeechSynthesizer::FromConfig(config, nullptr);
auto result = synthesizer->SpeakTextAsync(text).get();
auto audioDataStream = AudioDataStream::FromResult(result);
uint8_t buffer[16000];
uint32_t filledSize = 0;
while ((filledSize = audioDataStream->ReadData(buffer, sizeof(buffer))) > 0)
{
    cout << filledSize << " bytes received." << endl;
}

Sie können das PullAudioOutputStream-, PushAudioOutputStream-, Synthesizing-Ereignis und AudioDataStream des Speech SDK verwenden, um das Streaming zu aktivieren.

Nehmen wir AudioDataStream als Beispiel:

SpeechSynthesizer synthesizer = new SpeechSynthesizer(config, null);
SpeechSynthesisResult result = synthesizer.StartSpeakingTextAsync(text).get();
AudioDataStream audioDataStream = AudioDataStream.fromResult(result);
byte[] buffer = new byte[16000];
long filledSize = audioDataStream.readData(buffer);
while (filledSize > 0) {
    System.out.println(filledSize + " bytes received.");
    filledSize = audioDataStream.readData(buffer);
}

Sie können das PullAudioOutputStream-, PushAudioOutputStream-, Synthesizing-Ereignis und AudioDataStream des Speech SDK verwenden, um das Streaming zu aktivieren.

Nehmen wir AudioDataStream als Beispiel:

speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=None)
result = speech_synthesizer.start_speaking_text_async(text).get()
audio_data_stream = speechsdk.AudioDataStream(result)
audio_buffer = bytes(16000)
filled_size = audio_data_stream.read_data(audio_buffer)
while filled_size > 0:
    print("{} bytes received.".format(filled_size))
    filled_size = audio_data_stream.read_data(audio_buffer)

Sie können das SPXPullAudioOutputStream-, SPXPushAudioOutputStream-, Synthesizing-Ereignis und SPXAudioDataStream des Speech SDK verwenden, um das Streaming zu aktivieren.

Nehmen wir AudioDataStream als Beispiel:

SPXSpeechSynthesizer *synthesizer = [[SPXSpeechSynthesizer alloc] initWithSpeechConfiguration:speechConfig audioConfiguration:nil];
SPXSpeechSynthesisResult *speechResult = [synthesizer startSpeakingText:inputText];
SPXAudioDataStream *stream = [[SPXAudioDataStream alloc] initFromSynthesisResult:speechResult];
NSMutableData* data = [[NSMutableData alloc]initWithCapacity:16000];
while ([stream readData:data length:16000] > 0) {
    // Read data here
}

Vorabverbindung und Wiederverwendung von SpeechSynthesizer

Das Speech SDK verwendet ein Websocket für die Kommunikation mit dem Dienst. Im Idealfall sollte die Netzwerkwartezeit einer Netzwerkstreckenzeit entsprechen. Wenn die Verbindung neu hergestellt wird, erfordert die Netzwerkwartezeit zusätzliche Zeit zum Herstellen der Verbindung. Für die Einrichtung einer Websocket-Verbindung sind TCP-Handshake, SSL-Handshake, HTTP-Verbindung und Protokollupgrade erforderlich, was zu einer Zeitverzögerung führt. Um die Wartezeit für das Herstellen der Verbindung zu vermeiden, empfiehlt es sich, vorab eine Verbindung herzustellen und SpeechSynthesizer wiederzuverwenden.

Vor dem Herstellen einer Verbindung

Stellen Sie zum Herstellen einer Vorabverbindung eine Verbindung mit dem Speech-Dienst her, wenn Sie wissen, dass die Verbindung bald benötigt wird. Wenn Sie z. B. einen Sprachbot auf dem Client erstellen, können Sie vorab eine Verbindung mit dem Sprachsynthesedienst herstellen, wenn der Benutzer mit dem Sprechen beginnt, und SpeakTextAsync aufrufen, wenn der Antworttext des Bots bereit ist.

using (var synthesizer = new SpeechSynthesizer(uspConfig, null as AudioConfig))
{
    using (var connection = Connection.FromSpeechSynthesizer(synthesizer))
    {
        connection.Open(true);
    }
    await synthesizer.SpeakTextAsync(text);
}
auto synthesizer = SpeechSynthesizer::FromConfig(config, nullptr);
auto connection = Connection::FromSpeechSynthesizer(synthesizer);
connection->Open(true);
SpeechSynthesizer synthesizer = new SpeechSynthesizer(speechConfig, (AudioConfig) null);
Connection connection = Connection.fromSpeechSynthesizer(synthesizer);
connection.openConnection(true);
synthesizer = speechsdk.SpeechSynthesizer(config, None)
connection = speechsdk.Connection.from_speech_synthesizer(synthesizer)
connection.open(True)
SPXSpeechSynthesizer* synthesizer = [[SPXSpeechSynthesizer alloc]initWithSpeechConfiguration:self.speechConfig audioConfiguration:nil];
SPXConnection* connection = [[SPXConnection alloc]initFromSpeechSynthesizer:synthesizer];
[connection open:true];

Hinweis

Wenn der Text verfügbar ist, rufen Sie einfach SpeakTextAsync auf, um die Audiodaten zu synthetisieren. Das SDK verarbeitet die Verbindung.

Wiederverwenden von SpeechSynthesizer

Eine weitere Möglichkeit, die Verbindungswartezeit zu reduzieren, besteht darin, SpeechSynthesizer wiederzuverwenden, damit Sie für die einzelnen Synthesen keinen neuen SpeechSynthesizer erstellen müssen. Wir empfehlen die Verwendung des Objektpools im Dienstszenario. Sehen Sie sich unseren Beispielcode für C# und Java an.

Übertragen komprimierter Audiodaten über das Netzwerk

Wenn das Netzwerk instabil ist oder eine eingeschränkte Bandbreite aufweist, wirkt sich die Nutzdatengröße auch auf die Wartezeit aus. In der Zwischenzeit hilft ein komprimiertes Audioformat dabei, die Netzwerkbandbreite der Benutzer zu schonen, was besonders für mobile Benutzer nützlich ist.

Wir unterstützen viele komprimierte Formate, z. B. opus, webm, mp3, silk usw. Eine vollständige Liste finden Sie unter SpeechSynthesisOutputFormat. Die Bitrate des Riff24Khz16BitMonoPcm-Formats beträgt z. B. 384 KBit/s, während Audio24Khz48KBitRateMonoMp3 nur 48 KBit/s erfordert. Das Speech SDK verwendet automatisch ein komprimiertes Format für die Übertragung, wenn ein pcm-Ausgabeformat festgelegt ist. Für Linux und Windows ist GStreamer zum Aktivieren dieses Features erforderlich. Diese Anweisung enthält Informationen, um GStreamer zu installieren und für das Speech SDK zu konfigurieren. Für Android, iOS und macOS ist ab Version 1.20 keine zusätzliche Konfiguration erforderlich.

Streamen von Eingabetexten

Textstreaming ermöglicht die Echtzeit-Textverarbeitung für eine schnelle Audiogenerierung. Es eignet sich perfekt für die dynamische Textvokalisierung, z. B. das Lesen von Ausgaben von KI-Modellen wie GPT in Echtzeit. Diese Funktion minimiert die Latenz und verbessert die flüssige und reaktionsschnelle Audioausgabe, was sie ideal für interaktive Anwendungen, Live-Events und reaktionsschnelle KI-gesteuerte Dialoge macht.

Verwendung von Textstreaming

Textstreaming wird in C#, C++ und Python mit Speech-SDK unterstützt.

Um das Textstreamingfeature zu verwenden, stellen Sie eine Verbindung mit dem Websocket V2-Endpunkt her: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2

Sehen Sie sich den Beispielcode für die Einstellung des Endpunkts an:

// IMPORTANT: MUST use the websocket v2 endpoint
var ttsEndpoint = $"wss://{Environment.GetEnvironmentVariable("AZURE_TTS_REGION")}.tts.speech.microsoft.com/cognitiveservices/websocket/v2";
var speechConfig = SpeechConfig.FromEndpoint(
    new Uri(ttsEndpoint),
    Environment.GetEnvironmentVariable("AZURE_TTS_API_KEY"));

Wichtigste Schritte

  1. Erstellen einer Textstreamanforderung: Verwenden Sie SpeechSynthesisRequestInputType.TextStream, um einen Textstream zu initiieren.

  2. Festlegen globaler Eigenschaften: Passen Sie Einstellungen wie das Ausgabeformat und den Namen der Stimme direkt an, da die Funktion nur teilweise Texteingaben verarbeitet und SSML nicht unterstützt. Anweisungen zum Festlegen finden Sie im folgenden Beispielcode. Stimmen der OpenAI-Sprachsynthese werden vom Textstreaming-Feature nicht unterstützt. In dieser Sprachtabelle finden Sie alle Informationen zur Sprachunterstützung.

    // Set output format
    speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm);
    
    // Set a voice name
    SpeechConfig.SetProperty(PropertyId.SpeechServiceConnection_SynthVoice, "en-US-AvaMultilingualNeural");
    
  3. Streamen Sie Ihren Text: Verwenden Sie request.InputStream.Write(text); für jeden Textabschnitt, der aus einem GPT-Modell generiert wurde, um den Text an den Datenstrom zu senden.

  4. Schließen Sie den Datenstrom: Sobald das GPT-Modell die Ausgabe abgeschlossen hat, schließen Sie den Datenstrom mithilfe von request.InputStream.Close();.

Eine ausführliche Implementierung finden Sie im Beispielcode auf GitHub.

Um das Textstreamingfeature zu verwenden, stellen Sie eine Verbindung mit dem Websocket V2-Endpunkt her: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2

Sehen Sie sich den Beispielcode für die Einstellung des Endpunkts an:

# IMPORTANT: MUST use the websocket v2 endpoint
speech_config = speechsdk.SpeechConfig(endpoint=f"wss://{os.getenv('AZURE_TTS_REGION')}.tts.speech.microsoft.com/cognitiveservices/websocket/v2",
                                       subscription=os.getenv("AZURE_TTS_API_KEY"))

Wichtigste Schritte

  1. Erstellen einer Textstreamanforderung: Verwenden Sie speechsdk.SpeechSynthesisRequestInputType.TextStream, um einen Textstream zu initiieren.

  2. Festlegen globaler Eigenschaften: Passen Sie Einstellungen wie das Ausgabeformat und den Namen der Stimme direkt an, da die Funktion nur teilweise Texteingaben verarbeitet und SSML nicht unterstützt. Anweisungen zum Festlegen finden Sie im folgenden Beispielcode. Stimmen der OpenAI-Sprachsynthese werden vom Textstreaming-Feature nicht unterstützt. In dieser Sprachtabelle finden Sie alle Informationen zur Sprachunterstützung.

    # set a voice name
    speech_config.speech_synthesis_voice_name = "en-US-AvaMultilingualNeural"
    
  3. Streamen Sie Ihren Text: Verwenden Sie request.input_stream.write(text) für jeden Textabschnitt, der aus einem GPT-Modell generiert wurde, um den Text an den Datenstrom zu senden.

  4. Schließen Sie den Datenstrom: Sobald das GPT-Modell die Ausgabe abgeschlossen hat, schließen Sie den Datenstrom mithilfe von request.input_stream.close().

Eine ausführliche Implementierung finden Sie im Beispielcode auf GitHub.

Der C++-Beispielcode ist aktuell nicht verfügbar. Den Beispielcode, der zeigt, wie Textstreaming verwendet wird, finden Sie unter:

Den Beispielcode, der zeigt, wie Textstreaming verwendet wird, finden Sie unter:

Den Beispielcode, der zeigt, wie Textstreaming verwendet wird, finden Sie unter:

Weitere Tipps

Zwischenspeichern von Zertifikatsperrlistendateien

Das Speech SDK verwendet Zertifikatsperrlistendateien, um die Zertifizierung zu überprüfen. Durch das Zwischenspeichern der Sperrlistendateien bis zu deren Ablauf können Sie vermeiden, jedes Mal die Sperrlistendateien herunterzuladen. Weitere Informationen finden Sie unter Konfigurieren von OpenSSL für Linux.

Verwenden des neuesten Speech SDK

Wir verbessern weiterhin die Leistung des Speech SDK. Versuchen Sie daher, das neueste Speech SDK in Ihrer Anwendung zu verwenden.

Richtlinie für Auslastungstests

Sie können den Auslastungstest verwenden, um die Kapazität und Wartezeit des Sprachsynthesediensts zu testen. Hier finden Sie einige Richtlinien:

  • Der Sprachsynthesedienst verfügt über die Möglichkeit, die automatische Skalierung zu aktivieren, erfordert jedoch Zeit zum Skalieren. Wenn die Parallelität in kurzer Zeit erhöht wird, hat der Client möglicherweise eine hohe Latenz oder einen 429-Fehlercode (zu viele Anforderungen). Daher wird empfohlen, die Parallelität schrittweise im Auslastungstest zu erhöhen. In diesem Artikel finden Sie weitere Einzelheiten, insbesondere dieses Beispiel für Workloadmuster.
  • Sie können unser Beispiel verwenden, indem Sie einen Objektpool (C# und Java) für Auslastungstests verwenden und die Latenzwerte erhalten. Sie können die Testdurchläufe und die Parallelität im Beispiel ändern, um Ihre Zielparallelität zu erreichen.
  • Der Dienst verfügt basierend auf dem tatsächlichen Datenverkehr über eine Kontingentgrenze. Wenn Sie also einen Auslastungstest mit einer höheren Parallelität als dem tatsächlichen Datenverkehr durchführen möchten, stellen Sie vor dem Test eine Verbindung her.

Nächste Schritte