Skip to content

fix(transpose): open a song at the key its saved record holds - #830

Merged
thcp merged 2 commits into
big-releasefrom
fix/transpose-switch-song-781
Oct 22, 2026
Merged

thcp merged 2 commits into
big-releasefrom
fix/transpose-switch-song-781

Conversation

@thcp

@thcp thcp commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #781.

Root cause

On 0.20 the global key was never saved: reopening a song restored each lane's absolute key from the store (audio still -2) but reset the counter to 0. big-release saves __global.pitch, which fixes a newly saved song, but three paths still let the counter, the engine and the saved record disagree:

  • A record saved by 0.20 has lane keys and no global key: counter 0 over lanes at -2 (the reporter's data after upgrading).
  • Opening a song inside the 300 ms save debounce read the old record; the late write then stored the key behind a counter and engine at 0.
  • The engine's ready handler pushed in-memory state before the record was read (desktop reads it over IPC), and nothing re-pushed it.

What changed

  • pitchBus.savedGlobalPitch: the global key comes from the record, or from its lanes when the record has none.
  • loadMixIntoState flushes the pending write for that record before reading it.
  • player.js: gains, lane keys, EQs, speed and global key are pushed after the record is in; the key controls stay disabled until then.
  • New transpose-switch-song.spec.mjs (Practice, Advanced, Karaoke; global and per-lane key; reload; 0.20 record; debounce; late record; speed and mutes; no AudioContext growth). The audio check reads the graph routing, not only the engine's reported value.
  • transpose.spec reopen test expected 0, which only held because of the debounce race; it now expects the key to stay.

What did not change

  • Performance mode has no key control. Click and count-in stay unpitched by design. Loop region is still reset when you open a song.
  • The slow-speed value (stemdeck:slow-speed) is per browser, not per song: a song saved at 0.75x reopened after the slow value changed plays 0.75x with no speed button lit.

thcp added 2 commits October 22, 2026 00:53
Checks the counter, every lane's stepper, the audio graph's routing and the saved record agree in Practice, Advanced and Karaoke, after switching back and after a reload. Also a record saved by 0.20, a reopen inside the save debounce, a record read after the engine is up, speed and mutes, and that switching leaves no extra AudioContext or pitch chain.

The transpose spec's reopen test asserted the key went back to 0, which only held because the reopen beat the debounced save.
The counter, the engine and the saved record could disagree when a song was opened again:

- A record saved by 0.20 has each lane's absolute key but no global key, so the counter opened at 0 over lanes still playing transposed. The global key is now read back from the lanes when the record has none (pitchBus.savedGlobalPitch).
- Opening a song inside the 300 ms save debounce read the record from before the change; the late write then stored it behind a screen and an engine at 0. The pending write is flushed before the read.
- The engine's ready handler pushed whatever was in memory when the record had not been read yet. Gains, lane keys, EQs, speed and the global key now wait for the record, and the key controls stay off until it is in.

Closes #781
@thcp
thcp marked this pull request as ready for review October 22, 2026 00:26
@thcp
thcp merged commit dd1b356 into big-release Oct 22, 2026
10 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