Skip to content

Tensor inference, opt-in vocal separation, GPU runtime, and cross-platform CI - #9

Merged
soficis merged 72 commits into
masterfrom
feat/tensor-inference
Oct 5, 2026
Merged

soficis merged 72 commits into
masterfrom
feat/tensor-inference

Conversation

@soficis

@soficis soficis commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds on-device tensor inference and opt-in AI vocal separation, an optional per-user GPU runtime, pinned PhaseLimiter acquisition, and cross-platform CI and release packaging. 60 commits; 98 files.

AI / tensor inference

  • ONNX tensor inference with opt-in BS-RoFormer vocal separation, plus a per-session Vocal Model toggle. Off by default.
  • GPU acceleration through ONNX Runtime providers (CUDA/TensorRT, CoreML/ANE, WebGPU logic) with per-model allow-lists. CPU is always the fallback.
  • On-demand per-user CUDA runtime pack with a driver gate. fp32 GPU separation is gated on device memory; otherwise the quantized CPU build installs.
  • Per-user model hub with a one-time migration from the old location.
  • Low-memory Macs: on Macs under 12 GiB, automatic provider selection skips CoreML/ANE (opt in with AUTOMIX_ENABLE_COREML=1 or by naming the provider). The app warns before the vocal model runs on the CPU there.
  • Open-Unmix (MIT, 36 MB) curated as a light vocal model: 60 s of audio separates in 0.9 s on an 8 GiB MacBook Neo, where BS-RoFormer took over 5 minutes per chunk. It is pinned to revision e06097d and its sha256, so a later push to the HF repo cannot change what installs.

Renderers

  • PhaseLimiter is opt-in: it runs only when selected, matching the owner decision of 2026-10-01 recorded in CLAUDE.md. The default chain is unchanged and applyPlan() stage order is untouched.
  • PhaseLimiter binaries are pinned per platform (v0.2.0-native3) with strict SHA-256 checks and a single source of truth for the pins. A missing ffmpeg is a render error, not a reason to report PhaseLimiter unavailable.

Tools

  • automix_dev_tools model bench: measures the requested provider and exits 4 on a provider mismatch, exits 5 when any inference call fails (instead of timing failures), adds --skip-cpu-baseline, and prints progress per run.

Build / CI / release

  • Pinned ONNX Runtime fetch, cross-platform staging/rpath, and native ARM64 Linux runners (QEMU removed). The deps cache is keyed per architecture.
  • Curl enabled on Linux so HTTPS model downloads work; CI smoke-tests a real download.
  • TSan and golden eval run on master and feature branches. CI forbids masked failures and unpinned actions; a hygiene checker and gitleaks run in CI.
  • Release workflow builds Windows ZIP, macOS, .deb and Flatpak packages; a dry_run input builds and verifies everything without publishing.

Testing

  • Windows: ctest 261/261 pass (two tensor probes skip without the hardware).
  • CI green on the head commit 3b034bf: Native ORT on Windows, macOS 15, Linux x64 and Linux arm64; TSan; golden eval; hygiene and gitleaks.
  • Release dry run passed on 3b034bf (run 37336088500): Windows x64/arm64, macOS x64/arm64, Linux x64/arm64 packages built and verified; publish skipped, no tag or release created.
  • Measured: CUDA ~9x CPU for BS-RoFormer on an RTX 5060 Ti.

Known gaps

  • WebGPU fails every BS-RoFormer inference (ONNX Runtime's generated shaders exceed the device's buffer limits). The bench now reports this as exit 5, and the model's allow-list stays CUDA-only.
  • CoreML/ANE speed on Apple Silicon is unmeasured: on the 8 GiB test Mac the OS killed both during the first inference.
  • Not yet verified: HTTPS downloads from the installed Flatpak and .deb packages.
  • On Windows, a fully parallel build occasionally races while copying the ORT DLLs; building targets one at a time works.

