Туториал по синтезу речи

Подготовка

В данном туториале будем запускать примеры из репозитория с примерами.

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

1
2
export VOICEKIT_API_KEY="PUT_YOUR_API_KEY_HERE"
export VOICEKIT_SECRET_KEY="PUT_YOUR_SECRET_KEY_HERE"

Клонируем репозиторий с примерами:

1
git clone --recursive https://github.com/TinkoffCreditSystems/voicekit-examples.git

Устанавливаем зависимости:

1
2
sudo apt-get install python3 python-pyaudio python3-pyaudio
sudo python3 -m pip install -r requirements/all.txt

Методы совершения запросов

Для синтеза речи в сервисе реализовано 2 метода: Synthesize и StreamingSynthesize.

Метод "Synthesize" позволяет сгенерировать аудио целиком, обычно полезен только для генерации аудиофайлов (поэтому здесь будет только один пример).

Остальные примеры посвящены методу "StreamingSynthesize".
Этот метод нужен для синтеза речи в реальном времени, т. е., позволяет получать синтезированный аудиопоток для произвольного текста по мере синтеза.

Метод StreamingSynthesize()

Шлём несжатое аудио

Пример 1: метод StreamingSynthesize() для LINEAR16
1
$ ./tts_streaming_synthesize_linear16_to_wav.py

Синтезируем в Linear16 48KHz и сохраняем в WAV.

Импортируем модули:

1
2
3
4
5
from tinkoff.cloud.tts.v1 import tts_pb2_grpc, tts_pb2
from auth import authorization_metadata
import grpc
import os
import wave

Получаем конфигурацию:

1
2
3
4
5
6
# можно получать из переменных среды, либо заменить в сниппете
endpoint = os.environ.get("VOICEKIT_ENDPOINT") or "tts.tinkoff.ru:443"
api_key = os.environ["VOICEKIT_API_KEY"]
secret_key = os.environ["VOICEKIT_SECRET_KEY"]

sample_rate = 48000

Реализуем создание запроса:

1
2
3
4
5
6
7
8
def build_request():
    return tts_pb2.SynthesizeSpeechRequest(
        input=tts_pb2.SynthesisInput(text="И мысли тоже тяжелые и медлительные, падают неторопливо и редко одна за другой, точно песчинки в разленившихся песочных часах."),
        audio_config=tts_pb2.AudioConfig(
            audio_encoding=tts_pb2.LINEAR16,
            sample_rate_hertz=sample_rate,
        ),
    )

Открываем ".wav" файл для записи синтезированного аудио.
Шлём запрос и записываем полученные сэмплы.
Попутно печатаем приблизительную длительность аудио:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
with wave.open("synthesised.wav", "wb") as f:
    f.setframerate(sample_rate)
    f.setnchannels(1)
    f.setsampwidth(2)

    stub = tts_pb2_grpc.TextToSpeechStub(grpc.secure_channel(endpoint, grpc.ssl_channel_credentials()))
    request = build_request()
    metadata = authorization_metadata(api_key, secret_key, "tinkoff.cloud.tts")
    responses = stub.StreamingSynthesize(request, metadata=metadata)
    for key, value in responses.initial_metadata():
        if key == "x-audio-num-samples":
            print("Estimated audio duration is " + str(int(value)/sample_rate) + " seconds")
            break
    for stream_response in responses:
        f.writeframes(stream_response.audio_chunk)

Исходный код примера

Воспроизводим на лету

Пример 2: воспроизводим на лету
1
$ ./tts_streaming_synthesize_linear16_playback.py

Для удобства тестирования синтезируем и воспроизводим на звукой карте.

Импортируем

pyaudio
:

1
import pyaudio

Открываем устройство воспроизведения на звуковой карте:

1
2
pyaudio_lib = pyaudio.PyAudio()
f = pyaudio_lib.open(output=True, channels=1, format=pyaudio.paInt16, rate=sample_rate)

Пишем сэмплы в устройство:

1
2
for stream_response in responses:
    f.write(stream_response.audio_chunk)

Исходный код примера

Кодеки

Пример 3: синтезируем в Opus
1
$ ./tts_streaming_synthesize_raw_opus_playback.py

Синтезируем в Opus и воспроизводим на звукой карте.
Sample rate для Opus не имеет значения, но примерная длительность аудио передаётся в сэмплах исходя из заданного значения sample rate.

Импортируем модуль для декорирования Opus:

1
import opuslib

Актуализируем сэмпл рэйт:

1
sample_rate = 16000

Выставляем кодек:

1
            audio_encoding=tts_pb2.RAW_OPUS,

Инициализируем декодер:

1
opus_decoder = opuslib.Decoder(sample_rate, 1)

Пишем декодированные фрэймы в устройство:

1
    f.write(opus_decoder.decode(stream_response.audio_chunk, 5760)) # 5760 - максимальный размер фрэйма: в адекватном биндинге libopus для Питона вычислялось бы автоматически в зависимости от частоты дискретизации

Исходный код примера