fix(voice): skip sounddevice on macOS to avoid TCC media-library prompt - #62601
fix(voice): skip sounddevice on macOS to avoid TCC media-library prompt#62601simonmmafs wants to merge 2 commits into
Conversation
On macOS, initializing PortAudio/CoreAudio via sounddevice triggers a kTCCServiceMediaLibrary permission dialog even when no media-library access is needed. afplay already handles WAV (and every other format) natively, so route macOS playback straight to it and keep sounddevice for other platforms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Automated review by Hermes Agent: No obvious issues found.
Reviewed by Hermes Agent
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Automated review by Hermes Agent: No obvious issues found.
Reviewed by Hermes Agent
Duplicate of #13291 — both route macOS audio playback in |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the macOS WAV fallback path. The condition at tools/voice_mode.py:1065 would correctly make that particular path fall through to the existing afplay player.
Problems
tools/voice_mode.py:291-321still imports and plays throughsounddevicefor cue beeps. Those beeps are invoked by the CLI voice wrapper before recording athermes_cli/voice.py:427and after it athermes_cli/voice.py:536, so this does not yet skip macOS sounddevice output entirely.- Streaming ElevenLabs TTS bypasses
play_audio_file()and openssounddevice.OutputStreamdirectly attools/tts_tool.py:2645-2654. tests/tools/test_voice_mode.py:884-904covers the generic WAV sounddevice path, but no test covers the new Darwin routing behavior.
Suggested changes
- Apply one explicit macOS output policy to WAV playback, cue beeps, and streaming TTS, then add Darwin-focused tests proving the selected routes do not initialize sounddevice.
- Please reconcile this with the overlapping broader approach in #13291 before salvage.
Automated hermes-sweeper review.
| # On macOS, skip sounddevice entirely — PortAudio/CoreAudio init triggers | ||
| # a kTCCServiceMediaLibrary permission prompt even though we don't need it. | ||
| # afplay handles all formats natively without touching the media stack. | ||
| if file_path.endswith(".wav") and platform.system() != "Darwin": |
There was a problem hiding this comment.
This bypasses sounddevice only for WAV file playback. play_beep() still imports and plays through sounddevice (tools/voice_mode.py:291-321), and the CLI invokes it before recording at hermes_cli/voice.py:427; please cover that output path as well if the goal is to avoid macOS PortAudio/CoreAudio initialization.
Addresses review on NousResearch#62601. Applies a single rule — no sounddevice for audio OUTPUT on macOS (PortAudio/CoreAudio init triggers a kTCCServiceMediaLibrary prompt) — consistently at all three output sites: - play_audio_file: WAV playback (already routed to afplay) now uses the shared _sounddevice_output_allowed() helper. - play_beep: synthesize the tone with numpy only, then on macOS play it via a temp WAV through afplay instead of sounddevice. - stream_tts_to_speaker (tts_tool): on macOS, skip the sounddevice OutputStream so playback falls through to the existing tempfile/afplay path. Audio INPUT (recording) is untouched — it legitimately needs mic permission. Tests: TestMacOSAudioOutputPolicy (voice_mode) proves WAV + beep routing does not import sounddevice on Darwin and still uses it off Darwin; test_tts_macos_output proves streaming TTS skips the OutputStream on Darwin. Existing test_play_wav_via_sounddevice pinned to non-Darwin for determinism. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the detailed review — addressed all three points in c7571ae by applying one consistent policy ("no sounddevice for audio output on macOS") at every output site rather than just the WAV branch:
Audio input (recording) is deliberately untouched — it legitimately needs mic permission. Tests: Happy to reconcile with #13291 — if you'd prefer that one as the canonical fix I can close this, or rebase this consolidated approach on top. Your call. |
Addresses review on #62601. Applies a single rule — no sounddevice for audio OUTPUT on macOS (PortAudio/CoreAudio init triggers a kTCCServiceMediaLibrary prompt) — consistently at all three output sites: - play_audio_file: WAV playback (already routed to afplay) now uses the shared _sounddevice_output_allowed() helper. - play_beep: synthesize the tone with numpy only, then on macOS play it via a temp WAV through afplay instead of sounddevice. - stream_tts_to_speaker (tts_tool): on macOS, skip the sounddevice OutputStream so playback falls through to the existing tempfile/afplay path. Audio INPUT (recording) is untouched — it legitimately needs mic permission. Tests: TestMacOSAudioOutputPolicy (voice_mode) proves WAV + beep routing does not import sounddevice on Darwin and still uses it off Darwin; test_tts_macos_output proves streaming TTS skips the OutputStream on Darwin. Existing test_play_wav_via_sounddevice pinned to non-Darwin for determinism. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Merged into main via consolidated salvage PR #73520 (merge Your contribution is credited to you in git history. Thank you! Closing this PR as merged-via-salvage. |
Addresses review on NousResearch#62601. Applies a single rule — no sounddevice for audio OUTPUT on macOS (PortAudio/CoreAudio init triggers a kTCCServiceMediaLibrary prompt) — consistently at all three output sites: - play_audio_file: WAV playback (already routed to afplay) now uses the shared _sounddevice_output_allowed() helper. - play_beep: synthesize the tone with numpy only, then on macOS play it via a temp WAV through afplay instead of sounddevice. - stream_tts_to_speaker (tts_tool): on macOS, skip the sounddevice OutputStream so playback falls through to the existing tempfile/afplay path. Audio INPUT (recording) is untouched — it legitimately needs mic permission. Tests: TestMacOSAudioOutputPolicy (voice_mode) proves WAV + beep routing does not import sounddevice on Darwin and still uses it off Darwin; test_tts_macos_output proves streaming TTS skips the OutputStream on Darwin. Existing test_play_wav_via_sounddevice pinned to non-Darwin for determinism. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem
On macOS,
play_audio_file()initializes PortAudio/CoreAudio throughsounddevicefor WAV playback. That init triggers a kTCCServiceMediaLibrary permission dialog even though voice mode never needs media-library access — a confusing prompt for users, and a blocker for headless/gateway installs where nobody is at the screen to click it.Fix
Skip the
sounddevicepath entirely on Darwin and fall through toafplay, which handles WAV (and every other format) natively without touching the TCC-gated media stack. Other platforms keep the existing sounddevice-first behavior.Running as a local patch on a macOS gateway install (v0.15.1 → v0.18.2) since June with no regressions.
🤖 Generated with Claude Code