A convolution based alternative to electrical loudspeaker correction networks

... DRC itself could probably be tricked into doing it as a test convolution with the two files inserted as dummies. ...
I looked at the drc.cpp code where the TCOutFile is created. It looks like one could add another set of arguments to load an optional merge filter (i.e. XO filter), merge with the generated correction filter, output the aggregate filter and use the aggregate filter to create the TCOutFile (test convolution against the mic corrected original signal).

There would be details to be worked out, but the basic code structure doesn't look too difficult.

Hopefully this would allow the full range target to be used to correct each individual driver before doing a full-range correction in a second pass. If the pre-filter/XO pass does it's job, there shouldn't be much to be done in the second pass.
Trace arithmetic works, DRC itself could probably be tricked into doing it as a test convolution with the two files inserted as dummies.

A example of a numpy convolution reverb script here a few posts down that looks like it would work too.

Initial results looks promising.

  • Added arguments to load an optional XOFilter InFile (no mic compensation needed).
  • Added arguments to specify the merged FIR OutFile.
  • Added code to convolve the PSFilter with the optional XOFilter to create a XOMergedFilter (maybe rename to PSXOFilter ???).
  • Added code to Test Convolve the [mic compensated] BCInfile IR with the XOMergedFilter.

Blue plot is the original full range IR (soon to be tested with individual driver IRs).
Purple plot is the existing full range PSOutFile PSFilter.
Green plot is the RePhase XO FIR.
Black plot is the XOMergedFilter (PSFilter/XOFilter)
Red plot is the Test Convolution between the BCInfile and XOMergedFilter.

The Black plot appears to mirror the Purple plot's corrections within the bounds established by the Green plot.

Doing initial dry run tests with the full range signal and the 4 individual RePhase XO's (sub, low, mid, high).

The phase of the filters do not plot well in REW for some reason so I will have to test the phase with actual measurements.

Hopefully this per-driver approach will pre-correct the phase/time of each driver individually instead of averaging the boundaries of adjacent drivers such as a subwoofer and bass baffles located in 2 different physical locations.

Merged Target FIR and XO FIR.jpg
Looks promising, if it works it would be good to see the script or code used, always handy to have another tool in the toolbox :)
Thanks for giving me the idea.

I manually "pre-filter" the sub and bass drivers before using full range DRC-FIR which is a labor intensive and error prone process. I am trying to automate this "pre-filtering" and am happy to share any changes. I learned how to do git-hub branch submissions last year, but unfortunately DRC-FIR isn't on git-hub so I will have to find another way to share the code changes once settled.

I am experimenting with derriving the Target Points file from the RePhase's FIR wav XO files which already has a copious amount of target frequency, amplitude and phase points. It would also eliminate any conflicts between the 2 since both would be a single source.

Had some more time today and fixed some ImpulseCenter and off-by-1 issues.

Also made progress with derriving the points target from the XO wav file should that option be more suitable for certain use cases.

The main issue was the normalization being used and the ImpulseCenter settings.

REW doesn't distort the displays anymore when adding/removing a FDW.

The TC's are tracking very closely to the XO's.
The summation is in black again.
Trying to produce the lowest noise floor on each side of the XO's.
Testing with different # taps.

ImpulseCenter Corrections.jpg
Last edited:

CamillaDSP can read a raw PCM file and output a raw PCM file. It can also read FIR filters in wav formats, so it appears it can merge filters as well. I am comparing REW's merge results against the modded DRC-FIR results as a sanity check.

One thing I stumbled on, is that extra_samples needs to be set in the CamillaDSP config file because the IR files are so short.
Merging FIR File Update:

This is a CamillaDSP config file that combines 2 - 256K tap 64-bit Double FIR input files. One input file is in raw F64 PCM format and the other is in WAV F64 format, each containing 1 channel. It can be modified to merge multi-channel files (stereo, 8-channel, etc) and SoX can be used to switch between raw PCM and WAV formats (without and with header files).

This is configured for 176400 sample rates and 256K taps in both files and can be changed accordingly to support other sample rates and # of taps.

The resulting merged output is tracking very close to the output of the DRC-FIR code mods. File size is off by 1 64-bit sample which I am looking into.

Usage: camilladsp config_in.yml


  samplerate: 176400
  capture_samplerate: 176400
  queuelimit: 1
  chunksize: 262144

  resampler_type: Synchronous

    extra_samples: 262144

    channels: 1      # Captures a single channel from the PCM file
    format: FLOAT64LE
    type: File
    # Target PS FIR target solution
    filename: /path_to/PSOutfile.pcm      # e.g. From DRC-FIR

    channels: 1     # Writes a single channel to the PCM file
    format: FLOAT64LE
    type: File
    filename: /path_to/MERGED_PS_XOOutfile.pcm   # merged file

    type: Conv
      type: Wav
      channel: 0   # Reads channel 0 from the WAV file
      filename: /path_to/XO_FIR.wav      # e.g. From RePhase

  - type: Filter
    channel: 0   # Applies XO FIR to channel 0
      - XO_FIR
