Latência de sintetização de voz mais reduzida com o SDK de Voz
Neste artigo, apresentamos as melhores práticas para reduzir a latência de síntese de texto para fala e trazer o melhor desempenho para seus usuários finais.
Normalmente, medimos a latência por first byte latency
e finish latency
, da seguinte forma:
Latência | Description | Chave da propriedade SpeechSynthesisResult |
---|---|---|
latência do primeiro byte | Indica o intervalo de tempo entre o início da tarefa de síntese e o recebimento do primeiro bloco de dados de áudio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
latência de conclusão | Indica o intervalo 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 Properties do 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;
Latência | Description | Chave da propriedade SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o intervalo de tempo entre o início da síntese e o recebimento do primeiro bloco de áudio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica o intervalo de tempo entre o início da síntese e todo o áudio sintetizado é recebido. | SpeechServiceResponse_SynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no saco de propriedades do SpeechSynthesisResult
. Consulte os seguintes códigos 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;
Latência | Description | Chave da propriedade SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o intervalo de tempo entre o início da síntese e o recebimento do primeiro bloco de áudio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica o intervalo de tempo entre o início da síntese e todo o áudio sintetizado é recebido. | SpeechServiceResponse_SynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no saco de propriedades do SpeechSynthesisResult
. Consulte os seguintes códigos 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();
Latência | Description | Chave da propriedade SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o intervalo de tempo entre o início da síntese e o recebimento do primeiro bloco de áudio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica o intervalo de tempo entre o início da síntese e todo o áudio sintetizado é recebido. | SpeechServiceResponse_SynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no saco de propriedades do SpeechSynthesisResult
. Consulte os seguintes códigos 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
Latência | Description | Chave de propriedade SPXSpeechSynthesisResult |
---|---|---|
first byte latency |
Indica o intervalo de tempo entre o início da síntese e o recebimento do primeiro bloco de áudio. | SPXSpeechServiceResponseSynthesisFirstByteLatencyMs |
finish latency |
Indica o intervalo de tempo entre o início da síntese e todo o áudio sintetizado é recebido. | SPXSpeechServiceResponseSynthesisFinishLatencyMs |
O SDK de Fala mediu as latências e as colocou no saco de propriedades do SPXSpeechSynthesisResult
. Consulte os seguintes códigos 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 término 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.
Idealmente, queremos minimizar a latência experimentada pelo 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 primeira latência de bloco de áudio do serviço de síntese de fala.
Transmissão
A transmissão em fluxo é fundamental para reduzir a latência. O código do cliente pode iniciar a reprodução quando o primeiro segmento de áudio for recebido. Num cenário de serviço, pode reencaminhar os segmentos de áudio imediatamente para os clientes em vez de esperar pelo áudio inteiro.
Você pode usar o PullAudioOutputStream
PushAudioOutputStream
, , Synthesizing
evento 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 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 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 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 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.
Idealmente, a latência da rede deve ser um tempo de viagem de rota (RTT).
Se a conexão for recém-estabelecida, a latência da rede inclui tempo extra para estabelecer a conexão.
O estabelecimento de uma conexão websocket precisa do handshake TCP, handshake SSL, conexão HTTP e atualização de protocolo, o que introduz atraso de tempo.
Para evitar a latência da conexão, recomendamos pré-conectar e reutilizar o SpeechSynthesizer
.
Pré-conexão
Para pré-conectar, estabeleça uma conexão com o serviço de Fala quando souber que a conexão é necessária em breve. Por exemplo, se você estiver criando um bot de fala no cliente, poderá se pré-conectar ao serviço de síntese de fala quando o usuário começar a falar e ligar 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];
Nota
Se o texto estiver disponível, basta ligar SpeakTextAsync
para sintetizar o áudio. O SDK tratará da conexão.
Reutilize o SpeechSynthesizer
Outra maneira de reduzir a latência da conexão é reutilizar o SpeechSynthesizer
para que você não precise criar um novo SpeechSynthesizer
para cada síntese.
Recomendamos o uso do pool de objetos no cenário de serviço. Veja nosso código de exemplo para C# e Java.
Transmita áudio comprimido através da rede
Quando a rede é instável ou com largura de banda limitada, o tamanho da carga útil também afeta a latência. Enquanto isso, um formato de áudio comprimido ajuda a economizar a largura de banda da rede dos usuários, o que é especialmente valioso para usuários móveis.
Suportamos muitos formatos comprimidos, incluindo opus
, webm
, mp3
, silk
, e assim por diante, veja a lista completa em SpeechSynthesisOutputFormat.
Por exemplo, a taxa de bits do formato é de Riff24Khz16BitMonoPcm
384 kbps, enquanto Audio24Khz48KBitRateMonoMp3
custa apenas 48 kbps.
O SDK de fala usa automaticamente um formato compactado para transmissão quando um formato de pcm
saída é definido.
Para Linux e Windows, GStreamer
é necessário habilitar esse recurso.
Consulte esta instrução para instalar e configurar GStreamer
o SDK de fala.
Para Android, iOS e macOS, nenhuma configuração extra é necessária a partir da versão 1.20.
Fluxo de texto de entrada
O streaming de texto permite o processamento de texto em tempo real para geração rápida de áudio. É perfeito para vocalização dinâmica de texto, como leitura de saídas de modelos de IA como 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 orientados por IA.
Como usar o streaming de texto
O streaming de texto é suportado em C#, C++ e Python com o Speech SDK.
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 passos
Criar uma solicitação de fluxo de texto: use
SpeechSynthesisRequestInputType.TextStream
para iniciar um fluxo de texto.Definir propriedades globais: ajuste configurações como formato de saída e nome de voz diretamente, pois o recurso lida com entradas parciais de texto e não suporta SSML. Consulte o código de exemplo a seguir para obter instruções sobre como defini-los. As vozes de texto para fala do OpenAI não são suportadas pelo recurso de streaming de texto. Consulte esta tabela de idiomas para obter suporte completo a idiomas.
// Set output format speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm); // Set a voice name SpeechConfig.SetProperty(PropertyId.SpeechServiceConnection_SynthVoice, "en-US-AvaMultilingualNeural");
Transmitir seu texto: para cada bloco de texto gerado a partir de um modelo GPT, use
request.InputStream.Write(text);
para enviar o texto para o fluxo.Feche o fluxo: Quando 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 passos
Criar uma solicitação de fluxo de texto: use
speechsdk.SpeechSynthesisRequestInputType.TextStream
para iniciar um fluxo de texto.Definir propriedades globais: ajuste configurações como formato de saída e nome de voz diretamente, pois o recurso lida com entradas parciais de texto e não suporta SSML. Consulte o código de exemplo a seguir para obter instruções sobre como defini-los. As vozes de texto para fala do OpenAI não são suportadas pelo recurso de streaming de texto. Consulte esta tabela de idiomas para obter suporte completo a idiomas.
# set a voice name speech_config.speech_synthesis_voice_name = "en-US-AvaMultilingualNeural"
Transmitir seu texto: para cada bloco de texto gerado a partir de um modelo GPT, use
request.input_stream.write(text)
para enviar o texto para o fluxo.Feche o fluxo: Quando 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 C++ não está disponível agora. Para obter o código de exemplo que mostra como usar o fluxo de texto, consulte:
Para obter o código de exemplo que mostra como usar o fluxo de texto, consulte:
Para obter o código de exemplo que mostra como usar o fluxo de texto, consulte:
Outras dicas
Arquivos CRL em cache
O Speech SDK usa arquivos CRL para verificar a certificação. Armazenar em cache os arquivos CRL até expirar ajuda a evitar o download de arquivos CRL sempre. Veja Como configurar o OpenSSL para Linux para obter detalhes.
Usar o SDK de fala mais recente
Continuamos a melhorar o desempenho do SDK de Voz, por isso tente utilizar o mais recente na sua aplicação.
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 dimensionamento automático, mas leva tempo para ser dimensionado. Se a simultaneidade for aumentada em um curto espaço de tempo, o cliente pode obter latência longa ou
429
código de erro (muitas solicitações). Então, recomendamos que você aumente sua simultaneidade passo a passo 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 obtenção dos números de latência. Você pode modificar as voltas de teste e simultaneidade na amostra para atender à simultaneidade de destino.
- O serviço tem limitação de cota com base no tráfego real, portanto, se você quiser realizar o teste de carga com a simultaneidade maior do que o seu tráfego real, conecte-se antes do seu teste.
Próximos passos
- Veja os exemplos no GitHub