CNA-BUG-142: Without a mixer (SDL2 or NULL audio) songs never end: queued copies carry no Duration, so the elapsed-time fallback never fires
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
LoadSong copies only a Song's file and name, so every queued Song has a zero Duration and, in builds without SOUND_ENABLED, DetectSongEndedByElapsedTime never reports an end: MediaPlayer stays Playing and the queue never advances.
- Identifier
CNA-BUG-142- Category
- Bug
- Subsystem
- Audio & media
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- MediaPlayer::Update() (via FrameworkDispatcher::Update), MediaState, ActiveSongChanged and queue advance under CNA_AUDIO_PLATFORM=SDL2 or NULL
Expected behaviour
DetectSongEndedByElapsedTime is documented in MediaPlayer.hpp as the song-end detector for builds without a native track-stopped signal, reporting an end whenever the duration is genuinely known. A Song loaded from .xnb carries its duration (Song(path, name, durationMS)), as do library songs whose duration was probed, so their end should be detected and the queue should advance as in XNA and FNA.
Actual behaviour at TARGET
modules/CMakeLists.txt defines SOUND_ENABLED only for SDL3 and ALSA audio. In the other builds Update in MediaPlayer.cpp asks DetectSongEndedByElapsedTime(activeSong, TimerElapsed()), where activeSong is the queue's copy. LoadSong builds that copy with new Song(song->getHandle(), song->getNameProperty()), dropping the duration, and PlaySong sets none without a mixer. The detector ignores a zero duration, so it never fires: the player stays Playing forever, MediaStateChanged and ActiveSongChanged are never raised for a natural end, and repeat, shuffle and queue advance never happen. The fallback is reachable only by calling the static function directly, which is what its tests do.
Source locations
modules/media/src/Xna/MediaPlayer.cpp— MediaPlayer::LoadSong, PlaySong (non-SOUND_ENABLED branch), Update, DetectSongEndedByElapsedTimemodules/media/include/Microsoft/Xna/Framework/Media/MediaPlayer.hpp— DetectSongEndedByElapsedTime documentationmodules/CMakeLists.txt— SOUND_ENABLED only for CNA_AUDIO_PLATFORM SDL3 and ALSAmodules/media/tests/Microsoft/Xna/Framework/Media/MediaPlayerTests.cpp— MediaPlayerNoSoundFallbackTest cases call the function directly
Evidence
Checked by reading at 009d40f5; not executed. The copy never receives a duration on this path in any configuration, so the conclusion does not depend on timing.
Independently observed as a separate finding (merged): LoadSong queues a copy built from the Song's handle and name only, so it has no Duration, and the elapsed-time end detection used in builds without SOUND_ENABLED never fires.
Focused reproduction
Illustrative; not compiled or run (CNA_AUDIO_PLATFORM=NULL).
Song s("Content/jingle.ogg", "jingle", 500); // known 0.5 s duration
MediaPlayer::Play(&s);
// ... one second of frames calling FrameworkDispatcher::Update()
MediaPlayer::getStateProperty(); // still MediaState::Playing
Current tests
The four MediaPlayerNoSoundFallbackTest cases in MediaPlayerTests.cpp call DetectSongEndedByElapsedTime with a hand-built Song that has a duration; none plays a Song through Play and Update in a no-mixer build.
Regression test
In a NULL-audio build: Play a Song constructed with a 100 ms duration, advance Update past 100 ms and require Stopped (or the next song). The fix is for LoadSong to copy the duration.
Blast radius
SDL2 and NULL audio selections only, where music is silent anyway: games whose logic waits for a song to end, playlists, repeat and shuffle. SDL3 and ALSA builds end songs through the mixer's stopped callback.
Workaround
End or advance songs from game code with a timer in these profiles.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- Platforms: audio selections
- Known issues
- Bug index