Pulseaudio Crossover Rack - multi-way crossover design & implementation with linux

As far as I can see this relies on multichannel audio output via HDMI. If you get this working reliably you should be golden. If so, this looks like a really promising board for a no-frills multichannel audio output solution for the RPi. But: No one has reported yet that multichannel audio output via HDMI actually works on the RPi or how to get it working, there have only been some rumors that this doesn't work out of the box. Maybe the new RPi 4 will work better in this regard? I for myself can't tell as I don't use an RPi, at least not for audio/video.

Thanks buddy.
I'm reasonably competent on Linux and low-level hacking, so I'll keep everyone updated if I get it to work. Just ordered a RasPi 4.
BTW, I've played with Qt a bit, and think you've done an outstanding job with the UI and the architecture of your app.
 

I have been looking at exactly the same solution.
As far as I understand it can be tricky to use HDMI audio as really separate streams instead of some kind of surround stream. But I might be completely wrong ... :D
 
So here's my shoutout to developers and people having deeper knowledge of FIR filters: Do you happen to know a fast and efficient FIR filter library in C/C++/Rust that I can use for a LADSPA plugin?


No developer here, but maybe this link might help: SSE / AVX FIR VST PC Channel divider (crossover), delay, EQ, DRC - koonaudioprojects
I used those VST-plugins quite a while ago with great success, low latency, low CPU usage. Maybe the provided sourcecode can point you in the right direction...


As for the multichannel sound over HDMI on Raspberry Pi problem, the solution is documented in a German forum: Raspberry Pi 2 Möglichkeiten? - Seite 11
At least with the jesse core this works. Tranlation:
Multichannel audio via HDMI was removed from the current kernel. However, you can compile your own. To activate multichannel audio you just need to change /linux/sound/arm/bcm2835-pcm.c within the kernel sources. The changed section should look like this:
Code:
]/* hardware definition */
static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
    .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
         SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
    .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
    .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
    .rate_min = 8000,
    .rate_max = 192000,
    .channels_min = 1,
    .channels_max = 8,
    .buffer_bytes_max = 128 * 1024,
    .period_bytes_min =   1 * 1024,
    .period_bytes_max = 128 * 1024,
    .periods_min = 1,
    .periods_max = 128,
};

static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
    .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
         SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
    .formats = SNDRV_PCM_FMTBIT_S16_LE,
    .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
        SNDRV_PCM_RATE_192000,
    .rate_min = 44100,
    .rate_max = 192000,
    .channels_min = 2,
    .channels_max = 8,
    .buffer_bytes_max = 128 * 1024,
    .period_bytes_min =   1 * 1024,
    .period_bytes_max = 128 * 1024,
    .periods_min = 1,
    .periods_max = 128,
};
 
I'm reasonably competent on Linux and low-level hacking, so I'll keep everyone updated if I get it to work. Just ordered a RasPi 4.
Yes, please keep us updated, especially the RPi lovers out there...

BTW, I've played with Qt a bit, and think you've done an outstanding job with the UI and the architecture of your app.
Thanks, very much appreciated! I'm quite proud of the code I produced myself I have to say :D:D
 
I have been looking at exactly the same solution.
As far as I understand it can be tricky to use HDMI audio as really separate streams instead of some kind of surround stream. But I might be completely wrong ... :D

As far as I know there is nothing special about HDMI vs. other multi channel solutions. Splitting/filtering/routing the signals is done by pulseaudio crossover rack, so in theory it should just work ;)
 
No developer here, but maybe this link might help: SSE / AVX FIR VST PC Channel divider (crossover), delay, EQ, DRC - koonaudioprojects
I used those VST-plugins quite a while ago with great success, low latency, low CPU usage. Maybe the provided sourcecode can point you in the right direction...

Thanks very much! Just glanced over it and he's talking a lot about SSE and AVX, which would make this code not protable to any other architecture than i686 (SSE) or even amd64 (AVX)... Might have to dig into the brutefir code some day (*shrugs* :hypno1:)
 
Performance testing...

For all those who have asked about performance etc.:

The other day at a friend's we did some brute-force performance testing with a modified DIYINHK multichannel XMOS USB->I2S converter with our custom firmware, using 5 I2S lines, i.e. 10 channels running at 192kHz (The screenshot says hardware not available, as I made the screenshot at home where the device was unavailable to me). The pulseaudio backend was running at 384kHz, sample format was float32le.

The file packs five ways of stereo output with each channel using a fully activated 5-band eq, two LR-4 low- and highpass filters each and a subsample delay.

The CPU usage varied a bit, depending on the current boost clock of the CPU, which is a Intel i5 8250U laptop processor with 4 cores, 8 threads, base clock of 1.6GHz and max boost of 3.4GHz. As I recently was made aware of the architecture of pulseaudio makes all the LADSPA plugins run in the context of the ALSA output thread, so only one CPU core is used for DSP stuff! Keep that in mind when designing a crossover PC...

