Files
UmbraAudio/specs/00-project-overview.md
B.D. Murphy df36dad8aa Weekly commit: attribution QA, dramatic variations, GPU TTS, AR export
Squashes ~3 weeks of uncommitted work (2026-09-03 → 09-21). The repo is the
only durable copy; a single accidental `git checkout` on routes.py nearly lost
a month of work (recovered from an editor snapshot, not git). See AGENTS.md →
"Commit Cadence" — commit every Sunday.

Attribution & QA
- New umbraaudio/analysis/attribution_qa.py: mechanical check_attribution /
  fix_high_confidence / verify_conversation_flow (speech-tag mismatch, narrator
  dialogue, role-tag guessing) plus scripts/qa_attribution.py CLI.
- llm_attributor prompt rules 3e-3h (unnamed roles, pronoun coreference,
  repeated lines) with wider scene/position context.
- Per-utterance QA box in chapter audio detail UI: hover issues, Recheck
  (auto-fix loop) and Accept (persisted qa_accepted* + text/speaker signature,
  auto re-surfaces when either changes). New POST /api/utterance/{id}/recheck
  and /api/utterance/{id}/qa-accept. _ensure_utterance_columns migration fixes
  legacy DBs that lacked the columns.

Generation
- Dramatic utterance variations: 26 emotions + pace + custom instruct,
  composed into every TTS voice mode (clone/icl/ref/embedding) and all
  generation sites; markers record the instruct so edits re-trigger.
- Staleness markers are JSON {speaker,text}; legacy plain-text markers always
  stale.
- Scene-break segmentation (generation/scene_break.py) + resegment merge.
- AR package exporter (assembly/ar_export.py) with tests.
- Voice ref resolution: rglob refs, canonical alias / first-word fallback.
- Edge-TTS fallback visibility: durable voice_engines.json ledger + UI badge.

GPU / TTS server
- qwen_tts_server: instruct + clone/icl support (voice_description →
  instruct_ids prepended to the talker input).
- qwen_tts.py remote path sends voice_description alongside voice_ref/slug.

Config / ops
- .env: UMBRA_TTS__QWEN_TTS_URL=http://localhost:8881.
- .gitignore: book projects, tmp_out, logs, one-off samples A/B demo.
- AGENTS.md + README.md: Commit Cadence (commit at least weekly, on Sunday).

New tests: test_qa_acceptance, test_variations, test_ar_export,
test_scene_break, test_resegment_merge, test_vocals_project. Suite: 327 pass
(6 pre-existing voice-profile accent-wording failures, unrelated).
2026-09-21 06:44:22 -07:00

10 KiB

UmbraAudio — Project Overview & Architecture

Purpose

UmbraAudio is a quality-first, multi-pass audiobook generation system that converts English manuscripts (EPUB, PDF, TXT, MD) into fully produced audiobooks using neural TTS. Each pass has quality gates. Quality is the primary concern; generation speed is secondary.

Two acoustic-quality invariants run through every pass and are non-negotiable for release:

  1. Narrator consistency — the narrator's voice stays calm, even, and low-emphasis for the entire book, independent of how dramatic the underlying scene text is.
  2. Cast distinctiveness — every character who shares dialogue proximity with another character must be audibly distinguishable, not just textually different.

A third principle governs delivery: punctuation and typography are prosody instructions, not decoration. A question mark, an ellipsis, an em-dash interruption, or a shouted ALL-CAPS word must change how a line is spoken, not just how it reads.

A book is a project. Its source, analysis, intermitent files, and outputs are kept seperate from other projects.

High-Level Pipeline

EPUB/PDF/TXT/MD
    │
    ▼
