diff --git a/mpcore/src/main/cpp/emu/fmod_shims.cpp b/mpcore/src/main/cpp/emu/fmod_shims.cpp index 5473028..39fae87 100644 --- a/mpcore/src/main/cpp/emu/fmod_shims.cpp +++ b/mpcore/src/main/cpp/emu/fmod_shims.cpp @@ -1,5 +1,7 @@ #include "fmod_shims.h" #include +#include +#include namespace { @@ -256,9 +258,37 @@ uint32_t Shim_System_getSoftwareFormat(GuestEngine& eng, uint32_t, uint32_t out1 } uint32_t Shim_System_setSoftwareFormat(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; } uint32_t Shim_System_set3DSpeakerPosition(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; } +// FIX (2026-09-19, task #49). Channels handed out by System::playSound and +// not yet stopped. Without this the music never played: playSound reported +// success and returned a fake channel, then Channel::isPlaying answered +// FALSE for it, so the game concluded the track had finished and immediately +// started the next one - forever. Measured in the pause menu: a different +// track roughly every 1.5s, picked non-sequentially (DEAD SARA, DEAD SARA, +// GREEN DAY, SKRILLEX, GREEN DAY, CROSSES, ICONA POP, JOY FORMIDABLE), so +// not a walk through playlists.sb but a fresh selection each time. +// +// Third instance today of the same failure shape - a shim reporting SUCCESS +// while handing back an empty/negative value (see AndroidBitmap_lockPixels +// in gles_shim.cpp, task #41). Hence the log below: this engine has no audio +// backend, so playback here is simulated, and that fact belongs in the log +// rather than only in a comment. +std::set g_playingChannels; +std::mutex g_playingChannelsMutex; + uint32_t Shim_System_playSound(GuestEngine& eng, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t sp) { uint32_t out = eng.ReadIncomingArg(4, 0, 0, 0, 0, sp); // 5th total arg (this,channelid,sound,paused,+channel**) - OutPtr(eng, out, FakeHandle(eng)); + GuestAddr channel = FakeHandle(eng); + OutPtr(eng, out, channel); + { + std::lock_guard lock(g_playingChannelsMutex); + g_playingChannels.insert(channel); + } + static std::once_flag once; + std::call_once(once, [] { + Log("fmod_shims: no audio backend - System::playSound will report channels as PLAYING until " + "the guest stops them. Sound is silent by design; without this the guest sees every track " + "finish instantly and thrashes through the playlist (task #49)."); + }); return kFmodOk; } uint32_t Shim_System_setOutput(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; } @@ -266,14 +296,29 @@ uint32_t Shim_System_setOutput(GuestEngine&, uint32_t, uint32_t, uint32_t, uint3 // ---- Channel ---- uint32_t Shim_Channel_setCallback(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; } uint32_t Shim_Channel_setPriority(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; } -uint32_t Shim_Channel_stop(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; } +uint32_t Shim_Channel_stop(GuestEngine&, uint32_t r0, uint32_t, uint32_t, uint32_t, uint32_t) { + // r0 is `this`. Dropping it here is what lets the guest end a track on its + // own terms - the next isPlaying() then honestly answers false, so normal + // transitions (race ends, menu change) still work. + std::lock_guard lock(g_playingChannelsMutex); + g_playingChannels.erase(r0); + return kFmodOk; +} uint32_t Shim_Channel_setMute(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; } uint32_t Shim_Channel_getPaused(GuestEngine& eng, uint32_t, uint32_t out, uint32_t, uint32_t, uint32_t) { OutBool(eng, out, false); return kFmodOk; } -uint32_t Shim_Channel_isPlaying(GuestEngine& eng, uint32_t, uint32_t out, uint32_t, uint32_t, uint32_t) { - OutBool(eng, out, false); +uint32_t Shim_Channel_isPlaying(GuestEngine& eng, uint32_t r0, uint32_t out, uint32_t, uint32_t, uint32_t) { + // See g_playingChannels' comment. Answering "playing" only for channels we + // actually handed out - rather than a blanket true - keeps a stale or + // never-started channel honest. + bool playing; + { + std::lock_guard lock(g_playingChannelsMutex); + playing = g_playingChannels.count(r0) != 0; + } + OutBool(eng, out, playing); return kFmodOk; } uint32_t Shim_Channel_setPaused(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; }