PBToolboxAI v3 ← Site

speechout — n_pbt_speechout #

← Référence des composants · Sommaire du guide

Lecture à voix haute : l'application lit un texte phrase par phrase et vous dit où elle en est. Aucun service tiers, aucune clé d'API — la synthèse est celle du poste.

▶ Le voir en vrai — Application de démonstration, tuile Speech out : l'aperçu, le code qui le produit et cette page, côte à côte.


En bref #

Objet non visueln_pbt_speechout
Sert àFaire entendre un texte : accessibilité, mains occupées, notification qu'on ne regarde pas
PrincipeVous donnez le texte ; le composant le découpe en phrases et vous dit laquelle il lit
DépendanceLa synthèse vocale du poste — aucun service tiers, aucune clé d'API

Démarrage rapide #

// once, when the window opens
inv_voice.of_open()

// then, wherever you need it
inv_voice.is_lang = inv_voice.LANG_FR_FR
inv_voice.of_speak("Bonjour. Votre commande est expediee.")

Non visuel : le câblage des événements #

Une voix n'a rien à montrer. Le composant ne dessine donc rien : pas de barre de lecture à caser sur un écran, pas de place prise à ce qu'elle sert.

Et vous n'avez rien à câbler pour cela : un objet non visuel n'a pas de fenêtre, donc pas de sonnette, mais le composant tire lui-même ses événements sur la boucle PowerBuilder tant que la voix est ouverte, et les lève sur l'objet. Vous n'écrivez que les gestionnaires ue_* (aucun receveur, aucun timer).

// Just speak -- the ue_* events arrive on their own :
inv_voice.of_speak("Good morning. Your order has shipped.")
// the ue_sentence / ue_word / ue_stopped events arrive on their own

Suivre la lecture dans VOTRE texte #

ue_sentence porte l'index et le texte de la phrase en cours de lecture. C'est de là que vient la surbrillance — dans votre mle_, votre datawindow ou votre statictext : vous savez où est votre texte, nous jamais.

Le chemin inverse existe aussi : of_speak_from() reprend à une phrase précise, ce qu'on branche sur le clic d'un paragraphe. Les numéros viennent de ue_sentence, donc ils désignent toujours ce qui a réellement été lu.

La découpe est celle du composant, pas la vôtre : of_sentence_count() rend son compte à lui. Ne recomptez pas de votre côté, les deux dériveraient.


Ce que le poste sait vraiment dire #

of_languages() rend les langues que ce poste sait effectivement prononcer, sans doublons. C'est la question que se pose un utilisateur : non pas « quelles voix existent », mais « est-ce que ma langue est là ».

of_voices() descend d'un cran et nomme les voix elles-mêmes. Les deux listes viennent de la machine, pas de nous : ne codez jamais un nom en dur.

La reconnaissance vocale n'a pas d'équivalent, et ce n'est pas un oubli : la reconnaissance ne porte aucune liste des langues qu'elle accepte.

Sans aucun composant de votre côté, gnv_utils.of_speech_languages(as_tags[]) donne la même liste — la DLL interroge une voix cachée, puis la détruit : c'est ce qu'une fenêtre demande avant que la voix existe, quelle langue proposer, dans laquelle lire. L'appel est synchrone et peut prendre jusqu'à deux secondes la première fois : la liste des voix arrive tard, et il l'attend. Et gnv_utils.of_speech_voices(as_names[], as_langs[]) donne les voix elles-mêmes avec leur langue, pour proposer « Hortense » ou « Julie » plutôt qu'une balise ; gnv_utils.of_locale_name(as_tag) donne à une balise son nom lisible — « français (France) » pour fr-FR.

string ls_tags[]

if inv_voice.of_languages(ls_tags) > 0 then inv_voice.is_lang = ls_tags[1]

Propriétés #

PropriétéTypeDéfautRôle
is_langstringen-USLangue lue, en BCP-47 (constantes LANG_*). Décide quelle voix est choisie — tant que is_voice est vide, un nom de voix fixant sa propre langue. Sans voix pour cette langue, le poste lit avec celle qu'il a et ue_voice_fallback nomme les deux
is_textstring""Le texte à lire. Le composant le coupe en phrases ; trois balises disent COMMENT lire un morceau : [pause=500], [spell]…[/spell], [say-as=digits]…[/say-as] (voir plus bas)
is_voicestring""Nom de la voix, pris dans of_voices(). Vide = la première qui parle is_lang
ii_rateinteger100Débit, en POUR CENT du débit normal (10 à 400). Changé en cours de lecture, il s'applique à la phrase suivante
ii_pitchinteger100Hauteur de la voix, en POUR CENT de la hauteur normale (0 à 200)
ii_volumeinteger100Volume en pour cent (0 muet à 100), l'échelle du lecteur vidéo
il_timeout_mslong300000Durée maximale d'une lecture SYNCHRONE : cinq minutes. Au-delà, of_speak_sync rend -4 et la lecture est coupée (is_last_error dit pourquoi)
is_last_errorstring""Pourquoi le dernier of_speak_sync a rendu -4 : voix impossible à créer, moteur en échec, délai écoulé

