Во второй части мы разобрали вопрос, как управлять параметрами эффектов в реальном времени, без перезапуска PipeWire.
Теперь рассмотрим, как можно добавить сторонний плагин, реализующий эффект, который не встроен в PipeWire, и как управлять его параметрами.
Модуль filter-chain, помимо встроенных (builtin) эффектов, поддерживает два самых распространенных в Linux стандарта DSP плагинов - LADSPA и LV2. Прежде всего, эти плагины предназначены для использования в DAW системе (цифровая звуковая рабочая станция, в которой «делают музыку»), такой как Ardour, Qtractor, LMMS. Они реализуют множество эффектов, таких как эквалайзер, компрессор, лимитер и тому подобное.
LADSPA это более простой и более старый стандарт, LV2 более развитый, но большинство популярных в Linux наборов LV2 плагинов поставляются и в LADSPA варианте. PipeWire может использовать плагины обоих стандартов, поэтому используем сразу LV2 вариант.
Реализуем очень нужный многим эффект – нормализацию громкости, или компрессию. То есть, нам нужно, чтобы тихие звуки стали громче, и были слышны в зашумлённой обстановке (на работе, к примеру) из плохих динамиков ноутбука. Также эта проблема очень актуальна при просмотре фильмов, часто есть огромная разница в громкости между диалогами и активными сценами (когда начинают бить морды). Надо эту разницу уменьшить.
Для этого мы используем замечательный набор плагинов LSP. Эти плагины доступны во всех основных форматах, мы же используем LV2 версию. В составе набора есть плагин Compressor Mono, который хорошо подходит для нашей задачи – там предусмотрен «обратный» режим работы, когда тихие звуки делаются более громкими (а не только громкие – более тихими, как в большинстве аналогичных плагинов). Это именно то, что нужно.
Добавим этот плагин в цепочку фильтров filter-chain. Для этого, сначала надо узнать такую вещь, как URI плагина. Это такая форма его названия, в виде веб-адреса. Но это не настоящая веб-ссылка, просто такая форма ))). В общем, это тяжело объяснить.
Чтобы узнать URI плагина, выполним команду lv2ls
. Команда выдаст URI всех плагинов, которые установлены в системе. Среди них должен быть нужный нам плагин http://lsp-plug.in/plugins/lv2/compressor_mono
. Если такого нет, надо установить набор плагинов LSP, при помощи пакетного менеджера дистрибутива.
Теперь добавим в конфиг-файл из предыдущих частей вот такой кусок, в массив nodes (полная версия конфига будет в конце статьи):
{
type = lv2
name = compressor
label = compressor
plugin = "http://lsp-plug.in/plugins/lv2/compressor_mono"
control = { "cm" = 1 "cr" = 4.0}
}
Как видно, это очень похоже на добавление builtin эффекта. Только теперь тип будет lv2
, plugin
- указываем URI плагина. name
, label
– название, которое мы сами даём этому эффекту, может быть любое. По нему потом всё будет доступно через pw-cli
для изменения параметров. Добавляем два параметра - "cm" = 1
это режим работы, который делает тихие звуки громче (а не громкие – тише). "cr" = 4.0
– это степень компрессии, во сколько раз. Чем больше это значение, тем больше будет выравниваться громкость.
Здесь важно, что параметров у этого плагина намного больше, а нам нужно указать только те, которые мы изменяем – остальные будут иметь значение по умолчанию.
Очень тяжелый вопрос: откуда же брать названия параметров плагина, допустимые значения этих параметров, чтобы прописать сюда? Как узнать, что есть вот именно два этих параметра, и они называются вот так?
Для этого используем утилиту lv2info
. Выполним команду
lv2info http://lsp-plug.in/plugins/lv2/compressor_mono
В выводе команды видим всю информацию о плагине, нас интересуют блоки вида Port N:
. Порты у плагина, упрощенно, есть двух видов - это входы/выходы, через которые передается звук, и параметры самого плагина, которые можно задавать и изменять. Находим среди портов те, которые соответствуют нужным нам параметрам. Ищем, ориентируясь на поле Name
, оно говорящее, сразу понятно что это.
Port 17:
Type:
http://lv2plug.in/ns/lv2core#ControlPort
http://lv2plug.in/ns/lv2core#InputPort
Scale Points:
0 = "Down"
1 = "Up"
2 = "Boot"
Symbol: cm
Name: Compression mode
Minimum: 0.000000
Maximum: 2.000000
Default: 0.000000
Properties: http://lv2plug.in/ns/ext/port-props#hasStrictBounds
http://lv2plug.in/ns/lv2core#integer
http://lv2plug.in/ns/lv2core#enumeration
Это описание параметра cm
, который мы меняем в конфиг-файле. Из этого куска мы узнаем, что есть параметр Name: Compression mode
, он имеет имя Symbol: cm
, может принимать значения 0, 1 или 2.
Scale Points:
0 = "Down"
1 = "Up"
2 = "Boot"
Нам нужен режим Up (делать громче тихий звук), значит мы должны установить этот параметр в значение 1.
Далее, надо «залинковать» этот дополнительный эффект к уже добавленному ранее эквалайзеру. Для этого добавляем в конфиг-файле, в массив links:
{ output = "eq_band_15:Out" input = "compressor:in" }
то есть подключить вход компрессора к выходу 15 полосы эквалайзера. И последний штрих: надо сделать выходом всей цепочки эффектов – выход компрессора.
outputs = [ "compressor:out" ]
Теперь, помимо эквалайзера, у нас в цепочке обработки будет компрессор. Какими параметрами может потребоваться управлять? Можно включать/отключать компрессор
pw-cli s 36 Props '{params = ["compressor:enabled" 0]}'
pw-cli s 36 Props '{params = ["compressor:enabled" 1]}'
Не забываем, что 36 – это номер ноды Equalizer Sink, у вас он может быть совсем другим. Как его определить - см. Часть 2.
Ещё можно подрегулировать степень компрессии, параметр cr
.
pw-cli s 36 Props '{params = ["compressor:cr" 10.0]}'
Вот и всё, два основных эффекта мы реализовали )). Полная версия получившегося конфиг-файла будет выглядеть так:
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" = 5.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" = 3.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 }
}
{
type = lv2
name = compressor
label = compressor
plugin = "http://lsp-plug.in/plugins/lv2/compressor_mono"
control = { "cm" = 1 "cr" = 4.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" }
{ output = "eq_band_15:Out" input = "compressor:in" }
]
inputs = [ "eq_preamp:In" ]
outputs = [ "compressor: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]
}
}
}
]