Поделиться через


Уменьшение задержки синтеза речи с помощью пакета SDK службы "Речь"

В этой статье мы введем рекомендации по снижению задержки синтеза речи и повышению производительности для конечных пользователей.

Обычно задержка измерятся параметрами first byte latency и finish latency следующим образом:

Задержка Описание Ключ свойства SpeechSynthesisResult
first byte client latency Указывает задержку времени между началом синтеза и первым блоком звука, полученным на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish client latency Указывает задержку времени между началом синтеза и получением всего синтезированного звука на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFinishLatencyMs
network latency Задержка сети между клиентом и службой TTS Azure. SpeechServiceResponse_SynthesisNetworkLatencyMs
first byte service latency Указывает задержку времени между запросом синтеза, полученным службой Azure TTS, и возвратом первого блока аудио. SpeechServiceResponse_SynthesisServiceLatencyMs

В пакете SDK для службы "Речь" значения длительности задержки находятся в коллекции свойств SpeechSynthesisResult. Эти значения показаны в образце кода ниже.

var result = await synthesizer.SpeakTextAsync(text);
Console.WriteLine($"first byte client latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs)} ms");
Console.WriteLine($"finish client latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs)} ms");
Console.WriteLine($"network latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisNetworkLatencyMs)} ms");
Console.WriteLine($"first byte service latency: \t{result.Properties.GetProperty(PropertyId.SpeechServiceResponse_SynthesisServiceLatencyMs)} ms");
// you can also get the result id, and send to us when you need help for diagnosis
var resultId = result.ResultId;
Задержка Описание Ключ свойства SpeechSynthesisResult
first byte client latency Указывает задержку времени между началом синтеза и первым блоком звука, полученным на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish client latency Указывает задержку времени между началом синтеза и получением всего синтезированного звука на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFinishLatencyMs
network latency Задержка сети между клиентом и службой TTS Azure. SpeechServiceResponse_SynthesisNetworkLatencyMs
first byte service latency Указывает на задержку времени между получением запросом на синтез службой TTS Azure и возвращением первого аудиофрагмента. SpeechServiceResponse_SynthesisServiceLatencyMs

SDK для речи измеряет задержки и помещает их в набор свойств SpeechSynthesisResult. Обратитесь к следующим кодам, чтобы получить их.

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));
auto firstByteLatency = std::stoi(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_SynthesisNetworkLatencyMs));
auto firstByteLatency = std::stoi(result->Properties.GetProperty(PropertyId::SpeechServiceResponse_SynthesisServiceLatencyMs));
// you can also get the result id, and send to us when you need help for diagnosis
auto resultId = result->ResultId;
Задержка Описание Ключ свойства SpeechSynthesisResult
first byte client latency Указывает задержку времени между началом синтеза и первым блоком звука, полученным на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish client latency Указывает задержку времени между началом синтеза и получением всего синтезированного звука на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFinishLatencyMs
network latency Задержка сети между клиентом и службой TTS Azure. SpeechServiceResponse_SynthesisNetworkLatencyMs
first byte service latency Указывает задержку времени между службой TTS Azure после получения запроса на синтез и возвратом первого звукового блока. SpeechServiceResponse_SynthesisServiceLatencyMs

SDK для распознавания речи измеряет задержки и помещает их в контейнер свойств SpeechSynthesisResult. Используйте следующие коды, чтобы получить их.