Méthodes #

MéthodeRôle
of_open ( )Crée la voix. Facultatif — of_speak le fait — mais l'appeler à l'ouverture de la fenêtre paie le coût une fois, loin de la première phrase. Rend un nombre positif quand la voix existe, 0 ou moins si elle n'a pas pu être créée
of_is_open ( )Vrai une fois la voix créée
of_speak ( string as_text )Pose le texte et le lit, depuis la première phrase. Sans argument, relit is_text. Rend 0 une fois envoyé, -1 si la voix n'a pas pu être créée
of_speak_from ( long al_index )Reprend la lecture à une phrase précise. Rend 0 une fois envoyé, -1 si la voix n'a pas pu être créée
of_pause ( )Suspend la lecture où elle en est. Rend 0 une fois envoyé, -1 si la voix n'a pas pu être créée
of_resume ( )Reprend là où of_pause s'était arrêté. Rend 0 une fois envoyé, -1 si la voix n'a pas pu être créée
of_stop ( )Arrête la lecture ; of_speak repart de la première phrase
of_is_speaking ( )Vrai tant qu'une phrase est lue — une lecture en pause compte encore. Demandé au composant, jamais une copie périmée
of_sentence_count ( )Rend le nombre de phrases que le composant a faites du texte
of_count ( ) → integerCombien de phrases le composant a faites du texte — le même nombre que of_sentence_count. La bibliothèque pose cette question sous un seul nom partout
of_voices ( ref string as_names[] )Remplit le tableau avec les voix installées sur ce poste et rend leur nombre
of_languages ( ref string as_tags[] )Remplit le tableau avec les langues que ce poste sait prononcer, sans doublons, et rend leur nombre
of_voice_used ( )La voix que le composant remettra réellement au moteur — pas toujours celle que is_lang demande : un poste porte les voix qu'on y a installées et pas d'autres. Vide = le composant n'en impose aucune, et le moteur prend la sienne, celle de la langue du système. La nommer serait une devinette. ue_error le dit aussi au moment de lire ; ceci se lit avant
of_speak_sync ( { string as_text } )Lit et ATTEND la fin : la ligne suivante s'exécute après la dernière phrase, la fenêtre continue de se peindre. Rend 0 à la fin, -4 en cas d'échec ou de délai écoulé (is_last_error)
of_enqueue ( string as_text )Met un texte en FILE : lu tout de suite si la voix est libre, après la lecture en cours sinon, sans jamais la couper. Rend 0, -1 si la voix n'a pu être créée
of_clear_queue ( )Oublie les textes en attente, sans couper celui qui se lit. Rend 0, -1 si la voix n'a pu être créée
of_queue_count ( )Rend le nombre de textes encore en attente (celui qui se lit n'est pas compté)
of_add_replacement ( string as_from, string as_to )Une règle de prononciation : chaque mot ENTIER as_from est lu as_to (PB → PowerBuilder). Appliquée avant les balises, gardée par l'objet. Rend 0, -5 si as_from est vide
of_clear_replacements ( )Vide le dictionnaire. Rend 0
of_replacement_count ( )Rend le nombre de règles du dictionnaire
of_pick_voice ( string as_lang, string as_gender )Choisit une voix INSTALLÉE pour une langue et, s'il en existe, un genre (GENDER_FEMALE, GENDER_MALE, GENDER_ANY) : langue exacte, puis sa famille. La pose dans is_voice et la rend ; vide si aucune voix ne parle cette langue
of_duration ( )Rend le nombre ESTIMÉ de millisecondes que prendra la lecture (mots par minute au débit demandé, pauses comprises) : pour une barre de progression, pas pour un chronomètre. Le débit APPREND la voix : chaque phrase lue jusqu'au bout mesure le vrai, retenu par voix sur ce poste
of_position ( )Rend le nombre estimé de millisecondes déjà lues, affiné par les mots que le moteur rapporte ; 0 quand rien ne se lit
of_progress ( )Rend l'avancement estimé, de 0 à 100
of_spoken_text ( )Les phrases TELLES QUE la voix les reçoit, une par ligne (dictionnaire appliqué, balises résolues) : le texte à afficher pour suivre mot à mot
of_process_events ( )Vidange les événements en attente et les lève sur cet objet. Le pump interne du composant l'appelle pour vous tant que la voix est ouverte — vous ne l'appelez jamais
of_close ( )Libère la voix, en arrêtant d'abord ce qu'elle disait. Le destructeur l'appelle
of_reset ( )Remet toutes les propriétés à leur valeur d'origine

Événements #

ÉvénementDéclenché quand
ue_started (string as_lang)La lecture commence ; as_lang rappelle dans quelle langue
ue_stopped ( )La dernière phrase est finie, ou of_stop a été appelé
ue_paused ( )La lecture est suspendue
ue_resumed ( )La lecture repart
ue_sentence (long al_index, string as_text)Pour chaque phrase, avec son rang et son texte : c'est ainsi qu'on suit la lecture ailleurs dans la fenêtre
ue_error (string as_message)Le poste n'a aucun moteur ou aucune voix, ou la voix échoue. Une voix simplement ABSENTE n'est pas une erreur : c'est ue_voice_fallback
ue_voices_ready (long al_count)Le moteur a fini de remplir sa liste de voix — elle arrive tard ; of_voices, of_languages, of_voice_used et of_pick_voice l'attendent d'eux-mêmes (2,5 s au plus), cet event dit seulement QUAND elle est arrivée ; al_count dit combien le poste en a
ue_word (long al_index, long al_start, long al_length)Le MOT en cours dans la phrase al_index : Mid(phrase, al_start, al_length). Quand le moteur rapporte les mots (la plupart des voix Windows)
ue_queue_done ( )Le dernier texte de la file (of_enqueue) est lu
ue_voice_fallback (string as_wanted, string as_used)La voix ou la langue demandée n'est pas sur ce poste ; as_used nomme celle qui lit à la place. Une information, pas une erreur : la lecture continue

Prononciation : pauses, épellation, dictionnaire #

WebView2 n'a pas de SSML. Le texte porte donc trois balises à lui, résolues avant la coupe en phrases, et un dictionnaire de mots entiers appliqué avant elles. Une balise inconnue est lue telle quelle.

BaliseEffet
[pause=500]Un silence de 500 ms (10 s au plus). La pause termine la phrase en cours
[spell]ABC12[/spell]Chaque caractère dit un par un : « A, B, C, 1, 2 »
[say-as=digits]4152[/say-as]Les chiffres un par un, pas « quatre mille cent cinquante-deux »
[say-as=characters]…[/say-as]Comme [spell]
// The dictionary : whole words, in the order added, case-sensitive
inv_voice.of_add_replacement("PB", "PowerBuilder")
inv_voice.of_add_replacement("Mme", "Madame")
inv_voice.of_add_replacement("4152", "[say-as=digits]4152[/say-as]")   // a rule may add a tag

inv_voice.of_speak("Mme Durand, PB order 4152 [pause=600] code [spell]PBT[/spell].")

File d'attente, lecture synchrone, progression #


Exemples #

Lire une notification #

inv_voice.of_speak("Order 4152 has been shipped. It arrives on Thursday.")

Éclairer la phrase en cours #

// in ue_sentence, on the nonvisual object
st_read.text = as_text

// and a click on a paragraph resumes from it
inv_voice.of_speak_from(2)

Choisir la voix et le débit #

string ls_voices[]

if inv_voice.of_voices(ls_voices) > 0 then inv_voice.is_voice = ls_voices[1]
inv_voice.ii_rate = 75
inv_voice.of_speak()

Annoncer sans couper : la file #

// Each event of the application is queued : all of them are heard, in order
inv_voice.of_enqueue("Order 4152 has been shipped.")
inv_voice.of_enqueue("Order 4153 is ready.")
// ue_queue_done fires after the last one

Éclairer le mot en cours #

// in ue_sentence : keep the sentence
is_sentence = as_text

// in ue_word : the word is Mid(is_sentence, al_start, al_length)
st_read.text = Left(is_sentence, al_start - 1) + "[" + Mid(is_sentence, al_start, al_length) + "]" + Mid(is_sentence, al_start + al_length)

Une voix par langue et genre, puis lire en attendant la fin #

inv_voice.of_pick_voice(n_pbt_speechout.LANG_FR_FR, n_pbt_speechout.GENDER_FEMALE)
if inv_voice.of_speak_sync("Please confirm the order.") = 0 then
	li_answer = MessageBox("Order", "Confirm ?", Question!, YesNo!)
end if

Bonnes pratiques #


← Référence des composants · Sommaire du guide