Generate provisional dictation audio so dictée missions can be played

Manual testing confirmed the "La clairière de l'écho" dictée mission
was unplayable: no audio file existed yet (decisions.md point 8), so
there was nothing to listen to before transcribing the sentence.

Adds tools/generate_dictation_audio.py (offline eSpeak NG synthesis +
ffmpeg encoding to Ogg Vorbis, exposed as `make
generate-dictation-audio`) and uses it to generate
ce1_accord_adjectif_dictee_01.ogg. A new headless test
(tests/data/test_dictation_audio.gd) guards against future dictées
being added without regenerating their audio.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Denis Defolie
2026-10-11 18:40:11 +02:00
parent 04a1b5ee1b
commit 73440ba226
10 changed files with 184 additions and 4 deletions
+26
View File
@@ -0,0 +1,26 @@
extends GutTest
## L'absence de fichier audio pour une dictee bloque tout test manuel de ce
## type d'exercice (voir docs/specs/decisions.md, point 19) : ce test garantit
## que chaque dictee marquee `audio_provisional: true` a reellement un fichier
## `.ogg` chargeable, pas seulement une entree de donnees qui le promet.
func test_every_provisional_dictation_has_a_loadable_audio_file() -> void:
var dictation_paths := DataLoader.find_json_files("res://data/dictations")
assert_false(dictation_paths.is_empty(), "il doit y avoir au moins une dictee a verifier")
for path: String in dictation_paths:
var dictation: Dictionary = DataLoader.read_json(path)
if not dictation.get("audio_provisional", false):
continue
var audio_id: String = dictation["audio_id"]
var audio_path := "res://assets/audio/dictations/%s.ogg" % audio_id
assert_true(
ResourceLoader.exists(audio_path),
"audio manquant pour %s : %s" % [audio_id, audio_path],
)
var stream: Variant = load(audio_path)
assert_true(
stream is AudioStream, "le fichier charge pour %s doit etre un AudioStream" % audio_id
)
+1
View File
@@ -0,0 +1 @@
uid://cjjn1kkkc0is6