Files
UmbraAudio/specs/16-audiobook-production.md
B.D. Murphy 6ca8f80087 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.
2026-09-26 07:51:44 -07:00

9.5 KiB
Raw Permalink Blame History

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 HUNTER echo) are skipped (no audio).
  • Chapter-track sequence NN is 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, 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).
  • 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 with QwenTTSUnavailableError. (~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 chapter in _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).