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.VolatileReadon 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,FastSqrtfor DSP - Telemetry:
Interlocked.Exchangecounters 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.