Год назад для собственных нужд я написал обертку Yandex SpeechKit на Python, она получилась настолько простая и универсальная, что грех не поделиться : )

Кстати, вот она: Yandex SpeechKit Python SDK!

Для того, чтобы начать распознавать и синтезировать речь нам понадобится Python 3.6 или выше. Установить модуль можно из PyPI:

pip install speechkit

Разбираемся с консолью яндекса

Прежде чем писать код, стоит сказать пару слов про то, как устроена платформа Yandex Cloud. Первым делом, Вам нужно зайти в консоль cloud.yandex.ru c помощью акаунта яндекса или создать такой. Затем, создать платежный аккаунт, активировав пробный период. В итоге получится что-то такое:

Стартовая страница консоли управления
Стартовая страница консоли управления

Получить идентификатор каталога можно из URL страницы каталога в консоли управления:

https://console.cloud.yandex.ru/folders/b1gd129pp9ha0vnvf5g7 , где b1gd129pp9ha0vnvf5g7 — это идентификатор каталога. Он нам скоро понадобиться.

Для запроса к API SpeechKit нужно получить IAM-токен, Вам об этом беспокоиться не нужно, это уже "вшито" в библиотеку, нужно лишь передать исходные данные для создания сессии. Проще всего получить IAM-токен из OAuth-токена, Вашего аккаунта на яндексе, для этого перейдите по ссылке, нажмите Разрешить и скопируйте полученный OAuth-токен. В серьезных проектах лучше использовать авторизацию по JWT-токену или API-ключу для сервисного аккаунта.

Синтез речи

Итак, у нас есть все, чтобы программа обрела голос, осталось лишь написать несколько строчек:

from speechkit import Session, SpeechSynthesis

oauth_token = "AQAAAAAsHJkgAAhjwXshft5QgUuRkX0Wubhjlk"
catalog_id = "b1gd129pp9ha0vnvf5g7"

# Экземпляр класса `Session` можно получать из разных данных 
session = Session.from_yandex_passport_oauth_token(oauth_token, catalog_id)

# Создаем экземляр класса `SpeechSynthesis`, передавая `session`,
# который уже содержит нужный нам IAM-токен 
# и другие необходимые для API реквизиты для входа
synthesizeAudio = SpeechSynthesis(session)

# Метод `.synthesize()` позволяет синтезировать речь и сохранять ее в файл
synthesizeAudio.synthesize(
    'out.wav', text='Привет мир!',
    voice='oksana', format='lpcm', sampleRateHertz='16000'
)

# `.synthesize_stream()` возвращает объект типа `io.BytesIO()` с аудиофайлом
audio_data = synthesizeAudio.synthesize_stream(
		text='Привет мир, снова!',
    voice='oksana', format='lpcm', sampleRateHertz='16000'
)

Вот так просто! Значение и полный список параметров можно посмотреть в документации яндекса или в документации библиотеки.

Теперь этот аудиофайл можно проиграть с помощью плеера или, например, с помощью библиотеки PyAudio. Кстати, в этом случае разумнее будет передавать объект с данными, а не сохранять их в файл, вот так:

import pyaudio


def pyaudio_play_audio_function(audio_data, num_channels=1, 
                                sample_rate=16000, chunk_size=4000) -> None:
    """
    Воспроизводит бинарный объект с аудио данными в формате lpcm (WAV)
    
    :param bytes audio_data: данные сгенерированные спичкитом
    :param integer num_channels: количество каналов, спичкит генерирует 
        моно дорожку, поэтому стоит оставить значение `1`
    :param integer sample_rate: частота дискретизации, такая же 
        какую вы указали в параметре sampleRateHertz
    :param integer chunk_size: размер семпла воспроизведения, 
        можно отрегулировать если появится потрескивание
    """
    p = pyaudio.PyAudio()
    stream = p.open(
        format=pyaudio.paInt16,
        channels=num_channels,
        rate=sample_rate,
        output=True,
        frames_per_buffer=chunk_size
    )

    try:
        for i in range(0, len(audio_data), chunk_size):
            stream.write(audio_data[i:i + chunk_size])
    finally:
        stream.stop_stream()
        stream.close()
        p.terminate()

