Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
7d54c64
Add REST API server, standalone web interface, and missing pieces
claude Jul 25, 2026
13d2afd
feat(app): preset narration voices, French UI, long-text chunking & p…
Jul 27, 2026
c61f509
feat(app): archive every generation to output/ with a descriptive fil…
Jul 27, 2026
a26dc0e
feat(app): expose max-characters-per-chunk as a UI slider
Jul 27, 2026
a7a95e8
feat(app): optional per-language voice filtering
Jul 27, 2026
4250e2a
feat(scripts): robust whole-book narration + long-form usage guide
Jul 27, 2026
8c7e555
perf(engine): use float32 on CPU for low-precision checkpoints (~1.5x…
Jul 27, 2026
08b992d
feat(narrate_book): experimental --continuity (prompt-cache chaining)
Jul 27, 2026
e43c57d
feat(narration): torch-free pipeline package for audiobook production
Jul 28, 2026
7031c55
feat(app): "Livre audio" tab driving the full narration pipeline
Jul 28, 2026
2a81860
feat(scripts): port narrate_book to the pipeline, add assemble_audiobook
Jul 28, 2026
a9bfd80
docs: document the audiobook production chain
Jul 28, 2026
6227320
feat(narration): detect the segments the engine got wrong
Jul 28, 2026
6ac39a8
feat(narrate_book): inspect and re-roll defective segments
Jul 28, 2026
70073b6
feat(app): surface the quality pass in the audiobook tab
Jul 28, 2026
77223fa
docs: explain the quality pass and where its thresholds come from
Jul 28, 2026
bd31ea0
fix(app): preview the voice that is selected, not the text boxes
Jul 28, 2026
f9dccba
feat(voices): seven more French narration voices
Jul 28, 2026
d7bcbb5
refactor(quality): re-base the speech-rate figures on all fourteen vo…
Jul 28, 2026
6767ca3
feat(app): choose and audition the narration voice from the book tab
Jul 28, 2026
8a71bbf
docs: the book tab now picks its own voice
Jul 28, 2026
978c845
feat(narration): repair one segment without re-narrating its chapter
Jul 28, 2026
f35ed2c
feat(app): repair a flagged segment from the audiobook tab
Jul 28, 2026
76a6a08
fix(app): level voice previews so they can be heard and compared
Jul 28, 2026
20b68d5
feat(narration): narrate a book straight from its .epub
Jul 31, 2026
ca161be
feat(epub): stop narrating the licence and the contents page
Jul 31, 2026
655f652
feat(narration): credit the recording the way distributors require
Jul 31, 2026
0adb16d
feat(delivery): produce the folder a distributor actually accepts
Jul 31, 2026
cc700dd
feat(app): check a book against distribution standards from the tab
Jul 31, 2026
c4b4e23
feat(assemble): let the assembled file choose its bitrate
Jul 31, 2026
00dc9e0
feat(epub): carry the book's cover through to the assembled file
Jul 31, 2026
7fe9165
feat(narrate_book): make a book narrated by the script repairable
Jul 31, 2026
21a446d
docs: ffmpeg is installable, and the encoding is now verified
Jul 31, 2026
8aa50bb
feat(polish): the studio chain, and the loudness the platforms actual…
Aug 4, 2026
3bf14de
feat(cloud): narrate from anywhere, and stop giving the model away
Aug 4, 2026
e833309
feat(text_fr): a lexicon entry that knows where it applies
Aug 4, 2026
799e294
feat(text_en): narrate in English
Aug 4, 2026
a47fde7
feat(narrate_book): narrate a whole book in a cloned voice
Aug 4, 2026
0f35a84
feat(voices): a cloned voice can be a preset like any other
Aug 4, 2026
9d160e3
test: keep a bare `pytest` run from aborting on a standalone self-check
Aug 5, 2026
5a98b43
feat(quality): catch a cloning recording its transcript does not cover
Aug 5, 2026
435e827
fix(voices): cut the Edwin reference where its transcript ends
Aug 5, 2026
4d49b36
fix(text_fr): a whole hour swallowed the space that followed it
Aug 5, 2026
0dfc2d0
feat(voices): Aurore, for books and for guided meditation
Aug 5, 2026
9274fd3
feat(voices): Alex Somerset
Aug 5, 2026
b258b10
feat(cloud): renting a GPU per book, without paying twice for the sam…
Aug 7, 2026
7634a66
feat(queue): narrate a catalogue unattended, from Markdown to delivery
Aug 7, 2026
506ca5a
fix(queue): a full disk must not cost nineteen books
Aug 7, 2026
4d7c802
fix(assemble): a French title must not destroy a book already narrated
Aug 7, 2026
caba0bb
fix(chunking): two characters are debris, not a sentence
Aug 7, 2026
e0a5fbc
fix(chunking): an over-long sentence is truncated, not read slowly
Aug 8, 2026
7a46b70
fix(queue): the sweep was missing the 1.6 GB it was written to remove
Aug 8, 2026
981ed98
fix(engine): the badcase message must not be what kills the book
Aug 8, 2026
068c841
feat(pronunciation): hear the candidates before betting twenty books …
Aug 8, 2026
3c6f8ce
fix(pronunciation): the script could not import the app it needs
Aug 8, 2026
0f25ce4
fix(acx): the retail sample must come from the introduction
Aug 8, 2026
44b2b37
fix(prepare): 969 stage directions were about to be read aloud
Aug 8, 2026
385c897
feat(credits): name the synthetic voice without pretending it is a pe…
Aug 8, 2026
4bce2fb
feat(lexicon): stack lexicons, so a book can name its own abbreviations
Aug 8, 2026
5aae71d
fix(queue): five books announced their own file name as their title
Aug 8, 2026
7f2c992
fix(polish): nine books were heading for rejection over two decibels
Aug 8, 2026
0e31400
test(polish): "everything off" must include the stage just added
Aug 8, 2026
b38ea03
feat(audit): find the mispronounced words without listening to them
Aug 8, 2026
f98c771
fix(audit): match Whisper's dtype rather than assume float32
Aug 8, 2026
2636879
fix(audit): report only what the method can actually judge
Aug 8, 2026
a15a189
docs: the text-preparation method, as a skill
Aug 8, 2026
0674277
feat(credits): let the publisher drop the synthetic-voice disclosure
Aug 8, 2026
3bc5fcb
fix(queue): "repaired" was counting attempts, not repairs
Aug 8, 2026
e051011
feat(prepare): a chapter marker that says more than its number
Aug 8, 2026
c61059b
feat(lexicon): a domain dictionary for the catalogue's own vocabulary
Aug 8, 2026
1afc5e7
fix(lexicon): "ces" and "ses" lost their final sound, and now keep it
Aug 8, 2026
d06220f
fix(prepare): one dash in a chapter title, not two
Aug 8, 2026
8103006
fix(prepare): the mispronounced word was a misspelled one
Aug 9, 2026
8b7f582
fix(quality): a lone chapter title is not a runaway generation
Aug 9, 2026
5fe3813
fix(quality): bounding the rate rule left a hole, now closed
Aug 9, 2026
2325816
fix(text_fr): a superscript letter killed a book forty-one minutes in
Aug 9, 2026
e30baea
feat(text_fr): say the symbols a manuscript keeps, or say nothing
Aug 9, 2026
6882cb7
feat(audit): one ranked list for the catalogue, not one report per book
Aug 9, 2026
84f12d4
fix(audit): --merge was documented, wired, and never declared
Aug 9, 2026
6571e2b
fix(engine): an empty string must not kill a book at minute ninety-eight
Aug 9, 2026
cf86c3c
fix(engine): normalisation is a nicety, not a reason to lose a book
Aug 9, 2026
2c1700a
fix(audit): a botched take is not a mispronounced word
Aug 9, 2026
9f079a8
fix(audit): report proper nouns and acronyms, and nothing else
Aug 10, 2026
6cadd03
Aplatir les incises entre parenthèses avant la synthèse
Aug 10, 2026
5fe3d13
Effacer les livrables de la prise précédente avant une reprise
Aug 11, 2026
3a30ad0
fix(text_fr): trois choses que le manuscrit garde et ne dit jamais
Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# cloud_setup.sh est récupéré par `curl | bash` sur une machine Linux. En CRLF,
# le shebang emporte un \r et la machine louée répond « bad interpreter » — sur
# un poste Windows, où core.autocrlf convertit à la volée, l'erreur ne se voit
# jamais avant d'avoir payé l'instance.
*.sh text eol=lf

# Symétriquement, Windows PowerShell 5.1 lit un .ps1 sans BOM en CP1252 : les
# accents se cassent et un tiret cadratin devient un guillemet fermant, donc un
# délimiteur de chaîne. Git ne doit pas toucher à ces fichiers.
*.ps1 -text
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,13 @@ voxcpm.egg-info
.DS_Store
./pretrained_models/
app_local.py

# Generated voice-preview cache (regenerate with scripts/pregenerate_previews.py)
assets/voice_previews/
# Recordings a cloned voice is built from. Never committed: this repository is
# public, and a voice sample is the one asset that lets anyone impersonate its
# owner. The preset entry that points at one is committed; the audio is not.
assets/voices/

# Archived generations
output/
40 changes: 40 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# VoxCPM API server image (GPU-ready, also runs on CPU)
#
# Build:
# docker build -t voxcpm-server .
#
# Run (GPU):
# docker run --gpus all -p 8000:8000 -v voxcpm-cache:/root/.cache voxcpm-server
#
# Run (CPU only — slow, for testing):
# docker run -p 8000:8000 -e VOXCPM_DEVICE=cpu -v voxcpm-cache:/root/.cache voxcpm-server
#
# Then open http://localhost:8000

FROM pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime

ENV PYTHONUNBUFFERED=1 \
TOKENIZERS_PARALLELISM=false \
HF_HUB_ENABLE_HF_TRANSFER=0

RUN apt-get update \
&& apt-get install -y --no-install-recommends git libsndfile1 ffmpeg \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY pyproject.toml README.md LICENSE ./
COPY src ./src
COPY server.py app.py ./
COPY web ./web
COPY assets ./assets

# SETUPTOOLS_SCM_PRETEND_VERSION: .git is not copied into the image, so
# setuptools_scm cannot derive the version from tags.
RUN SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 pip install --no-cache-dir -e ".[server]"

EXPOSE 8000

# Model weights are downloaded on first request and cached in /root/.cache —
# mount a volume there to persist them across container restarts.
CMD ["python", "server.py", "--host", "0.0.0.0", "--port", "8000"]
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,30 @@ voxcpm --help

### Web Demo

Two web interfaces are available:

**VoxCPM Studio — lightweight web UI + REST API** (no funasr required):

```bash
pip install -e ".[server]"
python server.py --port 8000 # then open in browser: http://localhost:8000
```

A modern standalone interface (English/French) covering all three generation modes,
backed by a REST API with an **OpenAI-compatible** `/v1/audio/speech` endpoint
(interactive docs at `http://localhost:8000/docs`). See the
[Guide en français](docs/GUIDE_FR.md) for a full walkthrough.

```bash
# REST API example
curl -X POST http://localhost:8000/api/tts \
-F "text=Hello from the VoxCPM API!" \
-F "control=warm female voice" \
-o out.wav
```

**Gradio demo** (with automatic reference-audio transcription via funasr):

```bash
python app.py --port 8808 # then open in browser: http://localhost:8808
```
Expand Down
1,698 changes: 1,611 additions & 87 deletions app.py

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions conf/lexique/neurosciences.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"_comment": "Termes de neurosciences et de psychotraumatologie du catalogue. S'empile sur conf/pronunciation_fr.json : python scripts/narrate_book.py ... --lexicon conf/pronunciation_fr.json --lexicon conf/lexique/neurosciences.json",

"_regle": "Les entrées ci-dessous sont DÉSACTIVÉES (préfixe _ = ignoré). Écoutez d'abord : le moteur lit peut-être déjà « cortisol » ou « hippocampe » très bien, et une correction inutile ne peut que dégrader. Retirez le préfixe uniquement pour ce que vous avez entendu faux.",

"_methode": "Pour trouver la bonne graphie : python scripts/try_pronunciation.py --phrase '...' --mot amygdale --candidats 'amigdale' 'a-mig-dale' --voice '...'. Le premier extrait produit est toujours le texte non modifié, pour savoir si la correction bat le défaut de départ.",

"_frequences": "Mesurées sur les 20 livres de la file : TSPT 89, cortisol 60, EMDR 59, cortex 59, neuroplasticité 46, dopamine 41, préfrontal 36, hypervigilance 35, hippocampe 30, dissociation 29, rumination 28, sympathique 22, amygdale 20, parasympathique 19, vagal 15, accumbens 3. Un terme lu 89 fois coûte 89 fautes ; un terme lu 3 fois en coûte 3.",

"_sigles": "--- sigles, à épeler ou non selon ce qu'on entend ---",
"_EMDR": "E M D R",
"_TSPT": "T S P T",

"_latin": "--- termes latins, sans règle française qui les gouverne ---",
"_accumbens": "acumbennsse",
"_insula": "inn-sula",

"_anatomie": "--- structures cérébrales ---",
"_amygdale": "amigdale",
"_hippocampe": "hipocampe",
"_thalamus": "talamusse",
"_hypothalamus": "ipotalamusse",
"_cingulaire": "saingulaire",

"_hormones": "--- ---",
"_cortisol": "cortizol",
"_ocytocine": "ocitocine",
"_sérotonine": "sérotonine",

"_etrangers": "--- mots anglais lus avec des règles françaises ---",
"_mindfulness": "maïndfoulness",
"_flashback": "flachbaque",

"_noms": "--- noms propres du domaine ---",
"_Porges": "Porguesse",
"_Shapiro": "Chapiro",
"_Bessel van der Kolk": "Bessel vane der Kolk",
"_Francine": "Francine"
}
14 changes: 14 additions & 0 deletions conf/lexique_hfd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"_comment": "Lexique propre à « TENIR DEBOUT — La dépression masquée des cadres performants » (livre-33-depression-masquee). S'empile sur conf/pronunciation_fr.json, il ne le remplace pas.",

