Пошаговое руководство с использованием AssemblyAI и Streamlit

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

Материал подготовлен к старту курса по Fullstack-разработке на Python.


Введение

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

  • можно сразу записывать идеи и беседы. Это очень полезная функция для людей, которые работают в быстро меняющейся среде, и для людей с большим количеством идей;

  • развивать навыки общения, ведь теперь вы увидите, как говорите вы сами и как говорят другие.

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

Мы научимся создавать приложение для динамического преобразования речи в текст и сделаем это с помощью API AssemblyAI (серверная часть) и Streamlit (клиентская часть).

Вот видеоверсия статьи:

Обзор приложения

Для приложения понадобятся следующие библиотеки для Python:

  • streamlit — веб-фреймворком воспользуемся для размещения всех виджетов ввода и вывода;

  • websocket — позволяет приложению взаимодействовать с API AssemblyAI;

  • asyncio — позволяет выполнять всевозможный речевой ввод и вывод асинхронно;

  • base64 — кодирует и декодирует аудиосигнал перед его отправкой в API ​​AssemblyAI;

  • json — считывает речевой вывод, сгенерированный через API AssemblyAI (например, расшифрованный текст);

  • pyaudio — обрабатывает речевой ввод через библиотеку PortAudio;

  • os и pathlib — используются для перехода по различным папкам проекта и работы с файлами.

Настройка рабочей среды

Чтобы воссоздать приложение для динамической расшифровки речи на вашем компьютере, мы создадим среду conda под названием transcription:

conda create -n transcription python=3.9

Возможно, высветится запрос на установку зависимостей Python-библиотеки. Если так, нажмите клавишу Y, чтобы подтвердить действие и продолжить.

После создания среды conda активировать её можно так:

conda activate transcription

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

conda deactivate

Загрузка GitHub-репозитория

Загрузим с GitHub весь репозиторий приложения динамической расшифровки речи:

git clone https://github.com/dataprofessor/realtime-transcription

Переходим в папку realtime-transcription:

cd realtime-transcription

Можно установить обязательные библиотеки, которыми пользуется приложение:

pip install -r requirements.txt

Получение ключа от API AssemblyAI

Получить доступ к API в AssemblyAI крайне просто. Для начала зарегистрируйтесь на AssemblyAI, это бесплатно. Зайдите в учётную запись. На панели справа вы увидите API-ключ:

Как получить API-ключ в AssemblyAI
Как получить API-ключ в AssemblyAI

Теперь, когда вы скопировали API-ключ в память, необходимо добавить его в файл secrets.toml из папки.streamlit. Путь к файлу выглядит так: .streamlit/secrets.toml. Его содержание должно быть таким:

api_key = 'xxxxx'

Вместо xxxxx вставьте свой ключ от API. Получить этот ключ мы сможем с помощью строки кода st.secrets[‘api_key’].

Запуск приложения

Прежде чем запускать приложение, давайте рассмотрим содержимое рабочей директории (то, что открывается по команде tree в Bash):

 Содержимое папки realtime-transcription

Теперь мы готовы запустить своё приложение:

streamlit run streamlit_app.py

Этот код позволит открыть приложение в новом окне браузера:

Скриншот приложения
Скриншот приложения

 Посмотрим, как работает приложение:

Работа приложения
Работа приложения

 

Объяснение кода

Ниже объясняется базовый код приложения.

  • Строки 1–8 — импорт обязательных библиотек веб-приложения.

  • Строки 10-13 — начальное состоянии сессии приложения.

  • Строки 15-22 — ввод для приём пользовательского ввода параметров аудио представлены виджетом text_input.

  • Строки 24-31 — для открытия потока аудиоданных через pyaudio используются входные параметры аудио из блока кода выше.

  • Строки 33-46 — определяют 3 пользовательские функции (например, start_listening, stop_listening и download_transcription), которые будут вызываться в коде (см. ниже).

  • Строка 49 — отображает название приложения через строку st.title.

  • Строки 51–62 — отображает информацию о приложении (раздел About) с помощью строки st.expander.

  • Строки 64–67 — создают 2 столбца строкой st.columns для размещения кнопок «Пуск» (Start) и «Стоп» (Stop). То есть они используют start_listening и stop_listening через параметр on_click виджета кнопки.

  • Строки 69–139 — здесь выполняется обработка речевого входа и выхода: аудиосигнал передаётся в API AssemblyAI, где расшифрованный текст выдаётся в формате JSON. Эта часть была изменена и адаптирована из блока кода, написанного Мисрой Турп и Джорджиосом Мириантусом

  • Строки 141–144 — отображают кнопку загрузки расшифровки, а затем удаляют файл.

