A fuzz test exposed OpenCL error -44 failures caused by out-of-bounds GPU memory access that persisted across teardown, apparently a driver bug.
Evan Mezeske
May 2024
Envelope followers are working, and reusing delay lines instead of a new link type made them simpler and added free signal delay as a bonus.
Host automation is finished, with golden tests that map MIDI CCs onto automation lanes, plus a nasty subscriptions bug fix for stale undo state.
DAW automation works via 32 fixed parameter slots that modulators bind to, the only mapping that stays stable since DAWs can't add params dynamically.
Worked out every combination of invert, exponential, and multiply on modulator links, then added an exhaustive test proving CPU and GPU modulation match.
Apr 2024
The modulator link editor now looks up per-parameter ranges, units, and exponential scaling, and handles bulk edits of mismatched links.
New exponential and multiplicative modulation modes make the MIDI note follower play in tune and also unlock audio-rate FM synthesis.
A rare debug build flagged seven crash bugs, including an unordered_set passed to std set_symmetric_difference that MSVC's checked STL caught at runtime.
I think I can get easy polyphony by running virtual instances of the physics model and using time dilation to pitch each note from one system.
MIDI routes can now use wildcard any values for channel and note, which fixes quirky controllers today and lays the routing groundwork MPE mode will need.
Anukari now handles every MIDI message type, including poly pressure, which Ableton's MIDI export silently drops. Next up is the mess that is MPE.
Finding the abstraction that lets modulator sub-types like LFOs and envelopes share GUI editor code, ahead of a batch of new MIDI types.
MIDI routing is done, with a MidiRoutable interface that lets entities subscribe to note, CC, pitch, and pressure events by ID.
The data model refactor is done, but model.h still triggers huge recompiles, and a pimpl fix ran aground on {fmt} formatters needing complete types.
I replaced the data model's macro hacks with CRTP templates across 15k lines. Golden and fuzz tests made the refactor safe before MIDI routing work.
A JUCE crash I patched upstream, then flaky GUI tests that only failed when I alt-tabbed away, since some JUCE code cares whether the window is foreground.
Mar 2024
MIDI note envelopes came together in a day, then Ableton and Anukari disagreed about middle C. C3 vs C4 vs C5 varies by vendor, so I went with C3.
LFO retriggering now works via the generalized MIDI note routing, and unit tests caught a dangling route left behind by deleted LFOs.
The visual note picker keeps sprouting complexity, from cross-window undo hotkeys to an undo stack restriction and a flickering focus hack.
Despite a cluster headache, the MIDI note picker works, and note routing is being generalized from hard-coded Exciters to any entity for LFO retrigger.
Building a general MIDI note picker with a live piano keyboard and a learn button, fed by an SPSC queue moving MIDI off the audio thread without blocking.
While building a golden test for the tempo-synced LFO I caught a MIDI bug where note off and on in the same audio block always ran as on then off.
Tempo-synced LFOs were easy since JUCE hands over the DAW's BPM. The fun part was beat subdivision with triplets, solved by a plain switch statement.
A leftover OpenCL map with CL_MAP_WRITE_INVALIDATE_REGION corrupted my PRNG seeds and made the audio nondeterministic. A pause-toggle test caught it.
Seeding the GPU PRNG from the current sample number made rendered audio depend on buffer size, so it now seeds once at startup and stays deterministic.
All 65 golden tests covering the physics engine are done, bringing the suite to 249 cases, with a GUI chaos monkey planned as the next testing push.
Golden tests now render a preset and MIDI clip to audio and diff against a verified .flac, so optimizations and GPU ports can't silently break the sound.
Testing the Selection class, frustum picking, raycasting, duplication and all, with zero mocks so far thanks to APIs designed for testability.
New unit tests keep finding real bugs, including a broken plane-sphere intersection in frustum selection and an infinity check that accepted NaN.
Exhaustive PrecisionSlider unit tests caught a NaN-producing bug, prompting an audit of where the 36 kLOC codebase still needs test coverage.
Feb 2024
Adding interval labels to the semitone slider exposed a snap-to-grid bug in the scaling transform, so PrecisionSlider is finally getting unit tests.
Master output level meters and a GPU meter are in. The GPU meter shows the share of the latency budget each block eats, like Ableton's CPU meter.
Bigger JUCE typefaces and slider hover polish, plus finding Google Filament, which beats DiligentEngine for cross-platform graphics.
No usable OpenCL obfuscator exists, so I wrote one in about 100 lines of Python with libclang. It renames symbols and keeps line numbers intact.
Fixed a bug where MIDI note off events were missed with the plugin GUI hidden, because GUI flow control froze the simulation timestamp.
Grinding through pre-alpha chores like window position saving, a recent presets menu, open source license credits, and LLC paperwork.
The precision sliders are finally done, and loading 3D assets in a thread pool took the GUI startup time from about 4 seconds down to 400ms.
Multi-value sliders finally feel right, leaving one last problem, displaying float32 ranges with low visible precision until you click to edit.
Multi-value sliders now store a range instead of one float, so scaling spring stiffness across a selection keeps its non-uniform shape.
Surveying no-code tools for the sales site and hating it. Lock-in, revenue cuts, and $1k/year for a custom product registration domain.
Designing sliders for editing 50 springs at once, with whiskers showing the value spread and drags that scale every stiffness proportionally.
Getting JUCE mouse wheel events to reach the new PrecisionSlider but still bubble up for viewport scrolling took big workarounds of the listener system.
Dials are gone. The new PrecisionSlider pairs a coarse slider with an adaptive thumbwheel so parameters spanning orders of magnitude stay precise.
Undo now works for mouse-wheel edits on Dials, tricky work nobody will ever notice. Along the way I hit a focus bug in the undo and redo hotkeys.
Jan 2024
Orthogonal-view mouse picking was janky from float precision in ray cylinder intersection, fixed with doubles, plus time-batching mouse wheel undo spam.
Realtime-safe logging is finished. No allocation or locks on the audio thread, plus a synchronous escape hatch so crash stacktraces always reach disk.
Replacing stream-based logging with format-style formatters so I can safely get logs off the audio thread, plus a defense of local macros.
After finishing Delay Line edge cases, I started replacing Abseil logging. No library is zero-allocation and wait-free, so I'm writing my own on libfmt.
Delay lines work now, but sensor buffer indices must stay stable across delete and undo so a delay never plays back another sensor's old audio.
Tracing a delay line bug to CL_WRITE_MAP_INVALIDATE_REGION skipping the DMA read on pinned memory, so stale data landed in the internal sample buffers.
Delay lines work well enough for ping-pong and feedback experiments, but a ring buffer bug that only appears near maximum delay is resisting me.
Instead of a new entity type, delay lines reuse the AudioSignal exciters via a DelayLine link, with GPU audio moved into ring buffers.
After deleting everything down to a 5-line sine generator, The Bug turned out to live in my Focusrite Scarlett drivers, not in Anukari at all.
The Bug strikes every 19 seconds, but only on some launches, and it survives replacing all the physics with a pure sine wave. Nothing adds up yet.
Dec 2023
With no repro for The Bug, I beefed up the fuzz tests and hardened the GPU code against inf and NaN, hoping to rule out causes along the way.
Compressors work and the microphones glow blue with gain reduction, but a rare few-second distortion with no repro case is haunting me.
After a jam session made distortion problems obvious, I built a GPU compressor for the microphones so live experimenting stays clean.
Found a cheap way to modulate rotation by restricting it to the Y axis for exciters and mics, and pushed panning modulation down to the GPU for free.
Sample and hold exposed how non-random my GPU noise PRNG was. I replaced the linear congruential generator with xorshift64* after a Galois LFSR detour.
Phase-state oscillators fixed retrigger dings, and gain modulation moved from linear space to decibels, worth the pow() calls on the GPU.
New UX explains dependent GPU memory limits, so maxing out masses visibly cuts your microphone budget instead of failing mysteriously.
Tens of thousands of random presets now get serialized to GPU format and simulated in a fuzz test, proving the new warp alignment code doesn't crash.
Padding GPU memory so each warp runs a single entity type cut kernel latency 25 to 50 percent and made performance far more stable.
Modulation values now reach the GUI, so a mic driven by two LFOs visibly circles in the 3D view. CPU-latched parameters like mallet hardness work too.
FM finally works on LFOs and oscillators after I dropped my stateless GPU waveform trick for phase-incrementing state, which turned out faster anyway.
A free-running LFO now computes on the GPU, but its stateless design means the value never lands in memory, so GUI animation needs a per-block write-back.
Modulators can now modulate springs on the GPU, including modulators modulating other modulators. Basic LFOs and a demo video come next.
GUI work for linking modulators to springs and to other modulators is done, and one MIDI CC knob modulating another now simulates correctly.
Modulators can now link to springs, with groundwork for modulating modulators. Fuzz testing caught bugs in the new code and old undo/redo paths.
Nov 2023
To let modulators drive spring stiffness the Link class itself becomes Linkable, kept safe by an invariant allowing one layer of recursion.
MIDI control modulators now render as spinning 3D knobs, and I applied an ADC trick using arch-specific pause instructions in the spin-loop sync code.
My ADC23 talk is done and off my mind. The prep was a huge distraction, but the audio dev connections and go-to-market talks made the trip worth it.
Modulation now covers sensors and exciters, and I'm weighing one flat layer of modulator recursion since GPUs can't do real recursion.
Modulators are becoming their own 3D entities with routing stored on links, plus a JUCE crash fix upstream and more polish on my ADC23 slides.
Oct 2023
The Audio Units logo and the Audio Units symbol are trademarks of Apple Computer, Inc.
VST is a trademark of Steinberg Media Technologies GmbH, registered in Europe and other countries.