воскресенье, 15 июня 2014 г.

Звук на Linux по Bluetooth

Речь пойдёт о настройке Bluetooth-колонок или наушников на Дебиане и схожих с ним линуксах.

Часть 1. Bluetooth + ALSA

Предполагается, что у вас есть работающий драйвер Bluetooth, а из звуковых драйверов имеется только ALSA (стандартный звуковой драйвер ядра Linux), без всяких прочих OSS, JACK, aRts, ESD и Pulseaudio.
  1. Устанавливаем пакет для работы с устройствами Bluetooth, а также драйвер для ALSA:
    $ sudo apt-get install bluez bluez-alsa
  2. Получаем MAC-адрес устройства:
    $ hcitool scan
    Scanning ...
     XX:XX:XX:XX:XX:XX <Имя устройства>
    
  3. Устройство не будет работать, если не будет «спарено» с компьютером. Спаривание осуществляется следующей командой:
    bluez-simple-agent hci0 XX:XX:XX:XX:XX:XX
    Первым аргументом идёт имя bluetooth-карточки на компьютере (скорее всего hci0), а за ним идёт MAC-адрес устройства, который вы получили в п. 2. Если спаривание из командной строки указанной командой не удаётся, то можно не морочить себе голову, поставить пакет blueman и выполнить спаривание в нём. В любом случае, информация о спаренном устройстве сохраняется в системе, и после перезагрузки повторять спаривание не придётся.
  4. Судя по сведениям с форумов, для некоторых систем в файле /etc/bluetooth/audio.conf должны быть указаны следующие магические опции:
    [General]
    Enable=Socket
    AutoConnect=true
    
    После редактирования файла нужно перезапустить сервис:
    $ sudo /etc/init.d/bluetooth restart
  5. Вся последующая работа будет связана со связкой Bluetooth-ALSA, которая настраивается в файле /etc/asound.conf, или в ~/.asoundrc, по желанию. В простейшем варианте содержимое этого файла будет выглядеть так:
    pcm.bt { # "bt" -- произвольно выбранное имя нашего звукового устройства в ALSA
      type bluetooth
      device XX:XX:XX:XX:XX:XX # MAC-адрес устройства (см. п. 2)
      profile "auto"
    }
    pcm.!default bt # чтобы новое устройство использовалось по умолчанию
    После создания /etc/asound.conf ничего перезапускать не нужно. Звук должен появиться по всех приложениях.
  6. Звук теперь идёт на новое устройство, а регулирование уровня звука осуществляется всё ещё на старом устройстве (т.е. на звуковой карте по умолчанию). Попробуем переставить регулятор на новое устройство:
    ctl.bt {
      type bluetooth
    }
    ctl.!default bt
    
    На самом деле, регулирование уровня звука на Bluetooth-устройстве с компьютера не всегда возможно. Вы можете увидеть следующее сообщение об ошибке:
    audio/ctl_bluetooth.c:167:(bluetooth_send_ctl) Unable to receive new volume value from server
    audio/ctl_bluetooth.c:161:(bluetooth_send_ctl) Unable to request new volume value to server
    Оно говорит о том, что устройство не поддерживает регулировку уровня звука. В таком случае ничего не остаётся, кроме как на устройстве выставить звук на максимум вручную, а регулировку на компьютере осуществлять программным образом. Для этого существует плагин softvol:
    pcm.bt {
      type bluetooth
      device XX:XX:XX:XX:XX:XX
      profile "auto"
    }
    pcm.bt_softvol {
      type softvol
      slave.pcm "bt"           # Имя дочернего устройства
      control.name "Bluetooth" # Произвольное имя канала
      control.card 0           # Без этой опции почему-то не работает
      resolution 100           # Количество делений на шкале
    }
    pcm.!default plug:bt_softvol # Без "plug:" происходит путаница с диапазоном уровня звука
    
    Созданный канал будет будет виден в alsamixer и других утилитах, связанных с ALSA. Можно вместо создания канала «Bluetooth» заместить один из существующих каналов, например «PCM». Можно попробовать заместить и «Master», но это чревато проблемами, лучше не надо. В приложениях можно настроить регулируемый по умолчанию канал, например в mplayer для этого есть опция --mixer-channel=.

Часть 2. Bluetooth + звуковая карта + ALSA

Допустим, что возникла задача использовать проводные колонки на встроенной звуковой карте параллельно с Bluetooth-устройством, посылая на них тот же самый звуковой сигнал.
  1. Укажем встроенную звуковую карту в /etc/asound.conf:
    pcm.wire {
      type hw
      card 0
    }
    
    С помощью опций в приложениях можно направлять звук на проводные колонки вместо дефолтного устройства bt_softvol. Например, в mplayer это делается с помощью опции --ao=alsa:device=wire.
  2. Звучание проводных колонок одновременно с Bluetooth достигается следующим трюком: создаётся 4-канальное устройство multi, у которого 0-й и 1-й каналы идут на wire, а 2-й и 3-й — на bt; и 2-канальное устройство both, которое перенаправляет каждый свой канал на два канала multi.
    pcm.multi {
      type multi;
      slaves.a.pcm "wire"
      slaves.b.pcm "bt"
      slaves.a.channels  2
      slaves.b.channels  2
    
      bindings.0.slave   a
      bindings.0.channel 0
      bindings.1.slave   a
      bindings.1.channel 1
        
      bindings.2.slave   b
      bindings.2.channel 0
      bindings.3.slave   b
      bindings.3.channel 1
    }
    pcm.both {
      type route
      slave.pcm "multi"
       
      ttable.0.0 1 # 0-й канал both идёт на 0-й канал multi с множителем 1
      ttable.1.1 1 # 1-й канал both идёт на 1-й канал multi с множителем 1
      
      ttable.0.2 1 # 0-й канал both идёт на 2-й канал multi с множителем 1
      ttable.1.3 1 # 1-й канал both идёт на 3-й канал multi с множителем 1
    }
    pcm.!default plug:both
    
  3. Не забудем о регулировке звука, которая теперь должна работать не для bt, а для общего канала both:
    pcm.both_softvol {
      type  softvol
      slave.pcm "both"
      control.name "PCM"
      control.card 0
      resolution 100
    }
    pcm.!default plug:both_softvol
    
    Итоговый asound.conf

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

