Skip to content

fix(app): improve mic device change handling with testable restart policy#58

Merged
pasrom merged 1 commit intomainfrom
fix/mic-device-change-handling
Mar 22, 2026
Merged

fix(app): improve mic device change handling with testable restart policy#58
pasrom merged 1 commit intomainfrom
fix/mic-device-change-handling

Conversation

@pasrom
Copy link
Copy Markdown
Owner

@pasrom pasrom commented Mar 22, 2026

Summary

  • Add AVAudioEngine.configurationChangeNotification observer to catch format/route changes (not just default device changes)
  • Preserve selected device UID across restarts; verify availability before reuse, fall back to system default with warning
  • Extract restart decision logic into pure MicRestartPolicy.decideRestart() function (Functional Core, Imperative Shell)
  • Add reentrancy guard (isRestarting), deinit { stop() } safety net, isRecording = false on restart failure
  • Pre-compute resampling ratio to avoid division in audio callback hot path
  • Remove redundant listenerInstalled state (replaced by deviceChangeListener != nil)
  • Add 8 unit tests for MicRestartPolicy (first tests in AudioTapLib)

Test plan

  • swift test passes (745 app tests + 8 AudioTapLib tests)
  • ./scripts/lint.sh passes (0 violations)
  • Manual test: AirPods → Mac mic switch during recording — transcript continuous, no gap
  • CI passes

…licy

What: Add AVAudioEngine.configurationChangeNotification observer, preserve
selected device UID across restarts, and extract restart logic into a pure
MicRestartPolicy function for unit testing.

Reasoning:
- Problem: Only kAudioHardwarePropertyDefaultInputDevice was monitored,
  missing format/route changes. Restart logic (reentrancy guard, device
  fallback) was untestable without hardware.
- Approach: "Functional Core, Imperative Shell" — pure decision function
  takes state as input, returns action enum. MicCaptureHandler becomes
  thin shell that calls policy + executes CoreAudio/AVAudioEngine ops.
- Add deinit safety net, isRestarting guard, isRecording=false on error.
- 8 unit tests cover all decision paths without mocks or hardware.
@github-actions github-actions bot added the bug Something isn't working label Mar 22, 2026
@pasrom pasrom merged commit 31b359b into main Mar 22, 2026
8 checks passed
@pasrom pasrom deleted the fix/mic-device-change-handling branch March 22, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant