Распознавание речи в Telegram «на лету»

от автора

Задача распознавания голосовых сообщений в Telegram уже давно не новая. На эту тему написано много статей, разработано немало Telegram-ботов. С некоторыми решениями я ознакомился во время работы над функцией распознавания голосовых напоминаний для бота @RemindMegaBot и заметил, что в этих решениях используется не всегда оправданный подход:

Для распознавания речи аудиофайл загружается на диск.

Возникает справедливый вопрос — неужели нельзя обойтись без записи файла на диск? Ведь это освободит операционную систему от лишних операций и сократит время обработки данных!

Почему же разработчики используют именно такой подход?

Дело в том, что голосовые сообщения в Telegram записываются в ogg-формате и кодируются opus-кодеком. Наиболее популярные сервисы распознавания голоса не поддерживают этот формат (или кодек), поэтому приходится его преобразовывать в .wav, .mp3 или даже в тот же .ogg, но использовать уже vorbis-кодек. Для этого авторы решений рекомендуют использовать ffmpeg, который, в свою очередь, требует сохранения аудио-файлов на диск.

Но использовать ffmpeg необязательно. Есть альтернативные решения, которые позволяют декодировать opus-данные. Ниже я приведу одно из таких решений, реализованное на языке Go.

В нашем примере будем подключаться к сервису распознавания речи wit.ai. Сервис wit.ai поддерживает формат «audio/raw«. Для декодирования голосового сообщения Telegram будем использовать библиотеку opus. Для работы с API wit.ai будем использовать официальную библиотеку wit-go.

Общий алгоритм функции распознавания речи выглядит прозрачно:

func Recognize(fileDirectURL string) (string, error) {     // 1. Получаем содержимое аудиофайла по ссылке     fileBody, err := getFileBody(fileDirectURL)     if err != nil {         return "", err     }     // 2. Преобразовываем данные в формат audio/raw     audioRawBuffer, err := getAudioRawBuffer(fileBody)     if err != nil {         return "", err     }     // 3. Распознаем голосовое сообщение     client := witai.NewClient("YOUR_WIT_AI_TOKEN")     msg, err := client.Speech(&witai.MessageRequest{         Speech: &witai.Speech{             File:        audioRawBuffer,             ContentType: "audio/raw;encoding=signed-integer;bits=16;rate=48000;endian=little",         },     })     if err != nil {         return "", err     }      return msg.Text, nil }

Содержимое аудиофайла получаем с использованием стандартных библиотек:

func getFileBody(fileDirectURL string) ([]byte, error) {     resp, err := http.Get(fileDirectURL)     if err != nil {         return nil, err     }     defer resp.Body.Close()     fileBody, err := io.ReadAll(resp.Body)     if err != nil {         return nil, err     }     return fileBody, nil }

Opus-данные декодируем по инструкции, выложенной разработчиками библиотеки.

func getAudioRawBuffer(fileBody []byte) (*bytes.Buffer, error) {     channels := 1     s, err := opus.NewStream(bytes.NewReader(fileBody))     if err != nil {         return nil, err     }     defer s.Close()      audioRawBuffer := new(bytes.Buffer)     pcmbuf := make([]int16, 16384)     for {         n, err := s.Read(pcmbuf)         if err == io.EOF {             break         } else if err != nil {             return nil, err         }         pcm := pcmbuf[:n*channels]         err = binary.Write(audioRawBuffer, binary.LittleEndian, pcm)         if err != nil {             return nil, err         }     }     return audioRawBuffer, nil }

После этого содержимое буфера пересылаем в wit.ai и получаем распознанный текст.

На этом пока всё. Спасибо за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/587442/