SpeechSynthesisResult result = synthesizer.SpeakTextAsync(text).get();
System.out.println("first byte client latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs) + " ms.");
System.out.println("finish client latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs) + " ms.");
System.out.println("network latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisNetworkLatencyMs) + " ms.");
System.out.println("first byte service latency: \t" + result.getProperties().getProperty(PropertyId.SpeechServiceResponse_SynthesisServiceLatencyMs) + " ms.");
// you can also get the result id, and send to us when you need help for diagnosis
String resultId = result.getResultId();
Задержка Описание Ключ свойства SpeechSynthesisResult
first byte client latency Указывает задержку времени между началом синтеза и первым блоком звука, полученным на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFirstByteLatencyMs
finish client latency Указывает задержку времени между началом синтеза и получением всего синтезированного звука на клиенте, включая задержку сети. SpeechServiceResponse_SynthesisFinishLatencyMs
network latency Задержка сети между клиентом и службой TTS Azure. SpeechServiceResponse_SynthesisNetworkLatencyMs
first byte service latency Указывает задержку времени между службой TTS Azure, полученной запросом синтеза, и возвращается первый блок звука. SpeechServiceResponse_SynthesisServiceLatencyMs

Пакет SDK службы "Речь" измеряет задержки и помещает их в контейнер свойств SpeechSynthesisResult. В коде ниже показано, как их получить.

result = synthesizer.speak_text_async(text).get()
first_byte_client_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisFirstByteLatencyMs))
finished_client_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisFinishLatencyMs))
network_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisNetworkLatencyMs))
first_byte_service_latency = int(result.properties.get_property(speechsdk.PropertyId.SpeechServiceResponse_SynthesisServiceLatencyMs))
# you can also get the result id, and send to us when you need help for diagnosis
result_id = result.result_id
Задержка Описание Ключ свойства SPXSpeechSynthesisResult
first byte client latency Указывает задержку времени между началом синтеза и первым блоком звука, полученным на клиенте, включая задержку сети. SPXSpeechServiceResponseSynthesisFirstByteLatencyMs
finish client latency Указывает задержку времени между началом синтеза и получением всего синтезированного звука на клиенте, включая задержку сети. SPXSpeechServiceResponseSynthesisFinishLatencyMs
network latency Задержка сети между клиентом и службой TTS Azure. SPXSpeechServiceResponseSynthesisNetworkLatencyMs
first byte service latency Указывает временную задержку между получением службой Azure TTS запроса на синтез и возвращением первого звукового фрагмента. SPXSpeechServiceResponseSynthesisServiceLatencyMs

Средства разработки для речевых технологий измеряют задержки и помещают их в пакет свойств SPXSpeechSynthesisResult. Обратитесь к следующим кодам, чтобы их получить.

SPXSpeechSynthesisResult *speechResult = [speechSynthesizer speakText:text];
int firstByteClientLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisFirstByteLatencyMs]];
int finishedClientLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisFinishLatencyMs]];
int networkLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisNetworkLatencyMs]];
int firstByteServiceLatency = [intString [speechResult.properties getPropertyById:SPXSpeechServiceResponseSynthesisServiceLatencyMs]];
// you can also get the result id, and send to us when you need help for diagnosis
NSString *resultId = result.resultId;

Первая задержка байтов ниже, чем задержка завершения в большинстве случаев. Задержка первого байта не зависит от длины текста, а задержка завершения растет по мере увеличения этой длины.

В идеальном случае требуется минимизировать задержку для пользователя (время перед тем, как пользователь услышит звук) до времени однократного прохождения сетевого маршрута плюс задержка первого звукового фрагмента от службы синтеза речи.

Стриминг

Потоковая передача — важнейший фактор снижения задержки. Клиентский код может начать воспроизведение при получении первого звукового фрагмента. В сценарии обслуживания вы можете сразу пересылать клиентам звуковые фрагменты, не дожидаясь формирования всего звукового пакета.

Для реализации потоковой передачи можно использовать PullAudioOutputStream, PushAudioOutputStream, Synthesizing событие и AudioDataStream пакета SDK службы "Речь".

Рассмотрим AudioDataStream в качестве примера:

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.");
            }
        }
    }
}

Для активации потоковой передачи можно использовать PullAudioOutputStream, PushAudioOutputStream, Synthesizingсобытие и AudioDataStream из Speech SDK.

Рассмотрим AudioDataStream в качестве примера:

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;
}

Для реализации потоковой передачи можно использовать PullAudioOutputStream, PushAudioOutputStream, Synthesizing событие и AudioDataStream из SDK речевых функций.

