mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
watchsettings.c: resync in-stage BGM (track 1) on Music commit and level entry (D355)
This commit is contained in:
1 parent
61263ad303
commit
2b4c5ba7dc
4 files changed
+55
-8
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Watch-backed settings in PC Options and F10 — proposal for review
|
||||
|
||||
Status: **watch-backed subset implemented (D350), robustness fixes landed (D352), Turok-style regrouping done (D353), and front-end F10 routing + transparent default Bond-file selection landed (D354: non-stage contexts use the chooser path, default = explicit pick > `selected_folder_num` > first valid file, with a one-shot `fileBuildWriteNewSave(FOLDER1)` fallback; GE_WSPROBE/GE_WSPROBE_FRONT headless probes).** Wider M3 remains outstanding: master volume, window-mode/display rows, per-section reset (Gate E), key rebinding. PC-only per-file persistence and explicit Bond-file chooser approved; details below. See `docs/dev/findings.md` D350–D354 for verification. This is the watch-integration part of M3, not a replacement for the native watch menu. The older tracked `docs/dev/OPTIONS-MENU-PLAN.md` and local `docs/dev/notes/OPTIONS-MENU-PLAN.md` describe the wider M3 scope (master volume, window mode/display, resets).
|
||||
Status: **watch-backed subset implemented (D350), robustness fixes landed (D352), Turok-style regrouping done (D353), front-end F10 routing + transparent default Bond-file selection landed (D354: non-stage contexts use the chooser path, default = explicit pick > `selected_folder_num` > first valid file, with a one-shot `fileBuildWriteNewSave(FOLDER1)` fallback; GE_WSPROBE/GE_WSPROBE_FRONT headless probes), and the in-stage BGM resync landed (D355: the Music commit also drives the track-1 BGM player, matching the game's own MP-path resync; by-ear confirmed).** Wider M3 remains outstanding: master volume, window-mode/display rows, per-section reset (Gate E), key rebinding. PC-only per-file persistence and explicit Bond-file chooser approved; details below. See `docs/dev/findings.md` D350–D355 for verification. This is the watch-integration part of M3, not a replacement for the native watch menu. The older tracked `docs/dev/OPTIONS-MENU-PLAN.md` and local `docs/dev/notes/OPTIONS-MENU-PLAN.md` describe the wider M3 scope (master volume, window mode/display, resets).
|
||||
|
||||
## Goal and ownership
|
||||
|
||||
@@ -57,4 +57,6 @@ Thus a front-end row that edits only live watch state will be silently undone be
|
||||
- **Approved:** PC-UI edits persist per selected Bond file; watch-only edits stay session-only. The front screen gets an explicit file chooser; F10 uses the active file. Preserve stored controller type; toggles/enums save on change, slider drags on release. No `src/game` behavior change or N64 save-format change.
|
||||
- **Still separate product decisions:** whether/how to expose GE Screen/Ratio alongside PC video settings; whether to expose control style at all given the port's forced mapping (deferred); whether Bond-file reset needs its own confirmation rather than being bundled with global PC defaults.
|
||||
- **Landed 2026-09-27 (D352/D353, port-only):** bridge robustness fixes (D352) and the Turok-style row-table regrouping (D353: six sections — DISPLAY, GRAPHICS, BOND FILE, MOUSE / AIM, CONTROLLER, GAME; the music/FX sliders sit in BOND FILE; the page title annotates the selected file; `buildPages` overflow logs).
|
||||
- **Landed 2026-09-27 (D354, port-only):** front-end F10 routing + transparent default Bond-file selection (see item 4 above); in-stage F10 verified working by the `GE_WSPROBE` probe. A standalone AUDIO section (master volume + the config-side volume keys) and per-section reset-to-defaults (Gate E) are the next settings-scope items; key rebinding stays Phase 4.
|
||||
- **Landed 2026-09-27 (D354, port-only):** front-end F10 routing + transparent default Bond-file selection (see item 4 above); in-stage F10 verified working by the `GE_WSPROBE` probe.
|
||||
- **Landed 2026-09-27 (D355, port-only):** the Music commit now also resyncs the in-stage BGM (track 1) via `musicTrack1ApplySeqpVol`, plus a once-per-activation resync on level entry; by-ear confirmed. N64 watch path unchanged.
|
||||
- A standalone AUDIO section (master volume + the config-side volume keys) and per-section reset-to-defaults (Gate E) are the next settings-scope items; key rebinding stays Phase 4.
|
||||
@@ -232,3 +232,4 @@ D351,FIXED,Existing D346 value/unit rows had positional initializers shifted int
|
||||
D352,FIXED,D350 review of 1303c0eb:,"FIXED (port-only, port/src/watchsettings.c):"
|
||||
D353,FIXED,Turok-style settings regrouping:,"FIXED (port-only, optionsoverlay.c/frontoptions.c):"
|
||||
D354,FIXED,Front-end F10 Bond rows silently disabled:,"FIXED (port-only, port/src/watchsettings.c):"
|
||||
D355,FIXED,F10/options Music slider inaudible in-stage:,"FIXED (port-only, port/src/watchsettings.c):"
|
||||
|
+17
-1
@@ -640,6 +640,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D352 | **D350 review of 1303c0eb: stage transitions silently drop queued F10 setting changes (queue drained before the `stageActive` check); the UI thread's chooser state is unsynchronized; rapid slider drags push one RSP command per detent and re-persist the same value each frame; persist failures are silent (2026-09-27).** — full `## D352` entry at file tail | FIXED (port-only, `port/src/watchsettings.c`): hold-not-drop at stage transitions, spinlock on chooser state + front-screen guard, per-field coalesced queue commits, warning logs on all no-op paths, `PersistField` made static. NTSC build clean, headless menu smoke clean. |
|
||||
| D353 | **Turok-style settings regrouping: the D346 VIDEO mega-section (14–15 rows, at the MAX_PROWS limit) re-split into DISPLAY + GRAPHICS, the "AUDIO (BOND FILE)" hack header dropped (music/FX sliders fold into BOND FILE), all remaining rows converted to designated initializers, `buildPages` overflow now logs, and the Bond-file page title annotates the selected file (2026-09-27).** — full `## D353` entry at file tail | FIXED (port-only, `optionsoverlay.c`/`frontoptions.c`): 6 sections of 3–11 rows, all rows designated-initialized, `optionsRowIsBondChooser` accessor, build clean + headless smoke clean. Master-volume row, per-section reset (Gate E) and key rebinding remain in the M3 backlog. |
|
||||
| D354 | **Front-end F10 Bond rows silently disabled: `watchSettingsAvailable()` mis-routed F10-on-file-select through the stage path (returned 0), so with the hidden chooser stuck at "none" the volume sliders read "Select file" and did nothing; maintainer-reported (2026-09-27).** — full `## D354` entry at file tail | FIXED (port-only, `port/src/watchsettings.c`): all non-stage contexts route through the front path with a lazily-resolved default folder (explicit pick > `selected_folder_num` > first valid), one-shot game-thread `fileBuildWriteNewSave(FOLDER1)` fallback, GE_WSPROBE/GE_WSPROBE_FRONT env probes headless-verified (stage: live+save changed; front: default file 1 + save changed). |
|
||||
| D355 | **F10/options Music slider inaudible in-stage: the slider drives only the X-track (track 2) via `set_mTrack2Vol`, but in-stage BGM is track 1, which the game pins to `VOLUME_MAX` at level start (lv.c:364) and never rescales from `mTrack2Vol` in solo -- so neither the N64 watch nor the F10 could change the music the player was hearing; maintainer-reported (2026-09-27).** — full `## D355` entry at file tail | FIXED (port-only, `port/src/watchsettings.c`): the Music commit now also calls `musicTrack1ApplySeqpVol(v)` (the exact resync the MP path already does, mpmenu.c:354) and a once-per-activation resync runs on stage entry; by-ear confirmed by the maintainer. N64 watch path untouched. |
|
||||
|
||||
Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS
|
||||
threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software
|
||||
@@ -13834,4 +13835,19 @@ Maintainer report: the F10 volume settings "don't apply", while the watch and th
|
||||
- `watchSettingsChooseFile` guard flipped to "refuse in stage context" (front-end control only); "none" remains reachable by explicit cycling.
|
||||
- `GE_WSPROBE_FRONT=<tick>` probe verifies the same front commit headlessly: default boot (menu) resolves folder 0 (file 1) with no interaction, commit 4096 → save.music 255→32.
|
||||
|
||||
Verification: NTSC build clean; both probes pass headless; in-stage behavior unchanged (probe-identical). The D350 threading gate is unchanged in effect: the front path's `fileWriteSave` (EEPROM) now also runs from the F10 input thread in front contexts, but only one settings surface is ever active at a time and the game thread performs no save writes on menus, so writes stay serialized; reads (`saves[]`/CRC) are stable menu state. Manual by-ear check owed: F10 music/FX on file select, and default-file behavior after erase.
|
||||
Verification: NTSC build clean; both probes pass headless; in-stage behavior unchanged (probe-identical). The D350 threading gate is unchanged in effect: the front path's `fileWriteSave` (EEPROM) now also runs from the F10 input thread in front contexts, but only one settings surface is ever active at a time and the game thread performs no save writes on menus, so writes stay serialized; reads (`saves[]`/CRC) are stable menu state. Manual by-ear check owed: F10 music/FX on file select, and default-file behavior after erase. (By-ear confirmed by the maintainer in the D355 session: FX applies live; the music follow-on gap became D355.)
|
||||
|
||||
## D355 — F10/options Music slider inaudible on in-stage BGM (2026-09-27)
|
||||
|
||||
Maintainer report (after D354): F10 sound-FX changes apply in real time, but the Music slider does not; opening the in-game watch menu seemed to be what "applied" it. Diagnosis (port-only):
|
||||
|
||||
- **Root cause: the slider and the audible music are on different tracks.** The Music row drives `mTrack2Vol` → `set_mTrack2Vol` → the **X-track (track 2)** CSP player (cutscene/stinger music). In-stage BGM, however, is **track 1**: solo level start plays `lvlPlayMusicTrack1(getmusictrack_or_randomtrack(...))` (lv.c:304) and the level-start reset pins **all three** players to `VOLUME_MAX` (lv.c:364–366) — it never rescales track 1 from `mTrack2Vol` in solo. The MP path is the exception: `mpmenu.c:354` explicitly feeds `get_mTrack2Vol()` into `musicTrack1ApplySeqpVol` at level entry, and `mp_music.c` mission-state transitions do the same before each `musicTrack1Play`.
|
||||
- **Why the watch "fixed" it:** the in-game watch re-applies `set_mTrack2Vol` every frame while its volume screen is drawn (`draw_music_volume_slider`), so the watch-screen/stinger music (X track) audibly tracked the slider — the in-stage BGM never did. A one-shot F10 commit has the same effect as the N64 watch: correct value stored, inaudible on BGM.
|
||||
- **Fix (`port/src/watchsettings.c`, port-side only):**
|
||||
- `applyValue(WATCH_SETTING_MUSIC)` now calls `musicTrack1ApplySeqpVol(value)` after `set_mTrack2Vol(value)` — the *same* resync the game's own MP path performs (mpmenu.c:354), so F10/options Music edits are audible on the BGM player in real time (mirroring how FX always was via the global SFX scaler).
|
||||
- **Once-per-activation resync** in `watchSettingsGameTick`: level start pins track 1 to `VOLUME_MAX` before `init_watch_at_start_of_stage` (lv.c:474, level load) re-applies the saved music value to the X track; the hook resyncs track 1 to `get_mTrack2Vol()` on the first active tick of each activation (`wsStageWasActive` reset whenever the stage is inactive), so the player's setting survives level starts, not just live edits. The `!=` guard keeps it a no-op when the game already set an equal value.
|
||||
- Track 3 (MP BGM) is intentionally untouched — the game drives it from its own volume, and the PC port is solo.
|
||||
- The N64 watch-menu path is **unchanged** (ground truth: the watch scales the X track only); this is a PC-settings-surface extension, in the same category as D350's existing `set_mTrack2Vol` call from the port.
|
||||
- GE_WSPROBE logs extended with the track-1 volume (`t1=`) so the resync is visible headless.
|
||||
|
||||
Verification: NTSC build clean; **by-ear confirmed by the maintainer** — F10 Music now changes in-stage music in real time. The front-end commit path (input thread) now also calls `musicTrack1ApplySeqpVol`, which wraps `alCSPSetVol` on the track-1 player — same thread pattern as the pre-existing track-2 call, so the D350 threading gate is unchanged in effect.
|
||||
@@ -20,6 +20,9 @@ extern void fileWriteSave(save_data *save);
|
||||
extern s32 fileGamePakProbe(void);
|
||||
extern void fileGenerateCRC(u8 *start, u8 *end, save_data *result);
|
||||
extern void sub_GAME_7F0A91A0(u16 volume);
|
||||
extern void musicTrack1ApplySeqpVol(u16 volume);
|
||||
extern u16 musicTrack1GetVolume(void);
|
||||
extern u16 get_mTrack2Vol(void);
|
||||
extern void fileBuildWriteNewSave(u32 folder);
|
||||
extern void set_cur_player_look_vertical_inverted(u32 value);
|
||||
extern void cur_player_set_aim_control(u32 value);
|
||||
@@ -38,6 +41,11 @@ static int wsProbeTick = -1;
|
||||
static int wsProbeTickN = 0;
|
||||
static int wsProbePhase = 0; /* 0=armed, 1=queued (log pre), 2=done (log post) */
|
||||
|
||||
/* D355: per-activation flag for the level-start BGM resync (reset whenever the
|
||||
* stage is not active, so the next activation resyncs again -- lv.c:364
|
||||
* re-pins the BGM player to VOLUME_MAX on every level start). */
|
||||
static int wsStageWasActive = 0;
|
||||
|
||||
|
||||
static const char *const keys[WATCH_SETTING_COUNT] = {
|
||||
"Bond.Music", "Bond.FX", "Bond.Look", "Bond.AutoAim",
|
||||
@@ -353,7 +361,19 @@ static void applyValue(enum WatchSettingField field, int value)
|
||||
if (field <= WATCH_SETTING_FX && value > 32767) value = 32767;
|
||||
else if (field > WATCH_SETTING_FX) value = !!value;
|
||||
switch (field) {
|
||||
case WATCH_SETTING_MUSIC: set_mTrack2Vol((u16)value); break;
|
||||
case WATCH_SETTING_MUSIC:
|
||||
/* D355: the slider's value drives the X-track (track 2) via the game's
|
||||
* own setter, but in-stage BGM is track 1, which the game pins to
|
||||
* VOLUME_MAX at level start (lv.c:364) and never rescales from
|
||||
* mTrack2Vol in solo -- so the N64 watch (and the F10 before this)
|
||||
* could not change the music a player was actually hearing. The MP
|
||||
* path already does exactly this resync (mpmenu.c:354 feeds
|
||||
* get_mTrack2Vol() to musicTrack1ApplySeqpVol); mirror it here so a
|
||||
* F10/options Music change is audible on the BGM player in real time
|
||||
* (FX already was). Port-side only; the N64 watch path is untouched. */
|
||||
set_mTrack2Vol((u16)value);
|
||||
musicTrack1ApplySeqpVol((u16)value);
|
||||
break;
|
||||
case WATCH_SETTING_FX: sub_GAME_7F0A91A0((u16)value); break;
|
||||
case WATCH_SETTING_LOOK: set_cur_player_look_vertical_inverted(value); break;
|
||||
case WATCH_SETTING_AUTOAIM: cur_player_set_autoaim(value); break;
|
||||
@@ -374,6 +394,7 @@ void watchSettingsGameTick(void)
|
||||
* against selected_folder_num at apply time, so a stale cross-file edit
|
||||
* is still dropped, never misapplied. */
|
||||
if (!stageActive() || !g_CurrentPlayer) {
|
||||
wsStageWasActive = 0;
|
||||
/* D354: one-shot fallback -- if not a single folder holds a valid
|
||||
* save (wiped/corrupt eeprom), build the game's own blank save for
|
||||
* file 1 (game thread, so the EEPROM write is on the owning thread)
|
||||
@@ -423,6 +444,13 @@ void watchSettingsGameTick(void)
|
||||
SDL_AtomicUnlock(&lock);
|
||||
return;
|
||||
}
|
||||
/* D355: level start pins the BGM player to VOLUME_MAX (lv.c:364) before
|
||||
* init_watch re-applies the saved music value to the X track; resync the
|
||||
* BGM (track 1) to the same live value once per activation so the player's
|
||||
* music setting survives level starts, not just live F10 edits. */
|
||||
if (!wsStageWasActive && musicTrack1GetVolume() != get_mTrack2Vol())
|
||||
musicTrack1ApplySeqpVol(get_mTrack2Vol());
|
||||
wsStageWasActive = 1;
|
||||
struct Command batch[CMD_CAP];
|
||||
int n;
|
||||
SDL_AtomicLock(&lock);
|
||||
@@ -447,8 +475,8 @@ void watchSettingsGameTick(void)
|
||||
if (wsProbePhase == 0 && wsProbeTickN++ == (size_t)wsProbeTick) {
|
||||
wsProbePhase = 1;
|
||||
save_data *save = validSave(selected_folder_num);
|
||||
sysLogPrintf(LOG_INFO, "wsprobe: pre music=%d fx=%d save.music=%d save.fx=%d (folder %d, %d queued)",
|
||||
get_mTrack2Vol(), call_sndGetSfxSlotFirstNaturalVolume(),
|
||||
sysLogPrintf(LOG_INFO, "wsprobe: pre music=%d fx=%d t1=%d save.music=%d save.fx=%d (folder %d, %d queued)",
|
||||
get_mTrack2Vol(), call_sndGetSfxSlotFirstNaturalVolume(), musicTrack1GetVolume(),
|
||||
save ? (int)save->music_vol : -1, save ? (int)save->sfx_vol : -1,
|
||||
(int)selected_folder_num, n);
|
||||
watchSettingsSet(WATCH_SETTING_MUSIC, 4096, 1);
|
||||
@@ -456,8 +484,8 @@ void watchSettingsGameTick(void)
|
||||
} else if (wsProbePhase == 1 && wsProbeTickN++ == (size_t)wsProbeTick + 1) {
|
||||
wsProbePhase = 2;
|
||||
save_data *save = validSave(selected_folder_num);
|
||||
sysLogPrintf(LOG_INFO, "wsprobe: post music=%d fx=%d save.music=%d save.fx=%d",
|
||||
get_mTrack2Vol(), call_sndGetSfxSlotFirstNaturalVolume(),
|
||||
sysLogPrintf(LOG_INFO, "wsprobe: post music=%d fx=%d t1=%d save.music=%d save.fx=%d",
|
||||
get_mTrack2Vol(), call_sndGetSfxSlotFirstNaturalVolume(), musicTrack1GetVolume(),
|
||||
save ? (int)save->music_vol : -1, save ? (int)save->sfx_vol : -1);
|
||||
}
|
||||
int values[WATCH_SETTING_COUNT];
|
||||
|
||||
Reference in new issue
Block a user