ВСТУПЛЕНИЕ
Звуковая система PipeWire принесла, без преувеличения, революцию в мир десктопно-мультимедийного Linux. Наконец то эта ОС получила продуманную, сделанную по уму понимающими в теме людьми, и сделанную качественно звуковую систему. Многие ее возможности присутствовали и раньше, в других более старых звуковых системах, но в PipeWire они собраны воедино и сформировали целую единую платформу, на которой можно реализовать огромное количество самых разных задач.
Но в пользовательской среде, эта революция практически не заметна, часто можно встретить мнение вида «ну играет ютуб и играет, мне хватает». Поэтому я решил сделать небольшой цикл статей, посвященный более продвинутому использованию PipeWire, раскрывающему его, без преувеличения, поразительные возможности. (Ну и чтобы изучить их самому в процессе, хехехе).
ЧАСТЬ 1: EasyEffects, давай, до свидания!
Начнем с самого простого и нужного. На ноутбуках мы часто или практически всегда имеем плохие динамики, которые требуют эквалайзера. Для пользователя удобнее всего, если такой эквалайзер будет системным, независимым от приложения, потому что, ну попробуйте добавить эквалайзер в Google Chrome )) да и синхронизировать настройки эквалайзера по всем приложениям никому не нужно.
Здесь выходит на сцену известное приложение EasyEffects, которое предоставляет и эквалайзер, и много чего кроме него, как раз общесистемно. НО! Если вы пробовали его использовать, то видите сразу его недостатки – это довольно тяжелое для своей задачи приложение, на GTK4, запускается и работает в фоне тоже как GUI приложение со скрытым окном, потребляет излишне много памяти и ресурсов. Заглянув чуть больше под капот, мы увидим еще более пугающие вещи – EasyEffects принимает на себя задачу линковки (соединения нод в графе звуковой системы), то есть он сам начинает подключать приложения к тем нодам, к каким он хочет. Но, это же задача менеджера сессии PipeWire, то есть WirePlumber!
Проще говоря, EasyEffects берет на себя прямую функцию WirePlumber и они борются друг с другом. Надо ли говорить, что подобная вакханалия и тяжеловесность для простейшей задачи «просто сделать эквалайзер» попадает в полной мере под термин bloatware! Но как же быть?
А что, если я скажу, что PipeWire, сам голый, непосредственно, УЖЕ содержит все возможности для реализации эквалайзера, и не только эквалайзера, а вообще многого, что только может понадобиться делать со звуком? Что если я скажу, что вам достаточно создать один текстовый файл, и САМ PipeWire будет выполнять всю работу, прямо в своем процессе, заменяя не то что EasyEffects, а даже, например, софтовые гитарные процессоры типа Guitarix и KPP?? Для этого не потребуется запуск дополнительных приложений, демонов, хостов плагинов. Только PipeWire. One love. Как тебе такое, Илон Маск R_He_Po6oT?
Сегодня мы сделаем общесистемный эквалайзер. Немного теории, в двух предложениях. Все приложения, работающие с PipeWire, создают ноды – узлы, через которые идет обмен звуковыми потоками. Через ноду приложение может получать либо выводить звук. Все ноды соединены в граф – «схему» из нод, соединенных связями. Нода, В КОТОРУЮ можно выводить звук, называется Sink, а ноду, которая сама что-то выдает из себя, назовем Source или Stream.
Тогда, чтобы сделать эквалайзер, нам надо добавить в граф две ноды – Equalizer Sink к которому все приложения будут подключаться и выводить в него звук, и Stream который будет выдавать обработанный, эквализованный звук в звуковую карту. Звуковая карта – это тоже нода типа Sink, она уже есть изначально, и по умолчанию все приложения подключаются именно к ней. А мы создадим Equalizer Sink и назначим его «синком по умолчанию». Тогда WirePlumber, менеджер сессии, начнет подключать все приложения на наш Equalizer Sink.
Осталось решить, что же, какое приложение или какая сущность, будет выполнять саму обработку, что будет связано с этими двумя нодами, получать, обрабатывать и отдавать звук. Ноды может создавать любое приложение, используя PipeWire API, что и делает EasyEffects, например. Но мы же хотим от него избавиться! Помимо приложений, это могут делать модули PipeWire. Модули – это плагины, библиотеки формата .so, загружаемые в процесс PipeWire. В стандартной поставке PipeWire есть модуль filter-chain, который мы рассмотрим подробнее.
filter-chain создает две ноды, Sink и Stream, и обрабатывает звуковой поток, идущий между ними. Это именно то, что нам нужно – filter-chain позволяет применить к звуку любую обработку. filter-chain позволяет использовать внутри себя плагины LADSPA, плагины LV2, и самое главное – уже содержит набор встроенных фильтров, для которых не нужно ничего внешнего. Уже понятно, что если мы подключим LV2 плагины из набора LSP например, то можно вытворять со звуком все что угодно, т. к. это студийные плагины для DAW системы профессионального уровня. НО! НО! НО! Оказывается, что даже встроенными эффектами, без дополнительных плагинов вообще, уже можно делать очень многое!
И для начала мы сделаем графический эквалайзер на 15 полос. Для этого, надо создать вот такой конфиг-файл, по адресу ~.config/pipewire/pipewire.conf.d/eq.conf
:
context.modules = [
{ name = libpipewire-module-filter-chain
args = {
node.description = "Equalizer Sink"
media.name = "Equalizer Sink"
filter.graph = {
nodes = [
{
type = builtin
name = eq_preamp
label = bq_highshelf
control = { "Freq" = 0 "Q" = 1.0 "Gain" = 0.0 }
}
{
type = builtin
name = eq_band_1
label = bq_peaking
control = { "Freq" = 25.0 "Q" = 1.7 "Gain" = -10.0 }
}
{
type = builtin
name = eq_band_2
label = bq_peaking
control = { "Freq" = 40.0 "Q" = 1.7 "Gain" = -10.0 }
}
{
type = builtin
name = eq_band_3
label = bq_peaking
control = { "Freq" = 63.0 "Q" = 1.7 "Gain" = -9.0}
}
{
type = builtin
name = eq_band_4
label = bq_peaking
control = { "Freq" = 100.0 "Q" = 1.7 "Gain" = 3.0 }
}
{
type = builtin
name = eq_band_5
label = bq_peaking
control = { "Freq" = 160.0 "Q" = 1.7 "Gain" = 3.0 }
}
{
type = builtin
name = eq_band_6
label = bq_peaking
control = { "Freq" = 250.0 "Q" = 1.7 "Gain" = -2.0 }
}
{
type = builtin
name = eq_band_7
label = bq_peaking
control = { "Freq" = 400.0 "Q" = 1.7 "Gain" = -5.0 }
}
{
type = builtin
name = eq_band_8
label = bq_peaking
control = { "Freq" = 630.0 "Q" = 1.7 "Gain" = -5.0 }
}
{
type = builtin
name = eq_band_9
label = bq_peaking
control = { "Freq" = 1000.0 "Q" = 1.7 "Gain" = -3.0 }
}
{
type = builtin
name = eq_band_10
label = bq_peaking
control = { "Freq" = 1600.0 "Q" = 1.7 "Gain" = -3.0 }
}
{
type = builtin
name = eq_band_11
label = bq_peaking
control = { "Freq" = 2500.0 "Q" = 1.7 "Gain" = -3.0 }
}
{
type = builtin
name = eq_band_12
label = bq_peaking
control = { "Freq" = 4000.0 "Q" = 1.7 "Gain" = -5.0 }
}
{
type = builtin
name = eq_band_13
label = bq_peaking
control = { "Freq" = 6300.0 "Q" = 1.7 "Gain" = -3.0 }
}
{
type = builtin
name = eq_band_14
label = bq_peaking
control = { "Freq" = 10000.0 "Q" = 1.7 "Gain" = 0.0 }
}
{
type = builtin
name = eq_band_15
label = bq_peaking
control = { "Freq" = 16000.0 "Q" = 1.7 "Gain" = 0.0 }
}
]
links = [
{ output = "eq_preamp:Out" input = "eq_band_1:In" }
{ output = "eq_band_1:Out" input = "eq_band_2:In" }
{ output = "eq_band_2:Out" input = "eq_band_3:In" }
{ output = "eq_band_3:Out" input = "eq_band_4:In" }
{ output = "eq_band_4:Out" input = "eq_band_5:In" }
{ output = "eq_band_5:Out" input = "eq_band_6:In" }
{ output = "eq_band_6:Out" input = "eq_band_7:In" }
{ output = "eq_band_7:Out" input = "eq_band_8:In" }
{ output = "eq_band_8:Out" input = "eq_band_9:In" }
{ output = "eq_band_9:Out" input = "eq_band_10:In" }
{ output = "eq_band_10:Out" input = "eq_band_11:In" }
{ output = "eq_band_11:Out" input = "eq_band_12:In" }
{ output = "eq_band_12:Out" input = "eq_band_13:In" }
{ output = "eq_band_13:Out" input = "eq_band_14:In" }
{ output = "eq_band_14:Out" input = "eq_band_15:In" }
]
inputs = [ "eq_preamp:In" ]
outputs = [ "eq_band_15:Out" ]
}
capture.props = {
node.name = "effect_input.eq6"
media.class = Audio/Sink
audio.channels = 2
audio.position=[FL FR]
}
playback.props = {
node.name = "effect_output.eq6"
node.passive = true
audio.channels = 2
audio.position=[FL FR]
}
}
}
]
Разберем его подробнее. name = libpipewire-module-filter-chain
– подключаем волшебный модуль filter-chain. media.name = "Equalizer Sink"
- название ноды, которое мы увидим в графе pipewire, и в GUI типа pavucontrol или kmix. Далее в массиве nodes перечислены фильтры, через которые пройдет звук. Здесь мы используем эффект eq_preamp как общий регулятор громкости эквалайзера, и 15 раз bq_peaking - каждый из них реализует одну из 15 полос нашего графического эквалайзера. Тут же задаем параметры полос – Freq это средняя частота полосы в герцах, Q - добротность, Gain - уровень этой полосы в децибелах. Частоты и добротности я задал примерно стандартные для 2/3 октавного эквалайзера (свиснул из qmmp). Значения Gain настройте как угодно, под ваши потребности.
Далее идет массив links - это соединения между нодами эффектов, которых у нас 16. Они должны быть соединены по цепочке, друг за другом, что и сделано.
И в конце, описываются параметры двух нод, которые добавятся в граф PipeWire, о чем я писал выше. capture.props это поисание ноды Equalizer Sink, на которую будут подключаться все пиложения. playback.props – свойства выходной ноды, которая будет выводить обработанный звук в звуковую карту.
После того, как вы добавите этот конфиг-файл на его место, надо перезапустить демон pipewire или перезагрузиться, или выйти-войти в GUI-сеанс. Перезапустить PipeWire можно командой systemctl --user restart pipewire
.
После перезапуска PipeWire, в pavucontrol на вкладке «Устройства вывода» должно добавиться устройство Equalizer Sink. Если включить птичку «Установить по умолчанию» напротив него, то все приложения начнут выводить звук через наш эквалайзер. Для того, чтобы отключить эквалайзер, надо включить эту птичку на любое другое устройство, то которое там было до добавления эквалайзера, у меня это «Встроенное аудио Аналоговый стерео».
То же самое можно делать через плазмоид регулятора громкости в KDE Plasma. Там добавится устройство Equalizer Sink и можно будет переключить радиокнопку на него.
Основной недостаток этого всего один – нет GUI для руления настройками! Чтобы их изменить, надо, в простейшем случае на сегодня, изменить числа в конфиге и перезапустить PipeWire.
Вот и все! В следующий раз мы создадим более сложные эффекты, и разберем вопрос, можно ли рулить настройками по-живому, без перезапуска PipeWire.