Рассмотрим AudioDataStream в качестве примера:

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);
}

Вы можете использовать PullAudioOutputStream, PushAudioOutputStream, Synthesizing событие и AudioDataStream в SDK для потоковой передачи речи.

Рассмотрим AudioDataStream в качестве примера:

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)

Для включения потоковой передачи можно использовать SPXPullAudioOutputStream, SPXPushAudioOutputStream, Synthesizingсобытие и SPXAudioDataStream Speech SDK.

Рассмотрим AudioDataStream в качестве примера:

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
}

Предварительное подключение и повторное использование SpeechSynthesizer

SDK "Речь" использует WebSocket для обмена данными со службой. В идеале задержка сети должна составлять время одного сетевого маршрута (RTT). Если подключение установлено недавно, задержка сети включает дополнительное время для установки подключения. Для установки подключения WebSocket требуется подтверждение TCP, подтверждение SSL, HTTP-подключение и обновление протокола, что ведет к появлению задержки. Чтобы избежать задержки подключения, рекомендуется предварительно подключить и повторно использовать SpeechSynthesizer.

Предварительное подключение

Чтобы предварительно подключиться, установите подключение к службе "Речь", когда вы знаете, что подключение необходимо в ближайшее время. Например, если вы создаете бот речи в клиенте, вы можете предварительно подключиться к службе синтеза речи, когда пользователь начнет говорить, и вызвать SpeakTextAsync , когда текст ответа бота готов.

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];

Примечание.

Если текст доступен, просто вызовите SpeakTextAsync, чтобы синтезировать аудио. Пакет SDK обеспечит подключение.

Повторное использование синтезатора речи

Снизить задержку подключения также можно путем повторного использования SpeechSynthesizer, чтобы не создавать новый объект SpeechSynthesizer для каждой операции синтеза. Рекомендуется использовать пул объектов в сценарии службы. Ознакомьтесь с нашим примером кода для C# и Java.

Передача сжатого звука по сети

Когда сеть нестабильна или доступна ограниченная пропускная способность, размер полезных данных также влияет на задержку. В то же время сжатие аудио позволяет уменьшить нагрузку на пропускную способность сети, что особенно важно для мобильных пользователей.

Поддерживается множество форматов сжатия, включая opus, webm, mp3, silk и т. п. (полный список см. в разделе SpeechSynthesisOutputFormat). Например, скорость для формата Riff24Khz16BitMonoPcm составляет 384 кбит/с, а для Audio24Khz48KBitRateMonoMp3 — всего 48 кбит/с. Speech SDK автоматически использует сжатый формат для передачи, когда установлен выходной pcm формат. В Linux и Windows для включения этой функции потребуется GStreamer. Инструкции по установке и настройке пакета SDK для службы "Речь" см. GStreamer. Для Android, iOS и macOS дополнительная конфигурация не требуется, начиная с версии 1.20.

Потоковая трансляция вводимого текста

Потоковая передача текста позволяет обрабатывать текст в режиме реального времени для быстрого создания звука. Это идеально подходит для динамической вокализации текста, например чтения выходных данных из моделей ИИ, таких как GPT в режиме реального времени. Эта функция сводит к минимуму задержку и повышает гибкость и скорость отклика звуковых выходных данных, что делает его идеальным для интерактивных приложений, трансляций и адаптивных диалогов на основе искусственного интеллекта.

Использование потоковой передачи текста

Потоковая передача текста поддерживается в C#, C++ и Python с использованием Speech SDK.

Чтобы использовать функцию потоковой передачи текста, подключитесь к конечной точке websocket версии 2: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2

См. пример кода для настройки конечной точки:

// 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"));

