Skip to content

feat: add dynamic audio output handling (#985)#1034

Open
colinchambachan wants to merge 2 commits intoTheBoredTeam:devfrom
colinchambachan:feature/audio-output-icons
Open

feat: add dynamic audio output handling (#985)#1034
colinchambachan wants to merge 2 commits intoTheBoredTeam:devfrom
colinchambachan:feature/audio-output-icons

Conversation

@colinchambachan
Copy link

@colinchambachan colinchambachan commented Feb 11, 2026

Summary

This PR updates volume icon behavior to reflect the active audio output route instead of using a single static/speaker-only icon path. Closes #985

What changed

  • Added output route classification in VolumeManager:
    • built-in speaker
    • wired headphones
    • AirPods
    • AirPods Pro
    • AirPods Max
    • generic Bluetooth headphones
    • external speaker
    • unknown fallback
  • Added a centralized icon mapper (volumeHUDSymbol(for:)) so all volume surfaces use the same logic.
  • Wired icon mapping into:
    • closed HUD: SystemEventIndicatorModifier
    • inline HUD: InlineHUD
    • open notch HUD: OpenNotchHUD
    • notch home volume control button: NotchHomeView
  • Adjusted routing behavior:
    • USB audio output is treated as wired headphones.
    • Non-AirPods Bluetooth devices use the generic headphones icon.
    • AirPods variants are matched by explicit device name checks.

Why

The previous behavior did not reliably reflect actual output hardware (for example, AirPods Bluetooth devices showing non-AirPods-style icon (general speaker).

macOS support

  • No deployment target change in this PR.
  • Project target remains macOS 14.0+.

Testing

Manual validation performed for:

  • Built-in output
speakeroutput
  • Bluetooth (non-AirPods) output and Wired Output (via 3.5 mm or USB audio dongle)-> generic headphones icon
nonapple
  • AirPods Pro
airpodspro

AirPods model-specific paths are implemented via name matching and need hardware verification on:

  • AirPods
  • AirPods Max

Below screenshots include UI via hardcoded changes for demonstration purposes:
airpods

airpodsmax

Risk / Notes

  • Route classification is intentionally conservative with explicit fallbacks to avoid incorrect AirPods labeling.
  • Unknown routes fall back to speaker-level icon behavior.

@Alexander5015
Copy link
Member

The previous behavior did not reliably reflect actual output hardware (for example, non-AirPods Bluetooth devices showing AirPods-style icons, and
USB audio dongle routes not mapping consistently to wired headphones).

What do you mean by this?

@colinchambachan
Copy link
Author

The previous behavior did not reliably reflect actual output hardware (for example, non-AirPods Bluetooth devices showing AirPods-style icons, and
USB audio dongle routes not mapping consistently to wired headphones).

What do you mean by this?

Currently the notch displays the same speaker icon in the HUD volume slider irrespective of the device connected. This PR updates the behaviour s.t. if Airpods are connected, an Airpods icon is displayed (same with Airpods Pro, Airpods Max, as well as wired and bluetooth outputs as well).

@Alexander5015
Copy link
Member

That’s what I thought, but what confused me was that you also previously wrote that "non-AirPods Bluetooth devices [would show] AirPods-style icons," and that this change fixes that behaviour.

@colinchambachan
Copy link
Author

That’s what I thought, but what confused me was that you also previously wrote that "non-AirPods Bluetooth devices [would show] AirPods-style icons," and that this change fixes that behaviour.

Oh good catch, I meant the opposite - updated now

Copy link
Member

@Alexander5015 Alexander5015 left a comment

Choose a reason for hiding this comment

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

Please rebase and fix conflicts.

@colinchambachan colinchambachan force-pushed the feature/audio-output-icons branch from d848aea to 5326abe Compare March 5, 2026 18:00
case kAudioDeviceTransportTypeUSB:
return .wiredHeadphones
case kAudioDeviceTransportTypeHDMI, kAudioDeviceTransportTypeDisplayPort:
return isHeadphonesLike ? .wiredHeadphones : .externalSpeaker
Copy link
Member

Choose a reason for hiding this comment

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

What is the point of this check? My understanding is this will always be an externalSpeaker if its a display, right?

case kAudioDeviceTransportTypeBuiltIn:
return isHeadphonesLike ? .wiredHeadphones : .builtInSpeaker
case kAudioDeviceTransportTypeBluetooth, kAudioDeviceTransportTypeBluetoothLE:
return .bluetoothHeadphones
Copy link
Member

@Alexander5015 Alexander5015 Mar 6, 2026

Choose a reason for hiding this comment

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

I think some improvements can be made here, but I have to look into this more. This is more of a note to me than a blocker for merging.

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