Last edited:
Another Merging FIR File Update:

In between house and honey-do lists, I have been continuing the FIR merge efforts, leaning the code and the maths/windows/FFTs behind the code.

I added the ability to load additional PCM numerical formats into DRC-FIR (so there is less reliance on SoX), added more windowing functions, added the ability to dump "DIRAC DELTA" versions of each windowing function for unit testing, analysis in REW and baseline testing, adding the ability to "re-tap" FIR filters (from other sources) with specified windowing functions (use case: lower the amount of taps in a merged/aggregate FIR if it grows too long to reduce latencies or make multiple FIRs all the same number of taps), found/fixed some subtle precision issues, reduced a bit of the redundant cut-and-paste code and started to add the ability to internally process in quad-precision floats.

The FFT library in use already had some quad-precision support, but in some places still assumed double precision (defeating the quad increased precision) and had some consistency deltas that showed up with the quad-precision porting/testing. Updating the main code to use quad-precision as well, but a few gremlins still exist before full quad-precision is completely working. The C++ standard template library needs a version 11+ compiler it to work at quad-precision without dropping down to double precision. I added compile time switches to substitute equivalent quad-precision C-library routines for the older C++ standard template libraries to bypass this compiler version requisite. Debian's version 11 C++ is in testing and not released yet. DRC-FIR was originally written in 93 so quad-precision wasn't a thing back then and average processors circa 1993 couldn't handle it compared to today's processors.

Here is an example of the 176.4kHz synthetic Bartlett window created using double (BLUE) and quad (RED) precision, both stored in F64 (double precision) from the same algorithm. Even though both results are stored in F64, you can see the blue plots have issues every couple of samples as well as clipping where the red plot does not using the identical code (sans "double" versus "long double" deltas).

This suggests higher precision processing math is more accurate even when stored in a lower precision format. How this translates to audibility is TBD, but more accuracy shouldn't degrade the playback.

I am getting into the accuracy limits of REW's plotting algorithms, so I wrote unit test code to verify these periodic "drifts" due to precision round-off issues. The quad-precision produces a more "analog" progression in the algorithms without "jitter/jumps" for a lack of better terms.

So far, am learning a lot and will be testing the CamillaDSP merges against the added DRC-FIR merges with these extra tools and baselines.

Also have my 64-bit Wav2Pcm and Pcm2Wav code working which bypasses the SoX 32-bit limitiation in converting 64-bit between WAV and PCM formats. It is a wrapper around SoX which only handles the 32-bit precision compromised 64-bit to 64-bit case and passes everything else onto SoX to avoid reinventing the wheel.

FWIW, the deltas in the Group Delay plot show up under 700Hz and above 1.3kHz. Within the [700 - 1300]Hz band, both REW plots are virtually the identical between double and quad.

Double Precsion Stored in F64.jpg

Quad Precsion Stored in F64.jpg

Double vs Quad Precsion Stored in F64 a.jpg
I found a new plugin that might be interesting.

It's called Pulse from Lancaster Audio.

It looks cool as you can load a right and left correction file.
It also does rate sampling on the fly.
And they mention it adds zero latency.

I haven't tested it much, but the price is just right for those who would like to try it... it's free!

Pulse - The only Free IR Loader for Pro Tools

Looks quite interesting, especially for Mac users who could use the plugin with a VST loader like Hosting AU, which lets you route the system sounds through it, so it is possible to measure before and after DSP.

Hosting AU
Yes, I have also used pulse IR Loader. Infact, It is my personal favourite. I like its simplicity and easy to use interface. I have tried other free Impulse Response Loaders as well, which are awesome. But again it all depends on the personal preferance and compatibility. But do try out other options as well maybe you will like them.
Yet another update:

Here is a single full range sweep (top purple plot).

Sub, bass, midrange and tweeter linear phase RePhase XO's plots appear under the top line (smooth plots).

The squiggly plots riding on top the sub XO and just under the bass, midrange and tweeter XO plots are the RePhase linear phase XO's merged with their respective correction FIR filters merged into individual driver Correction/XO FIRs and test convolved against the original signal.

Next step is to generate the individual combination XO/Correction FIRs from individual driver sweeps so each correction is purely individual driver based (so far partially working/tested).

The end goal is to use linear phase RePhase XO's (as the driver correction targets) and individual driver sweeps to automate the generation of driver level correction FIRs with corresponding gain levels to be fed into CamillaDSP and/or JRMC's convolution engine and driver gain configurations.

