# I got bored and built a 2x2 MIMO FxNLMS ANC simulator in Julia + C23

**URL:** https://discourse.julialang.org/t/i-got-bored-and-built-a-2x2-mimo-fxnlms-anc-simulator-in-julia-c23/138826
**Category:** Signal and Image Processing
**Tags:** ffi, dsp, controlsystems
**Created:** [August 15, 2026, 1:42pm UTC](https://discourse.julialang.org/t/i-got-bored-and-built-a-2x2-mimo-fxnlms-anc-simulator-in-julia-c23/138826 "2026-08-15T13:42:42Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![blah\_blah.jl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blah_blah.jl/32/223304_2.png) [@blah\_blah.jl](https://discourse.julialang.org/u/blah_blah.jl)
#### Post date: [August 15, 2026, 1:42pm UTC](https://discourse.julialang.org/t/i-got-bored-and-built-a-2x2-mimo-fxnlms-anc-simulator-in-julia-c23/138826/1 "2026-08-15T13:42:43Z")

</div>

Hey folks,

Today morning was aggressively boring, so naturally I ended up building a 2x2 MIMO active noise control simulator.

I have recently started poking around the border between DSP and control systems, and for some reason I am enjoying it far more than expected. I also do not entirely know why I am posting my random experiments, but maybe someone finds this useful, finds a cursed coefficient, or tells me that I have misunderstood acoustics in a particularly creative way.

The project is a cross-coupled FxNLMS system with:

- 2 reference microphones
- 2 anti-noise speakers
- 2 error microphones
- A full 2x2 primary acoustic-path matrix
- A full 2x2 secondary-path matrix
- Four 64-tap adaptive controller filters
- Noisy offline secondary-path identification
- Slowly drifting secondary paths
- Mixed, stereo WAV, and impulsive-noise scenarios

Julia handles the signal generation, acoustic models, WAV processing, metrics, diagnostics, and plots. I kept a complete Julia implementation as the numerical oracle and moved the sample-processing kernel into C23.

The C backend currently matches Julia to around `1.55e-15`, with exactly `0.0 dB` ERLE difference across all three scenarios. It is approximately `1.76x` faster in the warmed benchmark.

Current final-window results at 8 kHz:

| Scenario | Combined ERLE |
| --- | --- |
| Mixed moving noise | 16.25 dB |
| Stereo WAV | 24.06 dB |
| Impulsive noise | 18.10 dB |

The impulsive benchmark reaches a sustained 80% of final cancellation in under 25 ms.

I tried to keep the simulation honest:

- The adaptive controller never sees the true secondary paths.
- It only receives reference signals, measured residuals, and separately estimated secondary paths.
- The C processing function performs zero heap allocations.
- Julia and C run the same deterministic scenarios.
- The C side is compiled as C23 with warnings treated as errors.
- The project runs GCC and Clang static analysis plus deterministic unit tests.

The default backend is C:

```c
main()

```

The Julia oracle is still available:

```julia
main(:julia)

```

Repository:

> **[anc-fxlms](https://codeberg.org/TheMaskedSignal/anc-fxlms)**
>
> Julia + C23 2x2 MIMO FxNLMS active noise control simulator with machine-precision backend parity.

This is still an offline simulator rather than a real headset or hardware ANC controller. The next genuinely different step would be separating render and adaptation into a real-time API with measured audio latency.  
Feedback is very welcome, especially around the MIMO normalization, acoustic scenarios, Julia/C boundary, or anything that looks slightly cursed.  
Have a great, day or night or whatever is it at your timezone  
Adios  
-JB
