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.
9.5 KiB
Spec: Audiobook Production for Author's Republic (MP3 Package)
Status: In development. This spec defines how the Deal Hunter audiobook is packaged for submission to Author's Republic (AR). It maps every AR audio requirement to a concrete artifact in the build so we can verify compliance before upload. The existing M4B build is unchanged; the AR submission is a parallel export produced from the same chapter audio.
Reviewed: 2026-09-05 against https://authorsrepublic.zendesk.com/hc/en-us/articles/17274538468244--Audio-Requirements-List
1. Goal
Produce a single self-contained MP3 package that satisfies AR's 18 audio-file requirements, plus a dashboard "Build Final Audiobook (MP3)" step that builds it, shows per-track statistics, and offers a download for every track.
2. AR requirements → how Deal Hunter meets them
| # | AR requirement | How we meet it |
|---|---|---|
| 1 | Consistent overall sound & formatting | Every track: same mono channel layout, same 44.1 kHz sample rate, same 192 kbps CBR MP3, same 2 s leading/trailing silence, same loudness target (≈ −20 dB RMS). |
| 2 | Mono or stereo for all files | Force mono (-ac 1) on every track. |
| 3 | Same bit rate for all files | All tracks libmp3lame 192 kbps CBR (no VBR). |
| 4 | Opening and closing credits in separate tracks | Dedicated 01_Opening.mp3 (title/author/narrator per cover+metadata) and …_Closing.mp3 (ends with «The End.» + full credits). |
| 5 | Sample track 1–5 min | 02_Sample.mp3 = Chapter 1 promo (≈ 1 min 35 s; within 1–5 min). It was written as the audiobook intro and introduces the book, Umbra Audio, and several voices. |
| 6 | One chapter/section per file | One content track per manuscript chapter. |
| 7 | ≤ 119 min per track | Longest chapter today ≈ 38 min (Salvage); no split needed, but the builder flags any track > 119 min. |
| 8 | Chapter announcement at start of each file | Narrator-voice announcement «Chapter N, Title.» synthesized and prepended to every story chapter track (Salvage already self-announces «…Chapter one, Salvage.» so it is not duplicated). |
| 9 | 1–5 s silence at start and end of each track | 2 s silence applied to both ends of every track during encoding. |
| 10 | No plosives/pops/mouse clicks | Existing audio pipeline (utterance-level generation + LCM/resampled TTS) has no live recording artifacts. |
| 11 | −23 to −18 dB RMS | Chapters are normalized to a −20 dB RMS target; builder measures and reports RMS per track (ffmpeg volumedetect). |
| 12 | Peak ≤ −3 dB | Global peak limit −3 dB applied by the normalizer; builder reports peak per track. |
| 13 | Noise floor ≤ −60 dB RMS | Digital silence between utterances is ≥ −90 dB; TTS output is clean. |
| 14 | 192 kbps or higher | 192 kbps CBR on all tracks. |
| 15 | MP3 file format | .mp3 for every track. |
| 16 | Constant Bit Rate (CBR) | -b:a 192k with libmp3lame = CBR. |
| 17 | 44.1 kHz | -ar 44100 on every track (source WAVs are 24 kHz → resampled). |
| 18 | ≤ 170 MB per file | Longest chapter output is well under; total package ≈ 400 MB across ~34 files. |
Opening / closing / sample / chapter-track rules (from AR's intros guidance)
Opening track (…_01_Opening_Credits.mp3, < 3 min)
- Must include title, author, narrator, matching cover art + metadata exactly.
- Text (from
m4b_meta.json, kept in sync):This is Deal Hunter. Written by B. D. Murphy, and read for you by Umbra Audio.
- Narrator voice, ≈ 25–45 s including silence.
Sample track (…_02_Sample.mp3, 1–5 min)
- Chapter 1 ("Welcome to the audio book Deal Hunter…") — the purpose-built intro.
Chapter tracks (…_NN_Title.mp3)
- One per content chapter, in manuscript order, correctly sorted (zero-padded sequence in the file name).
- Spoken announcement prepended for story chapters that do not already self-announce.
Closing track (…_NN_Closing_Credits.mp3, < 3 min)
- Base = final track of the book, Chapter 33 in the UI («Thank You for listening to Deal Hunter…» / About-the-author).
- Appended narrator line provides the required book-ending reference + full credits:
The End. You have been listening to Deal Hunter. Written by B. D. Murphy, and read for you by Umbra Audio. Published by WorstAuthor LLC. Artwork by WorstAuthor LLC.
3. Track manifest (as of this session)
Sequence numbers are assigned at build time (opening first, then sample, then chapters in book order, closing last). Base name derives from m4b_meta.json output_name → Deal_Hunter.
| Seq | File | Role | Source |
|---|---|---|---|
| 01 | Deal_Hunter_01_Opening_Credits.mp3 |
opening (synth) | narrator TTS from template |
| 02 | Deal_Hunter_02_Sample.mp3 |
sample | ch1_full.wav (promo intro) |
| 03 | Deal_Hunter_03_Copyright.mp3 |
chapter | ch4_full.wav (Copyright) |
| 04 | Deal_Hunter_04_Authors_Note.mp3 |
chapter | ch5_full.wav (Author's Note) |
| 05 | Deal_Hunter_05_Salvage.mp3 |
chapter | ch6_full.wav (self-announces) |
| 06… | Deal_Hunter_06_Family_Reunion.mp3 … |
chapter | ch7..ch32_full.wav + synth announcement |
| NN | Deal_Hunter_NN_Closing_Credits.mp3 |
closing | ch35_full.wav + appended credits |
Notes:
- Empty title-page chapters (
Title,DEAL HUNTERecho) are skipped (no audio). - Chapter-track sequence
NNis zero-padded so retailer file-sort = book order. - Announcement numbering follows the book's story order (Salvage = chapter one), e.g. Family Reunion → «Chapter 2, Family Reunion.»
4. Build flow (dashboard → /api/audiobook/export-mp3)
m4b_meta.json (title/author/narrator/publisher) ─┐
project.db (chapter order + titles + first ütterance) │
chapter_audio/chN_full.wav (content) ───────────┴─▶ ARPackageExporter
voices/narrator_ref.wav (voice for new narration) ─▶ 1. synth opening/announcements/closing-tail
2. assemble per-track concatenation
3. encode 44.1 kHz / mono / 192k CBR MP3
with 2 s leading+trailing silence
4. measure duration/size/RMS/peak per track
5. write export/ar_package/ + ar_package.json
- Work/narration clips cache to
export/ar_work/(announcements regenerated only when text changes). - Background thread mirrors the existing M4B export UX (status JSON + polling).
- Output lands under
export/so the existing Step-7 download list picks it up automatically.
5. New/changed code
| File | Change |
|---|---|
specs/16-audiobook-production.md |
This spec |
umbraaudio/assembly/ar_export.py |
New ARPackageExporter (build + stats + manifest) |
umbraaudio/web/routes.py |
POST /api/audiobook/export-mp3, GET /api/audiobook/ar-status, GET /api/audiobook/ar-files |
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, andNarratoris the correct value. Aspeaker_nameofscene_breakis 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 thescene_breaktype only, neverspeaker_name. Both generation paths short-circuit before voice resolution —generate_all_audio.py:167andchapter_builder.py:514(write_scene_break_audio(out)thencontinue, ahead of_resolve_voice_refat: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).
- RMS in [−23, −18] dB, peak ≤ −3 dB (volumedetect). Manual spot-check on final files.
- Every chapter track has a spoken announcement; opening/closing ≤ 3 min; sample 1–5 min.
- No track > 119 min; no file > 170 MB.
- Naming sorted so retailer display order == book order.
7. Open decisions / risks
- Qwen TTS server dependency: opening/closing/announcement narration needs the
TTS server (port 8881) up with the narrator vmatrix cached (
prompt_slug="narrator"). If it is down the build fails fast withQwenTTSUnavailableError. (~26 short clips.) - "About the author" becomes part of the closing track. If instead it should stay
as its own numbered chapter track, move its role to
chapterin_ABOUT_AUTHOR_ROLE. - Loudness: if final per-track RMS drifts outside [−23, −18] dB the encode should
add
-af loudnorm; today chapters are normalized to −20 dB so this is expected-pass. - Narrator metadata mismatch: opening/closing text is templated from
m4b_meta.json; any change to title/author/narrator there must be re-exported (or spoken credits won't match the metadata/cover as AR requires).