Skip to content

feat(audio): add live mono audio for stereo output - #2860

Open
simonwjackson wants to merge 1 commit into
NotRexed:mainfrom
simonwjackson:feat-mono-audio
Open

simonwjackson wants to merge 1 commit into
NotRexed:mainfrom
simonwjackson:feat-mono-audio

Conversation

@simonwjackson

@simonwjackson simonwjackson commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Players who hear with one ear, or use one earbud, miss sounds that the game pans to the other side. This adds an opt-in Mono audio row that plays the average of the left and right channels in both ears (Game Accessibility Guidelines, hearing, intermediate).

What changed

  • Insert → Audio → Mono audio: Off / On, default Off. It applies at once, with no restart, and is saved like the other rows.
  • host/nn_audio.c averages the final stereo PCM with a 32-bit sum, so full-scale samples cannot overflow. With the row Off every sample is untouched. 5.1 output is unchanged; the row's help says to choose Stereo.
  • Option ID 53 (main uses 46 for Button prompts and 52 for Skip opening). Neither MkwVkHost nor MkwVkRenderer changes, so MKWVK_API_VERSION stays at 20.
  • A README row, tests/test_audio_mono.py, and one row in docs/tested-configurations.md.

Audio tab, Mono audio On and Stereo output, 1920x1080

Run in the game

One 306-second run, standing on Link's house balcony in Kokiri Forest, from copied saves. RX 7900 XT, RADV (Mesa 26.2.3), Ryzen 5 7600X, 32 GB, NixOS 26.11 / Linux 7.2.7, headless Sway 1.12 + Xwayland. 2560x1440 output, FSR 3.1 from 1707x960, 1920x1080 window, frame generation 2x, compute mesh path, udmabuf on.

The row was switched Off → On → Off in the menu without a restart. The host's post-mix stereo PCM was captured at 48 kHz for 30 seconds in each state (HOS_AUDIO=0, so nothing played on speakers):

State Frames Frames with left ≠ right Non-silent frames Game fps, median of 16 log readings
Off 1,440,000 1,438,853 1,439,994 63.3
On 1,440,000 0 1,437,615 62.25
Off again 1,440,000 1,438,700 1,439,993 64.6

With the row On, both channels are equal in every captured frame and the output is not silent. Stereo separation returns when it is turned Off.

The fps values are from the log's N presents, X fps lines. They do not prove an unchanged frame rate: On is 2.4% under the two Off medians, and the two Off medians differ from each other by 2.1%. The mix is one pass over each 240-frame audio block on the audio thread.

The run used ab25784c on main 8e6c15af (log line hookshot unknown (source ee840c7d): built from a source copy without Git metadata). The branch was then rebased onto newer main (now 15d4ae8c), which moved the option ID from 52 to 53; that last revision has not been run in the game again.

Checks

  • tests/test_audio_mono.py fails on untouched main and passes here. It compiles the real mixing helper with warnings as errors and checks sample extremes, silence, Off identity, On → Off, unchanged 5.1, the live wiring, the Off default and that the option ID is unique.
  • python -I -B tests/run_all.py on the branch at main 3abfa0a0: 484 passed, 2 failed, 20 skipped (Windows-only, or need the game). Both failures are the same on untouched main: test_readme_menu_rows.py (main's Skip opening row has no README line) and test_linux_saves_move.py.
  • After the last rebase onto 15d4ae8c, the checks around the change pass: mono audio, option IDs, opening skip, menu translation, the README rows and the program-cache test. The full suite was not repeated after that rebase.

Not tested

Windows, other GPUs, a 5.1 device (the no-change rule for 5.1 is covered by the test, not by a device), and listening by ear.

Average the final stereo channels into both ears with a widened sum. Off and 5.1 output remain unchanged. Use option 53 because main assigns 46 to Button prompts and 52 to Skip opening; neither API struct changes.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant