Menor latência de síntese de fala usando o SDK de Fala
A latência de síntese é crítica para seus aplicativos. Neste artigo, apresentaremos as melhores práticas para reduzir a latência e trazer o melhor desempenho para os usuários finais.
Normalmente, medimos a latência por first byte latency
e finish latency
, da seguinte forma:
Latency | Descrição | Chave de propriedade SpeechSynthesisResult |
---|---|---|
latência de primeiro byte | Indica o atraso de tempo entre o início da tarefa de síntese e o recebimento da primeira parte dos dados de áudio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
latência de conclusão | Indica o atraso de tempo entre o início da tarefa de síntese e o recebimento de todos os dados de áudio sintetizados. | SpeechServiceResponse_SynthesisFinishLatencyMs |
O SDK de Fala coloca as durações de latência na coleção Propriedades de SpeechSynthesisResult
. O código de exemplo a seguir mostra esses valores.
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 | Descrição | Chave de propriedade SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o atraso de tempo entre o início da síntese e o momento quando a primeira parte de áudio é recebida. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica o atraso de tempo entre o início da síntese e momento quando todo o áudio sintetizado é recebido. | SpeechServiceResponse_SynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no pacote de propriedades de SpeechSynthesisResult
. Consulte os códigos a seguir para obtê-los.
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 | Descrição | Chave de propriedade SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o atraso de tempo entre o início da síntese e o momento quando a primeira parte de áudio é recebida. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica o atraso de tempo entre o início da síntese e momento quando todo o áudio sintetizado é recebido. | SpeechServiceResponse_SynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no pacote de propriedades de SpeechSynthesisResult
. Consulte os códigos a seguir para obtê-los.
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 | Descrição | Chave de propriedade SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o atraso de tempo entre o início da síntese e o momento quando a primeira parte de áudio é recebida. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica o atraso de tempo entre o início da síntese e momento quando todo o áudio sintetizado é recebido. | SpeechServiceResponse_SynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no pacote de propriedades de SpeechSynthesisResult
. Consulte os códigos a seguir para obtê-los.
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 | Descrição | Chave de propriedade SPXSpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o atraso de tempo entre o início da síntese e o momento quando a primeira parte de áudio é recebida. | SPXSpeechServiceResponseSynthesisFirstByteLatencyMs |
finish latency |
Indica o atraso de tempo entre o início da síntese e momento quando todo o áudio sintetizado é recebido. | SPXSpeechServiceResponseSynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no pacote de propriedades de SPXSpeechSynthesisResult
. Consulte os códigos a seguir para obtê-los.
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;
A latência do primeiro byte é menor do que a latência de conclusão na maioria dos casos. A latência do primeiro byte é independente do comprimento do texto, enquanto a latência de conclusão aumenta com o comprimento do texto.
O ideal é minimizar a latência de experiência do usuário (a latência antes que o usuário ouça o som) para um tempo de viagem de rota de rede mais a latência da primeira parte do áudio do serviço de síntese de fala.
Streaming
O streaming é essencial para reduzir a latência. O código do cliente pode iniciar a reprodução quando a primeira parte do áudio é recebida. Em um cenário de serviço, você pode encaminhar imediatamente as partes de áudio para seus clientes em vez de aguardar o áudio todo.
Você pode usar PullAudioOutputStream
, PushAudioOutputStream
, evento Synthesizing
e AudioDataStream
do SDK de Fala para habilitar o streaming.
Tomando AudioDataStream
como exemplo:
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.");
}
}
}
}
Você pode usar o PullAudioOutputStream
, PushAudioOutputStream
, o Synthesizing
evento e o AudioDataStream
do SDK de Fala para habilitar o streaming.
Tomando AudioDataStream
como exemplo:
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;
}
Você pode usar o PullAudioOutputStream
, PushAudioOutputStream
, o Synthesizing
evento e o AudioDataStream
do SDK de Fala para habilitar o streaming.
Tomando AudioDataStream
como exemplo:
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);
}
Você pode usar o PullAudioOutputStream
, PushAudioOutputStream
, o Synthesizing
evento e o AudioDataStream
do SDK de Fala para habilitar o streaming.
Tomando AudioDataStream
como exemplo:
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)
Você pode usar o SPXPullAudioOutputStream
, SPXPushAudioOutputStream
, o Synthesizing
evento e o SPXAudioDataStream
do SDK de Fala para habilitar o streaming.
Tomando AudioDataStream
como exemplo:
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
}
Pré-conectar e reutilizar o SpeechSynthesizer
O SDK de Fala usa um websocket para se comunicar com o serviço.
O ideal é que a latência de rede seja de um RTT (tempo de viagem de rota).
Se a conexão tiver sido estabelecida recentemente, a latência de rede inclui tempo extra para estabelecer a conexão.
O estabelecimento de uma conexão websocket precisa do handshake TCP, do handshake SSL, da conexão HTTP e da atualização do protocolo, o que introduz atraso de tempo.
Para evitar a latência de conexão, recomendamos a pré-conexão e a reutilização do SpeechSynthesizer
.
Antes de conectar-se
Para fazer a pré-conexão, estabeleça uma conexão com o serviço de Fala quando você souber que a conexão será necessária em breve. Por exemplo, se você estiver criando um bot de fala no cliente, poderá fazer a pré-conexão ao serviço de síntese de fala quando o usuário começar a falar, e chamar SpeakTextAsync
quando o texto de resposta do bot estiver 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];
Observação
Se o texto sintetizado estiver disponível, basta chamar SpeakTextAsync
para sintetizar o áudio. O SDK manipulará a conexão.
Reutilizar o SpeechSynthesizer
Outra maneira de reduzir a latência de conexão é reutilizar o SpeechSynthesizer
para que você não precise criar um novo SpeechSynthesizer
para cada síntese.
É recomendável usar o pool de objetos no cenário de serviço. Consulte nosso código de exemplo para C# e Java.
Transmitir áudio compactado pela rede
Quando a rede está instável ou com largura de banda limitada, o tamanho do conteúdo também afeta a latência. Enquanto isso, um formato de áudio compactado ajuda a economizar a largura de banda de rede dos usuários, o que é especialmente valioso para usuários de dispositivos móveis.
Damos suporte a muitos formatos compactados, incluindo opus
, webm
, mp3
, silk
, e outros. Consulte a lista completa em SpeechSynthesisOutputFormat.
Por exemplo, a taxa de bits do formato Riff24Khz16BitMonoPcm
é de 384 kbps, enquanto Audio24Khz48KBitRateMonoMp3
é de apenas 48 kbps.
Nosso SDK de Fala usará automaticamente um formato compactado para transmissão quando um formato de saída pcm
estiver definido.
Para o Linux e o Windows, o GStreamer
é necessário habilitar esse recurso.
Consulte esta instrução para instalar e configurar GStreamer
para o SDK de Fala.
Para Android, iOS e macOS, nenhuma configuração extra é necessária a partir da versão 1.20.
Outras dicas
Armazenar arquivos CRL em cache
O SDK de Fala usa arquivos CRL para verificar a certificação. Armazenar arquivos CRL em cache até que eles expirem ajuda a evitar o download desses arquivos a cada vez que os usar. Confira Como configurar o OpenSSL para Linux para conhecer mais detalhes.
Usar o SDK de Fala mais recente
Estamos continuamente melhorando o desempenho do SDK de Fala; portanto, procure usar o SDK de Fala mais recente em seu aplicativo.
Diretriz de teste de carga
Você pode usar o teste de carga para testar a capacidade e a latência do serviço de síntese de fala. Aqui estão algumas diretrizes:
- O serviço de síntese de fala tem a capacidade de dimensionar automaticamente, mas leva tempo para escalar horizontalmente. Se a simultaneidade for aumentada em um curto período de tempo, o cliente poderá enfrentar longa latência ou obter o código de erro
429
(muitas solicitações). Portanto, recomendamos que você aumente sua simultaneidade aos poucos no teste de carga. Consulte este artigo para obter mais detalhes, especialmente este exemplo de padrões de carga de trabalho. - Você pode usar nosso exemplo usando o pool de objetos (C# e Java) para teste de carga e para obter os números de latência. Você pode modificar os turnos de teste e a simultaneidade no exemplo para atender à sua simultaneidade de destino.
- O serviço tem uma limitação de cota com base no tráfego real, portanto, se você quiser executar o teste de carga com a simultaneidade maior do que o tráfego real, conecte-se antes do teste.
Próximas etapas
- Veja os exemplos no GitHub