Skip to content

Add per-track band diagnosis; record captures from --start - #129

Merged
6uclz1 merged 3 commits into
mainfrom
feat/per-track-band-diagnosis
Oct 22, 2026
Merged

6uclz1 merged 3 commits into
mainfrom
feat/per-track-band-diagnosis

Conversation

@6uclz1

@6uclz1 6uclz1 commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Stacked on #128, which is stacked on #127. Merge those first; this PR's base then moves down.

Bug fix: session capture never honored --start

capture_session moved the playhead while the transport was stopped, which Live ignores. It then fired the clip and pressed play, so each capture began wherever the previous stop left the playhead. Firing from a stopped transport restarts the song at beat 0.

Back-to-back captures drifted through the song. On the Phase Trance set:

  • A Kick capture was silent for its first 13 s.
  • Perc, Sub, Bass and Lead captures were silent throughout.
  • The Remote Script already notes this ("Live ignores a playhead moved while stopped … the CLI re-applies it once playback runs"). transport play re-applies the playhead; capture_session called transport_play directly and never did.

Fix:

  • Start playback, move the playhead one bar before --start, then fire. Live's 1-bar launch quantization begins the recording exactly at --start. Verified in Live: a capture at beat 252 had the kick enter 1.8 s in, matching the arrangement.
  • Verify afterwards: recorded_start_beat = stop position − file length (from the WAV header). If it is more than half a beat from --start (e.g. launch quantization ≠ 1 Bar), the command fails with step: verify_recorded_start.
  • Contract change: --start is now required and must be at least one bar, because the pre-roll needs a bar before it. session capture's old default of 0 can no longer be honored.

New commands

  • audio spectrum shares --stem LABEL=PATH … reports, per band:
    • each stem's level
    • shares_pct: each stem's share of the band's summed energy
    • dominant: the stem with the largest share
    • band_totals_dbfs: the summed level
    • with --focus LABEL, focus.margin_db: the focus stem minus the loudest other stem (negative = gap to close)
    • --resolution third-octave uses the 29 ISO bands from 25 Hz to 16 kHz.
  • session capture-tracks --start B --track-index N … captures each track on its own:
    • Solos each track and captures it into a temporary audio track (Resampling, volume 0), which it deletes afterwards.
    • --bypass-main-devices turns off active main devices for the capture.
    • Solos, the temporary track and bypassed devices are restored in finally blocks, even when a capture fails.
    • Refuses to run while a track is soloed.
    • --analyze, --resolution, --focus feed spectrum shares, with labels from track names (index:name when names repeat).
  • Band measurement: every band now runs in one ffmpeg pass (asplit into named astats@bN instances, joined by amix), with a 16384-point FFT so the 22–28 Hz third-octave band holds more than one bin.

Public contract impact (intentional)

  • New commands: audio spectrum shares, session capture-tracks.
  • session capture: --start is required (≥ one bar), and the result adds recorded_start_beat.
  • Command-surface and public-contract snapshots are regenerated (additions, plus the required option). README, SKILL.md and docs/mastering.md are updated.

Tests

  • tests/test_capture.py: call order (play before playhead before fire), pre-roll per time signature, pre-roll validation, recorded-start mismatch, and existing behaviors on the new flow.
  • tests/test_capture_tracks.py: solo/temporary-track sequence, device bypass and restore, restore on failure, duplicate labels, --analyze wiring, validation (empty, duplicate, unknown, already soloed, --focus without --analyze, bad resolution before capturing), --read-only blocking.
  • tests/test_spectrum_shares.py: share math, silent bands, focus margins, third-octave bands, validation, CLI --stem parsing and report, missing file; plus real ffmpeg (an 80/20 split of a tone at half amplitude).
  • tests/test_audio_spectrum_bands.py: single-pass graph, custom bands, readings found by instance name.

Validation

  • ruff check, ruff format --check, dev_checks: 1432 passed
  • Quality harness: exit 0, 0 failures
  • Live (Phase Trance, Live 12.4):
    • Drop and breakdown capture-tracks produced audio for the full window on every track, with solos, the temporary track and main devices restored.
    • Master session capture --start 192 reported recorded_start_beat: 192.0.

Known limit: shares average the whole window, so a track that rests for part of it reads low. The docs say to check its level while it plays.

🤖 Generated with Claude Code

6uclz1 and others added 2 commits October 23, 2026 00:06
session capture never honored --start: it moved the playhead while the
transport was stopped, which Live ignores, then fired the clip. Each
capture began wherever the last stop left the playhead (or at beat 0
when firing from a stopped transport). Back-to-back captures drifted
through the song, so section measurements came from the wrong bars.

Captures now start playback, move the playhead one bar before --start,
and fire the clip, so 1-bar launch quantization begins the recording
at --start. The stop position minus the file's length gives
recorded_start_beat; a recording more than half a beat off fails with
step verify_recorded_start. --start is now required and must be at
least one bar, on session capture and the new capture-tracks.

New commands:

- audio spectrum shares --stem LABEL=PATH ...: each stem's level per
  band, its share of the band's summed energy, the dominant stem, and
  with --focus the margin over the loudest other stem. --resolution
  third-octave uses the 29 ISO bands from 25 Hz to 16 kHz.
- session capture-tracks --track-index N ... --start B: solos each
  track into a temporary capture track, optionally bypassing active
  main devices; solos, the temporary track, and devices are restored
  even when a capture fails. It refuses to run while a track is
  soloed. --analyze reports spectrum shares by track.

Band measurement now runs every band in one ffmpeg pass (asplit into
named astats instances) with a 16384-point FFT for the narrow low
third-octave bands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three tests passed only on a host with Live and ffmpeg:

- capture-tracks under --read-only reached tracks_list (a read) and
  failed to connect on CI. The command now refuses up front, like
  set automation write, since every capture writes.
- spectrum shares checked for ffmpeg before checking the stem files,
  so a missing file reported CONFIG_INVALID on CI. Stem paths are now
  validated with the other arguments, before the ffmpeg check.
- The missing --start usage error is colored on CI, which splits the
  option name; the test strips ANSI codes.

The quality harness failed on warning growth (26 over a baseline of 20
allowed). capture_tracks takes a ShareAnalysis instead of three
analysis arguments, restores bypassed main devices in a context
manager, and the shares, band-graph, and command-registration code is
split into helpers. --focus without --analyze is now rejected by the
CLI, where both options live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@6uclz1
6uclz1 changed the base branch from feat/spectrum-band-measurement to main October 22, 2026 15:25
@6uclz1
6uclz1 merged commit 2b1921d into main Oct 22, 2026
11 checks passed
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