Весь код
import streamlit as st
import websockets
import asyncio
import base64
import json
import pyaudio
import os
from pathlib import Path

# Состояние сессии
if 'text' not in st.session_state:
	st.session_state['text'] = 'Listening...'
	st.session_state['run'] = False

# Параметры аудио
st.sidebar.header('Audio Parameters')

FRAMES_PER_BUFFER = int(st.sidebar.text_input('Frames per buffer', 3200))
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = int(st.sidebar.text_input('Rate', 16000))
p = pyaudio.PyAudio()

# Открываем аудиопоток с указанными выше параметрами
stream = p.open(
   format=FORMAT,
   channels=CHANNELS,
   rate=RATE,
   input=True,
   frames_per_buffer=FRAMES_PER_BUFFER
)

# Запуск и остановка прослушивания
def start_listening():
	st.session_state['run'] = True

def download_transcription():
	read_txt = open('transcription.txt', 'r')
	st.download_button(
		label="Download transcription",
		data=read_txt,
		file_name='transcription_output.txt',
		mime='text/plain')

def stop_listening():
	st.session_state['run'] = False

# Веб-интерфейс (фронтенд)
st.title('????️ Real-Time Transcription App')

with st.expander('About this App'):
	st.markdown('''
	This Streamlit app uses the AssemblyAI API to perform real-time transcription.
	
	Libraries used:
	- `streamlit` - web framework
	- `pyaudio` - a Python library providing bindings to [PortAudio](http://www.portaudio.com/) (cross-platform audio processing library)
	- `websockets` - allows interaction with the API
	- `asyncio` - allows concurrent input/output processing
	- `base64` - encode/decode audio data
	- `json` - allows reading of AssemblyAI audio output in JSON format
	''')

col1, col2 = st.columns(2)

col1.button('Start', on_click=start_listening)
col2.button('Stop', on_click=stop_listening)

# Отправляем аудио (ввод)
async def send_receive():
	URL = f"wss://api.assemblyai.com/v2/realtime/ws?sample_rate={RATE}"

	print(f'Connecting websocket to url ${URL}')

	async with websockets.connect(
		URL,
		extra_headers=(("Authorization", st.secrets['api_key']),),
		ping_interval=5,
		ping_timeout=20
	) as _ws:

		r = await asyncio.sleep(0.1)
		print("Receiving messages ...")

		session_begins = await _ws.recv()
		print(session_begins)
		print("Sending messages ...")


		async def send():
			while st.session_state['run']:
				try:
					data = stream.read(FRAMES_PER_BUFFER)
					data = base64.b64encode(data).decode("utf-8")
					json_data = json.dumps({"audio_data":str(data)})
					r = await _ws.send(json_data)

				except websockets.exceptions.ConnectionClosedError as e:
					print(e)
					assert e.code == 4008
					break

				except Exception as e:
					print(e)
					assert False, "Not a websocket 4008 error"

				r = await asyncio.sleep(0.01)

    # Принимаем транскрипцию (вывод)
		async def receive():
			while st.session_state['run']:
				try:
					result_str = await _ws.recv()
					result = json.loads(result_str)['text']

					if json.loads(result_str)['message_type']=='FinalTranscript':
						print(result)
						st.session_state['text'] = result
						st.write(st.session_state['text'])

						transcription_txt = open('transcription.txt', 'a')
						transcription_txt.write(st.session_state['text'])
						transcription_txt.write(' ')
						transcription_txt.close()


				except websockets.exceptions.ConnectionClosedError as e:
					print(e)
					assert e.code == 4008
					break

				except Exception as e:
					print(e)
					assert False, "Not a websocket 4008 error"
			
		send_result, receive_result = await asyncio.gather(send(), receive())


asyncio.run(send_receive())

if Path('transcription.txt').is_file():
	st.markdown('### Download')
	download_transcription()
	os.remove('transcription.txt')

# Ссылки (этот код — адаптация кода по ссылкам ниже)
# 1. https://github.com/misraturp/Real-time-transcription-from-microphone
# 2. https://medium.com/towards-data-science/real-time-speech-recognition-python-assemblyai-13d35eeed226

Заключение

Поздравляем, вы создали приложение для динамического преобразования речи на Python с помощью API AssemblyAI. Как уже говорилось, у таких приложений есть несколько вариантов использования (диктовка статьи/сочинения или письма, развитие навыков общения, преобразование речи для людей с нарушением слуха и т. д.).

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

