Skip to content

The board catches up with the seed, and the save stops lying about its channels - #49

Closed
Gamah wants to merge 1 commit into
masterfrom
feat/sbox-panel-parity
Closed

The board catches up with the seed, and the save stops lying about its channels#49
Gamah wants to merge 1 commit into
masterfrom
feat/sbox-panel-parity

Conversation

@Gamah

@Gamah Gamah commented Aug 13, 2026

Copy link
Copy Markdown
Owner

PLAN row 98. The s&box panel catches up with tag:n[:genre][:vibe], and the in-game save stops writing stereo PCM under a mono header.

What changed

Pinning is per seed part. RandomEverySong (one switch over both) becomes RandomGenreEverySong + RandomVibeEverySong, and _genrePinned folds into the first rather than sitting beside it — one switch could not express half the seeds the parser accepts. PlaySeed sets each from what the string wrote down; StationSeed reads them back to build the seed as it stands.

Panel, matching the web element:

  • genre strip gains a Random entry (RollGenre(), which had no caller)
  • two copy buttons — this song fully resolved, or the station as it stands
  • the knob matrix is behind a 🎛 Tinker button, and the controls that decide what plays (new station / save / tinker) moved out of the mixer into a row of their own
  • under the sliders: 🎲 Randomize (draws a fresh vibe and pins it — always moves every knob) and ↺ Random each song (the unpin, dimmed when there is no pin to drop)
  • the reroll stops claiming it rerolls the genre: 🎲 New station is a fresh random tag at song 0, pins kept

Two bugs the split turned up. StartSequence pinned the current song into the ledger to survive shuffle — with a per-part pin applying to every song, that entry could only shadow VibeForN/GenreForN. And EffectiveConfig() returned the live knobs, which under a rolled vibe is not the song playing: the mixer drew knobs you could not hear, and dragging one snapped the other 35 to values nobody chose. It is ConfigForN(_curN) now, and so is SetVibe's starting point.

The save. SaveCurrentToFile passed channels: 1 for _curRaw — the interleaved stereo buffer SoundStream( _sr, MusicGen.Channels ) is fed. Wav.FromSamples no longer takes a channel count at all: output is always MusicGen.Channels, so the argument could only agree with the buffer or be silently wrong, and wrong plays at half speed with the channels folded together, which sounds like a mix decision rather than a broken file.

Also skafinity_station and skafinity_random genre|vibe|both; skafinity_status reports which parts are rolling.

Testing

  • test-engine: 556 checks pass, including WAV is stereo.
  • node suite (smoke, page, queue, player, element, palette, encode): all pass.
  • web/_framework re-published (AOT) and re-stamped — Engine/Wav.cs and wasm/Exports.cs are stamp inputs, so the gate needed it. No audio change: Exports already passed 2.
  • The s&box half cannot be built or run here — panel, player and commands are review-and-grep only. Push-and-listen.

Not in this PR

s&box still has no shuffle in the sense the web now means it (every next song is a whole new station). SeedCodec.RollTagFor exists in the engine for it and only the web calls it; giving SkafinityPlayer the same means the positions-vs-indices rework the web did in aeb9067. Filed as PLAN row 55.

…s channels

The s&box panel was still the panel the old vibe format left behind. The genre
strip had no way back out of a genre once one was chosen, "share this" had one
button for two different things, the reroll said it rerolled the genre while
rerolling the vibe, and the knob matrix was the first thing anybody met.

It now says what the seed says. `tag:n[:genre][:vibe]` gives the genre and the
vibe a part each, so pinning is per part: `RandomEverySong` splits into
`RandomGenreEverySong` and `RandomVibeEverySong`, and `_genrePinned` folds into
the first of them rather than sitting beside it. That is what the Random entry
on the genre strip and `↺ random each song` under the sliders now write to, and
what `StationSeed` reads to build the seed as it stands, next to `CurrentSeed`
and its fully-resolved copy. The two are separate copy buttons because neither
can be recovered from the other. The matrix moved behind a tinker button and
the controls that decide what plays moved out of it, matching the web.

Two things the split fixed on the way past. `StartSequence` pinned the current
song into the ledger to survive shuffle; a per-part pin applies to every song,
so the entry could only shadow the answer `VibeForN`/`GenreForN` already give.
And `EffectiveConfig` handed back the live knobs, which with a rolled vibe is
not the song playing — the mixer drew knobs you could not hear, and dragging
one snapped the other 35 to values nobody chose.

The save wrote `channels: 1` for `_curRaw`, which is the interleaved stereo
buffer `SoundStream( _sr, MusicGen.Channels )` is fed: every in-game save was
stereo PCM under a mono header, i.e. half speed with the channels folded into
each other. `Wav.FromSamples` no longer takes a channel count at all. Output is
always `MusicGen.Channels`, so the argument could only agree or be silently
wrong, and wrong sounds like a mix decision rather than a broken file.

Verified by review and the engine suite; the s&box half cannot be compiled here.
@Gamah Gamah closed this Aug 13, 2026
@Gamah
Gamah deleted the feat/sbox-panel-parity branch August 13, 2026 03:01
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