LINUX.ORG.RU

EasyEffects не нужно, или PipeWire для продвинутых: часть 2

 , ,


1

2

В первой части мы создали конфиг-файл, который добавляет 15-полосный эквалайзер в граф PipeWire. Теперь разберем вопрос, а как же изменять его параметры на лету, без перезапуска PipeWire. Вопрос очень животрепещущий, редакция завалена письмами с ним. ))

В этой части мы разберем задачу на «низком» уровне CLI-утилит. На этой базе несложно сделать любой GUI-фронтенд, который позволит управлять параметрами уже из GUI.

Сначала получим текущие настройки эквалайзера из PipeWire. Эти настройки хранятся в ноде Equalizer Sink, надо просто знать как их извлечь оттуда. Для этого есть два способа и две CLI утилиты – pw-cli и pw-dump. Начнем с первого варианта, пригодного для ручной работы (но он неудобен для скрипта).

Сначала надо получить номер ноды Equalizer Sink. Для этого выполним команду pw-cli ls Node. Получаем список всех нод, и в нем видим ноду с именем node.name = "effect_input.eq6", это как раз то имя, которое мы задали в конфиг-файле.

id 39, type PipeWire:Interface:Node/3
                object.serial = "39"
                factory.id = "19"
                client.id = "38"
                node.description = "Equalizer Sink"
                node.name = "effect_input.eq6"
                media.class = "Audio/Sink"

Следовательно, наш номер 39. запомним его. Теперь получим список параметров filter-chain, с их значениями, командой pw-cli enum-params 39 Props, где 39 – номер ноды, найденный ранее. В выводе этой команды, посмотрев чуть дальше начала, видим такое:

String "eq_preamp:Freq"
Float 0,000000
String "eq_preamp:Q"
Float 1,000000
String "eq_preamp:Gain"
Float 5,000000
String "eq_preamp:b0"
Float 1,778279
String "eq_preamp:b1"
Float 0,000000
...

И так далее. Это и есть параметры, заданные нами в конфиг-файле, с их текущими значениями. Тут есть еще параметры вида a0, b0 которые мы не задавали, это особенность эффекта bq_peak, он их сам добавляет, нам это не сильно интересно. Нам нужны параметры, заканчивающиеся на «:Gain».

Мы получили то что хотели, но в такой форме оно не удобно для человека и еще менее удобно для обработки в скрипте. Поэтому есть второй способ — выполним команду pw-dump. Эта команда выводит все параметры всех объектов pipewire в JSON формате. В этой огромной простыне, видим вот что:

{
            "params": [
              "eq_preamp:Freq",
              0.000000,
              "eq_preamp:Q",
              1.000000,
              "eq_preamp:Gain",
              5.000000,
              "eq_preamp:b0",
              1.778279,
              "eq_preamp:b1",
              0.000000,
              "eq_preamp:b2",
              0.000000,
              "eq_preamp:a0",
              1.000000,
              "eq_preamp:a1",
              0.000000,
              "eq_preamp:a2",

и так далее, все это внутри ноды 39 c node.name=«effect_input.eq6», то есть все то же самое, только в JSON формате, который уже удобно парсить в скрипте либо в приложении. Как извлечь оттуда нужную информацию в python скрипте, показано в конце статьи.

Теперь разберем главный вопрос – как эти параметры изменять. Это тоже позволяет делать утилита pw-cli, команда имеет следующую форму:

pw-cli s 39 Props '{params = [ "eq_band_6:Gain" 5.0 ]}'

s – значит set, нода 39, массив Props, имя параметра eq_band_6:Gain, установить значение 5.0. Таким простым образом можно изменить любой параметр, если мы знаем его имя и номер ноды!

Теперь, реализуем простейший Python скрипт, который будет работать как CLI утилита управления эквалайзером. Пользоваться им так:

eqcli.py get – получить текущие значения параметров, выводит вот что (номер полосы эквалайзера, ее частота, ее значение):

0. Freq:  0.0   Gain:  5.0
1. Freq:  25.0   Gain:  -10.0
2. Freq:  40.0   Gain:  -10.0
3. Freq:  63.0   Gain:  -9.0
4. Freq:  100.0   Gain:  3.0
5. Freq:  160.0   Gain:  3.0
6. Freq:  250.0   Gain:  3.0
7. Freq:  400.0   Gain:  -5.0
8. Freq:  630.0   Gain:  -5.0
9. Freq:  1000.0   Gain:  -3.0
10. Freq:  1600.0   Gain:  -3.0
11. Freq:  2500.0   Gain:  -3.0
12. Freq:  4000.0   Gain:  -5.0
13. Freq:  6300.0   Gain:  10.0
14. Freq:  10000.0   Gain:  0.0
15. Freq:  16000.0   Gain:  0.0

eqcli.py set 6 -3.5 – установить полосу номер 6 на значение -3.5 децибел. Эта команда тоже выведет значения параметров, как и команда get, но уже измененных.

Вот листинг скрипта, надо сохранить в файл eqcli.py

#!/usr/bin/python

import sys
import subprocess
import json

def parse_pw_data():
  result = subprocess.run(["pw-dump"], capture_output=True)

  parsed_data = json.loads(result.stdout.decode())

  eq_item_id = 0
  eq_item = 0

  for item in parsed_data:
    try:
      if item['info']['props']['node.name'] == 'effect_input.eq6':
        eq_item_id = int(item['id'])
        eq_item = item
    except:
      pass

  return eq_item_id, eq_item

def get_func(eq_item):
  eq_params = eq_item['info']['params']['Props'][1]['params']
  i = 0
  while i < len(eq_params):
    print(str(int(i / 18)) + ". Freq:  " + str(eq_params[i + 1]) + "   Gain:  " + str(eq_params[i + 5]))
    i = i + 18

def set_func(eq_item_id, eq_item):
  try:
    band_num = int(sys.argv[2])
    band_gain = float(sys.argv[3])
    set_command_line = ""
    if band_num == 0:
      set_command_line = "{params = [ \"eq_preamp:Gain\" " + str(band_gain) + " ]}"
    else:
      set_command_line = "{params = [ \"eq_band_" + str(band_num) + ":Gain\" " + str(band_gain) + " ]}"
    result = subprocess.run(["pw-cli", "s", str(eq_item_id), "Props", set_command_line], capture_output=True)
    print(result.stdout.decode())
    eq_item_id, eq_item = parse_pw_data()
    get_func(eq_item)
  except:
    print("Wrong parameters")

eq_item_id, eq_item = parse_pw_data()

if eq_item_id == 0:
  print("No equalizer found, exiting...")
  exit()

try:
  if (sys.argv[1] == 'get'):
    get_func(eq_item)
  elif (sys.argv[1] == 'set'):
    set_func(eq_item_id, eq_item)
  else:
    print("Unknown command")
except:
  print("No command")

Как это работает? Сначала скрипт получает JSON-описание всех объектов командой pw-dump. Находит нужную ноду. Извлекает из нее значения параметров и печатает их, если дана команда get. Формирует и вызывает pw-cli s ... если дана команда set.

Я думаю, очевидно, что этот пример не сложно расширить до GUI-варианта на PyQt, к примеру, и получить графическую «морду» для настройки нашего эквалайзера. К этому мы вернемся позднее.

★★★★

Проверено: hobbit ()
Последнее исправление: dataman (всего исправлений: 5)

на «низком» уровне CLI-утилит

А на уровне библиотек есть что-нибудь? Какой-нибудь libpw, который было бы удобнее дёргать из программ НЕ на Питоне…

hobbit ★★★★★
()
Ответ на: комментарий от hobbit

Есть конечно, сам pw-cli то как-то получает эти данные. Только там толком недокументированный и нестабильный API.

А питон здесь просто для простоты примера. Ничего не мешает парсить JSON на C или C++, это вообще не проблема.

James_Holden ★★★★
() автор топика
Ответ на: комментарий от James_Holden

Это хорошо. Если колоночки не очень, то лимитером тихое вытащить было бы очень полезно. Может даже полезнее, чем эквалайзер.

ist76 ★★★★★
()

На этой базе не сложно сделать любой GUI-фронтенд, который позволит управлять параметрами уже из GUI.

И которого почему-то до сих пор никто не сделал. И загружалку плагинов к гуевым менеджерам соединений тоже никто не прикрутил.

То есть, снова «смотрите, как славно, имея лишь лишь блокнот, гигабайт документации и всю беззаботную жизнь впереди, можно запердолиться с нашей тулзой».

Всё. Как. Всегда.

thesis ★★★★★
()
Ответ на: комментарий от dataman

Это интересный проект, но тут тоже проблемный вопрос - не тот профиль, это не для простого пользователя десктопа. А я собираюсь сделать просто базовый простой «улучшайзер» для обычных людей, нечто вроде тех что идут с драйверами в Windows. Это куда проще и в разработке, и в использовании юзером.

Он настолько просто будет устроен, что я разберу его прямо в рамках этих статей.

James_Holden ★★★★
() автор топика
Ответ на: комментарий от artempl

Да, только конфиг будет чуть чуть отличаться, чтобы цеплялось ко входу а не к выходу. Но принцип точно такой же.

James_Holden ★★★★
() автор топика
Ответ на: комментарий от James_Holden

А я себе отдельную обработку на выход поставил, чтобы обрабатывать звук с микрофона товарища, очень удобно кстати и звук во всех программах работает одинаково.

Dr64h ★★★
()
Ответ на: комментарий от thesis

Намного проще, всего на минималках. Для тех кому надо максимально легкое.

Эквалайзер + нормализация + псевдо 3D пространство.

James_Holden ★★★★
() автор топика
Последнее исправление: James_Holden (всего исправлений: 1)
Ответ на: комментарий от James_Holden

Клево. Это, в принципе, это ровно то, что обычно и требуется. И быстрый выбор пресетов сверху.

thesis ★★★★★
()
Последнее исправление: thesis (всего исправлений: 1)

Маленькая просьба - делай ссылки на предыдущую и следующую части. Потом удобнее будет.

А через pw-cli s можно ladspa/lv2 плагинам подкручивать? Раньше нельзя было. Или я забегаю вперёд?

R_He_Po6oT ★★★★★
()
Ответ на: комментарий от R_He_Po6oT

Это описано в третьей части, которая весь день весит в неподтвержденных пока ))

James_Holden ★★★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.