Выбрать другую востребованную профессию.

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


  1. praeivis
    02.06.2022 08:23
    +14

    Название статьи должно быт: "Как с помощью AssemblyAI создать приложение для расшифровки речи в реальном времени", так как питон тут вторичен.


    1. Akon32
      02.06.2022 11:41
      +2

      "Как вызвать функцию из библиотеки веб-сервиса"...


      1. nonickname227
        02.06.2022 13:04

        ... и получить:

        Receiving messages ...
        {"error": "This feature is paid-only and requires you add a credit card. Please visit http://www.assemblyai.com/account/ to add a credit card to your account"}


        1. stranger777
          02.06.2022 13:14

          Я понимал, что подобная ситуация, к сожалению, может возникнуть сегодня даже на самом открытом сервисе, поэтому дописал об адаптации кода для российского бекенда


      1. stranger777
        02.06.2022 13:35

        Огромное количество современных веб-приложений (и не только веб) сводится к тому, чтобы правильно, аккуратно и красиво вызвать по API уже существующую на бекенде функцию. Начиная, например, с навыков "Маруси" или "Алисы".

        Здесь автор не просто "вызывает функцию", но и оборачивает её в лаконичный, открытый фронтенд, который любой разработчик сможет адаптировать под себя, работает с веб-сокетами, а значит, с asyncio, и PyAudio. Получается небольшой MVP. Он может быть полезен людям, которые пишут подобное приложение для своих задач.

        Если вы посмотрите на оригинал статьи, то среди неравнодушных к материалу людей найдёте даже архитектора ПО


        1. Akon32
          02.06.2022 15:00

          Здесь автор не просто "вызывает функцию", но и оборачивает её в лаконичный, открытый фронтенд, который любой разработчик сможет адаптировать под себя,

          Возможно, туториал действительно полезен, но тем не менее, это уровень вызова функции из функции. Речь ведь не о реализации распознавалки речи, как можно было подумать по названию, и даже не использования готовой нейросети, а о подключении готового сервиса. Если AssemblyAI отключит этот сервис - от туториала ничего не останется.

          Если вы посмотрите на оригинал статьи, то среди неравнодушных к материалу людей найдёте даже архитектора ПО

          Странная аргументация; к тому же, я посмотрел и ничего такого не нашёл.


          1. stranger777
            02.06.2022 16:57

            Список людей, которым понравилась статья, откроется по клику на число "хлопков" (не на ладонь для хлопка). Когда относительно простые статьи замечает человек с высокой квалификацией, как правило, это означает, что, кроме простоты, в ней есть что-то ещё. Здесь это, скорее всего, — доступность подачи.

            А одна из наших задач — снимать страх перед технологиями. Поэтому мы с самого начала указываем на простоту реализации с поверхности и рассказываем именно о ней:

            Но мы покажем, насколько простыми стали распознавание и транскрипция речи, по крайней мере, на поверхности.

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

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


            1. Akon32
              02.06.2022 22:27

              Когда относительно простые статьи замечает человек с высокой
              квалификацией, как правило, это означает, что, кроме простоты, в ней
              есть что-то ещё.

              Поверьте, лайк даже не означает, что статья прочитана. С остальным не спорю.


    1. stranger777
      02.06.2022 12:59

      Если есть API, то вторичен любой язык, но автор использует Python, а примеры работы с API удобно делить по языкам


  1. Mansur_85
    02.06.2022 08:46
    +1

    Очень интересно. Надо попробовать


  1. SaberMG
    02.06.2022 12:41
    +1

    Введение

    Приложение расшифровки речи в режиме реального времени автоматически преобразует текст в речь. Этот текст почти мгновенно отображается на экране, ...

    Перечитайте вдумчиво, логика первого предложения явно нарушена.


    1. stranger777
      02.06.2022 12:42

      Поправили, большое спасибо!


  1. MegaMANGO
    03.06.2022 12:30
    +1

    Это ПОЛЕЗНО, но нифига не интересно. Я ожидал тут data science , а тут скачивается всё готовенькое. Это, в общем-то, не ПРОГРАММИРОВАНИЕ и не PYTHON даже, ненужны навыки в пайтонне чтобы скачать все необходимые программки и написать пару строчек кода ¯\_(ツ)_/¯.

    Так что, по сути, название "создаём ******* с помощью Python" – кликбейт