🤖 Generated with Claude Code

soficis and others added 30 commits September 30, 2026 18:59
… roots

defaultRoots() collected candidates in a std::set, so roots were searched
in lexicographic order rather than priority order. AUTOMIX_ASSET_ROOT only
acted as an override when its path happened to sort before the executable's
tree; from a checkout under C:\2AHOLD the repo's own bundled binary won and
"PhaseLimiter discovery supports AUTOMIX_ASSET_ROOT override" failed.

Roots are now an ordered vector (override first), deduplicated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a tensor-graph inference path alongside the scalar IModelInference
one, and wires an MIT-licensed BS-RoFormer ONNX export into single-mix
import as an opt-in separator. Off by default; with the flag off,
separation output is byte-identical (tested).

- TensorTypes / ITensorInference / OnnxTensorInference: float32 tensor
  I/O with graph spec probing; ORT 1.30.0, CPU provider only. Without the
  native SDK every load fails with a diagnostic, never an approximation.
- SpectrogramFrontEnd: torch.stft-compatible STFT/iSTFT (golden fixture).
- SeparationRunner: chunked overlap-add, mask/direct output modes,
  residual stems (instrumental = mix - vocals), all-or-nothing on failure.
- ModelPack tensor_contract (optional, no schema bump) with load-time
  checks that name the tensor and both shapes on mismatch.
- Catalog: xycld/BS-RoFormer-ONNX, pinned to the single-file quantized
  build (the alphabetically-first .onnx is an fp32 stub that needs a
  640 MB sidecar). Install probes the graph and writes real tensor names.
  MIT: attribution in NOTICE and the licensing audit, no consent prompt.
- RenderSettings::tensorSeparationEnabled (default false) ->
  ImportController -> StemSeparator. Any tensor failure falls back to the
  existing separator and says why in the log.
- Latent fixes reached by the ON build: EP header glob for the flat
  release zip, EnableProfiling wchar_t path, corrupt model no longer
  recorded as a failed cpu provider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ModelLicensePolicy::licenseUrl() and consentReason() return
std::string_view, which juce::String cannot be constructed from. Since
549cf1d the GUI target (AutoMixMasterApp) failed to compile with C2440;
the test and tool targets never include ModelBrowserPanel, so no suite
caught it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ControlDeck: "Vocal Model" toggle beside AI Stem Separation, enabled
  only while separation is on. Drives
  RenderSettings::tensorSeparationEnabled, synced on session load/save.
- Session JSON persists tensorSeparationEnabled; sessions saved before
  the key existed load with it off (tested).
- automix_dev_tools separate --mix --out [--pack] [--tensor] [--json]
  runs StemSeparator exactly as single-mix import does.

Verified against the real xycld/BS-RoFormer-ONNX quantized build:
hub install pinned the quantized file and probed names input/output;
a 196 s track separated in 33 chunks, with the vocal stem ~97 dB below a
full-level mix in instrumental sections. CPU-only run took 687 s
(~3.5x real time).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reader thread can load the meter atomics before the writer's first
store, so it sees the initial -60 and the range check reported a torn
read that never happened. Scheduling-dependent: it failed 4/5 runs on the
CUDA build. Now 0/60 across all three build configurations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generic string-keyed AppendExecutionProvider() does not accept CUDA
or TensorRT ("Unknown provider name 'CUDA'"), so every CUDA request threw
and OnnxModelInference silently ran on CPU while recording cuda as failed.
CUDA has never actually been used by the app.

- Provider appending moves to a shared header (OrtSessionProviders.h);
  CUDA and TensorRT use AppendExecutionProvider_*_V2.
- CUDA arena uses kSameAsRequested (needed for per-run shrinkage).
- A session that fails to open because of the model itself (ORT error
  codes NO_SUCHFILE / NO_MODEL / INVALID_PROTOBUF / INVALID_GRAPH /
  MODEL_LOADED) no longer marks the GPU provider as failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cancellation
