-
Notifications
You must be signed in to change notification settings - Fork 2
Architecture
iQualize captures and processes system audio entirely in-process — no virtual audio driver, no kernel extension, no background daemon.
The foundation is CATap (Core Audio Taps), introduced in macOS 14.2. Unlike virtual audio devices (BlackHole, eqMac's driver approach), CATap intercepts audio directly from the HAL output stream. You keep system volume control, don't break DRM-protected audio, and avoid the latency of a secondary audio path.
iQualize creates a process tap that captures all system audio except its own output (preventing feedback loops), writes samples into a ring buffer, and feeds them into AVAudioEngine for processing.
macOS Audio Server
│
├── App Audio ──┬── Output Device (muted by tap)
│ │
│ └── CATap ──► iQualize IOProc
│ │
│ ▼
│ Ring Buffer
│ │
│ ▼
│ AVAudioSourceNode
│ │
│ ▼
│ AVAudioUnitEQ
│ (parametric EQ)
│ │
│ ▼
│ Output Gain Node
│ │
│ ▼
│ AUPeakLimiter
│ │
│ ▼
│ Output Device
The ring buffer decouples the real-time IOProc callback (fixed hardware timing) from AVAudioEngine's pull model (variable render timing). Parameter changes are written atomically — no locks on the audio thread, no glitches on slider drags.
iQualize supports two EQ modes:
-
Linked mode — uses a single
AVAudioUnitEQfor both channels. Simple and efficient. -
Split-channel mode — bypasses
AVAudioUnitEQand runs independent biquad filter chains per channel in the render callback, allowing separate L/R EQ curves.
Real-time spectrum visualization uses Accelerate.framework's vDSP for SIMD-accelerated FFT:
- 2048-point FFT with Hann windowing
- 1024 FFT bins mapped to 128 log-frequency display bins (20 Hz – 20 kHz)
- Instant-attack, exponential-decay smoothing per bin
- Peak hold lines with ~2 second hold time
- Dual analyzers: pre-EQ (raw input) and post-EQ (processed output)
The spectrum data uses a double-buffered lock-free design:
- Two alternating
Floatarrays (magnitudes + peaks) - Audio thread writes to the inactive buffer, then atomically flips an index (guaranteed atomic on ARM64)
- UI thread reads from the active buffer at 60 fps
- Worst case: one stale frame (imperceptible)
- Zero locks, zero allocation, zero contention on the audio thread
The biquad frequency response curve is computed using Audio EQ Cookbook formulas — standard IIR coefficient calculations for parametric, shelf, high/low pass, band pass, and notch filters. The transfer function H(z) is evaluated at each display frequency to plot the expected magnitude response before it hits the audio engine.
| Framework | Usage |
|---|---|
| Core Audio | CATap, aggregate devices, IOProc callbacks |
| AVFAudio | AVAudioEngine pipeline, AVAudioUnitEQ, AUPeakLimiter |
| Accelerate | vDSP FFT, vectorized spectrum processing |
| AppKit | Menu bar UI, NSWindow, NSView-based EQ interface |
| ServiceManagement | Start at Login (SMAppService) |
iQualize Wiki