"_pourquoi": "L'abréviation HFD revient 160 fois et n'est jamais développée dans le texte : elle apparaît dès le chapitre 1. À l'écrit, un lecteur remonte quelques pages ou devine ; à l'oral il entend « ache-èf-dé » cent soixante fois sans savoir de quoi on parle. Edwin a choisi de la développer partout.",

"_ordre": "Le lexique applique les clés de la plus longue à la plus courte, ce qui règle les accords : « mécanisme HFD » et « les HFD » sont traités avant « HFD » seul, donc leur forme particulière gagne.",

"mécanisme HFD": "mécanisme de la dépression de haut fonctionnement",

"_pluriel": "Les trois « les HFD » sont suivis d'accords féminins pluriels déjà en place — « non encore prises en charge », « non aiguës » — donc seul le nom est à mettre au pluriel.",
"les HFD": "les dépressions de haut fonctionnement",

"HFD": "dépression de haut fonctionnement"
}
172 changes: 172 additions & 0 deletions conf/preset_voices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
[
{
"name": "Narrateur profond & calme",
"description": "Voix masculine française de narrateur pour livre audio, profonde, calme et posée, timbre chaleureux et rassurant, débit lent et immersif, diction claire et articulée",
"seed": 4110390676,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Narratrice douce & naturelle",
"description": "Voix féminine française de narratrice pour livre audio, douce et naturelle, timbre chaleureux et authentique, débit fluide et posé, diction claire, ton captivant et apaisant",
"seed": 3227543575,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Conteur jeune & dynamique",
"description": "Voix masculine française de jeune conteur d'environ vingt-cinq ans pour livre audio, dynamique et expressive, ton vivant et engageant, débit naturel, idéale pour la narration d'histoires",
"seed": 2151638728,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Narratrice chaleureuse & conversationnelle",
"description": "Voix féminine française d'âge mûr pour livre audio, chaleureuse et engageante, style conversationnel et charmant, ton bienveillant et proche de l'auditeur, diction naturelle",
"seed": 3023399458,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Narrateur documentaire velouté",
"description": "Voix masculine française de narrateur de documentaire, veloutée et posée, ton professionnel empreint de mystère et d'émerveillement, diction soignée, idéale pour nature, science et histoire",
"seed": 1468538221,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Narrateur moderne & professionnel",
"description": "Voix masculine française moderne, claire et profonde, ton assuré et régulier, débit confiant et professionnel, idéale pour la narration, les podcasts et les livres audio contemporains",
"seed": 3515672692,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Méditation guidée (grave & lente)",
"description": "Voix masculine française très grave et profonde pour méditation guidée, extrêmement lente et douce, ton chaud, apaisant et enveloppant, chuchoté et relaxant, longues pauses, respiration calme, idéale pour la détente et la relaxation",
"seed": 560505514,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Conteuse jeune & pétillante",
"description": "Voix féminine française de jeune conteuse d'environ vingt-cinq ans pour livre audio, pétillante et expressive, ton vivant et complice, débit naturel et enjoué, idéale pour la jeunesse et le roman contemporain",
"seed": 1794602311,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Narratrice grave & intense",
"description": "Voix féminine française grave et posée, timbre sombre et affirmé, ton tendu et maîtrisé, débit lent et pesant, diction précise, idéale pour le thriller, le polar et le récit sombre",
"seed": 2884176053,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Vieux conteur au coin du feu",
"description": "Voix masculine française de conteur âgé, timbre patiné et légèrement rocailleux, ton bienveillant et malicieux, débit lent et posé avec des silences, idéale pour les contes, les légendes et les récits pour enfants",
"seed": 3361208947,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Méditation guidée (voix féminine)",
"description": "Voix féminine française très douce et aérienne pour méditation guidée, extrêmement lente, presque chuchotée, ton apaisant et enveloppant, longues pauses, respiration calme, idéale pour la relaxation et le sommeil",
"seed": 1029384756,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Pédagogue clair & précis",
"description": "Voix masculine française neutre et pédagogique, timbre clair et présent, ton didactique et bienveillant, débit régulier et articulé, idéale pour l'essai, le livre pratique et la vulgarisation",
"seed": 2470915638,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Narratrice lumineuse & posée",
"description": "Voix féminine française lumineuse et posée, timbre clair et soyeux, ton élégant et maîtrisé, débit fluide et régulier, diction soignée, idéale pour la littérature blanche et le récit intimiste",
"seed": 4055721384,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Narrateur dramatique & théâtral",
"description": "Voix masculine française théâtrale et ample, timbre riche et projeté, ton dramatique et habité, débit varié avec des ruptures marquées, idéale pour la fiction intense, l'épopée et le récit historique",
"seed": 1613947205,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr"
},
{
"name": "Edwin Dérivé (V1)",
"description": "",
"seed": 1234,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr",
"reference": "assets/voices/edwin_derive_v1_phrase.wav",
"reference_text": "Bienvenue dans cet espace de méditation conçu spécialement pour vous."
},
{
"name": "Aurore — livre audio",
"description": "",
"seed": 1235,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr",
"reference": "assets/voices/aurore_livre.wav",
"reference_text": "Il est neuf heures du matin, dans le bureau parisien d'une grande entreprise de service. Sarah, trente-huit ans, manager depuis cinq ans, entre dans la pièce. Elle a mal dormi. Elle pose son sac, allume son ordinateur. L'écran affiche trois cent quarante-sept mails non lus."
},
{
"name": "Aurore — méditation guidée",
"description": "",
"seed": 1236,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr",
"reference": "assets/voices/aurore_meditation.wav",
"reference_text": "Bienvenue dans cet espace de méditation conçu spécialement pour vous. Pendant les vingt prochaines minutes, je vous invite à mettre en pause vos responsabilités, vos emails et toute forme de distraction pour vous offrir un moment précieux de calme et de recentrage."
},
{
"name": "Alex Somerset",
"description": "",
"seed": 1237,
"cfg": 2.0,
"diffusion_steps": 10,
"normalize": true,
"lang": "fr",
"reference": "assets/voices/alex_somerset.wav",
"reference_text": "Si l'esprit commence à vagabonder, utilisez ce moment comme opportunité pour cultiver patience et bienveillance envers soi-même. Revenez simplement au souffle, sans jugement ni frustration. Nous allons maintenant entamer un cycle respiratoire conscient."
}
]
59 changes: 59 additions & 0 deletions conf/pronunciation_fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"_comment": "Lexique de prononciation. Clé = ce qui est écrit dans le texte, valeur = ce qui doit être prononcé. Le remplacement est insensible à la casse et ne s'applique qu'à des mots entiers. Les clés commençant par _ sont ignorées (commentaires). Utile surtout pour les noms propres, les sigles et les mots étrangers d'un livre donné.",
"_exemple_sigles": "--- sigles lus lettre par lettre ---",
"SNCF": "S N C F",
"RATP": "R A T P",
"ONU": "O N U",
"URSS": "U R S S",
"_exemple_etrangers": "--- mots étrangers ---",
"Wi-Fi": "wifi",
"email": "i-mail",
"_exemple_noms": "--- noms propres à adapter à votre livre ---",
"Nietzsche": "Nitche",
"_comment_contexte": "Une valeur peut aussi être un objet, pour ne remplacer QUE dans un contexte donné : {\"prononcer\": \"...\", \"après\": \"regex\", \"avant\": \"regex\"}. C'est le seul moyen de traiter un homographe : « il est » et « à l'est » s'écrivent pareil et ne se disent pas pareil, donc une règle qui vise le mot seul casse forcément l'un des deux.",
"_comment_homographes": "Les entrées ci-dessous sont des MODÈLES, volontairement désactivées (préfixe _ = ignoré). Écoutez d'abord : si la voix lit déjà correctement « l'est » ou « le couvent », n'y touchez pas — une correction inutile ne peut que dégrader. Quand vous en repérez une fausse, recopiez la ligne sans le préfixe et ajustez l'orthographe phonétique à l'oreille.",
"_est": {
"prononcer": "èsste",
"après": "à l'|dans l'|vers l'|de l'|l'",
"_pourquoi": "le point cardinal, contre le verbe être"
},
"_fils": {
"prononcer": "fisse",
"après": "mon|son|ton|le|un|leur",
"_pourquoi": "l'enfant, contre les fils électriques"
},
"_couvent": {
"prononcer": "couvan",
"après": "le|du|au|ce|un",
"_pourquoi": "le monastère, contre le verbe couver (ils couvent)"
},
"_portions": {
"prononcer": "porcions",
"après": "les|des|deux|trois|quelques",
"_pourquoi": "les parts, contre le verbe porter (nous portions)"
},
"_violent": {
"prononcer": "violan",
"après": "un|le|ce|très|si|plus",
"_pourquoi": "l'adjectif, contre le verbe violer (ils violent)"
},
"_content": {
"prononcer": "contan",
"après": "je suis|il est|elle est|très|si|pas",
"_pourquoi": "l'adjectif, contre le verbe conter (ils content)"
},
"_négligent": {
"prononcer": "négligean",
"après": "un|le|ce|très|si",
"_pourquoi": "l'adjectif, contre le verbe négliger (ils négligent)"
},
"_plus": {
"prononcer": "pluss",
"avant": "de|que|d'",
"_pourquoi": "le s se prononce dans « plus de dix », pas dans « plus tard »"
},
"_comment_ces": "Validé à l'oreille par Edwin le 2026-08-09 : le moteur avale le s final de « ces » et le dit « ce ». « cés » rétablit le son /se/. Ce n'est pas un homographe — « ces » se dit toujours /se/ — donc aucune règle de contexte n'est nécessaire ici, contrairement à « plus » ou « fils ».",
"ces": "cés",
"_comment_ses": "Même défaut que « ces », même correction par analogie : l'accent rétablit le /e/ final. Appliqué sans validation directe à l'oreille — le parallèle est exact, mais si « sés » sonne faux, retirez cette ligne.",
"ses": "sés"
}
Loading