- RunnerConfig::cancelRequested is checked before every chunk, and
  ITensorInference::runCancellable lets the ORT backend abort an in-flight
  Run via RunOptions::SetTerminate from a watcher thread. A cancelled
  separation writes nothing and never falls back to another separator.
- Import passes ThreadPoolJob cancellation through. Real model: cancel at
  5.0 s returned at 5.7 s, mid-chunk (a CPU chunk takes ~20 s).

GPU
- Tensor sessions try GPU providers in order and fall back to CPU,
  reporting the provider actually used; a GPU run that fails mid-way is
  retried once on CPU. Import honours preferHardwareAcceleration and
  gpuExecutionProvider; a named provider missing from this runtime still
  means "use a GPU".
- Per-run CUDA arena shrinkage: without it BS-RoFormer's second chunk
  overflowed 16 GB of VRAM into shared memory (42 s/chunk vs ~2.5 s).
- Catalog installs the fp32 BS-RoFormer build where a GPU session really
  opens (in-memory probe), else the quantized build. Measured on an
  RTX 5060 Ti, 196 s track: fp32/CUDA 85-91 s, quantized/CPU 687 s,
  quantized/CUDA no faster than CPU, fp32/CPU ~45% slower than quantized.
- ORT 1.30 cannot load the fp32 export (shape inference cannot read
  external initializers), so the installer folds "<model>.data" into the
  model in place with inlineExternalData(), a dependency-free protobuf
  rewrite that refuses locations outside the model directory. No weights
  are re-hosted.
- CMake stages ORT (and optional AUTOMIX_CUDA_RUNTIME_DIR) DLLs beside
  each executable so a System32 onnxruntime.dll can no longer shadow it.
- dev tools: separate --provider and --cancel-after-ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fp32 BS-RoFormer build peaks at 9442 MiB of device memory per chunk
(RTX 5060 Ti, CUDA 13, arena shrinkage). Below that it spills into shared
system memory, which measured slower than running on CPU.

- queryCudaDeviceMemory(): free/total of CUDA device 0 via cudaMemGetInfo
  from the CUDA runtime loaded at run time; nothing links against CUDA.
- Install: fp32 is chosen only when the GPU probe opened a CUDA session
  and the device totals >= 10 GiB + 1.5 GiB headroom (12 GB cards
  qualify, 8 GB cards get the quantized build).
- Run: packs declare gpu_memory_mb (10240 for fp32); when free memory is
  below it, separation runs on CPU and the log says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CUDA execution provider needs NVIDIA's CUDA runtime, cuBLAS, cuFFT
and cuDNN (~1.3 GB unpacked). Rather than ship them with every install,
the app offers them once, to machines that can use them.

- GpuRuntimePack: NVIDIA's redistributable wheels from PyPI, pinned by
  URL + SHA-256 (~1.02 GB download); only DLLs are extracted (entries
  escaping the archive are refused) into
  %LOCALAPPDATA%\AutoMixMaster\gpu-runtime\<version>\bin. A versioned
  marker is written last, so failed, cancelled or older installs are
  never trusted. No admin rights, no system CUDA, no PATH changes.
- preload() loads the DLLs by full path before the GPU probe, tensor
  sessions and the device-memory query; the probe now caches only
  success so a pack installed mid-session takes effect.
- Offer: when Vocal Model is switched on and an NVIDIA adapter with
  >= 11.5 GiB (read via DXGI) is present, the ORT build has CUDA and the
  pack is missing, a consent dialog names NVIDIA's licence; the download
  runs in the background with progress in the task history and stops if
  the window closes. Offered at most once per run.
- automix_dev_tools gpu-runtime status|install.
- NOTICE: GPU runtime section (not shipped or re-hosted; NVIDIA terms).