pcm_hw.c:1557:(snd_pcm_hw_open) open '/dev/snd/pcmC0D0p' failed (-16): Device or resource busy
Сведение звука от разных программ в одно устройство называется микшированием (mixing). На первый взгляд, микширование не самая необходимая вещь на свете — никто ведь не станет слушать два музыкальных трека одновременно. Звуки различных нотификаций тоже не очень нужны. Но просмотр видео в браузере через flash без микширования сулит юзеру страдания. Дело в том, что плагин от Adobe крайне неохотно «отпускает» звуковое устройство после нажатия паузы. Может пройти около 10 секунд, прежде чем устройство станет доступно другим программам. Само собой, если запустить видео в браузере при работающем mplayer, плагин будет работать без звука. Если закрыть mplayer, звук в браузере не появится до тех пор, пока вы не перезагрузите страницу с видео. Аналогичные эффекты возникают, если в роли mplayer выступает другое окно браузера с другим видео.

Короче говоря, микширование необходимо. В давние времена звуковые карты обладали возможностью аппаратного микширования, но на подавляющем большинстве современных карт такой возможности нет, тем более на портативных Bluetooth-устройствах. Поэтому драйверы звука обзавелись поддержкой программного микширования. В ALSA соответствующий плагин называется Dmix:

pcm.wire_dmix {
  type dmix
  slave {
    pcm "wire"       # дочернее физическое устройство
    period_time 0    # параметры микширования
    period_size 2048 
    channels 2       # количество каналов
  }
  ipc_key 1024       # уникальный в рамках asound.conf ключ
  bindings {
    0 0              # 0-й канал wire_dmix идёт на 0-й канал wire
    1 1              # 1-й канал wire_dmix идёт на 1-й канал wire
  }
}
Устройство wire_dmix даёт возможность запускать сколько угодно mplayer-ов с ключом
--ao=alsa:device=wire_dmix, и звук от всех будет микшироваться. Но попробуем сделать то же самое с Bluetooth-устройством:
pcm.bt_dmix {
  type dmix
  slave {
    pcm "bt"
    period_time 0
    period_size 2048
    channels 2
  }
  ipc_key 2048
  bindings {
    0 0
    1 1
  }
}
При попытке проиграть что-либо на устройство bt_dmix выводится следующая ошибка:
pcm_direct.c:1522:(_snd_pcm_direct_get_slave_ipc_offset) Invalid type 'bluetooth' for slave PCM
Это ошибку не обойти. Попытка навесить dmix на устройство both вместо bt ничего не даст. Dmix работает только со звуковыми картами, а с Bluetooth он не работает, и вряд ли будет.

Выход — признать, что звуковой сервер Pulseaudio — не бесполезная прослойка, и использовать его. Микширование на Pulseaudio универсально для любых устройств, в т.ч. для работающих под управлением ALSA.

Часть 3. Bluetooth + звуковая карта + ALSA + Pulseaudio

  1. Инсталляция Pulseaudio:
    $ sudo apt-get install pulseaudio
    С этого момента приложения, поддерживающие Pulseaudio (т.е. подавляющее большинство) играют звук через Pulseaudio, а не ALSA. Микширование работает сразу же, без дополнительных настроек. Однако, звук с Pulseaudio по умолчанию идёт не на дефолтное устройство ALSА (both_softvol), а на дефолтное устройство Pulseaudio, т.е. на звуковую карту. Bluetooth-устройство молчит.
  2. Попробуем направить звук с Pulseaudio на ALSA-устройство. Для этого нужно отредактировать файл /etc/pulse/default.pa, добавив в него следующие строки:
    load-module module-alsa-sink device=both sink_name=alsa_both
    set-default-sink alsa_both
    После обновления default.pa требуется перезапуск Pulseaudio:
    $ pulseaudio -k
    $ pulseaudio --start
    Звук по Bluetooth начинает работать. Но что мы слышим из проводных колонок? Знаменитый треск Pulseaudio, знакомый многим со времён появления этого продукта. Проблема обходится следующим образом: звук по Bluetoooth остаётся на ALSA, обслуживание звуковой карты переносится целиком в Pulseaudio, и дублирование звука на Bluetooth и карту делается тоже на стороне Pulseaudio. Для этого есть команда module-combine-sink:
    load-module module-alsa-sink device=bt sink_name=alsa_bt
    load-module module-combine-sink sink_name=combined
    set-default-sink combined
    Осталось в последний раз перезапустить Pulseaudio, и готово.
В принципе, в Pulseaudio есть собственный модуль для работы с Bluetooth-устройствами. Его уместно будет использовать, когда в состав Debian войдёт Bluez 5, который не поддерживается в ALSA в связи с кардинальной переделкой API.