Ridurre la latenza della sintesi vocale con Speech SDK
In questo articolo verranno illustrate le procedure consigliate per ridurre la latenza della sintesi vocale e offrire prestazioni ottimali agli utenti finali.
In genere, la latenza viene misurata in base a first byte latency
e finish latency
, come indicato di seguito:
Latenza | Descrizione | Chiave della proprietà SpeechSynthesisResult |
---|---|---|
latenza primo byte | Indica il ritardo di tempo tra l'inizio dell'attività di sintesi e la ricezione del primo blocco di dati audio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
latenza di fine | Indica il ritardo di tempo tra l'inizio dell'attività di sintesi e la ricezione dei dati audio sintetizzati completi. | SpeechServiceResponse_SynthesisFinishLatencyMs |
L'SDK di Voce inserisce le durate della latenza nella raccolta Proprietà di SpeechSynthesisResult
. Il codice di esempio seguente mostra questi valori.
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;
Latenza | Descrizione | Chiave della proprietà SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica il ritardo di tempo tra l'inizio della sintesi e il primo blocco audio ricevuto. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica il ritardo di tempo tra l'inizio della sintesi e la ricezione dell'intero audio sintetizzato. | SpeechServiceResponse_SynthesisFinishLatencyMs |
L'SDK di Voce misura le latenze e le inserisce nel contenitore delle proprietà di SpeechSynthesisResult
. Per ottenerle, fare riferimento ai codici seguenti.
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;
Latenza | Descrizione | Chiave della proprietà SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica il ritardo di tempo tra l'inizio della sintesi e il primo blocco audio ricevuto. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica il ritardo di tempo tra l'inizio della sintesi e la ricezione dell'intero audio sintetizzato. | SpeechServiceResponse_SynthesisFinishLatencyMs |
L'SDK di Voce misura le latenze e le inserisce nel contenitore delle proprietà di SpeechSynthesisResult
. Per ottenerle, fare riferimento ai codici seguenti.
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();
Latenza | Descrizione | Chiave della proprietà SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica il ritardo di tempo tra l'inizio della sintesi e il primo blocco audio ricevuto. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica il ritardo di tempo tra l'inizio della sintesi e la ricezione dell'intero audio sintetizzato. | SpeechServiceResponse_SynthesisFinishLatencyMs |
L'SDK di Voce misura le latenze e le inserisce nel contenitore delle proprietà di SpeechSynthesisResult
. Per ottenerle, fare riferimento ai codici seguenti.
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
Latenza | Descrizione | Chiave della proprietà SPXSpeechSynthesisResult |
---|---|---|
first byte latency |
Indica il ritardo di tempo tra l'inizio della sintesi e il primo blocco audio ricevuto. | SPXSpeechServiceResponseSynthesisFirstByteLatencyMs |
finish latency |
Indica il ritardo di tempo tra l'inizio della sintesi e la ricezione dell'intero audio sintetizzato. | SPXSpeechServiceResponseSynthesisFinishLatencyMs |
L'SDK di Voce misura le latenze e le inserisce nel contenitore delle proprietà di SPXSpeechSynthesisResult
. Per ottenerle, fare riferimento ai codici seguenti.
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;
La latenza di primo byte è inferiore rispetto alla latenza di fine nella maggior parte dei casi. La latenza di primo byte è indipendente dalla lunghezza del testo, mentre la latenza di fine aumenta con la lunghezza del testo.
Idealmente, si vuole ridurre al minimo la latenza riscontrata dall'utente (la latenza prima che l'utente senta il suono) a un tempo di percorrenza della route di rete più la prima latenza audio in blocchi del servizio di sintesi vocale.
Streaming
Lo streaming è fondamentale per ridurre la latenza. Il codice client può avviare la riproduzione quando viene ricevuto il primo blocco audio. In uno scenario di servizio, è possibile inoltrare immediatamente i blocchi audio ai client invece di attendere l'intero audio.
È possibile usare PullAudioOutputStream
, PushAudioOutputStream
, l'Synthesizing
evento e AudioDataStream
dell'SDK di Voce per abilitare lo streaming.
Prendendo AudioDataStream
come esempio:
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.");
}
}
}
}
È possibile usare PullAudioOutputStream
, PushAudioOutputStream
, l'Synthesizing
evento e AudioDataStream
dell'SDK di Voce per abilitare lo streaming.
Prendendo AudioDataStream
come esempio:
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;
}
È possibile usare PullAudioOutputStream
, PushAudioOutputStream
, l'Synthesizing
evento e AudioDataStream
dell'SDK di Voce per abilitare lo streaming.
Prendendo AudioDataStream
come esempio:
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);
}
È possibile usare PullAudioOutputStream
, PushAudioOutputStream
, l'Synthesizing
evento e AudioDataStream
dell'SDK di Voce per abilitare lo streaming.
Prendendo AudioDataStream
come esempio:
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)
È possibile usare SPXPullAudioOutputStream
, SPXPushAudioOutputStream
, l'Synthesizing
evento e SPXAudioDataStream
dell'SDK di Voce per abilitare lo streaming.
Prendendo AudioDataStream
come esempio:
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
}
Pre-connettere e riutilizzare SpeechSynthesizer
L'SDK di Voce usa un WebSocket per comunicare con il servizio.
Idealmente, la latenza di rete deve essere un tempo di percorrenza della route (RTT).
Se la connessione è stata appena stabilita, la latenza di rete include tempo aggiuntivo per stabilire la connessione.
La creazione di una connessione WebSocket richiede l'handshake TCP, l'handshake SSL, la connessione HTTP e l'aggiornamento del protocollo, che introduce un ritardo di tempo.
Per evitare la latenza di connessione, è consigliabile pre-connettersi e riutilizzare SpeechSynthesizer
.
Pre-connessione
Per effettuare la pre-connessione, stabilire una connessione al servizio Voce quando si sa che la connessione è necessaria entro breve. Ad esempio, se si sta creando un bot vocale nel client, è possibile pre-connettersi al servizio di sintesi vocale quando l'utente inizia a parlare e chiamare SpeakTextAsync
quando il testo di risposta del bot è pronto.
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];
Nota
Se il testo è disponibile, è sufficiente chiamare SpeakTextAsync
per sintetizzare l'audio. L'SDK gestirà la connessione.
Reuse SpeechSynthesizer
Un altro modo per ridurre la latenza di connessione consiste nel riutilizzare SpeechSynthesizer
in modo da non dover creare una nuovo SpeechSynthesizer
per ogni sintesi.
È consigliabile usare il pool di oggetti nello scenario del servizio. Vedere il codice di esempio per C# e Java.
Trasmettere audio compresso sulla rete
Quando la rete è instabile o con larghezza di banda limitata, le dimensioni del payload influiscono anche sulla latenza. Nel frattempo, un formato audio compresso consente di far risparmiare larghezza di banda di rete agli utenti, cosa particolarmente utile per gli utenti di dispositivi mobili.
Sono supportati molti formati compressi, tra cui opus
, webm
, mp3
, silk
e così via. Vedere l'elenco completo in SpeechSynthesisOutputFormat.
Ad esempio, la velocità in bit del formato Riff24Khz16BitMonoPcm
è di 384 kbps, mentre Audio24Khz48KBitRateMonoMp3
è di solo 48 kbps.
L'SDK di Voce usa automaticamente un formato compresso per la trasmissione quando viene impostato un formato di output pcm
.
Per Linux e Windows, GStreamer
è necessario per abilitare la funzionalità.
Fare riferimento a questa istruzione per installare e configurare GStreamer
per l'SDK di Voce.
Per Android, iOS e macOS, non è necessaria alcuna configurazione aggiuntiva a partire dalla versione 1.20.
Streaming di testo di input
Lo streaming di testo consente l'elaborazione di testo in tempo reale per la generazione rapida di audio. È perfetto per la vocalizzazione di testo dinamico, ad esempio la lettura di output da modelli di intelligenza artificiale come GPT in tempo reale. Questa funzionalità riduce al minimo la latenza e migliora la fluidità e la velocità di risposta degli output audio, rendendola ideale per applicazioni interattive, eventi live e dialoghi basati sull'intelligenza artificiale reattiva.
Come usare lo streaming di testo
Con l'SDK di Voce, lo streaming di testo è supportato in C#, C++ e Python.
Per usare la funzionalità di streaming di testo, connettersi all'endpoint websocket V2: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2
Vedere il codice di esempio per l'impostazione dell'endpoint:
// 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"));
Passaggi chiave
Creare una richiesta di flusso di testo: usare
SpeechSynthesisRequestInputType.TextStream
per avviare un flusso di testo.Impostare le proprietà globali: regolare direttamente le impostazioni, ad esempio il formato di output e il nome della voce, perché la funzionalità gestisce input di testo parziali e non supporta SSML. Per istruzioni su come impostarle, vedere il codice di esempio seguente. Le voci di sintesi vocale OpenAI non sono supportate dalla funzionalità di streaming del testo. Vedere questa tabella lingua per il supporto completo della lingua.
// Set output format speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm); // Set a voice name SpeechConfig.SetProperty(PropertyId.SpeechServiceConnection_SynthVoice, "en-US-AvaMultilingualNeural");
Trasmettere il testo: per ogni blocco di testo generato da un modello GPT, usare
request.InputStream.Write(text);
per inviare il testo al flusso.Chiudere il flusso: dopo il completamento dell'output del modello GPT, chiudere il flusso usando
request.InputStream.Close();
.
Per un'implementazione dettagliata, vedere il codice di esempio in GitHub
Per usare la funzionalità di streaming di testo, connettersi all'endpoint websocket V2: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2
Vedere il codice di esempio per l'impostazione dell'endpoint:
# 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"))
Passaggi chiave
Creare una richiesta di flusso di testo: usare
speechsdk.SpeechSynthesisRequestInputType.TextStream
per avviare un flusso di testo.Impostare le proprietà globali: regolare direttamente le impostazioni, ad esempio il formato di output e il nome della voce, perché la funzionalità gestisce input di testo parziali e non supporta SSML. Per istruzioni su come impostarle, vedere il codice di esempio seguente. Le voci di sintesi vocale OpenAI non sono supportate dalla funzionalità di streaming del testo. Vedere questa tabella lingua per il supporto completo della lingua.
# set a voice name speech_config.speech_synthesis_voice_name = "en-US-AvaMultilingualNeural"
Trasmettere il testo: per ogni blocco di testo generato da un modello GPT, usare
request.input_stream.write(text)
per inviare il testo al flusso.Chiudere il flusso: dopo il completamento dell'output del modello GPT, chiudere il flusso usando
request.input_stream.close()
.
Per un'implementazione dettagliata, vedere il codice di esempio in GitHub.
Il codice di esempio C++ non è attualmente disponibile. Per il codice di esempio che mostra come usare lo streaming di testo, vedere:
Per il codice di esempio che mostra come usare lo streaming di testo, vedere:
Per il codice di esempio che mostra come usare lo streaming di testo, vedere:
Altri suggerimenti
Memorizzare nella cache i file CRL
SDK di Voce usa i file CRL per controllare la certificazione. La memorizzazione nella cache dei file CRL fino alla loro scadenza consente di evitare di scaricare i file CRL ogni volta. Per informazioni dettagliate, vedere Come configurare OpenSSL per Linux.
Usare l'SDK di Voce più recente
Continuiamo a migliorare le prestazioni dell'SDK Voce, provare quindi a usare quello più recente nell'applicazione.
Linee guida per i test di carico
È possibile usare il test di carico per testare la capacità del servizio di sintesi vocale e latenza. Ecco alcune linee guida:
- Il servizio di sintesi vocale ha la possibilità di attivare il ridimensionamento automatico, ma richiede tempo aumentare il numero di istanze. Se la concorrenza viene aumentata in breve tempo, il client può ottenere una latenza prolungata o il codice di errore
429
(troppe richieste). È quindi consigliabile aumentare la concorrenza gradualmente nel test di carico. Vedere questo articolo per altri dettagli, in particolare questo esempio di modelli di carico di lavoro. - È possibile sfruttare l'esempio usando il pool di oggetti (C# e Java) per il test di carico e ottenere i numeri di latenza. È possibile modificare i turni di test e la concorrenza dell'esempio per soddisfare la concorrenza di destinazione.
- Il servizio presenta una limitazione della quota in base al traffico reale, pertanto, se si vuole eseguire test di carico con la concorrenza più elevata rispetto al traffico reale, connettersi prima del test.
Passaggi successivi
- Vedere gli esempi su GitHub