Menor latencia de síntesis de voz mediante el SDK de Voz
La latencia de síntesis es crítica para las aplicaciones. En este artículo, se presentarán los procedimientos recomendados para reducir la latencia y que los usuarios finales tengan el mejor rendimiento posible.
Habitualmente, la latencia se mide por first byte latency
y finish latency
, como se muestra a continuación:
Latencia | Descripción | Clave de la propiedad SpeechSynthesisResult |
---|---|---|
latencia del primer byte | Indica el retraso entre el inicio de la tarea de síntesis y la recepción del primer fragmento de datos de audio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
latencia de finalización | Indica el retraso entre el inicio de la tarea de síntesis y la recepción de todos los datos de audio sintetizados. | SpeechServiceResponse_SynthesisFinishLatencyMs |
El SDK de Voz coloca las duraciones de la latencia en la colección Properties de SpeechSynthesisResult
. En el siguiente código de ejemplo se muestran estos 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;
Latencia | Descripción | Clave de la propiedad SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el primer fragmento de audio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el todo el audio sintetizado. | SpeechServiceResponse_SynthesisFinishLatencyMs |
El SDK de Voz ha medido las latencias y las coloca en el bolsa de propiedades de SpeechSynthesisResult
. Consulte los códigos siguientes para obtenerlas.
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;
Latencia | Descripción | Clave de la propiedad SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el primer fragmento de audio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el todo el audio sintetizado. | SpeechServiceResponse_SynthesisFinishLatencyMs |
El SDK de Voz ha medido las latencias y las coloca en el bolsa de propiedades de SpeechSynthesisResult
. Consulte los códigos siguientes para obtenerlas.
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();
Latencia | Descripción | Clave de la propiedad SpeechSynthesisResult |
---|---|---|
first byte latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el primer fragmento de audio. | SpeechServiceResponse_SynthesisFirstByteLatencyMs |
finish latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el todo el audio sintetizado. | SpeechServiceResponse_SynthesisFinishLatencyMs |
El SDK de Voz ha medido las latencias y las coloca en el bolsa de propiedades de SpeechSynthesisResult
. Consulte los códigos siguientes para obtenerlas.
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
Latencia | Descripción | Clave de la propiedad SPXSpeechSynthesisResult |
---|---|---|
first byte latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el primer fragmento de audio. | SPXSpeechServiceResponseSynthesisFirstByteLatencyMs |
finish latency |
Indica el retraso entre que se inicia la síntesis y que se recibe el todo el audio sintetizado. | SPXSpeechServiceResponseSynthesisFinishLatencyMs |
El SDK de Voz ha medido las latencias y las coloca en el bolsa de propiedades de SPXSpeechSynthesisResult
. Consulte los códigos siguientes para obtenerlas.
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 latencia del primer byte es menor que la latencia de finalización en la mayoría de los casos. La latencia del primer byte es independiente de la longitud del texto, mientras que la latencia de finalización aumenta con la longitud del texto.
Lo ideal es minimizar la latencia experimentada por el usuario (la latencia antes de que el usuario escucha el sonido) al tiempo de recorrido de una ruta de red, más la latencia del primer fragmento de audio del servicio de síntesis de voz.
Streaming
El streaming es fundamental para reducir la latencia. El código de cliente puede iniciar la reproducción cuando se recibe el primer fragmento de audio. En un escenario de servicio, puede reenviar los fragmentos de audio inmediatamente a los clientes, en lugar de esperar todo el audio.
Puede usar PullAudioOutputStream
, PushAudioOutputStream
, el evento Synthesizing
y AudioDataStream
del SDK de Voz para habilitar el streaming.
Tomando AudioDataStream
como ejemplo:
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.");
}
}
}
}
Puede usar PullAudioOutputStream
, PushAudioOutputStream
, el evento Synthesizing
y AudioDataStream
del SDK de Voz para habilitar el streaming.
Tomando AudioDataStream
como ejemplo:
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;
}
Puede usar PullAudioOutputStream
, PushAudioOutputStream
, el evento Synthesizing
y AudioDataStream
del SDK de Voz para habilitar el streaming.
Tomando AudioDataStream
como ejemplo:
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);
}
Puede usar PullAudioOutputStream
, PushAudioOutputStream
, el evento Synthesizing
y AudioDataStream
del SDK de Voz para habilitar el streaming.
Tomando AudioDataStream
como ejemplo:
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)
Puede usar SPXPullAudioOutputStream
, SPXPushAudioOutputStream
, el evento Synthesizing
y SPXAudioDataStream
del SDK de Voz para habilitar el streaming.
Tomando AudioDataStream
como ejemplo:
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
}
Conexión previa y reutilización de SpeechSynthesizer
El SDK de Voz usa un WebSocket para comunicarse con el servicio.
Lo ideal sería que la latencia de red fuera un tiempo de recorrido de ruta (RTT).
Si la conexión se acaba de establecer, la latencia de red incluye tiempo adicional para establecer la conexión.
El establecimiento de una conexión WebSocket necesita el protocolo de enlace TCP, el protocolo de enlace SSL, la conexión HTTP y la actualización del protocolo, lo que introduce un retraso.
Para evitar la latencia de la conexión, se recomienda realizar una conexión previa y volver a usar SpeechSynthesizer
.
Antes de conectar
Para realizar una conexión previa, establezca una conexión con el servicio Voz cuando sepa que la conexión se necesitará pronto. Por ejemplo, si va a crear un bot de voz en el cliente, puede conectarse previamente al servicio de síntesis de voz cuando el usuario empiece a hablar y llamar a SpeakTextAsync
cuando el texto de respuesta del bot esté listo.
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:
Si el texto de síntesis está disponible, solo hay que llamar a SpeakTextAsync
para sintetizar el audio. El SDK controlará la conexión.
Reutilización de SpeechSynthesizer
Otra forma de reducir la latencia de la conexión es volver a usar SpeechSynthesizer
para que no sea necesario crear SpeechSynthesizer
para cada síntesis.
Se recomienda usar un grupo de objetos en el escenario de servicio; consulte nuestro código de ejemplo para C# y Java.
Transmisión de audio comprimido a través de la red
Si la red no es estable o tiene un límite de ancho de banda, el tamaño de la carga también afecta a la latencia. Entretanto, un formato de audio comprimido ayuda a ahorrar el ancho de banda de red de los usuarios, algo que resulta especialmente valioso para los usuarios de dispositivos móviles.
Se admiten muchos formatos comprimidos, como opus
, webm
, mp3
, silk
, etc. La lista completa se puede ver en SpeechSynthesisOutputFormat.
Por ejemplo, la velocidad de bits del formato Riff24Khz16BitMonoPcm
es de 384 kbps, mientras que Audio24Khz48KBitRateMonoMp3
solo cuesta 48 kbps.
Nuestro SDK de Voz usará automáticamente un formato comprimido para la transmisión cuando se establece un formato de salida pcm
.
Para Linux y Windows, se necesita GStreamer
para habilitar esta característica.
Consulte esta instrucción para instalar y configurar GStreamer
para el SDK de Voz.
Para Android, iOS y macOS, no se necesita ninguna configuración adicional a partir de la versión 1.20.
Otras sugerencias
Almacenamiento en caché de archivos CRL
El SDK de Voz usa archivos CRL para comprobar la certificación. El almacenamiento en caché de los archivos CRL hasta que hayan expirado le ayuda a evitar tener que descargar archivos CRL cada vez. Para más información, consulte Configuración de OpenSSL para Linux.
Uso del SDK de Voz más reciente
El rendimiento del SDK de Voz sigue mejorando, así que intente usar el SDK de Voz más reciente en la aplicación.
Directriz de pruebas de carga
Puede usar la prueba de carga para probar la latencia y capacidad del servicio de síntesis de voz. Estas son algunas directrices:
- El servicio de síntesis de voz tiene la capacidad de escalabilidad automática, pero tarda tiempo en escalar horizontalmente. Si la simultaneidad aumenta en un breve tiempo, es posible que el cliente obtenga una latencia larga o el código de error
429
(demasiadas solicitudes). Por consiguiente, se recomienda aumentar la simultaneidad paso a paso en la prueba de carga. Consulte este artículo para obtener más información, en particular, este ejemplo de patrones de carga de trabajo. - Puede usar nuestro ejemplo con el grupo de objetos (C# y Java) para la prueba de carga y para obtener los números de latencia. Puede modificar los turnos de prueba y la simultaneidad en el ejemplo para satisfacer la simultaneidad de destino.
- El servicio tiene una limitación de cuota basada en el tráfico real; por tanto, si quiere realizar la prueba de carga con la simultaneidad mayor que el tráfico real, conéctese antes de la prueba.
Pasos siguientes
- Consulte los ejemplos en GitHub