Пошаговое руководство с использованием 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-ключ в память, необходимо добавить его в файл 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)
SaberMG
02.06.2022 12:41+1Введение
Приложение расшифровки речи в режиме реального времени автоматически преобразует текст в речь. Этот текст почти мгновенно отображается на экране, ...
Перечитайте вдумчиво, логика первого предложения явно нарушена.
MegaMANGO
03.06.2022 12:30+1Это ПОЛЕЗНО, но нифига не интересно. Я ожидал тут data science , а тут скачивается всё готовенькое. Это, в общем-то, не ПРОГРАММИРОВАНИЕ и не PYTHON даже, ненужны навыки в пайтонне чтобы скачать все необходимые программки и написать пару строчек кода ¯\_(ツ)_/¯.
Так что, по сути, название "создаём ******* с помощью Python" – кликбейт
praeivis
Название статьи должно быт: "Как с помощью AssemblyAI создать приложение для расшифровки речи в реальном времени", так как питон тут вторичен.
Akon32
"Как вызвать функцию из
библиотекивеб-сервиса"...nonickname227
... и получить:
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"}
stranger777
Я понимал, что подобная ситуация, к сожалению, может возникнуть сегодня даже на самом открытом сервисе, поэтому дописал об адаптации кода для российского бекенда
stranger777
Огромное количество современных веб-приложений (и не только веб) сводится к тому, чтобы правильно, аккуратно и красиво вызвать по API уже существующую на бекенде функцию. Начиная, например, с навыков "Маруси" или "Алисы".
Здесь автор не просто "вызывает функцию", но и оборачивает её в лаконичный, открытый фронтенд, который любой разработчик сможет адаптировать под себя, работает с веб-сокетами, а значит, с asyncio, и PyAudio. Получается небольшой MVP. Он может быть полезен людям, которые пишут подобное приложение для своих задач.
Если вы посмотрите на оригинал статьи, то среди неравнодушных к материалу людей найдёте даже архитектора ПО
Akon32
Возможно, туториал действительно полезен, но тем не менее, это уровень вызова функции из функции. Речь ведь не о реализации распознавалки речи, как можно было подумать по названию, и даже не использования готовой нейросети, а о подключении готового сервиса. Если AssemblyAI отключит этот сервис - от туториала ничего не останется.
Странная аргументация; к тому же, я посмотрел и ничего такого не нашёл.
stranger777
Список людей, которым понравилась статья, откроется по клику на число "хлопков" (не на ладонь для хлопка). Когда относительно простые статьи замечает человек с высокой квалификацией, как правило, это означает, что, кроме простоты, в ней есть что-то ещё. Здесь это, скорее всего, — доступность подачи.
А одна из наших задач — снимать страх перед технологиями. Поэтому мы с самого начала указываем на простоту реализации с поверхности и рассказываем именно о ней:
Подзаголовок указывает, что используются сервисы, фреймворки и т. д., которые, так или иначе, сводят сложные задачи к вызовам единичных функций.
И всё же шаблонный код интерфейса от статьи останется, хотя и не весь. API работы с аудио естественным образом построены на одних и тех же понятиях, но вот этот пример скорее напугает новичка, он создан для специалиста.
Akon32
Поверьте, лайк даже не означает, что статья прочитана. С остальным не спорю.
stranger777
Если есть API, то вторичен любой язык, но автор использует Python, а примеры работы с API удобно делить по языкам