BruteFIR module for PulseAudio/PipeWire

Hi all,

after many unsuccessful attempts to integrate BruteFIR into my stereo system (LibreELEC.tv, which is missing the ALSA-loopback module) i decided to write an IO-module to allow direct sound-output to PulseAudio/PipeWire from within BruteFIR.

The module provides a pulseaudio sink-input and source-output in PulseAudio/Pipewire which can then be routed using pactl or another patchbay (e.g. Helvum).

Currently the module has hard-coded settings for 2 channels, a sample-rate of 44.1 KHz and a sample-format of S16LE, but i expect this to change in the future ;).

To use the new module configure the input-/-output device of BruteFIR to be of type pulse, e.g.

Code:
input "front-left", "front-right" {
        device: "pulse" {};  # Use pulseaudio-module
        sample: "S16_LE";   # sample format
        channels: 2/0,1;    # number of open channels / which to use
        mute: false,false;  # mute active on startup for each channel
};

output "front-left", "front-right" {
        device: "pulse" {};  # Use pulseaudio-module
        sample: "S16_LE";   # sample format
        channels: 2/0,1;    # number of open channels / which to use
        mute: false,false;  # mute active on startup for each channel
};

To filter your audio signal the resulting sink-input and source-output must be connected to their appropriate sinks/sources.

For BruteFIR-input load the PulseAudio-module module-null-sink via

Code:
pactl load-module module-null-sink sink_name=BruteFIR format=s16le rate=44100 channels=2
.

Its monitor-output BruteFIR.monitor should be connected to BruteFIRs input. BruteFIRs output has then to be routed to your desired (virtual) audio-device.

Hopefully the attached screenshot shows how such a setup would look like.

If you are intereested the source-code can be found on https://github.com/chipfunk/brutefir

I appreciate any feedback.
For reporting bugs please use the GitHub issue-tracker.


Happy filtering :)
 

Attachments

  • helvum_brutefir_pipewire.png
    helvum_brutefir_pipewire.png
    68.3 KB · Views: 70
Last edited:
  • Like
Reactions: 1 user
Thanks Henrik, means a lot to me.

How do you handle the difference in sample clock between the null-sink with the real playback device?
I didn't think about that as i'm targetting a functional system/integration as a first step. Currently i expect PulseAudio to handle any timing/delay/synchronization issues. The libsimple-API i am using exhibits blocking-behavior, so the read()/write()-calls simply pass on the specified amount of samples to read/write into/from BruteFIRs buffer (see pa_simple_read()) when the data is available.

I'm already looking into libpulse and its asynchronous non-blocking behaviour to allow for greater control/configurability.
 
I'm happy to report i could release an initial version yesterday.

Link to sources

This release features:
  • initial PulseAudio support as BFIO-module
  • configurable target device-names to control/hint connectivity for PulseAudio-server
  • PulseAudio-server, application- and stream-name are also configurable, to allow for networked multi-filter/-process/-stream setups (not tested though)

To use this IO-module in BruteFIR configure it as device "pulse".

Compiling from sources works just as the original, run make.

My next goal is an implementation using PulseAudio's asynchronous API to allow for some delay-awareness/-control. Will see how this plays out. Thanks for the question @HenrikEnquist, got me thinking :).


Hear you soon

P.S. This release allowed me to build the corresponding LibreELEC.tv addon, too
 
@b_force I could try to package a Fedora RPM, as that is the OS i am using. I do not have access to a Windows-system or -compiler. I will take a quick look into mingw64 for cross-compilation as other projects seem to use it successfully. I think its a good idea get a build-job on GitHub running, then expand for the different target platforms.

What systems do you need binaries for?
 
Last edited:
  • Like
Reactions: 1 user
@b_force Thanks for your clarification, i get your point. It should be possible to build these packages in docker on GitHub. If i find some free time i will look into it here. My primary goal is to have the I/O-module included in the main BruteFIR source. That way all existing distributions could leverage that.

P.S. i provide binaries for LE 11.0 here
 
Last edited:
  • Like
Reactions: 1 user
@phofman Thanks for explanation, allows me to understand the challenge better.

If someone wants to experiment ... i committed configurable buffer-attributes for PulseAudio, but have to do more testing before making new release.

A config-section "input" (or "output") would look like this:

Code:
input "left", "right" {
        device: "pulse" {
                device: "BruteFIR.monitor";
                buffer_attr: {
                        maxlength: -1;
                        tlength: -1;
                        prebuf: -1;
                        minreq: -1;
                        fragsize: -1;
                };
        };
        sample: "S16_LE";
        channels: 2/0,1;
        mute: false,false;
};

Please see here and here for more information about the buffer-settings.
 
Last edited:
Ooops,

i accidentially left the repository's visibility set to private, that wasn't very helpful. It's public now and should be visible to anyone. Sorry for that.

In the meantime i was able to implement most of PulseAudio's asynchronous- as well as PipeWire's native-API by using BruteFIRs callback-feature for BFIO-modules. The remaining challenge is the handover of the audio-buffers to BruteFIR.

From what i've seen the CPU-usage dropped significantly using this callback-approach, lets hope it stays that way.

Based on this proress i hope to release a new version soon :).
 
Last edited:
  • Like
Reactions: 1 user