Ключевые шаги

  1. Создайте запрос текстового потока: используйте SpeechSynthesisRequestInputType.TextStream для запуска текстового потока.

  2. Задайте глобальные свойства: настройте такие параметры, как формат вывода и имя голоса напрямую, так как функция обрабатывает частичные текстовые входные данные и не поддерживает SSML. Ознакомьтесь со следующим примером кода, чтобы узнать, как задать их. Голоса текстовой речи OpenAI не поддерживаются функцией текстового стриминга. См. эту таблицу языков для полной поддержки языка.

    // Set output format
    speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm);
    
    // Set a voice name
    SpeechConfig.SetProperty(PropertyId.SpeechServiceConnection_SynthVoice, "en-US-AvaMultilingualNeural");
    
  3. Потоковая передача текста: для каждого фрагмента текста, созданного из модели GPT, используйте request.InputStream.Write(text); для отправки текста в поток.

  4. Закройте поток: после завершения выходных данных модели GPT закройте поток с помощью request.InputStream.Close();.

Подробные сведения о реализации см. в примере кода на GitHub

Чтобы использовать функцию потоковой передачи текста, подключитесь к конечной точке websocket версии 2: wss://{region}.tts.speech.microsoft.com/cognitiveservices/websocket/v2

См. пример кода для настройки конечной точки:

# 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"))

Ключевые шаги

  1. Создайте запрос на создание текстового потока: используйте speechsdk.SpeechSynthesisRequestInputType.TextStream для инициирования текстового потока.

  2. Задайте глобальные свойства: настройте такие параметры, как формат вывода и имя голоса напрямую, так как функция обрабатывает частичные текстовые входные данные и не поддерживает SSML. Ознакомьтесь со следующим примером кода, чтобы узнать, как задать их. Голоса озвучивания OpenAI не поддерживаются функцией потоковой передачи текста. См. эту таблицу языков для полной поддержки языка.

    # set a voice name
    speech_config.speech_synthesis_voice_name = "en-US-AvaMultilingualNeural"
    
  3. Потоковая передача текста: для каждого фрагмента текста, созданного из модели GPT, используйте request.input_stream.write(text) для отправки текста в поток.

  4. Закройте поток: после завершения выходных данных модели GPT закройте поток с помощью request.input_stream.close().

Подробные сведения о реализации см. в примере кода на сайте GitHub.

Пример кода C++ сейчас недоступен. Пример кода, демонстрирующий использование потоковой передачи текста, см. в следующих примерах:

Пример кода, демонстрирующий использование потоковой передачи текста, см. в следующих примерах:

Пример кода, демонстрирующий использование потоковой передачи текста, см. в следующих примерах:

Другие советы

Кэширование CRL-файлов

Пакет SDK "Речь" использует файлы списков отзыва сертификатов (CRL) для проверки сертификата. Хранение CRL-файлов в кэше до истечения срока их действия помогает избежать их повторной загрузки каждый раз. Дополнительные сведения см. в статье Настройка OpenSSL для Linux.

Используйте последнюю версию пакета SDK для преобразования речи

Мы продолжаем улучшать производительность Speech SDK, поэтому старайтесь использовать его последнюю версию в вашем приложении.

Советы в отношении нагрузочного теста

С помощью нагрузочного теста можно проверить емкость и задержку службы синтеза речи. Ниже приведены некоторые рекомендации.

  • Служба синтеза речи имеет возможность автомасштабирования, но требует времени для горизонтального масштабирования. Если параллелизм увеличивается в течение короткого времени, клиент может получить длинную задержку или 429 код ошибки (слишком много запросов). Поэтому мы рекомендуем пошагово увеличивать параллелизм в ходе нагрузочного теста. Дополнительные сведения см. в этой статье, особенно этот пример шаблонов рабочей нагрузки.
  • Вы можете использовать наш пример с помощью пула объектов (C# и Java) для нагрузочного теста и получения чисел задержки. Вы можете изменить шаги и параллелизм тестов в примере в соответствии с целевым параллелизмом.
  • Служба имеет ограничение квоты на основе реального трафика, поэтому если вы хотите выполнить нагрузочный тест с параллелизмом выше реального трафика, подключитесь перед тестом.

Следующие шаги