Default --outputs to a stereo pair instead of every output channel - #2
Merged
Conversation
Writing to every output channel is actively harmful on an Aggregate Device built around a loopback. On a BlackHole + speakers aggregate, channels 0-1 are BlackHole's outputs, which feed BlackHole's input - so the previous default routed the circuit's output back into its own input and produced runaway feedback through whatever distortion was being simulated. Default to the first two channels, and when a device has more outputs than were selected, print the full channel list marking which are in use. This is a defensive default, not automatic routing: it does NOT work out which pair is audible. On an aggregate the first pair is often the loopback rather than the speakers, so --outputs is still required there - the change turns a howling feedback loop into silence plus a printed list showing what to pass. Devices with two output channels are unaffected. Doing this properly would mean reading kAudioAggregateDevicePropertyFullSubDeviceList, mapping channel ranges to sub-devices, and preferring outputs on a sub-device that contributes no input. That is worth doing if aggregates become a common setup, but it is a heuristic that can guess wrong on unusual hardware, so it is deliberately left out of this change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
The previous default selected every output channel of the device. On an Aggregate Device built around a loopback that is actively harmful: on a BlackHole + speakers aggregate, channels 0–1 are BlackHole's outputs, and BlackHole's outputs feed its inputs — so the circuit's output was routed back into its own input, producing runaway feedback through whatever distortion was being simulated.
Now defaults to the first two channels, and prints the full channel list (marking which are selected) whenever a device has more outputs than were taken.
What this does not do
It does not work out which pair is audible. On the aggregate above the first pair is still the loopback, not the speakers, so
--outputs 2,3is still required there. What changes is the failure mode: a howling feedback loop becomes silence plus a printed list showing exactly what to pass.Devices with two output channels — speakers, USB interfaces, BlackHole standalone — behave exactly as before, with no warning.
Follow-up worth considering
Automatic routing would mean reading
kAudioAggregateDevicePropertyFullSubDeviceList, mapping channel ranges to sub-devices, and preferring outputs on a sub-device that contributes no input — which picks the speakers and skips the loopback. Roughly 100 lines of P/Invoke plus a heuristic that could guess wrong on unusual hardware, so it's deliberately not in this change. Worth doing if aggregates become a standing setup.Testing
Verified against the 4-channel aggregate (warning shown, 2 of 4 selected) and against a plain stereo device (no warning, unchanged behaviour). The live playback session that confirmed end-to-end audio — Apple Loops guitar through a Tube Screamer sim, out to speakers — ran on this build.
🤖 Generated with Claude Code