Repository navigation
fix(libSceAudioOut): reopen the output device the host stopped - #13694
Conversation
SDL marks an opened device as disabled when the backend reports it lost (SDL_OpenedAudioDeviceDisconnected in src/audio/SDL_audio.c of the bundled 3rdparty/SDL2 at 4b69833bc). The flag is set back only when a device is opened again, so a disabled device stays silent until it is closed, and SDL_GetAudioDeviceStatus reports SDL_AUDIO_STOPPED. AudioOut never checked that status for a port's default output, so after the output went away the title's audio stayed silent for the rest of its run while every output call still returned success. Before a default-output port is used, reopen its device when its status is stopped. A failed reopen leaves the port with no device, so it runs as a virtual port, as a port opened with no output does today. Such a port is not retried later.
The default-open harness gains a disconnect mode. It opens the main port on the default output, reports that device as stopped, and writes one buffer. The output must open a replacement device, queue into it and never queue into the stopped one, then close both devices with the port. On a tree without the reopen the mode fails at "the removed default output was not replaced".
A vibration port plays through the controller's own sound card, such as a DualSense over Bluetooth. When the backend disables that device, SDL keeps the port's device disabled until it is closed (see the default output fix), so the port's haptic audio stopped for the rest of the run. Before a vibration port's device is used, close it and open the controller sound card again when its status is stopped. A failed reopen leaves the port with no device, so its output runs as a virtual port, as a vibration port opened with no controller card does today. Such a port is not retried later.
…host stop The vibration harness gains a disconnect mode. It opens a vibration port on the controller sound card, reports that card as stopped, and writes one buffer. The output must open the card again, queue the PCM into the new device and never into the stopped one, then close both devices with the port. On a tree without the reopen the mode fails at "the stopped controller sound card was not reopened".
The reopen of a stopped host output has no console counterpart to test against, so its guest-visible behaviour is recorded as unmeasured.
A context's main host device is not reopened when the host stops it, a gap the branch does not fix. The console's behaviour is unmeasured.
The output path now asks SDL for the status of a port's device. The port lock and resampling harnesses link the shared library with fake device IDs, and without a stub the status query reaches the real SDL, which reports an unknown ID as stopped. The port then reopened its device and the harnesses' expectations about the calls they see broke. Both harnesses answer the query as playing, as the other AudioOut harnesses that stub the device functions do.
Bring the branch up to the current upstream main. The only conflict was the AudioOut debt doc, where both sides added a Functional entry; both are kept.
… harness The default open harness counted SDL_QueueAudio calls by target and no longer required the device id, so a queue to the wrong device passed in every mode. Keep the requirement; it is lifted only while the disconnect mode has removed the default output, where the counts check the target. No existing mode queues audio, so their results are unchanged.
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
|
Windows data point for |
|
Checked the reopen in |
What
An AudioOut port stayed silent for the rest of a title after the host stopped the device it played through. SDL disables an opened device when its backend reports the device lost, and it does not enable it again:
SDL_OpenedAudioDeviceDisconnectedinsrc/audio/SDL_audio.csetsenabledto 0, and only opening a device sets it back to 1 (bundled3rdparty/SDL2at 4b69833bc).SDL_GetAudioDeviceStatusreports such a device asSDL_AUDIO_STOPPED. AnyPS5 never read that status for a port's default output or controller sound card, so the output calls kept returning success while the audio went nowhere.This PR checks the status before a port's device is used and reopens the device when it is stopped:
sceAudioOutOpenwith the main/default route) reopens throughopenDevice;openVibrationDevice.A failed reopen leaves the port with no device, so it runs as a virtual port, as a port opened with no output does today. Such a port is not retried later. The guest-visible API is unchanged: no export, return value or argument changes. The change is host-side only.
Base:
upstream/mainat7ceda1326c, merged into the branch in714d7e40e6. Earlier merges in the history:9b6c28b8bd(of50843f45d9),2bdcad1490(ofac551a129a),01ffa65535(off25279c0) and376d9b6fc2(ofb852c46c7d). All merges are clean.Eight commits on top of
upstream/main, plus the merges:fix(libSceAudioOut): reopen the default output after the host stops ittest(libSceAudioOut): cover the default output reopen after a host stopfix(libSceAudioOut): reopen a controller sound card the host stoppedtest(libSceAudioOut): cover the controller sound card reopen after a host stopdocs(libSceAudioOut): record the host output reopen as debtdocs(libSceAudioOut): record the AudioOut2 main output as debttest(libSceAudioOut): answer the device status query in two harnessestest(libSceAudioOut): keep the queue target check in the default open harnessThe status query stub in the two harnesses makes the fake device IDs of the port-lock and resampling tests report as playing, which matches the upstream behaviour those tests were written against. The default open harness keeps its queue target check in every mode except the disconnect mode, which removes the default output on purpose.
Debt:
docs/dev/debt/libSceAudioOut.mdrecords the host reopen (the console's behaviour is not measured) and a gap this PR does not fix: a context's main host device in AudioOut2 is still opened once and not reopened.Not fixed, observed by reading only:
VK_ERROR_SURFACE_LOST_KHRis not handled inVulkanDevice.check()incore/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cppthrows on any non-success result exceptVK_ERROR_DEVICE_LOST, andpresentLoopincore/libs/prx/libSceVideoOut/src/VideoOutDriver.cppcallsstd::terminateon astd::exception. No headless seam exists to reproduce it, so it is not changed here.Tested
Linux x86_64 (Intel Xeon E5-1620 v2, 8 threads), GCC 12, Ninja,
-DBUILD_TESTING=ON, incremental build of the 30 AudioOut targets (the 27audio_out*andlibSceAudioOuttargets plus the threeaudio_out_host_*tests of upstream main). Head tested:714d7e40e6. The full runs were first done on2bdcad1490(baseac551a129a). The later merges (01ffa65535,376d9b6fc2,714d7e40e6) were each followed by an incremental build of the changed files andctest -R audio_out, which gave 61 of 61 with identical test names every time. Betweenf25279c0and7ceda1326cupstream made no net change to any file undercore/libs/prx/libSceAudioOut, to the libScePad, libc or libkernel sources, or todocs/dev/debt/libSceAudioOut.md; the other upstream changes are in other libraries (for exampleInventoryExports.hppheaders and thelibSceJxrCMake file).ctest -R audio_outon2bdcad1490: 61 of 61 pass (repeated on01ffa65535, see above). That is the 56 existing tests, the 3 upstream host tests, and the 2 new disconnect testsaudio_out_default_open_disconnectandaudio_out_vibration_device_disconnect.ac551a129a): the eight branch-only files (CMakeLists.txt,src/AudioOut.cpp, the five AudioOut tests, the debt doc) set to theirupstream/mainversions, same build tree,ctest -R audio_out: 59 of 59 pass. Per test, the names are identical to the branch run except the two disconnect tests. So the defaults are unchanged. The files were then restored from the branch and checked byte for byte.AudioOutPortLocks.cppandAudioOutResampling.cppfromde0b4c4163, before the status stub.audio_out_independent_output,audio_out_independent_close,audio_out_same_port_closeandaudio_out_resamplingabort: "AudioOut port locks: unknown device polled" (three times) and "resampling discarded the impulse response at the grain boundary". With the stub from4e06acd31athey pass. The stub removes no assertion.AudioOut.cppwith the branch tests):audio_out_default_open_disconnectfails with "the removed default output was not replaced";audio_out_vibration_device_disconnectfails with "the stopped controller sound card was not reopened". Both fail at the first assertion that depends on the reopen. The branch file was restored and both pass again. The later assertions of those two modes (queue into the live device, close count) follow the first failing assertion and were not controlled separately.port.device + 1. With the restored default open harness,audio_out_default_open_disconnectfails with "wrong queue written". With the loosened harness from4e06acd31ait fails with "output was queued to the removed default output". No existing default open mode callssceAudioOutOutput, so their results do not depend on this check. The clone was not modified by the control.python3 tools/env_reference.py --check: the same single failure on a cleanupstream/mainexport and on the branch (APS5_PAD_SPEAKER_FALLBACK_DB has a row in docs/dev/ENVIRONMENT.md but the code no longer reads it), a known baseline failure. The branch adds no new failure.python3 tools/check_conventions.py --base upstream/main: all conventions checks passed.git diff --check upstream/main HEAD: clean.python3 tools/pr_test_quality.py --jsonon the five changed AudioOut test files: five findings of kindpossible-unregistered-cpp-test. These are a path-format false positive: all five are registered incore/libs/prx/libSceAudioOut/CMakeLists.txtwith paths relative to that directory (tests/AudioOutBufferSize.cppetc.), and all five run in ctest.python3 tools/pr_overlap.py --branch fix/host-device-changes: exit 2 (fails closed). The tool reports 10 open PRs sharing files with the branch, and could not compare three of them because they conflict withmain: feat(aot): consolidate native ARM64 compiler, runtime and consumers #5021, feat(pad): stream vibration ports to a Bluetooth DualSense as haptics reports #9154, feat(libSceVideoOut): opt-in host pause with a key or on focus loss #12863. The rest of the report is in the tool output. Manual comparison of the three, against the PR metadata from GitHub:feat(aot): consolidate native ARM64 compiler, runtime and consumers): an AOT compiler PR. Its metadata lists 100 files, the GitHub list limit, none of them underlibSceAudioOut; the tool reports it touchesCMakeLists.txtand the debt doc. Its audio-related files beyond that list were not inspected.feat(pad): stream vibration ports to a Bluetooth DualSense as haptics reports): changesAudioOut.cppinoutputVirtualPortandAudioOut2Context.cpp. Its diff contains no device-status or reopen logic. It is in a different function from this PR's changes.feat(libSceVideoOut): opt-in host pause with a key or on focus loss): changesAudioOut.cppinqueueAudio,AudioOut2Context.cpp,AudioOutBufferSize.cpp, the debt doc and newHostPausefiles. Its diff contains no device-status or reopen logic. It is in a different function from this PR's changes.gh: fix(libSceAudioOut): return from a blocking AudioOut2 push once the queue has room #12954 (fix(libSceAudioOut): return from a blocking AudioOut2 push once the queue has room) is MERGED and touchesAudioOut2Context.cppand the debt doc. feat(libSceAudioOut): host volume and mute hotkeys, opt-in mute on focus loss #12569 (host volume and mute) is MERGED and is part of currentmain. fix(agc): keep presenting after a SUBOPTIMAL present #9881, feat(videoout): add ANYPS5_FPS_LIMIT to cap the rate the window presents #12536, feat(libSceVideoOut): limit the guest display to the host resolution #11948 are OPEN and touch no AudioOut file. feat(libSceVideoOut): validate ANYPS5_FULLSCREEN, restore the windowed size and hide the cursor in fullscreen #12930 is OPEN and touches onlydocs/dev/debt/libSceVideoOut.md.NOT RUN: the rest of the ctest suite (only
audio_out*was run), Windows (WASAPI default-device path), macOS, real devices (no Bluetooth controller, no audio hotplug), console, GPUs and the Xvfb VUID run (no presentation code changed), performance.Checklist
main; no other open PR implements the same functions(based on
7ceda1326c, tested and overlap-checked on it;mainhad moved to813496001e(two commits, none changing a file this PR touches, merge clean) when the PR was opened; the overlap comparison above found no other open PR with the same device-status logic)NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)