Летом 2021 года в Slack появились новые голосовые легковесные чаты — huddles. Мне они очень приглянулись в каждодневной рутине, и достаточно быстро появилась идея использовать их для улучшения жизни на работе.
Дальше я расскажу о процессе создания бота и подробнее остановлюсь на моменте работы с виртуальными звуковыми устройствами для Ubuntu, потому что это заняло больше времени, чем ожидалось. Для людей практики весь опыт завернут в репозиторий, чтение опционально :)
В чем суть
Команды часто создают болталки на нерабочую тематику, чтобы как-то компенсировать недостаток живого общения в новой реальности распределенной работы.
Но в таких чатах обычно мало музыки (по крайней мере у нас), шерить ее неудобно, да и все привыкли к своим плейлистам. Было бы круто сделать что-то вроде локального радио, где можно ненавязчиво делиться музыкой и проектными мемами.
В целом, сделать свой интернет-поток сейчас нетрудно, но кто будет заходить в него и целенаправленно слушать, вот честно? Huddle, мне показалось, идеально для этого подходит — все уже есть на канале, не нужно отвлекаться от рабочих инструментов. Просто заходишь в болталку, еще и можешь с кем-то сразу обсудить то, что услышал. "Выглядит достаточно круто, чтобы попробовать", — решил я.
Реализация идеи легко разбивается на задачи.
Понять, как работать с huddle
Сделать аудио-поток
Завернуть этот поток в huddle
Проверить, что все работает, и наслаждаться восторженными аплодисментами в свою честь
Бесконечно докручивать фичи: текущая песня, добавление юзерами песен в плейлист, лайки и т.п.
Slack API
В общем-то huddles API я найти не смог. При открытии в браузере видно, что обмен данными происходит через вебсокет, но возиться с ним желания нет. Проще и быстрее сэмулировать поведение юзера и воспользоваться микрофонным входом.
Попробовал быстро накидать скрипт на знакомой связке python + selenium
, но клик по кнопке входа работал очень нестабильно. В итоге перешел на puppeteer
, где все нормально заработало, и реализовал логику входа-выхода.
Сделал маленькую виртуалку на ubuntu 20.04, установил туда pulseaudio
, google-chrome
, node js
и npm
, подробнее опять же в репозитории.
Аудиопоток
Самая простая часть. В общем-то источником можно использовать что угодно, основная идея — завернуть внутри операционной системы выход с источника на дефолтный вход, который slack по умолчанию обнаружит.
Я решил использовать liquidsoap для генерации постоянного потока. Через пакетный менеджер установилась не самая свежая версия 1.4.1, но для наших целей хватит.
Пришло время разбираться со звуковыми устройствами и конкретно с PulseAudio
.
PulseAudio. Настраиваем вход
По мере гугления обнаруживается много запросов на тему того, как примешать звук с приложения к микрофону и завернуть в тот же zoom, но мне они не помогли. После очередной вкладки stack overflow пришло осознание, что обычная работа программиста тут заканчивается и требуется реальное исследование. Поэтому здесь я остановлюсь подробнее и расскажу детали простым языком, но на объективность не претендую. Да, речь пойдет про семейство linux и безGUIвые разборки.
Есть ALSA
. Это максимально приближенный к железу звуковой карты софт. А есть PulseAudio
— это абстракция над драйверами, этакий микшер, с которым нам и стоит работать. Далее в словаре терминов встречаем Sinks
и Sources
, что везде переводят как выходы (колонки) и входы (микрофон). Окей, значит, мне нужно, вероятно, как-то сделать source, завести в него сигнал и использовать по умолчанию.
Для управления есть утилиты pactl
и pacmd
. Разница среди них есть, но можно пользоваться и тем, и тем. Например, убедимся, что у нас нет никаких входов
pactl list short sources
pacmd list-sources
Далее. У каждого выхода (sink) есть сущность monitor. Это как гнездо для подключения наушников у колонки, то есть вроде входа для чего-то еще. И кажется, это можно использовать как дефолтный микрофон! Разработчики PulseAudio
предусмотрели как раз модуль для этого.
Пробуем:
pacmd load-module module-null-sink sink_name=Virtual_Sink sink_properties=device.description=Virtual_Sink
pacmd set-default-source Virtual_Sink.monitor
И говорю liquidsoft стримить плейлист на это устройство через конфиг:
output.pulseaudio(device="Virtual_Sink", radio)
Как проверить? Попробуем сделать запись звука в системе командой arecord -f cd test.wav
(-f отвечает за качество записи). В моем случае записался кусок трека, который был в плейлисте, а значит, успех!
Запускаем бота npm start
, ждем подключения, и … тишина.
Pulseaudio. Настраиваем выход
Варианта два. Либо звук всё-таки теряется где-то на подходе к браузеру, либо сам браузер почему-то не хочет его использовать. Не буду описывать процесс исследования запуска Chrome через puppeteer, важно обнаружение флага –use-file-for-fake-audio-capture и рядом --use-fake-ui-for-media-stream. С их помощью запускаю бота с записанным ранее test.wav и слышу наконец знакомый трек в Slack! Значит, Chrome сам по себе может передать звук, придется идти в настройки. Дописываю бота, делаю скриншоты.
Так и есть, не найден ни один микрофон. Параллельно с проверкой Chrome я выполнял команды проверки списка входов выходов и заметил кое-что.
Команда pacmd list-sources
выводит стабильно 1 выход Virtual_Sink.monitor
. А что за команда pacmd list-source-outputs
? Что это за выходы входов? Как я в итоге понял, Virtual_Sink
хоть и может быть источником за счет monitor
, но создан как sink
и находится только как устройство выхода. А нам нужно устройство ввода, которое создается модулем module-virtual-source
. При этом оно должно забирать звук с нашего Virtual_Sink.monitor
явно, потому что попытка создать простой source
в надежде, что default-source
в него попадет, у меня не сработала.
По непонятной мне причине описания модуля module-virtual-source
нет на офф странице, через консоль аргументы создания я тоже не смог добыть. К счастью, гугл четвертой ссылкой привел меня к исходникам, где я нашел нужный мне параметр с именем master
.
pacmd load-module module-virtual-source master=Virtual_Sink.monitor source_name=MicOut source_properties=device.description=MicOut format=s16le
pactl set-default-source MicOut
Проверка и итоги
В итоге бот заработал как надо, но то, что я услышал… Ровно так же звучит музыка, которую мы слышим в телефоне при ожидании абонента, или просто поднесенный к микрофону плеер. При этом запись с этого выхода в самой системе получается нормальной. Я проверил форматы потока при передаче между девайсами, там везде 16 бит 44100, этого должно быть достаточно. Звук в приложении явно хуже. Вопрос в техподдержку расставил все по своим местам.
Кроме того, в huddle невозможно говорить нескольким людям одновременно, так что идея с обсуждением на фоне музыки тоже оказалась сейчас нереализуемой. Остается лишь надежда, что Slack решит доработать huddles для качественного звука, и тогда этот проект может возродиться.
mst_72
В целом по качеству звука ожидаемо. Но, с другой стороны, можно аудиокниги транслировать :) Или новости. Включился - залипаешь