speechout — n_pbt_speechout #
← Riferimento dei componenti · Sommario della guida
Lettura ad alta voce: l'applicazione legge un testo frase per frase e ti dice a che punto è. Nessun servizio di terze parti, nessuna chiave API — la sintesi è quella della postazione.
▶ Vederlo dal vivo — Applicazione dimostrativa, riquadro Speech out: l'anteprima, il codice che la produce e questa pagina, fianco a fianco.
In breve #
| Oggetto non visuale | n_pbt_speechout |
| Serve a | Far sentire un testo: accessibilità, mani occupate, una notifica che nessuno guarda |
| Principio | Tu dai il testo; il componente lo divide in frasi e ti dice quale sta leggendo |
| Dipendenza | La sintesi vocale della postazione — nessun servizio di terze parti, nessuna chiave API |
Avvio rapido #
// 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 visuale: il cablaggio degli eventi #
Una voce non ha nulla da mostrare. Il componente quindi non disegna niente: nessuna barra di riproduzione da sistemare, nessuno spazio tolto a ciò che serve.
E per questo non devi collegare nulla: un oggetto non visuale non ha finestra, quindi niente campanello, ma il componente preleva da solo i suoi eventi sul loop PowerBuilder finché la voce è aperta e li solleva sull'oggetto. Scrivi solo i gestori ue_* (nessun ricevitore, nessun 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
Seguire la lettura nel TUO testo #
ue_sentence porta l'indice e il testo della frase in lettura. Da lì viene l'evidenziazione — nel tuo mle_, nella tua datawindow o nel tuo statictext: tu sai dov'è il tuo testo, noi mai.
Esiste anche il percorso inverso: of_speak_from() riprende da una frase precisa, cosa che si collega al clic su un paragrafo. I numeri vengono da ue_sentence, quindi indicano sempre ciò che è stato davvero letto.
Il taglio è quello del componente, non il tuo:
of_sentence_count()restituisce il suo conteggio. Non ricontare dalla tua parte, i due divergerebbero.
Ciò che la postazione sa davvero dire #
of_languages() restituisce le lingue che questa postazione sa effettivamente pronunciare, senza duplicati. È la domanda che si pone un utente: non «quali voci esistono», ma «c'è la mia lingua».
of_voices() scende di un gradino e nomina le voci stesse. Entrambi gli elenchi vengono dalla macchina, non da noi: non scrivere mai un nome fisso nel codice.
Il riconoscimento vocale non ha un equivalente, e non è una dimenticanza: il riconoscimento non porta alcun elenco delle lingue che accetta.
Senza alcun componente da parte tua, gnv_utils.of_speech_languages(as_tags[]) dà lo stesso elenco — la DLL interroga una voce nascosta, poi la elimina: è ciò che una finestra chiede prima che la voce esista, quale lingua proporre, in quale leggere. La chiamata è sincrona e può richiedere fino a due secondi la prima volta: l'elenco delle voci arriva tardi, e la chiamata lo aspetta. E gnv_utils.of_speech_voices(as_names[], as_langs[]) dà le voci stesse con la loro lingua, per proporre «Hortense» o «Julie» anziché un tag; gnv_utils.of_locale_name(as_tag) dà a un tag il suo nome leggibile — «francese (Francia)» per fr-FR.
string ls_tags[]
if inv_voice.of_languages(ls_tags) > 0 then inv_voice.is_lang = ls_tags[1]
Proprietà #
| Proprietà | Tipo | Predefinito | Ruolo |
|---|---|---|---|
is_lang | string | en-US | Lingua letta, in BCP-47 (costanti LANG_*). Decide quale voce viene scelta — finché is_voice è vuota, poiché un nome di voce fissa la propria lingua. Senza una voce per quella lingua la postazione legge con quella che ha, e ue_voice_fallback le nomina entrambe |
is_text | string | "" | Il testo da leggere. Il componente lo taglia in frasi; tre tag dicono COME leggere un pezzo: [pause=500], [spell]…[/spell], [say-as=digits]…[/say-as] (vedi sotto) |
is_voice | string | "" | Nome della voce, preso da of_voices(). Vuoto = la prima che parla is_lang |
ii_rate | integer | 100 | Velocità, in PER CENTO di quella normale (da 10 a 400). Cambiata durante la lettura, vale dalla frase successiva |
ii_pitch | integer | 100 | Altezza della voce, in PER CENTO dell'altezza normale (da 0 a 200) |
ii_volume | integer | 100 | Volume in percento (0 muto a 100), la scala del lettore video |
il_timeout_ms | long | 300000 | Durata massima di una lettura SINCRONA: cinque minuti. Oltre, of_speak_sync rende -4 e la lettura viene interrotta (is_last_error dice perché) |
is_last_error | string | "" | Perché l'ultimo of_speak_sync ha reso -4: voce non creata, motore in errore, tempo scaduto |
Metodi #
| Metodo | Ruolo |
|---|---|
of_open ( ) | Crea la voce. Facoltativo — of_speak lo fa — ma chiamarlo all'apertura della finestra paga il costo una volta, lontano dalla prima frase. Restituisce un numero positivo quando la voce esiste, 0 o meno se non ha potuto essere creata |
of_is_open ( ) | Vero una volta creata la voce |
of_speak ( string as_text ) | Imposta il testo e lo legge, dalla prima frase. Senza argomento, rilegge is_text. Restituisce 0 una volta inviato, -1 se la voce non ha potuto essere creata |
of_speak_from ( long al_index ) | Riprende la lettura da una frase precisa. Restituisce 0 una volta inviato, -1 se la voce non ha potuto essere creata |
of_pause ( ) | Sospende la lettura dov'è. Restituisce 0 una volta inviato, -1 se la voce non ha potuto essere creata |
of_resume ( ) | Riprende da dove of_pause si era fermato. Restituisce 0 una volta inviato, -1 se la voce non ha potuto essere creata |
of_stop ( ) | Ferma la lettura; of_speak riparte dalla prima frase |
of_is_speaking ( ) | Vero finché una frase viene letta — una lettura in pausa conta ancora. Chiesto al componente, mai una copia scaduta |
of_sentence_count ( ) | Restituisce quante frasi il componente ha ricavato dal testo |
of_count ( ) → integer | Quante frasi il componente ha ricavato dal testo — lo stesso numero che rende of_sentence_count. La libreria pone questa domanda sotto un solo nome ovunque |
of_voices ( ref string as_names[] ) | Riempie l'array con le voci installate su questa postazione e ne restituisce il numero |
of_languages ( ref string as_tags[] ) | Riempie l'array con le lingue che questa postazione sa pronunciare, senza duplicati, e ne restituisce il numero |
of_voice_used ( ) | La voce che il componente consegnerà davvero al motore — non sempre quella chiesta da is_lang: una postazione porta le voci che qualcuno vi ha installato e nessun'altra. Vuoto = il componente non ne impone alcuna, e il motore prende la sua, quella della lingua di sistema. Nominarla sarebbe indovinare. ue_error lo dice al momento di leggere; questo si legge prima |
of_speak_sync ( { string as_text } ) | Legge e ATTENDE la fine: la riga seguente gira dopo l'ultima frase, la finestra continua a dipingersi. Rende 0 alla fine, -4 in caso di errore o tempo scaduto (is_last_error) |
of_enqueue ( string as_text ) | Mette un testo in CODA: letto subito se la voce è libera, dopo la lettura in corso altrimenti, senza mai tagliarla. Rende 0, -1 se la voce non ha potuto essere creata |
of_clear_queue ( ) | Dimentica i testi in attesa, senza tagliare quello in lettura. Rende 0, -1 se la voce non ha potuto essere creata |
of_queue_count ( ) | Rende il numero di testi ancora in attesa (quello in lettura non è contato) |
of_add_replacement ( string as_from, string as_to ) | Una regola di pronuncia: ogni parola INTERA as_from è letta as_to (PB → PowerBuilder). Applicata prima dei tag, conservata dall'oggetto. Rende 0, -5 se as_from è vuoto |
of_clear_replacements ( ) | Svuota il dizionario. Rende 0 |
of_replacement_count ( ) | Rende il numero di regole del dizionario |
of_pick_voice ( string as_lang, string as_gender ) | Sceglie una voce INSTALLATA per una lingua e, se esiste, un genere (GENDER_FEMALE, GENDER_MALE, GENDER_ANY): lingua esatta, poi la sua famiglia. La mette in is_voice e la rende; vuoto se nessuna voce parla quella lingua |
of_duration ( ) | Rende la durata STIMATA della lettura, in millisecondi (parole al minuto alla velocità richiesta, pause comprese): per una barra di avanzamento, non per un cronometro. Il ritmo IMPARA la voce: ogni frase letta fino in fondo misura quello reale, ricordato per voce su questa postazione |
of_position ( ) | Rende la posizione stimata della lettura, in millisecondi, affinata dalle parole che il motore riporta; 0 quando nulla si legge |
of_progress ( ) | Rende l'avanzamento stimato, da 0 a 100 |
of_spoken_text ( ) | Le frasi COME la voce le riceve, una per riga (dizionario applicato, tag risolti): il testo da mostrare per seguire parola per parola |
of_process_events ( ) | Svuota gli eventi in attesa e li solleva su questo oggetto. Il pump interno del componente lo chiama per te finché la voce è aperta — non lo chiami mai |
of_close ( ) | Libera la voce, fermando prima ciò che stava dicendo. Il distruttore lo chiama |
of_reset ( ) | Riporta tutte le proprietà al valore iniziale |
Eventi #
| Evento | Scatta quando |
|---|---|
ue_started (string as_lang) | La lettura comincia; as_lang ricorda in quale lingua |
ue_stopped ( ) | L'ultima frase è finita, oppure è stato chiamato of_stop |
ue_paused ( ) | La lettura è sospesa |
ue_resumed ( ) | La lettura riparte |
ue_sentence (long al_index, string as_text) | Per ogni frase, con il suo rango e il suo testo: così si segue la lettura altrove nella finestra |
ue_error (string as_message) | La postazione non ha alcun motore o alcuna voce, o la voce fallisce. Una voce semplicemente ASSENTE non è un errore: è ue_voice_fallback |
ue_voices_ready (long al_count) | Il motore ha riempito l'elenco delle voci — arriva tardi; of_voices, of_languages, of_voice_used e of_pick_voice lo attendono da soli (2,5 s al massimo), questo evento dice solo QUANDO è arrivato; al_count dice quante ne ha la postazione |
ue_word (long al_index, long al_start, long al_length) | La PAROLA in corso nella frase al_index: Mid(frase, al_start, al_length). Quando il motore riporta le parole (la maggior parte delle voci Windows) |
ue_queue_done ( ) | L'ultimo testo della coda (of_enqueue) è letto |
ue_voice_fallback (string as_wanted, string as_used) | La voce o la lingua richiesta non è su questa postazione; as_used nomina quella che legge al suo posto. Un'informazione, non un errore: la lettura continua |
Pronuncia: pause, compitazione, dizionario #
WebView2 non ha SSML. Il testo porta quindi tre tag suoi, risolti prima del taglio in frasi, e un dizionario di parole intere applicato prima di essi. Un tag sconosciuto è letto così com'è.
| Tag | Effetto |
|---|---|
[pause=500] | Un silenzio di 500 ms (10 s al massimo). La pausa termina la frase in corso |
[spell]ABC12[/spell] | Ogni carattere detto uno per uno: «A, B, C, 1, 2» |
[say-as=digits]4152[/say-as] | Le cifre una per una, non «quattromilacentocinquantadue» |
[say-as=characters]…[/say-as] | Come [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].")
Coda, lettura sincrona, avanzamento #
of_speaktaglia,of_enqueueattende. Un'applicazione che annuncia eventi (allarme, risultato, notifica) mette in coda: due annunci ravvicinati sono entrambi uditi, eue_queue_donedice quando l'ultimo è letto.of_clear_queuedimentica ciò che attende senza tagliare;of_stopfa entrambe le cose.of_speak_syncrestituisce il controllo alla fine. Lo script attende l'ultima frase (la finestra continua a dipingersi), limitato dail_timeout_ms— per «leggi questo, poi fai la domanda».of_duration,of_position,of_progresssono stime: il motore non dice nulla della durata, contano le parole alla velocità richiesta, affinate dalle parole che il motore riporta. Abbastanza per una barra di avanzamento letta da un timer, non per un cronometro.of_pick_voice(lingua, genere)sceglie una voce installata per lingua poi genere, e la mette inis_voice. Il genere viene dal nome che Windows dà a ogni voce; una voce dal nome sconosciuto risponde aGENDER_ANY.- Nessuna esportazione audio. La sintesi di WebView2 non rende alcun flusso: la lettura non può essere scritta in un file. Non è un'impostazione mancante, è il motore.
Esempi #
Leggere una notifica #
inv_voice.of_speak("Order 4152 has been shipped. It arrives on Thursday.")
Evidenziare la frase in corso #
// 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)
Scegliere la voce e la velocità #
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()
Annunciare senza tagliare: la coda #
// 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
Evidenziare la parola in corso #
// 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)
Una voce per lingua e genere, poi leggere aspettando la fine #
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
Buone pratiche #
- Una frase alla volta: il componente affida al motore una sola frase e concatena. È ciò che permette di fermarsi pulitamente fra due frasi, ed evita l'abbandono silenzioso di Chromium oltre una quindicina di secondi.
- Evidenzia la frase letta nel tuo testo, da
ue_sentence— è metà del senso di una lettura ad alta voce. - Chiama
of_languages()invece di supporre: una lingua installata da te può non esserlo dal cliente. - Non chiamare
of_speaka raffica su un insieme di dati: ogni chiamata taglia la precedente, e l'utente sente solo inizi. Per questo c'èof_enqueue.