┌─────────────────────────────────────────────────┐
│  PASS 1a: PARSE                                 │
│  Parse manuscript into chapters and scense "<<<>>>"│
│  Preserves emphasis markup + punctuation verbatim│
│  Output: chapters table (source_text)           │
└─────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────┐
│  PASS 1b: CHARACTER DISCOVERY                   │
│  LLM chapter-by-chapter character extraction    │
│  Merge across chapters, deduplicate aliases     │
│  Narrator identified (not a character)          │
| identify characteristics/voice of each character
| Each voice must be unique for the listener.
│  Voice reference generation via Qwen3-TTS      │
│  Verification samples generated                 │
│  Output: speakers.json, voice_assignments.json  │
└─────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────┐
│  PASS 2: TWO-PHASE SEGMENTATION                 │
│  Phase 1: Mechanical segmenter splits text      │
│    - Sentence splitting at .!?                  │
│    - Dialogue detection via quotes              │
│    - Tag-based attribution (forward/backward)   │
│    - Scene break (<<<>>>) handling               │
│  Phase 2: LLM speaker attribution               │
│    - Attributes ALL dialogue (overrides guesses)│
│    - Resolves pronouns and context              │
│    - Uses character profiles from speakers.json │
| Uterances are narrator "Bob said," and then Bob speaking.
│  Output: utterances table                       │
└─────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────┐
│  PASS 3: AUDIO GENERATION                       │
│  Qwen3-TTS Base → voice cloning from references │
|  Each character has a unique voice the listener can decern
│  Voice assignment from voice_assignments.json   │
│  Retry logic for rate limiting                  │
│  Output: chapter_audio/ch{id}/*.wav             │
└─────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────┐
│  PASS 4: REVIEW                                 │
│  Web UI for human review of:                    │
│    - Character profiles and voice assignments   │
│    - Voice test samples                         │
│    - Chapter audio with per-utterance playback  │
│    - Speaker attribution accuracy               │
│  Output: approved chapters                      │
└─────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────┐
│  EXPORT                                         │
│  Stitch chapter audio → full audiobook          │
│  Output: export/*.m4b, export/*.mp3             │
└─────────────────────────────────────────────────┘

Runtime Data Layout

.umbra/                          # Runtime data directory
├── project.db                   # SQLite database
├── speakers.json                # Character profiles (61 speakers)
├── voice_assignments.json       # Voice assignments (60 mappings)
├── voice_tests/                 # Verification audio samples
│   ├── {speaker_name}.wav
│   └── ...
├── chapter_audio/               # Generated chapter audio
│   ├── ch5/
│   │   ├── utt_0000.wav
│   │   └── ...
│   ├── ch6/
│   └── ch7/
├── analysis/
│   └── input.txt                # Extracted manuscript text
└── defects.json                 # Quality defect log

Technology Stack

Layer Technology Version
Language Python 3.13+
CLI Click + Rich 8.1+ / 13.0+
Database SQLite (aiosqlite) 0.19+
LLM OpenAI-compatible (LM Studio) 1.10+
TTS Qwen3-TTS 1.7B (local GPU) 0.1+
LLM Model google/gemma-4-12b-qat (remote LM Studio, context_length=8192) remote
Web FastAPI + HTMX + Jinja2 0.104+
Parsing ebooklib, beautifulsoup4, PyMuPDF 0.18+ / 4.0+ / 1.23+

GPU Requirements

  • Qwen3-TTS 1.7B Base: ~3.9GB VRAM (voice cloning)
  • Qwen3-TTS 1.7B VoiceDesign: ~3.9GB VRAM (voice reference generation)
  • LM Studio (google/gemma-4-12b-qat): remote server; load with context_length: 8192 (speaker attribution)
  • Note: Only one Qwen3-TTS model can be loaded at a time (auto-swaps). LM Studio must be unloaded before TTS generation if VRAM is tight.

Module Structure

umbraaudio/
├── cli.py              # Click CLI (all commands)
├── config.py           # Pydantic settings (AppConfig tree)
├── database.py         # SQLite schema + async CRUD
├── orchestrator.py     # Full pipeline coordinator
├── parsers/            # Manuscript parsing (EPUB, PDF, TXT, MD)
├── analysis/           # LLM character discovery, profiling
│   ├── character_discovery.py    # Chapter-by-chapter LLM extraction
│   ├── llm_attributor.py         # Two-phase speaker attribution
│   ├── llm_segmenter.py          # LLM-based segmentation (legacy)
│   └── CHARACTER_DISCOVERY_PROMPT.md
├── mapping/            # Mechanical segmentation, speaker attribution
│   └── segmenter.py              # Text splitting + tag-based attribution
├── voices/             # TTS engine and voice management
│   ├── qwen_tts.py               # Qwen3-TTS engine (voice design + cloning)
│   ├── lmstudio_lifecycle.py     # LM Studio VRAM management
│   ├── edge_tts.py               # Edge-TTS wrapper (legacy)
│   ├── voice_designer.py         # Voice reference generation pipeline
│   └── embeddings.py             # Speaker embedding extraction
├── generation/         # Audio generation
├── web/                # FastAPI review UI
└── scripts/            # Standalone scripts
    ├── resegment_all.py          # Batch segmentation + attribution
    ├── generate_voice_references.py  # Voice ref generation
    ├── generate_chapter_audio.py     # Per-chapter audio
    └── generate_all_audio.py         # Background full generation

Key Design Decisions

  1. Quality over speed: Each pass has quality gates; the pipeline can stop and wait for human review
  2. Two-phase segmentation: Mechanical segmenter handles text splitting (narration by sentence; quoted dialogue kept whole), LLM handles speaker attribution — this division of labor produces a 0.05% dialogue-as-narrator error rate (1/1,882)
  3. LLM attributes all dialogue: The LLM attributes every dialogue utterance, using the mechanical current_speaker as a hint and overriding it when wrong — ambiguous or incorrectly guessed lines get corrected
  4. Narrator is not a character: Dedicated soft-spoken male voice (en-US-GuyNeural:-10), never participates in character discovery
  5. 59 unique voices via Qwen3-TTS: VoiceDesign generates unique voice profiles from character descriptions; VoiceClone maintains consistency across utterances using reference audio
  6. Character profiles in JSON: speakers.json stores all character data; voice_assignments.json maps characters to voice references
  7. Scene break handling: <<<>>> markers preserved as special utterances, LLM maintains context across scene breaks
  8. Web review at checkpoints: HTMX-powered UI for human approval of voices, speaker assignments, and chapter audio
  9. Punctuation as prosody: Question marks, ellipses, em-dashes, and ALL-CAPS words change how lines are spoken
  10. Cast-wide distinctiveness gate: Characters must be audibly distinguishable, not just textually different
  11. Verification phrase: Rainbow Passage (phonetically balanced paragraph covering all English phonemes) used for voice testing
  12. Author watermark: "Author B.D. Murphy" embedded in final audiobook