Compartilhar via


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 Synthesizingevento 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 Synthesizingevento 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 Synthesizingevento 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 Synthesizingevento 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