Futureboard Studio 2026.8.9 Beta 1 — Hotfix
Futureboard Studio 2026.8.9 Beta 1
Hotfix — 9 August 2026
A one-day hotfix on top of 2026.8.8 Beta 1, the first public beta.
Two problems in that build were serious enough to warrant a same-week release: Professional activation was disabled in every CI-produced binary, and the plug-in scan could never finish, so the plug-in catalog never updated. Both are fixed here, along with plug-in classification, plug-in editor window behaviour, audio device persistence, one allocation on the audio thread, and a round of built-in plug-in work.
If you installed 2026.8.8 Beta 1, you want this build.
Highlights
- Activation works again — 2026.8.8 Beta 1 shipped with no activation endpoint baked in, so Professional users were told "Activation is not configured for this build"
- The plug-in scan completes — a hung bundle no longer stalls the whole sweep, so the catalog actually gets written
- No more duplicate plug-ins — bundle-format plug-ins were listed twice under two ids that dedup could not collapse
- Correct instrument / effect classification — read from what the plug-in declares instead of guessing from its name
- Kontakt 8 and other bundles that log to stdout are no longer silently discarded
- Plug-in editor windows: Space drives the transport, no more always-on-top editor, cleaner teardown
- Project sample rate is saved and restored; ASIO devices match reliably
- A latency-graph rebuild no longer allocates on the audio thread
- EQuz8, EchoSpace and MixStation improvements
Licensing
Professional activation was broken in 2026.8.8 Beta 1.
license.rs resolves the activation service from option_env!("FUTUREBOARD_LICENSE_API_URL"), but build.rs never supplied it — it baked only the account URL and the public key. The gap stayed invisible because a maintainer's shell exports that variable, so local builds baked it anyway. Every build without the export — CI above all — shipped a Professional binary with activation_endpoint() == None, which disables Activate and tells paying customers that activation is not configured for the build. That is what 2026.8.8 Beta 1 shipped.
The endpoint is now read from .env like the account URL, non-HTTPS values are refused at build time (the license key is sent to this endpoint, and license.rs rejects plaintext at runtime anyway), and a build with no endpoint emits a loud cargo:warning instead of silently producing an unactivatable binary.
Plug-in scanning and the catalog
The scan could not finish. run_isolated_bundle_scan waited on the scanner child with no deadline, and soothe3 — plus three Neural DSP / Supertone bundles — never returns from module load. The sweep hung forever, and because the catalog is written only after the sweep completes, it was never updated. Each bundle now gets a 30-second budget; the child is killed at the deadline, recorded as a failure, and the sweep carries on.
- Both scanner pipes are drained on their own threads. One Waves shell emits 718 classes, far past a pipe buffer.
- Bundles that log to stdout no longer vanish. Kontakt 8 writes two
[info]lines while loading, which sat in front of the scanner's JSON and failed the parse, discarding every class in the bundle. The child now points fd 1 at stderr for the duration of the scan and writes its payload to a saved duplicate behind a sentinel; the parent takes the text after the last marker, and still accepts an unmarked payload. - Every bundle-format plug-in was reported twice. A
.vst3bundle directory containsContents/x86_64-win/<same-name>.vst3, and both walkers matched the inner file as well — two different stable ids, which dedup could not collapse. Bundles are leaves now. - Failed modules are failures, not plug-ins. A module that fails to load carries a
loadErrorand is recorded as a scan failure instead of becoming a plug-in row named after its file. 32-bit-only bundles report an architecture mismatch. - Stale rows are pruned.
write_catalogremoves rows the scan did not produce, guarded so that an empty scan keeps the existing catalog — an unplugged drive cannot wipe it.
Measured against a real 972-class library: 1756 rows with 710 duplicate vendor+name+format pairs became 988 rows with none, scanning in 94 seconds with 5 named failures. A second rescan prunes nothing.
Plug-in classification
classify_kind matched the strings bass, drum, generator and piano against the plug-in name. Across the 972 VST3 classes surveyed, that misfiled 43 of them — Waves Fx|Bass, Fx|Drums, Fx|Generator and Ozone 12 Bass Control were all listed as Instruments.
Classification now reads only what the plug-in declares: VST3 subCategories, CLAP features, AU component type. A plug-in that declares nothing is PluginKind::Unknown rather than a guess, and Unknown stays insertable as an effect so an untagged effect is not lost.
sub_categoriesis now persisted — plug-in database schema v2, an additiveALTER TABLE, with reads tolerating its absence so a v1 database still loads read-only. A restart re-derives the same kind and category the scan did.- Project format v36 stores each insert's registry-resolved instrument/effect role, so an effect in slot zero is never mistaken for an instrument after a project load. Projects written before v36 keep their positional fallback.
Plug-in windows and editors
- Space drives the transport from a plug-in editor. The editor shell is a raw Win32 window in the main process, so a key pressed on its chrome reached neither GPUI nor the plug-in host, and Space did nothing.
- The legacy in-process editor is no longer always-on-top. It dropped
WS_EX_TOPMOST— which outranked every other application on the desktop — and is now owned by the app window. - Cleaner teardown. Pending editor work is cleaned up during unload, and focus resolves on the foreground UI thread.
reset_projectcloses the Add Insert window, which otherwise survived a close or switch aimed at a track that no longer exists.- The plug-in picker gains a Type column, and Enter now closes the picker. It previously inserted the plug-in but left the window showing a closed picker, unlike double-click and Add.
- The external Plugin Manager is integrated into Settings, and dense typography constants are standardised across that surface.
Audio devices and connections
- Project sample rate is persisted. Each project stores its nominal sample rate and restores it before loading audio sessions, including the deferred engine reopen path.
Realtime
A latency-graph rebuild no longer allocates on the audio thread.
refresh_runtime_latency_graph runs per block from the audio callback. Its steady-state scan was cheap, but the moment an observed latency differed from the active plan it rebuilt everything: a Vec of observed values, a fresh plan behind a HashMap<String, usize> with a cloned track id per entry, a delay-line resize, and two std::env::var_os calls — allocation, string hashing and the environment lock, all inside the callback.
Plug-ins that report a fixed latency never reached that branch, which is why it went unnoticed. A bridged plug-in whose reported latency tracks something live — a network plug-in following its own jitter buffer — re-enters it as often as its latency moves, and every visit is an xrun.
Latency planning now routes on the resolved index fields the render path already uses instead of hashing ids, so the algorithm runs in place over vectors that are already sized. PDC rings reserve capacity to the next power of two while their length stays exact, so a latency that grows mid-session is a length change inside memory the control thread already reserved. If a plan ever outgrows even that, the realtime refit declines rather than allocating, and the affected tracks pass through uncompensated until the next graph swap.
Built-in plug-ins
EQuz8
Every band opened inactive and flat, so the first gesture on one did nothing audible. Shaping a band — dragging its node, turning a knob, the wheel, arrow keys — now switches it on in the same edit. An explicit active in the patch (chip double-click, rack switch) is still honoured, so turning a band off still turns it off.
EchoSpace
- Tempo sync. Delay times can follow the project tempo, across 18 note divisions (straight, dotted, triplet), clamped at both ends so a bad tempo cannot produce a zero-length or infinite delay. Rust owns the division table and the conversion.
- L/R link. An edit on either side mirrors to the other, and enabling link snaps right onto left.
- Free
time_msvalues are left untouched while synced, so switching Sync off returns the line to where it was. - Built-ins previously had no transport at all. The host producer now publishes the block's tempo into the DSP, and
hostStatuscarriestempoBpmso the editor prints the length the line is really running at. Status is pushed on instance bind rather than up to a second later.
MixStation
- Serial-chain editor. The rack was a 3×2 grid, so processing order read across two rows. The chain is now one horizontal row — position on screen is position in the signal path — and the 176px module drawer is gone; an empty position is its own picker. Per-module accent colour carries from the faceplate spine through the position number, bypass switch and knob arcs.
- Cut filters are 24 dB/oct Butterworth cascades at the correct pole Qs, and drop out of the path entirely when parked at 20 Hz / 20 kHz instead of quietly shifting phase.
- Sweepable mids get proportional Q, 0.7 at unity to 1.9 at the extremes.
- Saturation — the bias was multiplied by drive, so at high drive the negative half collapsed to nothing and the stage became a half-wave rectifier. Bias now sits after the drive and the curve is normalised by the average of the two half-gains, so the halves bend apart while level stays matched. The curve tends to the identity as drive approaches zero, so leaving it at 0% no longer steps the level. First-order antiderivative anti-aliasing replaces pointwise evaluation — no oversampling, no latency, with the folded third harmonic down by more than 30%.
- Compressor smooths gain in dB with a program-dependent release: short hits recover at the set release, sustained material lets go several times slower.
- Limiter gains a soft knee 1.5 dB below the ceiling, a hold window and a second release pole, so it stops tracking the bass waveform. The ceiling stays absolute and latency stays zero.
Compatibility
- Project format v36. Projects saved by this build do not open in 2026.8.8 Beta 1. Older projects load normally.
- Plug-in database schema v2. Additive; a v1 database still loads read-only.
- MixStation saturation will sound different. Drive percentages now map to a different shaping amount, so existing projects using MixStation saturation will not sound identical.
- EchoSpace projects saved before this build load at the documented defaults for the new sync, division and link fields.
- A rescan is recommended after upgrading, so the catalog picks up the deduplication and the corrected classification.
Validation
Compile and test evidence for this release: cargo test -p mixstation (32 pass), echospace Rust (33) and its editor UI (43), cargo check --workspace, and both editor bundles build clean. The scanner figures above come from a scan of a real 972-class plug-in library.
The EchoSpace and MixStation changes were not verified by listening or in the host window — they are covered by unit tests only. Please report anything that sounds wrong.
Downloads
| Platform | File |
|---|---|
| Windows | Futureboard.Studio-2026.8.9-beta1-windows-x86_64-Setup.exe |
| macOS (Universal) | Futureboard.Studio-2026.8.9-beta1-macos-universal.dmg |
| macOS (Apple Silicon) | Futureboard.Studio-2026.8.9-beta1-macos-arm64.dmg |
| macOS (Intel) | Futureboard.Studio-2026.8.9-beta1-macos-x86_64.dmg |
| Linux | Futureboard.Studio-2026.8.9-beta1-x86_64.AppImage |
Full changelog: fd83d032...b4ea14c2