So, after rambling on quite a bit, what were the numbers? Using the speex-float-1 (default) resampler the CPU usage peaked around 45% on one core. When using the soxr-vhq resampler performance peaked around 55% on one core (had to recompile pulseaudio from source to do that, as linux mint 19.x still does not ship with SOXR enabled :()

Please don't ask about specific processor combinations and do the math yourself... :D But what I take away from this is: even a medium range, low TDP processor (15W in my case) should pack plenty of power for even a sophisticated crossover setup!
 

Attachments

  • excessive_five_way.paxor.zip
    791.4 KB · Views: 49
  • excessive_five_way.png
    excessive_five_way.png
    333.4 KB · Views: 236
Thanks very much! Just glanced over it and he's talking a lot about SSE and AVX, which would make this code not protable to any other architecture than i686 (SSE) or even amd64 (AVX)... Might have to dig into the brutefir code some day (*shrugs* :hypno1:)

Most C code (should be) is cross platform. Setting up the build chain should produce something that could run on ARM. Dunno myself.
 

I have experience with these board with a Raspberry Pi. I bought a couple branded as "SupTronics". I posted about them in another thread on this forum. If you plan to use them with the Pi you will need to tweak the kernel to get them to output more than 2 channels of audio (see link below for info). They have pretty good performance.

Check out this thread for more info:
2-in, 8-out DSP platform using the Raspberry Pi + HATs
 
Btw, would there be a way to "program" and change the filters besides the standard paxoverrack "software"?
I am mostly thinking about some kind of http type connection or so.
That way it would be possible to change, add, setup filters with a remote machine or something.

Remote desktop would be something, but that is a little bit overkill

Yes and No. What a f**king useless answer, I know ;)

a) There is a *very* low level shared memory interface available from the LADSPA plugins, If I were you I would not go this route. (No documentation available, if you disregard my advice, please read the plugin code!)
b) If you have a use case and want to develop a remote configuration utility, I would spend the time to implement a REST/http API for querying filters/changing filter parameters. I will not implement any user interface for this (Android app, iOS app, web interface) though. For my needs, remote desktop or vnc is perfectly fine. If you decide you want to implement a remote app (whatever platform it may be) I can flesh out a remote control API in less than a few hours because I implemented my own music player which has a remote API and a android remote control app.
BUT: I will only do this if you really want to implement something and you are willing to share it with the rest of us!

PS: This is open source, you can always hack the project and submit a merge request... phofman brought me down to earth on this once before, but I strongly believe that there are competent and willing hackers out there... :D
 
Last edited:
Yes and No. What a f**king useless answer, I know ;)

a) There is a *very* low level shared memory interface available from the LADSPA plugins, If I were you I would not go this route. (No documentation available, if you disregard my advice, please read the plugin code!)
b) If you have a use case and want to develop a remote configuration utility, I would spend the time to implement a REST/http API for querying filters/changing filter parameters. I will not implement any user interface for this (Android app, iOS app, web interface) though. For my needs, remote desktop or vnc is perfectly fine. If you decide you want to implement a remote app (whatever platform it may be) I can flesh out a remote control API in less than a few hours because I implemented my own music player which has a remote API and a android remote control app.
BUT: I will only do this if you really want to implement something and you are willing to share it with the rest of us!

PS: This is open source, you can always hack the project and submit a merge request... phofman brought me down to earth on this once before, but I strongly believe that there are competent and willing hackers out there... :D
I am all about the sharing, so that's not the issue.

But what I've seen is tons of very nice open source projects with heaps of potential. Unfortunately it's mostly the user interface that makes it difficult for a lot of users to use.

This is a good example of this. I mean that on a very positive way actually!
I see this totally working with just a simple Rasp Pi or Rasp Pi Zero with a little codec (ADC/DAC) or just as a standalone HTPC.

I would love to help you, but I am an acoustic/electronic and conceptional engineer developing systems and PCB's. So my knowledge is only limited to programming ucontrollers.

If you're open for it, I can write down the whole idea and use case for it.
But basically it doesn't have to be fancy at all.
 
There are plans but no estimate yet. Given the fact that all the basic filters for building analog-like crossovers are provided as of now my priority for FIR filters is low. At some point I will give this a shot though as I want to use non causal filters to introduce inverse bandpass filters to compensate for the phase shift introduced by crossover filters (i.e. 360° phase shift for a LR4 filter).

The problem at the moment is that there is no readymade FIR filter LADSPA plugin I can use as it will have to be able to load coefficients from disk. So I will have to write up my own and at the moment I do neither have the time to do that nor do I have the knowledge. I thought about using pipe communications to brutefir but after some research and thinking i ditched the idea because then the brutefir processes would have to be spawned by PaXoverRack (by the GUI). That would destroy the ability to have to whole filter chain setup just by the default.pa configuration file which means that filters are always started when pulseaudio starts. This feature i deem to be quite important for ease of use, just configure the filter chain once and have it running seamlessly in the background regardless if the system is restartet and whatnot.

