Skip to content

Latest commit

 

History

History
829 lines (680 loc) · 50.7 KB

File metadata and controls

829 lines (680 loc) · 50.7 KB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.5.18] - 2026-06-03

Fixed

  • Onboarding Escape ate the panic double-press. The 0.5.15 onboarding Escape-to-dismiss handler used capture-phase + stopImmediatePropagation, which swallowed the first of a double-Escape while the first-launch onboarding was visible — so the panic reset never fired. Onboarding now dismisses on Escape without stopping propagation; App's single Escape owner still runs. (Restores the advisory E2E 07-keyboard panic test to green.)

[0.5.17] - 2026-06-03

Fixed

  • CI Test Gate (test-only): the 0.5.16 EQ-smoothing change added an sr argument to ThreeBandEq::process_block, but the integration/bench callers in mixi-core/tests/ still passed one arg. cargo test --lib doesn't compile those targets, so the break only appeared in CI's full cargo test. Updated the callers; full cargo test is green. No production-code change — the 0.5.16 app artifact was already correct.

[0.5.16] - 2026-06-03

Round-2 audit tail — the remaining deferred + minor items (173 Rust + 586 JS tests, dual-path audio E2E 81/81 on both backends).

Fixed — audio

  • Seqlock torn snapshot (#10): the DSP worklet's param-bus seqlock could exit its retry loop on a still-torn read and run the engine on it. It now reads into a scratch, promotes to a last-good buffer only on a validated read, and always feeds the engine last-good — never a half-written batch.
  • Loop zero-cross snap lost on rebuild (#13): snapped loop bounds are now persisted on the transport and reused on every seek/cue/resume/segment rebuild (was re-applying the un-snapped store value, so the click-free seam was lost). snapToZeroCrossing finds a true sign-change across both channels.
  • Sum-bus headroom (#14): the two-deck sum is trimmed by 1/√2 before the master chain (Wasm path) so a both-decks-full mix lands at ~0 dBFS instead of slamming the dynamics; master gain is the make-up.

Performance

  • Control-path fan-out (#15): registerUserInteraction no longer fires a store-wide update on every drag tick when the AI is OFF; MIDI CC input is coalesced to one write per control per frame (a knob sweep was 200-400/s); PremiumJogWheel + PhaseMeter use selector subscriptions instead of firing on every store mutation.
  • Waveform overview (#17): two-layer canvas (static waveform painted once + transparent overlay) removes the full-canvas putImageData blit every frame.
  • Track list (#17): virtualized above 120 rows (measured row height) — a 1000-track library mounts ~30 rows instead of ~9000 DOM nodes.
  • Grab-bag (#19): worker DPR transform applied on init (no more first-paint blur); SharedParamBus reuses a scratch pair instead of allocating per get/setFloat; MasterLedScreen's RGBA LUT hoisted to module scope; crossfade/ segment cleanup timers tracked + cancelled on destroy; WaveformOverview drag listeners torn down on unmount; Python /ws/mixer teardown generation-guarded.

Notes

  • Visual smoke-test recommended for the two #17 UI rewrites (verified here for build/runtime soundness). Skipped as low-value/risky: flanger modulo→bitmask, per-sample denormal-flush gating, runtime DPR-change re-read.

[0.5.15] - 2026-06-03

Round-2 audit (latency / performance / concurrency / audio-quality / UX / UI): 3 severe + 12 moderate clusters fixed and verified (173 Rust + 586 JS tests, param-bus layout v3, dual-path audio E2E 81/81 on both WebAudio and Wasm DSP).

Fixed — severe

  • Deck stuck "playing" after any seek. Only play()'s source synced the store on its natural end; seek / crossfadeSeek / segment-transition sources didn't — so play → seek (hot-cue/waveform-click) → track-end left the deck lit and frozen forever. A single onNaturalEnd() now syncs all four sites; a per-deck transport epoch makes in-flight seeks bail when a play/pause races them. (Regression from 0.5.14's mutex work.)
  • Master fader couldn't mute. gain==0 was forced to unity in the Wasm DSP path — dragging master to 0 played at full volume. Removed the guard (the layout-version passthrough already protects an uninitialized bus).
  • Escape destroyed a live mix. One Escape hit three listeners (Panic + eject Deck A + VFX) and closed no dialog. Removed eject-on-Escape; App is now the single Escape owner (closes settings→browser→vfx, Panic only when nothing is open); SettingsModal/Onboarding got dialog roles + focus management.

Fixed — moderate

  • loadTrack is serialized against in-flight seeks (no more previous track playing over the new one).
  • WASM beatmatch PLL used current-BPM beat periods against original-BPM playheads (~3.2% drift, lock loss) — now uses original-BPM periods.
  • Silent failures: load / drag-import / record / audio-init now surface toasts instead of failing silently; audio-init failure keeps the start screen for retry instead of dropping into a broken no-audio app.
  • Wasm control latency: param flush is synchronous in Wasm mode (was rAF-gated, adding ~16-33ms to every fader/EQ/crossfader move).
  • EQ zipper/click: EQ dB smoothed per band (block-rate coeff recompute); biquads always run (unity at 0 dB) so returning a band to flat can't click.
  • Key Lock quality: JS-fallback shifter no longer collapses to mono (independent L/R grains); the granular look-back is ratio-aware and the ratio is clamped so reads never run past write_pos (no warble when slowing).
  • eject frees the decoded AudioBuffer (~105MB) + raw bytes immediately.
  • Master dynamics: limiter attack is a ~0.15ms one-pole (no bass-transient distortion from instant gain steps); Punch is true parallel dry/wet (continuous knob, no limiter slam); the beat-gate is driven by the deck BPM (new param-bus slot) instead of a hardcoded value that buzzed at ~441 Hz.
  • Mobile: consistent dark theme (removed the half-built light override), 44px touch targets, home-indicator safe-area inset, and an audio-clock-driven beat pulse (was a drifting setInterval that re-rendered the deck 4x/sec and leaked a timer).

Performance

  • DSP worklet caches its Wasm-memory TypedArray views (no ~700 allocs/s on the audio thread); master analyser RMS is frame-cached; the waveform tick loop honours fpsLimit; heavy deck components (jog/pads/pitch/overview) are memoised.

Known deferred

  • Master sum-bus headroom trim (loudness decision) and the WaveformOverview main-thread canvas rewrite + track-list virtualization (need profiling).

[0.5.14] - 2026-06-03

Rigorous backend/frontend/audio audit — 21 blocking-to-severe issues fixed and verified (586 unit + 173 Rust tests, full build, dual-path audio E2E 81/81 on both WebAudio and Wasm DSP).

Security

  • Command bridge now validates every argument against a per-action schema (type + range + enum) instead of only checking the action name — a whitelisted action can no longer be driven with out-of-range/wrong-typed values. Hardened the store sinks it feeds: hot-cue index/time bounds, EQ-band whitelist (blocks __proto__/constructor prototype pollution via [band]), and finite/in-range auto-loop beats.
  • LAN multi-device sync (UDP) — magic + exact-size validation on incoming packets before they reach the renderer, a real per-IP token-bucket rate limit (the old ">100/sec" comment was never enforced), and outgoing targetIp restricted to private/link-local/loopback. Idle buckets are pruned; state cleared on stop.
  • /api/stream SSRF — the host allow-list now matches on a dot boundary (a bare endswith accepted evilsoundcloud.com); the yt-dlp-resolved CDN URL is rejected if its host resolves to a private/loopback/link-local/reserved IP; and redirects are disabled on the proxy hop so a 302 can't bounce onto an internal host.
  • /ws/mixer caps inbound frames at 512 KB (app-level close 1009 + uvicorn ws_max_size).

Fixed

  • Doubled audio / wrong playhead on rapid seeks. seek, crossfadeSeek, and the 1 Hz segment-transition each await a decode between reading and mutating the deck's source; with nothing serializing them, two overlapping ops could leave an orphan BufferSource playing or an offset pointing at a stale segment. A per-deck async mutex now serializes all source-swapping ops; the segment-transition re-validates the playhead after acquiring the lock.
  • Deck frozen silent if the pitch-shift worklet died. connectSource pins source.playbackRate to 0 and lets the worklet drive it; a processor crash or a never-completing init left the deck silent forever. Added onprocessorerror + a bytes-based init with a 5 s ready timeout that fail over to the native playbackRate path (key lock disabled, transport continues).
  • Wasm DSP could read a half-written param batch. The worklet bulk-copies the param bus each block, so a coupled pair (FX amount+active, distortion, limiter) could be torn for one block. Added a seqlock (JS/worklet-only generation slot at offset 408; Rust unchanged) — the writer brackets coupled writes and the full flush, the worklet retries its snapshot copy on an odd/changed counter.
  • CPU pinned (~100×) by denormals when a deck went silent. denormal_kill only scrubbed output buffers; the IIR state kept its subnormals. Now flush_denorm cleans biquad (EQ/color/master filter) z-state per block, the delay/flanger feedback writes per sample, the phaser/DC-blocker memories — and NaN/Inf are flushed too (self-healing).
  • Phantom library tracks whose IndexedDB blob was gone made the loader fetch(''), which resolves to the app's own HTML (then "decoded" as audio). Tracks now resolve a real blob URL (recovered on demand) or are removed.
  • Garbage VU value — metering peakL reinterpreted the float's bit pattern as an Int32; now read as f32.
  • Python engine crash left the app on a dead port — it now respawns with exponential backoff (capped, reset after a clean minute, suppressed on shutdown).
  • PLL produced a NaN phase delta from an un-analyzed master (BPM 0/NaN) — now guarded like the slave period.
  • MIDI learn armed a 10 s auto-cancel timer that was never cleared on a successful map or panel unmount (late state mutation / leak) — now cleared in both.

Changed

  • Store→param-bus flushes are coalesced to one per animation frame (the store fires on every change, including non-DSP UI state); init and Wasm-activation flushes stay synchronous.

[0.5.13] - 2026-06-03

Fixed

  • Wasm DSP produced NO audio (silent). The shared DSP param bus was never populated — the DspParamWriter setters were dead code, so the Rust engine read every gain as 0 and smoothed to silence (deck FADER/XFADER_GAIN have no zero-guard: total_gain = fader³ × xfader). MixiEngine.flushParamStateFromStore() now writes the full mixer state to the bus with units matched to the Rust engine (FADER raw, EQ in dB, COLOR_FREQ in Hz, per-deck xfader gains, master gain/filter/dist/punch/limiter), kept live via a store subscription. With useWasmDsp on, audio now flows through the Rust DSP.
  • Channel VU meters were dark in Wasm DSP mode. With the deck's WebAudio output detached (the worklet carries the audio), the per-deck analyser branch had no path to the destination, so Web Audio stopped rendering it. A gain-0 keep-alive now keeps the meter branch live (no added audio).
  • Pitch-shift worklet ReferenceError: performance is not defined on every quantum (performance isn't in AudioWorkletGlobalScope) — now falls back to currentTime. Removed a false-positive scheduling_delay glitch telemetry detector (wall-clock between batched process() calls isn't a real metric).
  • App remount loop on launch (repeated Destroying AudioContext + WebSocket churn): MobileScaleWrapper rendered children in two different element structures across the needsScaling toggle, remounting the whole app on resize; now keeps one structure and toggles styles. useMixiSync memoises the engine reference so its effects don't churn.
  • Backend engine killed on a 10s timeout though the ~40MB PyInstaller engine cold-starts in ~13s — the bridge then failed forever against a dead port. Startup no longer blocks on or kills the engine; the bridge retries and connects when it's ready.
  • Dropped the obsolete --experimental-wasm-simd V8 flag (logged unrecognized flag at startup; SIMD is stable).

Added

  • npm run test:audio — a real-app dual-path audio E2E that launches the built Electron app and validates every mixer feature (per-deck play/mute/gain/EQ/colorFX/rate/keyLock/all FX, crossfader, master FX, hot cues, loops, quantize, sync/beatmatch, headphones/PFL, load/unload, VU meters, beat clock) against actual master-analyser RMS on both the WebAudio and Wasm DSP paths. 81/81 pass in both modes.

[0.5.12] - 2026-06-02

Fixed

  • Rust/Wasm DSP worklet now actually engages (was always falling back). The AudioWorklet was fed the full wasm-bindgen mixi_core_bg.wasm, which requires per-build hashed __wbindgen_* imports an AudioWorklet cannot satisfy — so it timed out after 5s and silently fell back to the WebAudio path. The DSP is now built as a dedicated, zero-import wasm (new mixi-dsp crate, extern "C" ABI, owned I/O buffers) that the worklet instantiates with an empty import object. With useWasmDsp enabled, audio now flows through the Rust DSP (verified in-Electron: "Wasm DSP path ACTIVE — Rust processing audio", no fallback, no warnings).

Changed

  • DspEngine (mixi-core/src/dsp) is now wasm-bindgen-free and shared by both mixi-core (analysis) and mixi-dsp (lean worklet wasm) via #[path] — single source of truth. The unused bindgen DspEngine/processRaw exports were removed from the analysis wasm.
  • scripts/build-wasm.mjs builds both wasm artifacts and fails the build if the lean DSP wasm ever gains an import or loses a required export (regression guard).
  • WasmDspBridge resolves the worklet/wasm URLs via document.baseURI (correct under the Electron file:// origin) and transfers the wasm bytes to the worklet instead of a WebAssembly.Module.

Build/CI

  • Set the Vite build target to es2022 — Vite 7 / esbuild 0.27 (locked since the dependency bumps) otherwise fail vite build trying to downlevel destructuring inside the top-level-await pass (broke Test Gate, Docs, Docker, and the release build).
  • dist:* scripts use cross-env for EXEC_EXT, fixing the Windows release build (the bare EXEC_EXT=… prefix isn't valid in cmd.exe).
  • CI builds + verifies the lean DSP wasm (mixi-dsp), and runs its native tests.

useWasmDsp remains opt-in (default off). The Rust DSP path is verified working but unchanged audio behaviour for existing users; flip the default once it's been exercised across sample rates/devices in real use.

[0.5.11] - 2026-06-02

Fixed

  • Fatal "Vs is not a function" crash on launch (P0)vite-plugin-wasm emits a top-level await, so vite-plugin-top-level-await rewrites every chunk that transitively contains it into an async-initialised chunk whose exports are assigned inside a deferred .then(). zustand's create/persist were co-bundled into such a chunk, but the lazily-loaded DesktopRoot chunk builds a store at module-eval time (sessionStore) without awaiting that init promise — so create was still undefinedTypeError caught by the ErrorBoundary on every open. Fixed by pinning react + react-dom + scheduler + zustand into one synchronous react-vendor chunk (none use top-level await), so those exports are assigned eagerly before any consumer runs. zustand is grouped with React to keep a single React instance.
  • Fatal "Failed to construct 'WebSocket'" crash on launch (P0)MixiBridge computed const WS_URL = \${WS_BASE}/ws/mixer`at module-eval, reading the top-level-await-deferredWS_BASEbefore it was assigned →undefined/ws/mixer, which under Electron's file://origin throwsSyntaxError: scheme must be ws/wss. The URL is now resolved lazily at connect()` time (after mount, once the chunk has initialised), and WebSocket construction is wrapped in try/catch so the optional AI-bridge feature can never crash the UI.
  • electron-builder "env EXEC_EXT is not defined" — electron-builder 26.x rejects the undefined env var in the extraResources pattern mixi-engine${env.EXEC_EXT}. The dist* scripts now set EXEC_EXT explicitly (empty for mac/linux, .exe for win).

Internal

  • vite preview now sends the same COOP/COEP headers as the dev server, so the CI e2e run (which serves the production build) and local preview have SharedArrayBuffer for the Wasm audio engine.

[0.5.6] - 2026-05-09

Security

  • Electron disk-recording IPC arbitrary file write (P0)disk-rec:save-as and disk-rec:recover validated only the source path and copied to any renderer-supplied destination. A compromised renderer (e.g. via a malicious community deck) could overwrite any user-writable file (~/.zshrc, ~/.ssh/authorized_keys, app config). Destinations must now match a path blessed by disk-rec:show-save-dialog; entries are single-use.

Fixed

  • WASM init race in 6 detectors (P1)BpmDetector, KeyDetector, DropDetector, WaveformAnalyzer, metadataParser, Blackboard each had their own import('mixi_core').then(m => wasmModule = m).catch(() => {}) racing against wasmBridge's init flag. When isWasmReady() was true but the local module ref was still null, detection silently fell back to the slower JS path and the empty .catch swallowed every failure. Replaced with a synchronous getWasm() getter on wasmBridge that returns the already-statically-imported namespace; zero race window, zero duplicate import.

[0.3.1] - 2026-04-08

Added

  • Beatgrid Editor (Phase 1+3) — DOM overlay macro-drag + GRID panel with SET downbeat, micro-nudge ±1ms, halve/double BPM, reset to auto
  • Settings: crossfader curve (smooth/sharp), pitch range (±8/16/50%), privacy/docs/changelog links in credits

Fixed

  • Beat offset snapped to first kick — open-bpm returned modular phase [0, beat_period); now walks grid to find first onset with RMS > -40dBFS
  • Sync rate clamp removed — was ±8%, now unlimited (full BPM range like Traktor)
  • Pitch fader locks when synced — prevents accidental rate override; shows "SYNC" label
  • Panic requires double-press — first press = dim flash (armed), second within 500ms = execute

UI Review (Industrial Design Overhaul)

  • Waveform: deck-tinted palette (cyan A / orange B), mono overview silhouette, white laser playhead
  • VU meters: segments 6→10px wide, columns 140→160px tall, glow 50% brighter
  • Kill buttons: deeper inset shadow (recessed hardware look)
  • Fader groove: near-black (#080808) with 3px/6px shadows (deep metal cut)
  • Pitch/FX modules: hardware chassis (rgba(20,20,22,0.7) + border + inset shadow)
  • Nudge buttons: 34×24→38×28px with thicker arrows
  • Track title: 12→14px, brighter (zinc-200)
  • Key badges: always white text + colored glow (WCAG pass on all Camelot colors)
  • FX/QT headers: gray 2px underline aligned with pad mode tabs
  • Status bar: aggressive gradient (transparent edges → black center), no static text

Privacy

  • Complete disclosure: all 3 watermark tiers, all 11 localStorage keys, update check clarified

[0.3.0] - 2026-04-08

Breaking

  • BPM detection engine replaced — mixi-core's single-estimator BPM detector (846 lines, IOI histogram) replaced by open-bpm as Cargo dependency. 7-estimator architecture with SuperFlux onset detection and metrical fusion. GiantSteps Acc1: 55% → 68.8%. Same wasm-bindgen API — zero TypeScript changes required.

Added

  • open-bpm integration: IOI + Comb + Autocorrelation + Spectral FFT + Hopf Oscillator + Tempogram + Low-band AC estimators
  • Segmented consensus analysis (3 chunks at 15%, 40%, 70% of track)
  • Metrical fusion for octave resolution (2×, ÷2, 4/3, 3/4) + EDM zone heuristics
  • SuperFlux onset detection with 3-band weighted merge

Changed

  • bpm.rs: 846 → 165 lines (thin wrapper around open-bpm)
  • No browser OfflineAudioContext needed for BPM (pure Rust)
  • Detection speed: ~300ms pure Rust (was ~500ms OfflineAudioContext + ~6ms Rust)

Performance

  • BPM accuracy: 68.8% Acc1 on GiantSteps 664 tracks (+25% relative)
  • Real tracks: 86.7% accuracy on 45 multi-genre tracks
  • vs librosa: 8.5:1 head-to-head (265:31 on 296 tracks)

[0.2.15] - 2026-04-08

Added

  • First-run onboarding — 4-step guided tutorial (Welcome, Mixer, Performance, Shortcuts). Shows on first launch, never again.
  • Notification HUD — dynamic-width toast area in topbar with global notify.info/success/warn/error() API. Auto-dismiss 3s.
  • Help button (?) — links to documentation, in right control group
  • Audio latency badge — shows baseLatency + outputLatency in ms after CPU badge, amber when > 20ms
  • Undo system — global undo stack (20 entries), Ctrl/Cmd+Z shortcut, hot cue deletion undoable with notification feedback
  • Privacy policy (PRIVACY.md) — zero data collection, all local storage documented, GDPR-compatible

Changed

  • Topbar reorganized — CPU moved to HudLeft, Q+M moved to HudRight before REC, HudCenter simplified to MasterClock only
  • Deck info mirrored — Deck B layout reversed (phase|BPM|B|dot) for symmetric visual alignment
  • OUT indicator — green dot only (no text label), 1px black border, centered on VU meter in status bar
  • VU meter bars — CSS mask fade from black over first 20px from center, 15px side padding
  • Status bar VU — center section narrowed (70%, max 200px)

Fixed

  • WCAG accessibility: role="slider", aria-valuemin/max/now, aria-label, tabIndex on ALL Knob and Fader components app-wide

[0.2.14] - 2026-04-07

Added

  • FX1/FX2 unit system — Traktor-style effect units with selectable effect (10 types), dry/wet knob, and ON/OFF toggle. Replaces 7-slot FxStrip. All 10 effects now accessible.
  • FX1/FX2 toggle buttons in performance pads left column, symmetric with Q/resolution on right
  • PhaseMeter dual-disc convergence — discs grow as they approach center, transition cyan/orange → green → white at sync
  • Rust pitch shift processor — granular overlap-add pitch shifter ported to Rust/Wasm AudioWorklet (was last JS on audio thread). JS fallback maintained.
  • Rust PLL analysis module — onset cross-correlation, phase cancellation detection, variable beatgrid, linear regression, predictive phase, auto-cue point — all ported to Rust
  • 100% Rust/Wasm coverage — zero JS hot paths remaining on audio thread

Changed

  • PhaseMeter track extended from 200px to 240px for more visual resolution
  • FX panel compact layout: selector + knob only (ON/OFF moved to pad buttons)

Fixed

  • TAPE effect was a stub — now implements LP frequency sweep (20kHz→200Hz) with resonance
  • Groovebox FX pads were visual-only — now wired to DeckChannel.setFx() via engine
  • Master VU was mono — L/R now read from separate analyserL/analyserR (true stereo)
  • MIDI DECK_PITCH hardcoded ±8% — now reads from settingsStore.pitchRange (configurable)
  • README FX count clarified: 7 in strip + 3 system = 10 total

Documentation

  • Phase 1: Getting Started (NEW, 230 lines) + Architecture (33→280 lines)
  • Phase 2-3: Mixer/EQ/Effects (15→350 lines) + TurboBass reference (NEW, 350 lines)
  • Phase 4: AI AutoMixer 18 intents + MIDI + Skins + Groovebox + Wasm DSP
  • Landing page stats fixed (343 tests, 20 skins)
  • Architecture values corrected from second source audit

Performance

  • Pitch shift: JS AudioWorklet (344×/sec) → Rust/Wasm AudioWorklet
  • Rust tests: 217 passed across 3 crates

[0.2.13] - 2026-04-07

Added

  • Triple HUD topbar architecture — Header refactored into 3 independent components (HudLeft, HudCenter, HudRight) aligned via CSS subgrid
  • HudStatusBar — Full-width status bar below topbar with real-time feedback ticker, symmetric master VU meter (L/R from center), global event bus (HudStatus.show())
  • Per-deck HUD telemetry — HudDeckInfo panels flanking center HUD showing live BPM, play state, sync status per deck

Changed

  • Topbar height increased to 48px (h-12) for better control spacing
  • All HUD panels unified with dark visualizer background (rgba(0,0,0,0.5), border, inset shadow)
  • Topbar background darkened (rgba(0,0,0,0.6))
  • Status bar font: sans-serif, uppercase, wide tracking
  • MiniMasterVu removed from HudCenter (replaced by symmetric meter in status bar)
  • MiniVu removed from MasterHud (deduplicated)
  • Grid layout: auto auto 1fr (topbar + status bar + main)

[0.2.12] - 2026-04-07

Added

  • TurboBass v3 — Complete acid synth rewrite with circuit-level DSP modeling
    • 4-pole diode ladder filter via AudioWorklet (mismatched first pole, per-sample tanh saturation, 2× oversampling, TPT zero-delay feedback)
    • All parameters mathematically derived: VT from geometric mean of pole signal levels, k_max from Barkhausen criterion, resonance compensation √(1+k), quadratic resonance curve
    • Bipolar filter envelope (spike → undershoot → recovery), octave-based env mod sweep
    • Variable duty-cycle square wave derived from sawtooth (71% low → 45% high)
    • Pre-filter highpass (44Hz), filter tracking (Devil Fish mod, logarithmic)
    • TIE (legato) sequencer support — gate stays open without envelope re-trigger
    • Gate length knob (10%–100%), slide time knob (5ms–300ms exponential)
    • Copy/paste pattern (CPY/PST buttons)
    • Two-row knob layout with logical grouping (SYNTH, CONTROL, TONE, EFFECTS)
    • BiquadFilter fallback when AudioWorklet unavailable
  • Waveform viewer tier 3 — cue/loop drag, context menu, zoom-on-mouse, colorblind mode
  • Auto-update check via GitHub Releases + 25 unit tests
  • Dedicated mobile UI with code-split entry point
  • Mobile custom deck infrastructure + TurboKick mobile deck + 42 tests
  • Waveform interaction pure function extraction + 37 unit tests
  • Track loading crumble text + deck quantize polish

Changed

  • Topbar: master VU meter, BPM sync leader, global quantize, CPU%, limiter RAF
  • Topbar: centered HUD grid, MIDI toggle, REC group, recording format selector
  • Splash screen: faster intro, CSS-only fade, enhanced parallax + ring
  • All VU/limiter RAF loops throttled to 30fps

Fixed

  • All ESLint errors resolved, React 19 compliance hardened
  • TurboKick BPM detection, analysis CPU spike, sync silent failure
  • 5 latency shortcuts eliminated across audio/sync/render pipeline
  • Topbar height consistency, divider cleanup, AI toggle collapse
  • YouTube references removed from URL placeholder text

Performance

  • VU/limiter animation loops throttled from 60fps to 30fps
  • 5 latency shortcuts eliminated in audio pipeline
  • Diode ladder worklet: coefficient caching, k-rate optimization

[0.2.11] - 2026-04-06

Added

  • Traktor-style visual improvements and FX strip cleanup
  • Phase meter UI with tabbed HUD and unified HP/MEQ strip
  • Nudge (pitch bend) and trim FX strip (remove tape, noise)
  • Smart auto-cue (grid-snapped) and shift grid ±1 beat
  • HP/MEQ strip redesign with label bar and rectangular buttons
  • Continuous PLL phase correction with anti-windup
  • Groove offset, drift compensation, audio clock reconciliation
  • Onset correlation, phase cancellation defense, phrase lock
  • Variable beatgrid, beatgrid editing, drop align, cross-sync
  • Harmonic sync, predictive phase, differential phase overlay (Sprint 5)
  • Eject safety, deck identity, contrast fixes, pop animation (T1 UI polish)
  • MPC module cards, vinyl drop zone, paste URL, safe mode (T2 UI polish)
  • Soft-center pitch fader mode with resolution tick marks
  • Waveform viewer tier 1 — min-max decimation, willReadFrequently, colour palette, zoom sync, scanner bars, BPM confidence
  • Waveform viewer tier 2 — drag-to-scrub, overview viewport drag, BPM ×2/÷2 edit, energy shadow, centre line, grid flash, Shift+Click beatgrid edit

Fixed

  • 10 critical audit fixes: ctx.resume catch, PLL isFinite guard, exitLoop on eject, settings migration, hotCue validation, engine init try-catch, path traversal on save-as/recover, URL validation + timeout, MIDI disconnect cleanup
  • 9 pre-release hardening fixes: disk-rec:discard path traversal, UDP socket error handler, process crash guards, worklet port.onmessage leak (×2), BpmDetector div-by-zero, PLL null guard, drag listener cleanup, nudge state cleanup
  • Phase meter shake bug — animation on slave box only, CSS keyframe preserves translateY
  • Eliminate getState()-per-frame in 4 components (PremiumJogWheel, VfxCanvas, PhaseMeter, VuMeter)
  • DOM write guards in TrackInfo and MasterClock safety counter
  • 5 CPU/GPU bottleneck eliminations from recent commits

Changed

  • 70 new beatmatching tests across 6 modules (239 total)
  • Beatmatching architecture doc (BEATMATCHING.md)
  • MIXI brand removed from mixer, expanded HUD and phase meter

[0.2.10] - 2026-04-05

Added

BPM Detection v3 — complete rewrite of Rust/Wasm engine

  • Multi-band onset detection (low/mid/high IIR split, weighted merge)
  • Comb filter resonator bank (280 combs, float accumulator, lerp interpolation)
  • PLL sinusoid grid offset (phase-based sweep, sub-millisecond alignment)
  • Genre pattern heuristics (Dubstep halving, D&B doubling, DJ-range bonus)
  • Smart chunking (3x15s at 0%/30%/70%, octave-normalized consensus)
  • Two-speed API: detect_bpm_fast (<50ms) + detect_bpm (full)
  • 58 Rust tests (was 7)

4 New Deck Effects (10 total)

  • Bitcrusher (staircase waveshaper, 3-16 step resolution)
  • Echo (dub delay with LP feedback, BPM-synced)
  • Tape Stop (tonal darkening simulation)
  • Noise (white noise sweep with resonant filter)

Playlists and Library

  • Playlist/crate management with rename, reorder, delete
  • Smart playlists (auto-filter by BPM range, key, rating, color tag)
  • Track rating (0-5 stars) and color tags
  • Session save/load (full mixer state snapshots)
  • Batch BPM/key analysis for entire library

Fixed

  • Tape FX dead signal path (no audio input connected to tapeWet)
  • 4 new FX corrupted Gate param bus offsets (aliased to same memory)
  • Track hydration race condition (async overwrite lost newly added tracks)
  • Dry/wet gain compensation (was dry+wet summed, now unity-gain)
  • Flanger feedback capped at 0.6 (was 0.8, resonant peaks above 0dB)
  • Noise filter Q capped at 4 (was 9, ~19dB resonant peak)
  • Echo feedback capped at 0.7 (was 0.85, low-freq buildup)
  • addTrack blob save failure now removes ghost entry from UI
  • loadSession now restores crossfaderCurve (was silently discarded)
  • BatchAnalyzer OfflineAudioContext reduced to mono 1-sample
  • ColHead FC extracted outside render (was remounting DOM every frame)
  • Multi-file drop now yields between CPU-bound analysis calls
  • All 6 stores use safeStorage (catches QuotaExceededError)
  • BPM: octave-normalized chunk consensus, branchless comb/PLL loops, f64 sliding window accumulators, compute_energy actual-size division, PLL integer phase iterator, snap threshold 0.15, fast parabola, lerp interpolation, 2-decimal precision, anti-denormal IIR filters

Changed

  • Deck Effects count: 6 to 10
  • BPM detection: single-band IOI to multi-band + comb filter + PLL
  • Rust test count: 152 to 203
  • README updated with new FX count, BPM engine description, test totals

[0.2.9] - 2026-04-05

Added — TurboBass v2 + Performance Features + MIDI Presets

TurboBass v2 — 5 Iterations

Audio Engine (Iter 1)

  • Sub-oscillator (sine, -1 octave) with mixable SUB level
  • Analog drift LFO (~0.1Hz, ±5 cents) for organic fatness
  • Pre-filter drive (tanh saturation) — the "squelch"
  • Accent click (1ms noise burst, -22dB) for percussive transients
  • Exponential slide with variable glide time (faster on accents)
  • Extended accent decay (+50%) with momentary resonance Q boost

FX Chain (Iter 2)

  • Rat-style asymmetric distortion (germanium diode modeling)
  • BPM-synced delay with HP filter (200Hz) in feedback loop
  • Ducking spring reverb (generated metallic IR, ducks on note-on)
  • Chorus (dual modulated delay lines), auto-pan (LFO → StereoPanner)
  • Filter LFO with BPM-synced subdivisions (1/4 → 1/32)
  • Internal brick-wall limiter (ratio 20:1, 1ms attack)

Usability (Iter 3)

  • Smart randomizer (6 scales: minor, phrygian, pentatonic, blues, dorian, chromatic)
  • Pattern mutate (progressive alteration preserving DNA)
  • ACID macro knob (single gesture: cutoff + envMod + resonance + decay)
  • Pattern shift ◀▶, polyrhythmic length (1-32 steps)
  • Transpose ±24 semitones, crossfader-linked cutoff

UI (Iter 4)

  • Industrial chassis (dark metallic gradient + brushed texture)
  • VFD display (cyan-green glow: bank/pattern/name/BPM/transpose)
  • LED glow sequencer with color-coded step indicators
  • Roland-style horizontal param rows (ACC/SLD/UP/DN per step)
  • Filter visualizer (real-time Canvas LP response curve)
  • Per-step note editor (right-click popup), 16/32-step toggle

Presets (Iter 5)

  • 32 factory patterns: 4 banks × 8 (Acid House, Techno, Minimal, Experimental)
  • Pattern bank selector (A/B/C/D + 1-8)
  • Ghost sequence (auto-generates pattern after 5min idle)
  • PANIC button (reset all synth+FX to safe defaults)

Performance Features

  • Slip mode, beat jump (1-32 beats), 3 EQ models (LR4 Isolator, DJ Peak, Xone Kill)
  • Master 3-band EQ, click-to-seek on waveform
  • 37 MIDI controller presets (Pioneer, Numark, Denon, Hercules, NI, Reloop, Roland, Akai, A&H, Behringer, Traktor)

Changed

  • TurboBass README description updated with full v2 feature set
  • js303 by thedjinn credited in Acknowledgements

0.2.8 - 2026-04-05

Added — House Decks + MIXI Sync Protocol + MIDI Clock

Pluggable Deck System

  • src/decks/ registry with lazy-loaded components and DeckMode union type
  • One-line registration: add mode to DeckMode, push entry to HOUSE_DECKS

TurboKick Deck — Real-time kick drum synthesizer + 16-step sequencer

  • Pitch/decay/click/drive synthesis, THUMP macro, dual valve distortion (tube + punch)
  • Filter + LFO, Berghain-style RUMBLE (dark reverb + rhythmic delay + sidechain pump)
  • BPM sync, quantized engage, speaker touchpad with shockwave animations

TurboBass Deck — TB-303 acid synth (pure WebAudio, no external Wasm dependency)

  • Persistent oscillator (saw/square) + resonant LP filter (Q up to 26)
  • Filter envelope with accent boost + env mod depth, slide (60ms portamento)
  • Per-step note/gate/accent/slide/octave, 16-step acid sequencer
  • tanh waveshaper distortion, BPM-synced dotted 8th delay

MIDI Clock Out + In (24 ppqn)

  • startClock() sends 0xFA Start + 24 ppqn 0xF8 ticks to all MIDI outputs
  • Dynamic BPM tracking from active deck
  • Clock input: calculates external BPM from received ticks
  • Two MIXI instances can now sync via standard MIDI Clock

MIXI Sync Protocol v1 (src/sync/)

  • 64-byte binary packet codec (encode/decode, fixed-point u32 phase)
  • PID phase lock controller with gain scheduling, hysteresis, phase unwrapping
  • UDP transport on port 4303 (Electron IPC + dgram)
  • BroadcastChannel fallback for browser-only sync
  • Predictive onset triggers (kick/snare/hihat countdown for VJ software)
  • Dynamic Dictatorship master election with epoch-generation split-brain resolution
  • Flywheel mode, dead reckoning, graceful degradation (phase-lock → tempo-match)
  • 5 final amendments: drain-to-newest, feed-forward pitch nudge, epoch poisoning guard, master claim cooldown

Documentation

  • CREATE_DECKS_AI.md: complete AI guide for building deck plugins (506 lines)
  • MIXI_PROTOCOL.md: scientific spec with 55 engineering directives + PDF export

Tests

  • 51 new sync tests (protocol codec + PID controller)
  • 2 new E2E sync tests (BroadcastChannel two-tab exchange)
  • Total: 169 JS unit + 7 E2E + 152 Rust = 328 tests

New files: src/decks/turbokick/ (TurboKickDeck.tsx, TurboKickEngine.ts, TurboKickBus.ts, kickSynth.ts, types.ts)

0.2.6 - 2026-04-04

Added — Mixer Hardening + Audit Fixes + Test Gate

Wasm DSP Engine — Now Actually Processing Audio

  • AudioWorklet calls Rust DspEngine.processRaw() via direct Wasm memory access
  • 2-input worklet (Deck A + Deck B), full signal chain in Rust
  • ParamLayout offset mismatch fixed (all FX byte offsets aligned TS ↔ Rust)
  • SharedParamBus with layout versioning (PARAM_LAYOUT_VERSION=2 at offset 508)
  • Wasm module fetched, compiled, and sent to worklet thread

Linkwitz-Riley 24dB/oct EQ (WebAudio path)

  • Parallel 3-band isolator with cascaded Butterworth = LR4
  • Flat magnitude sum at crossover, zero phase difference
  • Kill = gain 0 on one band only, others 100% unaffected

Audit Fixes (13/18 from ISSUES.md)

  • C1: Stereo mono bug — separate master_l/master_r instances
  • C2: Master double-processing — independent state per channel
  • C3+C4: Wasm URL + worklet paths — import.meta.url resolution
  • H1: processRaw bounds validation (offset + size <= memory size)
  • H2: ParamBus version check (Rust validates on every process() call)
  • H4: Wasm panic message extraction via TextDecoder
  • H5+M4+M7: Export name guessing → explicit error reporting
  • M1: NaN sort panic → unwrap_or(Equal) in drop_detect
  • M6: Waveshaper div-by-zero → .max(1e-10) guard

Headroom Hardening

  • Punch compressor gain compensation: 1/(1+wet) prevents +3.5dB overshoot

Comprehensive Test Gate (275 tests)

  • 118 JS unit tests (Vitest): stores, math, WAV header, ParamLayout, crossfader, GPU detect
  • 152 Rust tests: DSP modules (127 unit + 24 integration + 1 bench)
  • 5 Playwright E2E: app launch, layout, VFX toggle, REC button, console errors
  • CI pipeline: .github/workflows/test.yml — test-js + test-rust on every push

Stats

  • 275 total tests (118 JS + 152 Rust + 5 E2E)
  • Rust DSP benchmark: 10.76µs/block, 99.6% headroom
  • Build: 2.08s (Vite), 6.9s (wasm-pack)

0.2.5 - 2026-04-04

Fixed

  • Parallel 3-Band Isolator EQ: rewritten from series shelving to parallel crossover
  • Gate FX: rewritten as simple phase-based volume chop (was broken scheduling)
  • WebGPU VFX: fixed swap chain CopyDst error, render direct to swap chain
  • VFX tuning: thinner spectrum border, sensitivity boost, removed polar circle, softer CRT/vignette

0.2.4 - 2026-04-04

Added — VFX Tier 2 (7 VJ secrets, 16/30 total)

  • Ring texture spectrogram (128×64 r32float, 64-frame audio history)
  • Feedback loops (ping-pong render targets, zoom + hihat rotation)
  • CRT phosphor emulation (barrel distortion + RGB subpixel mask + curved scanlines)
  • Semantic color binding (CSS --clr-a/--clr-b → GPU uniforms)
  • Filter washout (HPF → white/contrast, LPF → dark/desaturate)
  • Beatgrid Tron floor (BPM-synced perspective grid, deck-colored)
  • Peak-hold downsampling (0.95 decay per frame)
  • 14-effect shader chain, spectrum border contour around page

0.2.3 - 2026-04-04

Added — WebGPU VFX Engine (Pillar 5 Step 1)

  • WebGPU audio-reactive visual engine with Canvas 2D fallback
  • 10 composited WGSL shader effects (single draw call per frame)
  • VJ secrets: FFT as GPU texture, isolated stems, BPM phase sync, energy derivative
  • Polar spectrum, chromatic aberration, dynamic film grain, Rule of Black
  • ESC kill-switch (instant GPU teardown)
  • float32-filterable feature negotiation (linear/nearest sampler adaptive)

0.2.2 - 2026-04-04

Added — Crash-Proof WAV Recording (Pillar 3)

  • Disk-backed WAV recording via SPSC ring buffer → IPC → fs.writeSync
  • Fixed ~1MB RAM regardless of recording length (6+ hour sets safe)
  • 32-bit float IEEE WAV format, orphan detection and recovery on crash
  • 9 Electron IPC handlers (open/flush/finalize/cancel/save-as/recover/discard)
  • Dual-mode RecPanel: Electron gets disk WAV, web keeps MediaRecorder → WebM

0.2.1 - 2026-04-04

Added — Native Audio I/O (Pillar 1, Phase D Complete)

  • mixi-native Rust crate: cpal 0.15 for direct hardware audio output
  • N-API addon loader with platform detection (darwin-arm64/x64, win32, linux)
  • Zero-copy SPSC ring buffer consumer from SharedArrayBuffer
  • Device enumeration, lock-free audio thread, 364KB ARM64 dylib
  • Settings UI: native output toggle + device dropdown
  • MixiEngine: switchToNativeOutput() / switchToWebOutput()

0.2.0 - 2026-04-04

Added — DSP Engine Hardening + Electron God Mode

  • Predictive limiter (0.2ms lookahead, poly-knee)
  • Electron audio optimization flags (128-sample buffer, GPU rasterization, SIMD)
  • Mobile landscape compact mode
  • Drop detection in Rust/Wasm

0.1.4 - 2026-04-04

Added -- Phase 3: Rust DSP Engine Foundation

DSP Abstraction Layer (Layer 0)

  • DspProcessor / DspChain / DspParamBus interfaces for backend-agnostic audio processing
  • Parameter Bus Layout: 512-byte shared memory map (40+ parameter offsets)
  • LocalParamBus (native mode) and SharedParamBus (Wasm mode with Atomics)
  • NativeDeckProcessor / NativeMasterProcessor: WebAudio node wrappers
  • AudioWorklet shell (passthrough, public/worklets/mixi-dsp-worklet.js)
  • Feature flag: useWasmDsp in Settings (default: off, persisted)

Lock-Free Ring Buffer (Layer 2)

  • SPSC ring buffer in Rust (ring_buffer.rs) with Acquire/Release atomic ordering
  • SharedArrayBuffer bridge: audio ring (93ms), param bus (512B), metering bus (24B)
  • Feature detection and MessagePort worklet communication

8 DSP Primitives in Rust (Layer 3)

  • Biquad filter: lowpass, highpass, lowshelf, highshelf, peaking (Audio EQ Cookbook)
  • ThreeBandEq: 250 Hz / 1 kHz / 4 kHz matching DeckChannel EQ
  • Gain: simple multiply + click-free ramp
  • Limiter: brick-wall peak limiter with instant attack
  • Compressor: parallel compression with auto-makeup gain
  • Delay: variable delay line with linear interpolation and feedback
  • Reverb: Schroeder algorithm (4 comb + 2 allpass, rate-scaled)
  • Flanger: LFO-modulated short delay with feedback
  • Phaser: 4-stage allpass chain with LFO frequency sweep
  • Gate: beat-locked amplitude gate with smoothed envelope
  • Waveshaper: tanh soft-clip distortion with quadratic drive scaling

Changed

  • mixi-core crate: 3,987 lines of Rust across 17 source files
  • .wasm binary: 120 KB (unchanged from v0.1.3 -- DSP not yet FFI-exported)

Stats

  • 176 total tests (103 Rust + 73 JavaScript)
  • Zero behavioral change (DSP modules ready but not wired to audio graph)
  • Full JS fallback preserved on all paths

0.1.3 - 2026-04-04

Added — 🦀 Rust/Wasm Integration (Phase 2 Complete)

  • mixi-core Crate: New mixi-core/ Rust crate compiled to WebAssembly (87 KB binary)

    • wasm-pack build pipeline with --target web, opt-level = "z", LTO
    • Vite integration via vite-plugin-wasm + vite-plugin-top-level-await
    • Cross-Origin Isolation (COOP/COEP headers) for SharedArrayBuffer support
    • Lazy-loading wasmBridge.ts singleton with TypeScript types
  • Waveform Analysis in Rust (waveform.rs)

    • compute_rms(), compute_rms_multichannel(): windowed RMS computation
    • normalise(): in-place peak normalization
    • peak_level(): absolute peak scan across all channels
    • build_waveform(): flat interleaved [low,mid,high,...] array builder
    • Automatic JS fallback when Wasm not loaded
  • BPM Detection in Rust (bpm.rs)

    • Adaptive spectral-flux onset detection
    • Multi-hop IOI histogram with Gaussian smoothing
    • Octave resolution with DJ range preference (100–185 BPM)
    • Grid alignment refinement (±2.5 BPM sweep at 0.1 steps)
    • Integer BPM snap with alignment validation
    • Beatgrid offset detection
  • Key Detection in Rust (key.rs)

    • Goertzel algorithm for pitch-class energy (48 bins, 4 octaves)
    • Krumhansl-Kessler key profiles (major + minor)
    • Pearson correlation for all 24 keys
    • Camelot wheel notation output
    • is_harmonic_match() for DJ compatibility check
  • AutoMix Computation in Rust (automix.rs)

    • Beat math: time_to_beat(), beat_to_time(), snap_to_phrase(), calc_mix_out_beat()
    • Phase alignment: compute_phase_alignment() with signed delta + alignment flag
    • Full blackboard computation: 18 raw inputs → 20 derived metrics per tick
    • Intent scoring helpers: urgency_curve(), score_bass_swap(), score_dead_air(), score_phase_correction()
    • 20 Hz AI tick now runs beat math in Rust
  • SystemHud WASM Indicator: Green dot status when Rust/Wasm module is loaded and active

  • Test Coverage: 112 total tests (39 Rust + 73 JS), all passing

Changed

  • WaveformAnalyzer.ts: Hot loops (RMS, normalization, peak) now use Rust fast path
  • BpmDetector.ts: Full BPM pipeline runs in Rust when available (log prefix [Rust])
  • KeyDetector.ts: Goertzel + key matching runs in Rust when available (log prefix [Rust])
  • Blackboard.ts: AI blackboard computation uses Rust compute_blackboard() at 20 Hz
  • Build Pipeline: npm run build:wasm integrated into main build script

Performance

  • .wasm binary: 87 KB (release, opt-level=z, LTO, stripped)
  • BPM detection: JS hot loops replaced by Rust/Wasm
  • Key detection: Goertzel on millions of samples now runs in Rust
  • AutoMix tick: beat math + phase alignment in Rust (20 Hz × 20 derived values)
  • All modules: automatic JS fallback if Wasm not loaded

0.1.1 - 2026-04-04

Added

  • MIDI Presets: Akai MIDI Mix factory CC preset (src/midi/presets/akaiMidiMix.ts)
  • MIDI Actions: MASTER_VOL, HEADPHONE_MIX, HEADPHONE_LEVEL added to MidiManager
  • MIDI Settings Tab: Full interactive parameter-by-parameter Learn UI in Settings
    • 24 mappable parameters organized by section (Deck A, Deck B, Master)
    • Per-parameter Learn button with visual feedback ("⏳ Move control…")
    • Per-parameter delete (✕) and global Clear All
    • Preset dropdown (Manual / Akai MIDI Mix)
  • MIDI Store: loadPreset(), exportMappings(), activePreset tracking
  • Master FX Store: Filter, Distortion, Punch migrated from React local state to Zustand store
    • 3 new store actions: setMasterFilter, setMasterDistortion, setMasterPunch
    • Full sync pipeline: store → useMixiSync → engine (bidirectional)
    • Bridge whitelist + state snapshot includes master FX (AI/MCP agent can control remotely)
    • AI ghost proxy marks filter/dist/punch for glow indicators
    • Panic handler resets master FX via store (single source of truth)
  • MixiEngine API: Public setLimiterEnabled() method replaces unsafe internal cast
  • Testing Suite: 73 unit tests across 5 test files (+121% from 33)
    • mixiStore.test.ts — 27 assertions (+11: master FX, clamping, modes, AI, crossfader curve)
    • mathUtils.test.ts — 21 assertions (NEW: dbToGain, crossfaderGains smooth/sharp, logFrequency, clamp)
    • logger.test.ts — 8 assertions (NEW: all log levels, console routing, DEV-only debug, extra data)
    • settingsStore.test.ts — 8 assertions
    • midiStore.test.ts — 9 assertions
  • Coverage: audio/utils 12.5% → 75%, utils/logger 38% → 100%
  • Dynamic Version: __APP_VERSION__ injected from package.json via Vite define
  • Type Declaration: src/vite-env.d.ts for Vite client types

Changed

  • MasterBus Distortion: Dynamic oversample toggle ('4x' when active, 'none' when bypassed) — dramatically reduces CPU when distortion is off
  • MasterBus Buffer: Simplified Float32Array(samples) allocation (no intermediate ArrayBuffer)
  • SystemHud CPU Meter: True O(1) ring buffer (Float64Array) replaces Array.shift() per-frame overhead
  • MasterLedScreen Lissajous: Pre-computed 256-entry RGBA alpha LUT eliminates ~61,440 string allocs/sec in inner loop
  • PremiumJogWheel Iris: Pre-computed HEX_LUT[256] replaces toString(16).padStart() in render loop
  • SampleManager: Fixed inverted logger arguments (message, tag)(tag, message)
  • Logging Policy: Zero console.* calls remain in entire src/ directory — all migrated to structured log.*
  • Empty Deck Overlay: Reduced from opaque black/blur to subtle frosted-glass
  • Demo Track Loading: Now loads every session (not one-shot)
  • WS Bridge Logging: Only first connection attempt logged; subsequent reconnects silent until success
  • Settings Persistence: Added explicit merge function to strip stale loadDemoTrack from localStorage
  • README: Tech stack corrected; test scripts added
  • DECKS.md: Fixed API mismatches
  • Audio Decode Error: Now includes browser's native error message and lists supported formats

Fixed

  • Deck B Crash: Removed forced setDeckTrackLoaded('B', true) at startup
  • Play Buttons Broken: loadDemoTrack localStorage fix
  • AudioContext Double-Close: Guard ctx.state !== 'closed'
  • Version String: SettingsModal reads from package.json
  • ArrayBuffer Consumed: decodeAudioData receives arrayBuffer.slice(0)
  • PitchStrip Nudge Timer: setTimeout now stored in ref and cleared on unmount (prevents stale timer leak)
  • MasterHud LimiterDot: Uses public MixiEngine API instead of unsafe internal cast

Removed

  • Tracked Build Artifacts: tsconfig.tsbuildinfo removed from git tracking
  • Stale .gitignore: Fixed malformed entries; added api/dist/, api/build/, *.spec, coverage/

0.1.0 - 2026-04-03

Added

  • Initial public release of Mixi DAW
  • Dual-deck DJ engine with Web Audio API (hand-wired graph, no wrappers)
  • 3-band EQ (Low Shelf / Mid Peak / High Shelf) with kill switches
  • Bipolar Color FX filter (LPF/HPF sweep)
  • 6 send effects per deck: Filter, Delay, Reverb, Phaser, Flanger, Gate
  • BPM detection with beat grid and phase-aware sync
  • Key detection (Chromagram → pitch class → Camelot notation)
  • Drop detection for mix-point suggestions
  • 8 hot cue slots per deck with quantize snap
  • Auto-loop engine (1/32 to 32 beats)
  • Performance pads with multiple modes
  • Step sequencer groovebox with 8-voice drum synth
  • AI Automixer with 18 intent modules across 5 categories
  • MIDI Learn system with WebMIDI API
  • Headphone cueing (PFL) with split mode
  • Recording engine (real-time WAV export)
  • 17 built-in skins with CSS custom property theming
  • Crossfader with smooth/sharp curve selection
  • Electron desktop app packaging (macOS, Windows, Linux)
  • Python backend with FastAPI for MCP bridge
  • VitePress documentation site with 5 guide pages
  • Mobile scale wrapper for responsive desktop-first layout