Public and inspectable work
WavLab: audio DSP in C++ and Rust
Paired C++20 and Rust libraries and command-line tools that turn degree-era audio programming into tested, cross-platform systems work.
- Role
- Creator and developer
- Period
- 2022–present
- Status
- Active public projects
- Focus
- C++20, Rust, Digital signal processing
Returning to lower-level engineering through audio
My degree combined music technology with audio systems and programming. WavLab revisits that foundation as two independently implemented libraries and command-line applications for reading, writing, generating, editing, and processing PCM WAV files: one in C++, and one in Rust.
The projects deliberately work below the level of a general-purpose audio framework. They parse RIFF chunks, preserve Broadcast Wave metadata, convert little-endian 8-, 16-, 24-, and 32-bit PCM samples, manipulate interleaved frames, and write the result back safely. That makes the work as much about bytes, numeric boundaries, file integrity, ownership, and state as it is about sound.
The DSP is a mixture of well-known published techniques and algorithms I first explored through university modules. The equalisation filters follow Robert Bristow-Johnson's Audio EQ Cookbook; the reverb uses the classic Schroeder parallel-comb and series-all-pass structure. The editing tools, circular-buffer delay foundations, and modulation effects build on work I implemented during my formal audio-programming education.
Relearning C++ by building something real
I created the original WavLab in 2022 after more than a decade away from substantial C++ development. My university work had used C89 and contemporary C++; this project brought that knowledge forward into a C++14 codebase that compiled with Visual Studio and GCC/CMake.
Rather than following isolated language exercises, I built a useful audio program end to end. It grew from WAV header and sample parsing into file editing, metering, waveform and noise generation, biquad filters, tape-style saturation, digital/analogue/tape delays, and algorithmic reverb. The early history records the awkward details honestly, including getting signed 24-bit PCM conversion correct across compilers and preserving BWF/BEXT chunks around audio data.
While developing that version, I wrote and ran a Razor lunch-and-learn series called C++ for C# Developers. I explained C++ concepts through the C# features the team already understood, using my current implementation work as the practical foundation. Teaching it forced me to make the similarities, differences, and trade-offs explicit rather than merely getting the code to compile.
My professional C++ exposure has been occasional rather than a full-time specialist role, but I have repeatedly been the person willing to take it on. At Virtual College, for example, I maintained an abandoned C# wrapper around an evolving C++ Sass compiler so the frontend team could keep using newer compiler capabilities. WavLab is the deeper public evidence behind that practical confidence.
Learning Rust by porting a known problem
In 2026 I read The Rust Programming Language and used WavLab as a deliberately demanding porting exercise. Reimplementing behaviour I already understood let me concentrate on Rust's ownership model, borrowing, errors, traits, modules, and tooling rather than inventing a second product at the same time.
The Rust version is not a binding around the C++ implementation. It has its own safe byte readers and writers, explicit signed 24-bit handling, sample conversion, WAV parser, processing library, and Clap command line. The application contains no unsafe blocks. Errors travel through typed results rather than process-wide exits, while complete effect settings are validated before replacing the original samples.
I used Codex as an implementation and test collaborator, particularly to turn the port into a stable, broadly exercised system. The current public revision passes 175 tests across the reusable library and executable, covering binary boundaries, file round trips, tools, generators, filters, delays, modulation, invalid settings, and actual command execution. Formatting and Clippy also pass with warnings denied, and GitHub Actions repeats the suite on Linux, Windows, and macOS.
Rust is now my preferred direction for new work at this level. Compilation can be slower than I would like, but ownership and borrowing move memory and lifetime correctness into the type system. That is a compelling trade when so many serious failures in long-lived C and C++ software come from memory management, invalid access, and unclear ownership.
Bringing the stronger baseline back to C++
Once the Rust version had a much firmer test foundation, I returned to the original repository with Codex and modernised the C++ implementation rather than treating the port as a reason to abandon it.
The current main branch is C++20 with compiler extensions disabled. It separates an installable wavlab::wavlab library from the CLI, places the public API in a namespace and dedicated include tree, uses typed CLI11 arguments, and replaces the destination only after a complete temporary WAV write. The binary layer now performs checked, portable little-endian conversion—including explicit sign extension for 24-bit samples—rather than relying on platform layout or unchecked stream state.
The modernisation also rebuilt the quality boundary around the code. A clean local archive passes 20 registered CTest checks spanning the library API, binary conversion, DSP, black-box CLI execution, generated documentation, deterministic fixtures, ffprobe validation, and installation from an independent consumer project.
CI builds with GCC and Clang on Linux, MSVC on Windows, and Apple Clang on macOS. Separate jobs exercise a shared library, an installed release package, and AddressSanitizer plus UndefinedBehaviorSanitizer. This matters in C++: conventional tests establish behaviour, while several classes of lifetime, bounds, and undefined-behaviour failure need compiler and runtime tooling designed specifically to expose them.
The same domain, two useful perspectives
| Comparison | C++ WavLab | Rust WavLab |
|---|---|---|
| Language baseline | C++20, evolved from the original C++14 application | Rust 2024 edition, written as a fresh port |
| Structure | Installable library, CLI, CMake package, examples | Library, CLI, Cargo package, generated command reference |
| Safety approach | Explicit types and checked I/O, plus ASan/UBSan and multiple compilers | Ownership, borrowing, typed errors, bounds checks, and no unsafe blocks |
| Verification | 20 local CTest checks; GCC, Clang, MSVC, and Apple Clang CI | 175 local tests; formatting, Clippy, and three-OS CI |
Keeping both implementations is the point. The pair shows that I can work with manual representation and performance concerns in C++, appreciate the discipline needed to make that code robust, and apply Rust's stronger compile-time guarantees to the same class of problem. The audio domain makes the results audible; the file, numeric, and state-management work makes the systems engineering inspectable.
Why I include this work
- Comfort working below managed application frameworks with bytes, binary formats, numeric conversion, circular buffers, and stateful signal processing.
- Practical C++ knowledge beyond occasional professional maintenance, without pretending it has been my primary career language.
- The ability to learn Rust independently by applying it to a substantial existing problem rather than completing disconnected tutorials.
- Audio and DSP understanding grounded in formal education, established references, and personally implemented algorithms.
- Translating technical knowledge for other engineers through the C++ for C# Developers lunch-and-learn series.
- Using Codex to accelerate implementation and verification, then retaining explicit architecture, cross-platform evidence, sanitizers, and tests.