Внимание

Этот блог переехал по адресу yktoo.com/ru/

Attention

This blog has moved to yktoo.com/en/

March 03, 2013

Индикатор-переключатель звукового устройства для Убунту ~ Sound Switcher Indicator for Ubuntu

Я не выдержал и написал индикатор для переключения звуковых устройств (входа/выхода) в Ubuntu.

I couldn't take it anymore so I created an audio input/output switcher indicator for Ubuntu.



Сколько лет пользовался Ubuntu и её оболочкой Unity, столько и пытался найти индикатор (application indicator) для удобного переключения звукового устройства. Индикаторов под Unity насоздавали уже вагон и маленькую тележку, включая совершенно бессмысленные на мой взгляд, но такой насущной вещи, как селектор выхода, так никто и не сподобился разработать. Поэтому каждый раз мне приходилось запускать апплет Sound, ждать, пока он запустится, выбирать нужную вкладку и переключать вход или выход. Особенно часто я пользуюсь переключателем для перенаправления звука на мой Bluetooth-адаптер Logitech, чтобы слушать музыку на хорошей акустике.

I've been trying to find one just as long as I've been using Ubuntu and Unity. It had to be an application indicator that would allow for easy switching of the default sound device. There's plenty of indicators for Unity already, including some pretty useless stuff, and yet no such a basic thing as sound selector available. Therefore one has to start the Sound applet, wait for it to open, choose the right tab and then select the required input or output device. Most often I used it to redirect audio to my Logitech Bluetooth adapter to listen to music via decent speakers.

В конце концов моё терпение лопнуло, и я занялся изучением принципов работы индикаторов. Быстро выяснилось, что написать индикатор для Unity очень просто, особенно на Python. Так я создал самый первый прототип переключателя, который использовал команду pacmd для смены выхода (sink), а также разбирал её вывод, чтобы составить список доступных аудиоустройств.

Приложение состояло из сотни строк на Питоне и в принципе работало, но всё-таки это было не очень серьёзно — список устройств сам не обновлялся, на переключение устройства в другом месте индикатор тоже никак не реагировал. Ну и к тому же требовал установленного pulseaudio-utils.

Всё это было как-то некошерно, так что я взялся за изучение PulseAudio API — очень утомительное увлекательное занятие. Тут надо ещё отметить, что библиотека PulseAudio по своей природе полностью асинхронная, так что обработку её событий очень желательно выполнять в отдельном потоке.

После долгих экспериментов с несколько ущербной реализацией многопоточности в Python (один только GIL чего стоит), я наконец добился полностью стабильной работы индикатора. Меню содержит три секции: входные устройства (отсортированные по имени), выходные устройства (то же самое) и статические пункты.

Значок индикатора сделал в Inkscape:
At last I decided to write such an indicator myself, so I began to read up on app indicators. It turned out creating a Unity indicator is pretty simple, especially with Python. So I made the very first prototype that used the pacmd command to switch output device (sink) and parsed its output to build device list.

It was a hundred line Python script, and it worked, but had a number of flaws. Device list wouldn't update automatically, and changing the default defice elsewhere didn't reflect in indicator's menu either. Furthermore, it depended on pulseaudio-utils.

All that wasn't very neat so I started learning the somewhat intricate PulseAudio API. Among other things, PulseAudio is almost completely asynchronous by design, so its events are best handled by a separate thread.

After a long string of experiments with Python's non-immaculate multithreading implementation (I'd mention GIL to start with), I made the application run smoothly. The menu consists of three sections: input devices (ordered by name), output devices (same) and static items.

The icon was designed in Inkscape:


Любые изменения состояния сервера PulseAudio отражаются на содержимом меню, причём, даже если оно открыто.

Следующей задачей было создать установочный .deb-пакет для Убунту, чтобы приложением могли легко пользоваться все желающие, и вот тут-то меня ждало самое настоящее испытание. Могу с уверенностью сказать, что написать многопоточный индикатор на Питоне заметно проще, чем собрать его в пакет и опубликовать на Launchpad. В Сети множество статей на эту тему, но при их прочтении меня постоянно преследовало ощущение, что все они описывают, как управлять самолётом в воздухе, не объясняя, как, собственно, нужно взлетать (для интересующихся темой настоятельно советую начать с прочтения документации по Distutils, после этого многое становится ясно. Следующим шагом может быть stdeb).
Any changes in PulseAudio daemon state are immediately reflected in menu's contents, even if it's open.

The next task was to build a .deb package for Ubuntu so that the application could be easily installed by anyone. Now, that was a real challenge. I am certain it's easier to create a multithreaded app indicator in Python than package it and publish at Launchpad. There's a lot of information on the subject available, but I couldn't help the feeling they're always explaining how to fly a plane without first describing how to take off (if you're interested in the subject, I'd strongly recommend starting with the Distutils guide, which makes clear a lot of things. The next step could be stdeb).
В общем, приложение собрано и доступно:
Чтобы его установить:
sudo add-apt-repository ppa:yktooo/ppa
sudo apt-get update
sudo apt-get install indicator-sound-switcher

О багах сообщайте в трекер на GitHub.
The application is built and available:
In order to install it:
sudo add-apt-repository ppa:yktooo/ppa
sudo apt-get update
sudo apt-get install indicator-sound-switcher

Bug reports are welcome at GitHub bugtracker.