Skip to content

The Nature of Sound — a Zig adaptation

A beginner-friendly, project-driven audio-DSP course, adapted from Yü Fang’s μ — a modular approach to audio programming (CMSC388V). Each lesson keeps the original C beside a verified Zig 0.16 port, and adds extra explanations of both the Zig and the math so neither is a black box.

Everything is offline: we compute samples and write .wav files you play afterwards. No real-time audio, no JACK, no drivers, no plugins — just the standard library and raw samples. (The original course is real-time; each chapter notes how the offline version maps to it.)

You are comfortable in a high-level language (JS, Python, Ruby…) but new to systems programming and to DSP. The course explains Zig’s lower-level ideas — allocators, slices, pointers, comptime, error unions — as they appear, and keeps the math practical: every formula comes with a plain-language note about what it means.

Work through the chapters in order; each builds on the last. Every chapter has two recurring callouts:

  • Zig note — what a piece of Zig syntax/idiom does and why.
  • Math note — the intuition behind a formula, in words.

Code blocks come in pairs: the original C first, then the Zig port. Type the Zig out and run it — that is where it sticks.

Math rendering. The notes use LaTeX math written as $...$ / $$...$$. It renders as equations on GitHub, in VS Code (with a Markdown+Math preview), Obsidian, Typora, or any MathJax/KaTeX viewer. In a plain text editor you will see the raw $...$ — still readable, just not typeset.

  1. Zig for TypeScript developers — a noisy primerstart here if you are new to Zig, especially coming from a high-level language. The layer underneath the runtime you’ve always had: the memory model (stack/heap, no GC, ownership), pointers, value-vs-reference semantics, optionals, sized integers/overflow, error unions, defer/errdefer, allocators, and comptime — each mapped to a TypeScript mental model, taught by making things beep. macOS Apple Silicon setup included.
  2. Zig primer, part 2 — MIDI, theory & rhythm — picks up right after part 1, using MIDI (gentler than real-time audio) to go deeper into Zig’s type system, again mapped to TypeScript: packed structs & @bitCast, enums with backing values, tagged unions (discriminated unions), error sets, iterators (vs generators), comptime lookup tables, and the built-in test runner — plus the music: scales/chords, a Euclidean sequencer, a real .mid writer, and a generative groove rendered to audio.

The audio course (the original μ chapters):

  1. wave — what sound is, sampling & quantization, the WAV format byte by byte, and your first tone. (Zig: the WAV writer, writeInt/endianness, bytebeat.)
  2. osc (part i) — why a naive sine clicks, the phasor (phase accumulator), and the four classic waveforms. (Zig: structs, if-expressions, the render loop.)
  3. osc (part ii) — the frequency domain: Fourier series, aliasing, and a bandlimited sawtooth via wavetables. (Zig: slices, allocators, table sets.)
  4. mix — mixing (addition), decibels, and click-free parameter smoothing (linear & one-pole). (Zig: multi-sequence for, reusable smoother structs.)
  5. adsrenvelopes, the sample-rate-independent time constant, and an ADSR state machine. (Zig: enums, exhaustive switch, f32 vs f64 precision.)
  6. delayecho, feedback, fractional delay, and the ring buffer. (Zig: fixed-array init, modular arithmetic, one struct → many effects.)

Intermezzo (optional, real-time):

  1. intermezzo: ctrl — sharing parameters between threads safely: data races, atomics, mutexes, a lock-free ring buffer, memory ordering, and control rate. Belongs conceptually between adsr and delay; not needed for offline WAV generation, but essential the moment you go real-time. (Zig: std.atomic.Value, explicit memory ordering, std.Thread, std.Io.Mutex.)

Install Zig 0.16.0 from https://ziglang.org/download/ and check zig version. Run any example with zig run file.zig, then open the generated output.wav in any player. (0.16 is required — its new I/O interface changed how files are written, so older snippets online will not compile. Every Zig example here was compiled and run on 0.16.)

Always turn your volume down before playing generated audio. A full-scale tone is loud, and a bug can be worse.

All Zig in this course was compiled and executed on Zig 0.16: the MIDI parser, .mid writer, and Euclidean sequencer (ch. 1), the WAV writer and bytebeat (ch. 2), the phasor and waveforms (ch. 3), the wavetable set and bandlimited saw (ch. 4), the smoothers and dB conversions (ch. 5), the ADSR state machine (ch. 6), and the ring-buffer delay/echo (ch. 7).

Adapted from mu.krj.st by Yü Fang (faculty supervisor José Manuel Calderón Trilla), CMSC388V, University of Maryland, Spring 2021 — an opinionated, wonderful course. The original is real-time C with JACK and goes much further (DFT, spectra, reverb, plugins); read it at https://mu.krj.st. This adaptation re-frames the first six chapters for offline Zig and adds beginner scaffolding; all teaching credit is Yü Fang’s.