By merging the corrections to the XO FIRs at the driver level, it adds another level of functionality to convolution engines that do not support multi-pass convolution such as JRMC that has been requested by their customer base .... (and subsequently ignored).

This is an attempt to automate the manual driver level corrections and with a little extra work, provide balanced gain levels across left and right channels for all drivers. Basically bring DIY/OpenSource closer to commercial packages that offer driver level corrections.

Use Case is for speaker configurations with digital XOs between Subs and Mains or Subs and Multi-way Mains with all digital XOs.

DRC-FIR currently works at the full range level (assuming full range speakers with analog XOs in a single baffle at a single physical location).

DRC-FIR does not uniquely correct at the driver level, thus treats crossover regions as a single driver rightly or wrongly applying a common correction to both adjacent drivers (assuming both drivers are off in the same manner, by the same amount) over the crossover region which doesn't account for spacial baffle deltas such as a sub and a main speaker or multi-baffle speakers (e.g. separate sub, bass and mid/tweet towers).

Hopefully it will also better address asymmetric room issues in an automated fashion by creating unique merged XO/Correction FIRs for each left and right driver respectively.

Also in the plan is to include test convolutions and test summations (with applied gains) of all drivers for inspection in REW prior to application to the actual convolution engine and speakers.

Full Range Sweep.jpg
Last edited:
Seems to track quite well. Be aware that final SPL level is determined per channel and not set at a fixed level.
So some manual tuning to get the levels correct is required, between channels in a side and the left/right output.
Might be something you want to address, though the way it is done is to avoid any clipping.

What does the end result look like in a more standard 50 dB range on the SPL level (left scale)?
Seems to track quite well. Be aware that final SPL level is determined per channel and not set at a fixed level.
So some manual tuning to get the levels correct is required, between channels in a side and the left/right output.
Might be something you want to address, though the way it is done is to avoid any clipping.

What does the end result look like in a more standard 50 dB range on the SPL level (left scale)?

The volume levels are still TBD. DRC-FIR tends to lower the overall/fullrange volume level the more I try to extend the sub's response using a full-range model (cuts vs gains to compensate for the OB/Dipole 6dB/octave rolloffs). I am currently solving from [10-24k]Hz in these plots. If I solve to 20Hz, the volume levels are considerably higher for the full range. Hopefully solving at the driver level (versus full range) will add another benefit by mitigating the full-range "cuts" if your subs have sufficient gain to compensate.

My subs have a lot of gain in reserve, so this could work out as a plus if I can get the software to only lower the volume of the sub's correction and not the rest of the drivers which could then be compensated for by adding back sub gain without touching the other drivers.

I was hoping I could programmatically calculate the RMS of each driver's test convolution and then compare the set of all N RMS values to programmatically generate the relative gain deltas for each driver to level match the system as a whole (or something similar).

Here are the results of individual sweeps of my right channel from last night. The SPL plots look pretty good, but the phase plot appears to still need some work.

One of my main design goals was to automatically match the phase of each driver so there were no discontinuities at the XO boundaries when combined (previously a time comsuming manual process). Looks like I still have some more work to do at the sub/bass and bass/midrange phase boundaries as seen in the second plot.

Note, the subs are in their own separate physical baffles and the bass panels are in their own separate baffles from the mid-tweeter towers which adds more work (different physical room locations and delays) to get them integrated right, thus the desire to programmatically automate the process.

Right Channel Individual Sweeps.jpg

6 cycle FDW applied to phase plot. Crossovers at 50 Hz, 250 Hz and 3 kHz.

Next to do all 8 individual driver sweeps and compare the resulting SPL and phase between the left and right channels.

Right Channel Aggregate Phase.jpg
Last edited:
I was hoping I could programmatically calculate the RMS of each driver's test convolution and then compare the set of all N RMS values to programmatically generate the relative gain deltas for each driver to level match the system as a whole (or something similar).

Another thought would be to calculate the RMS deltas between the individual ideal XO and the resulting test convolutions assuming all of ideal XO's are level set with respect to each other. Hopefully this will give usable gain data for each driver without having to know about the other drivers a priori.
Here are a couple of driver time alignment plots.

The first plot has some intentional driver time alignment issues in the bass and sub ( midrange and tweeter are artificially delayed). Even with the artificial delays, the results look promising when applying the driver level DRC-FIR mods.

Before Driver Time Alignment.jpg

The second is after adding the driver level DRC-FIR corrections. Removing the artificial delays beforehand would improve upon the automated solution.

After Driver Time Alignment.jpg

I also modified the test convolution code to re-tap the test convolution results back down to the number of taps used by the supplied XO for easier comparisons in REW (e.g. like-for-like in waterfall plots).

Here is what the before/after FDW(6 cycles) phase plots look like.

Before and After Phase Correction.jpg