Skip to content

feat(study): synchronized RSVP + audio reader (--study mode)#3

Open
DavidTbilisi wants to merge 1 commit into
masterfrom
feat/study-mode-rsvp-audio
Open

feat(study): synchronized RSVP + audio reader (--study mode)#3
DavidTbilisi wants to merge 1 commit into
masterfrom
feat/study-mode-rsvp-audio

Conversation

@DavidTbilisi

Copy link
Copy Markdown
Owner

Summary

  • Adds tts-ka --study — a synchronized RSVP word-flash + audio playback window driven by the per-word timing list fast_audio.generate_audio_with_subs already emits.
  • Five new modules + three test files; no rewrite of existing TTS paths.
  • Quiz generation is intentionally a swappable QuizProvider Protocol; the default RuleBasedProvider._generate_for_chunk is a TODO(human) slot left for a follow-up commit.

Architecture

Module Purpose
study_session.py Pure-logic timing model: bisect_right lookup over a precomputed start_ms array → O(log n) inside the 30 Hz sync loop.
study_player.py Tk + lazy-imported pygame window; centred big-word label + faded peripheral context. Excluded from coverage (matches gui.py).
quiz.py QuizProvider Protocol, RuleBasedProvider (zero-dep, _generate_for_chunk is the TODO slot), LLMQuizProvider stub for future swap.
session_log.py Append-only JSONL at ~/.tts_ka_study.jsonl (env override TTS_KA_STUDY_LOG); malformed lines skipped, not raised.
main.py patch --study and --study-chunk-words flags; lazy import so non-study users do not pay the pygame cost.

CLI

pip install -e ".[study]"
tts-ka --study --study-chunk-words 200 -l en file.txt
tts-ka --study cb -l en      # use clipboard text

Why this exists

Reading speed for non-fiction is bottlenecked by two independent axes — language-processing speed and eye mechanics. This mode trains the language-processing axis by pairing RSVP visual flash with audio playback of the same text, so the bottleneck cannot hide behind the other channel. The session log gives a longitudinal WPM trajectory for later analysis.

Test plan

  • pytest tests/test_study_session.py tests/test_quiz.py tests/test_session_log.py — 30 new tests pass
  • Manual: tts-ka --study cb -l en with a paragraph of English in the clipboard; verify audio plays and the big-word label tracks
  • Manual: hit "Quiz me" mid-playback and confirm the popup surfaces NotImplementedError cleanly (until the TODO is filled)
  • Manual: hit "Save session" and confirm a JSONL line lands at ~/.tts_ka_study.jsonl
  • Manual: tts-ka --study cb -l ka with Georgian text to verify the cross-language path
  • Follow-up PR: implement RuleBasedProvider._generate_for_chunk and update test_calls_into_todo_slot to assert real Question shape

🤖 Generated with Claude Code

Adds a dual-channel reading mode that flashes one word at a time in a
Tk window while the same text plays through pygame, driven by the
per-word timing list `fast_audio.generate_audio_with_subs` already
emits. Intended for use on a stationary bike or other hands-free
session where eyes and ears can be paced together.

Architecture (no rewrite of existing TTS paths):
- study_session.StudySession: pure-logic timing model; O(log n)
  bisect_right lookup over a precomputed start_ms array so the 30 Hz
  sync loop stays cheap.
- study_player.StudyPlayer: Tk + lazy-imported pygame; excluded from
  coverage same as gui.py.
- quiz.QuizProvider Protocol with RuleBasedProvider (zero-dep,
  _generate_for_chunk left as the single TODO slot) and a
  LLMQuizProvider stub so a cloud/local model can swap in later
  without touching the player.
- session_log.SessionRecord: append-only JSONL at
  ~/.tts_ka_study.jsonl (env override TTS_KA_STUDY_LOG); malformed
  lines are skipped, not raised.

CLI: `tts-ka --study --study-chunk-words 200 -l en file.txt`
Extra: pip install -e ".[study]" (adds pygame).

30 new tests pass; UI module excluded from the 70 percent coverage
floor, matching the existing gui.py / native_hotkeys.py convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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