codeTECHNICAL DEEP-DIVE

Core Architecture

Every Azimuth synth shares a common Core — a lock-free, sample-accurate audio engine built entirely in C#.

Why Procedural Over Samples?

Zero Disk Space

No sample libraries to ship. Every sound is generated from code — a few kilobytes of parameters instead of megabytes of WAV files.

Infinite Variation

Procedural synthesis generates unique sounds every time. No repetition, no looping artifacts, no "same footstep" fatigue.

Runtime Control

Change timbre, texture, and dynamics from gameplay at runtime. Something impossible with pre-recorded samples.

Full Source Code

Every synth ships with complete C# source. Modify, extend, and integrate deeply with your project.

The Core Engine

Base Architecture

Every synth inherits from BaseSynthEngineSO<TVoice, TCommand>, a generic ScriptableObject engine that handles voice pooling, command queuing, and sample-accurate scheduling.

  • Voice pool: 1-128 configurable voices, allocated at init
  • Command queue: 64-8192 entries, lock-free SPSC ring buffer
  • Scheduler heap: 128-16384 entries, binary min-heap ordered by DSP time
  • AudioSourceBridge: orchestrates all engines sequentially per audio callback

Lock-free Scheduling

The Core uses a lock-free command queue and min-heap scheduler for sample-accurate event timing — no locks, no atomics beyond volatile reads/writes on indices.

  • LockFreeCommandQueue<T>: SPSC ring buffer. Main thread writes, audio thread reads. Uses Volatile.Write / Thread.VolatileRead on indices only.
  • AudioThreadMinHeap<T>: Binary min-heap ordered by TargetDspTime. Each render sample, due commands are popped. O(log n) insert, O(1) peek.
  • Commands carry TargetDspTime for sample-accurate scheduling — notes start exactly when they should.

Voice Management

Fixed-size voice array at init. Active-voice cache rebuilt incrementally (O(1) insert) with full rebuild every 512 samples (~10.7ms at 48kHz).

  • 4-strategy voice stealing: (1) free voice, (2) quietest releasing at/below priority, (3) oldest at/below priority, (4) globally oldest
  • Energy protection: 1/sqrt(min(4, totalVoices)) prevents clipping as polyphony grows
  • State mirrors written via Volatile.Write, read by main thread for UI updates

Performance

Render loops iterate per-sample, dispatch due commands, sum voices, and apply FastTanh soft clipping. Budget guard checked every 128/256 samples to bail early on CPU overload.

  • FastMath: Allocation-free FastTanh, FastExp, FastSqrt for DSP
  • Telemetry: Interlocked.Exchange counters for dispatch rate, heap/queue peaks, overruns, budget bails
  • Budget guard: Automatic bail-out prevents audio thread starvation under CPU overload

Engine Architecture

// Every synth inherits this generic base
public abstract class BaseSynthEngineSO<TVoice, TCommand>
    : AbstractBaseSynthEngineSO
    where TVoice : struct, IVoice
    where TCommand : struct, ICommand
{
    // Lock-free command queue (SPSC ring buffer)
    protected LockFreeCommandQueue<TCommand> _commandQueue;

    // Min-heap scheduler (ordered by TargetDspTime)
    protected AudioThreadMinHeap<TCommand> _scheduler;

    // Fixed-size voice pool
    protected TVoice[] _voices;

    // Per-sample render loop
    public void ProcessStereo(float[] left, float[] right)
    {
        // 1. Dispatch due commands from scheduler
        // 2. Process each active voice
        // 3. Sum voices with energy protection
        // 4. Apply FastTanh soft clipping
    }
}

Unity Integration

ScriptableObject Architecture

Every engine is a ScriptableObject — configure voice count, queue size, and scheduler depth in the Inspector. No code changes needed for different polyphony requirements.

AudioSourceBridge

A single MonoBehaviour bridges all engines to Unity's audio system. Engines are processed sequentially per audio callback — one bridge, multiple synths, zero overhead.

UITK Editor Tools

Custom Unity UI Toolkit editors for every synth. Preview sounds, tune parameters, and browse presets — all inside the Unity Editor.