sample_rate = 16000 # частота дискретизации должна 
                    # совпадать при синтезе и воспроизведении
audio_data = synthesizeAudio.synthesize_stream(
    text='Привет мир, снова и снова!',
    voice='oksana', format='lpcm', sampleRateHertz=sample_rate
)
# Воспроизводим синтезированный файл
pyaudio_play_audio_function(audio_data, sample_rate=sample_rate)

Теперь мы можем проговорить голосом яндекса что-нибудь прямо из программы, осталось научиться распознавать команды и можно прикручивать голосовое управление к чайнику, кофеварке, утюгу и пылесосу!

Распознавание речи

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

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

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

Мы же будем использовать самый простой способ — синхронное распознавание. Для этого запишем сообщение в файл и сохраним в формате WAV.

from speechkit import ShortAudioRecognition


# Читаем файл
with open('voice.wav', 'rb') as f:
    data = f.read()
    
# Создаем экземпляр класса с помощью `session` полученного ранее
recognizeShortAudio = ShortAudioRecognition(session)

# Передаем файл и его формат в метод `.recognize()`, 
# который возвращает строку с текстом
text = recognizeShortAudio.recognize(
		data, format='lpcm', sampleRateHertz='48000')
print(text)

Ура! Теперь можно прикрутить голосовые команды к чему угодно. Вот ссылка на полное описание метода, если Вы хотите передавать файл в формате, отличном от WAV, например.

Напоследок покажу, как с помощью PyAudio записать голос, который можно будет потом передать в спичкит для распознавания:

import io
import wave
import pyaudio


def record_audio(seconds, sample_rate, 
                 chunk_size=4000, num_channels=1) -> bytes:
    """
    Записывает аудио данной продолжительности и возвращает бинарный объект с данными
    
    :param integer seconds: Время записи в секундах
    :param integer sample_rate: частота дискретизации, такая же 
        какую вы указали в параметре sampleRateHertz
    :param integer chunk_size: размер семпла записи
    :param integer num_channels: количество каналов, в режимер синхронного
        распознавания спичкит принимает моно дорожку, 
        поэтому стоит оставить значение `1`
    :return: Возвращает объект BytesIO с аудио данными в формате WAV
    :rtype: bytes
    """

    p = pyaudio.PyAudio()
    stream = p.open(
        format=pyaudio.paInt16,
        channels=num_channels,
        rate=sample_rate,
        input=True,
        frames_per_buffer=chunk_size
    )
    frames = []
    try:
        for i in range(0, int(sample_rate / chunk_size * seconds)):
            data = stream.read(chunk_size)
            frames.append(data)
    finally:
        stream.stop_stream()
        stream.close()
        p.terminate()
        
    container = io.BytesIO()
    wf = wave.open(container, 'wb')
    wf.setnchannels(num_channels)
    wf.setsampwidth(p.get_sample_size(pyaudio.paInt16))
    wf.setframerate(sample_rate)
    wf.writeframes(b''.join(frames))
    container.seek(0)
    return container

sample_rate = 16000 # частота дискретизации должна 
                    # совпадать при записи и распознавании
    
# Записываем аудио продолжительностью 3 секунды
data = record_audio(3, sample_rate)

# Отправляем на распознавание
text = recognizeShortAudio.recognize(
    data, format='lpcm', sampleRateHertz=sample_rate)
print(text)

На этом все. Надеюсь, вам пригодилась библиотека : ) Буду рад пул-реквестам!

Спасибо за внимание!

Комментарии (2)


  1. slavius
    10.08.2022 12:42

    Возможно это Вам будет интересно

    https://www.silero.ai/

    Решаем реальные проблемы с использованием Speech-To-Text, Text-To-Speech

    Есть и API.

    Качество генерации голоса просто поражает.


  1. denn70
    11.08.2022 22:42

    Спасибо за библиотеку. Почему то при проигрывании файла выскакивает ошибка

    OSError: [Errno -9997] Invalid sample rate