Skip to content

Restart microphone capture on audio device change#311

Open
MaxHeimbrock wants to merge 1 commit into
max/mic-samplerate-device-initfrom
max/mic-restart-on-device-change
Open

Restart microphone capture on audio device change#311
MaxHeimbrock wants to merge 1 commit into
max/mic-samplerate-device-initfrom
max/mic-restart-on-device-change

Conversation

@MaxHeimbrock

Copy link
Copy Markdown
Contributor

Problem

When a capture device disappears mid-call (e.g. unplugging a Bluetooth headset and falling back to the built-in mic), the local microphone went silent and never recovered:

  • MicrophoneSource stayed bound to the now-gone device name and never re-resolved to the new default.
  • Unity tears down its audio graph on a device change, which detaches the AudioProbe.OnAudioFilterRead tap — so even the capture tap stopped firing.

The playback side already handles this in AudioStream.OnAudioConfigurationChanged; this mirrors that pattern for capture.

Change

  • Subscribe MicrophoneSource to AudioSettings.OnAudioConfigurationChanged and restart capture on a device change (reusing the existing RestartMicrophone coroutine, which re-adds the AudioSource/AudioProbe and re-registers the tap on the rebuilt graph).
  • Resolve the capture device to the OS default (null) when the preferred device is gone, so an unplugged headset transparently hands off to the built-in mic.
  • Track the active device separately from the preferred one so Microphone.IsRecording/GetPosition/End target the device actually recording.
  • Guard against overlapping restarts when the event fires repeatedly around one hardware swap.

The native source's rate is fixed at construction, so if the device change moves Unity's DSP output rate, frames are still dropped; this case is warned about clearly. Full rate-change recovery is a follow-up (stacked PR).

Testing

  • Scripts~/run_unity.sh build macos → Build SUCCEEDED.
  • Manual runtime verification (join with BT headset, unplug mid-call, confirm a remote participant keeps hearing you) still recommended on a real device.

🤖 Generated with Claude Code

var outputSampleRate = (uint)AudioSettings.outputSampleRate;
if (outputSampleRate != _expectedSampleRate)
{
Utils.Warning($"MicrophoneSource: audio device change moved the DSP output rate to {outputSampleRate}Hz, but the native source is fixed at {_expectedSampleRate}Hz. Captured frames will be dropped until the track is recreated at the new rate.");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Triggered on Unity 6 editor on mac starting with MDR-1000x, disabling Bluetooth to switch to built in mic, then connecting to MDR-1000x again:

LiveKit: MicrophoneSource: audio device change moved the DSP output rate to 44100Hz, but the native source is fixed at 48000Hz. Captured frames will be dropped until the track is recreated at the new rate.
UnityEngine.Logger:LogWarning (string,object)
LiveKit.Internal.Utils:Warning (object) (at /Users/maxheimbrock/dev/unity/client-sdk-unity/Runtime/Scripts/Internal/Utils.cs:41)
LiveKit.MicrophoneSource:OnAudioConfigurationChanged (bool) (at /Users/maxheimbrock/dev/unity/client-sdk-unity/Runtime/Scripts/MicrophoneSource.cs:248)
UnityEngine.AudioSettings:InvokeOnAudioConfigurationChanged (bool) (at /Users/bokken/build/output/unity/unity/Modules/Audio/Public/ScriptBindings/Audio.bindings.cs:413)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@MaxHeimbrock MaxHeimbrock force-pushed the max/mic-samplerate-device-init branch from 266730d to 2a26265 Compare June 15, 2026 14:23
When a capture device disappears mid-call (e.g. unplugging a Bluetooth
headset), the local microphone went silent and never recovered:
MicrophoneSource stayed bound to the gone device name and never
re-registered the AudioProbe tap that Unity detaches when it rebuilds
its audio graph.

Subscribe to AudioSettings.OnAudioConfigurationChanged (mirroring the
playback-side AudioStream handler) and restart capture on a device
change, resolving to the OS default device when the preferred device is
no longer present. Track the active device separately from the preferred
one so Microphone.IsRecording/GetPosition/End target the right device,
and guard against overlapping restarts.

The native source's rate is fixed at construction, so if the device
change moves Unity's DSP output rate, frames are still dropped; warn
clearly in that case. Full rate-change recovery follows separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MaxHeimbrock MaxHeimbrock force-pushed the max/mic-restart-on-device-change branch from 37725a6 to d19145d Compare June 15, 2026 14:26
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.

1 participant