JACK и несколько звуковых карт
JACK — единственная звуковая система для UNIX систем, позволяющая коммутировать звуковые потоки на уровне приложений (самая распространённая на сегодняшний день система — Pulse Audio — умеет коммутировать звук только на уровне устройств, то есть можно перекинуть MP3-проигрыватель со внутренней звуковой карты на внешнюю, но нельзя перекинуть в Skype). Поэтому JACK является основой рабочей станции любого интернет-радиовещателя, использующего Linux.
Основная проблема JACK в том, что он умеет работать только с одной звуковой картой. Для вещания обычно используется внешнее звуковое устройство, подключаемое по USB, или USB микрофон, потому что встроенные в ноутбуки или другие компьютеры звуковые карты, какими крутыми бы они ни были, дают отвратительный звук с сильными шумами. Для переключения между внешним и внутренним устройствами нужно перезапускать JACK, а в случае с использованием внешнего USB микрофона вообще не ясно: проигрывать в него нельзя и проигрывать, получается, не во что.
Уточнение: при постоянном использовании JACK со внешним USB микрофоном можно
использовать ключи -P
и -C
командной строки; использовать разные карты для ввода и вывода звука JACK умеет. Однако если нужно отключать микрофон на ходу,
то без описанной здесь схемы не обойтись.
Пара слов об архитектуре JACK
Здесь надо сделать отступление и вкратце объяснить, как работает коммуникация в JACK. JACK оперирует портами. У звуковой карты обычно есть два выходных порта: левый и правый, называются они "system:playback_1" и "system:playback_2", и есть два входных порта: "system:capture_1" и "system:capture_2" (это микрофон или линейный вход, я не знаю как они делятся между собой).
Включение внешней звуковой карты в JACK
Для того, чтобы JACK увидел ещё одну звуковую карту, используются утилиты
alsa_in
и alsa_out
, которые создают новые порты и тупо ретранслируют туда
вход или выход указанной звуковой карты. (Этот подход оказался для меня
несколько неожиданным: я долго пытался найти способ «скрестить» две звуковые
карты средствами ALSA — и нашёл, но он оказался слишком сложным для моего
понимания.)
Итак, чтобы подружить JACK и внешнюю звуковую карту, достаточно запустить
alsa_out
, указав её имя:
$ alsa_out -d hw:USB
Чтобы узнать имя звуковой карты, используется утилита aplay
:
$ aplay -L
front:CARD=Intel,DEV=0
HDA Intel, ALC269 Analog
Front speakers
...
front:CARD=USB,DEV=0
E-MU 0202
Front speakers
(В этом примере видны две карты: "Intel" и "USB", эти названия и следует
указывать в качестве значений параметра -d
.)
Теперь в списке портов видны два новых:
$ jack_lsp
system:playback_1
system:playback_2
...
alsa_out:playback_1
alsa_out:playback_2
В эти порты можно играть звук, и он пойдёт на внешнюю звуковую карту (причём можно в обе сразу, хотя это мне никогда не бывает нужно). Можно на ходу отправить какую-нибудь программу играть звук во внешнюю карту:
$ jack_connect "MPlayer .*:out_0" "alsa_out:playback_1"
$ jack_connect "MPlayer .*:out_1" "alsa_out:playback_2"
Если теперь убить процесс alsa_out
или отключить внешнюю звуковую карту — она
перестанет издавать звук, приложение alsa_out
завершится, но больше ничего
плохого не произойдёт: программы, работающие со звуком, не зависнут и JACK
перезапускать нужды не будет.
Автоматическая коммутация
JACK не умеет сам определять что с чем коммутировать (философия UNIX: JACK
не этим занимается). Для автоматизации этого процесса используется утилита
jack.plumbing
, которая обычно висит в фоне и коммутирует потоки в соответствии с правилами из специального текстового файла, который называется
~/.jack.plumbing
. Автоматическая отправка MPlayer во внешнюю и внутреннюю
звуковые карты выглядит так:
# внутренняя карта
(connect "MPlayer .*:out_0" "system:playback_1")
(connect "MPlayer .*:out_1" "system:playback_1")
# внешняя карта
(connect "MPlayer .*:out_0" "alsa_out:playback_1")
(connect "MPlayer .*:out_1" "alsa_out:playback_1")
Теперь при подключении внешней звуковой карты MPlayer сразу будет начинать
играть через неё, в дополнение ко внутренней. (Можно использовать во втором
блоке директиву connect-exclusive
, тогда при подключении ко внешней карте он
будет отключен от внутренней, однако на практике при проведении прямых эфиров
эксклюзивное подключение бесполезно, т.к. MPlayer должен играть и в звуковую
карту, и в Skype, и в эфир радиостанции — это как минимум три неэксклюзивных
направления; на практике проще временно заглушить внутреннюю карту с помощью
утилит alsamixer
или amixer
.)
Примерный алгоритм работы
Для начала прямого эфира у меня есть скрипт, который выполняет следующие действия:
- Глушит внутреннюю карту утититой
amixer
. - Запускает в фоновом режиме утилиты
jack.plumbing
,alsa_out
иalsa_in
, если они не запущены. - Запускает в фоновом режиме
darkice
(вещание в радиостанцию). - Запускает утилиту
jack_meter
для наблюдения за уровнем записи.
Следить за развитием событий можно через RSS ленту или почтовую рассылку.