CNA-BUG-140: MediaPlayer::Play reports no error when a song cannot be played and can leave the player Playing with no track

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.

A mixer load, track or start failure makes PlaySong return silently after the previous track was destroyed, so the state may stay Playing indefinitely, and a vanished file or a missing audio device throws non-XNA exceptions after the queue was replaced.

Identifier
CNA-BUG-140
Category
Bug
Subsystem
Audio & media
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Severity
Medium (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::Play(Song*), Play(const SongCollection&), Play(const SongCollection&, intcs), MoveNext and MovePrevious: failure behaviour

Expected behaviour

XNA's MediaQueue.Play(Song) throws InvalidOperationException (SongPlaybackFailed) when the native player cannot play the song (decompiled reference). Whatever CNA chooses, a failed Play should leave a consistent player: either an exception and the previous state, or Stopped. MediaPlayer.hpp documents no failure mode.

Actual behaviour at TARGET

Three failure paths in MediaPlayer.cpp:

  1. Undecodable or unstartable song, silently. In a build with a mixer, PlaySong first destroys the current music track and audio and clears the song-ended flag, then returns without an error if LoadMixerAudioFile yields nothing (an unsupported format, a corrupt file), if no track can be created or bound, or if PlayMixerTrack fails. The timer and state are not touched: from Stopped the player stays Stopped with the song queued as active; from Playing it stays Playing with no track, PlayPosition keeps counting from the previous song, and Update never advances because the ended flag can no longer be set; from Paused, a later Resume reports Playing with nothing playing. Neither mixer's error text reaches the caller.
  2. No audio device: a raw std::runtime_error. The music load is the first mixer use in a game that plays music before any sound effect. On SDL3 audio LoadMixerAudioFile reaches GetMixer, on ALSA EnsureMixer, and either throws std::runtime_error out of Play after the queue was replaced. SoundEffect's constructors convert the same failure to NoAudioHardwareException; MediaPlayer does not.
  3. Vanished file. LoadSong copies the song with new Song(handle, name), whose constructor (Song.cpp) throws FileNotFoundException if the file no longer exists. The copy happens after queue_.Clear(), so the exception leaves an empty queue (or a partly refilled one for a collection) while the previous track keeps playing in the Playing state. FNA's LoadSong has the same shape.

Source locations

Evidence

Checked by reading at 009d40f5; not executed. The XNA behaviour was read from the decompiled MediaQueue.Play; FNA's LoadSong from FNA-XNA/FNA b3551247. The ALSA mixer records a decode error string in its engine that nothing in the media module reads.

Focused reproduction

Illustrative; not compiled or run (SDL3 audio, vendored SDL3_mixer).

Song music("Content/music.ogg", "music");
Song voice("Content/voice.opus", "voice");     // Opus is not decoded by this build
MediaPlayer::Play(&music);                     // Playing
MediaPlayer::Play(&voice);                     // music stops, nothing plays, no exception
MediaPlayer::getStateProperty();                  // still MediaState::Playing, indefinitely

Current tests

MediaPlayerTests.cpp plays only decodable fixtures with SDL_AUDIODRIVER=dummy; its own comment on VisualizationEnabledStateStaysConsistentWithGetVisualizationData notes that mixer creation therefore never fails in the suite. No test plays an undecodable or deleted file or runs without an audio device.

Regression test

With a song playing, Play an undecodable file (a zero-byte .ogg is enough) and require a defined outcome: an InvalidOperationException with the previous song still active, or Stopped, never Playing without a track. Add the same for a file deleted between constructing the Song and calling Play.

Blast radius

Games playing unsupported formats (see CNA-BUG-052), corrupt or deleted files, and games started on machines without an audio output device (SDL3 and ALSA builds). Music loaded from decodable files on a working device is unaffected. In builds without a mixer (SDL2 or NULL audio) PlaySong has no load step, so only the vanished-file path applies there.

Workaround

Use the four decodable formats, and check MediaPlayer::getStateProperty() together with a game-side timer rather than trusting Playing; catch std::exception around the first Play.

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

Known issues
Bug index