From 6ca8f80087c7f8e88ef09d9ec06f89ac2ec6a938 Mon Sep 17 00:00:00 2001 From: "B.D. Murphy" <161863146+bdmurphy73@users.noreply.github.com> Date: Sat, 26 Sep 2026 07:51:44 -0700 Subject: [PATCH] Document the scene-break rule and verify the 2 s silence Scene-break rows must carry speaker_name='Narrator'. The system requires a speaker on every utterance row, so 'scene_break' was never a valid authorial value - it was a generator artifact. Normalized 74 rows book-wide (81 total). The value is inert for rendering: is_scene_break() keys off the <<<>>> text and the scene_break type, never speaker_name, and both generation paths short-circuit before voice resolution. Verified 62/62 existing scene-break WAVs are exactly 2.000 s, matching SCENE_BREAK_SILENCE_MS. Recorded so the transient 'scene_break' speaker value is not mistaken for a deliberate authorial choice again. --- specs/16-audiobook-production.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/specs/16-audiobook-production.md b/specs/16-audiobook-production.md index 712bc95..65a1411 100644 --- a/specs/16-audiobook-production.md +++ b/specs/16-audiobook-production.md @@ -108,6 +108,26 @@ voices/narrator_ref.wav (voice for new narration) ─▶ 1. synth opening/annou | `umbraaudio/web/templates/index.html` | "Build Final Audiobook (MP3)" card + stats table + download links | | `tests/test_ar_export.py` | Unit tests with mocked TTS + ffmpeg | +## 5a. Scene breaks (`<<<>>>`) + +A scene break is a segmentation row whose `text` is exactly `<<<>>>`. It renders as +**2.000 s of silence**, never as speech. + +- **Author rule:** every scene-break utterance must carry `speaker_name = 'Narrator'`. + The system requires a speaker on every utterance row, and `Narrator` is the correct + value. A `speaker_name` of `scene_break` is **not** valid data — it was a transient + artifact of the generator, not an authorial choice. Normalized book-wide on + 2026-09-26 (74 rows changed; 81 total). +- **The speaker value is inert for rendering.** `is_scene_break(text, utt_type)` + (`umbraaudio/generation/scene_break.py:31`) keys off the `<<<>>>` text and the + `scene_break` type only, never `speaker_name`. Both generation paths short-circuit + before voice resolution — `generate_all_audio.py:167` and + `chapter_builder.py:514` (`write_scene_break_audio(out)` then `continue`, ahead of + `_resolve_voice_ref` at `:520`). So the value is required by the schema but cannot + affect the audio. +- **Duration constant:** `SCENE_BREAK_SILENCE_MS = 2000` (`scene_break.py:26`). + Verified 2026-09-26: 62/62 existing scene-break WAVs are exactly 2.000 s. + ## 6. Quality gates before upload - All tracks: 44.1 kHz, mono, 192 kbps CBR MP3 (ffprobe report in manifest).