Menor latência de síntese de fala usando o SDK de Fala
Neste artigo, apresentamos as práticas recomendadas para reduzir a latência da sintetização de conversão de texto em fala e trazer o melhor desempenho aos 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 estiver disponível, chame o 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. Confira 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.
O SDK de Fala usa automaticamente um formato compactado para transmissão quando um formato de saída pcm
é 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.
Streaming de texto de entrada
O streaming de texto permite o processamento de texto em tempo real para a geração rápida de áudio. É perfeito para vocalização dinâmica de texto, como ler saídas de modelos de IA como o GPT em tempo real. Esse recurso minimiza a latência e melhora a fluidez e a capacidade de resposta das saídas de áudio, tornando-o ideal para aplicativos interativos, eventos ao vivo e diálogos responsivos impulsionados por IA.
Como usar o streaming de texto
Há suporte para streaming de texto em C#, C++ e Python com o SDK de Fala.
Para usar o recurso de streaming de texto, conecte-se ao ponto de extremidade websocket V2: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2
Consulte o código de exemplo para definir o ponto de extremidade:
// 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"));
Principais etapas
Crie uma solicitação de fluxo de texto: use
SpeechSynthesisRequestInputType.TextStream
para iniciar um fluxo de texto.Defina propriedades globais: ajuste configurações como formato de saída e nome da voz diretamente, pois o recurso lida com entradas de texto parciais e não dá suporte a SSML. Consulte o código de exemplo a seguir para obter instruções sobre como configurá-los. O recurso de streaming de texto não dá suporte à conversão de texto em fala do OpenAI. Consulte esta tabela de idiomas para obter suporte completo ao idioma.
// Set output format speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm); // Set a voice name SpeechConfig.SetProperty(PropertyId.SpeechServiceConnection_SynthVoice, "en-US-AvaMultilingualNeural");
Transmita seu texto: para cada parte de texto gerado por um modelo GPT, use
request.InputStream.Write(text);
para enviar o texto para o fluxo.Feche o fluxo: depois que o modelo GPT concluir sua saída, feche o fluxo usando
request.InputStream.Close();
.
Para obter uma implementação detalhada, consulte o código de exemplo no GitHub
Para usar o recurso de streaming de texto, conecte-se ao ponto de extremidade websocket V2: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2
Consulte o código de exemplo para definir o ponto de extremidade:
# 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"))
Principais etapas
Crie uma solicitação de fluxo de texto: use
speechsdk.SpeechSynthesisRequestInputType.TextStream
para iniciar um fluxo de texto.Defina propriedades globais: ajuste configurações como formato de saída e nome da voz diretamente, pois o recurso lida com entradas de texto parciais e não dá suporte a SSML. Consulte o código de exemplo a seguir para obter instruções sobre como configurá-los. O recurso de streaming de texto não dá suporte à conversão de texto em fala do OpenAI. Consulte esta tabela de idiomas para obter suporte completo ao idioma.
# set a voice name speech_config.speech_synthesis_voice_name = "en-US-AvaMultilingualNeural"
Transmita seu texto: para cada parte de texto gerado por um modelo GPT, use
request.input_stream.write(text)
para enviar o texto para o fluxo.Feche o fluxo: depois que o modelo GPT concluir sua saída, feche o fluxo usando
request.input_stream.close()
.
Para obter uma implementação detalhada, consulte o código de exemplo no GitHub.
O código de exemplo em C++ não está disponível no momento. Para obter o código de exemplo que mostra como usar o streaming de texto, consulte:
Para obter o código de exemplo que mostra como usar o streaming de texto, consulte:
Para obter o código de exemplo que mostra como usar o streaming de texto, consulte:
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