All notable changes to ToooT are documented here. The format follows Keep a Changelog and the project adheres to Semantic Versioning.
- Session-view live-cell render path —
SongSnapshot.sessionLivecarries a render-thread-safe[SessionLiveSnapshot.Cell]derived fromSessionGrid.livePlayback+ per-columnliveSinceBeat. The render path treats live cells as priority-1 overrides over arrangement clips and the order list (matches the Ableton "session overrides arrangement" rule). Pattern cells substitute the row;.audioand.midicells loop with per-loop trigger / event dispatch via shared dispatch helper. Quantized launches now actually advance:Timeline.publishSnapshotcallsSessionGrid.advanceLaunches(nowBeat:)every UI tick. PlusPlaybackState.arrangementandPlaybackState.sessionGridwere added so the live engine consumes them — pre-fix only the test harness did. - Take comp regions + render path playback —
TakeLane.regions: [CompRegion]for region-based active-take selection.TakeLane.cacheInBank(_:)allocates each take's interleaved PCM into the unified sample bank; the render path triggers a voice from the cached bank region whenever the playhead crosses a region'sstartBeat.RecordingTakegainedbankOffset/bankLength(runtime-only — never persisted).TakeLaneSnapshotis the RT-safe handoff. - Modulation routing DAG — new
ToooT_Core/Modulation.swiftwithModSource,ModSlot,ModSnapshot. Sources: LFO, sidechain peak, envelope, MIDI CC. Destinations: channel volume / pan / send, bus volume, master volume.RenderResources.modBufferis a flat 128-slot Float* allocated at engine init.evaluateModulationruns at every row tick after automation, additively writing source × amount into the same destination fields automation already touches. Bipolar slots span[-amount, +amount]; unipolar map sin LFOs to[0, amount]. Atomic swap pattern + dealloc queue mirrorsAutomationSnapshot. - MaskingAnalyzer (offline frequency-masking) —
ToooT_Plugins/ MaskingAnalyzer.swiftis anactorthat consumes per-channel frozen PCM and produces ranked[MaskingSuggestion]. Hann-windowed FFT viavDSP.FFT, 10-band log-spaced binning matchingLinearPhaseEQ, pair-wise mean-energy comparison with configurable threshold + cap. Confidence score 0…1. Pure offline — never touches RT thread. - StemSeparator scaffold —
ToooT_Plugins/StemSeparator.swiftis the CoreML / Demucs integration scaffold. Streaming AVAudioFile I/O, 7.8 s window with 1 s overlap-add crossfade, per-stem WAV writers, cooperative cancellation, MainActor progress callback. Model is loaded from the bundle (Demucs_htdemucs.mlpackage); init throwsError.modelMissingwith a clear searched-path if absent. - MIDIGenerator scaffold —
ToooT_Plugins/MIDIGenerator.swiftis the same shape: actor, model probe, autoregressive token sampler (softmax with temperature), Magenta "performance" vocabulary decoder that emits[ClipMIDIEvent]ready for a.midiarrangement clip. - Take-comping UI —
ToooT_UI/CompingView.swiftrenders stacked take rows with painted comp regions; horizontal drag paints a region active for the dragged span on whichever take row was clicked. - AUv3 native-UI embedding —
ToooT_UI/AUv3PluginViewWindow.swiftshipsAUv3ViewCache(cachesNSViewControllerper plugin instance to skip XPC handshake on reopen),AUv3HostedView(NSViewControllerRepresentable),AUv3PluginWindow(sheet host with generic-parameter-list fallback when the plugin doesn't ship a custom UI). Uses the documentedrequestViewControllerpath so v3 plugins remain out-of-process. - CollabSession foundation —
ToooT_IO/CollabSession.swiftis the Network.framework P2P transport:NWBrowserBonjour discovery on_tooot._tcp.local.,NWListenerfor structural sync (length-prefixed blob channel — Automerge applies once dep is vendored), UDP multicast for liveParamPacketparameter drags. Fixed-size 36-byte packet wire format encoded with onewithUnsafeBytes, no per-call allocations. - DAW-Project XML exporter —
ToooT_IO/DAWProjectExporter.swiftserialisesArrangement(with tracks, pattern/audio/MIDI clips, fades, gains, MIDI notes) to the Bitwig-led MIT-licensed open interchange format. Replaces AAF / OMF as the open path for handing sessions to other DAWs. - Audio + MIDI clip render-path consumption —
Arrangementnow drives audio for everyClipKind, not just.pattern. Audio clips trigger a one-shot voice into the unified bank region at clip onset and silence the channel at clip end (Arrangement.audioClipsStarting/Ending(forChannel:atBeat:)). MIDI clips carry an embedded[ClipMIDIEvent]array (relative beat offsets,noteOn/noteOff, MIDI note + velocity + instrument) which the render path dispatches at row resolution via the existing voice trigger machinery (Arrangement.midiEvents(forChannel:atBeat:)). Pattern-clip override semantics unchanged. UAT 62 verifies onset, end, noteOn, noteOff for both kinds end-to-end. RTVoiceScheduler— opt-in worker pool for realtime voice processing — spawns N user-interactiveThreads coordinated via per-workerDispatchSemaphores plus a single done-semaphore. WhenEngineSharedState.realtimeMulticoreEnabled = 1, the realtime render block partitions active voices across the pool, each writing into its thread-local scratch slot; voice processing + PDC + sidechain peak run in parallel, then the audio thread does a serial vDSP_vsma reduce into the master + bus accumulators. Defaults to off; flip the flag to opt in. UAT 63 covers the scheduler primitive (full dispatch coverage, slot ownership, prompt teardown, post-stop fallback).
- UATRunner — section reorder + timeouts.
§15(60-second openmpt A/B fidelity test) now runs before the summary so its failures count toward the pass/fail tally, and the missing-reference-WAV path falls through instead ofexit(0).§26(AUv3 plugin discovery + hosting) runs last, bounded by a 15 s discovery timeout and a 30 s hosting timeout — so a coldAVAudioUnitComponentManagercache prints a skip-with-warning rather than gating the rest of the suite (was the v2.0.1 carry-over TODO).
2.0.1 — 2026-04-26
- Spatial Audio editor — proper top-down view — replaces the previous
Metal mesh-shader pipeline (which silently failed on base M1 hardware
and rendered as a black screen everywhere else due to clearColor.alpha
= 0). New
MetalSpatialViewis pure SwiftUI Canvas: listener glyph with nose-pointing-front, concentric distance rings (1 / 2 / 4 / 8 m), cardinal Front/Back/L/R labels, 16 colored channel dots distributed at 2 m default radius (so first-launch users see content immediately instead of every dot piled at origin), drag-to-reposition, hover readout: "Ch N · x ?.? m z ?.? m · vol ??%". Works on every macOS-supported GPU. - Three-pane Automation editor — replaces the single-line legacy view. Sidebar lists every available target (master volume, tempo BPM, per-channel volume + pan for the first 8 channels, per-bus volume — plugin params auto-appendable) with a status dot per active lane and point counts. Gridded canvas with bar lines (every 4th brighter at bar boundaries), value markers at 25 / 50 / 75 %, edge labels, curve fills, selected-point ring, empty-state hint. Inspector strip below shows TIME / VALUE / INDEX of the selected point + Linear / S-Curve picker + Delete. Header has Clear-lane and +Point-at-midpoint shortcuts. Click empty space to insert; drag to move; click a point to select.
- Generative panel rebuild — was 4 buttons on a 1100-character line.
Now a proper editor: 8 generators (Markov Melody / Euclidean Beat /
L-System Arp / Harmony Layer / Bassline / Drum Pattern / Chord
Progression / Variation), style picker (Techno / DnB / Ambient /
Hip-Hop / Jazz / Breakbeat), 12 chromatic keys, 8 scales, intensity
- complexity sliders with % readouts, target channel picker, variation count stepper. Generators actually use the controls — Bassline picks degrees from selected scale + key, Drum Pattern has style-specific layouts, Chord Prog plays I–V–vi–IV in the selected key. Status messages report what was generated ("Bassline in C minor", "Techno drum pattern").
- UX polish — welcome screen rewrite (drop "Mac Tracker for the
Post-Human Era", add ⌘N / ⌘O / ⌘K / Space / ⌘. / ⌘Z keyboard
shortcut card, drag-and-drop hint), HUD status with translucent
material card + smooth slide-in/out animation, transport tooltips
(Space / ⌘. / BPM / TPR),
showStatuslifetime bumped 3 s → 5 s so users actually see messages. - Master linear-phase EQ in the master chain —
LinearPhaseEQ's 4096-point overlap-save FFT convolution is now instantiated byAudioHostand wired intoRenderBlockWrapper.masterEQBlock. 10 log-spaced bands (31 Hz – 16 kHz).AudioHost.setMasterEQBand(_:dB:)EngineSharedState.isMasterEQEnabledfor runtime control. Master chain order: EQ → stereo widen → reverb → safety limiter.
- AUv3 plugin parameter automation —
AudioHost.pluginParamRegistrywalks each loaded plugin'sparameterTreeand registers parameters under target IDsplugin.<channel>.<slot>.<address>(channel inserts),plugin.<channel>.inst.<address>(channel instruments),plugin.bus.<bus>.<slot>.<address>(bus inserts).Timeline.syncEngineToUIevaluates plugin-targeted automation lanes at UI tick rate and writes throughAUParameter.setValue. Native engine params stay on the audio thread; plugin params live at UI rate where setValue is documented thread-safe. - Tempo automation — new target ID
tempo.bpmis honored by the audio thread'sapplyTarget. Lanes write directly tostate.pointee.bpmat row boundaries, clamped to [20, 999]. - Markers + time signatures —
Marker+TimeSignatureChange+TimingMapship inToooT_Core.PlaybackState.timingMapcarries named cue points and a list of meter changes;seekToMarker(named:)jumps the playhead. Persisted in the.madTOOOchunk viaexportAsPluginStateData. Mid-song meter consumption by the render path is a follow-up. - Recording take lanes —
RecordingTake+TakeLaneper channel,RecordingMode { replace, overdub, loop }.commitTakeFromRecordingfinalises the in-progress capture into the configured channel's stack honoring the mode. Replace clears prior takes; overdub / loop append latest-active. Codable for.madpersistence. - Arrangement engine consumption —
SongSnapshot.arrangementships as an optional reference. When non-nil,processTickSequencer's per-channel event lookup overrides the order-list path: a pattern clip on a track atchannelIndex Nroutes the engine to play that pattern's events for channel N. Pattern-grid playback is the fallback when no clip is active. Honors track mute / solo. UAT 61 verifies. Pressing play on a populated ArrangementView now actually produces audible output. Audio (.audio) and MIDI (.midi) clip kinds are still pending. - Cold-launch profiling instrumentation —
AudioHost.SetupTimingsexposes wall-clock per phase (engineBoot / internalDSPBoot / outputUnitBoot). UAT 57 measures and asserts < 250 ms steady-state.AUv3HostManagerno longer auto-scans on init; Timeline kicks offdiscoverPluginsAsync()on a detached utility Task, removing the 1–3 sAVAudioUnitComponentManagerscan from cold launch's main thread. - AppIntents (Shortcuts.app) —
OpenToooTProjectIntent(file → open in app),OpenLastAutosaveIntent,NewToooTProjectIntent, plusToooTShortcutsProviderso the intents surface in Spotlight and the Shortcuts gallery. - Crash-recovery prompt on launch —
TrackerAppViewchecksAudioHost.recentAutosaves(maxAgeSeconds: 86_400)on first appear; if anything is found,CrashRecoveryPromptViewis shown as a sheet with restore-latest / dismiss actions. Engine work was already shipped in 2.0.0; this wires the UI half. - Render-path automation evaluator —
AutomationSnapshotis published atomically toAudioRenderNodeand consumed at every row boundary. Supported target IDs:ch.<N>.{volume,pan,send.<bus>},bus.<B>.volume,master.volume. Lock-free read/write mirrors the song-snapshot pattern.Timeline.publishSnapshotrepublishes wheneverPlaybackStatechanges (UI Bezier lanes are converted to the Core lane format inline). - MAD metadata + thumbnail extractors —
MADMetadataReader.readreturns title, format, pattern/channel/instrument counts, and instrument names from.madand classic.mod/.xm/.it/.s3mfiles.MADThumbnail.renderPNGrenders a tile-style preview of the first pattern via CoreGraphics + ImageIO. Pure Foundation — drops into a Quick Look.appexor Spotlight.mdimporterextension target unmodified. Recipes indocs/MAD_QUICKLOOK_SPOTLIGHT.md. - Cold-launch
os_signpostinstrumentation —AudioHost.setupwraps an outer interval plus inner intervals onEngineBoot,InternalDSPBoot,OutputUnitBoot.AUv3Host.scanLogcovers plugin discovery. Subsystemcom.apple.ProjectToooT/ categoryColdLaunchfor filtering in Instruments.app. exportAudioswitched to multi-core path —renderOfflineConcurrentparallelizes voice processing across cores using a per-voice scratch pool. Bit-exact-modulo-fp-reorder parity with the serial path, verified by UAT 53.- UAT 53 rewritten as a real serial-vs-concurrent parity test (16 simultaneous voices on saw samples; max diff < 1e-6). Replaces the previous silent-input no-op.
- UAT 55 — automation snapshot lifecycle stress: 500 atomic swaps
with a concurrent reader thread driving
renderOfflinecalls; asserts every retired snapshot is released afterprocessDeallocationsdrains.
- Scratch-slot collision in
renderOfflineConcurrent— per-voice scratch slots were allocated asidx % voiceThreadSlotswithvoiceThreadSlots = 8.concurrentPerformdoesn't pin iterations to threads, so any two voices with the sameidx % 8could collide on the samethreadScratchL/R/Monobuffers from different threads. Symptom: max abs diff of 0.054 between serial and concurrent render on a 16-voice test. Fix:voiceThreadSlots = kMaxChannelsandslot = idx(no modulo). Memory cost ~20 MB allocated once at engine init. Material because v2.0.1'sexportAudioswitched to the concurrent path — anyone bouncing through it would have heard wrong output. - Undersized snapshot deallocation queues —
automationDealloc(capacity 64) and songdeallocationQueue(capacity 128) silently dropped entries whenpushreturned false, leaking the retained snapshot at each dropped pointer. Bumped both to 2048, comfortably absorbing realistic main-thread bursts.
renderResources.voiceThreadSlotsis nowkMaxChannels(was8) — sized for one slot per voice, no modulo aliasing.- UI Bezier automation types renamed —
AutomationLane/AutomationPointinToooT_UI/Helpers.swiftare nowBezierAutomationLane/BezierAutomationPointto disambiguate fromToooT_Core.AutomationLane(render-path representation).Timeline.publishSnapshotcontinues to convert between them. - Homebrew cask —
scripts/homebrew-tooot.rbsha256 filled in for the v2.0.0 DMG.
docs/FEATURE_ROADMAP.md— accuracy pass against actual code. Flipped 8 stale items to ✅ SHIPPED (#7 Automation, #10 Crash recovery, #11 Piano Roll CC, #12 MPE, #17 Scene automation, #23 Video sync, #25 Keyboard shortcuts, #26 Undo browser); 4 to PARTIAL (#2, #8, #16, #30, #39). Refreshed pie chart counts and rewrote next-session priorities — the original top-five all shipped.docs/MAD_QUICKLOOK_SPOTLIGHT.md— recipes for wrappingMADMetadataReaderandMADThumbnailin Quick Look.appexand Spotlight.mdimporterextension bundles via Xcode (SPM doesn't produce these bundle types directly).README.md— added AppIntents, multi-core export, render-path automation, MAD QL/Spotlight primitives, and macOS Native Integration sections; updated test count (100+ → 230+); removed broken CI badge; linked the new docs.
- Synthesis tier names — pretentious sci-fi labels replaced with
descriptive musical ones, with inline descriptions per choice:
Carbon→ Studio (clean, precise, tracker-classic)Biological→ Organic (humanized timing, natural vibrato)Xenomorph→ Generative (stochastic / glitchy / experimental) PlaybackState fields renamed in lockstep:carbonCorruption→studioCorruption,carbonGlitchRate→studioGlitchRate,bioArrhythmiaRate→organicTimingDrift,bioBreathiness→organicBreathiness,xenoFractalDim→generativeFractalDim,xenoVoidThreshold→generativeVoidThreshold.
- Cleaned all build warnings to zero: redundant
publicon KeyBindings extension static, unnecessarynonisolated(unsafe)on SendableOSLogconstants (AudioHost, AUv3Host),var ok→let, unusedsongLength, discardable result_ =,CLAPHost.processwrapped in nestedwithUnsafeMutablePointerto satisfy[#TemporaryPointers]. - Cleanup: gitignore release artifacts (
dist/,*.dmg,*.dSYM,*.zip); fixvar fadeClip→let fadeClipwarning in UATRunner.
2.0.0 — 2026-04-17
First major release. ~15 000 lines of new, tested code.
- CLAP host (BSD-3, MIT-compatible) — real-time-safe, modern ABI. u-he / FabFilter / Arturia / Surge XT supported.
- VST3 host — direct Steinberg SDK integration (no JUCE dependency).
- AUv3 host — per-channel insert chains (4 inserts + 1 instrument per channel) and bus inserts on the 4 aux buses.
- ITU-R BS.1770-4 LUFS metering (momentary / short-term / gated integrated).
- True-peak detection + look-ahead limiter AUv3.
- LUFS-normalized export to Spotify (−14) / Apple Music (−16) / YouTube (−14) / EBU R128 (−23) / Amazon Music.
- TPDF dither on bit-depth reduction.
- Multiband compressor (3-band Linkwitz-Riley) + linear-phase FFT EQ (4096-point) bundled.
- Arrangement timeline view (Pro Tools / Logic paradigm).
- Session grid view (Ableton Live paradigm) with quantized clip launch.
- Piano Roll with CC lanes (velocity / mod / expression / pan / pitch bend).
- MIDI 2.0 MPE — per-note pitch bend + pressure with voice tracking.
- Arpeggiator — 6 modes with hold, octaves, gate probability.
- Music theory — 16 scales, 14 chord qualities, frequency-domain quantization.
- Scenes — capture / recall full mixer state.
- Automation lanes with 5 curve types + read/write/touch/latch/trim modes.
- ⌘K Command Palette with fuzzy match over every shipped command.
- Keyboard shortcut customization — ToooT / Pro Tools / Logic Pro presets.
- Undo history browser — 50-level stack, jump-to-step.
- Project auto-save every 60 s with crash-recovery prompt on relaunch.
- JavaScript scripting via JavaScriptCore.
- Text-to-speech via AVSpeechSynthesizer +
/usr/bin/say. - Starter content — 32 in-code synthesized instruments.
- Template projects — Blank / Drum Starter / Ambient Pad / Techno Basic.
- MIDI panic (⌘.) — transport stop + all-notes-off.
- Variable sample rate — 44.1 / 48 / 88.2 / 96 / 192 kHz end-to-end.
- Multi-core offline render —
DispatchQueue.concurrentPerformwith per-thread scratch pool. - Zero-allocation audio thread (Swift 6 strict concurrency).
- GPU DSP — 5-kernel Metal compute library.
- SOLA time-stretch + pitch-shift.
- Track freeze / stems / mastered export paths wired end-to-end.
- UAT: 232 assertions / 0 failures.
- StressRunner: 11 integration scenarios / 0 failures.
- FuzzRunner for parser crash safety.
brew tap mstits/tooot
brew install --cask tooot
Or download the DMG and drag to Applications. macOS 14+, Apple Silicon.