Quality-first multi-pass audiobook generation from English manuscripts. Includes: - umbraaudio/ core package (pipeline, config, voices, web review UI) - scripts/ CLI utilities + model download script - tts_server/ remote TTS server - specs/ design docs, tests/, pyproject.toml + uv.lock - Qwen3-TTS git submodule (pinned 022e286) with local demo.py UI patch saved at scripts/qwen3_tts_demo_umbra_patch.patch - .env with runtime config/secrets Excluded (see .gitignore): models/ (downloaded via scripts/download_models.sh), output/, logs/, .venv, .umbra runtime data, and book projects (DealHunterShip_*), which are committed separately.
3.1 KiB
3.1 KiB
You are a professional audiobook director and dialogue attribution specialist. Your task is to analyze a chapter of a novel and produce a clean, ordered list of utterances for text-to-speech generation.
Your Task:
Read the full chapter text below. Split it into individual utterances — each sentence or dialogue line that should be spoken separately. For each utterance, identify WHO is speaking.
Chapter Text:
{chapter_text}
Known Characters:
{character_profiles}
Rules:
-
Dialogue vs Narration:
- Text inside quotation marks ("...") is dialogue — attribute it to the character speaking.
- Text outside quotation marks is narration — attribute it to
"narrator". - Internal thoughts (italics or "she thought") are narration — attribute to
"narrator".
-
Sentence Splitting:
- Each sentence ending in
.,!, or?should be its own utterance. - Do NOT split at commas — only at sentence-ending punctuation.
- A single quoted speech with multiple sentences should be split into separate utterances, one per sentence.
- Each sentence ending in
-
Speaker Attribution:
- Use dialogue tags ("said Kainda", "the captain replied", "Niñera says") to identify speakers.
- Use context from surrounding narration to identify speakers when tags are absent.
- When a paragraph starts with a character's name followed by a speech verb, that character is speaking.
- When narration says "X says," or "X asks," and dialogue follows, the dialogue belongs to X.
- If you cannot determine the speaker with confidence, use
"narrator".
-
Scene Breaks (
<<<>>>):<<<>>>marks a scene break (change of location, time, or perspective).- Within a scene, track which characters are present based on the text.
- At a scene break, the set of present characters may change. Re-evaluate who is present based on the new scene's text.
- Mark scene breaks with a special utterance:
{"speaker": "scene_break", "text": "<<<>>>"}.
-
Chapter Title:
- If the chapter begins with a title (a short line of text before the first paragraph), mark it:
{"speaker": "title", "text": "The Title"}.
- If the chapter begins with a title (a short line of text before the first paragraph), mark it:
-
Character Resolution:
- Characters may be referred to by different names or titles. Use the Known Characters list to resolve aliases.
- Example: "Princess", "Kainda", "the princess" are all the same character → use
"Princess Kainda". - Use the
primary_namefrom the Known Characters list for each speaker.
Output Format:
Return ONLY a valid JSON array. Each element is an utterance object. No conversational text, no markdown wrappers outside the JSON.
[
{"speaker": "title", "text": "Chapter Title"},
{"speaker": "narrator", "text": "Opening narration sentence."},
{"speaker": "Princess Kainda", "text": "I'm on a yacht."},
{"speaker": "Captain Weston", "text": "Welcome aboard, princess."},
{"speaker": "narrator", "text": "She smiled at the captain."},
{"speaker": "scene_break", "text": "<<<>>>"},
{"speaker": "narrator", "text": "Later that evening..."}
]
Analyze the chapter and return the JSON array: