Skip to content

Let a PortAudio device name carry its host API - #103

Open
chrisuthe wants to merge 2 commits into
mainfrom
feat/portaudio-host-api-qualifier
Open

chrisuthe wants to merge 2 commits into
mainfrom
feat/portaudio-host-api-qualifier

Conversation

@chrisuthe

Copy link
Copy Markdown
Member

Why

Windows lists every output device once per host API (MME, DirectSound, WASAPI, WDM-KS), and the copies share a name. -o portaudio:<name> refuses a name that matches more than one device, so a Windows user could not pick an output by name at all. An index works, but it shifts whenever a device comes or goes: Bluetooth, a monitor waking, a VR headset. That leaves no stable output = … to write, which blocks the Windows installer from asking which device to play through. That installer change follows as a separate PR.

What

  • portaudio:<host api>:<name> names exactly one device. The prefix is the new value of -l's host API column: mme, directsound, wasapi, wdmks, asio, alsa, jack, oss, coreaudio. It is matched case-insensitively, like names.
  • Backward compatible: a spec is read as <host api>:<name> only when no device has the whole string as its name. Every spec that resolved before resolves the same way, including names that themselves contain a colon, such as ALSA's HDA Intel PCH: ALC892 Analog (hw:0,0).
  • Still no guessing: a bare name under several host APIs is still refused. The error now lists each copy's index and host API and shows the prefixed form to use instead, for example: 3 output devices share that name (4 mme, 12 directsound, 19 wasapi) -- put its host API in front to pick one, e.g. -o portaudio:mme:Speakers (7- Schiit Modi+). When a prefix can't tell the copies apart, as with twins under one host API, the error points to the index as before.
  • -l: the host API column prints the prefix instead of PortAudio's display name. That is what you type, and it also fixes column alignment, which Windows DirectSound used to overflow. The help text explains the prefixed form.
  • The name matching is split out of resolve_pa_device() as match_pa_device_name(), which takes a plain device list, so it can be unit-tested without audio hardware. The code is generic, with no Windows-only #ifdef, and it serves --input too.
  • Docs: Advanced Usage (both -o and --input) and Getting Started on Windows.

Testing

  • New tests/portaudio_device_test.cpp, 9 tests: prefix mapping, a unique bare name, ambiguity with the suggested form, picking by prefix, a whole name winning over the prefix reading, names containing colons, unknown or absent prefixes, twins under one host API, and an unnameable host API.
  • Native Windows build (clang-cl, -Werror): 526/526 passed. The 517 existing tests are unchanged.
  • Against real devices on Windows 11, with throwaway players on a spare port:
    • The bare ambiguous name is refused with the message above.
    • directsound: and WASAPI: (case-insensitive) each resolve their own copy.
    • DirectSound's full Odyssey G95NC (NVIDIA High Definition Audio) resolves. MME truncates that name to 31 characters.
    • An unknown prefix gives "no output device by that name".
  • scripts/format.sh --check with clang-format 20.1.8.

Worth knowing for the installer default: on the same device, DirectSound advertised 65 formats (22.05 to 192 kHz), while WASAPI shared mode advertised only the device's 192 kHz mix rate.

Windows lists every output once per host API (MME, DirectSound, WASAPI,
WDM-KS) under the same name, so -o portaudio:<name> was refused as ambiguous
and only an index, which shifts as devices come and go, could pick one.
<host api>:<name> now names exactly one, using the prefix -l prints in its
host API column. A spec is read that way only when no device has the whole
string as its name, so every spec that resolved before resolves the same.
An ambiguous name's error lists each copy's host API and the prefixed form.

The matching is split from the PortAudio enumeration so it can be tested.

Copilot AI 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.

🟡 Changes recommended

Suggested device commands can select an unrelated device when prefixed names collide with whole names and can fail in the shell because device arguments are not quoted and escaped.

2 open issues
What changed in this PR

Adds host-API-qualified PortAudio device names, enabling stable selection when Windows exposes the same device through multiple APIs while preserving existing name matching.

Changes:

  • Adds case-insensitive <host api>:<name> matching for playback and capture, with whole-name precedence.
  • Displays usable host API prefixes in device listings and expands ambiguity diagnostics.
  • Adds hardware-independent matching tests and updates usage documentation.
File Description
tests/​portaudio_device_test.cpp Adds nine tests for prefixes, matching, and ambiguity.
src/​portaudio_source.cpp Shows host API prefixes in input listings.
src/​portaudio_sink.cpp Shows host API prefixes in output listings.
src/​portaudio_device.h Declares host API helpers and the testable name matcher.
src/​portaudio_device.cpp Implements qualified matching and ambiguity guidance.
src/​audio_source.cpp Adds qualified-name syntax to input listing help.
src/​audio_sink.cpp Explains qualified output selection in listing help.
docs/​wiki/​Getting-Started-on-Windows.md Documents stable device selection by API and name.
docs/​wiki/​Advanced-Usage.md Documents qualified playback and capture names.
CMakeLists.txt Registers the PortAudio device tests.

Review effort: Balanced

Comment thread src/portaudio_device.cpp
Comment on lines +193 to +195
if (host_api == nullptr) {
by_host_api = false;
}
Comment thread src/portaudio_device.cpp Outdated
Comment on lines +204 to +205
error += "put its host API in front to pick one, e.g. " + flag +
" portaudio:" + matches.front()->host_api + ":" + matches.front()->name;
A Windows device name carries spaces and parentheses, so the suggestion
only worked once quoted.
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