使用語音 SDK 降低語音合成延遲
合成延遲對於您的應用程式至關重要。 在本文中,我們將介紹最佳做法,以降低延遲,併為您的終端用戶帶來最佳效能。
一般而言,我們會依 first byte latency
和 finish latency
測量延遲,如下所示:
Latency | 描述 | SpeechSynthesisResult 屬性索引鍵 |
---|---|---|
第一個字節延遲 | 指出合成工作開始與收到第一個音訊數據區塊之間的時間延遲。 | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
完成延遲 | 指出合成工作開始與接收整個合成音訊數據之間的時間延遲。 | SpeechServiceResponse_SynthesisFinishLatencyMs |
語音 SDK 會將延遲持續時間放在 的 SpeechSynthesisResult
Properties 集合中。 下列範例程式代碼顯示這些值。
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;
Latency | 描述 | SpeechSynthesisResult 屬性索引鍵 |
---|---|---|
first byte latency |
表示合成開始與收到第一個音訊區塊之間的時間延遲。 | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
表示合成開始與接收整個合成音訊之間的時間延遲。 | SpeechServiceResponse_SynthesisFinishLatencyMs |
語音 SDK 會測量延遲,並將其放在 的屬性 SpeechSynthesisResult
包中。 請參閱下列程式代碼以取得它們。
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;
Latency | 描述 | SpeechSynthesisResult 屬性索引鍵 |
---|---|---|
first byte latency |
表示合成開始與收到第一個音訊區塊之間的時間延遲。 | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
表示合成開始與接收整個合成音訊之間的時間延遲。 | SpeechServiceResponse_SynthesisFinishLatencyMs |
語音 SDK 會測量延遲,並將其放在 的屬性 SpeechSynthesisResult
包中。 請參閱下列程式代碼以取得它們。
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();
Latency | 描述 | SpeechSynthesisResult 屬性索引鍵 |
---|---|---|
first byte latency |
表示合成開始與收到第一個音訊區塊之間的時間延遲。 | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
表示合成開始與接收整個合成音訊之間的時間延遲。 | SpeechServiceResponse_SynthesisFinishLatencyMs |
語音 SDK 會測量延遲,並將其放在 的屬性 SpeechSynthesisResult
包中。 請參閱下列程式代碼以取得它們。
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
Latency | 描述 | SPXSpeechSynthesisResult 屬性索引鍵 |
---|---|---|
first byte latency |
表示合成開始與收到第一個音訊區塊之間的時間延遲。 | SPXSpeechServiceResponseSynthesisFirstByteLatencyMs |
finish latency |
表示合成開始與接收整個合成音訊之間的時間延遲。 | SPXSpeechServiceResponseSynthesisFinishLatencyMs |
語音 SDK 會測量延遲,並將其放在 的屬性 SPXSpeechSynthesisResult
包中。 請參閱下列程式代碼以取得它們。
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;
在大部分情況下,第一個字節延遲低於完成延遲。 第一個字節延遲與文字長度無關,而完成延遲會隨著文字長度而增加。
在理想情況下,我們想要將使用者經歷的延遲降到一個網路路由行程時間加上語音合成服務的第一個音訊區塊延遲,以將使用者遇到的延遲降到最低。
串流
串流是降低延遲的關鍵。 用戶端程式碼可以在收到第一個音訊區塊時開始播放。 在服務案例中,您可以立即將音訊區塊轉寄給用戶端,而不用等候整個音訊。
您可以使用 、PullAudioOutputStream
PushAudioOutputStream
、Synthesizing
事件和AudioDataStream
語音 SDK 來啟用串流。
以 AudioDataStream
下列範例為例:
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.");
}
}
}
}
您可以使用 PullAudioOutputStream
、 PushAudioOutputStream
、 Synthesizing
事件和 AudioDataStream
語音 SDK 來啟用串流。
以 AudioDataStream
下列範例為例:
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;
}
您可以使用 PullAudioOutputStream
、 PushAudioOutputStream
、 Synthesizing
事件和 AudioDataStream
語音 SDK 來啟用串流。
以 AudioDataStream
下列範例為例:
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);
}
您可以使用 PullAudioOutputStream
、 PushAudioOutputStream
、 Synthesizing
事件和 AudioDataStream
語音 SDK 來啟用串流。
以 AudioDataStream
下列範例為例:
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)
您可以使用 SPXPullAudioOutputStream
、 SPXPushAudioOutputStream
、 Synthesizing
事件和 SPXAudioDataStream
語音 SDK 來啟用串流。
以 AudioDataStream
下列範例為例:
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
}
預先連線並重複使用SpeechSynthesizer
語音 SDK 會使用 websocket 與服務通訊。
在理想情況下,網路等待時間應該是一個路由車程時間(RTT)。
如果新建立連線,網路等待時間會包含建立連線的額外時間。
建立 websocket 連線需要 TCP 交握、SSL 交握、HTTP 連線和通訊協議升級,這會導致時間延遲。
若要避免連線延遲,建議您預先連線並重複使用 SpeechSynthesizer
。
連線前
若要預先連線,請在您很快知道需要連線時,建立與語音服務的連線。 例如,如果您要在用戶端中建置語音 Bot,您可以在使用者開始交談時預先連線到語音合成服務,並在 Bot 回復文字就緒時呼叫 SpeakTextAsync
。
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];
注意
如果合成文字可供使用,只要呼叫 SpeakTextAsync
以合成音訊即可。 SDK 會處理連線。
重複使用SpeechSynthesizer
減少連線延遲的另一種方法是重複使用 , SpeechSynthesizer
因此您不需要為每個合成建立新的 SpeechSynthesizer
。
建議您在服務案例中使用物件集區,請參閱 C# 和 Java 的範例程式代碼。
透過網路傳輸壓縮的音訊
當網路不穩定或頻寬有限時,承載大小也會影響延遲。 同時,壓縮的音訊格式有助於節省使用者的網路頻寬,這對行動用戶特別有用。
我們支援許多壓縮格式,包括 opus
、webm
、mp3
、 silk
等,請參閱SpeechSynthesisOutputFormat的完整清單。
例如,格式的 Riff24Khz16BitMonoPcm
比特率是 384 kbps,而 Audio24Khz48KBitRateMonoMp3
只需要花費 48 kbps。
設定輸出格式時 pcm
,我們的語音 SDK 會自動使用壓縮格式進行傳輸。
針對Linux和 Windows, GStreamer
必須啟用此功能。
請參閱 此指示 來安裝和設定 GStreamer
語音 SDK。
針對 Android、iOS 和 macOS,從 1.20 版開始不需要額外的設定。
其他秘訣
快取 CRL 檔案
語音 SDK 會使用 CRL 檔案來檢查認證。 快取 CRL 檔案直到過期為止,可協助您避免每次下載 CRL 檔案。 如需詳細資料,請參閱如何設定適用於 Linux 的 OpenSSL。
使用最新的語音 SDK
我們會持續改善語音 SDK 的效能,因此請嘗試在您的應用程式中使用最新的語音 SDK。
負載測試指導方針
您可以使用負載測試來測試語音合成服務容量和延遲。 以下是一些指導方針:
- 語音合成服務能夠自動調整,但需要時間來相應放大。如果並行存取在短時間內增加,用戶端可能會有很長的延遲或
429
錯誤碼(太多要求)。 因此,建議您在負載測試中逐步增加並行。 如需詳細資訊,請參閱這篇文章 ,特別是 此工作負載模式範例。 - 您可以使用我們的範例,使用物件集區 (C# 和 Java) 進行負載測試,並取得延遲數位。 您可以在範例中修改測試回合和並行,以符合目標並行。
- 因此,如果您想要使用高於實際流量的並行存取來執行負載測試,請在測試之前連線,服務會根據實際流量來限制配額限制。
下一步
意見反映
https://aka.ms/ContentUserFeedback。
即將推出:我們會在 2024 年淘汰 GitHub 問題,並以全新的意見反應系統取代並作為內容意見反應的渠道。 如需更多資訊,請參閱:提交及檢視以下的意見反映: