WIP: feat(audio): the microphone heard on audio interfaces, with a volume, a channel and a test on the Audio tab #2459

Draft
LuigiHard wants to merge 1 commit from LuigiHard/feat/mic-volume-test into main
LuigiHard commented 2026-11-11 20:08:21 +00:00 (Migrated from github.com)

The ocarina's microphone mode (humming or playing an instrument) heard nothing through an audio interface such as a Focusrite Scarlett Solo 4th Gen, and there was no way to make a quiet microphone louder or to see what the game hears. This PR makes the microphone work on such interfaces and adds a volume, a channel choice and a test to the Audio tab.

Why the ocarina heard nothing

The Scarlett Solo lists 4 channels: its two inputs, then two loopbacks of what the PC plays. Windows' processed capture stream, the one SDL's recording opens, gave apps only the first channel. On this setup that channel is silent, while the XLR microphone arrives on the second. The device opened with no error, and the game read silence.

Measured on the Scarlett while humming:

Path ch1 ch2 (the microphone) ch3/ch4 (loopback)
SDL's recording, as Hookshot opened it −84 dB −84 dB silent
SDL with SDL_AUDIO_DEVICE_RAW_STREAM=1 (SDL 3.4.20) −84 dB −84 dB silent (still opened as 2 ch, 48 kHz)
WASAPI raw (AUDCLNT_STREAMOPTIONS_RAW), through this PR's hos_sdl.c −81 dB −13 to −39 dB what the PC plays

SDL's mono mix of the 4 channels would also have cut a single-input microphone to a quarter (−12 dB).

What changed

  • host/hos_sdl.c: on Windows, the microphone is opened through WASAPI's raw stream: every channel, no capture effects. It's found by the name SDL lists it under, and read by a thread of its own into an SDL audio stream that converts it to the format the game asks for. It falls back to SDL's own recording, as before, when the device can't be opened this way, with HOS_MIC_RAW=0, or on SDL2 (Linux). hos_sdl_mic_channels gives the device's channel count.

  • host/nn_audio_in.c:

    • A mono stream's microphone is opened with the device's own channels, and one of them is given to the game: the Microphone channel row's choice, or on Auto the louder of the first two. The loopbacks come after an interface's inputs, so the ocarina doesn't hear the game's own music.
    • Microphone volume (0–400%) scales the signal, clipped at full scale. At 100% it is untouched.
  • Test microphone (new MkwVkHost::mic_test, MKWVK_API_VERSION 18): opens the chosen microphone on its own while the Audio tab shows the test, and shows:

    • a level meter at the chosen volume;
    • the steady note it hears (McLeod pitch, 60–1500 Hz);
    • the channel it hears ("ch 2/4").

    The microphone is released when the menu closes or another tab shows.

  • Menu: the Microphone row moves from Gameplay to Audio, next to Microphone volume, Microphone channel and Test microphone. New options in the ini: microphone_volume (HOS_OPT_MIC_GAIN 29) and microphone_channel (HOS_OPT_MIC_CHANNEL 30).

  • Docs: the README's menu table and Microphone notes, and the development guide's options table.

How it was tested

  • tests/test_audio_in.py (76 cases, no game, no hardware):
    • the volume: every sample format, clipping, and through a stream;
    • a 4-channel device: Auto picks the second channel, a loud loopback is left out, the menu's channel is followed, and a channel past the device's falls back to Auto;
    • the test listener's states, level and channel;
    • pitch detection: tones from 110 to 1200 Hz within 1%, a hum with harmonics (no octave error), noise and silence give none;
    • the note names and meter;
    • the option ids match on both sides, and Windows opens raw before SDL.
  • Full suite: python -I -B tests/run_all.py. 3 checks fail, and all 3 also fail on unmodified main on this PC: test_fpx_sticky and nx2/test_nup_paths (toolchain/encoding environment) and test_vram_budget_row (an unused-constant warning in its fixture with this clang).
  • On the hardware: hos_sdl.c's microphone path, compiled into a small harness on Windows 11 (build 26300) with a Scarlett Solo 4th Gen. The raw path opened 4 channels and the microphone came through on channel 2 (the table above).
  • In game: built and run on Windows 11, GTX 1070, with a Scarlett Solo 4th Gen (XLR on input 1). The Audio tab's rows show and respond. Before the raw-stream change, the Test microphone opened the Scarlett but heard silence (the cause above). After it, the game logged the default microphone open (48000 Hz, its 4 channels, one given to the game: Auto, ...), and the microphone works in game (reporter confirmed). Screenshots to follow.

Not tested / open questions

  • Performance: in a test build from this branch, the reporter saw lower frame rates than their older v2.0.14 build. It isn't settled whether that comes from this change or from upstream changes since v2.0.14. In one session, the game's own microphone mode was on (so the microphone was open) and the game ran at about 15 fps; in another session the microphone wasn't open and it ran at 26–30 fps. That needs an A/B on the same build, with the Microphone set to Off. Until then this is a draft.
  • Linux and SteamOS (SDL2 path: unchanged behaviour, mono as before), other interfaces, and Bluetooth headsets.
  • Two microphones with the same name on Windows' raw path: the first is opened.

🤖 Generated with Claude Code

The ocarina's microphone mode (humming or playing an instrument) heard nothing through an audio interface such as a Focusrite Scarlett Solo 4th Gen, and there was no way to make a quiet microphone louder or to see what the game hears. This PR makes the microphone work on such interfaces and adds a volume, a channel choice and a test to the Audio tab. ## Why the ocarina heard nothing The Scarlett Solo lists 4 channels: its two inputs, then two loopbacks of what the PC plays. Windows' **processed** capture stream, the one SDL's recording opens, gave apps only the first channel. On this setup that channel is silent, while the XLR microphone arrives on the **second**. The device opened with no error, and the game read silence. Measured on the Scarlett while humming: | Path | ch1 | ch2 (the microphone) | ch3/ch4 (loopback) | |---|---|---|---| | SDL's recording, as Hookshot opened it | −84 dB | −84 dB | silent | | SDL with `SDL_AUDIO_DEVICE_RAW_STREAM=1` (SDL 3.4.20) | −84 dB | −84 dB | silent (still opened as 2 ch, 48 kHz) | | WASAPI raw (`AUDCLNT_STREAMOPTIONS_RAW`), through this PR's `hos_sdl.c` | −81 dB | **−13 to −39 dB** | what the PC plays | SDL's mono mix of the 4 channels would also have cut a single-input microphone to a quarter (−12 dB). ## What changed - **`host/hos_sdl.c`:** on Windows, the microphone is opened through WASAPI's raw stream: every channel, no capture effects. It's found by the name SDL lists it under, and read by a thread of its own into an SDL audio stream that converts it to the format the game asks for. It falls back to SDL's own recording, as before, when the device can't be opened this way, with `HOS_MIC_RAW=0`, or on SDL2 (Linux). `hos_sdl_mic_channels` gives the device's channel count. - **`host/nn_audio_in.c`:** - A mono stream's microphone is opened with the device's own channels, and one of them is given to the game: the **Microphone channel** row's choice, or on **Auto** the louder of the first two. The loopbacks come after an interface's inputs, so the ocarina doesn't hear the game's own music. - **Microphone volume** (0–400%) scales the signal, clipped at full scale. At 100% it is untouched. - **Test microphone** (new `MkwVkHost::mic_test`, `MKWVK_API_VERSION` 18): opens the chosen microphone on its own while the Audio tab shows the test, and shows: - a level meter at the chosen volume; - the steady note it hears (McLeod pitch, 60–1500 Hz); - the channel it hears ("ch 2/4"). The microphone is released when the menu closes or another tab shows. - **Menu:** the Microphone row moves from Gameplay to Audio, next to Microphone volume, Microphone channel and Test microphone. New options in the ini: `microphone_volume` (`HOS_OPT_MIC_GAIN` 29) and `microphone_channel` (`HOS_OPT_MIC_CHANNEL` 30). - **Docs:** the README's menu table and Microphone notes, and the development guide's options table. ## How it was tested - **`tests/test_audio_in.py`** (76 cases, no game, no hardware): - the volume: every sample format, clipping, and through a stream; - a 4-channel device: Auto picks the second channel, a loud loopback is left out, the menu's channel is followed, and a channel past the device's falls back to Auto; - the test listener's states, level and channel; - pitch detection: tones from 110 to 1200 Hz within 1%, a hum with harmonics (no octave error), noise and silence give none; - the note names and meter; - the option ids match on both sides, and Windows opens raw before SDL. - **Full suite:** `python -I -B tests/run_all.py`. 3 checks fail, and all 3 also fail on unmodified `main` on this PC: `test_fpx_sticky` and `nx2/test_nup_paths` (toolchain/encoding environment) and `test_vram_budget_row` (an unused-constant warning in its fixture with this clang). - **On the hardware:** `hos_sdl.c`'s microphone path, compiled into a small harness on Windows 11 (build 26300) with a Scarlett Solo 4th Gen. The raw path opened 4 channels and the microphone came through on channel 2 (the table above). - **In game:** built and run on Windows 11, GTX 1070, with a Scarlett Solo 4th Gen (XLR on input 1). The Audio tab's rows show and respond. Before the raw-stream change, the Test microphone opened the Scarlett but heard silence (the cause above). After it, the game logged `the default microphone open (48000 Hz, its 4 channels, one given to the game: Auto, ...)`, and **the microphone works in game** (reporter confirmed). Screenshots to follow. ## Not tested / open questions - **Performance:** in a test build from this branch, the reporter saw lower frame rates than their older v2.0.14 build. It isn't settled whether that comes from this change or from upstream changes since v2.0.14. In one session, the game's own microphone mode was on (so the microphone was open) and the game ran at about 15 fps; in another session the microphone wasn't open and it ran at 26–30 fps. That needs an A/B on the same build, with the Microphone set to Off. Until then this is a draft. - Linux and SteamOS (SDL2 path: unchanged behaviour, mono as before), other interfaces, and Bluetooth headsets. - Two microphones with the same name on Windows' raw path: the first is opened. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:27:11 +00:00
Joey-Magnus (Migrated from github.com) left a comment

MKWVK_API_VERSION 18 is also claimed by #2317, #2261, #2478 and #2479, so whichever lands later will need to renumber.

`MKWVK_API_VERSION` 18 is also claimed by #2317, #2261, #2478 and #2479, so whichever lands later will need to renumber.
Commenting is not possible because the repository is archived.
No description provided.