Голосовой ассистент на python

в 6:18, , рубрики: python, голосовой ассистент

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

Не буду долго тянуть с приветствием и сразу перейду к делу. Этот гайд является по сути пошаговой инструкцией для создания базового кода, который можно будет потом расширять и дополнять. Голосовой ассистент - вещь весьма удобная, особенно если эту вещь можно настроить под свои нужды. Спасибо языку python, имеющему бесчисленное множество библиотек и фреймворков, позволяющих писать почти что угодно под почти любую платформу.

Для нашего же случая подойдут библиотеки silero (для tts), vosk (для stt) и sounddevice (для воспроизведения аудио)
сразу установим их: pip install silero vosk sounddevice

Преобразование текста в речь

Библиотека silero может преобразовывать текст в речь на множестве языков, нас же интересует русский язык, поэтому создадим небольшой класс на основе официальной документации:

import sounddevice as sd
import torch
import time

# константы голосов, поддерживаемых в silero
SPEAKER_AIDAR   = "aidar"
SPEAKER_BAYA    = "baya"
SPEAKER_KSENIYA = "kseniya"
SPEAKER_XENIA   = "xenia"
SPEAKER_RANDOM  = "random"

# константы девайсов для работы torch
DEVICE_CPU    = "cpu"
DEVICE_CUDA   = "cuda" 
DEVICE_VULKAN = "vulkan"
DEVICE_OPENGL = "opengl"
DEVICE_OPENCL = "opencl"

class TTS:
    def __init__(
            self, speaker: str = SPEAKER_BAYA, 
            device: str        = DEVICE_CPU, 
            samplerate: int    = 48_000
        ):
        
        # подгружаем модель 
        self.__MODEL__, _ = torch.hub.load(
            repo_or_dir="snakers4/silero-models",
            model="silero_tts",
            language="ru",
            speaker="ru_v3"
        )
        self.__MODEL__.to(torch.device(device))

        self.__SPEAKER__ = speaker
        self.__SAMPLERATE__ = samplerate
    
    def text2speech(self, text: str):
        # генерируем аудио из текста
        audio = self.__MODEL__.apply_tts(
            text=text,               
            speaker=self.__SPEAKER__,
            sample_rate=self.__SAMPLERATE__, 
            put_accent=True,
            put_yo=True
        )

        # проигрываем то что получилось
        sd.play(audio, samplerate=self.__SAMPLERATE__)
        time.sleep((len(audio)/self.__SAMPLERATE__))
        sd.stop()

При инициализации подгружается нейронная модель, если её нет на компьютере, то она будет скачана, иначе она будет взята из кэша загрузок.
Сама же нейронная модель работает на torch, поэтому можно будет запускать её не на процессоре а на видеокарте, для лучшей скорости работы. Правда мой ноутбук с RTX 3050 Ti mobile хоть и позволяет произвести запуск на видеокарте, всё равно на процессоре модель работает быстрее, поэтому поэкспериментируйте, чтобы найти наилучший способ.

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

Для распознавания речи будем использовать библиотеку vosk, сначала перейдём на официальный сайт с моделями распознавания и скачаем необходимую модель распознавания речи, обратите внимание что моделей две и та модель, которая больше весит, будет соответственно лучше распознавать речь, но и при этом будет медленнее подгружаться.
Создайте папку в вашей рабочей области и распакуйте скачанный архив в эту папку.

Далее создадим ещё один небольшой класс на основе документации:

import sounddevice as sd
import vosk
import sys
import queue
import json

class STT:
    def __init__(self, modelpath: str = "model", samplerate: int = 16000):
        self.__REC__ = vosk.KaldiRecognizer(vosk.Model(modelpath), samplerate)
        self.__Q__ = queue.Queue()
        self.__SAMPLERATE__ = samplerate

    
    def q_callback(self, indata, _, __, status):
        if status:
            print(status, file=sys.stderr)
        self.__Q__.put(bytes(indata))

    def listen(self, executor: callable):
        with sd.RawInputStream(
                samplerate=self.__SAMPLERATE__, 
                blocksize=8000, 
                device=1, 
                dtype='int16',
                channels=1, 
                callback=self.q_callback
            ):
            while True:
                data = self.__Q__.get()
                if self.__REC__.AcceptWaveform(data):
                    executor(json.loads(self.__REC__.Result())["text"])

При инициализации параметр modelpath это путь к вашей скачанной модели, а параметр executor в функции listen это функция, которая будет что-то делать с распознанным текстом.

Главный файл

На основе двух написанных классов создадим файл main.py со следующим кодом:

from fuzzywuzzy import fuzz

from tts import TTS
from stt import STT

commandsList = []

def equ(text, needed):
    return fuzz.ratio(text, needed) >= 70

def execute(text: str):
    print(f"> {text}")
    
    if equ(text, "расскажи анекдот"):
        text = "какой то анекдот!"
        tts.text2speech(text)
        print(f"- {text}")
    
    elif equ(text, "что ты умеешь"):
        text = "я умею всё, чему ты мен+я науч+ил!"
        tts.text2speech(text)
        print(f"- {text}")
    
    elif equ(text, "выключи"):
        text = "надеюсь, я не стану про+ектом, кот+орый ты забр+осишь!"
        tts.text2speech(text)
        print(f"- {text}")
        raise SystemExit

tts = TTS()
stt = STT(modelpath="model_small")

print("listen...")
stt.listen(execute)

В коде выше используется библиотека fuzzywuzzy, позволяющая нечётко сравнивать строки, данная библиотека необязательна, но лучше с ней чем без неё.

Также стоит отметить, что н+екоторые фр+азы для озв+учки я п+ишу вот так, плюсы в тексте позволяют указать ударение в слове

Мини-заключение

На этом костяк нашего проекта завершён, осталось лишь добавить необходимый функционал и всё готово!

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

Автор: PlayingPlate6667

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js