Skip to content

feat(editor): active-source switching — chart against an isolated stem - #296

Merged
byrongamatos merged 1 commit into
mainfrom
feat/editor-active-source
Jul 16, 2026
Merged

feat(editor): active-source switching — chart against an isolated stem#296
byrongamatos merged 1 commit into
mainfrom
feat/editor-active-source

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The last parity gap from #285 — completes the re-cut.

What it does

Clicking an audio track in the Tracks column (the master mix or any stem) makes it the active source: its decoded buffer becomes the one the main waveform shows and the onset tools (Suggest, snapping) analyze — so you can line the grid up against an isolated stem. Playback is unaffected: the newly-active source plays via the reference path and every other live source keeps playing through the scheduler, so what you hear is unchanged; only what you see and analyze follows the click.

How (faithful behavior without #285's full unified scheduler)

My stem engine is additive (the master keeps its own S.audioSource path), so I couldn't repurpose S.audioBuffer as a display buffer the way #285 does. Instead I generalized it minimally:

  • _liveAudioSources() now returns master + stems (master's URL held in the new S.masterAudioUrl so it survives while a stem is active and S.audioUrl points at the stem).
  • _startStemSources schedules every live source except the active one (exposed as the pure _scheduledSourceIdsPure); the active source rides the existing S.audioSource → _refGain path.
  • activateTrackAudioSource(id) decodes on demand (reusing the source cache), installs the buffer as the reference, recomputes the waveform, lets onsets self-invalidate on the buffer swap, and restarts the play split if playing. syncStemAudio adopts the already-decoded active buffer for free instead of re-fetching.

Net: master active (default) = the scheduler plays the stems, exactly as before. Focus a stem = it becomes the reference (waveform + onsets), the master joins the scheduler — everything still plays.

Wiring

host.selectTrackSessionSource → activateTrackAudioSource; the Tracks-column audio-row click focuses the source. resetStemAudioCache + the load/create paths reset activeAudioSourceId to master and seed S.masterAudioUrl.

Tests

stem_engine.test.mjs pins the active-vs-scheduled split (_scheduledSourceIdsPure: master-active → stems only; stem-active → master + other stems; unknown active excludes nothing). 184 JS suites, 291 pytest, lint 0 errors.

The re-cut is complete

With #294 (lock guard), #295 (mixer console + meters + +6 dB), and this, every #285 feature now exists on current main across #289#296. #285 is fully superseded.

⚠️ Interactive desktop verification pending — the build for that is next.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q

Summary by CodeRabbit

  • New Features

    • Added a first-class Master Mix strip with live fader/mute/solo controls.
    • Clicking a track/row now switches the active waveform/onset analysis source without affecting playback.
    • Mixer strips now reorder to match the current track order; the master strip label follows the loaded session.
  • Bug Fixes

    • Fixed stem/master waveform and onset rendering to use the correct decoded audio and apply accurate per-source timing offsets.
    • Improved stem/master audio sync, reference timing, tempo-guide reliability, and reduced stale/incorrect mixer state after track changes.
  • Tests

    • Added/expanded coverage for mixer ordering, stem cache naming, waveform peaks, and activation/sync edge cases.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8fede8d1-b493-45f5-8403-9124b8382793

📥 Commits

Reviewing files that changed from the base of the PR and between 21921eb and f60be62.

📒 Files selected for processing (18)
  • CHANGELOG.md
  • routes.py
  • src/arrangement.js
  • src/audio.js
  • src/file-ops.js
  • src/host.js
  • src/main.js
  • src/mixer-panel.js
  • src/parts-view.js
  • src/shortcuts.js
  • src/state.js
  • src/track-session.js
  • src/waveform.js
  • tests/audition_clock.test.mjs
  • tests/mixer_panel.test.mjs
  • tests/stem_engine.test.mjs
  • tests/test_editor_stem_cache.py
  • tests/track_session.test.mjs

📝 Walkthrough

Walkthrough

The editor now models master and stem audio as live sources with stable metadata, offsets, and cache names. Tracks can activate a source for waveform and onset analysis without changing playback. The mixer renders and orders a Master Mix strip, while waveform rendering, tempo-guide behavior, and removal return values are updated.

Changes

Audio source contracts and session loading

Layer / File(s) Summary
Source metadata and session state
routes.py, src/state.js, src/file-ops.js, src/track-session.js, tests/test_editor_stem_cache.py
Session responses and state retain master/stem URLs, names, offsets, stable cache names, and active-source metadata.
Active-source playback and analysis
src/audio.js, tests/audition_clock.test.mjs, tests/stem_engine.test.mjs
Audio synchronization, decoding, reference routing, scheduling, waveform peak extraction, and asynchronous source activation support master and stem sources.
Tracks focus and mixer integration
src/host.js, src/main.js, src/mixer-panel.js, src/parts-view.js, src/track-session.js, tests/mixer_panel.test.mjs, tests/track_session.test.mjs
Track selection activates analysis sources, mixer strips follow track order, and the master strip remains independently audible.
Source-aware rendering and supporting fixes
src/waveform.js, src/arrangement.js, src/shortcuts.js, CHANGELOG.md
Waveform/onset positioning uses source offsets; arrangement removal returns explicit failure values; and labels and changelog entries are updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

  • got-feed/feedBack#178 — The planned stem-swapping workflow overlaps with this PR’s source selection, muting, and mixing changes.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: editor active-source switching for waveform/charting on a selected stem.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/editor-active-source

Comment @coderabbitai help to get the list of available commands.

@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-mixer-meters branch from dc09c19 to 2d71828 Compare July 16, 2026 03:54
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-active-source branch from fe73592 to 3e195de Compare July 16, 2026 03:54
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-mixer-meters branch from 2d71828 to 85395ef Compare July 16, 2026 03:57
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-active-source branch from 3e195de to 378ccb5 Compare July 16, 2026 03:57
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-mixer-meters branch from 85395ef to ff7807b Compare July 16, 2026 04:11
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-active-source branch from 378ccb5 to e1f4e49 Compare July 16, 2026 04:11
byrongamatos added a commit that referenced this pull request Jul 16, 2026
…lo/mute

Byron's product call: audio:master is the final OUTPUT destination
(Logic/Ableton master bus), not a peer channel. Another stem/part's
mute or solo removes only that channel's own contribution and must never
silence the master output. Master's OWN mute (the output fader) still
mutes it.

Root cause was the shared audibility rule _mixerPartAudiblePure, where
#296 made the master a participant in the whole-map solo rule — so
soloing a stem or muting a part zeroed the active master reference gain
(host.partStripState → _ensureStemGain/applyStemMix all route through
this one rule). Exclude audio:master from the solo rule while keeping its
own mute check ahead of the guard.

Regression test fails pre-fix: with a stem soloed AND a different track
muted, audio:master stays audible; muting the master strip itself still
mutes it (over-correction guard).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@byrongamatos

Copy link
Copy Markdown
Collaborator

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (1)
tests/mixer_panel.test.mjs (1)

115-130: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the master strip in the ordering test.

The fixture omits audio:master, even though src/mixer-panel.js now prepends it and sends unlisted keys to the tail. Assert the intended result when audio:master is both present and absent from orderedKeys; otherwise a host/order contract mismatch can silently move the master strip away from its required position.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/mixer_panel.test.mjs` around lines 115 - 130, Update the ordering test
around _mixerOrderedPartsPure to include an audio:master fixture and assert it
remains in the required prepended position when orderedKeys includes it. Also
add coverage for when audio:master is absent from orderedKeys, verifying the
implementation’s unlisted-key tail behavior and preserving the expected relative
order of the remaining strips.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 10: Update the changelog headings so only one `### Added` section
remains: merge the later Added content into the existing section or rename the
later heading appropriately, preserving all entries and avoiding duplicate
markdown headings.
- Around line 17-18: Clarify the CHANGELOG terminology by distinguishing the
mixer’s master strip from the selectable Master source row in the Tracks pane,
then revise the entries around the Tracks pane and Master row so they
consistently describe their respective placement and controls.

In `@routes.py`:
- Around line 4202-4207: Update the synthetic master source construction and
related source-ID handling near _audio_sources so its ID uses a reserved runtime
namespace that cannot collide with manifest stem IDs such as "master". Keep stem
IDs unchanged, and ensure all frontend-facing references to the synthetic master
use the new namespaced ID consistently.

In `@src/audio.js`:
- Around line 1775-1791: Update the active-source repair logic around
activateTrackAudioSource so a failed fallback activation is not treated as
repaired. If the preferred fallback returns false, try another decoded live
source; if none activates successfully, clear the active source and audio state
and restart playback using the existing no-source reset path.

In `@src/file-ops.js`:
- Around line 193-199: Move the master metadata assignments for
S.masterAudioUrl, S.masterAudioDuration, and S.masterAudioName before the
installTrackSession() call so its immediate render uses the current song’s
values and cannot retain the previous song’s label.

In `@src/parts-view.js`:
- Around line 256-259: Update the header-row handling around
host.selectTrackSessionSource so the generic “Audio track” status is set before
activation, preserving any specific synchronous error status produced when the
source is unavailable. Keep the existing source selection behavior unchanged.

In `@src/track-session.js`:
- Around line 708-710: Update the delegated row handler around
host.selectTrackSessionSource so source activation occurs only for direct row or
name clicks, not when interacting with M/S, fader, or guide controls. Preserve
the existing control-handling behavior while preventing those controls from
switching the active waveform.
- Around line 417-428: Update the Parts-view source projection call to pass the
stable master URL expression S.masterAudioUrl || S.audioUrl instead of only
S.audioUrl, and provide the master display name using the same fallback order as
_liveSources: S.masterAudioName || S.title || 'Master Mix'. Preserve the
existing stems argument and projection behavior.
- Around line 912-927: Update the guide source selection logic around the
`guide-set` branch and `tempoGuideMode` assignment so reselecting the existing
`tempoGuideSourceId` preserves its metronome mode. Reset `next.tempoGuideMode`
to `'audio'` only when the selected source ID differs from the previous guide
source ID, while retaining the current source-selection behavior.
- Around line 536-543: Update trackSessionOrderedMixKeys() to derive mixer
ordering from the complete track hierarchy rather than _rowsLive().rows, which
excludes descendants of collapsed folders. Traverse all tracks regardless of
folder collapse state, retain only rows with mixKey, and preserve their
track-column order.
- Around line 464-468: When restoring the master in the initialization block
around S.activeAudioSourceId, also reset S.activeAudioSourceOffset to the
master’s default offset (zero) so no focused stem offset carries into waveform
and onset analysis.

In `@tests/stem_engine.test.mjs`:
- Around line 123-149: Update the test teardown for the repair and waveform
tests around syncStemAudio to restore every mutated audio-state field, including
duration, masterAudioDuration, audio URLs, active source identity and offset,
and related playback state. Save these fields before each test, call
resetStemAudioCache() during teardown, then restore the saved state so tests
remain order-independent.

---

Nitpick comments:
In `@tests/mixer_panel.test.mjs`:
- Around line 115-130: Update the ordering test around _mixerOrderedPartsPure to
include an audio:master fixture and assert it remains in the required prepended
position when orderedKeys includes it. Also add coverage for when audio:master
is absent from orderedKeys, verifying the implementation’s unlisted-key tail
behavior and preserving the expected relative order of the remaining strips.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 18fbcc0e-c127-4257-b104-d7969affafad

📥 Commits

Reviewing files that changed from the base of the PR and between ff7807b and d15def8.

📒 Files selected for processing (17)
  • CHANGELOG.md
  • routes.py
  • src/arrangement.js
  • src/audio.js
  • src/file-ops.js
  • src/host.js
  • src/main.js
  • src/mixer-panel.js
  • src/parts-view.js
  • src/shortcuts.js
  • src/state.js
  • src/track-session.js
  • src/waveform.js
  • tests/audition_clock.test.mjs
  • tests/mixer_panel.test.mjs
  • tests/stem_engine.test.mjs
  • tests/test_editor_stem_cache.py

Comment thread CHANGELOG.md
Comment thread CHANGELOG.md Outdated
Comment thread routes.py
Comment thread src/audio.js
Comment thread src/file-ops.js Outdated
Comment thread src/track-session.js
Comment thread src/track-session.js
Comment thread src/track-session.js
Comment thread src/track-session.js Outdated
Comment thread tests/stem_engine.test.mjs
byrongamatos added a commit that referenced this pull request Jul 16, 2026
…296)

- audio.js: on active-source removal, try every live source as a fallback;
  reset to the no-source state only when none decode, so a failed fallback
  can't strand the removed buffer/id as active.
- track-session.js: reset activeAudioSourceOffset on the create/import master
  restore; derive mixer order from the FULL tree (collapsed folders included)
  so collapse can't reshuffle strips; gate row-click source activation to
  direct row/name clicks (not M/S/fader/guide controls); preserve a guide's
  metronome mode when the same source is reselected; export _liveSources.
- parts-view.js: project unified rows from the stable master URL/name via
  _liveSources (not S.audioUrl, which points at the focused stem); set the
  generic status before activation so a specific source error survives.
- file-ops.js: assign master metadata (url/duration/name) before
  installTrackSession so its first render can't flash the previous song's label.
- CHANGELOG.md: merge the duplicate ### Added heading; clarify master row vs
  master strip placement.
- tests: regression tests for the failed-fallback reset, the create master
  offset reset, and collapse-preserving mix order (all fail pre-fix); restore
  every mutated audio-state field and the cache in the stem-engine teardowns.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/track_session.test.mjs`:
- Around line 202-209: Update the test around installCreatedTrackSession to
isolate its mutations to S: snapshot the affected session, URL, stems,
arrangements, and active-source fields before setup, then restore them in a
finally block (or use the existing suite reset fixture) after the assertions.
- Around line 211-223: Update the test “collapsing a folder does not drop its
stems from the mixer order” to capture the result of
trackSessionOrderedMixKeys() before setting folder:1.collapsed, then assert the
keys after collapsing are deeply identical to that captured order. Replace the
membership-only includes assertion while preserving the existing setup and
cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5de8af0c-93ba-49a4-b1c5-16e032dee9a6

📥 Commits

Reviewing files that changed from the base of the PR and between d15def8 and 0294942.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • src/audio.js
  • src/file-ops.js
  • src/parts-view.js
  • src/track-session.js
  • tests/stem_engine.test.mjs
  • tests/track_session.test.mjs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/file-ops.js
  • CHANGELOG.md
  • src/track-session.js
  • tests/stem_engine.test.mjs
  • src/audio.js

Comment thread tests/track_session.test.mjs
Comment thread tests/track_session.test.mjs
@byrongamatos

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@byrongamatos
byrongamatos changed the base branch from feat/editor-mixer-meters to main July 16, 2026 18:28
@byrongamatos
byrongamatos force-pushed the feat/editor-active-source branch from 21921eb to f60be62 Compare July 16, 2026 18:43
@byrongamatos
byrongamatos merged commit 9d8a661 into main Jul 16, 2026
3 checks passed
@byrongamatos
byrongamatos deleted the feat/editor-active-source branch July 16, 2026 18:43
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.

2 participants