mirror of
https://github.com/JMP-MS/ASPIC.git
synced 2026-10-27 16:07:10 +00:00
V1.011 Tests de dictées vocales
This commit is contained in:
+19
-2
@@ -27,6 +27,22 @@
|
||||
*
|
||||
* Session utilisateur : accès réservé aux utilisateurs identifiés, même
|
||||
* principe que les autres endpoints (agent_notes.php, todolist.php...).
|
||||
*
|
||||
* Ajout du 3 septembre 2026 (soir) — "mode adresse" de micro_test.js : le
|
||||
* corps JSON peut porter en plus un champ optionnel "texteNormalise"
|
||||
* (résultat du dictionnaire de conversion point/tiret/arobase appliqué
|
||||
* côté client sur une adresse mail/URL dictée). Stocké dans une colonne
|
||||
* séparée, DCT_TexteNormalise, JAMAIS à la place de DCT_Texte qui reste le
|
||||
* texte brut tel que rendu par le moteur de reconnaissance — voir le
|
||||
* commentaire d'en-tête de micro_test.js. NULL pour toutes les lignes hors
|
||||
* "mode adresse", comme avant ce jour.
|
||||
*
|
||||
* ⚠️ Nécessite d'avoir ajouté la colonne en base avant déploiement :
|
||||
* ALTER TABLE ASP_DicteesTest
|
||||
* ADD COLUMN DCT_TexteNormalise VARCHAR(255) NULL DEFAULT NULL
|
||||
* AFTER DCT_Texte;
|
||||
* (modification de schéma à faire par Jean-Marc lui-même, comme toujours —
|
||||
* voir réflexions stratégiques / journal sur ce point.)
|
||||
*/
|
||||
|
||||
session_start();
|
||||
@@ -47,6 +63,7 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$texte = trim((string) ($data['texte'] ?? ''));
|
||||
$texteNormalise = trim((string) ($data['texteNormalise'] ?? ''));
|
||||
|
||||
if ($texte === '') {
|
||||
http_response_code(400);
|
||||
@@ -69,9 +86,9 @@ try {
|
||||
// simple consigne de tâche.
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO ASP_DicteesTest (DCT_Texte, DCT_Utilisateur) VALUES (?, ?)'
|
||||
'INSERT INTO ASP_DicteesTest (DCT_Texte, DCT_Utilisateur, DCT_TexteNormalise) VALUES (?, ?, ?)'
|
||||
);
|
||||
$stmt->execute([$texte, $_SESSION['USR_Code']]);
|
||||
$stmt->execute([$texte, $_SESSION['USR_Code'], $texteNormalise !== '' ? $texteNormalise : null]);
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]);
|
||||
|
||||
@@ -82,10 +82,17 @@ Version incrémentée une seule fois pour l'ensemble de cette session (pastille
|
||||
|
||||
**3 septembre 2026 (soirée) : micro du bureau branché sur la reconnaissance vocale réelle, puis détourné pour constituer une banque de dictées de test (V1.010).** Détail complet en sections 27 et 28 du journal. Résumé : `micro.js` cherche désormais le bouton micro par `#bouton-micro` (bandeau) ou, à défaut, `#micro-desk` (écran de fond) — un seul comportement partagé, sans dupliquer la logique — et le micro du bureau déclenche bien la reconnaissance vocale réelle depuis ce soir. Discussion stratégique dans la foulée (Réflexions stratégiques, section 12) : Jean-Marc, après avoir montré l'application à sa compagne (confrontée à SAP dans son travail), s'inquiète de la fiabilité du pilotage vocal une fois étendu aux contacts, au catalogue, aux devis, à la facturation — distinction actée avec Claude entre le problème de reconnaissance du mot déclencheur (déjà traité, sa surface d'erreur ne grandit pas avec les modules) et celui de l'interprétation d'une dictée bien captée (à traiter via un LLM à sortie structurée contre un catalogue d'actions, le RAG/pgvector restant réservé à la recherche floue dans un corpus, pas au déclenchement d'action). Décision : constituer d'abord un vrai corpus de dictées avant d'écrire la moindre règle. Micro du bureau temporairement détourné vers un nouvel outil de mise au point (`micro_test.js`/`dictees_test.php`, table `ASP_DicteesTest`, thème "Gestion des Contacts") — écoute continue sans mot déclencheur, restitution au fur et à mesure dans une fenêtre dédiée, aucune suppression possible même pour un essai raté (décision explicite de Jean-Marc : la banque doit refléter la réalité). Premier essai réel concluant le soir même. Version incrémentée une seule fois pour l'ensemble de cette session (branchement réel + outil de test) : **V1.010**.
|
||||
|
||||
**3 septembre 2026 (nuit) : dictée d'adresses mail — diagnostic et deux bugs de robustesse corrigés dans l'outil de banque de dictées (V1.011).** Détail complet en section 27 du journal ; comparatif de prix des API de reconnaissance vocale en réflexions stratégiques, section 12 (addendum du même soir). Résumé : les premiers essais réels de Jean-Marc ont montré un taux d'échec très élevé (~1/50) sur la dictée d'une adresse mail. Diagnostic à partir d'exports réels de `ASP_DicteesTest` : trois causes distinctes — vocabulaire de ponctuation dicté ("point", "tiret", "arobase") converti de façon incohérente par le moteur gratuit du navigateur, segmentation de la dictée en plusieurs lignes non fiable, et des lettres purement avalées sur des mots sans support linguistique réel (ex. "mvl-tls.fr" → "mvtls.fr", perte acoustique jamais rattrapable après coup). Ajout d'un "mode adresse" dans `micro_test.js` : buffer explicite (activé/désactivé au clic, pour s'affranchir de la segmentation) + dictionnaire de conversion + validation de forme + compteur de conformité en direct ; nouvelle colonne `DCT_TexteNormalise` (ajoutée en base par Jean-Marc). En testant, deux bugs de robustesse préexistants découverts grâce à un bloc de diagnostic temporaire ajouté en direct (affichage de chaque démarrage/arrêt/erreur du moteur) : la fenêtre de test était entièrement vidée à chaque redémarrage automatique du moteur (invisible jusqu'ici, devenu très gênant avec une dictée hésitante) ; et une boucle de redémarrage incontrôlée pouvant atteindre plus de 1800 cycles en quelques secondes, sans aucune protection dans le code d'origine. Les deux corrigés le même soir (réinitialisation de fenêtre restreinte à l'ouverture volontaire ; coupe-circuit après 5 redémarrages rapides consécutifs). Sur un tout petit échantillon (4 essais) après correctifs : 2 adresses parfaitement normalisées, 1 quasi-bonne, 1 ratée par oubli de cliquer "Valider" entre deux adresses distinctes — encourageant, mais échantillon trop petit pour conclure. Piste d'une API payante avec vocabulaire personnalisé (Google Cloud, Deepgram) documentée et chiffrée en réserve, non retenue pour l'instant (coût en dollars négligeable, mais changement d'architecture réel). Version incrémentée une seule fois pour l'ensemble de cette session : **V1.011**. Jean-Marc prévoit une campagne de tests en nombre le 4 septembre et enverra les résultats.
|
||||
|
||||
**Reste à faire, noté au passage :**
|
||||
- Vérifier concrètement que le `.htaccess` du dossier `/journal` bloque bien l'accès direct (`curl -I` sur un fichier du dossier doit répondre `403`, pas `200` — point en suspens depuis le 31 août).
|
||||
- Confirmer le déploiement du correctif `.htaccess` de cache livré le 3 septembre (fichier `htaccess-racine-aspic.txt`, à renommer et déposer à la racine de l'application).
|
||||
- Laisser Jean-Marc constituer un vrai volume de dictées sur "Gestion des Contacts" (nouvel outil, voir ci-dessus) avant toute analyse ; puis concevoir le catalogue d'actions et le mécanisme LLM à sortie structurée correspondant (voir réflexions stratégiques, section 12, et journal section 27).
|
||||
- Récupérer les résultats de la campagne de tests d'adresses mail que Jean-Marc prévoit de mener le 4 septembre (mode adresse de `micro_test.js`) pour mesurer un vrai taux de réussite, sur un échantillon plus significatif que les 4 essais du 3 septembre.
|
||||
- Retirer le bloc de diagnostic temporaire (`DIAGNOSTIC_ACTIF`/`logDiag`) de `micro_test.js` une fois son comportement confirmé stable sur cette campagne.
|
||||
- Décider si le cas "deux adresses dictées sans clic Valider entre les deux" (mode adresse) mérite un traitement automatique, ou reste une simple consigne d'usage.
|
||||
- Si le taux d'échec mesuré début septembre reste trop élevé : évaluer en conditions réelles (pas seulement sur la documentation) Google Cloud Speech-to-Text (phrase hints/boost) et Deepgram (keyterm prompting) avec le vocabulaire métier d'ASPIC — voir réflexions stratégiques, section 12.
|
||||
- Le problème des lettres avalées sur les mots inventés/sigles (ex. "mvl-tls.fr") reste entier, sans solution de contournement identifiée à ce stade — à garder en tête, pas à considérer comme résolu par le mode adresse.
|
||||
- Reconnecter `micro.js` (vrai pilotage) sur le micro de l'écran de fond une fois la phase de mise au point terminée — une ligne à changer dans `ecran_fond.html`.
|
||||
- Étendre `MARQUEUR_FIN_LIGNE` dans `micro.js` pour accepter "puis" en plus de "ensuite" (retour d'usage de Jean-Marc, 3 septembre) — sans urgence.
|
||||
- Continuer à observer les agents actifs (Viktor, Matilda, Léon, Nikita) en usage réel ; ajuster `AGN_Variantes` en base si de nouvelles confusions apparaissent.
|
||||
|
||||
@@ -150,3 +150,13 @@ Jean-Marc a proposé un signe graphique sur le post-it de l'assistant pour ident
|
||||
**Décision pour la suite immédiate : ne pas concevoir les règles d'interprétation à l'avance, constituer d'abord un corpus réel.** Cohérent avec la leçon déjà tirée de Lilou/"les loups" — ce qui paraît naturel en le pensant seul se révèle différent une fois dicté réellement et de façon détendue. Jean-Marc en a fourni la preuve lui-même, avant même d'avoir l'outil en main : il a par exemple relevé qu'il commence souvent une instruction par "il faut"/"il faudrait" plutôt que par le prénom d'un agent, et utilise aussi bien "puis" que "ensuite" pour enchaîner deux idées — deux détails qu'aucune conception à la table n'aurait fait ressortir aussi vite. Voir journal de développement, section 27, pour l'outil concret construit ce soir-là (`micro_test.js`/`dictees_test.php`, table `ASP_DicteesTest`) : aucune règle d'interprétation n'y est codée, volontairement — l'objectif de cette phase est uniquement d'accumuler la matière première (dictées réelles, y compris les essais ratés) qui permettra, plus tard, de concevoir le catalogue d'actions et les règles d'extraction avec des exemples réels plutôt qu'hypothétiques.
|
||||
|
||||
**Statut : orientation actée, aucun développement de la couche d'interprétation elle-même engagé.** Le développement immédiat porte uniquement sur la constitution du corpus (voir journal, section 27) — le catalogue d'actions, l'appel LLM à sortie structurée et les deux signes graphiques du post-it restent à concevoir, une fois ce corpus jugé suffisant.
|
||||
|
||||
**Addendum du 3 septembre 2026 (nuit) — la reconnaissance brute d'un mot rare/inventé est un problème à part, que ni le LLM d'interprétation ni le RAG ne résolvent.** Les premiers essais réels de Jean-Marc sur le thème "Gestion des Contacts" (voir journal, section 27) ont fait remonter, en marge de la constitution du corpus, un problème concret qui recoupe la distinction posée plus haut mais s'y ajoute : la dictée d'une adresse mail échoue très souvent, pour trois raisons distinctes — un vocabulaire de ponctuation dicté ("point", "tiret", "arobase") converti de façon incohérente par le moteur gratuit du navigateur, une segmentation de la dictée en plusieurs lignes non fiable, et surtout des lettres purement et simplement avalées sur des mots sans aucun support linguistique réel (ex. "mvl-tls.fr" transcrit "mvtls.fr") — cette dernière étant une perte acoustique en amont de tout traitement, jamais rattrapable par un post-traitement quel qu'il soit, contrairement aux deux premières.
|
||||
|
||||
Point explicitement acté avec Jean-Marc : **aucune solution, gratuite ou payante, ne garantit un fonctionnement parfait en toute situation** sur ce genre de mot rare/inventé — à ne jamais présenter comme un problème que "changer de moteur" réglerait entièrement. Le levier le plus directement pertinent pour ce cas précis n'est pas le modèle d'interprétation (LLM à sortie structurée, voir plus haut) mais le **vocabulaire personnalisé côté reconnaissance elle-même** ("boosting") — un mécanisme distinct de celui décrit plus haut dans cette section, en amont de l'interprétation, pas à sa place :
|
||||
- **Google Cloud Speech-to-Text** (*speech adaptation* / *phrase hints*) et **Deepgram** (*keyterm prompting*, récemment étendu à plusieurs langues) permettent de fournir au moteur une liste de mots/expressions attendus (noms de domaine ASPIC, société, agents...) pour augmenter leur probabilité de reconnaissance au moment de la décision — conceptuellement la même idée que `AGN_Variantes`, déjà validée pour les prénoms d'agents, appliquée cette fois au vocabulaire métier.
|
||||
- **Non disponible côté gratuit :** l'API du navigateur (`webkitSpeechRecognition`) expose bien un paramètre `grammars` prévu à l'origine pour ce genre d'usage, mais Chrome ne l'exploite pas réellement en pratique (accepté sans effet) — ce levier suppose de passer par une vraie API payante.
|
||||
- **Prix vérifiés le 3 septembre 2026** (susceptibles d'évoluer) : Google Cloud Speech-to-Text v2, reconnaissance standard, environ 0,016 $/minute (dégressif au volume) — seul fournisseur à documenter une option dédiée à la conversion ponctuation dictée→symbole (`enableSpokenPunctuation`), sans confirmation que le français soit couvert par cette option précise. Deepgram Nova-3 multilingue, environ 0,005 $/minute, formatage automatique ("Smart Formatting") inclus sans supplément, 200 $ de crédit d'essai gratuit — couverture française du *keyterm prompting* spécifiquement non confirmée par la documentation à ce jour. OpenAI (gpt-transcribe), environ 0,0045 $/minute en asynchrone (plus cher en flux continu), sans fonction de ponctuation dictée documentée.
|
||||
- **Le coût en dollars est négligeable à l'usage envisagé** (quelques dollars par mois même à un usage quotidien soutenu) — ce n'est pas ce qui doit trancher. Ce qui coûte réellement, c'est le changement d'architecture : ces API ne s'utilisent pas depuis le navigateur comme aujourd'hui (gratuit, zéro serveur) — il faudrait capturer l'audio et le faire transiter par un point d'entrée serveur détenant une clé tenue secrète, fin de la solution 100% navigateur actuelle.
|
||||
|
||||
**Décision : épuiser d'abord la piste gratuite (dictionnaire de conversion + segmentation explicite au clic, "mode adresse" de `micro_test.js`, voir journal section 27) avant d'investir dans une API.** Pas de décision définitive sur le recours à une API — dépendra du taux d'échec réellement mesuré sur la campagne de tests que Jean-Marc prévoit de mener le 4 septembre. Si le taux reste insuffisant, Deepgram est le candidat à tester en premier (le moins cher, formatage inclus), Google Cloud restant l'option la plus directement conçue pour le problème de ponctuation dictée mais à vérifier en vrai plutôt que sur la seule doc, notamment pour la couverture du français.
|
||||
|
||||
@@ -104,3 +104,39 @@ Testé par Claude en navigateur headless avec un faux moteur de reconnaissance p
|
||||
- Étendre `MARQUEUR_FIN_LIGNE` dans `micro.js` pour accepter "puis" en plus de "ensuite" (retour d'usage de Jean-Marc, 3 septembre) — sans urgence, à faire quand on retouche `micro.js`.
|
||||
- Concevoir les deux signes graphiques distincts pour les post-its "à corriger" / "à valider" évoqués plus haut — non codé, ni même définitivement choisi pour "à valider" (le point d'interrogation de "à corriger" est acté).
|
||||
- Étendre l'outil de banque de dictées à d'autres thèmes (catalogue, devis...) le jour où ces modules existeront — `DCT_Theme` est déjà prévu pour ça, seule la valeur par défaut en base et le script de capture sont à dupliquer/adapter.
|
||||
|
||||
## 3 septembre 2026 (nuit) : dictée d'adresses mail — diagnostic en plusieurs couches et deux bugs de robustesse trouvés grâce à un diagnostic en direct (V1.011)
|
||||
|
||||
Reprise de session le même soir, Jean-Marc ayant commencé à utiliser réellement l'outil de banque de dictées (section précédente) pour s'entraîner sur le thème "Gestion des Contacts". Constat immédiat et sévère : la dictée d'une adresse mail échoue environ 49 fois sur 50 — bien au-delà de ce qu'un utilisateur tolérerait, quelle que soit la qualité du reste du pilotage vocal (Jean-Marc : *"si une dictée d'adresse mail pose problème dans plus de 10% des cas, la solution ne sera pas utilisée"*).
|
||||
|
||||
**Diagnostic établi à partir d'exports réels de `ASP_DicteesTest`, pas d'hypothèses de table** (même méthode que celle qui avait permis de trouver le bug "les loups", voir plus haut) — trois causes distinctes, à ne pas confondre :
|
||||
- **Vocabulaire incohérent.** Les mots dictés pour la ponctuation ("point", "tiret", "arobase", "tiret du bas") ne sont convertis en symbole par le moteur gratuit du navigateur que de façon incohérente — parfois convertis, parfois laissés en toutes lettres, parfois avalés purement et simplement. "tiret" est en plus régulièrement confondu avec le verbe "tirer" (homophone). Vérifié auprès de la documentation officielle : Google propose bien une option dédiée à cette conversion (`enableSpokenPunctuation`) dans son API Cloud Speech-to-Text payante, mais elle n'est pas exposée dans l'API gratuite du navigateur (`webkitSpeechRecognition`) — l'application doit donc faire elle-même cette conversion.
|
||||
- **Segmentation non fiable.** Le moteur découpe parfois une même adresse dictée en plusieurs lignes indépendantes, sans qu'un délai de silence permette de distinguer fiablement "il continue la même adresse" de "il change de sujet" (des écarts de 3 à 4 secondes s'observent dans les deux cas sur un même export réel).
|
||||
- **Lettres avalées sur les mots inventés.** Distinct des deux points précédents et plus grave : sur un mot sans aucun support linguistique réel (ex. "mvl-tls", un sigle interne), le moteur perd parfois des lettres entières ("mvl-tls.fr" transcrit "mvtls.fr") — une perte acoustique pure, invisible dans le texte reçu et donc jamais rattrapable par un quelconque post-traitement. Explication retenue : les moteurs de reconnaissance actuels s'appuient sur un modèle de langage qui favorise les séquences ressemblant à quelque chose de connu (d'où la fiabilité, à l'inverse, de "gmail.com" — une chaîne quasi mémorisée tant elle est fréquente) ; un sigle inventé n'a aucun appui de ce genre.
|
||||
|
||||
**Position actée avec Jean-Marc sur ce dernier point : aucune solution, gratuite ou payante, ne peut garantir un fonctionnement parfait en toute situation** sur un mot rare/inventé — dit explicitement pour ne pas laisser croire qu'une simple bascule vers une API payante réglerait tout. Le vrai levier pour ce cas précis serait le vocabulaire personnalisé ("boosting" — Google Cloud *speech adaptation*/*phrase hints*, Deepgram *keyterm prompting*), qui permet de fournir au moteur une liste de mots attendus (noms de domaine ASPIC, société, agents...) pour augmenter leur probabilité de reconnaissance — même principe que `AGN_Variantes` déjà en place pour les agents, appliqué cette fois au vocabulaire métier. Comparatif de prix et détail de la piste API conservés dans les réflexions stratégiques (section 12, addendum du même soir) plutôt que dans ce journal technique.
|
||||
|
||||
**Décision : épuiser d'abord la piste gratuite avant d'envisager une API payante**, le coût en dollars étant négligeable à l'usage envisagé mais le changement d'architecture bien réel (audio à faire transiter par un serveur détenant une clé, fin de la solution 100% navigateur/sans serveur actuelle).
|
||||
|
||||
**"Mode adresse" ajouté à `micro_test.js`.** Plutôt que de deviner où s'arrête une adresse dans un flux libre, reprise du principe déjà éprouvé du mode notation de `micro.js` (ouverture/clôture explicites) — ici au clic plutôt qu'à la phrase, pour ne pas dépendre d'un mot de clôture lui-même mal reconnaissable :
|
||||
- Activé/désactivé via un bouton dédié dans la fenêtre de test ; tant qu'il est actif, chaque fragment reconnu s'empile dans un buffer affiché en direct au lieu d'être enregistré comme une ligne indépendante — la segmentation du moteur n'a alors plus d'importance.
|
||||
- Le bouton "Valider" clôt le buffer : les fragments sont concaténés, passés dans un dictionnaire de conversion (point/tiret/tiret du bas/arobase → symboles, y compris l'alias "tirer"→"tiret" restreint à ce mode précis) qui retire aussi les espaces restants, puis comparés à une forme email/domaine attendue pour un simple retour visuel vert/orange — indicatif seulement, n'empêche jamais l'enregistrement (cohérent avec la règle "aucun essai raté supprimé" actée le 3 septembre après-midi).
|
||||
- Le texte brut (fragments concaténés tels que dictés) et le texte normalisé sont conservés tous les deux — nouvelle colonne `DCT_TexteNormalise` (ajoutée en base par Jean-Marc lui-même, `VARCHAR(255) NULL`), `NULL` pour toutes les lignes hors "mode adresse". Rien n'écrase la matière brute déjà actée comme précieuse pour la suite.
|
||||
- Un compteur simple (conforme/non conforme) affiché dans la fenêtre donne une lecture immédiate du taux d'échec au fil des essais.
|
||||
|
||||
**Deux bugs de robustesse trouvés en cours de test réel, tous deux corrigés le même soir grâce à un bloc de diagnostic temporaire ajouté spécifiquement pour ça** (`DIAGNOSTIC_ACTIF`/`logDiag`, affichage en direct de chaque démarrage/arrêt/erreur du moteur avec horodatage précis — y compris les erreurs normalement masquées comme "no-speech") :
|
||||
1. **La fenêtre entière était vidée à chaque redémarrage automatique du moteur**, pas seulement à l'ouverture volontaire du micro — bug préexistant à ce soir, resté invisible tant que les dictées s'enchaînaient vite, devenu très visible avec une dictée hésitante (adresse mail, beaucoup de silences de réflexion → beaucoup de redémarrages automatiques → fenêtre effacée avant d'avoir pu être lue). Signalé côté utilisateur par l'indicateur micro de Chrome clignotant en continu. Corrigé en ne réinitialisant la fenêtre qu'au tout premier démarrage suivant un clic utilisateur réel, jamais lors d'une relance automatique.
|
||||
2. **Boucle de redémarrage incontrôlée**, révélée par le diagnostic une fois le premier bug corrigé : jusqu'à plus de 1800 cycles démarrage/arrêt en quelques secondes, avec des erreurs "aborted" au passage — le code d'origine relançait `reconnaissance.start()` immédiatement à chaque arrêt, sans aucun délai ni protection. Corrigé par un coupe-circuit : au-delà de 5 redémarrages consécutifs en moins de 800 ms, l'écoute s'arrête proprement avec un message explicite, plutôt que de boucler indéfiniment en silence.
|
||||
|
||||
**Résultat après correctifs, sur un tout petit échantillon (4 essais passés par le mode adresse) :** 2 adresses parfaitement normalisées, 1 quasi-bonne (un mot parasite collé en tête, sans lien avec la ponctuation), 1 ratée — non pas à cause d'un bug, mais parce que deux adresses distinctes avaient été dictées à la suite sans clic "Valider" entre les deux, fusionnées en une seule chaîne par le buffer (comportement voulu du mode adresse, pas un défaut). Progrès net par rapport au ~1/50 de départ, mais échantillon bien trop petit pour trancher — Jean-Marc prévoit une campagne de tests en nombre le lendemain (4 septembre) et enverra les résultats.
|
||||
|
||||
### Fichiers touchés (3 septembre, nuit)
|
||||
|
||||
`micro_test.js` (mode adresse + diagnostic temporaire + deux correctifs de robustesse), `dictees_test.php` (colonne `DCT_TexteNormalise` optionnelle en entrée), `version.php` (`V1.011`). Colonne `DCT_TexteNormalise` ajoutée par Jean-Marc directement en base (`ALTER TABLE`, communiqué par Claude, jamais exécuté côté Claude — voir convention actée en tête de journal).
|
||||
|
||||
**Reste à faire.**
|
||||
- Laisser Jean-Marc constituer un vrai volume d'essais d'adresses mail via le mode adresse (le 4 septembre) pour mesurer un taux de réussite fiable, plutôt que de statuer sur un échantillon de 4.
|
||||
- Retirer le bloc de diagnostic temporaire (`DIAGNOSTIC_ACTIF`/`logDiag`) une fois le comportement confirmé stable sur cette campagne — pas destiné à rester en l'état.
|
||||
- Décider si le cas "deux adresses dictées sans clic Valider entre les deux" (voir résultat ci-dessus) mérite un traitement automatique, ou reste une simple consigne d'usage.
|
||||
- Si le taux d'échec mesuré reste trop élevé après cette campagne : évaluer en conditions réelles (pas seulement sur la documentation) Google Cloud Speech-to-Text (phrase hints/boost) et Deepgram (keyterm prompting) avec le vocabulaire métier d'ASPIC — voir réflexions stratégiques, section 12.
|
||||
- Le problème des "lettres avalées" sur les mots inventés (ex. "mvl-tls.fr") reste entier et sans solution de contournement identifiée à ce stade, y compris avec le mode adresse — à garder en tête pour la suite plutôt qu'à considérer comme résolu.
|
||||
|
||||
+329
-10
@@ -32,6 +32,7 @@
|
||||
* correspond déjà à "ce qui a été dit avant une pause" ; on ne choisit
|
||||
* pas la durée exacte de cette pause, elle est interne au navigateur —
|
||||
* même mécanisme que celui déjà éprouvé en production dans micro.js).
|
||||
* **Exception : le "mode adresse", voir plus bas.**
|
||||
* - Restitution au fur et à mesure dans une fenêtre dédiée (fond noir,
|
||||
* texte blanc, défilement) : la ligne en cours s'affiche pendant la
|
||||
* dictée (interimResults activé, contrairement à micro.js) puis se fige
|
||||
@@ -55,6 +56,88 @@
|
||||
* dépendre de micro.js lui-même : ce fichier est volontairement autonome et
|
||||
* plus simple, pour ne jamais risquer de contaminer le vrai pilotage avec
|
||||
* de la logique de test jetable.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
* AJOUT DU 3 SEPTEMBRE 2026 (soirée) — "Mode adresse" (email / URL).
|
||||
* ---------------------------------------------------------------------
|
||||
* Origine : essais réels de Jean-Marc montrant que la dictée d'une adresse
|
||||
* mail échoue très souvent (voir réflexions stratégiques). Deux causes
|
||||
* distinctes identifiées à partir d'un export réel de ASP_DicteesTest :
|
||||
*
|
||||
* 1. Le mot "point" (et "tiret", "arobase", "tiret du bas"...) n'est
|
||||
* converti en symbole ("." "-" "@" "_") que de façon incohérente par le
|
||||
* moteur gratuit du navigateur — parfois converti, parfois laissé en
|
||||
* toutes lettres, parfois avalé. "tiret" est en plus régulièrement
|
||||
* confondu avec le verbe "tirer" (homophone). Un problème de
|
||||
* *vocabulaire*, rattrapable par un dictionnaire de conversion.
|
||||
* 2. Le moteur découpe une même adresse dictée en plusieurs lignes
|
||||
* indépendantes (plusieurs événements isFinal) sans que la durée du
|
||||
* silence permette de distinguer fiablement "il continue la même
|
||||
* adresse" de "il change de sujet" (des écarts de 3-4 secondes
|
||||
* s'observent dans les deux cas sur le corpus réel). Un problème de
|
||||
* *segmentation*, qu'un dictionnaire seul ne peut pas résoudre.
|
||||
*
|
||||
* Le "mode adresse" répond aux deux à la fois en réutilisant le principe
|
||||
* déjà éprouvé du mode notation de micro.js (ouverture/clôture explicite)
|
||||
* mais ici au clic plutôt qu'à la phrase, pour ne pas dépendre d'un mot
|
||||
* de clôture qui pourrait lui-même être mal reconnu :
|
||||
* - Activé via le bouton "Mode adresse" de la fenêtre de test.
|
||||
* - Tant qu'il est actif, chaque fragment reconnu (isFinal) est empilé
|
||||
* dans un buffer au lieu d'être enregistré comme une ligne indépendante
|
||||
* — la segmentation du moteur n'a donc plus d'importance.
|
||||
* - Le bouton "Valider" (ou la désactivation du mode) clôt le buffer :
|
||||
* les fragments sont concaténés, passés à normaliserTexteAdresse() qui
|
||||
* applique le dictionnaire de conversion et retire les espaces
|
||||
* restants (une adresse mail/URL n'en contient jamais), puis le résultat
|
||||
* est comparé à une forme attendue (email ou domaine) pour un simple
|
||||
* retour visuel (vert/orange) — indicatif seulement, n'empêche jamais
|
||||
* l'enregistrement (cohérent avec la règle "aucun essai raté supprimé").
|
||||
* - Le texte brut (tel que dicté, fragments concaténés) ET le texte
|
||||
* normalisé sont envoyés à dictees_test.php et conservés tous les deux
|
||||
* (DCT_Texte / DCT_TexteNormalise) — rien n'écrase la matière brute déjà
|
||||
* actée comme précieuse pour la suite ; DCT_TexteNormalise est une
|
||||
* colonne en plus, NULL pour toutes les lignes hors "mode adresse".
|
||||
* ⚠️ Nécessite l'ajout manuel de cette colonne en base avant déploiement
|
||||
* (voir le message livré avec ce fichier — Claude n'exécute jamais de
|
||||
* modification de schéma lui-même, cf. réflexions stratégiques).
|
||||
* - Un compteur simple (conforme / non conforme) s'affiche dans la fenêtre
|
||||
* pour donner une lecture immédiate du taux d'échec au fil des essais.
|
||||
*
|
||||
* Portée volontairement limitée à ce problème précis : aucune tentative
|
||||
* de deviner where s'arrête une adresse dans un flux libre, aucune
|
||||
* interprétation sémantique — juste un buffer explicite et un dictionnaire
|
||||
* de conversion, dans le même esprit "pas de sur-ingénierie" que le reste
|
||||
* du projet.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
* CORRECTIF DU 3 SEPTEMBRE 2026 (fin de soirée) — fenêtre effacée à
|
||||
* chaque redémarrage automatique du moteur (bug préexistant, pas lié au
|
||||
* "mode adresse" ci-dessus).
|
||||
* ---------------------------------------------------------------------
|
||||
* `ouvrirFenetre()` (qui vide entièrement le contenu affiché) était
|
||||
* appelée à chaque événement "start" du moteur de reconnaissance — or ce
|
||||
* moteur se coupe et se relance tout seul en silence après une pause
|
||||
* (comportement voulu, voir plus haut), ce qui déclenche un nouvel
|
||||
* événement "start" à chaque fois. Avec des dictées enchaînées, ça ne se
|
||||
* voyait pas ; avec une dictée hésitante (adresse mail, beaucoup de
|
||||
* silences de réflexion), les redémarrages deviennent fréquents et la
|
||||
* fenêtre se vide sans cesse avant d'avoir pu être lue — signalé par
|
||||
* l'indicateur micro de Chrome qui clignote en continu. Corrigé en ne
|
||||
* réinitialisant la fenêtre qu'au tout premier "start" suivant un clic
|
||||
* utilisateur réel (`ouvertureInitiale`), jamais lors d'une relance
|
||||
* automatique. Un bloc de diagnostic temporaire (`DIAGNOSTIC_ACTIF`,
|
||||
* fonction logDiag) a été ajouté en parallèle pour visualiser les cycles
|
||||
* démarrage/arrêt/erreur en direct dans la fenêtre — à retirer une fois
|
||||
* le comportement confirmé stable.
|
||||
*
|
||||
* Ce diagnostic a révélé un second problème, plus grave : dans certaines
|
||||
* conditions le moteur enchaîne des centaines de redémarrages par seconde
|
||||
* (des "aborted" apparaissent parmi les erreurs), sans aucun frein côté
|
||||
* code d'origine (relance immédiate à chaque "end", aucun délai). Ajout
|
||||
* d'un coupe-circuit (`cyclesRapidesConsecutifs` / `MAX_CYCLES_RAPIDES`) :
|
||||
* au-delà de 5 redémarrages consécutifs en moins de 800 ms, l'écoute
|
||||
* s'arrête proprement avec un message, plutôt que de boucler indéfiniment
|
||||
* et de noyer la fenêtre de diagnostic.
|
||||
*/
|
||||
(function(){
|
||||
"use strict";
|
||||
@@ -104,6 +187,47 @@
|
||||
reconnaissance.stop();
|
||||
}
|
||||
|
||||
// -------- Mode adresse : dictionnaire de conversion --------
|
||||
// Volontairement limité aux mots utiles à une adresse mail/URL dictée en
|
||||
// français — jamais appliqué en dehors du "mode adresse" (voir plus bas),
|
||||
// pour ne jamais risquer de transformer un "point" ou un "tiret" dit dans
|
||||
// une phrase normale ailleurs dans l'outil.
|
||||
const REMPLACEMENTS_ADRESSE = [
|
||||
[/\btiret du bas\b/g, "_"],
|
||||
[/\btiret bas\b/g, "_"],
|
||||
[/\bunderscore\b/g, "_"],
|
||||
[/\barobase\b/g, "@"],
|
||||
[/\barobas\b/g, "@"],
|
||||
[/\btrait d'union\b/g, "-"],
|
||||
// "tirer" (verbe) est une confusion acoustique fréquente avec "tiret"
|
||||
// dans ce contexte précis (voir export du 3 septembre) — alias
|
||||
// volontairement restreint au mode adresse, jamais appliqué ailleurs.
|
||||
[/\btiret\b/g, "-"],
|
||||
[/\btirer\b/g, "-"],
|
||||
[/\bpoint\b/g, "."]
|
||||
];
|
||||
|
||||
function normaliserTexteAdresse(brut){
|
||||
let t = " " + brut.toLowerCase().trim() + " ";
|
||||
REMPLACEMENTS_ADRESSE.forEach(function(paire){
|
||||
t = t.replace(paire[0], paire[1]);
|
||||
});
|
||||
// Une adresse mail ou une URL ne contient jamais d'espace : tout espace
|
||||
// restant à ce stade vient de la reconnaissance vocale elle-même
|
||||
// (silences entre syllabes/mots), pas d'une intention de l'utilisateur.
|
||||
t = t.replace(/\s+/g, "");
|
||||
return t;
|
||||
}
|
||||
|
||||
const FORME_EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const FORME_DOMAINE = /^[a-z0-9-]+(\.[a-z0-9-]+)+$/;
|
||||
|
||||
function evaluerConformite(normalise){
|
||||
if(FORME_EMAIL.test(normalise)) return {ok:true, categorie:"email"};
|
||||
if(FORME_DOMAINE.test(normalise)) return {ok:true, categorie:"domaine"};
|
||||
return {ok:false, categorie:null};
|
||||
}
|
||||
|
||||
// -------- Fenêtre de restitution --------
|
||||
// Construite ici plutôt que dans ecran_fond.html, pour que ce fichier
|
||||
// reste entièrement autonome — le retirer (remettre micro.js) ne laisse
|
||||
@@ -111,6 +235,16 @@
|
||||
let fenetre = null;
|
||||
let zoneLignes = null;
|
||||
let ligneEnCours = null;
|
||||
let boutonModeAdresse = null;
|
||||
let boutonValider = null;
|
||||
let zoneBuffer = null;
|
||||
let compteurEl = null;
|
||||
|
||||
// État du mode adresse
|
||||
let modeAdresse = false;
|
||||
let bufferFragments = []; // fragments isFinal accumulés depuis l'activation/le dernier "Valider"
|
||||
let compteurConforme = 0;
|
||||
let compteurNonConforme = 0;
|
||||
|
||||
function construireFenetre(){
|
||||
if(fenetre) return;
|
||||
@@ -118,7 +252,7 @@
|
||||
const style = document.createElement("style");
|
||||
style.textContent =
|
||||
"#fenetre-dictees-test{position:fixed;top:8%;left:50%;" +
|
||||
"transform:translateX(-50%);width:min(560px,80vw);height:min(420px,70vh);" +
|
||||
"transform:translateX(-50%);width:min(560px,80vw);height:min(460px,72vh);" +
|
||||
"background:#000;border:1px solid #333;border-radius:6px;" +
|
||||
"box-shadow:0 10px 40px rgba(0,0,0,.6);z-index:950;display:none;" +
|
||||
"flex-direction:column;overflow:hidden;" +
|
||||
@@ -126,10 +260,28 @@
|
||||
"#fenetre-dictees-test.visible{display:flex;}" +
|
||||
"#fenetre-dictees-test .entete{color:#888;font-size:11px;" +
|
||||
"padding:6px 10px;border-bottom:1px solid #222;flex:none;}" +
|
||||
"#fenetre-dictees-test .controles{display:flex;align-items:center;" +
|
||||
"gap:8px;padding:6px 10px;border-bottom:1px solid #222;flex:none;}" +
|
||||
"#fenetre-dictees-test .controles button{font:inherit;font-size:12px;" +
|
||||
"background:#1a1a1a;color:#ccc;border:1px solid #444;border-radius:4px;" +
|
||||
"padding:4px 10px;cursor:pointer;}" +
|
||||
"#fenetre-dictees-test .controles button.actif{background:#2a4d2a;" +
|
||||
"color:#bdf7bd;border-color:#3a6d3a;}" +
|
||||
"#fenetre-dictees-test .controles button:disabled{opacity:.4;" +
|
||||
"cursor:default;}" +
|
||||
"#fenetre-dictees-test .controles .compteur{margin-left:auto;" +
|
||||
"color:#777;font-size:11px;}" +
|
||||
"#fenetre-dictees-test .buffer{padding:6px 10px;color:#ffd76a;" +
|
||||
"font-size:13px;border-bottom:1px solid #222;min-height:1.4em;" +
|
||||
"flex:none;white-space:pre-wrap;}" +
|
||||
"#fenetre-dictees-test .lignes{flex:1;overflow-y:auto;padding:8px 10px;" +
|
||||
"color:#fff;font-size:14px;line-height:1.6;white-space:pre-wrap;}" +
|
||||
"#fenetre-dictees-test .lignes .en-cours{color:#8a8a8a;font-style:italic;}" +
|
||||
"#fenetre-dictees-test .lignes .erreur{color:#e14a5c;}";
|
||||
"#fenetre-dictees-test .lignes .erreur{color:#e14a5c;}" +
|
||||
"#fenetre-dictees-test .lignes .adresse-ok{color:#7be07b;}" +
|
||||
"#fenetre-dictees-test .lignes .adresse-ko{color:#e0a95a;}" +
|
||||
"#fenetre-dictees-test .lignes .adresse-brut{color:#777;font-size:12px;}" +
|
||||
"#fenetre-dictees-test .lignes .diag{color:#6ab8ff;font-size:11px;}";
|
||||
document.head.appendChild(style);
|
||||
|
||||
fenetre = document.createElement("div");
|
||||
@@ -137,15 +289,95 @@
|
||||
fenetre.innerHTML =
|
||||
'<div class="entete">Banque de dictées — Gestion des Contacts ' +
|
||||
'(outil de mise au point, non destiné aux utilisateurs finaux)</div>' +
|
||||
'<div class="controles">' +
|
||||
'<button type="button" class="btn-mode-adresse">Mode adresse : OFF</button>' +
|
||||
'<button type="button" class="btn-valider" disabled>Valider</button>' +
|
||||
'<span class="compteur"></span>' +
|
||||
'</div>' +
|
||||
'<div class="buffer"></div>' +
|
||||
'<div class="lignes"></div>';
|
||||
document.body.appendChild(fenetre);
|
||||
zoneLignes = fenetre.querySelector(".lignes");
|
||||
zoneBuffer = fenetre.querySelector(".buffer");
|
||||
boutonModeAdresse = fenetre.querySelector(".btn-mode-adresse");
|
||||
boutonValider = fenetre.querySelector(".btn-valider");
|
||||
compteurEl = fenetre.querySelector(".compteur");
|
||||
|
||||
boutonModeAdresse.addEventListener("click", function(){
|
||||
if(modeAdresse){
|
||||
// Désactivation : on ne perd jamais un buffer en cours, on le
|
||||
// valide d'abord (même logique que le bouton "Valider").
|
||||
if(bufferFragments.length) validerBufferAdresse();
|
||||
modeAdresse = false;
|
||||
boutonModeAdresse.textContent = "Mode adresse : OFF";
|
||||
boutonModeAdresse.classList.remove("actif");
|
||||
boutonValider.disabled = true;
|
||||
afficherBuffer();
|
||||
} else {
|
||||
modeAdresse = true;
|
||||
boutonModeAdresse.textContent = "Mode adresse : ON";
|
||||
boutonModeAdresse.classList.add("actif");
|
||||
boutonValider.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
boutonValider.addEventListener("click", function(){
|
||||
validerBufferAdresse();
|
||||
});
|
||||
}
|
||||
|
||||
function majCompteur(){
|
||||
if(!compteurEl) return;
|
||||
const total = compteurConforme + compteurNonConforme;
|
||||
compteurEl.textContent = total
|
||||
? "adresses : " + compteurConforme + "/" + total + " conformes"
|
||||
: "";
|
||||
}
|
||||
|
||||
function afficherBuffer(){
|
||||
if(!zoneBuffer) return;
|
||||
zoneBuffer.textContent = modeAdresse
|
||||
? "[mode adresse] " + bufferFragments.join(" ")
|
||||
: "";
|
||||
}
|
||||
|
||||
function validerBufferAdresse(){
|
||||
const brutConcatene = bufferFragments.join(" ").trim();
|
||||
bufferFragments = [];
|
||||
afficherBuffer();
|
||||
if(!brutConcatene) return;
|
||||
|
||||
const normalise = normaliserTexteAdresse(brutConcatene);
|
||||
const conformite = evaluerConformite(normalise);
|
||||
|
||||
if(conformite.ok) compteurConforme++; else compteurNonConforme++;
|
||||
majCompteur();
|
||||
|
||||
const ligneBrut = document.createElement("div");
|
||||
ligneBrut.className = "adresse-brut";
|
||||
ligneBrut.textContent = "brut : " + brutConcatene;
|
||||
zoneLignes.appendChild(ligneBrut);
|
||||
|
||||
const ligneNormalisee = document.createElement("div");
|
||||
ligneNormalisee.className = conformite.ok ? "adresse-ok" : "adresse-ko";
|
||||
ligneNormalisee.textContent = "→ " + normalise +
|
||||
(conformite.ok ? " (" + conformite.categorie + " reconnu)" : " (forme non reconnue)");
|
||||
zoneLignes.appendChild(ligneNormalisee);
|
||||
zoneLignes.scrollTop = zoneLignes.scrollHeight;
|
||||
|
||||
enregistrerDictee(brutConcatene, normalise);
|
||||
reinitialiserMinuteurInactivite();
|
||||
}
|
||||
|
||||
function ouvrirFenetre(){
|
||||
construireFenetre();
|
||||
zoneLignes.innerHTML = "";
|
||||
ligneEnCours = null;
|
||||
bufferFragments = [];
|
||||
compteurConforme = 0;
|
||||
compteurNonConforme = 0;
|
||||
majCompteur();
|
||||
afficherBuffer();
|
||||
fenetre.classList.add("visible");
|
||||
}
|
||||
|
||||
@@ -153,6 +385,7 @@
|
||||
if(fenetre) fenetre.classList.remove("visible");
|
||||
if(zoneLignes) zoneLignes.innerHTML = "";
|
||||
ligneEnCours = null;
|
||||
bufferFragments = [];
|
||||
}
|
||||
|
||||
function afficherEnCours(texte){
|
||||
@@ -187,15 +420,41 @@
|
||||
zoneLignes.scrollTop = zoneLignes.scrollHeight;
|
||||
}
|
||||
|
||||
// -------- DIAGNOSTIC TEMPORAIRE (3 septembre 2026, soir) --------
|
||||
// Ajouté uniquement pour comprendre pourquoi rien ne s'affiche chez
|
||||
// Jean-Marc (indicateur micro de Chrome qui clignote sans arrêt — signe
|
||||
// possible d'un cycle démarrage/arrêt en boucle rapide). Affiche TOUT,
|
||||
// y compris les événements normalement filtrés (ex. "no-speech"), avec
|
||||
// horodatage précis. À RETIRER une fois le problème identifié — ce n'est
|
||||
// pas un affichage destiné à rester.
|
||||
const DIAGNOSTIC_ACTIF = true;
|
||||
let diagCompteurDemarrages = 0;
|
||||
|
||||
function logDiag(texte){
|
||||
if(!DIAGNOSTIC_ACTIF || !zoneLignes) return;
|
||||
const maintenant = new Date();
|
||||
const heure = maintenant.toLocaleTimeString("fr-FR") + "." +
|
||||
String(maintenant.getMilliseconds()).padStart(3, "0");
|
||||
const div = document.createElement("div");
|
||||
div.className = "diag";
|
||||
div.textContent = "[diag " + heure + "] " + texte;
|
||||
zoneLignes.appendChild(div);
|
||||
zoneLignes.scrollTop = zoneLignes.scrollHeight;
|
||||
}
|
||||
|
||||
// -------- Enregistrement --------
|
||||
// Aucune interprétation, aucun filtrage : tout ce qui est capté part vers
|
||||
// dictees_test.php, essai raté compris (décision de Jean-Marc, voir
|
||||
// commentaire d'en-tête).
|
||||
function enregistrerDictee(texte){
|
||||
// commentaire d'en-tête). En mode adresse, le texte normalisé est envoyé
|
||||
// en plus (jamais à la place) du texte brut — voir commentaire d'en-tête
|
||||
// du 3 septembre sur DCT_TexteNormalise.
|
||||
function enregistrerDictee(texte, texteNormalise){
|
||||
const corps = {texte: texte};
|
||||
if(texteNormalise) corps.texteNormalise = texteNormalise;
|
||||
fetch("dictees_test.php", {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({texte: texte})
|
||||
body: JSON.stringify(corps)
|
||||
}).then(function(reponse){
|
||||
if(!reponse.ok) throw new Error("HTTP " + reponse.status);
|
||||
}).catch(function(){
|
||||
@@ -203,10 +462,23 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Vrai uniquement pour le tout prochain événement "start" déclenché par un
|
||||
// clic utilisateur réel — jamais pour une relance automatique après coupure
|
||||
// silencieuse (voir diagnostic du 3 septembre, soir : la fenêtre entière
|
||||
// était effacée à CHAQUE redémarrage du moteur, pas seulement à l'ouverture
|
||||
// volontaire, ce qui devenait très visible dès que les silences — donc les
|
||||
// redémarrages — se multipliaient pendant une dictée hésitante).
|
||||
let ouvertureInitiale = false;
|
||||
|
||||
function passerEnEcoute(){
|
||||
bouton.classList.add("ecoute");
|
||||
bouton.title = "Le micro est ouvert (banque de dictées contacts — outil de mise au point)";
|
||||
ouvrirFenetre();
|
||||
if(ouvertureInitiale){
|
||||
ouvertureInitiale = false;
|
||||
ouvrirFenetre(); // construit/affiche et réinitialise réellement, une seule fois
|
||||
} else if(fenetre){
|
||||
fenetre.classList.add("visible"); // relance automatique : rien n'est effacé
|
||||
}
|
||||
reinitialiserMinuteurInactivite();
|
||||
}
|
||||
|
||||
@@ -228,18 +500,52 @@
|
||||
}
|
||||
ecouteVoulue = true;
|
||||
erreurBloquante = false;
|
||||
ouvertureInitiale = true;
|
||||
try{
|
||||
reconnaissance.start();
|
||||
}catch(erreur){
|
||||
ecouteVoulue = false;
|
||||
ouvertureInitiale = false;
|
||||
afficherErreur("Impossible de démarrer le micro (" + erreur.name + ").");
|
||||
}
|
||||
});
|
||||
|
||||
reconnaissance.addEventListener("start", passerEnEcoute);
|
||||
// Coupe-circuit anti-boucle (3 septembre 2026, fin de soirée) : le
|
||||
// diagnostic en direct a montré des centaines de redémarrages par
|
||||
// seconde dans certaines conditions (service en défaut, ou toute autre
|
||||
// cause empêchant le moteur de rester ouvert) — sans aucun frein, le code
|
||||
// d'origine relançait immédiatement à chaque "end", ce qui peut boucler
|
||||
// indéfiniment et rendre la fenêtre illisible. Si plusieurs redémarrages
|
||||
// se suivent anormalement vite, on arrête proprement plutôt que de
|
||||
// continuer à boucler en silence.
|
||||
let dernierDemarrageHorodatage = 0;
|
||||
let cyclesRapidesConsecutifs = 0;
|
||||
const SEUIL_CYCLE_RAPIDE_MS = 800;
|
||||
const MAX_CYCLES_RAPIDES = 5;
|
||||
|
||||
reconnaissance.addEventListener("start", function(){
|
||||
diagCompteurDemarrages++;
|
||||
dernierDemarrageHorodatage = Date.now();
|
||||
passerEnEcoute();
|
||||
logDiag("start #" + diagCompteurDemarrages);
|
||||
});
|
||||
|
||||
reconnaissance.addEventListener("end", function(){
|
||||
logDiag("end (ecouteVoulue=" + ecouteVoulue + ", erreurBloquante=" + erreurBloquante + ")");
|
||||
if(ecouteVoulue && !erreurBloquante){
|
||||
const ecoule = Date.now() - dernierDemarrageHorodatage;
|
||||
cyclesRapidesConsecutifs = (ecoule < SEUIL_CYCLE_RAPIDE_MS) ? (cyclesRapidesConsecutifs + 1) : 0;
|
||||
|
||||
if(cyclesRapidesConsecutifs >= MAX_CYCLES_RAPIDES){
|
||||
erreurBloquante = true;
|
||||
revenirAuRepos();
|
||||
afficherErreur("Le micro redémarre en boucle anormalement vite (" +
|
||||
cyclesRapidesConsecutifs + " fois de suite en moins de " +
|
||||
SEUIL_CYCLE_RAPIDE_MS + " ms) — arrêt automatique pour ne pas " +
|
||||
"boucler indéfiniment. Attends quelques secondes puis rouvre le micro.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Le moteur s'est arrêté tout seul (silence prolongé, limite interne
|
||||
// du navigateur...) alors que l'écoute reste voulue : relance
|
||||
// invisible pour l'utilisateur, même principe que micro.js.
|
||||
@@ -260,20 +566,33 @@
|
||||
if(!texte) return;
|
||||
|
||||
if(dernier.isFinal){
|
||||
if(modeAdresse){
|
||||
// Accumulation dans le buffer plutôt qu'enregistrement immédiat —
|
||||
// voir commentaire d'en-tête du 3 septembre sur le "mode adresse".
|
||||
bufferFragments.push(texte);
|
||||
afficherBuffer();
|
||||
} else {
|
||||
// Silence détecté par le moteur : la ligne se fige et s'enregistre
|
||||
// immédiatement, essai raté ou non — aucun jugement porté ici sur la
|
||||
// qualité du texte reconnu.
|
||||
// immédiatement, essai raté ou non — aucun jugement porté ici sur
|
||||
// la qualité du texte reconnu.
|
||||
figerLigne(texte);
|
||||
enregistrerDictee(texte);
|
||||
enregistrerDictee(texte, null);
|
||||
}
|
||||
reinitialiserMinuteurInactivite();
|
||||
} else {
|
||||
// Restitution au fur et à mesure, avant que le silence ne fige la
|
||||
// ligne — demande explicite de Jean-Marc.
|
||||
if(modeAdresse){
|
||||
zoneBuffer.textContent = "[mode adresse] " +
|
||||
(bufferFragments.concat([texte])).join(" ");
|
||||
} else {
|
||||
afficherEnCours(texte);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
reconnaissance.addEventListener("error", function(evenement){
|
||||
logDiag("error : " + evenement.error);
|
||||
const messagesBloquants = {
|
||||
"not-allowed": "Micro refusé — vérifie l'autorisation dans le navigateur.",
|
||||
"service-not-allowed": "Micro refusé — vérifie l'autorisation dans le navigateur.",
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
* d'identification, avant toute connexion.
|
||||
*/
|
||||
|
||||
const ASPIC_VERSION = 'V1.010';
|
||||
const ASPIC_VERSION = 'V1.011';
|
||||
|
||||
if (basename($_SERVER['SCRIPT_NAME']) === 'version.php') {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
Reference in New Issue
Block a user