CNA-BUG-138: MediaPlayer flags ActiveSongChanged on every Play(Song*), even for the same song, and never for Play(const SongCollection&[, index])
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*) compares the queue's copy with the caller's pointer, so the event fires on every call, while playing a collection never raises it even when the active song changes.
- Identifier
CNA-BUG-138- 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::ActiveSongChanged as raised by MediaPlayer::Play(Song*), Play(const SongCollection&) and Play(const SongCollection&, intcs)
Expected behaviour
MediaPlayer.hpp documents the event as “Raised when the active song changes”. XNA raises it from the native player whenever its active song changes (the Media_ActiveSongChanged managed call dispatched by FrameworkDispatcher, decompiled reference), which includes starting a collection; whether XNA raises it when the same song is replayed is decided in native code the managed IL does not show. FNA's Play(Song), which CNA's code transcribes, raises it only when previousSong != song under Song's overloaded operator, that is, when the file differs.
Actual behaviour at TARGET
Play(Song*)inMediaPlayer.cppkeepsSong* previousSong = queue_[0]and flags the event whenpreviousSong != song. The queue holds copies made byLoadSong, so a raw pointer comparison with the caller's Song is always unequal (except in the use-after-free case of the entry on queue-owned songs): replaying the same song raisesActiveSongChangedevery time. CNA'sSong::Equalscompares handles as FNA's operator does, but is not used here.Play(const SongCollection&, index)clears and refills the queue and callsPlaySongwithout settingFrameworkDispatcher::ActiveSongChanged. OnlyMediaStateChangedcan fire, and only if the state changes, so switching from one playlist to another while music plays raises neither event.NextSongand the end-of-queue path inUpdatedo set the flag. FNA's overload has the same omission.
Source locations
modules/media/src/Xna/MediaPlayer.cpp— MediaPlayer::Play(Song*), Play(const SongCollection&, intcs), NextSong, Updatemodules/media/include/Microsoft/Xna/Framework/Media/MediaPlayer.hpp— ActiveSongChanged documentationmodules/media/src/Xna/Song.cpp— Song::Equals and operator== compare handlesmodules/media/tests/Microsoft/Xna/Framework/Media/MediaPlayerTests.cpp— ActiveSongChangedAndMediaStateChangedFireThroughFrameworkDispatcherUpdate
Evidence
Checked by reading at 009d40f5; not executed. FNA was read at FNA-XNA/FNA b3551247 (MediaPlayer.cs, Song.cs); XNA's dispatch path from the decompiled FrameworkDispatcher and the native callback shim.
Focused reproduction
Illustrative; not compiled or run.
Song a("Content/a.ogg", "a");
MediaPlayer::Play(&a);
FrameworkDispatcher::Update(); // ActiveSongChanged (expected)
MediaPlayer::Play(&a);
FrameworkDispatcher::Update(); // ActiveSongChanged again: same file (FNA: no event)
MediaPlayer::Play(otherPlaylist);
FrameworkDispatcher::Update(); // no ActiveSongChanged although the song changed
Current tests
ActiveSongChangedAndMediaStateChangedFireThroughFrameworkDispatcherUpdate in MediaPlayerTests.cpp covers one Play(Song*) from an empty queue. No test replays a song or plays a collection and checks the event.
Regression test
Two dispatcher-driven cases: replaying the same Song raises no ActiveSongChanged (compare with Equals), and Play(collection) while another song is active raises it once.
Blast radius
Games that update a now-playing display or statistics from ActiveSongChanged. Playback itself is unaffected.
Workaround
Track the active song in the game (compare getActiveSongProperty() handles each frame) instead of relying on the event.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Known issues
- Bug index