CNA-BUG-139: MediaPlayer::Play(Song*) records Duration and PlayCount on the caller's Song instead of the queued copy, and Stop never resets that PlayCount

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.

Play(Song*) hands the caller's Song to PlaySong, so the mixer duration and a PlayCount increment land on the library or content Song while the queue's active copy keeps zero, contrary to MediaLibrary.cpp's comment.

Identifier
CNA-BUG-139
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
Song::getDurationProperty() and Song::getPlayCountProperty() of the caller's Song and of MediaQueue::getActiveSongProperty() after MediaPlayer::Play(Song*)

Expected behaviour

CNA copies the Song into the queue on purpose (FNA's comment: XNA “duplicates the Song object and then assigns a bunch of stuff to it at Play time”), and MediaLibrary.cpp (BuildFromRoots) relies on it: “MediaPlayer::Play() operates on a duplicate … not the library's original Song instance”. The active song in the queue should therefore carry what playback learns, the same way for every Play overload, and Stop should reset the counts it maintains.

Actual behaviour at TARGET

In MediaPlayer.cpp, Play(Song*) calls PlaySong(song) with the caller's object, not the copy it just queued. In a build with a mixer PlaySong overwrites that Song's Duration with the mixer-reported length and increments its PlayCount. Play(const SongCollection&, index) and NextSong pass the queue's copy instead. Consequences:

  • after Play(&song) the queue's active Song reports a zero Duration, after Play(collection) it reports the mixer value;
  • a library Song's PlayCount grows by one on every Play(Song*) and is never reset, because Stop resets PlayCount only on the queue's copies;
  • the MediaLibrary.cpp comment is wrong for this overload.

FNA also passes the caller's Song to PlaySong (so its Duration lands there too) but never increments PlayCount; XNA's queue Song is a new wrapper around the same native media item, whose duration and play count are read from native metadata, so both objects agree.

Source locations

Evidence

Checked by reading at 009d40f5; not executed. FNA read at FNA-XNA/FNA b3551247 (MediaPlayer.cs).

Focused reproduction

Illustrative; not compiled or run (SDL3 audio).

Song s("Content/theme.ogg", "theme");   // or a MediaLibrary song
MediaPlayer::Play(&s);
s.getPlayCountProperty();                                                // 1, grows on every Play(&s)
MediaPlayer::getQueueProperty().getActiveSongProperty()->getDurationProperty(); // zero
MediaPlayer::Stop();                                                     // s keeps its PlayCount

Current tests

PlayEnqueuesADuplicateNotTheOriginalInstance in MediaPlayerTests.cpp checks that setting the original's PlayCount does not change the copy, the opposite direction; nothing checks which object PlaySong updates.

Regression test

With SDL3 audio and the dummy driver: after Play(&song) the queue's active Song has a non-zero Duration and PlayCount 1 while the caller's Song is unchanged (or, if the caller's Song is meant to be updated, document it and reset it in Stop).

Blast radius

Games that display a song's length or play count from the queue's active Song, and MediaLibrary songs whose PlayCount is shown. Playback is unaffected.

Workaround

Read Duration from the Song object the game passed to Play(Song*), or play through a SongCollection.

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

Known issues
Bug index