mirror of
https://github.com/deunix-educ/aventuriers-des-mots.git
synced 2026-10-13 02:11:58 +02:00
Replace eSpeak NG with Piper neural voice for dictation audio
Manual test reported the eSpeak NG dictation voice as unintelligible. Formant synthesis is inherently mechanical; slowing it down doesn't fix that. Switched tools/generate_dictation_audio.py to piper-tts (fr_FR-siwis-medium neural voice, MIT/CC-BY 4.0, attribution in assets/CREDITS.md). The voice model (~60MB) downloads once on demand into the untracked .cache/piper-voices/, never at game runtime. Adds piper-tts to tools/setup.sh's venv and a pyrightconfig.json pointing pyright at .venv, needed once tools/ imports a real third-party package (`make lint` was silently checking against the wrong interpreter before). Regenerated ce1_accord_adjectif_dictee_01.ogg with the new voice. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,9 @@ build/
|
|||||||
addons/gut/
|
addons/gut/
|
||||||
.venv/
|
.venv/
|
||||||
|
|
||||||
|
# Modeles de voix telecharges par tools/generate_dictation_audio.py, non versionnes
|
||||||
|
.cache/
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Le `Makefile` est le seul point d'entrée pour les opérations système. Tenir c
|
|||||||
- `make test` : tests en mode headless
|
- `make test` : tests en mode headless
|
||||||
- `make check` : lint + validate-data + test (à passer avant chaque commit)
|
- `make check` : lint + validate-data + test (à passer avant chaque commit)
|
||||||
- `make content-review` : régénère `docs/review/content-review.md` à partir de `data/`
|
- `make content-review` : régénère `docs/review/content-review.md` à partir de `data/`
|
||||||
- `make generate-dictation-audio` : génère l'audio provisoire des dictées (eSpeak-ng + ffmpeg, requis sur la machine) dans `assets/audio/dictations/`, sans écraser l'existant (`--force` pour régénérer)
|
- `make generate-dictation-audio` : génère l'audio provisoire des dictées (voix neuronale Piper via `.venv`, `ffmpeg` requis sur la machine) dans `assets/audio/dictations/`, sans écraser l'existant (`--force` pour régénérer)
|
||||||
- `make export-linux` / `make export-windows` : exports dans `build/`
|
- `make export-linux` / `make export-windows` : exports dans `build/`
|
||||||
|
|
||||||
## Conventions de code
|
## Conventions de code
|
||||||
|
|||||||
+3
-1
@@ -5,6 +5,8 @@ Tous les graphismes et sons actuels sont provisoires, crees pour ce projet, sans
|
|||||||
| Fichier | Origine | Licence |
|
| Fichier | Origine | Licence |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `icon.svg` | Forme geometrique simple, creee pour ce projet | AGPL-3.0 (code et assets du depot) |
|
| `icon.svg` | Forme geometrique simple, creee pour ce projet | AGPL-3.0 (code et assets du depot) |
|
||||||
| `audio/dictations/ce1_accord_adjectif_dictee_01.ogg` | Voix de synthese generee hors ligne avec eSpeak NG 1.52 (voix `fr-fr`), via `tools/generate_dictation_audio.py` | GPL-3.0 (eSpeak NG) ; fichier genere verse en AGPL-3.0 avec le reste du depot |
|
| `audio/dictations/ce1_accord_adjectif_dictee_01.ogg` | Voix de synthese neuronale generee hors ligne avec Piper (modele `fr_FR-siwis-medium`, entraine sur le corpus SIWIS de l'EPFL), via `tools/generate_dictation_audio.py` | MIT (Piper) ; dataset SIWIS en CC-BY 4.0 (attribution requise) ; fichier genere verse en AGPL-3.0 avec le reste du depot |
|
||||||
|
|
||||||
Chaque nouvelle dictee ajoutee a `data/dictations/` doit avoir son audio genere avec `make generate-dictation-audio`, puis une ligne ajoutee ici. Toutes les voix actuelles sont provisoires (`audio_provisional: true` dans la donnee, badge « voix provisoire » affiche a l'enfant) : a remplacer par une vraie voix humaine avant diffusion.
|
Chaque nouvelle dictee ajoutee a `data/dictations/` doit avoir son audio genere avec `make generate-dictation-audio`, puis une ligne ajoutee ici. Toutes les voix actuelles sont provisoires (`audio_provisional: true` dans la donnee, badge « voix provisoire » affiche a l'enfant) : a remplacer par une vraie voix humaine avant diffusion.
|
||||||
|
|
||||||
|
Attribution requise par la licence CC-BY 4.0 du corpus SIWIS : Honnet, P.-E., Lazaridis, A., Garner, P. N., & Yamagishi, J. (2017). *The SIWIS French Speech Synthesis Database*. Idiap Research Institute / EPFL.
|
||||||
|
|||||||
Binary file not shown.
@@ -172,3 +172,15 @@ Ajouts à l'arborescence proposée :
|
|||||||
**Généré** : `ce1_accord_adjectif_dictee_01.ogg` (seule dictée existante). Source notée dans `assets/CREDITS.md`.
|
**Généré** : `ce1_accord_adjectif_dictee_01.ogg` (seule dictée existante). Source notée dans `assets/CREDITS.md`.
|
||||||
|
|
||||||
**Vérification** : nouveau test headless (`tests/data/test_dictation_audio.gd`) qui échoue si une dictée `audio_provisional` n'a pas de fichier `.ogg` chargeable — garde-fou contre un futur ajout de dictée sans régénération de l'audio, repéré plus tôt que par un test manuel. La qualité d'écoute elle-même (volume, clarté) reste non vérifiable sans écran/haut-parleur, donc à faire à la main (voir `docs/testing/manual-checklist.md`).
|
**Vérification** : nouveau test headless (`tests/data/test_dictation_audio.gd`) qui échoue si une dictée `audio_provisional` n'a pas de fichier `.ogg` chargeable — garde-fou contre un futur ajout de dictée sans régénération de l'audio, repéré plus tôt que par un test manuel. La qualité d'écoute elle-même (volume, clarté) reste non vérifiable sans écran/haut-parleur, donc à faire à la main (voir `docs/testing/manual-checklist.md`).
|
||||||
|
|
||||||
|
**Suite** : la voix eSpeak NG utilisée ici s'est révélée inintelligible à l'écoute (voir point 20) et a été remplacée.
|
||||||
|
|
||||||
|
## 20. Audio provisoire des dictées : eSpeak NG remplacé par une voix neuronale Piper
|
||||||
|
|
||||||
|
**Symptôme** : test manuel sur `ce1_accord_adjectif_dictee_01.ogg` (point 19, voix eSpeak NG par synthèse de formants) : « voix inintelligible ». Ralentir le débit ou ajuster les paramètres d'eSpeak NG ne change pas la nature du problème — une synthèse par formants reste mécanique par construction, ce n'est pas qu'une question de vitesse.
|
||||||
|
|
||||||
|
**Remplacement** : `tools/generate_dictation_audio.py` utilise maintenant une voix neuronale hors ligne via la bibliothèque Python `piper-tts` (modèle `fr_FR-siwis-medium`, un locuteur, qualité medium, licence MIT pour Piper / CC-BY 4.0 pour le corpus SIWIS sous-jacent — attribution dans `assets/CREDITS.md`). Toujours aucun accès réseau à l'exécution du jeu : le modèle de voix (~60 Mo) est téléchargé une seule fois, à la demande, dans `.cache/piper-voices/` (non versionné, voir `.gitignore`), au moment de la rédaction de contenu, jamais par le jeu lui-même. `LENGTH_SCALE = 1.1` ralentit légèrement le débit par rapport à une diction adulte par défaut. eSpeak NG n'est plus une dépendance du projet ; `ffmpeg` (déjà présent sur la machine) reste utilisé pour l'encodage final en Ogg Vorbis. `piper-tts` est ajouté à `tools/setup.sh` (environnement Python partagé avec gdtoolkit/pyright).
|
||||||
|
|
||||||
|
**Effet de bord** : `pyright` ne résolvait pas l'import `piper` en ciblant l'environnement Python par défaut plutôt que `.venv/`. Ajout de `pyrightconfig.json` (`venvPath`/`venv`) à la racine pour que `make lint` continue de fonctionner sans option supplémentaire.
|
||||||
|
|
||||||
|
**Régénéré** : `ce1_accord_adjectif_dictee_01.ogg` avec la nouvelle voix. Non encore réécouté par un humain au moment d'écrire ceci — à confirmer en test manuel que l'intelligibilité est bien résolue, pas seulement supposée meilleure.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Tout ce qui suit ne peut pas être vérifié sans écran (rendu, mise en page, a
|
|||||||
8. Mission « Le pré des petits mots » (choix multiple) : les réponses sont cliquables, une réponse juste affiche « Bravo ! », une réponse fausse affiche « Pas tout à fait » avec un indice.
|
8. Mission « Le pré des petits mots » (choix multiple) : les réponses sont cliquables, une réponse juste affiche « Bravo ! », une réponse fausse affiche « Pas tout à fait » avec un indice.
|
||||||
9. Refaire une erreur sur le même exercice : after la 2e erreur, l'explication remplace l'indice ; après la 3e, la bonne réponse est montrée et l'exercice passe au suivant sans blocage.
|
9. Refaire une erreur sur le même exercice : after la 2e erreur, l'explication remplace l'indice ; après la 3e, la bonne réponse est montrée et l'exercice passe au suivant sans blocage.
|
||||||
10. Mission « Le pont des phrases » (correction de phrase) : toucher un mot le fait apparaître comme sélectionné (état visuel distinct, pas seulement une couleur) ; un champ de saisie apparaît sous le mot sélectionné ; toucher à nouveau le mot fait disparaître le champ.
|
10. Mission « Le pont des phrases » (correction de phrase) : toucher un mot le fait apparaître comme sélectionné (état visuel distinct, pas seulement une couleur) ; un champ de saisie apparaît sous le mot sélectionné ; toucher à nouveau le mot fait disparaître le champ.
|
||||||
11. Mission « La clairière de l'écho » (dictée) : à l'arrivée sur l'exercice, la voix de synthèse doit se faire entendre automatiquement ; le bouton « Réécouter » la rejoue sans planter ; le badge « Voix provisoire » est visible ; la zone de saisie accepte le texte et les accents. Le volume et la clarté de cette voix provisoire (eSpeak NG) sont à juger à l'oreille — rien d'étonnant à ce qu'elle sonne robotique, c'est volontairement provisoire (`audio_provisional: true`).
|
11. Mission « La clairière de l'écho » (dictée) : à l'arrivée sur l'exercice, la voix de synthèse doit se faire entendre automatiquement ; le bouton « Réécouter » la rejoue sans planter ; le badge « Voix provisoire » est visible ; la zone de saisie accepte le texte et les accents. La voix (Piper, point 20 de `decisions.md`) doit être clairement intelligible — une première version avec eSpeak NG a été jugée inintelligible et remplacée ; à reconfirmer à l'oreille que ce n'est plus le cas.
|
||||||
12. Après 3 exercices, écran de récompense avec le nombre d'étoiles, puis retour à la carte.
|
12. Après 3 exercices, écran de récompense avec le nombre d'étoiles, puis retour à la carte.
|
||||||
13. Quitter le jeu puis relancer `make run` : le profil et sa progression doivent être conservés (sélectionner le même profil, vérifier que l'état affiché sur la carte a changé par rapport à l'état initial).
|
13. Quitter le jeu puis relancer `make run` : le profil et sa progression doivent être conservés (sélectionner le même profil, vérifier que l'état affiché sur la carte a changé par rapport à l'état initial).
|
||||||
14. Depuis la sélection de profil, supprimer un profil : une confirmation explicite doit apparaître avant la suppression effective ; annuler doit ne rien supprimer.
|
14. Depuis la sélection de profil, supprimer un profil : une confirmation explicite doit apparaître avant la suppression effective ; annuler doit ne rien supprimer.
|
||||||
@@ -28,7 +28,7 @@ Tout ce qui suit ne peut pas être vérifié sans écran (rendu, mise en page, a
|
|||||||
|
|
||||||
## Audio
|
## Audio
|
||||||
|
|
||||||
- `ce1_accord_adjectif_dictee_01` a un fichier audio provisoire généré par `make generate-dictation-audio` (eSpeak NG + ffmpeg, voir `assets/CREDITS.md`). À vérifier à l'oreille : la voix se déclenche à l'arrivée sur l'exercice et au clic sur « Réécouter », le volume est audible, la phrase reste comprise malgré l'accent robotique de la synthèse.
|
- `ce1_accord_adjectif_dictee_01` a un fichier audio provisoire généré par `make generate-dictation-audio` (voix neuronale Piper, voir `assets/CREDITS.md`). À vérifier à l'oreille : la voix se déclenche à l'arrivée sur l'exercice et au clic sur « Réécouter », le volume est audible, la phrase est clairement comprise (pas seulement devinée).
|
||||||
- Toute dictée ajoutée sans régénérer son audio se dégrade silencieusement (avertissement en console, pas de plantage) : vérifier qu'aucune dictée manquante ne passe inaperçue avant diffusion (`make generate-dictation-audio` ne régénère pas l'existant, `--force` pour forcer).
|
- Toute dictée ajoutée sans régénérer son audio se dégrade silencieusement (avertissement en console, pas de plantage) : vérifier qu'aucune dictée manquante ne passe inaperçue avant diffusion (`make generate-dictation-audio` ne régénère pas l'existant, `--force` pour forcer).
|
||||||
|
|
||||||
## Exports
|
## Exports
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"venvPath": ".",
|
||||||
|
"venv": ".venv"
|
||||||
|
}
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Genere l'audio provisoire des dictees a partir de `data/dictations/*.json`.
|
"""Genere l'audio provisoire des dictees a partir de `data/dictations/*.json`.
|
||||||
|
|
||||||
Synthese vocale hors ligne (eSpeak NG) + encodage en Ogg Vorbis (ffmpeg), tel
|
Synthese vocale hors ligne (voix neuronale Piper, francaise) + encodage en
|
||||||
que decide dans docs/specs/decisions.md, point 8. Ne tourne jamais a
|
Ogg Vorbis (ffmpeg), tel que decide dans docs/specs/decisions.md, points 8 et
|
||||||
l'execution du jeu : c'est un outil de redaction de contenu, lance a la main.
|
19. Ne tourne jamais a l'execution du jeu : c'est un outil de redaction de
|
||||||
Ne regenere pas un fichier deja present, sauf avec --force.
|
contenu, lance a la main. Ne regenere pas un fichier deja present, sauf avec
|
||||||
|
--force.
|
||||||
|
|
||||||
|
Un premier essai avec eSpeak NG (synthese par formants) s'est revele
|
||||||
|
inintelligible a l'oreille lors d'un test manuel : remplace par une voix
|
||||||
|
neuronale Piper, nettement plus naturelle tout en restant generee hors ligne
|
||||||
|
sans aucun acces reseau au moment de jouer (le telechargement du modele de
|
||||||
|
voix, ci-dessous, n'a lieu qu'une fois, a la redaction du contenu).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -14,15 +21,28 @@ import json
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import wave
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from urllib.request import urlretrieve
|
||||||
|
|
||||||
|
from piper import PiperVoice
|
||||||
|
from piper.config import SynthesisConfig
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
DICTATIONS_DIR = REPO_ROOT / "data" / "dictations"
|
DICTATIONS_DIR = REPO_ROOT / "data" / "dictations"
|
||||||
AUDIO_DIR = REPO_ROOT / "assets" / "audio" / "dictations"
|
AUDIO_DIR = REPO_ROOT / "assets" / "audio" / "dictations"
|
||||||
|
|
||||||
ESPEAK_VOICE = "fr-fr"
|
# Cache local, non versionne (voir .gitignore) : le modele de voix (~60 Mo)
|
||||||
ESPEAK_SPEED_WPM = "135"
|
# n'a pas sa place dans le depot, seul l'audio genere l'a.
|
||||||
|
VOICE_CACHE_DIR = REPO_ROOT / ".cache" / "piper-voices"
|
||||||
|
VOICE_NAME = "fr_FR-siwis-medium"
|
||||||
|
VOICE_BASE_URL = "https://huggingface.co/rhasspy/piper-voices/resolve/main/fr/fr_FR/siwis/medium"
|
||||||
|
|
||||||
|
# > 1.0 ralentit legerement le debit (duree des phonemes etiree), plus
|
||||||
|
# confortable pour un enfant de 7-9 ans qu'une diction par defaut pensee
|
||||||
|
# pour un adulte.
|
||||||
|
LENGTH_SCALE = 1.1
|
||||||
|
|
||||||
|
|
||||||
def _require_tool(name: str, install_hint: str) -> str:
|
def _require_tool(name: str, install_hint: str) -> str:
|
||||||
@@ -32,6 +52,20 @@ def _require_tool(name: str, install_hint: str) -> str:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_voice() -> Path:
|
||||||
|
VOICE_CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
model_path = VOICE_CACHE_DIR / f"{VOICE_NAME}.onnx"
|
||||||
|
config_path = VOICE_CACHE_DIR / f"{VOICE_NAME}.onnx.json"
|
||||||
|
|
||||||
|
if not model_path.exists():
|
||||||
|
print(f"Telechargement de la voix {VOICE_NAME} (environ 60 Mo, une seule fois)...")
|
||||||
|
urlretrieve(f"{VOICE_BASE_URL}/{VOICE_NAME}.onnx", model_path)
|
||||||
|
if not config_path.exists():
|
||||||
|
urlretrieve(f"{VOICE_BASE_URL}/{VOICE_NAME}.onnx.json", config_path)
|
||||||
|
|
||||||
|
return model_path
|
||||||
|
|
||||||
|
|
||||||
def load_dictations() -> list[dict[str, Any]]:
|
def load_dictations() -> list[dict[str, Any]]:
|
||||||
return [
|
return [
|
||||||
json.loads(path.read_text(encoding="utf-8"))
|
json.loads(path.read_text(encoding="utf-8"))
|
||||||
@@ -39,26 +73,16 @@ def load_dictations() -> list[dict[str, Any]]:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def generate_one(espeak_bin: str, ffmpeg_bin: str, dictation: dict[str, Any]) -> Path:
|
def generate_one(voice: PiperVoice, ffmpeg_bin: str, dictation: dict[str, Any]) -> Path:
|
||||||
audio_id: str = dictation["audio_id"]
|
audio_id: str = dictation["audio_id"]
|
||||||
target_text: str = dictation["target_text"]
|
target_text: str = dictation["target_text"]
|
||||||
output_path = AUDIO_DIR / f"{audio_id}.ogg"
|
output_path = AUDIO_DIR / f"{audio_id}.ogg"
|
||||||
|
syn_config = SynthesisConfig(length_scale=LENGTH_SCALE)
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
tmp_wav = Path(tmp_dir) / f"{audio_id}.wav"
|
tmp_wav = Path(tmp_dir) / f"{audio_id}.wav"
|
||||||
subprocess.run(
|
with wave.open(str(tmp_wav), "wb") as wav_file:
|
||||||
[
|
voice.synthesize_wav(target_text, wav_file, syn_config=syn_config)
|
||||||
espeak_bin,
|
|
||||||
"-v",
|
|
||||||
ESPEAK_VOICE,
|
|
||||||
"-s",
|
|
||||||
ESPEAK_SPEED_WPM,
|
|
||||||
"-w",
|
|
||||||
str(tmp_wav),
|
|
||||||
target_text,
|
|
||||||
],
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
ffmpeg_bin,
|
ffmpeg_bin,
|
||||||
@@ -87,27 +111,26 @@ def main() -> None:
|
|||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
espeak_bin = _require_tool(
|
|
||||||
"espeak-ng",
|
|
||||||
"A installer via le gestionnaire de paquets du systeme (ex. "
|
|
||||||
"`sudo apt install espeak-ng` sous Debian/Ubuntu, `brew install espeak-ng` sous macOS).",
|
|
||||||
)
|
|
||||||
ffmpeg_bin = _require_tool("ffmpeg", "A installer via le gestionnaire de paquets du systeme.")
|
ffmpeg_bin = _require_tool("ffmpeg", "A installer via le gestionnaire de paquets du systeme.")
|
||||||
|
|
||||||
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
|
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
generated: list[str] = []
|
provisional_dictations = [d for d in load_dictations() if d.get("audio_provisional", False)]
|
||||||
|
dictations_to_generate: list[dict[str, Any]] = []
|
||||||
skipped: list[str] = []
|
skipped: list[str] = []
|
||||||
for dictation in load_dictations():
|
for dictation in provisional_dictations:
|
||||||
if not dictation.get("audio_provisional", False):
|
output_path = AUDIO_DIR / f"{dictation['audio_id']}.ogg"
|
||||||
continue
|
|
||||||
audio_id: str = dictation["audio_id"]
|
|
||||||
output_path = AUDIO_DIR / f"{audio_id}.ogg"
|
|
||||||
if output_path.exists() and not args.force:
|
if output_path.exists() and not args.force:
|
||||||
skipped.append(audio_id)
|
skipped.append(dictation["audio_id"])
|
||||||
continue
|
else:
|
||||||
generate_one(espeak_bin, ffmpeg_bin, dictation)
|
dictations_to_generate.append(dictation)
|
||||||
generated.append(audio_id)
|
|
||||||
|
generated: list[str] = []
|
||||||
|
if dictations_to_generate:
|
||||||
|
voice = PiperVoice.load(str(_ensure_voice()))
|
||||||
|
for dictation in dictations_to_generate:
|
||||||
|
generate_one(voice, ffmpeg_bin, dictation)
|
||||||
|
generated.append(dictation["audio_id"])
|
||||||
|
|
||||||
print(f"Audio genere : {len(generated)} fichier(s) ({', '.join(generated) or 'aucun'}).")
|
print(f"Audio genere : {len(generated)} fichier(s) ({', '.join(generated) or 'aucun'}).")
|
||||||
if skipped:
|
if skipped:
|
||||||
|
|||||||
+5
-3
@@ -1,7 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Installe l'outillage de developpement : binaire Godot pinne, addon GUT,
|
# Installe l'outillage de developpement : binaire Godot pinne, addon GUT,
|
||||||
# environnement Python pour gdtoolkit. Rien de ceci n'est commite (voir .gitignore) :
|
# environnement Python pour gdtoolkit/pyright/piper-tts (ce dernier pour
|
||||||
# ce script est la seule source de verite pour reconstruire l'outillage.
|
# tools/generate_dictation_audio.py). Rien de ceci n'est commite (voir
|
||||||
|
# .gitignore) : ce script est la seule source de verite pour reconstruire
|
||||||
|
# l'outillage.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
@@ -66,7 +68,7 @@ if [ ! -d ".venv" ]; then
|
|||||||
python3 -m venv .venv
|
python3 -m venv .venv
|
||||||
fi
|
fi
|
||||||
./.venv/bin/pip install --quiet --upgrade pip
|
./.venv/bin/pip install --quiet --upgrade pip
|
||||||
./.venv/bin/pip install --quiet "gdtoolkit==${GDTOOLKIT_VERSION}" "pyright==${PYRIGHT_VERSION}"
|
./.venv/bin/pip install --quiet "gdtoolkit==${GDTOOLKIT_VERSION}" "pyright==${PYRIGHT_VERSION}" piper-tts
|
||||||
|
|
||||||
echo "Import du projet (enregistre les class_name globaux : SchemaValidator, DataLoader...)."
|
echo "Import du projet (enregistre les class_name globaux : SchemaValidator, DataLoader...)."
|
||||||
"${GODOT_BIN}" --headless --path . --import >/dev/null
|
"${GODOT_BIN}" --headless --path . --import >/dev/null
|
||||||
|
|||||||
Reference in New Issue
Block a user