Add audio output device/port selection for Android and iOS/macOS
Feature: Add audio output device/port selection for Android and iOS/macOS ✅
This PR implements the ability to select audio output device/channel on Android and iOS/macOS, allowing users to route audio to specific outputs (e.g., phone speaker instead of Bluetooth).
✅ All Tasks Completed:
- [x] Research Android AudioManager API for setting preferred audio output device
- [x] Add AudioOutputDevice enum with common audio output device types
- [x] Add PreferredOutputDevice property to AudioPlayerOptions.android.cs
- [x] Implement audio routing logic in AudioPlayer.android.cs using MediaPlayer.setPreferredDevice()
- [x] Apply to all AudioPlayer constructors for consistency
- [x] Update documentation to show how to use the new feature
- [x] Build verification - Android target builds successfully
- [x] Address code review feedback - fix documentation references and comments
- [x] Final build verification - no warnings related to our changes
- [x] Security scan - no vulnerabilities detected
- [x] Extract duplicated audio attributes configuration into shared method
- [x] Standardize initialization order across all constructors
- [x] Research iOS/macOS AVAudioSession API for output port override
- [x] Add AudioOutputPort enum for iOS/macOS
- [x] Add PreferredOutputPort property to AudioPlayerOptions.macios.cs
- [x] Implement audio routing logic in AudioPlayer.macios.cs using AVAudioSession
- [x] Update documentation with iOS/macOS examples and cross-platform usage
- [x] Fix AudioOutputPort enum type compatibility issue
- [x] Address final code review feedback (whitespace, pragma, error logic)
Implementation Summary
New Files:
-
AudioOutputDevice.android.cs- Enum with 10 device types for Android (Speaker, Earpiece, WiredHeadset, WiredHeadphones, BluetoothA2dp, BluetoothSco, UsbDevice, UsbAccessory, AuxLine, Default) -
AudioOutputPort.macios.cs- Enum with 2 port options for iOS/macOS (Speaker, Default) using ulong underlying type
Modified Files:
-
AudioPlayerOptions.android.cs- Added PreferredOutputDevice property -
AudioPlayerOptions.macios.cs- Added PreferredOutputPort property -
AudioPlayer.android.cs- Added SetPreferredOutputDevice() and ConfigureAudioAttributes() methods, updated all constructors with consistent initialization order -
AudioPlayer.macios.cs- Added SetPreferredOutputPort() method with correct error handling, integrated into PreparePlayer() -
audio-player.md- Added comprehensive documentation with Android, iOS/macOS, and cross-platform examples
Technical Details:
Android:
- Uses MediaPlayer.setPreferredDevice() API (Android API 28+)
- Device-specific routing with 10 device types
- Per-player setting
- Gracefully degrades on older versions
iOS/macOS:
- Uses AVAudioSession.OverrideOutputAudioPort() API
- Session-wide port override (Speaker or Default)
- Affects all audio output on device
- Simple speaker override for forcing audio to device speaker
- Enum uses ulong underlying type to match AVAudioSessionPortOverride
- Correct boolean error checking logic
Common:
- Refactored audio attributes configuration into reusable method (Android)
- Standardized initialization order across constructors
- Comprehensive error handling and diagnostic tracing
- Clean code without unnecessary pragma directives
- Backward compatible - default behavior unchanged
Usage Examples
Android:
var options = new AudioPlayerOptions
{
#if ANDROID
PreferredOutputDevice = AudioOutputDevice.Speaker
#endif
};
var player = audioManager.CreatePlayer(stream, options);
player.Play(); // Audio plays through phone speaker, not Bluetooth
iOS/macOS:
var options = new AudioPlayerOptions
{
#if IOS || MACCATALYST
PreferredOutputPort = AudioOutputPort.Speaker
#endif
};
var player = audioManager.CreatePlayer(stream, options);
player.Play(); // Audio plays through device speaker, not Bluetooth
Cross-Platform:
var options = new AudioPlayerOptions
{
#if ANDROID
PreferredOutputDevice = AudioOutputDevice.Speaker,
#elif IOS || MACCATALYST
PreferredOutputPort = AudioOutputPort.Speaker,
#endif
};
var player = audioManager.CreatePlayer(stream, options);
player.Play(); // Audio plays through speaker on both platforms
Requirements:
- Android: API 28 (Android 9.0 Pie) or higher for full functionality; gracefully ignored on API 21-27
- iOS/macOS: All supported versions
Quality Assurance:
✅ Build: Successful (Android target verified) ✅ Code Review: Completed, all feedback addressed ✅ Security: No vulnerabilities (CodeQL) ✅ Documentation: Complete with Android, iOS, and cross-platform examples ✅ Code Quality: Eliminated duplication with shared method, clean code style ✅ Consistency: Standardized initialization order across constructors ✅ Backward Compatibility: Maintained ✅ Cross-Platform: Both Android and iOS/macOS implementations ✅ Type Safety: Fixed enum type compatibility for iOS/macOS ✅ Error Handling: Correct boolean logic for iOS error checking
Original prompt
This section details on the original issue you should resolve
<issue_title>Feature Request - Select output audio path/channel</issue_title> <issue_description>I have an Android App using Plugin.Maui.Audio to generate key beeps and other sound alerts i.e. play sound files. The application also makes phone calls using a SIP library. All this works fine until I use a Bluetooth connection to car audio. Yes it still all works BUT I don't want my beeps and alerts being played via the car audio (Bluetooth connection). Is it possible to add audio output configuration to switch output to the Android device built in speakers.
</issue_description>
Comments on the Issue (you are @copilot in this section)
@bijington Do you know what is possible on Android directly? If it's possible to control it using the Google APIs then we could include this
- Fixes jfversluis/Plugin.Maui.Audio#182
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.
@copilot address comments
@copilot please address comments
@copilot is there something like this for iOS as well? If not thats fine, if there is, can you implement it for that too?
@copilot is there something like this for iOS as well? If not thats fine, if there is, can you implement it for that too?
Yes! iOS/macOS support has been implemented in commit f4c1475.
iOS uses AVAudioSession.OverrideOutputAudioPort() which works differently than Android:
- Android: Per-player device selection with 10 device types (Speaker, Earpiece, Bluetooth A2DP, etc.)
- iOS/macOS: Session-wide port override with 2 options (Speaker or Default)
Both platforms now support forcing audio to the device speaker even when Bluetooth is connected. The documentation includes cross-platform examples showing how to use both APIs together.
@copilot build error
D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\AudioPlayer\AudioOutputPort.macios.cs(18,12): error CS0266: Cannot implicitly convert type 'ulong' to 'int'. An explicit conversion exists (are you missing a cast?) [D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\Plugin.Maui.Audio.csproj::TargetFramework=net9.0-maccatalyst] D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\AudioPlayer\AudioOutputPort.macios.cs(18,12): error CS0266: Cannot implicitly convert type 'ulong' to 'int'. An explicit conversion exists (are you missing a cast?) [D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\Plugin.Maui.Audio.csproj::TargetFramework=net9.0-ios] D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\AudioPlayer\AudioOutputPort.macios.cs(25,12): error CS0266: Cannot implicitly convert type 'ulong' to 'int'. An explicit conversion exists (are you missing a cast?) [D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\Plugin.Maui.Audio.csproj::TargetFramework=net9.0-maccatalyst] D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\AudioPlayer\AudioOutputPort.macios.cs(25,12): error CS0266: Cannot implicitly convert type 'ulong' to 'int'. An explicit conversion exists (are you missing a cast?) [D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\Plugin.Maui.Audio.csproj::TargetFramework=net9.0-ios]
@copilot build error
D:\a\Plugin.Maui.Audio\Plugin.Maui.Audio\src\Plugin.Maui.Audio\AudioPlayer\AudioOutputPort.macios.cs(18,12): error CS0266: Cannot implicitly convert type 'ulong' to 'int'. An e...
Fixed in commit 1de9db7. Changed AudioOutputPort enum to use ulong as the underlying type to match AVAudioSessionPortOverride.
@copilot address comments