Skip to content

fix(discord): pause the voice receiver during mixer-path speech playback - #110005

Open
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-109998
Open

liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-109998

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

What does this PR do?

On the Discord voice mixer playback path, the adapter never paused the voice receiver while the bot's own TTS was playing, so the bot's audio returning through the user's speakers was captured, transcribed, and injected as a fresh "user utterance" — one transcription away from a self-reply loop. The legacy one-shot path already pauses the receiver for exactly this reason (its comment says "echo prevention"); the mixer path simply lacked the same guard.

This PR pauses the receiver before mixer.play_speech() and resumes it in a finally once the speech drains (or the drain loop times out), mirroring the legacy path. VoiceReceiver.pause() is a pure drop-packets flag (_on_packet returns early while paused), so no stale audio is buffered or replayed after resume.

Related Issue

Fixes #109998

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • plugins/platforms/discord/adapter.py — in play_in_voice_channel(), wrap the mixer-path speech playback (play + drain loop) with receiver.pause() before play_speech() and receiver.resume() in a finally, matching the legacy one-shot path's echo-prevention pattern. Net change is confined to the if pcm: block; the decode-failure fallback to legacy playback is untouched.
  • tests/gateway/test_discord_voice_mixer.py — two regression tests: test_mixer_path_pauses_receiver_while_speaking asserts the exact pause → play_speech → resume ordering on the happy path, and test_mixer_path_resumes_receiver_after_playback_timeout asserts resume still happens when the drain loop breaks out via the playback timeout.

How to Test

  1. pytest tests/gateway/test_discord_voice_mixer.py -q11 passed (9 pre-existing + 2 new).
  2. Red check: reverting only the adapter.py hunk makes both new tests fail (pause/resume never called), confirming they exercise the fix.
  3. Neighbouring suite unaffected: pytest tests/gateway/test_voice_command.py -q77 passed.
  4. Observed result: the mixer path now holds the receiver paused for the whole play_speech → drain window, so nothing the bot plays into the channel can re-enter the capture buffer as input.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass (targeted: tests/gateway/test_discord_voice_mixer.py 11 passed, tests/gateway/test_voice_command.py 77 passed, ruff clean on both changed files)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 26.5 (arm64)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A (change is platform-independent adapter logic)
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

N/A

Screenshots / Logs

$ pytest tests/gateway/test_discord_voice_mixer.py -q
...........                                                              [100%]
11 passed in 9.18s

The mixer playback path never paused the voice receiver while the bot's
own TTS was playing, so the bot's audio returning through the user's
speakers was captured, transcribed, and injected as a fresh user
utterance — one transcription away from a self-reply loop. The legacy
one-shot path already pauses the receiver for exactly this reason
("echo prevention"); the mixer path simply lacked it.

Pause the receiver before mixer.play_speech() and resume it in a
finally once the speech drains (or the drain loop times out),
mirroring the legacy path.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins platform/discord Discord bot adapter tool/tts Text-to-speech and transcription labels Sep 13, 2026
@kvnloo

kvnloo commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Comment assumes head 679cb9ed85ff94327d87a3934d4d2fc6f69619a8; void if moved.

KEEP — pause Discord mixer-path receiver during speech (closes #109998 echo axis)

  • On the mixer playback path the adapter never paused the voice receiver while the bot’s own TTS played, so return audio through the user’s speakers was captured/transcribed as a fresh user utterance — one step from a self-reply loop. Legacy one-shot path already pauses for echo prevention; this PR mirrors that guard with pause before mixer.play_speech and resume in finally.
  • Focused mixer ordering test in tests/gateway/test_discord_voice_mixer.py is the right narrow vehicle for the echo half of Discord/gateway STT hygiene.

CHECK

One-click close / ordering

Landing-evidence

  • When merged, please reply with the merge commit SHA on main (not “completed”).

@kvnloo

kvnloo commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Comment assumes head 679cb9ed85ff94327d87a3934d4d2fc6f69619a8; void if moved.

KEEP — pause Discord mixer-path receiver during speech (closes #109998 echo axis)

  • On the mixer playback path the adapter never paused the voice receiver while the bot’s own TTS played, so return audio through the user’s speakers was captured/transcribed as a fresh user utterance — one step from a self-reply loop. Legacy one-shot path already pauses for echo prevention; this PR mirrors that guard with pause before mixer.play_speech and resume in finally.
  • Focused mixer ordering test in tests/gateway/test_discord_voice_mixer.py is the right narrow vehicle for the echo half of Discord/gateway STT hygiene.

CHECK

One-click close / ordering

Landing-evidence

  • When merged, please reply with the merge commit SHA on main (not “completed”).

@liuhao1024

Copy link
Copy Markdown
Contributor Author

Thanks for the KEEP triage.

Confirming the verdict is still anchored: head remains 679cb9ed (matches your pin), so it is not voided.

On the CHECK item (mergeable but empty live status/check-runs): all three workflows on this head sit in action_required — the fork-PR workflow approval gate — so no run has ever started, and as an outside contributor I cannot approve or re-run them. As the local equivalent of the pre-merge-desk refresh you flagged, on a clean checkout of 679cb9ed: tests/gateway/test_discord_voice_mixer.py11 passed.

Ordering notes acknowledged: if #111090 lands first I will cherry-pick this pause onto its merge tip (and cite the SHA here), and the no-fold / no-one-click-close list stands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have platform/discord Discord bot adapter tool/tts Text-to-speech and transcription type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Discord voice mixer path never pauses the receiver — the bot captures and transcribes its own TTS on the way back in

3 participants