Skip to content

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 into
NotRexed:mainfrom
LuigiHard:feat/mic-volume-test
Draft

LuigiHard wants to merge 1 commit into
NotRexed:mainfrom
LuigiHard:feat/mic-volume-test

Conversation

@LuigiHard

@LuigiHard LuigiHard commented Nov 11, 2026 •

Copy link
Copy Markdown

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

… a channel and a test on the Audio tab

The ocarina's microphone mode (humming or playing an instrument) heard nothing through a Focusrite Scarlett Solo 4th
Gen: Windows' processed stream, which SDL's recording opens whatever SDL_AUDIO_DEVICE_RAW_STREAM says (SDL 3.4.20),
gave apps only the interface's first channel, silent, while the XLR microphone was on its second, and SDL's mono mix
of the 4 channels would have cut it to a quarter anyway. There was also no way to make a quiet microphone louder or
to see what the game hears.

- host/hos_sdl.c: on Windows the microphone is opened through WASAPI's raw stream (AUDCLNT_STREAMOPTIONS_RAW: every
  channel, no capture effects), found by the name SDL lists it under, read by a thread of its own into an SDL audio
  stream that converts it to the format asked for. One that cannot be opened this way, HOS_MIC_RAW=0, or SDL2: SDL's
  own recording, as before. hos_sdl_mic_channels: 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 is given to the
  game: the Microphone channel row's (HOS_OPT_MIC_CHANNEL), or on Auto the louder of the first two (an interface's
  inputs; its loopbacks of what the PC plays come after them, so the ocarina does not hear the game's music). The
  Microphone volume (HOS_OPT_MIC_GAIN, 0 to 400 %) scales it, clipped at full scale; 100 % leaves it untouched.
- Test microphone (MkwVkHost mic_test, API version bump): the chosen microphone opened on its own while the Audio
  tab shows the test, a level meter at the volume, the steady note it hears (McLeod pitch, 60 to 1500 Hz) and the
  channel ("ch 2/4"). Let go when the menu closes or another tab shows.
- The Microphone row moves from Gameplay to Audio, beside Microphone volume, Microphone channel and Test microphone.
- tests/test_audio_in.py: the volume (every format, clipping, through a stream), a 4-channel device (Auto's second
  channel, a loud loopback left out, the menu's channel, one past the device's), the test's states, level and channel,
  the pitch (tones, a hum's harmonics, noise, silence), the notes and meter, the option ids on both sides, and that
  Windows opens raw before SDL.

Measured on the Scarlett while humming: SDL's processed stream -84 dB on every channel; the raw path, through
hos_sdl.c itself, -13 to -39 dB on channel 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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.

2 participants