GTS Drive Strip · Changelog

What changed, and why.

The engineering changelog, verbatim from the repository. Numbers are measurements from the build that shipped them; a beta entry is a signed test build, and the plain version tag is the release.

Release tags are lodestone-vX.Y.Z. See "Two numbering systems" at the bottom before reading the history — this plugin has an internal iteration count and a product version, and they are not the same numbers.


[1.1.0] — 2026-10-03

1.1.0 — 2026-10-03, tag lodestone-v1.1.0: the first public release since 1.0.0, and the first under the name GTS Drive Strip. It is rc.2 — the build testers have run since 2026-09-17 — plus the rename, the editor licence-callback fix and the bank auto-gain refit below (≤ 0.12 dB, low drive only). Locked: a serial or the 14-day trial to process, audio passes untouched until then. Signed + notarized, universal (Apple Silicon + Intel), macOS 11+, AU / VST3 / CLAP. Sessions and serials from 1.0.0, every beta and both rcs open unchanged.
  • Fix: GOLD's band split stays below Nyquist (2026-10-01). At absurdly low host rates (clap-validator's 1234.57 Hz) the 2.5 kHz split went unstable; the split corners now clamp to 0.45 × the engine rate. Engages only below 5.56 kHz, so every real sample rate is bit-identical (756 output hashes, 44.1–768 kHz, before = after).
  • Fix: the editor unhooks its licence callback when it closes (2026-10-01). The processor outlives its editor; a licence change after the window closed (an activation finishing, a trial expiring) could call into a deleted editor and crash the host. Found while building GTS Limit, whose editor already did this.
  • LODESTONE is now GTS Drive Strip (2026-10-01). The name changes; nothing a host, a session or a licence matches on does: same AU codes (aumf Ldst Gtsd), VST3 class, CLAP id, bundle id and licence code (LDST), and activations stay in the same folder, so sessions and serials from any beta or rc open unchanged. The bundle is now GTS Drive Strip.vst3 (and .component, .clap); the installer removes the old Lodestone.* bundles first so hosts never list the plugin twice. Proven by a build under the new name: identical ids, pluginval strictness 5 and clap-validator clean. Older entries below keep the name they shipped under.
  • Internal, no change in sound or behaviour (2026-10-01): the LEARN window and its state machine moved into gts_dsp/loudness/LearnWindow.hpp, and the LEARN button's readout into gts_ui's LearnButton::show, shared with GTS Drive Lite (test_state and test_outmatch print byte-identical output before and after; null unchanged). The installer is built by the shared tools/installer/build_installer.sh; installer/build_installer.sh is a wrapper and the pkg is unchanged (same distribution, ids, versions, install locations and payload).
  • Bank auto-gain refit (2026-10-01; Rob chose this refit, option C, over the LEARN-level fit). The bank voicings' static auto-gain is re-measured with the 2026-09-17 voicing signatures and the rectify ADAA in the path, same −18 dBFS-RMS pink plane as before. With Auto Gain on, output moves by at most 0.122 dB, only below drive 2 (Grit −0.12, Rectify −0.11, Silk −0.09, Gold −0.08, Tape +0.06, Hearth −0.01 dB at drive ≤ 1; Rectify +0.02 dB above drive 4). The heritage voicings (Warm/Clean/Bite/Dirty) are unchanged: on real programme their error is spectral, not the calibration level (ROADMAP M3).
1.1.0-rc.2 — 2026-09-17, tag lodestone-v1.1.0-rc.2: rc.1 plus the voicing signatures — each of the six bank voicings now carries its own subtle colour from the bottom of the DRIVE range (Rob's "whisper" verdict from the listening pass: nothing over 0.6 dB), fading to nothing by drive 4 so the pushed and cranked tiers sound as they did. Match Voice draws it as the EQ's zero line. The aliasing gate's audible cut is 16 kHz on the record; every voicing clears −80 dBc at the shipped 4×. Locked like rc.1; this is the build testers receive, with complimentary serials. Sessions from any beta or rc.1 open unchanged — a bank voicing at low drive gains its signature, nothing else moves.
  • Voicing signatures — whisper, fading with drive (2026-09-17, Rob, from the hub listening pass: "clear was a little too noticeable on some, whisper felt great on all and added just enough character to start with"). Measured, the bank voicings were near-flat below drive 4 because a pure shaper is a wire at low drive; Rob wanted character at the beginning. Each voicing now carries one or two RBJ shelves/peaks after its shaper at half the auditioned dB — silk a sheen at 10 kHz, tape a 70 Hz bump and a 10 kHz roll, gold weight at 90 Hz and BAX-geometry air at 20 kHz, hearth glow at 220 Hz with the top eased, grit presence at 2.5 kHz with the lows tightened, rectify edge at 6 kHz with the lows out of the way. The depth is full below drive 1, linear in log2(drive) to zero at drive 4. Lab reference and C++ twin (bank_ref / BankCore.hpp EqBiquad), bank null −220.3 dBFS, fingerprints refit so Match Voice's zero line includes it.
  • Aliasing gate: audible cut 16 kHz (2026-09-17, Rob). Rectify's two surviving folds at 16.1 / 18.0 kHz are low-order harmonics past Nyquist that no oversampling factor changes; the gate now measures the voicing, not the decimator. Rectify reads −84.1 dBc audible at 4×; plots re-archived to the hub.
1.1.0-rc.1 — 2026-09-09, tag lodestone-v1.1.0-rc.1: the first locked build — signed + notarized, universal, needs a serial or a 14-day trial to process, passes audio untouched until it has one. Everything measured since beta.5 is in it: Clip Drive's loudness compensation, the output reference riding the wheel on MATCH, and the activation path in the About box (activate, trial, deactivate, offline keyfile, machine id). The store is Stripe Managed Payments; the serial arrives by email on purchase. Release gates closed this week: the null test is enforced at −200 dBFS in CI, the aliasing/THD plots are archived, universal binary and preset versioning are verified, the user guide gains its Activation page (seven pages), and the licensing chain is proven end to end — server smoke 27/27, client walk 25/25 (activate, seats, trial, clock rollback, expiry), store webhook 6/6. Sessions from any beta open unchanged. (rc.1 never went to testers — rc.2 carries the voicing signatures and is the build they receive, with complimentary serials.)
1.1.0-beta.5 — 2026-09-08, tag lodestone-v1.1.0-beta.5: signed + notarized test build, same day as beta.4, from Rob's first audition of it in the DAW. The I/O rail grows up: OUT MATCH holds the output at the reference, APPLY commits learned and matched gain into the trim knobs (and the plane into OUT TRIM), and Match Voice becomes the measured zero line for every voicing. TONE and AUTO leave the face; EQ and compressor start off and wake when touched; DELTA trim starts at unity. The About box behind the wordmark arrives (beta builds stay unlocked). User guide updated (six pages). Sessions from beta.4 load unchanged — OUT MATCH is off for them.
1.1.0-beta.4 — 2026-09-08, tag lodestone-v1.1.0-beta.4: signed + notarized test build. Everything measured since beta.3 goes to testers' ears at once: the rebuilt compressor detector and its six families, delta monitoring, adaptive Q, the anti-aliased Rectify, and the two readout fixes. The compressor sounds different from beta.3 at identical settings — that is the detector rebuild, on purpose. User guide PDF updated to match (compressor families, DELTA, beta note).
  • OUT MATCH — the output loudness reference (2026-09-08, Rob: "can we get an auto level on the output as well?"): Auto Gain keeps the DRIVE stage at the calibration plane, but nothing after it was compensated — EQ boosts, compressor makeup and clip loss all moved the output, and pushing Clip Drive with Auto Gain on made things quieter (the wet path was pulled down by the full drive gain while the ceiling removed the peaks). New out_match (pill under the OUT title) and out_ref (−24…−6 LUFS, default −18, no face control yet): a K-weighted, absolute-gated measurement of the finished wet path over a 3 s settle window, held as a static makeup so the output sits at the reference. The window restarts whenever a chain parameter or the learned input trim changes and the previous makeup holds meanwhile — so nothing pumps, and identical material at identical settings always lands the same makeup (loudness-honest A/B, deterministic). Sits after the clip and before Mix / Out / plane, exactly the flow the roadmap's "output plane reference" proposal described; per instance for now. The pill reads the held makeup (MATCH +3.1), the OUT arc shows the reference zone while it holds. tools/test_outmatch.cpp: pink noise at −18 through a loud chain leaves at −21.3 with Match off and −18.1 with it on; ref −14 lands at −14.1; a mid-run EQ change re-measures and lands again; run-to-run makeup identical to 1e-6 dB. state_version 4 → 5: new instances default ON; anything saved earlier loads OFF, so no existing session changes level.
  • TONE pill removed from the face (2026-09-08, Rob): it switched the heritage voicing's measured tilt, existed for one voicing of seven and had no amount to set. The tone parameter stays in the layout (hidden, default on) so old sessions and automation load unchanged; tilt lives in the EQ and Match Voice. AUTO GAIN moves up into the space.
  • Match Voice redefined — the EQ's zero line is the voicing (2026-09-08, Rob: "default Match Voice on … EQ settings adjust automatically based on Drive amount on any voicing … turned off the EQ can return to flat but maintain the drive voicing"): Match Voice used to write Warm's two tone shelves into the EQ parameters once, for one voicing, and re-write them as the drive moved — so it changed the sound, fought your own EQ moves, and did nothing on the six bank voicings. Now it writes nothing. With Match on, the EQ window's zero line is the active voicing's measured fingerprint at the current drive — the 1/6-octave tilt the voicing puts on pink noise at the −18 LUFS plane, distortion products included, measured through the same lab references the engines null against, for all seven voicings at seven drives (lab/fit_fingerprints.py → lab/atlas/fingerprints.json → dsp/Fingerprints.hpp). Handles rest on that line and ride the drive knob (and Drift); what the EQ applies is only your deviation from it, so dragging a handle back to flat removes that part of the voicing's tilt, and toggling Match never changes the sound by itself — it changes what "flat" means. Default on, every voicing; off = plain EQ, handles at their own gains. Measured for the record: the bank voicings stay within ±0.3 dB of flat up to drive 4 and tilt 2–6 dB across the band by drive 16 (Silk / Hearth lose lows and lift highs; Gold gains 3–4 dB of lows); Warm carries its −1.2 dB low trim and +1.3 dB air at every drive, and its flux bloom adds up to 12 dB of low end by drive 16 — that is the drive, and now the window says so. The old "tone ghost" (dashed shelves, Warm only) is retired; the dashed line is now the baseline itself. eqFollowVoicing stays a session property, not a parameter.
  • APPLY — the commit move, IN and OUT (2026-09-08, Rob: "when users are done messing around they can hit apply and leave the output gain in place … similar to the input auto gain … instead of saying auto should just be the apply button"): every learned or measured gain in the I/O rail now has one way to become a knob value. IN: LEARN → freeze as before (the learned trim is engaged and the button reads it); APPLY folds that trim into IN TRIM and the learn goes idle. The AUTO pill leaves the face — al_on stays a hidden parameter, freezing still engages it, old sessions and automation are untouched. OUT: while MATCH holds a makeup, APPLY folds it and the plane offset into OUT TRIM, the plane returns to PRESENT and Match switches off — so PRESENT means "the current setting", and a mix move you made on the rail becomes the new zero at a press. Switching MATCH back on re-references: what APPLY put into OUT TRIM comes back out (your own trim moves stay), the window measures, and the output lands at the reference again. APPLY on OUT is also offered with Match off when the plane is off-centre (commit a depth move alone). Both folds are inaudible by construction: the audio thread keeps applying the learned trim / held makeup until it sees the knobs at their new values, then drops it in that same block — test_outmatch: the applied run nulls against a never-applied run at −65 dB across the fold (the residue is the knob's 0.01 dB step), OUT TRIM carries makeup + plane to 0.02 dB, MATCH on again puts the output back at −18 with the plane at PRESENT; the IN fold holds loudness to 0.05 LU. The applied amount rides the state (out_applied). This is the GTS Flow's commit move — every GTS product with a learned input or a matched output gets the same LEARN/MATCH → APPLY pair.
  • DELTA trim defaults to 0 dB (2026-09-08, Rob: "I always have to turn it down when I toggle"): the difference signal plays at unity unless you lift it; the 0–40 dB range is unchanged and saved sessions keep their own value.
  • EQ and compressor default OFF (2026-09-08, Rob: "a cleaner starting point once the plugin is loaded"): a fresh instance is the drive stage and the filters alone; switch a section IN when you want it. Factory presets still land as designed — a preset that sets any EQ or compressor value switches that section on — and saved sessions carry their own switches. Touching a section wakes it: dragging or wheeling any EQ or compressor knob, or grabbing an EQ handle in the window, switches that section IN if it was off — a user gesture only; host automation of a knob never flips the switch.
  • Clip Drive is loudness-honest under Auto Gain (2026-09-08, the last of Rob's audition notes): the compensation used to pull the wet path down by the full Clip Drive, as if the ceiling removed nothing, so pushing the clip got quieter by exactly the clipping loss. Now it gives back what the stage actually did to loudness, from a measured table — pink noise at the −18 LUFS plane through the reference clipper, hard and soft, per dB of drive (lab/fit_clip_loudness.py → dsp/ClipLoudness.hpp). test_outmatch: with Auto Gain on and Match off, drive 0 / 12 / 24 land within 0.6 LU of each other in both modes. Only the Auto Gain path changes; with it off the clip is as raw as ever. (OUT MATCH already covered this at the output; this fixes the level the compressor and OUT MATCH see, and the sound with Match off.)
  • The OUT MATCH reference has a control (2026-09-08): wheel over the MATCH pill to step out_ref by 1 LU (−24…−6); the pill reads REF −16 for a moment and the OUT arc's gold zone moves to sit on the reference. No new knob — the same convention as Q on the wheel over an EQ handle.
  • Header: the DELTA cluster re-seated (2026-09-08): the pill overlapped the preset box's right edge by a few pixels, the version string was drawn underneath it, and the trim knob was squeezed into 38 px so its caption sat on the hairline. Now: pill 8 px clear of the preset box on its centre line, the trim knob given the full header height (face, value and caption keep their proportions, caption clears the rule), version moved into the About box.
  • Licensing client — the About box behind the product name (2026-09-08): click the wordmark in the header and the About box opens: product, version, build tag, formats, the registration line, and the actions the state allows — Activate (email + serial), Start 14-day trial, Load keyfile…, Deactivate this machine, the machine id with a copy button for the offline path, links to the guide, changelog and account page. Hallmark = bypass, wordmark = about; both live in the shared gts_ui::HeaderBar, so every GTS product gets the same door (Rob's call: not every product will have LODESTONE's drawer). A small chip beside the wordmark carries the states that deserve one glance — TRIAL - 9 DAYS, UNLOCK — and nothing when licensed. The engine (modules/gts_licensing) verifies keyfiles with the same public JUCE calls OnlineUnlockStatus uses (proven byte-for-byte against the server by tools/licensing/parity.py), keeps the keyfile in ~/Library/Application Support/Golden Turtle Sound/Lodestone/, and talks to goldenturtlesound.com only when a button is pressed — no phone-home, no periodic check. An unlicensed or expired plugin passes audio dry through the same latency-exact line the hallmark bypass uses: never silence, never a damaged session. Trials carry their expiry inside the signed keyfile; a clock set back past a day of slack counts as expired. Beta builds stay unlocked (LODESTONE_UNLOCKED_BUILD=ON, the About box says "Beta build - unlocked"); the release candidate flips it. Exercised end to end against the live server by tools/test_license.cpp (activate → licensed → deactivate → trial → rollback → refusals: ALL PASS). Shots 07–09 in the harness render the box and the chip.
  • Rectify anti-aliased with ADAA1 — the voicing kept (2026-09-06): Rectify was the one voicing failing the −80 dBc aliasing gate audibly — the 16th harmonic of a 12 kHz tone folded to 112 Hz at −56 dBc. The obvious fix, blunting the full-wave corner with a larger eps, passes the meter and wrecks the voicing: |x| is homogeneous of degree 1, which is what makes the harmonic ratio scale-invariant, and at eps 6e-2 h2 spreads 15.9 dB over a 30 dB input range against 0.14 dB shipped. First-order antiderivative anti-aliasing (rectifyAdaa1 in gts_dsp/BankCore.hpp, closed-form, one sample of state per channel) clears it with no oversampling change and no CPU: 112 Hz −56.0 → −94.0 dBc, everything below 13 kHz now ≤ −84 dBc, h2 on its fitted targets with 0.14 dB spread. The lab reference carries the same ADAA1 and the port nulls at −231.7 dBFS or better. Costs half a sample of group delay at the oversampled rate, Rectify only — under reportable latency. Two folds at 16.1 and 18.0 kHz remain (low-order harmonics just past Nyquist that no oversampling factor touches); whether the gate's audible cut moves to 16 kHz is a deliberate call still open.
1.1.0-beta.3 — 2026-09-06, tag lodestone-v1.1.0-beta.3: signed + notarized test build. One fix, but a load-bearing one — every build before this, 1.0.0 included, refused to load on any Mac older than the machine it was built on.
1.1.0-beta.2 — 2026-08-28, tag lodestone-v1.1.0-beta.2: signed + notarized public test build cut from this state. Ships the docs rewritten for 1.1 and restyled to the GTS token system — the overview PDF (IBM Plex embedded from gts_ui assets, chassis/champagne/tier palette, the banks table, listener/planes/bypass/soft-clip content, a beta note) and the installer welcome/conclusion panes (light site palette); make_pdf.sh now renders beside its source so relative font/asset url()s resolve. Everything below remains "toward 1.1" until the release gates run.
  • Two readout bugs the marketing shots found (2026-09-06): every symmetric parameter — both trims, all four EQ gains — read -0.00 at its centre. Snapping to a float interval leaves the midpoint a few hundred nanounits below zero and the formatter kept the sign; gts::stripNegativeZero, applied after the parameter attachment installs its own formatter, drops a minus in front of something that displays as zero. And the LEARN countdown read FREEZE 3.98222s: JUCE reads a decimal-place count of 0 as "as many digits as it takes", so the seconds are rounded to an int now. Both were in 1.0.0 and in every 1.1 beta.
  • Compressor family: Bus (2026-09-06): a fifth measured character, from the SSL Bus 2 / G3 / The Glue auto-release positions. It needed a mechanism the other four do not use — two parallel release paths, the slow one descending with its own long attack and releasing ~107× slower, with the output taking whichever is reducing more. The reservoir the other families use cannot express it at any setting: it discharges during the release, so the stretch collapses partway through and the memory ratio saturates near 5 against a measured 15.66. A short burst still releases at the knob, because the slow path has not engaged yet. Fitted at the rig's own 5 s measurement window, landing shape 3.05 (target 3.01) and memory 14.26 (target 15.66) with threshold invariance at 0.00 dB.
  • Delta monitoring — hear what the plugin is adding (2026-09-06): new delta toggle in the header with its own delta_trim (0–40 dB, default +12). Output minus the dry input, so you hear the difference and nothing else — the saturation's harmonics, the EQ's lift, the compressor's gain movement. Global rather than per-section, and per-section comes free: combine it with the existing IN pills to isolate a stage (delta on, EQ out, clip out = the compressor alone). Per-zone deltas would also be misleading — once a saturator has run, "what the EQ added" is not well defined at the output. The subtraction is against the finished output at the current Mix, so the delta reflects what the plugin contributes as set; delta of a bypassed plugin is therefore silence. It reuses the same PDC-exact dry line the hallmark bypass uses, so the alignment is the one already measured at −150.5 dBFS. Verified against the plugin's own bypass render: delta == wet − dry to −143.8 dBFS re peak, and a bypassed delta at −199.9 dBFS. The trim is deliberately manual — auto-normalising would hide how much is being added, which is half of what the mode is for. Shared code lives in gts_dsp/DeltaMonitor.hpp so HEARTHSTONE and the rest of the suite inherit it. Engaging it frames the entire chassis in amber with a standing label, because the way a difference-monitor hurts you is silently: a mix bounced while listening to one.
  • Compressor families — VCA / FET / Opto / Vari-Mu (2026-09-06): the four characters measured in the Dynamics Atlas (ten reference compressors, 58 settings) ship as a new comp_family control, default Clean. Each family is six numbers fitted to measurements — release shape exponent, a program-dependence pair, knee width, detector tilt, voiced ripple and a static even term — generated into dsp/CompFamilies.hpp from lab/atlas/comp_families.json, so the fitted file stays the source of truth. Verified through the shipped C++ path: release shape, memory ratio, H3 at 30 Hz and H2 at 1 kHz all land on their measured targets, with threshold invariance at 0.00 dB and the release knob honest to 0.1 ms. Additive, not a swap. The roadmap called for the families to replace Classic/Unlinked/ZeroAttack, but the atlas measured those as "three switch positions on one detector, not three characters" — stereo routing, an orthogonal axis. Swapping would have deleted the link control to make room for something that does not conflict with it, so comp_mode is unchanged and the families sit on their own parameter. That also avoids rescaling existing automation, since JUCE choice parameters normalise by count — the trap the voicing banks had to dodge. state_version 3 → 4; anything older loads on Clean and is unchanged. FET's detector hold is derived from its own measured data (8.3 ms, its fastest measured release) rather than the 20 ms default, which its release knob would otherwise not be able to reach.
  • Compressor detector rebuilt — the threshold now means something (2026-09-06): this changes how the compressor sounds. The old detector ran one branching one-pole on |x| and computed gain from it, so on a steady tone the envelope settled somewhere between the mean and the peak depending on how the attack and release coefficients compared — the panel threshold moved with the time constants. The Dynamics Atlas measured 7.4 dB of steady-GR drift across the attack/release grid: at attack 100 / release 10 the compressor delivered less than half the reduction it promised, and every time-constant tweak silently re-thresholded the unit. Level measurement and ballistics are now separate stages — |x| → peak-hold → gain computer → attack/release smoothing of the gain, in dB. Steady state converges to exactly the static gain reduction whatever the coefficients are, so invariance is by construction, not by tuning. Measured through the shipped path: 0.00 dB spread across the whole grid at 30 Hz, 100 Hz, 1 kHz and 8 kHz, at both +6 and +18 dB over threshold. It also removes the LF ripple distortion, because the peak-hold does not track the rectified wave within a cycle: THD at 30 Hz goes 0.97 % → 0.000 % at the default release, and 9.07 % → 0.000 % at a 10 ms release. (The compressor is therefore no longer a candidate in the low-drive crackle hunt.) Knobs got more honest as a side effect: attack t63 is now within 0.5 % of the panel at every setting (was up to −22 % off) and release within ±3.4 % from 50 ms up (was +30 to +40 %). One deliberate trade: the 20 ms detector hold is a floor on release — a 10 ms release measures ~23 ms. The alternative was keeping 9 % THD there. holdMs is a settable design constant rather than a literal, because a fast-release family (the 1176 measures ~8 ms) will need to lower it. Lab reference gtslab.dynamics.reference_compressor_v2; C++ nulls against it at −146 dBFS or better (the CLI feeds float32 on purpose, so −120 is the honest gate, same as drive and bank parity). Both atlas criteria plus the null are now gated in validate_parity.py, and 7 new lab tests cover the reference. Lab suite 312 green.
  • EQ adaptive Q (2026-09-06): the EQ's Q now follows band gain by a proportional-Q law measured in the Saturation Atlas — Q = 0.5 + 1.15 · (|g|/15)^1.2, shipped as gts_dsp::adaptiveQ. The shape is the fitted p = 1.22 from the SSL Native CS2 E gain ladder; the anchors are taste, chosen so |g| = 3 dB lands near the old default Q of 0.7071 and confirmed by Rob. Rising, not falling — the two "SSL 4000 E" models measured disagree on the law's sign, and rising is right for a knob-only EQ, where wide-at-high- gain floods the neighbours exactly when you push hardest. New eq_aq parameter (ADAPT Q pill in the EQ header), default on. Existing sessions and presets are untouched: state_version goes 2 → 3, and anything older loads with the law disengaged and its saved per-band Q intact — verified by test_state, which round-trips a real processor and asserts an old state runs at the saved Q rather than the law's. eq_*_q stay in state for automation and preset compatibility, and explicit Q is one gesture away: the scroll wheel on a graph handle seeds every band's Q from the law and disengages eq_aq, so the curve does not jump and the wheel is never a dead control. The drawn curve reads Q through the same eqQFor() the DSP uses — reading the raw parameters would have drawn a curve the EQ was not producing. The C++ law is gated against lab/atlas/qlaw.json in validate_parity.py to ±1e-9, so a refit and the shipped header cannot drift apart. auval, pluginval ×3, clap-validator 34/0.
  • Minimum macOS is now actually 11.0 (2026-09-06): CMAKE_OSX_DEPLOYMENT_TARGET was set after project(), where a plain set(... CACHE) is a silent no-op — project() has already created the (empty) cache entry. With nothing set, the toolchain falls back to the build machine's OS, so the studio mini stamped minos 26.0 into every slice of every format. On any Mac below Tahoe dyld refused the bundle outright: Logic reported "did not pass the Apple AU validation" and excluded it, and the plugin never ran a sample. Reported by a Logic tester on 2026-09-06. Moved above project() beside the architecture block (with FORCE, so warm build trees pick it up), and all four formats now build universal at minos 11.0. The installer had claimed <os-version min="11.0"/> since 1.0.0 — the pkg would install happily on a Mac that then could not load a single format. No audio change: null test passes unchanged, auval passes, binaries are otherwise identical in behaviour.
  • Output clip — soft-knee mode (2026-08-20): new clip_soft parameter (SOFT pill beside IN in the CLIP section), default off so every existing state is bit-identical. On, the 16× ceiling gets a 4.5 dB quadratic knee (Rob's number): unity below −5 dBFS in, 0.1 % THD there, 1 % at −3, 5.4 % at 0 dBFS with less high-order spray than the hard ceiling (H5 −40 vs −34 dBc). Same ceiling, same trim, same oversampler — only the shape of the last half-decibel changes. Fitted in the lab, ported as gts_dsp::softCeiling, validated against the Python reference by validate_parity.py (THD match to 0.001 %, soft nulls hard at −600 dBFS below the knee). Hard stays the default and CLIP stays on by default.
  • Voicing banks — the D/A/G orbs are LODESTONE's bank switch (2026-08-20): new bank parameter (Digital / Analog / Gold); the voicing choice stays four entries and addresses a slot within the bank, so automation never rescales. Rosters: D = Grit · Rectify, A = Warm · Silk · Tape · Hearth (default), G = Gold — the six atlas-designed voicings Rob approved by ear, running on gts_dsp/BankCore.hpp inside the 4× oversampled drive block, crossfaded on switch like before. Drive moves each voicing's measured target (1 / 2 / 4 = subtle / pushed / cranked, and keeps going past that into the nasty on purpose). Auto Gain has K-weighted tables for every bank voicing. The tier accent follows the bank (steel / amber / champagne). Bank voicings have no tone stage (TONE dims). Removed: Clean, Bite, Dirty. Rob's call — the new voicings fill their spots and are more original, and no sessions exist yet to protect. Old states with voicing 1–3 now load Silk / Tape / Hearth; factory presets that used them were re-pointed (Clean→Silk, Bite→Grit, Dirty→Rectify) and want a re-voice by ear.
  • Plugin bypass — the hallmark is the switch (2026-08-20): new bypass parameter, also exported as the host bypass parameter (AU/VST3/CLAP), so the turtle hallmark in the header and the host's bypass button are one control that can never disagree. Engaged = linework filled with the tier accent under a faint halo; bypassed = plain engraving (lit = active, same polarity as the rest of the field; the accent doubles as the D/A/G tier readout). ~25 ms equal-weight crossfade, and the bypassed signal is the raw input delayed by exactly the reported latency — measured null −150.5 dBFS against host PDC (the Mix knob's 0% keeps its fractional-delay tap, which is coherent with the wet path but sits 0.35 smp off the integer PDC value; that's why bypass gets its own integer line). Engaged audio is bit-identical to the previous build. Meters and the analyzer keep running while bypassed.
  • Auto Gain recalibrated, loudness-first (2026-08-19): the per-voicing compensation table is now a BS.1770 K-weighted loudness match on −18 dBFS pink (was an RMS/energy match). Fixes biases the Saturation Atlas measured (lab/atlas/FINDINGS.md): up to −4.2 LU on Clean at high drive, a constant +1.7 LU on Bite, and a hidden 4.7 LU loudness step when A/B-ing Clean between drive 1 and 4. With Clip enabled, Auto Gain now also gives back the clip's −0.5 dB static input trim (a flat −0.49 LU honesty offset in the default chain); real clipping loss stays uncompensated — that's the clip's job. With Auto Gain on, output level changes vs 1.0.0 at identical settings; residual loudness error at the −18 LUFS calibration plane is now ≤0.09 LU across all voicings and drives.
  • Auto Level (the listener's Calibrate move): LEARN accumulates up to 30 s of raw input, FREEZE runs the null-tested BS.1770 gated measurement and locks a static trim to −18 LUFS; Auto Level engages it alongside the Trim knob (which never moves). Learned trim is plugin state and survives save/recall. LEARN is a UI action, not a parameter.
  • Plane parameter: output depth stepper, ±6 dB in 1.5 dB steps, its own gain stage after Out (nudging never moves the Out knob, never re-drives the clip ceiling). Neutral = Present.
  • State format now carries state_version (2); 1.0.0 states load as before.
  • Universal binary (arm64 + x86_64); CLAP state-recall, absurd-sample-rate, and parameter-text fixes (2026-08-18); tail length reported (2 s).

[1.0.0] — 2026-08-01

Tag: lodestone-v1.0.0 → commit 4e4d41a (internal iteration v1.16) Shipped as Lodestone-1.0.0.pkg, macOS arm64, signed + notarized (Team PMS449B9XJ).

First public release. Clean-room transformer-drive channel strip, ported to C++/JUCE from the Phase 2 Python prototype and null-validated against it.

Drive engine

  • Four voicings — Warm, Clean, Bite, Dirty — from measured fits (lab/phase2/voicings.json)
  • Drive 0.25–16× (1.0 = the Mystic2 reference point)
  • Model rev 1.1 bounded odd residual; the unbounded Phase 2 path made Clean a runaway phase-inverted LF booster at high drive (+21 dB at drive 8)
  • Static calibrated Auto-Gain, pink-noise referenced — deterministic, so no pumping

Tone shaping

  • 4-band EQ (Low/Mid/High/Air) with an interactive draggable curve view
  • Resonant SVF high-pass / low-pass, full 20 Hz–20 kHz range, with filter keytracking
  • Tone ▸ EQ: the voicing fingerprint becomes the EQ zero point; Match Voice tilts live with Drive
  • 3-mode compressor
  • Drift modulation section with tempo sync (division picker, transport phase-lock)
  • Output clip stage, 16× oversampled
  • Latency-matched global Mix

Interface

  • Tabbed GUI (Strip / Drift / Modulate), custom look, factory preset banks
  • Spectrum + THD analyzers with IN/OUT legend, full-height I/O and GR metering
  • Colour themes: Lodestone, Ember, Golden Turtle, Amethyst, Malachite

Formats: AU, VST3, CLAP, Standalone. Requires macOS 11.0+.

Verification at release: C++ nulls against the Python reference at −210.3 dBFS worst case across 28 drive+tone cells (gate: −120). Safety clip THD 3.63% @ 0 dBFS, H3 −31 dBc, silent below −3 dBFS.


Unreleased

  • Universal binary — CMAKE_OSX_ARCHITECTURES is now set to arm64;x86_64. 1.0.0 shipped arm64-only purely because this was never set.

Verified on the studio machine 2026-08-17: - AU, VST3 and CLAP all report x86_64 arm64 under lipo -info - the x86_64 slice is validly sealed (codesign -dv --arch x86_64) - native auval -v aumf Ldst Gtsd passes on the fresh build - the Standalone launches under Rosetta, so the Intel slice executes

Still outstanding before this can ship: Intel AU validation in a clean plugin environment, and a signed + notarized universal pkg. Neither is a code problem.

Per the convention below, the version bump, the tag, and this section move together in whichever commit gets released — so project(LODESTONE VERSION ...) stays at 1.0.0 until that release commit.


Two numbering systems (read this before tagging anything)

The 34 commits from 2026-08-01 are labelled v0.1 … v1.16. Those are internal iteration numbers, not product versions. The entire sequence produced exactly one public artifact: product version 1.0.0.

So v1.16 does not mean "version 1.16" — it means "the sixteenth iteration of the 1.x UI work", and it is the commit that built product 1.0.0.

Established 2026-08-17, verified from build timestamps: internal v1.15 (19:46) added the installer and set project(LODESTONE VERSION 1.0.0), internal v1.16 (20:56) followed, and the binaries were signed at 21:10:39 with no source file modified in between. The shipped 1.0.0 was therefore built from v1.16. An earlier reading that guessed v1.15 was the ship point was wrong.

Convention going forward

  • Internal iteration labels are retired. Commit messages describe the change; they do not carry a version.
  • Releases get an annotated tag lodestone-vX.Y.Z on the exact commit that was built.
  • project(LODESTONE VERSION ...) in CMakeLists.txt is bumped in the same commit that gets tagged. That is the anti-drift rule: the version compiled into the binary, the tag, and this file always move together, so CFBundleShortVersionString on any build can be traced to one commit.
  • Add the release section here in that same commit.