So here's my shoutout to developers and people having deeper knowledge of FIR filters: Do you happen to know a fast and efficient FIR filter library in C/C++/Rust that I can use for a LADSPA plugin?


I have and am continuing to develop a convolution engine for FIR processing on Linux.



However:


After investigating various integration points into the Linux audio stack, I've started integrating my library as an ALSA ioplug, instead of a LADSPA plugin. My primary reasons for choosing this path are the (IMO) very limited API offered to LADSPA plugin authors and the absence of access to the ALSA Configuration API.


ATM, several computationally intensive routines of my library rely on x86-specific SIMD code, but these could be fairly simply translated for the ARM NEON or similar equivalents, with the correct run-time path chosen based on compiler built-ins.


Oh, and nice project! I've been following this thread for a while now, and wondering when the topic of FIR processing would rear its ugly head ;-)
 
I have and am continuing to develop a convolution engine for FIR processing on Linux.



However:


After investigating various integration points into the Linux audio stack, I've started integrating my library as an ALSA ioplug, instead of a LADSPA plugin. My primary reasons for choosing this path are the (IMO) very limited API offered to LADSPA plugin authors and the absence of access to the ALSA Configuration API.


ATM, several computationally intensive routines of my library rely on x86-specific SIMD code, but these could be fairly simply translated for the ARM NEON or similar equivalents, with the correct run-time path chosen based on compiler built-ins.


Oh, and nice project! I've been following this thread for a while now, and wondering when the topic of FIR processing would rear its ugly head ;-)

I have toyed with the idea of creating an FIR LADSPA plugin. To get around the API issue, I would have the user explicitly inline the coefficients as const doubles in the plugin and then recompile whenever the coefficients change. You have to do that with all of the other LADSPA plugins that I wrote, or any that do not come thru a distro.

What do you use as the convolution engine and all the other stuff necessary for FIR processing? FFTW? I would be interested to know. If you are willing to share code (I write om C/C++) I or we could try to adapt it to LADSPA... drop me a PM if you are interested.
 
In my humble experience it is pretty hard to beat BruteFIIR. What are you attempting to craft?


I would be grateful if you could provide some performance benchmarks of BruteFIR, other than the ones here, and on modern hardware.


But, in what sense is it hard to beat?
Throughput? Latency? Features? Ease of use? Extensibility? Support?


I needed such a tool for a diy speaker project and have the skills to do it. My intent was merely to (re-)learn some DSP and have fun along the way.
 
To get around the API issue, I would have the user explicitly inline the coefficients as const doubles in the plugin and then recompile whenever the coefficients change.


With respect, I think there are much more sensible ways to do this that don't require recompilation.

What do you use as the convolution engine and all the other stuff necessary for FIR processing? FFTW? I would be interested to know.


Yes, I use FFTW to transform the input and filter coefficients for spectral convolution. The rest of the framework is my own, but based upon relatively recent research into implementing partitioned convolution.



If you are willing to share code (I write om C/C++) I or we could try to adapt it to LADSPA... drop me a PM if you are interested.


The code base is under heavy development in my spare time, but yes, I would share when it's fit for public consumption.


Perhaps you might do me favor and explain the appeal (as you see it) of LADSPA? Every time I look at the code base there, I come away asking myself, "why is this a thing?"...
 
With respect, I think there are much more sensible ways to do this that don't require recompilation.

I think so too. My plan was to have a directory for impulse responses (~/.config/PaXoverRack/IRs) and put files named 1.wav, 2.wav ... etc in there. Then I would use one LADSPA parameter to choose the Impulse response file. A bit ugly, but nontheless it will probably work.

Yes, I use FFTW to transform the input and filter coefficients for spectral convolution. The rest of the framework is my own, but based upon relatively recent research into implementing partitioned convolution.

The code base is under heavy development in my spare time, but yes, I would share when it's fit for public consumption.

I have read a lot about partitioned convolution using FFT lately, to the point I think I have understood the concept. So my plan was to craft a LADSPA plugin with
a) a "dumb concolution algorithm" - just to proove it works and then
b) implement partitioned convolution using FFTW.
To make FFTW start up fast I would create the necessary wisdom files in the GUI program ahead of time, also store them in some configuration dir and the LADSPA plugin could then read it and be up and running fast.

What do you guys think of that? If I have the concepts down correctly then we can talk about code sharing...

Perhaps you might do me favor and explain the appeal (as you see it) of LADSPA? Every time I look at the code base there, I come away asking myself, "why is this a thing?"...

I simply chose LADSPA because they plug in nicely into pulseaudio. Maybe that was not the best choice but well... You never really know until you try it yourself ;)