Skip to content

DSP policy for handling audio devices requiring filter chains

Dmitry Sharshakov requested to merge dsseng/wireplumber:dsp-monitor into master

Scope

Addresses pipewire#2210

The issue should remain open for further improvements yet to come with 0.5 (next-rebased)

Description

For now, a DSP policy is added for automatically adding appropriate filter chains by rules from configuration.

Progress

  • Load filter chains on demand
  • Unload filter chains when corresponding sink is unplugged (needs the fix from pipewire!1351 (merged))
  • A way to update profile
    • Prevent profile policy from touching the profile Use device-profile to switch the DSP nodes
  • Hide sinks from the graph (might require PipeWire changes, but access policy should be enough)
    • Hide intermediate streams
    • Disable hiding in a config
    • Check for validity (not conflicting with other policies)
    • Disable the possibility of user switching the profile in pavucontrol or somewhere else
    • Prevent streams from being transitionable. For now, EasyEffects could move the stream to its sink (This will be achieved when streams are hidden)
  • Support sources (microphone with DSP)
  • Also filter by device constraints (if really useful)

Optional:

  • Better way to describe filter-chains (pipewire!1359 (closed))
    • Load JSON files from WP
    • Populate target serial into the configuration

The remaining tasks to be moved to next-rebased are now tracked back in the original issue: pipewire#2210 (comment 1518875)

Testing

This is written into my src/config/policy.lua.d/51-dsp-config.lua (should really be somewhere in /usr/share):

table.insert (dsp_policy.policy.rules,
  {
    matches = {
      {
        { "media.class", "matches", "*/Sink" },
        { "node.name", "=", "alsa_output.pci-0000_03_00.6.analog-stereo" }
      },
    },
    device_matches = {
      {
        { "device.name", "=", "alsa_card.pci-0000_03_00.6" }
      },
    },
    filter_chain = '{"media.name":"My DSP Speakers 2", "capture.props":{"node.virtual":false, "audio.channels":2, "audio.position":["FL", "FR"], "node.name":"audio_output.platform-audio.analog-stereo-laptop", "media.class":"Audio/Sink", "device.api":"dsp"}, "node.description":"My DSP Speakers 2", "filter.graph":{"outputs":["conv_l:Out", "conv_r:Out"], "links":[{"output":"mix:Out", "input":"copy_l:In"}, {"output":"mix:Out", "input":"copy_r:In"}, {"output":"copy_l:Out", "input":"conv_l:In"}, {"output":"copy_r:Out", "input":"conv_r:In"}], "nodes":[{"type":"builtin", "control":{"Gain 1":0.500000, "Gain 2":0.500000}, "label":"mixer", "name":"mix"}, {"type":"builtin", "label":"copy", "name":"copy_l"}, {"type":"builtin", "label":"copy", "name":"copy_r"}, {"type":"builtin", "name":"conv_l", "label":"convolver", "config":{"filename":"/tmp/firs_j314_tweeters.wav"}}, {"type":"builtin", "name":"conv_r", "label":"convolver", "config":{"filename":"/tmp/firs_j314_tweeters.wav"}}], "inputs":["mix:In 1", "mix:In 2"]}, "playback.props":{"audio.channels":2, "audio.position":["AUX0", "AUX1"], "target.object":"alsa_output.pci-0000_03_00.6.analog-stereo", "node.passive":true, "node.name": "dsp_output.platform-audio.analog-stereo-laptop"}}',
    properties = {
      profile = "output:analog-stereo+input:analog-stereo",
    },
  }
)

-- Used to hide streams
table.insert (dsp_policy.policy.rules,
  {
    matches = {
      {
        { "node.name", "=", "dsp_output.platform-audio.analog-stereo-laptop" }
      },
    },
    properties = {
      hide = true,
    },
  }
)

table.insert (dsp_policy.policy.rules,
  {
    matches = {
      {
        { "media.class", "matches", "*/Source" },
        { "node.name", "=", "alsa_input.pci-0000_03_00.6.analog-stereo" }
      },
    },
    filter_chain = '{"media.name":"My DSP mic", "capture.props":{"audio.channels":2, "audio.position":["AUX0", "AUX1"], "target.object":"alsa_input.pci-0000_03_00.6.analog-stereo", "node.passive":true, "node.name": "dsp_input.platform-audio.mic"}, "node.description":"My DSP mic 2", "filter.graph":{"outputs":["conv_l:Out", "conv_r:Out"], "links":[{"output":"mix:Out", "input":"copy_l:In"}, {"output":"mix:Out", "input":"copy_r:In"}, {"output":"copy_l:Out", "input":"conv_l:In"}, {"output":"copy_r:Out", "input":"conv_r:In"}], "nodes":[{"type":"builtin", "control":{"Gain 1":0.500000, "Gain 2":0.500000}, "label":"mixer", "name":"mix"}, {"type":"builtin", "label":"copy", "name":"copy_l"}, {"type":"builtin", "label":"copy", "name":"copy_r"}, {"type":"builtin", "name":"conv_l", "label":"convolver", "config":{"filename":"/tmp/firs_j314_tweeters.wav"}}, {"type":"builtin", "name":"conv_r", "label":"convolver", "config":{"filename":"/tmp/firs_j314_tweeters.wav"}}], "inputs":["mix:In 1", "mix:In 2"]}, "playback.props":{"node.virtual":false, "audio.channels":2, "audio.position":["FL", "FR"], "node.name":"audio_input.platform-audio.mic", "media.class":"Audio/Source", "device.api":"dsp"}}',
    properties = {
      profile = "output:analog-stereo+input:analog-stereo",
    },
  }
)

table.insert (dsp_policy.policy.rules,
  {
    matches = {
      {
        { "node.name", "=", "dsp_input.platform-audio.mic" }
      },
    },
    properties = {
      hide = true,
    },
  }
)
Edited by Dmitry Sharshakov

Merge request reports