md2speech 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,30 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]"
28
+
29
+ - name: Run unit tests
30
+ run: pytest -m "not integration"
@@ -0,0 +1,57 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+
22
+ - name: Install dependencies
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install -e ".[dev]"
26
+
27
+ - name: Run unit tests
28
+ run: pytest -m "not integration"
29
+
30
+ publish:
31
+ needs: test
32
+ runs-on: ubuntu-latest
33
+ environment:
34
+ name: pypi
35
+ url: https://pypi.org/project/md2speech/
36
+
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+
40
+ - uses: actions/setup-python@v5
41
+ with:
42
+ python-version: "3.12"
43
+
44
+ - name: Install build tools
45
+ run: python -m pip install --upgrade pip build
46
+
47
+ - name: Build wheel and sdist
48
+ run: python -m build
49
+
50
+ - name: Publish to PyPI
51
+ uses: pypa/gh-action-pypi-publish@release/v1
52
+
53
+ - name: Create GitHub Release
54
+ uses: softprops/action-gh-release@v2
55
+ with:
56
+ generate_release_notes: true
57
+ files: dist/*
@@ -0,0 +1,20 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .coverage
9
+ htmlcov/
10
+ *.mp3
11
+ *.wav
12
+ .DS_Store
13
+ dist/
14
+ build/
15
+ .pytest_cache/
16
+ .coverage
17
+ htmlcov/
18
+ *.mp3
19
+ *.wav
20
+ .DS_Store
@@ -0,0 +1,3 @@
1
+ {
2
+ "default": false
3
+ }
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 md2speech contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,520 @@
1
+ # Agent Prompt: Standalone Markdown/Text-to-Speech Python Package
2
+
3
+ > **Generated:** 2026-07-08
4
+ > **Purpose:** Implement a **completely independent** Python package (no DriveSpeak code, imports, or repo coupling) that converts Markdown or plain-text files to speech audio using the same **free, local** TTS stack as DriveSpeak: **Kokoro-82M** via the `kokoro` Python package.
5
+ > **Reference only:** A prior analysis of DriveSpeak's TTS pipeline is summarized in §3 below. Use it as architectural guidance — do **not** copy DriveSpeak source files or depend on `drivespeak_ml`.
6
+
7
+ ---
8
+
9
+ ## How to Use This Prompt
10
+
11
+ This is a single deliverable: a new, self-contained Python package with CLI, library API, tests, and user documentation. Execute the phases in order. Verify acceptance criteria after each phase before proceeding.
12
+
13
+ ```
14
+ Phase 1 — Project scaffold & packaging
15
+ Phase 2 — Text ingestion (Markdown + plain text)
16
+ Phase 3 — TTS engine (Kokoro) & long-text synthesis
17
+ Phase 4 — Audio post-processing & format conversion
18
+ Phase 5 — CLI & library public API
19
+ Phase 6 — Tests, docs, and install verification
20
+ ```
21
+
22
+ **Critical rules:**
23
+
24
+ 1. The package must be **fully standalone** — installable via `pip install .` or `uv sync` from its own directory, with zero references to DriveSpeak paths, modules, or configs.
25
+ 2. **Default engine is Kokoro only** — free, local, offline. Do not add paid API dependencies (ElevenLabs, OpenAI TTS, etc.).
26
+ 3. **First-run experience must work** — installation pulls Python dependencies; model weights download automatically on first synthesis (via `huggingface-hub`, as Kokoro does).
27
+ 4. **Default output format is MP3** — the package must handle encoding itself (not require the user to run external tools manually).
28
+ 5. Write **comprehensive user documentation** in `README.md` (see §8).
29
+
30
+ ---
31
+
32
+ ## 1. Product Requirements
33
+
34
+ ### 1.1 CLI Interface
35
+
36
+ Implement a console script (e.g. `md2speech` or `text2speech` — choose a clear, memorable name and use it consistently).
37
+
38
+ **Required positional argument:**
39
+
40
+ | Argument | Description |
41
+ |----------|-------------|
42
+ | `input` | Path to a `.md`, `.markdown`, or `.txt` file |
43
+
44
+ **Optional arguments:**
45
+
46
+ | Flag | Default | Description |
47
+ |------|---------|-------------|
48
+ | `--output` / `-o` | Derived from input filename | Output audio file path |
49
+ | `--format` / `-f` | `mp3` | Output audio format (see §1.3) |
50
+ | `--voice` / `-v` | `af_heart` | Kokoro voice ID (document available voices) |
51
+ | `--speed` | `1.0` | Speech speed multiplier |
52
+ | `--lang` | `a` | Kokoro language code (`a` = American English) |
53
+ | `--no-normalize` | off | Skip text normalization (numbers, abbreviations, etc.) |
54
+ | `--paragraph-pause` | `0.4` | Seconds of silence between paragraphs |
55
+ | `--verbose` / `-v` (or `-V` if `-v` is voice) | off | Enable debug logging |
56
+
57
+ **Default output path logic** (when `--output` is omitted):
58
+
59
+ - Same directory as the input file
60
+ - Basename matches input basename
61
+ - Extension matches `--format` (e.g. `article.md` → `article.mp3`)
62
+
63
+ **Example invocations** (must work after install):
64
+
65
+ ```bash
66
+ # Minimal — converts article.md → article.mp3 in the same folder
67
+ md2speech article.md
68
+
69
+ # Explicit output path and format
70
+ md2speech chapter.txt -o ~/Audio/chapter.wav --format wav
71
+
72
+ # Long Markdown document with a specific voice
73
+ md2speech book.md -o book.mp3 --voice am_adam --speed 0.95
74
+ ```
75
+
76
+ ### 1.2 Library API
77
+
78
+ Expose a clean Python API (not only CLI) so the package can be imported:
79
+
80
+ ```python
81
+ from <package_name> import synthesize_file
82
+
83
+ result = synthesize_file(
84
+ "article.md",
85
+ output_path="article.mp3", # optional
86
+ output_format="mp3", # optional, default "mp3"
87
+ voice="af_heart",
88
+ speed=1.0,
89
+ )
90
+ # result.path → Path to written audio file
91
+ # result.duration_seconds → float
92
+ ```
93
+
94
+ Also expose lower-level building blocks (`read_document`, `extract_plain_text`, `TTSEngine`, `AudioWriter`) for advanced users. Document all public symbols.
95
+
96
+ ### 1.3 Supported Output Formats
97
+
98
+ | Format | Required | Notes |
99
+ |--------|----------|-------|
100
+ | `mp3` | **Yes (default)** | Primary delivery format |
101
+ | `wav` | Yes | Uncompressed PCM |
102
+ | `ogg` | Nice to have | Vorbis via pydub/ffmpeg |
103
+ | `flac` | Nice to have | Lossless |
104
+
105
+ Implementation: use **`pydub`** (backed by **ffmpeg**) for MP3/OGG/FLAC encoding. Declare `ffmpeg` as a **system prerequisite** in the README and detect it at runtime with a clear error message if missing.
106
+
107
+ For WAV, write directly with `soundfile` (no ffmpeg needed). For MP3 and other compressed formats, pydub/ffmpeg is the standard approach.
108
+
109
+ ### 1.4 Input File Handling
110
+
111
+ **Plain text (`.txt`):** Read as UTF-8. Handle BOM if present.
112
+
113
+ **Markdown (`.md`, `.markdown`):**
114
+
115
+ 1. Read as UTF-8.
116
+ 2. Strip YAML front matter if present (`---` delimited block at top).
117
+ 3. Convert Markdown to plain speakable text:
118
+ - Remove or ignore HTML comments.
119
+ - Strip inline/formatting markup (bold, italic, links → link text only, code spans → speak the code text or skip fenced code blocks — **document the choice**; prefer skipping fenced code blocks and speaking inline code literally).
120
+ - Convert headings to plain text (do not say "hash hash heading").
121
+ - Preserve paragraph breaks (`\n\n`) for natural pacing.
122
+ - Preserve list items as speakable lines.
123
+ 4. Do **not** require a full Markdown renderer with network access. Use a lightweight approach (`markdown-it-py`, `mistune`, or regex-based stripping). Keep dependencies minimal.
124
+
125
+ Reject unsupported extensions with a clear error.
126
+
127
+ ---
128
+
129
+ ## 2. Technical Architecture
130
+
131
+ ### 2.1 Recommended Package Layout
132
+
133
+ Create the package in a **new top-level directory** inside the workspace (sibling to `ml/`, not inside it):
134
+
135
+ ```
136
+ <text2speech-package>/
137
+ ├── pyproject.toml # Metadata, dependencies, console_scripts entry point
138
+ ├── README.md # Comprehensive user documentation (§8)
139
+ ├── LICENSE # MIT (or Apache 2.0 to match Kokoro)
140
+ ├── src/
141
+ │ └── <package_name>/
142
+ │ ├── __init__.py # Public API exports
143
+ │ ├── cli.py # argparse/typer CLI
144
+ │ ├── reader.py # File reading, Markdown → plain text
145
+ │ ├── normalize.py # Text normalization (numbers, abbreviations)
146
+ │ ├── engine.py # Kokoro TTS wrapper
147
+ │ ├── synthesize.py # Long-text chunking & orchestration
148
+ │ ├── audio.py # Post-processing, resampling, format export
149
+ │ └── __main__.py # python -m <package_name> support
150
+ ├── tests/
151
+ │ ├── test_reader.py
152
+ │ ├── test_normalize.py
153
+ │ ├── test_audio.py
154
+ │ └── fixtures/
155
+ │ ├── sample.txt
156
+ │ └── sample.md
157
+ └── .python-version # 3.11 or 3.12
158
+ ```
159
+
160
+ Use **`hatchling`** or **`uv`** as the build backend (match modern Python packaging). Pin `requires-python = ">=3.11,<3.13"` (Kokoro/torch compatibility).
161
+
162
+ ### 2.2 Core Dependencies
163
+
164
+ | Package | Purpose |
165
+ |---------|---------|
166
+ | `kokoro` | TTS engine (wraps Kokoro-82M) |
167
+ | `torch` | Kokoro runtime |
168
+ | `numpy` | Audio arrays |
169
+ | `scipy` | Resampling (polyphase) |
170
+ | `soundfile` | WAV I/O |
171
+ | `pydub` | MP3/OGG/FLAC encoding |
172
+ | `inflect` | Number/word normalization |
173
+ | `tqdm` | Optional progress for long documents |
174
+
175
+ **System dependency:** `ffmpeg` must be on `PATH` for MP3 export. Document install commands for macOS (`brew install ffmpeg`), Ubuntu (`apt install ffmpeg`), and Windows (`winget`/`choco`).
176
+
177
+ Do **not** add DriveSpeak, `drivespeak_ml`, or Firebase as dependencies.
178
+
179
+ ### 2.3 Console Script Entry Point
180
+
181
+ In `pyproject.toml`:
182
+
183
+ ```toml
184
+ [project.scripts]
185
+ md2speech = "<package_name>.cli:main"
186
+ ```
187
+
188
+ (Replace `md2speech` and `<package_name>` with your chosen names.)
189
+
190
+ ---
191
+
192
+ ## 3. Reference Architecture (from DriveSpeak — guidance only)
193
+
194
+ DriveSpeak's free TTS pipeline uses these parameters. Replicate the **behavior**, not the code:
195
+
196
+ ### 3.1 TTS Model & Engine
197
+
198
+ | Setting | Value |
199
+ |---------|-------|
200
+ | Model | `hexgrad/Kokoro-82M` on Hugging Face |
201
+ | Python API | `from kokoro import KPipeline` |
202
+ | Language code | `"a"` (American English) |
203
+ | Native sample rate | 24,000 Hz |
204
+ | Inference | Local CPU or GPU via PyTorch |
205
+
206
+ **Engine wrapper pattern:**
207
+
208
+ ```python
209
+ pipeline = KPipeline(lang_code="a", repo_id="hexgrad/Kokoro-82M", device=None)
210
+ for result in pipeline(text, voice=voice_id, speed=speed):
211
+ # collect result.audio tensors → concatenate → float32 numpy array
212
+ ```
213
+
214
+ ### 3.2 Recommended Voices
215
+
216
+ Document these in the README:
217
+
218
+ | Voice ID | Gender | Character |
219
+ |----------|--------|-----------|
220
+ | `af_heart` | Female | Warm, clear — **default** |
221
+ | `am_adam` | Male | Deep, steady |
222
+ | `af_bella` | Female | Soft |
223
+ | `am_michael` | Male | Conversational |
224
+
225
+ Full list: 11 female (`af_*`) and 9 male (`am_*`) — see Kokoro docs.
226
+
227
+ ### 3.3 Long-Text Synthesis Strategy
228
+
229
+ For documents with multiple paragraphs, **do not** send the entire file as one Kokoro call. Instead:
230
+
231
+ 1. Split text on `\n\n` into paragraphs.
232
+ 2. Within each paragraph, split on `\n` into lines.
233
+ 3. Synthesize each line separately.
234
+ 4. Insert **400 ms silence** between paragraphs (configurable via `--paragraph-pause`).
235
+ 5. Concatenate all audio into one output file.
236
+
237
+ Apply text normalization before each synthesis call.
238
+
239
+ ### 3.4 Text Normalization
240
+
241
+ Before TTS, expand:
242
+
243
+ - Abbreviations (`Dr.` → `Doctor`, `Mr.` → `Mister`, etc.)
244
+ - Ordinals (`1st` → `first`)
245
+ - Currency (`$12.50` → `twelve dollars and fifty cents`)
246
+ - Percentages (`85%` → `eighty-five percent`)
247
+ - Times (`3:45 PM` → spoken form)
248
+ - Plain numbers → words
249
+
250
+ Use the `inflect` library. This dramatically improves speech quality for non-fiction text.
251
+
252
+ ### 3.5 Audio Post-Processing
253
+
254
+ Apply this chain before format export:
255
+
256
+ | Step | Parameter |
257
+ |------|-----------|
258
+ | Peak normalization | **-1.5 dBFS** |
259
+ | Silence padding | **100 ms** before and after the final concatenated audio |
260
+ | Resampling | 24 kHz → **44,100 Hz** (scipy `resample_poly`) |
261
+ | Export | User-selected format |
262
+
263
+ DriveSpeak targets `.caf` (AAC) for iOS; this package targets **MP3 by default** instead.
264
+
265
+ ---
266
+
267
+ ## 4. Phase-by-Phase Implementation
268
+
269
+ ### Phase 1 — Project Scaffold & Packaging
270
+
271
+ **Tasks:**
272
+
273
+ 1. Create the package directory structure (§2.1).
274
+ 2. Write `pyproject.toml` with name, version `0.1.0`, description, authors, license, Python bound, dependencies, and console script.
275
+ 3. Add `src/<package_name>/__init__.py` with version string.
276
+ 4. Verify `pip install -e .` or `uv sync && uv pip install -e .` succeeds.
277
+
278
+ **Acceptance criteria:**
279
+
280
+ - [ ] `pip install -e .` completes without error
281
+ - [ ] `md2speech --help` prints usage
282
+ - [ ] Package imports: `python -c "import <package_name>"`
283
+
284
+ ---
285
+
286
+ ### Phase 2 — Text Ingestion
287
+
288
+ **Tasks:**
289
+
290
+ 1. Implement `read_document(path: Path) -> str` in `reader.py`.
291
+ 2. Support `.txt`, `.md`, `.markdown`.
292
+ 3. Implement Markdown → plain text extraction (§1.4).
293
+ 4. Add unit tests with fixture files (short `.txt` and `.md` with headings, links, code blocks, front matter).
294
+
295
+ **Acceptance criteria:**
296
+
297
+ - [ ] `# Title\n\nHello **world**` → speakable text without markup
298
+ - [ ] Front matter stripped
299
+ - [ ] Fenced code blocks handled consistently (documented behavior)
300
+ - [ ] Tests pass
301
+
302
+ ---
303
+
304
+ ### Phase 3 — TTS Engine & Long-Text Synthesis
305
+
306
+ **Tasks:**
307
+
308
+ 1. Implement `engine.py` — thin `TTSEngine` class wrapping `KPipeline`.
309
+ 2. Implement `normalize.py` — text normalization (§3.4).
310
+ 3. Implement `synthesize.py`:
311
+ - `synthesize_text(text, voice, speed) -> np.ndarray` for short strings
312
+ - `synthesize_long_text(text, voice, speed, paragraph_pause) -> np.ndarray` with chunking (§3.3)
313
+ 4. Lazy-load the pipeline (instantiate on first use, not import).
314
+ 5. Log model download progress on first run.
315
+
316
+ **Acceptance criteria:**
317
+
318
+ - [ ] Short string synthesizes to 24 kHz float32 numpy array
319
+ - [ ] Multi-paragraph input produces audio with audible pauses between paragraphs
320
+ - [ ] Normalization expands `Dr. Smith paid $5` correctly
321
+ - [ ] Voice and speed parameters work
322
+
323
+ ---
324
+
325
+ ### Phase 4 — Audio Post-Processing & Format Conversion
326
+
327
+ **Tasks:**
328
+
329
+ 1. Implement `audio.py`:
330
+ - `peak_normalize(audio, target_dbfs=-1.5)`
331
+ - `add_silence_padding(audio, sample_rate, pad_ms=100)`
332
+ - `resample(audio, orig_sr, target_sr=44100)`
333
+ - `write_audio(audio, path, format, sample_rate=44100)` — dispatches to soundfile (WAV) or pydub (MP3/OGG/FLAC)
334
+ 2. Implement ffmpeg presence check with actionable error:
335
+ ```
336
+ Error: ffmpeg not found. MP3 export requires ffmpeg.
337
+ Install: brew install ffmpeg (macOS)
338
+ ```
339
+ 3. Return metadata (duration, output path) from write function.
340
+
341
+ **Acceptance criteria:**
342
+
343
+ - [ ] WAV export works without ffmpeg
344
+ - [ ] MP3 export produces playable file when ffmpeg is installed
345
+ - [ ] Peak level is consistent across outputs
346
+ - [ ] Output sample rate is 44,100 Hz
347
+
348
+ ---
349
+
350
+ ### Phase 5 — CLI & Public API
351
+
352
+ **Tasks:**
353
+
354
+ 1. Wire `cli.py` with `argparse` (or `typer` if you prefer).
355
+ 2. Implement `synthesize_file()` in `__init__.py` or `synthesize.py`.
356
+ 3. Add `python -m <package_name>` entry via `__main__.py`.
357
+ 4. Add `--verbose` logging (show paragraph count, synthesis progress, output path, duration).
358
+ 5. Handle errors gracefully: missing input file, unsupported format, missing ffmpeg, empty document.
359
+
360
+ **Acceptance criteria:**
361
+
362
+ - [ ] `md2speech sample.md` creates `sample.mp3` next to input
363
+ - [ ] `md2speech sample.txt -o /tmp/out.wav --format wav` works
364
+ - [ ] `--help` documents all options
365
+ - [ ] Exit code 0 on success, non-zero on failure
366
+ - [ ] Library API works from Python REPL
367
+
368
+ ---
369
+
370
+ ### Phase 6 — Tests, Documentation & Install Verification
371
+
372
+ **Tasks:**
373
+
374
+ 1. Write tests (§2.1). Mock Kokoro in unit tests to avoid downloading the model in CI; optionally mark one integration test `@pytest.mark.integration` that runs real synthesis.
375
+ 2. Write comprehensive `README.md` (§8).
376
+ 3. Add a `LICENSE` file.
377
+ 4. Run full install on a clean virtualenv and convert a sample file end-to-end.
378
+
379
+ **Acceptance criteria:**
380
+
381
+ - [ ] `pytest` passes (unit tests; integration test optional/skipped in CI)
382
+ - [ ] README covers all sections in §8
383
+ - [ ] Fresh `pip install .` → `md2speech` works on a sample file
384
+ - [ ] No references to DriveSpeak in code or user-facing docs (optional brief credit in README acknowledgments is fine)
385
+
386
+ ---
387
+
388
+ ## 5. Error Handling & Edge Cases
389
+
390
+ | Scenario | Behavior |
391
+ |----------|----------|
392
+ | Input file not found | Clear error, exit 1 |
393
+ | Empty file after extraction | Clear error, exit 1 |
394
+ | Unsupported extension | Clear error listing supported types |
395
+ | ffmpeg missing for MP3 | Actionable install instructions |
396
+ | Very long document (100+ paragraphs) | Show tqdm progress, do not load entire audio in duplicate |
397
+ | GPU available | Pass `device="cuda"` or `device="mps"` if torch detects it; default CPU |
398
+ | Model download fails (no network) | Clear error mentioning Hugging Face access |
399
+
400
+ ---
401
+
402
+ ## 6. Non-Goals (Do Not Implement)
403
+
404
+ - Real-time / streaming playback
405
+ - SSML or phoneme-level control
406
+ - Voice cloning or custom voice training
407
+ - Paid TTS APIs (ElevenLabs, OpenAI, Google Cloud TTS)
408
+ - Web UI or REST server (CLI + library only)
409
+ - DriveSpeak `.caf` output (iOS-specific; MP3/WAV is sufficient)
410
+ - PDF or DOCX input (text/Markdown only for v0.1)
411
+
412
+ ---
413
+
414
+ ## 7. Quality Bar
415
+
416
+ - **Type hints** on all public functions
417
+ - **`logging`** module (not bare `print`) for operational messages; `print` only for CLI output if desired
418
+ - **No over-engineering** — a focused v0.1 beats a framework
419
+ - **Consistent naming** — pick `md2speech` or `text2speech` as the CLI name and stick with it
420
+ - **Pinned or bounded dependencies** in `pyproject.toml` for reproducible installs
421
+
422
+ ---
423
+
424
+ ## 8. README Documentation Requirements
425
+
426
+ The `README.md` must include all of the following sections:
427
+
428
+ 1. **Overview** — What the tool does in 2–3 sentences
429
+ 2. **Features** — Bullet list (free, offline, Markdown support, MP3 default, etc.)
430
+ 3. **Requirements** — Python version, ffmpeg (for MP3), disk space for model (~hundreds of MB)
431
+ 4. **Installation**
432
+ - `pip install .` from source
433
+ - `uv sync` alternative
434
+ - ffmpeg install per platform
435
+ - Note that first run downloads Kokoro-82M from Hugging Face
436
+ 5. **Quick Start** — Copy-paste examples (at least 3)
437
+ 6. **CLI Reference** — Table of all arguments
438
+ 7. **Python API** — `synthesize_file()` example and advanced imports
439
+ 8. **Voice Reference** — Table of recommended Kokoro voice IDs
440
+ 9. **Supported Formats** — mp3, wav, etc. and which need ffmpeg
441
+ 10. **How It Works** — Brief pipeline diagram or numbered steps (read → normalize → chunk → synthesize → post-process → encode)
442
+ 11. **Markdown Handling** — What markup is stripped, how code blocks are treated
443
+ 12. **Troubleshooting** — ffmpeg not found, model download, torch/MPS/CUDA, memory
444
+ 13. **License** — MIT/Apache
445
+ 14. **Acknowledgments** — Kokoro-82M (hexgrad), Hugging Face
446
+
447
+ ---
448
+
449
+ ## 9. Suggested Package Names
450
+
451
+ Pick one during implementation (check PyPI availability if publishing):
452
+
453
+ | Name | CLI command |
454
+ |------|-------------|
455
+ | `md2speech` | `md2speech` |
456
+ | `text2speech` | `text2speech` |
457
+ | `kokoro-read` | `kokoro-read` |
458
+ | `speakmd` | `speakmd` |
459
+
460
+ ---
461
+
462
+ ## 10. Final Deliverables Checklist
463
+
464
+ Before marking the task complete, confirm:
465
+
466
+ - [ ] Standalone package directory with `pyproject.toml`
467
+ - [ ] `pip install .` installs all Python dependencies
468
+ - [ ] CLI: `md2speech <input.md> [-o path] [--format mp3]`
469
+ - [ ] Default output: same basename as input, `.mp3` extension, same directory
470
+ - [ ] Markdown and plain text supported
471
+ - [ ] Kokoro-82M local TTS (free, no API key)
472
+ - [ ] MP3 export via pydub/ffmpeg; WAV via soundfile
473
+ - [ ] Long-text paragraph chunking with pauses
474
+ - [ ] Text normalization (numbers, abbreviations)
475
+ - [ ] Audio post-processing (normalize, pad, resample to 44.1 kHz)
476
+ - [ ] Comprehensive `README.md`
477
+ - [ ] Unit tests with mocked TTS
478
+ - [ ] No DriveSpeak code dependencies
479
+
480
+ ---
481
+
482
+ ## 11. Smoke Test Script
483
+
484
+ After implementation, this sequence must succeed on macOS/Linux:
485
+
486
+ ```bash
487
+ cd <package-directory>
488
+ python -m venv .venv && source .venv/bin/activate
489
+ pip install -e ".[dev]" # or: uv sync
490
+
491
+ # Verify ffmpeg for MP3
492
+ ffmpeg -version
493
+
494
+ # Create a test file
495
+ cat > /tmp/test_doc.md << 'EOF'
496
+ ---
497
+ title: Test
498
+ ---
499
+
500
+ # Introduction
501
+
502
+ Dr. Smith ordered 3 cups of coffee at 9:15 AM.
503
+
504
+ ## Details
505
+
506
+ The total was **$12.50**. Visit [our site](https://example.com) for more.
507
+ EOF
508
+
509
+ # Convert
510
+ md2speech /tmp/test_doc.md -o /tmp/test_doc.mp3 --verbose
511
+
512
+ # Verify output exists and is non-trivial size
513
+ test -f /tmp/test_doc.mp3
514
+ test $(stat -f%z /tmp/test_doc.mp3 2>/dev/null || stat -c%s /tmp/test_doc.mp3) -gt 1000
515
+ echo "Smoke test passed"
516
+ ```
517
+
518
+ ---
519
+
520
+ *End of agent prompt.*