Skip to content
Shiny.NET
Commercial SupportGuaranteed response times on Shiny bug fixes, straight from the maintainers. Plans from US$1,500 a year.See the plans

Devices & Routes

IAudioDevices lists the audio inputs and outputs the OS can see, reports which ones are in use, and raises Changed when the route changes, for example when a Bluetooth speaker connects or headphones are unplugged.

Available on iOS, Mac Catalyst, macOS, Android, Windows and Linux. IAudio.Devices throws PlatformNotSupportedException in the browser.

Its most reliable use, on every platform, is showing the current route and updating it when the route changes, for example “Output: JBL Flip 5 · Bluetooth”:

using Shiny.Audio;
var devices = audio.Devices;
string input = devices.CurrentInput?.Name ?? "—"; // .Type: BuiltInMic, Bluetooth, ...
string output = devices.CurrentOutput?.Name ?? "—"; // .Type: BuiltInSpeaker, BluetoothA2dp, ...
devices.Changed += (_, _) => Refresh(); // route added, removed or switched
Member
GetInputs() Capture devices: built-in mic, headset mics, USB mics, …
GetOutputs() Render devices: speaker, headphones, Bluetooth, HDMI, …
CurrentInput / CurrentOutput The route in use, or null
Changed Raised when the input or output route changes. It can come from a platform thread
ShowOutputPicker() Reserved for a system route picker. It does nothing today. See iOS

Each AudioDevice has an Id (a stable platform identifier), a Name (“JBL Flip 5”, “iPhone Microphone”), Io (Input or Output), a normalized Type and IsCurrent.

AudioDeviceType values: BuiltInMic, BuiltInSpeaker, BuiltInReceiver, WiredHeadset, WiredHeadphones, Bluetooth (HFP/SCO, can have a mic), BluetoothA2dp (stereo, output only), Usb, CarAudio, Hdmi, AirPlay and Unknown.

Rather than matching enum values by hand, use the classification helpers. Each one exists on both AudioDevice and AudioDeviceType:

var output = audio.Devices.CurrentOutput;
if (output?.IsWired() == true) { /* 3.5mm jack, Lightning or USB-C */ }
if (output?.IsBluetooth() == true) { /* HFP/SCO or A2DP */ }
if (output?.IsBuiltIn() == true) { /* phone speaker or earpiece, nothing attached */ }
// Is the audio private to the user? For example, before reading a message aloud.
if (output?.IsHeadphones() == true)
await audio.Player.PlayAsync(messageUrl);
// Will capture come from the accessory, or from the phone's own mic?
bool accessoryMic = output?.HasMicrophone() == true;
Helper True for
IsWired() WiredHeadphones, WiredHeadset, Usb
IsBluetooth() Bluetooth (HFP/SCO), BluetoothA2dp
IsBuiltIn() BuiltInMic, BuiltInSpeaker, BuiltInReceiver
IsHeadphones() WiredHeadphones, WiredHeadset, Bluetooth, BluetoothA2dp
HasMicrophone() WiredHeadset, Bluetooth

Changed fires on plug and unplug on every supported platform, so you can combine it with these helpers. For example, you could pause playback when headphones are removed.

Selecting a route is much more limited by platform than listing them.

Microphone: pass an input from GetInputs() to AudioCaptureOptions.InputDevice, AudioRecordingOptions.InputDevice, AudioDuplexOptions.InputDevice or IAudioMonitor.SetInputDevice. This works on every platform that has IAudioDevices.

Output: only the live monitor has an output selector:

await audio.Monitor.SetInputDevice(chosenInput); // Android, iOS, Linux
await audio.Monitor.SetOutputDevice(chosenOutput); // Android and Linux. iOS: built-in speaker toggle only
Platform Lists inputs Lists outputs Choose the mic Choose the output
Android ✅ ✅ ✅ ✅ (monitor)
iOS / Mac Catalyst ✅ Current route only ✅ preferred input Built-in speaker on/off
macOS ✅ ❌ ✅ capture, recorder, duplex ❌
Windows ✅ ✅ ✅ capture, recorder ❌
Linux ✅ ✅ ✅ ✅ (monitor)

Apple deliberately doesn’t let apps list or freely choose output routes. On iOS, GetOutputs() returns only the current output, and SetOutputDevice can only switch the built-in speaker on or off. AirPlay and Bluetooth output are chosen in the system route picker (AVRoutePickerView). There is no API for routing to a particular HomePod or speaker from code. The picker is a view, so host an AVRoutePickerView in your own UI. ShowOutputPicker() can’t display it on its own and does nothing.

Input can be chosen on iOS (AVAudioSession.SetPreferredInput).

Windows lists devices through the Windows device enumeration API and classifies them by name (Bluetooth, HDMI, USB, headset, headphones, otherwise built-in). The input Id it returns is what AudioGraph uses to open that mic for capture. Linux lists PulseAudio/PipeWire sources and sinks, or ALSA devices on the fallback. Changed fires on PulseAudio/PipeWire only, because ALSA has no change notification.