12 Commits

Author SHA1 Message Date
user
3c475107e3 refactor: simplify audition to single-bot playback
derp now handles both listening and speaking, so audition no longer
needs cross-bot lookup or dual-play through merlin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 23:07:08 +01:00
user
717bf59a05 feat: playlist save/load, queue durations, whisper bias, greet fix
- Move TTS greeting from mumble._play_greet to voice.on_connected
  (fires once on first connect, gated on _is_audio_ready)
- Add initial_prompt multipart field to Whisper STT for trigger word
  bias (auto-generated from trigger config, overridable)
- Enhanced !queue: elapsed/total on now-playing, per-track durations,
  footer with track count and total time
- New !playlist command: save/load/list/del named playlists via
  bot.state persistence (playlist:<name> keys)
- Fix duck floor test (1% -> 2% to match default change)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 19:23:03 +01:00
user
c522d30c36 feat: ack tone, duck-before-TTS, instant ducking on voice/unmute
- Add ascending two-tone chime (880Hz/1320Hz) before TTS playback
  as audible acknowledgment that the voice trigger was recognized
- Signal music ducking 1.5s before TTS starts so music is already
  lowered when audio begins playing
- Snap duck volume to floor instantly on voice packet or user unmute
  via pymumble callback, eliminating the 1s poll delay
- Register USERUPDATED callback to preemptively duck when a user
  unmutes (they're about to speak)
- Strip leading punctuation from trigger remainder (Whisper artifacts)
2026-02-22 18:46:33 +01:00
user
068734d931 fix: kept file protection, skip/autoplay, TTS routing, video ID expansion
- _cleanup_track: never delete files from kept directory (data/music/)
  even when track.keep=False -- fixes kept files vanishing on replay
- !kept rm: skip to next track if removing the currently playing one
- !skip: silent (no reply), restarts play loop for autoplay on empty queue
- TTS plays through merlin's own connection instead of derp's, preventing
  choppy audio when music and TTS compete for the same output buffer
- !play recognizes bare YouTube video IDs (11-char alphanumeric)
- !kept rm <id> subcommand for removing individual kept tracks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:00:23 +01:00
user
6083de13f9 feat: playlist shuffle, lazy resolution, TTS ducking, kept repair
Some checks failed
CI / gitleaks (push) Failing after 3s
CI / lint (push) Successful in 22s
CI / test (3.11) (push) Failing after 2m47s
CI / test (3.13) (push) Failing after 2m52s
CI / test (3.12) (push) Failing after 2m54s
CI / build (push) Has been skipped
Music:
- #random URL fragment shuffles playlist tracks before enqueuing
- Lazy playlist resolution: first 10 tracks resolve immediately,
  remaining are fetched in a background task
- !kept repair re-downloads kept tracks with missing local files
- !kept shows [MISSING] marker for tracks without local files
- TTS ducking: music ducks when merlin speaks via voice peer,
  smooth restore after TTS finishes

Performance (from profiling):
- Connection pool: preload_content=True for SOCKS connection reuse
- Pool tuning: 30 pools / 8 connections (up from 20/4)
- _PooledResponse wrapper for stdlib-compatible read interface
- Iterative _extract_videos (replace 51K-deep recursion with stack)
- proxy=False for local SearXNG

Voice + multi-bot:
- Per-bot voice config lookup ([<username>.voice] in TOML)
- Mute detection: skip duck silence when all users muted
- Autoplay shuffle deck (no repeats until full cycle)
- Seek clamp to track duration (prevent seek-past-end stall)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:21:47 +01:00
user
e9d17e8b00 feat: voice profiles, rubberband FX, per-bot plugin filtering
- Add rubberband package to container for pitch-shifting FX
- Split FX chain: rubberband CLI for pitch, ffmpeg for filters
- Configurable voice profile (voice, fx, piper params) in [voice]
- Extra bots inherit voice config (minus trigger) for own TTS
- Greeting is voice-only, spoken directly by the greeting bot
- Per-bot only_plugins/except_plugins filtering on Mumble
- Alias plugin, core plugin tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:41:00 +01:00
user
ba1af461de fix: use gitleaks CLI instead of licensed action, fix lint errors
Some checks failed
CI / gitleaks (push) Failing after 10s
CI / lint (push) Successful in 18s
CI / test (3.11) (push) Failing after 19s
CI / test (3.12) (push) Failing after 20s
CI / test (3.13) (push) Failing after 20s
CI / build (push) Has been skipped
- Replace gitleaks-action (requires paid license) with direct CLI
  invocation -- same engine, no license needed
- Fix ruff I001 import sorting in voice.py and test_llm.py
- Remove unused imports: _chat_request (test_llm), Path (test_music)
- Remove unused assignment: original_spawn (test_voice)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 05:58:10 +01:00
user
eded764f6a fix: update Piper TTS endpoint and request format
Piper is on 192.168.129.9:5100, not :5000. It expects POST with
JSON body {"text": "..."}, not GET with query params. Also update
Whisper default to 192.168.129.9.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 04:56:24 +01:00
user
9783365b1e feat: add extra Mumble bot instances and TTS greeting
Support [[mumble.extra]] config for additional Mumble identities that
inherit connection settings from the main [mumble] section. Extra bots
get their own state DB and do not run the voice trigger by default.

Add TTS greeting on first connect via mumble.greet config option.
Merlin joins as a second identity with his own client certificate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 04:34:10 +01:00
user
221cb1f06b fix: voice trigger not receiving audio from pymumble
pymumble passes a User object, not a dict. The isinstance(user, dict)
check returned False, setting name to None and silently discarding
every voice packet. Use try/except for dict-like access instead.
2026-02-22 04:02:23 +01:00
user
e127f72660 feat: add always-on voice trigger mode with TTS echo
When [voice] trigger is set in config, the bot continuously listens and
transcribes voice. Speech starting with the trigger word is stripped and
echoed back via TTS. Non-triggered speech is silently discarded unless
!listen is also active.
2026-02-22 03:24:03 +01:00
user
9fbf45f67d feat: add voice plugin with STT and TTS
Whisper STT: buffers incoming voice PCM per user, transcribes on
silence gap via local whisper.cpp endpoint, posts results as actions.

Piper TTS: !say fetches WAV from local Piper endpoint and plays via
stream_audio(). 37 tests cover buffering, flush logic, transcription,
WAV encoding, commands, and lifecycle.
2026-02-22 03:08:02 +01:00