Verified on an executable with the CUDA ORT build but no CUDA DLLs:
before install no GPU session opens; after `gpu-runtime install` (84 s,
16 libraries) preload succeeds, a CUDA session opens and the fp32 vocal
model separates the 196 s test track on CUDA in 66 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…time

- Offer the GPU runtime only with an NVIDIA driver >= 580 (CUDA 13), read
  from the DXGI user-mode driver version (32.0.16.1074 -> 610.74).
- After the runtime installs, a BS-RoFormer pack still on its quantized
  build is reinstalled as the fp32 GPU build and the old file removed
  (upgradeBsRoformerForGpu; verified 166 MB -> 645 MB, idempotent).
- Settings gains a GPU acceleration row (status + Install/Remove).
  uninstall() drops the marker first and defers files locked by this
  process to completePendingRemoval() at the next start; it refuses
  folders that are not a runtime pack.
- Skip the two libraries ONNX Runtime never loads (cufftw, nvblas). A
  BS-RoFormer run loads only cuBLAS/cuBLASLt and three cuDNN parts, but
  convolution, FFT and RNN graphs need the rest, so nothing else is cut.
- OnnxModelInference now preloads the runtime pack too; without it scalar
  models could never use CUDA on a user's machine.
- The GPU recovery tests load on CPU before pinning providers, so they no
  longer depend on whether CUDA libraries exist on the test machine.
- dev tools: gpu-runtime remove|upgrade-models.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- install() + CPack produce a portable ZIP of the "application"
  component only (the fetched dependencies' headers and libraries stay
  out): the app, its assets, NOTICE/README and the ONNX Runtime DLLs.
  An install-time guard fails if any .onnx file or NVIDIA CUDA library
  lands in the package.
- packaging/windows/build-release.ps1: fresh configure, build, tests,
  cpack. Verified end to end: 224/224 tests, 196 MB ZIP; the extracted
  app starts and loads its own onnxruntime.dll.
- The post-build step copied all of assets/phaselimiter next to the app
  on every GUI build, including 13 GB of renderer scratch audio (tmp/)
  and 352 MB of stray downloaded models; it now stages only bin/,
  resource/, licenses/, LICENSE and README.md.
- README: GPU acceleration and release-packaging sections.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PhaseLimiter never rendered: the renderer passed -mastering=true without
-mastering_reference_file, so phase_limiter.exe read
./mastering_reference.json, failed with "auto mastering error: syntax
error", and every export silently fell back to BuiltIn. Cleanup ran only
on success, so each failure leaked its input WAV and work directory into
installRoot/tmp (733 of each, 13 GB, on this machine).

- Pass mastering_reference.json and sound_quality2_cache by absolute
  path; an install without the reference counts as unavailable.
- Scratch lives in %TEMP%\automix_phaselimiter\<run> behind an RAII
  guard that removes it on every exit path. The process-wide working
  directory change (racy under parallel renders) is gone: absolute
  paths, including non-ASCII ones, work from any cwd.
- First render per process sweeps only the leaked legacy patterns
  (input_*.wav, phase_output_*.wav, work_*) and our own runs >24 h old.

Owner decision: PhaseLimiter is opt-in.
- Default renderer and built-in profiles use BuiltIn; selecting
  PhaseLimiter (single mode, logical_all primary or a custom chain) is
  the opt-in. logical_all is otherwise unchanged and no longer adds
  PhaseLimiter as an automatic stage.
- Session schema 3: sessions saved earlier that stored "PhaseLimiter"
  (the old default) load as BuiltIn - exactly what they always rendered.

Golden files are unaffected (the regression harness renders with
BuiltIn directly).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Downloaded models lived in "assets/modelhub" relative to the working
directory: it moved with the cwd and is not writable under Program
Files.

- defaultModelHubRoot(): %LOCALAPPDATA%\AutoMixMaster\modelhub (user
  app data elsewhere), or AUTOMIX_MODEL_HUB_ROOT for portable installs.
  Both hubs, ModelManager, ModelController, MainLayout and dev tools use
  it; bundled read-only packs ("ModelPacks") are still scanned.
- migrateModelHub() moves the legacy hub once at startup: pack folders
  (copy+delete across volumes), install_registry.json and
  license_consents.json merged by modelId with the target winning,
  registry installPaths rewritten, install log appended, MIGRATED.txt
  left behind. Recorded licence consents therefore survive the move.
- Uninstall refuses a registry installPath outside the hub instead of
  remove_all-ing whatever the file names.
- Tests run against a throwaway hub (TestMain.cpp listener), so suites
  never write to the user's profile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… claim

- Uninstall refused any registry installPath outside the model hub, even when
  that directory no longer existed, so a stale entry (e.g. an unmigrated
  legacy assets/modelhub path) could never be removed. A missing directory is
  now just unregistered; an existing one outside the hub is still refused.
- ControllerTests: hostile uninstall cases against the real ModelController
  (outside dir, ../.. traversal, NTFS junction to outside, stale entry,
  normal in-hub removal).
- The quantized BS-RoFormer build is not "no faster than CPU" on CUDA: it
  takes 303-504 s vs 687 s on CPU for the 196 s track (the old figure was
  summed CPU time). fp32 on CUDA (73-91 s) is still 4-7x faster, so the
  GPU upgrade stands; comment and README corrected.
- Remove model-hub metadata accidentally committed under
  assets/phaselimiter/assets (leaked there by the old cwd change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GUI upgrade replaced the quantized BS-RoFormer file with the fp32 build
but left the scanned pack pointing at the deleted quantized file, so every
separation until restart was rejected ('missing model file') and silently
fell back to the frequency splitter. Found by driving the GUI: after the
fix, the post-upgrade import ran tensor separation on the fp32 pack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mb gpu allow-lists

Update priority chain to include webgpu (ANE, CoreML, CUDA, WebGPU, OpenVINO, DirectML, CPU) and deliberately update pinned-order tests. Map WebGpuExecutionProvider/webgpu/wgpu canonical names and set Windows platform-preferred provider to webgpu. Implement per-pack gpu_providers allow-list filtering for tensor candidates, plumb allow-list through ModelPackLoader -> StemSeparator -> OnnxTensorInference, and restrict BS-RoFormer to CUDA until measured. Update macOS version requirements in GpuCapabilityDetector.
…dened OrtRuntime and probe cache

Phase A implementation:
- A1: Distinguish tuned and untuned session options via gpu::sessionConfigPlan. Untuned sessions (tensor sessions and probes) leave ORT thread defaults intact, fixing CPU single-threading regression.
- A2: Automatically default legacy BS-RoFormer pack manifests to [cuda] allow-list in ModelPackLoader.
- A3: Harden OrtRuntime with #if AUTOMIX_HAS_EP_PLUGIN compile guards, std::call_once init order safety, intentionally leaked singleton static pointer, 32k wchar path resolution, device info in diagnostics, asynchronous warm-up, and diagnostics surfaced in OnnxTensorInference.
- A4: Implement failure caching in tensorProviderUsable, add invalidateTensorProviderProbeCache(), and invoke it on GPU runtime install/uninstall in MainLayout.cpp and ModelCommands.cpp.
- A5 spike result: WebGPU plugin registered successfully on Windows x64 with 2 WebGPU devices found (chosen: vendor=NVIDIA, deviceId=11524, type=1).

Co-Authored-By: Muse Spark 1.3 <noreply@opencode>
…t hashes, and stage assets offline

Round 2 follow-up addressing R2-2, R2-3, R2-5, R2-6/P3, and R2-8:

- Point linux-x64 back at upstream static release.tar.xz (0b382ba7...) to eliminate distro glibc/boost regressions
- Remove unverified empty-hash pins from download table and reject empty-hash downloads before network fetch
- Add cmake/FetchPhaseLimiter.cmake to fetch and stage bin/ and resource/ assets beside executables (AUTOMIX_FETCH_PHASELIMITER)
- Add macOS Gatekeeper first-launch approval instructions in README.md
- Add strict 64-hex SHA-256 assertions and empty-hash rejection test in PhaseLimiterDiscoveryTests
soficis and others added 16 commits October 3, 2026 18:12
…hen the vocal model would run on CPU there

Measured on an 8 GiB MacBook Neo: CoreML and ANE were SIGKILLed during the first
BS-RoFormer inference and the CPU path took over 5 minutes per chunk.
AUTOMIX_ENABLE_COREML=1 opts in; naming the provider explicitly still works.
…mory machines

Adds magnitude_channels input and ratio_mask output to the tensor runner (mix phase kept),
a curated MIT pack (36 MB, CPU), NOTICE and licensing audit rows, and points the low-memory
Mac warning at it. 60 s of audio separates in 0.9 s on an 8 GiB MacBook Neo, where
BS-RoFormer needed over 5 minutes per chunk.
…mory warning when it is already active

The curated Open-Unmix pack took its revision and sha256 from the live HF API, so a
later push to the repo would change what installs. applyCuratedPin overrides both with
pinned values, and the existing hash check rejects anything else.

The low-memory Mac warning told users to install Open-Unmix even when it was already
the active separation pack; it now stays quiet in that case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tatus badge, Default Streaming as the default master

- The shortcuts dialog was never added to the desktop, so "?" and Ctrl+/ showed
  nothing and swallowed the next click; it now opens as a DialogWindow. Key names
  and the batch em dash were built with the integer String constructor ("8212").
- Closing with unsaved changes now asks Save / Don't Save / Cancel; the header shows
  "*" while the session differs from the last save or load.
- Clear's confirmation no longer contradicts itself or shows an internal ticket id.
- The status badge uses dark text where white failed contrast; READY replaces IDLE.
- New sessions default to the Default Streaming master preset instead of Udio Optimized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…age separation controls

- Button variants (primary/secondary/quiet/danger): Import is the one primary action
  until stems exist, then Mix + Master; Save/Load/Models/Settings are quiet; Clear is
  styled as destructive.
- Actions that need stems are disabled until stems are imported; transport too.
- The empty waveform is a bordered drop zone that highlights on drag and opens
  Import on click.
- Vocal Model and the model name appear only when AI Stem Separation is on; model
  names are plain language instead of Hugging Face repo ids.
- Renderer and Active chain move into Advanced; combos widened; tooltips for Master,
  Platform, Renderer and Mode; accessibility titles for combos, sliders and transport.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at the minimum window size

- The batch strip shows only while a batch has items (nothing fed it before, so it
  only ever showed placeholders); the log is collapsible, collapsed by default, and
  opens on failure; dialog-open noise is no longer logged; idle progress is hidden.
- Meters read "--" instead of -70.0 when there is no signal.
- The header profile selector, which was never populated, is hidden; the deck's
  Profile combo is the single profile control.
- Settings has Export / GPU acceleration / Audio output sections, and removing the
  GPU runtime asks first. The Model Browser opens at 760x560.
- Disabled buttons are outlined instead of filled, so they no longer outshine enabled ones.
- At 960x640 the settings row wraps instead of clipping Platform, and action buttons
  use a compact font instead of wrapping.
- Tests: ControlDeck stem gating, separation visibility, meter formatting and layout
  at 940/1500 px; TaskCenterPanel batch strip, log and idle progress visibility.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
soficis and others added 5 commits October 5, 2026 16:40
… warm-up thread at shutdown

A sidecar for a model in a repo subfolder was downloaded into that
subfolder, but the inline step looked it up by bare file name and never
found it. All hub assets now land directly in the install directory.

The warm-up thread was detached, so quitting during ONNX Runtime start-up
could race library teardown. It is now joined in shutdown().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, restore the single-file tip

- Quitting while a task runs now asks first, then continues into the
  unsaved-changes prompt.
- The empty state opens Import only on a left click released inside it,
  and only when the session has no stems; stems without a preview show
  "Preview unavailable" instead of the import prompt.
- The empty state again points single-file users at AI Stem Separation.
- Session files saved before the master preset field existed load as
  Udio Optimized again, as they were mastered; new sessions still
  default to Default Streaming.
- Unit tests cover the quit decision, the click decision and the
  preset fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erified cached binaries

The MP3 fallback downloaded an encoder and ran it with no integrity check
beyond "--version prints something".

- Each built-in source now carries the SHA-256 of the exact archive. A
  download that does not match is deleted before anything is extracted
  or run, and a source with no hash is refused.
- Homebrew bottles are fetched directly by their pinned digest instead of
  resolving a mutable tag through two manifests.
- AUTOMIX_LAME_DOWNLOAD_URL and AUTOMIX_LAME_VERSION now require
  AUTOMIX_LAME_DOWNLOAD_SHA256.
- The cached binary is used only when a marker written by a verified
  install still matches it, so binaries cached before this change are
  downloaded again.
- Windows on ARM uses the x64 build; its previous URL returns 404.

Debian and Homebrew hashes come from their published indexes. rarewares.org
publishes none, so the two Windows pins are the files as served on
2026-10-05.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in list

Fixed pins alone break the MP3 fallback when an upstream replaces a file
(Debian stable has already moved to 3.100-6+b3 and Homebrew to LAME 4.0).

- assets/lame-pins.json is the current pin list. The app reads it from
  master before downloading and tries those sources first; the built-in
  pins remain as the fallback when the list is unreachable, invalid, or
  names a build that does not run on the machine.
- The list may only name files under the known rarewares.org and Debian
  locations, or a Homebrew bottle digest. One bad entry rejects the list.
- tools/update_lame_pins.py regenerates the list from Debian's package
  index, the Homebrew bottle index and the rarewares.org files.
- A weekly workflow runs it and opens a pull request when pins change.
  It is not merged automatically.
- AUTOMIX_LAME_SKIP_PIN_UPDATE=1 keeps the app on the built-in pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
soficis and others added 5 commits October 5, 2026 17:41
…Linux bottle sources

The app copies just bin/lame out of a Homebrew bottle. Checked against the
real bottles and on an Apple-silicon Mac:

- 3.100 macOS bottles link only system libraries and run.
- 4.0 macOS bottles need Homebrew's libmpg123 and fail to launch, so
  publishing them only added a wasted download before the fallback.
- Linux bottles of both versions use a Homebrew placeholder as their
  loader path and can never run, so the built-in Linux bottle sources
  were dead and are removed. Linux uses the Debian package.

The updater now downloads each candidate bottle, checks it against its
digest and skips it when the encoder still points at a Homebrew path. A
platform with no usable bottle is left out of the published list and the
app uses its built-in pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nus glyph and clipped meter captions

README
- Corrects the shortcuts (Ctrl+Shift+A is Auto Master; Ctrl+/ opens the
  shortcut list) and the inference notes, which still said no audio-tensor
  path exists.
- Adds presets, the light vocal model, export formats and sessions.
- Build steps and developer notes move into collapsed sections.

UI
- The zoom-out button showed "?": a \u escape in a narrow string is
  mangled by MSVC. It is now UTF-8 bytes.
- The L/R captions were drawn above the meter panel and clipped.
- The loudness bar was painted behind the first readout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UI/UX pass: working help, unsaved-changes prompt, clear hierarchy, Default Streaming default
Pin LAME downloads to SHA-256, with a reviewed pin list that follows upstream
@soficis
soficis merged commit 86f1e9a into master Oct 5, 2026
9 checks passed
@soficis
soficis deleted the feat/tensor-inference branch October 5, 2026 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant