До этого самого времени, я никогда не писал код на Pyhton и Node JS. И поэтому мне было очень сложно скрестить эти системы. И поэтому решил написать об этом пост, так как готовых примеров в Яндексе нет за исключением MRCP про которого было немало нелестных отзывов от пользователей этой системы, но пруфы в данное время предоставить не могу. Я решил не пользоваться этим костылем и изобрести велосипед на костылях сам. Для этого мне в помощь прослужила сама документация с Яндекса и некоторые примеры с StackOverflow.

Код распознает и повторяет что вы говорили на русском некий эхо.

Сначала читаем документ Яндекса о потоковом распознавании следуем все четко по документации выбираем язык Python, так как с Node JS у меня не получилось читать поток из Asteriska EAGI file descrition 3, а так в принципе можете сами попробовать и, если получится написать об этом статью в хабре и дать мне на него ссылку.

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

;;YANDEX SPEACH
exten = 444,1,Answer
exten = 444,n,EAGI(test.py)
exten = 444,n,Hangup

Затем меняем расположение папки AGI, в файле /etc/asterisk/asterisk.conf

На ту папку, где установлен у нас Яндекс cloudapi/output (из документации яндекса). Изменения в этом файле начинают действовать, как я заметил, только после перезагрузки Asterisk, через службы или kill.

Внимание, был подвох со знаком! его надо было удалить.

Далее сам код, который лежит в папке cloudapi output test.py

#!/usr/bin/python3.6
#coding=utf8
import argparse
from asterisk.agi import *

import requests
import grpc
import os
import yandex.cloud.ai.stt.v2.stt_service_pb2 as stt_service_pb2
import yandex.cloud.ai.stt.v2.stt_service_pb2_grpc as stt_service_pb2_grpc
agi = AGI()
CHUNK_SIZE = 4000
folder = "берете в яндексе"
token = "берете в яндексе"
text = ""


def gen(folder_id):
    # Задать настройки распознавания.
    specification = stt_service_pb2.RecognitionSpec(
        language_code='ru-RU',
        profanity_filter=True,
        model='general',
        partial_results=True,
        audio_encoding='LINEAR16_PCM',
        sample_rate_hertz=8000
    )

    streaming_config = stt_service_pb2.RecognitionConfig(specification=specification, folder_id=folder_id)

    # Отправить сообщение с настройками распознавания.
    yield stt_service_pb2.StreamingRecognitionRequest(config=streaming_config)

    # Прочитать аудиофайл и отправить его содержимое порциями.
    with os.fdopen(3, 'rb') as f:
        data = f.read(CHUNK_SIZE)
        while data != b'':
            yield stt_service_pb2.StreamingRecognitionRequest(audio_content=data)
            data = f.read(CHUNK_SIZE)


def run(folder_id, iam_token):
    # Установить соединение с сервером.
    cred = grpc.ssl_channel_credentials()
    channel = grpc.secure_channel('stt.api.cloud.yandex.net:443', cred)
    stub = stt_service_pb2_grpc.SttServiceStub(channel)

    # Отправить данные для распознавания.
    it = stub.StreamingRecognize(gen(folder_id), metadata=(('authorization', 'Bearer %s' % iam_token),))

    # Обработать ответы сервера и вывести результат в консоль.
    try:
        cont = True
        while cont:
                agi.verbose('START START START')
                for r in it:
                    try:
                        agi.verbose('Start chunk: ')
                        for alternative in r.chunks[0].alternatives:
                            agi.verbose(alternative.text)
                            if(alternative.text == "привет"):
                                cont = true
                                break
                        if r.chunks[0].final:
                            agi.verbose("FINAL")
                            with open("/var/lib/asterisk/agi-bin/cloudapi/output/echo123.raw", "wb") as f:
                                for audio_content in synthesize(alternative.text):
                                    f.write(audio_content)
                            agi.stream_file("/var/lib/asterisk/agi-bin/cloudapi/output/echo123")
                    except LookupError:
                        agi.verbose('Not available chunks')
    except grpc._channel._Rendezvous as err:
        agi.verbose('Error code %s, message: %s' % (err._state.code, err._state.details))


def synthesize(text):
    url = 'https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize'
    headers = {
        'Authorization': 'Bearer '+token
    }

    data = {
        'text': text,
        'lang': 'ru-RU',
        'folderId': folder,
        'sampleRateHertz': '8000',
        'format': 'lpcm'
    }

    with requests.post(url, headers=headers, data=data, stream=True) as resp:
        if resp.status_code != 200:
            raise RuntimeError("Invalid response received: code: %d, message: %s" % (resp.status_code, resp.text))

        for chunk in resp.iter_content(chunk_size=None):
            yield chunk



if __name__ == '__main__':
    run(folder,token)


У меня нет желания писать, как устанавливать питон и его зависимости все просто через pip install. Так как эта статья не для самых маленьких. Кстати, Asterisk распространяемый в Ubuntu репозиториях, но я не смог из него получить поток звука, через EAGI. Получил только после собирания Asterisk из исходников с соответствующей настройкой menuconfig включением eagi потоков

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


  1. arheops
    25.07.2023 19:42

    О. Еще один велосипед. Есть же сокет и unistream