Fix music never playing - game speed nearly doubled (task #49)
Channel::isPlaying answered FALSE for a channel System::playSound had just
handed out with a success code. The guest polled it, concluded the track
had finished, and started the next one - forever. Each cycle reopened the
OBB asset filesystem ON THE GLThread, stalling frames directly.
Found from a user observation ("треки меняются очень быстро в меню паузы"),
then measured: 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.
Now tracks the channels playSound hands out and reports those as playing
until Channel::stop drops them, so normal transitions still work. A
one-time log states plainly that there is no audio backend and playback is
simulated, instead of leaving that only in a comment.
Measured on the Pixel 6a with the race's own clock, same method both times:
game time vs wall clock 7.69x -> 3.99x
useAssetsFileSystem 1.00/s -> 0.00/s (in-race, fully stopped)
faults 0 -> 0
27s of wall time advanced the race timer 18.82 -> 25.58 (6.76s); the two
sub-intervals gave 4.18x and 3.81x.
Third instance today of one failure shape: a shim reporting SUCCESS while
handing back an empty or negative value. See AndroidBitmap_lockPixels
(task #41) and the synthetic-swap premise (task #47).
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
#include "fmod_shims.h"
|
#include "fmod_shims.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace {
|
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_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; }
|
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<uint32_t> 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 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**)
|
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<std::mutex> 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;
|
return kFmodOk;
|
||||||
}
|
}
|
||||||
uint32_t Shim_System_setOutput(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { 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 ----
|
// ---- Channel ----
|
||||||
uint32_t Shim_Channel_setCallback(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; }
|
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_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<std::mutex> 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_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) {
|
uint32_t Shim_Channel_getPaused(GuestEngine& eng, uint32_t, uint32_t out, uint32_t, uint32_t, uint32_t) {
|
||||||
OutBool(eng, out, false);
|
OutBool(eng, out, false);
|
||||||
return kFmodOk;
|
return kFmodOk;
|
||||||
}
|
}
|
||||||
uint32_t Shim_Channel_isPlaying(GuestEngine& eng, uint32_t, uint32_t out, uint32_t, uint32_t, uint32_t) {
|
uint32_t Shim_Channel_isPlaying(GuestEngine& eng, uint32_t r0, uint32_t out, uint32_t, uint32_t, uint32_t) {
|
||||||
OutBool(eng, out, false);
|
// 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<std::mutex> lock(g_playingChannelsMutex);
|
||||||
|
playing = g_playingChannels.count(r0) != 0;
|
||||||
|
}
|
||||||
|
OutBool(eng, out, playing);
|
||||||
return kFmodOk;
|
return kFmodOk;
|
||||||
}
|
}
|
||||||
uint32_t Shim_Channel_setPaused(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; }
|
uint32_t Shim_Channel_setPaused(GuestEngine&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { return kFmodOk; }
|
||||||
|
|||||||
Reference in New Issue
Block a user