Repository navigation
Mix for 5.1 when the default output has 5.1 speakers - #1182
Merged
Merged
Conversation
The game asks once for the output's channel count (GetActiveAudioDeviceChannelCountForOutput) and pans for that many speakers. Hookshot always answered 2, so nothing was ever placed behind the player and a 5.1 system played the whole game from the front pair. Now the answer is the default output's: its shared-mode mix format through IMMDevice/IAudioClient on Windows, SDL3's SDL_GetAudioDeviceFormat or SDL2's SDL_GetDefaultAudioInfo on Linux. With 6 or more channels the game is told 5.1, the device sink's six channels go out as they are (FL FR FC LFE BL BR, the same order on Windows and SDL), and the device is opened with six channels (WAVE_FORMAT_EXTENSIBLE with the speaker mask on Windows). Otherwise, or when a 5.1 open fails, it stays stereo with the existing downmix. The limiter uses one gain for every channel. HOS_AUDIO_CHANNELS=2 or 6 overrides the choice. SDL3's stream takes only whole sample frames: the pull now hands it pieces that are whole 12-byte 5.1 frames (4096 bytes is not, and every piece was refused). Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 tasks done
|
Please add menu options so we can pick 2.0 or 5.1 output. |
janvitos
added a commit
to janvitos/Hookshot
that referenced
this pull request
Nov 9, 2026
A follow-up to NotRexed#1182, as suggested in its review: the speakers the game mixes for can be chosen in the Insert menu (Gameplay), not only with HOS_AUDIO_CHANNELS. Auto keeps NotRexed#1182's behaviour (5.1 when the default output has 5.1 or more, else stereo); Stereo or 5.1 chooses one whatever the device has. The choice is saved as audio_output= in hookshot_options.ini and read by host/nn_audio.c from the file, as nn_applet.c reads the Handheld profile: the game asks for its speakers once, at its start. The row is a restart row (Row::restart), so the menu marks a change "(after restart)" and offers a restart on closing; each value has a hint line. HOS_AUDIO_CHANNELS still wins over the menu. tests/test_audio_output_menu.py compiles the real au_speakers with the menu's ini_key, value texts and hints, so the key the menu saves is the one the host reads, and checks the row is a preset-exempt restart row. Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
github-actions Bot
pushed a commit
to notquiteog/Hookshot
that referenced
this pull request
Nov 9, 2026
## [2.0.12](v2.0.11...v2.0.12) (2026-11-09) ### Features * a timed span per FSR pass, so the cost has a number per pass ([1d7f262](1d7f262)) * add a Quit option to the gameplay menu ([0b02149](0b02149)) * **audio:** Audio output in the Insert menu: Auto, Stereo or 5.1 ([4856258](4856258)), closes [NotRexed#1182](https://github.com/notquiteog/Hookshot/issues/1182) * census the render target sets, to settle whether a G-buffer exists ([8f2fda3](8f2fda3)) * compile AMD's FSR 3.1 upscaler passes from the FidelityFX SDK ([37afa2e](37afa2e)), closes [NotRexed#577](https://github.com/notquiteog/Hookshot/issues/577) * compose the scene's view projection, and transpose it for the shader ([df82d90](df82d90)) * count FSR evaluations, because nine passes a call is cheap or ruinous ([85a3ea2](85a3ea2)) * create FSR 3.1's pipelines and internal images ([6b51433](6b51433)) * derive camera motion from the scene's depth, and keep that depth ([29c7cbe](29c7cbe)) * DLSS at the seam, and a stage switch for the FSR cost ([6aa9f48](6aa9f48)) * DLSS Super Resolution through NGX, behind a build-time SDK gate ([aff6abb](aff6abb)) * frame generation asks for its own extensions and the whole input set ([ad291d1](ad291d1)) * FSR 3 frame interpolation, and a jitter for the upscalers to undo ([384c41b](384c41b)) * give FSR the assembled depth and camera motion ([481b957](481b957)) * HOS_DISPLAY_VRR, which is what actually holds the frame rate at 60 ([c65c68e](c65c68e)) * HOS_FPS_FORCE, because raising the limiter alone does not hold ([2008ac7](2008ac7)) * HOS_VSYNC_HZ, the hardcoded 60 that actually paces the game ([5eb9f8b](5eb9f8b)) * link NGX properly, frame generation at the seam, AMD's interpolation sources ([43e3360](43e3360)) * Monitor's shape for screens narrower than 16:9 (4:3, 16:10, 5:4) ([b44c9cd](b44c9cd)) * name each network binding's role in the upscaler input contract ([d9566a1](d9566a1)) * publish Ocarina's camera, and make a backend reachable without the stand-in ([8bd7c8d](8bd7c8d)) * record FSR 3.1's nine dispatches ([8769c80](8769c80)) * route the game's upscale dispatch through one backend seam ([1a036c8](1a036c8)) * RX_VK_VP_HUNT, to find the view projection from a value only it holds ([3c02213](3c02213)) * the hunt found the projection, so widen it to find the view ([2946a32](2946a32)) * the parallel lock says which site took it, and the answer is two sites ([b8543c9](b8543c9)) * the two backends have rows in the Insert menu ([cf8b3b1](cf8b3b1)) * the two lock sites say why they had to take it ([614f552](614f552)) * the upscaler and frame generation follow the menu while you play ([61d7d4a](61d7d4a)) * the upscalers and frame generation are the native settings, not new rows ([a0ce7de](a0ce7de)) * the vsync event follows the display, and 0 turns it off ([a67e471](a67e471)) * **vk:** AMD FSR 4 upscaling and frame generation through AMD's runtime ([65003cc](65003cc)) * **vk:** choose the frame generation method in the Insert menu ([c4f7d84](c4f7d84)) * **vk:** DLSS 5 neural rendering through NGX directly, beside Streamline ([7784399](7784399)) * **vk:** DLSS 5 neural rendering, with the model's own settings in the menu ([2851bc6](2851bc6)) * **vk:** frame generation Auto: a frame for each of the display's refreshes ([a5a7440](a5a7440)) * **vk:** say why DLSS 5 neural rendering is refused, and the RTX 20-40 route ([60f1d2e](60f1d2e)) * **vk:** time a capture's GPU wait for its late pages apart in the log ([e3b622f](e3b622f)) ### Bug Fixes * a fork releases its own work, not upstream's ([47dd097](47dd097)) * a Windows build could ask for DLSS and then fail to link ([d72441a](d72441a)) * **audio:** 5.1 surround when the default output has it ([NotRexed#1124](https://github.com/notquiteog/Hookshot/issues/1124)) ([b0abeca](b0abeca)) * **audio:** a finished wave buffer reaches the game at the renderer update, not mid-pass ([28e4a11](28e4a11)) * **build:** a Windows build linked the ARM64 NGX library, not the x64 one ([70b7d25](70b7d25)) * **builder:** copies from a read-only install stay writable ([70ffa5e](70ffa5e)) * **builder:** stop when generated source cleanup fails ([d254e6f](d254e6f)) * **builder:** the window icon is copied writable too ([4b2d871](4b2d871)) * **build:** NGX asks for Microsoft's C++ runtime, which llvm-mingw has not ([114e917](114e917)) * **build:** the feature announcement swallowed the whole Vulkan build ([1004f37](1004f37)) * **build:** the twelve MSVC runtime symbols NGX asks of an llvm-mingw link ([d00159a](d00159a)) * clear the SPD counter between the pyramid passes (did not fix the cost) ([66e8c3a](66e8c3a)) * contain pipeline cache save allocation failures ([4518a20](4518a20)), closes [NotRexed#1022](https://github.com/notquiteog/Hookshot/issues/1022) * HOS_FPS_FORCE must not touch the nominal rate: it is slow motion on screen ([8681f2e](8681f2e)) * **lift:** clear an earlier lift's hot pieces before the patches; count every made and raw output-size place ([816a472](816a472)), closes [NotRexed#912](https://github.com/notquiteog/Hookshot/issues/912) * **lift:** edit_c in chunks.py, so its tests import without the lift's tools ([1c5258f](1c5258f)) * **lift:** group page marks after the patches; a missing output-size edit stops the lift ([3fa6300](3fa6300)) * **linux:** the AppImage's parts list names the repository it is built in ([2228816](2228816)) * narrow screens: window at the picture's shape, save picture's middle band, comments ([3060261](3060261)), closes [NotRexed#1183](https://github.com/notquiteog/Hookshot/issues/1183) * preserve NUP member names during implicit reads ([1fdfeff](1fdfeff)) * publish the camera from the inlined projection copies too ([c630d59](c630d59)) * read the upscaler contract in descriptor order, not from draw_sampled ([492bcd0](492bcd0)) * **release:** the fork's releases follow upstream's v2.0.x line ([d495994](d495994)) * **release:** the setup, the Builder and the game update from the repository they were built in ([6fd4364](6fd4364)) * **release:** the Windows release installs the renderer CI built, with DLSS, NR and FSR 4 ([0fa18bd](0fa18bd)) * scan for the view projection once a target set, not once a draw ([12d4d83](12d4d83)) * snapshot the projection's fields, and name what caps the frame rate ([3790556](3790556)) * the conventionalcommits preset has to match the changelog writer ([ca6595f](ca6595f)) * the DLSS contract test links again ([86b6988](86b6988)) * the game's network takes one texture, so it is not a temporal upscaler ([853cfe3](853cfe3)) * the history reset the document specifies, which nothing implemented ([9858599](9858599)) * the host camera symbols must be weak, or the renderer does not load at all ([70ecdf4](70ecdf4)) * the release's commit-analyzer preset is a package, and it was not declared ([3ff9fbd](3ff9fbd)) * the renderer would not load, and the build now says so ([c871a3c](c871a3c)) * the shader check compared glslang's version, and ran where glslang is not ([f48483c](f48483c)) * the view projection scan ran per draw, which stuttered hard ([2d9b4e8](2d9b4e8)) * **vk:** a draw that waits for its pipeline runs the build at once if it is still queued ([aafbaf5](aafbaf5)) * **vk:** DLSS frame generation sets NGX's parameters through its C functions ([2c19b63](2c19b63)) * **vk:** frame generation Auto: the snap distance is not named near (windows.h defines it empty) ([d156a6d](d156a6d)) * **vk:** keep the last sRGB UNORM surface format, so drivers that list only sRGB keep theirs ([3408437](3408437)) * **vk:** Linux/Wayland: take an sRGB surface format for the swapchain ([7416487](7416487)) * **vk:** no mirror is made inside a parallel replay's segment (an earlier segment read it unfilled) ([8f0ed68](8f0ed68)) * **vk:** tag the uplift colour pair as whole resources, as the shipping integration does ([dd3e61e](dd3e61e)) * **vulkan:** bind only bytes accessed by buffer-image copies ([b0a9034](b0a9034)) * **vulkan:** clean up failed mesh argument initialization ([b2f5692](b2f5692)) * **vulkan:** preserve guest mip levels in partial clears ([d7a1207](d7a1207)) * **vulkan:** release modules from rejected cache records ([2838e89](2838e89)) * **widescreen:** the save picture shows the middle 16:9 on ultrawide ([NotRexed#952](https://github.com/notquiteog/Hookshot/issues/952)) ([e2e155e](e2e155e)) ### Performance Improvements * **fpx:** keep the hardware result for FMUL/FMLA S when the flags are already set ([6de3bf1](6de3bf1)) * **lift:** one page mark per straight-line group of stores through a base register ([825861b](825861b)) * **lift:** the interrupt poll before each backward goto, calling a64_poll_cur() with no argument, so a loop's guest registers stay in host registers ([0d6ed03](0d6ed03)) * stream sampled profiles instead of loading every line ([8cf6d1e](8cf6d1e))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1124.
What
Hookshot now gives the game the default output's real channel count, so it mixes 5.1 when the output has 5.1.
SDL_GetAudioDeviceFormat, or SDL2'sSDL_GetDefaultAudioInfo. That call is looked up at runtime, so an older SDL still plays, in stereo.WAVE_FORMAT_EXTENSIBLEwith the speaker mask.HOS_AUDIO_CHANNELS=2or6, documented with the otherHOS_AUDIO_*switches at the top ofnn_audio.c.Why
The game asks once for the output's channel count (
GetActiveAudioDeviceChannelCountForOutput) and pans for that many speakers. Hookshot always answered 2, so nothing was ever placed behind the player. On a 5.1 system the whole game came from the front speakers; in the issue, Epona's hooves in the intro are "but a whisper" instead of behind you.How it was tested
audio: 5.1 for the game (the default output has 6 channels). In gameplay all six channels carry sound, centre, LFE and rears included.tests/test_audio_output_errors.pyfollows the newrender_framesignature. On this branch (rebased on 1f6fbdb),python -I -B tests/run_all.pygives 131 passed, 0 failed, 17 skipped on Linux.During testing I also hit a speaker channel going silent at random, in stereo too. That is a separate bug with a separate fix in #1181. This PR doesn't depend on it.
🤖 Generated with Claude Code