JUCE 6 convolution issue?

Hello, I am using JUCE 6.0.8. I modified the Example ConvolutionDemo slightly to be able to load a high pass filter and low pass filter.

In load file, a 5 Hz to 24 kHz sweep file is loaded. It has a start and stop chirp so that the REW measurement software waits for the chirp to synchronize starting and stopping the analyzer.

Selecting Bypass in the ConvolutionDemo and running a sweep shows a perfectly flat response. I am using VB-Cable loopback to route the ConvolutionDemo output into the input of REW. As far as I know, everything is at 32 bit float 48 kHz SR.

I imported the LPF and HPF filters in REW. I then selected each filter in ConvolutionDemo and swept the low pass and then high pass. Here are the results:

Is this to be expected?

Filters and sweep files

Kind regards,
Mitch

If you mean the finite suppression skirts on the sides that is expected when using FIR filters.

I’m guessing that the LPF and HPF are IIR filters, and then you take their impulse response and load the first N coefficients into the Convolution’s impulse response. The reason that causes a change is that the original filter had an infinite impulse response but you only took the first N sample.

If you loaded longer and longer impulse responses you should see the observed transfer become closer to the original.

1 Like

Thanks for your response. I generated 4th order LW FIR crossovers at 131,072 taps. Here are the filters: FIR filters

I loaded those into ConvolutionDemo and using the sweeps from the first post, here are the results:

I then turned off trimming and there is good improvement in the HPF and a little better for the LPF.

Still the expected performance?

Kind regards,
Mitch

No, I guess something must be off then. Those are some pretty long filters and looking at your HPF wav you should be getting way more suppression in the stop band.

The Convolution class itself should not modify the impulse response, so given that it works correctly that does not explain the difference you are seeing. I did a quick test with the ConvolutionDemo myself and couldn’t find an obvious problem. First I thought it may be truncating the impulse response but that doesn’t seem to be the case.

Your setup is complicated enough already that it may be tricky to pinpoint the root of the problem. The plots you have obtained reflect not just properties of the ConvolutionDemo but the transfer of the entire system involved in the measurement, including the measurement technique itself. Measuring a constant magnitude in bypassed mode does not guarantee perfect measurement in all situations.

So what you could do is make sure that the ConvolutionDemo itself works correctly. You could do that by instrumenting your code so that you send a single impulse [1.0, 0.0, ..., 0.0] to its process() function and save all samples that it outputs. The time domain result should match your loaded impulse response apart from floating point errors.

Thanks for looking into this. I hear you on the round trip on the test setup, but I have checked it with other convolvers and there is much more suppression in the stop band than the ConvolutionDemo. Hence my post.

Since I am already setup, and as quick check, I loaded a Dirac pulse: Dirac_Pulse_32bit_48kHz. With the filter loaded, the frequency and phase response measured perfectly flat, group delay flat, step response looks identical save some high frequency ringing. Looking at the impulse response.

Dirac pulse:

DemoConvolution measured Dirac pulse:

DemoConvolution Bypass:

I could go the next step as you say to look at the recorded samples, but based on this measurement, it looks as it should…

Kind regards,
Mitch

The bypassed magnitude response already looks low-pass filtered, which if you don’t have any physical cables or analogue interfaces included in the signal processing path is a bit suspect.

If the sampling rate of the IR wav, the sampling rate of the ConvolutionDemo and the sampling rate of REW are all equal, then it should be possible to get back the single dirac pulse without the low pass smearing that we see here.

If it was only the ConvolutionDemo (and not the bypass too) I would have said to check if the IR is not being downsampled from 48 to 44.1 kHz. You could do that by putting a breakpoint in resampleImpulseResponse() inside juce_Convolution.cpp and see if srcSampleRate and destSampleRate are 48 kHz and equal.

Thanks again. I loaded the Dirac impulse response to play in the ConvolutionDemo and then convolved it with the same Dirac pulse and recorded the output into Audacity through VB-Cable loopback and imported that into REW:

I would think this is close enough validation that there are no timing issues and any small errors are a result of the Windows OS, Audacity, VB-Cable and REW test setup.

Given the test HPF and LPF filters supplied, can you test for the stop band suppression issue please.

Kind regards,
Mitch

More info. JRiver and Roon are music players with built in convolvers. Running the sweeps through the filters for each convolver produced near identical results to the Convolution Demo:

The original issue was reported by a customer comparing to: GitHub - JB-Luke/X-MCFX at develop on the Mac. It looks like it has been a handful of problem solving. First the trim feature was enabled, 2nd the initial filters may have not had enough taps and 3rd I am still having a spot of trouble with my test system as I can’t account for the HF roll off in these charts…

I am having my customer rerun the tests of his setup to see if he gets similar results as this may all be a limitation of my test config. Will report back.

Sorry for the hoops.

Kind regards,
Mitch

1 Like

I ran the exact same test on my Mac M1:

I don’t know what is wrong with my PC test setup, but on the Mac this looks textbook perfect. Case closed.

Sorry for the detour.

Kind regards,
Mitch

On PC a different FFT library is used. Either Intel IPP/MKL (if enabled) or the native JUCE implementation. On Mac, it probably uses the Accelerate framework (vDSP).

This points to the Intel/JUCE-native version using a different FFT format or not calculating the phases correctly.

At least for IPP vs vDSP I would be surprised by that. We use an in-house wrapper class template for various FFTs including IPP and vDSP and one of our unit test cases is to check if both generate the same results, never had test failures here (although of course a limited set of transform sizes is tested)

Just to follow up. I tried running ConvolutionDemo on a 2nd Windows 10 PC and while I don’t get the HF roll off like in my other Windows machine, I still don’t get the same results as I did on the Mac:

I enabled IPP and ran a Release build and got the same result (Do I need to install a library?)

So two different Windows 10 PC’s are producing the same results, which are not close to the result on the Mac. Unless I am missing something, looks like some sort of issue with Convolution on Windows…

As far as I can tell the ConvolutionDemo is producing a sample accurate convolution of the input signal and filter coefficients apart from floating point rounding errors. I think this was confirmed by the fact that you measured identical results using JRiver and Roon.

What you are seeing here is not characteristic of the convolution or the FFT library, but your measurement setup. It could be how REW exactly does the measurement (maybe a sweeping sine) or how this virtual loopback cable is implemented. Maybe it has jitter or sample inaccuracies on Windows and a more robust implementation on MacOS.

I’ve made a plot myself from the output of ConvolutionDemo on Windows with your filters. They are made up of the samples as they were put into the audio callback buffer. So what is happening in your case I believe is happening outside the ConvolutionDemo.

ConvolutionDemo_magnitude_response2

1 Like

@attila Wow! Fantastic. Thank you for the definitive answer.

Kind regards,
Mitch