Condividi tramite


Ridurre la latenza della sintesi vocale con Speech SDK

La latenza di sintesi è fondamentale per le applicazioni. In questo articolo verranno presentate le procedure consigliate per ridurre la latenza 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 di 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 dell'intero dati audio sintetizzato. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK inserisce le durate di latenza nella raccolta Properties 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 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 l'intero audio sintetizzato. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK ha misurato le latenze e le inserisce nel contenitore delle proprietà di SpeechSynthesisResult. Per ottenerli, 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 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 l'intero audio sintetizzato. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK ha misurato le latenze e le inserisce nel contenitore delle proprietà di SpeechSynthesisResult. Per ottenerli, 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 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 l'intero audio sintetizzato. SpeechServiceResponse_SynthesisFinishLatencyMs

Speech SDK ha misurato le latenze e le inserisce nel contenitore delle proprietà di SpeechSynthesisResult. Per ottenerli, 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 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 l'intero audio sintetizzato. SPXSpeechServiceResponseSynthesisFinishLatencyMs

Speech SDK ha misurato le latenze e le inserisce nel contenitore delle proprietà di SPXSpeechSynthesisResult. Per ottenerli, 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 prima latenza di byte è inferiore alla latenza di fine nella maggior parte dei casi. La prima latenza dei 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 vissuto dall'utente (la latenza prima che l'utente senta il suono) a un tempo di viaggio di 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 l'evento PullAudioOutputStream, PushAudioOutputStream, Synthesizing e AudioDataStream di Speech SDK 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'eventoSynthesizing e AudioDataStream speech SDK 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'eventoSynthesizing e AudioDataStream speech SDK 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'eventoSynthesizing e AudioDataStream speech SDK 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'eventoSynthesizing e SPXAudioDataStream speech SDK 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

Speech SDK usa un websocket per comunicare con il servizio. Idealmente, la latenza di rete deve essere un tempo di viaggio di route (RTT). Se la connessione viene 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 a breve. Ad esempio, se si sta creando un bot di riconoscimento 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 di sintesi è disponibile, è sufficiente chiamare SpeakTextAsync per sintetizzare l'audio. L'SDK gestirà la connessione.

Riutilizzare SpeechSynthesizer

Un altro modo per ridurre la latenza di connessione consiste nel riutilizzare in SpeechSynthesizer modo da non dover creare un nuovo SpeechSynthesizer oggetto 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 in rete

Quando la rete è instabile o con larghezza di banda limitata, anche le dimensioni del payload influiscono sulla latenza. Nel frattempo, un formato audio compresso consente di risparmiare la larghezza di banda di rete degli utenti, che è particolarmente utile per gli utenti mobili.

Sono supportati molti formati compressi, tra cui opus, webmmp3, silke così via, vedere l'elenco completo in SpeechSynthesisOutputFormat. Ad esempio, la velocità in bit del Riff24Khz16BitMonoPcm formato è di 384 kbps, mentre Audio24Khz48KBitRateMonoMp3 costa solo 48 kbps. Speech SDK userà automaticamente un formato compresso per la trasmissione quando viene impostato un pcm formato di output. Per Linux e Windows, GStreamer è necessario per abilitare questa funzionalità. Fare riferimento a questa istruzione per installare e configurare GStreamer Speech SDK. Per Android, iOS e macOS, non è necessaria alcuna configurazione aggiuntiva a partire dalla versione 1.20.

Altri suggerimenti

Memorizzare nella cache i file CRL

Speech SDK usa i file CRL per controllare la certificazione. La memorizzazione nella cache dei file CRL fino alla scadenza consente di evitare di scaricare i file CRL ogni volta. Per informazioni dettagliate, vedere Come configurare OpenSSL per Linux.

Usare Speech SDK 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à e la latenza del servizio di sintesi vocale. Ecco alcune linee guida:

  • Il servizio di sintesi vocale ha la possibilità di ridimensionare automaticamente, ma richiede tempo per aumentare il numero di istanze. Se la concorrenza viene aumentata in breve tempo, il client potrebbe ottenere una latenza 429 o un codice di errore lungo (troppe richieste). È quindi consigliabile aumentare la concorrenza passo dopo passo nel test di carico. Per altri dettagli, vedere questo articolo , in particolare questo esempio di modelli di carico di lavoro.
  • È possibile usare 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 nell'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 superiore al traffico reale, connettersi prima del test.

Passaggi successivi