CNA-BUG-222: Audio comments describe behaviour the code does not have: SoundEffect::Play's instance limit, FireAndForgetPanState freeing and SoundEffectReader's WAV route

CNA snapshot 3b43bd86  ·  Known Issues › Current bugs  ·  source links pinned to 3b43bd86

✓

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 3b43bd86; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

SoundEffect.hpp promises that Play returns false at an instance limit CNA does not implement, and two internal comments in the audio and content modules contradict the code next to them.

Identifier
CNA-BUG-222
Category
Bug
Subsystem
Documentation & release tooling
Status
Narrowed (partially fixed; describes only what survives)
Verified against
CNA 3b43bd86 (3b43bd8687ed46523d873f9bec4b32bd8481dcb5)
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
SoundEffect::Play() and SoundEffect::Play(float, float, float) documentation; maintainer comments in SoundEffect.cpp, SoundEffectInstance.hpp and SoundEffectContentTypeReader.hpp

Expected behaviour

Comments and Doxygen describe what the code does. For SoundEffect::Play the XNA meaning of a false return is specific: XNA 4.0's SoundEffect.Play(float, float, float) returns false when it catches InstancePlayLimitException (decompiled reference, Microsoft.Xna.Framework.Audio/SoundEffect.cs).

Actual behaviour at TARGET

  1. SoundEffect.hpp documents both Play overloads as returning false “if the instance limit was reached”. SoundEffect::Play(float, float, float) in SoundEffect.cpp checks no limit and CNA has none: it returns false for a disposed effect, a missing native audio handle, a track that cannot be created, bound or started, and always in a build without SOUND_ENABLED.
  2. In SoundEffect.cpp the comment above FireAndForgetPanState says it is “freed in OnFireAndForgetStopped”, and the comment at SetMixerTrackStoppedCallback says the stopped callback frees it. OnFireAndForgetStopped only pushes it onto g_pendingPanStateCleanup; the next Play or process exit frees it. The deferral is deliberate (an AddressSanitizer heap-use-after-free, P11-PAN-002), so the stale comment invites reintroducing the bug.
  3. SoundEffectContentTypeReader.hpp says 8-bit PCM, float and ADPCM are wrapped in a synthetic WAV and decoded through SoundEffect::FromStream. SoundEffectReader::Read in SoundEffectContentTypeReader.cpp converts every format with ConvertXnbSoundToImportedSound and ProcessImportedSoundEffect and constructs through the raw 16-bit PCM buffer constructor.

Source locations

Evidence

Checked by reading at 3b43bd86; nothing executed. The XNA meaning of the return value was read from the decompiled XNA 4.0 SoundEffect. The fire-and-forget pan path is exercised by SoundEffectTest.PlayWithHardPanDoesNotCrash, which does not observe when the pan state is freed.

Narrowed at c6dec91f: 514a6a8c2 (2026-10-21, CNA-DEMOS-ISSUE-073) rewrote the is3D_ member comment in SoundEffectInstance.hpp to describe the latch as the code implements it (Pan may clear it before packet submission and is refused afterwards until Stop); the other three comments are unchanged.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

Comments are not tested. The code paths are covered by SoundEffectTests.cpp and SoundEffectInstanceTests.cpp (SDL3 audio only).

Regression test

No behavioural test applies: correct the three comments, and document the false-return cases SoundEffect::Play really has (or implement an instance limit and keep the XNA wording).

Blast radius

Readers of the public SoundEffect header, who may code against a limit that never triggers, and maintainers of the fire-and-forget pan and XNB sound paths.

Workaround

No workaround is known.

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

Known issues
Bug index