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

CNA snapshot 009d40f5  ·  Known Issues › Current bugs  ·  source links pinned to 009d40f5

✓

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

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.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Known issues
Bug index