Audio (Monitor & Devices)
Shiny.Audio is the standalone audio backbone under Shiny.Speech — usable on its own for
recording and playback without the speech stack. Alongside capture (IAudioSource) and playback
(IAudioPlayer) it provides a live microphone monitor (IAudioMonitor) and audio route
enumeration/selection (IAudioDevices), all discoverable through a single IAudio facade.
Registration
Section titled “Registration”builder .UseMauiApp<App>() .UseShiny(); // required on Android for RECORD_AUDIO permission routing
builder.Services.AddAudioServices(); // IAudioPlayer + IAudioSource + IAudioMonitor + IAudioDevices + IAudioAddSpeechServices() already registers the capture/playback pieces; call AddAudioServices() when
you also want the monitor/devices/facade. The individual registrations still exist
(AddAudioSource(), AddAudioPlayer(), AddAudioMonitor(), AddAudioDevices()), and every focused
interface remains injectable directly — the facade doesn’t replace them.
The IAudio facade
Section titled “The IAudio facade”Inject one service to discover the whole surface. Each member is the same service you could inject
directly; lifetimes are preserved (player/monitor/devices singletons, IAudioSource a fresh
transient per access).
using Shiny.Audio;
public class AudioViewModel(IAudio audio){ Task Play() => audio.Player.PlayAsync("https://example.com/clip.mp3"); Task StartMic() => audio.Monitor.Start(); var Outputs => audio.Devices.GetOutputs();}Monitor and Devices are implemented on iOS / Mac Catalyst and Android only; accessing them on
other platforms throws PlatformNotSupportedException.
Microphone monitor (IAudioMonitor)
Section titled “Microphone monitor (IAudioMonitor)”Routes the microphone straight to the current audio output in near-real-time — a PA / megaphone. Ideal for “talk into the phone, hear it over a Bluetooth speaker”.
using Shiny.Audio;
public partial class MicViewModel(IAudio audio) : ObservableObject{ [ObservableProperty] double level; // bind to a VU bar
public async Task Toggle() { var monitor = audio.Monitor; if (monitor.IsMonitoring) { await monitor.Stop(); return; }
if (await monitor.RequestAccess() != AccessState.Available) return;
monitor.InputLevelChanged += (_, l) => MainThread.BeginInvokeOnMainThread(() => Level = l);
// No Processing → routes to a Bluetooth A2DP speaker (see the trade-off below). await monitor.Start(new AudioMonitorOptions { Gain = 1.0 }); }}The output goes to the current route — the **built-in speaker, a wired output, or a connected
Bluetooth speaker (A2DP) using the phone’s own mic. Gain (0.0–1.0) is adjustable live;
InputLevelChanged emits a normalized level for a meter.
Implementation: iOS/Mac Catalyst use AVAudioEngine (input node → main mixer → output) in a
PlayAndRecord session (no DefaultToSpeaker, so it follows a Bluetooth/wired route; Default mode
unless processing is requested); the engine rebuilds on route changes so monitoring follows a
newly-connected output. Android uses AudioRecord → AudioTrack. The audio session is snapshotted
and restored on Stop.
AirPlay (HomePod / Apple TV) is not supported for a live mic — iOS only permits AirPlay for playback, not while recording. Use Bluetooth for a wireless live PA.
Audio devices (IAudioDevices)
Section titled “Audio devices (IAudioDevices)”Its most reliable use is displaying where audio is flowing and reacting to route changes — e.g.
“Output: JBL Flip · Bluetooth”. Read CurrentInput / CurrentOutput (each an AudioDevice with a
normalized Type) and subscribe to Changed:
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 / switchedEach AudioDevice carries Id, Name, Io (input/output), a normalized Type (BuiltInMic,
BluetoothA2dp, WiredHeadphones, BuiltInSpeaker, …) and IsCurrent.
Selection (GetInputs/GetOutputs + IAudioMonitor.SetInputDevice/SetOutputDevice) is a full
selector on Android only:
await audio.Monitor.SetInputDevice(chosenInput); // Android + iOSawait audio.Monitor.SetOutputDevice(chosenOutput); // Android only (best-effort elsewhere)Volume (IAudioPlayer)
Section titled “Volume (IAudioPlayer)”IAudioPlayer exposes the device media volume — the level the hardware buttons control, normalized
0.0–1.0 and independent of any per-request TTS Volume. Reading works on every platform; setting
is platform-limited — always guard a set with IsVolumeControlSupported.
using Shiny.Audio;
public partial class VolumeViewModel(IAudio audio) : ObservableObject{ [ObservableProperty] float volume; // bind to a Slider public bool CanSet => audio.Player.IsVolumeControlSupported;
public VolumeViewModel(IAudio audio) : this(audio) { Volume = audio.Player.Volume; // reading works everywhere
audio.Player.VolumeChanged += (_, v) => // hardware buttons, OS UI, or a successful set MainThread.BeginInvokeOnMainThread(() => Volume = v); }
partial void OnVolumeChanged(float value) { if (audio.Player.IsVolumeControlSupported) // iOS / Mac Catalyst setter throws audio.Player.Volume = value; }}| Platform | Read | Set | VolumeChanged source | Backing API |
|---|---|---|---|---|
| Android | ✅ | ✅ | system settings observer | AudioManager STREAM_MUSIC (integer-stepped → set values quantize) |
| Windows | ✅ | ✅ | endpoint volume callback | WASAPI IAudioEndpointVolume (default render endpoint) |
| macOS | ✅ | ✅ † | CoreAudio property listener | HAL virtual main volume of the default output device |
| iOS / Mac Catalyst | ✅ | ❌ | KVO on outputVolume | AVAudioSession.OutputVolume (read-only) |
| Browser (WASM) | ✅ | ✅ | echoed on set | HTMLAudioElement.volume — app-local, not the OS volume |
Platform support
Section titled “Platform support”| Capability | iOS / Mac Catalyst | Android | Windows | Browser |
|---|---|---|---|---|
IAudioPlayer / IAudioSource | ✅ | ✅ | ✅ | ✅ |
IAudioPlayer.Volume (read) | ✅ | ✅ | ✅ | ✅ |
IAudioPlayer.Volume (set) | ❌ ¹ | ✅ | ✅ | ✅ |
IAudioMonitor (live monitor) | ✅ | ✅ | ❌ | ❌ |
IAudioDevices (routes) | ✅ | ✅ | ❌ | ❌ |
¹ Native macOS (not Mac Catalyst) can set the volume via CoreAudio — see the Volume section.