speechout — n_pbt_speechout #
← Referencia de componentes · Índice de la guía
Lectura en voz alta: la aplicación lee un texto frase por frase y le dice por dónde va. Sin servicio de terceros, sin clave de API — la síntesis es la del equipo.
▶ Verlo en vivo — Aplicación de demostración, mosaico Speech out: la vista previa, el código que la produce y esta página, lado a lado.
En resumen #
| Objeto no visual | n_pbt_speechout |
| Sirve para | Hacer oír un texto: accesibilidad, manos ocupadas, un aviso que nadie mira |
| Principio | Usted da el texto; el componente lo corta en frases y le dice cuál está leyendo |
| Dependencia | La síntesis de voz del equipo — sin servicio de terceros, sin clave de API |
Inicio rápido #
// 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.")
No visual: el cableado de los eventos #
Una voz no tiene nada que mostrar. El componente no dibuja por tanto nada: ninguna barra de reproducción que colocar, ningún espacio quitado a lo que sirve.
Y para ello no tiene que cablear nada: un objeto no visual no tiene ventana, así que no hay timbre, pero el componente recoge sus propios eventos en el bucle de PowerBuilder mientras la voz está abierta y los lanza en el objeto. Solo escribe los manejadores ue_* (sin receptor, sin temporizador).
// 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
Seguir la lectura en SU texto #
ue_sentence lleva el índice y el texto de la frase que se está leyendo. De ahí sale el resaltado — en su mle_, su datawindow o su statictext: usted sabe dónde está su texto, nosotros nunca.
El camino inverso también existe: of_speak_from() retoma en una frase concreta, lo que se conecta al clic sobre un párrafo. Los números vienen de ue_sentence, así que siempre señalan lo que realmente se leyó.
El corte es el del componente, no el suyo:
of_sentence_count()devuelve su propia cuenta. No vuelva a contar por su lado, las dos derivarían.
Lo que el equipo sabe decir de verdad #
of_languages() devuelve los idiomas que este equipo sabe pronunciar de verdad, sin duplicados. Es la pregunta que se hace un usuario: no «qué voces existen», sino «está mi idioma».
of_voices() baja un escalón y nombra las voces mismas. Ambas listas vienen de la máquina, no de nosotros: nunca codifique un nombre fijo.
El reconocimiento de voz no tiene equivalente, y no es un olvido: el reconocimiento no lleva ninguna lista de los idiomas que acepta.
Sin ningún componente por su parte, gnv_utils.of_speech_languages(as_tags[]) da la misma lista — la DLL consulta una voz oculta y luego la descarta: es lo que una ventana pregunta antes de que la voz exista, qué idioma ofrecer, en cuál leer. La llamada es síncrona y puede tardar hasta dos segundos la primera vez: la lista de voces llega tarde, y la llamada la espera. Y gnv_utils.of_speech_voices(as_names[], as_langs[]) da las voces mismas con su idioma, para ofrecer «Hortense» o «Julie» en lugar de una etiqueta; gnv_utils.of_locale_name(as_tag) da a una etiqueta su nombre legible — «francés (Francia)» para fr-FR.
string ls_tags[]
if inv_voice.of_languages(ls_tags) > 0 then inv_voice.is_lang = ls_tags[1]
Propiedades #
| Propiedad | Tipo | Predeterminado | Función |
|---|---|---|---|
is_lang | string | en-US | Idioma leído, en BCP-47 (constantes LANG_*). Decide qué voz se elige — mientras is_voice esté vacía, ya que un nombre de voz fija su propio idioma. Sin voz para ese idioma el equipo lee con la que tiene, y ue_voice_fallback nombra ambas |
is_text | string | "" | El texto a leer. El componente lo corta en frases; tres etiquetas dicen CÓMO leer un trozo: [pause=500], [spell]…[/spell], [say-as=digits]…[/say-as] (ver más abajo) |
is_voice | string | "" | Nombre de la voz, tomado de of_voices(). Vacío = la primera que habla is_lang |
ii_rate | integer | 100 | Velocidad, en POR CIENTO de la normal (10 a 400). Cambiada durante la lectura, se aplica a la frase siguiente |
ii_pitch | integer | 100 | Tono de la voz, en POR CIENTO del tono normal (0 a 200) |
ii_volume | integer | 100 | Volumen en porcentaje (0 mudo a 100), la escala del reproductor de vídeo |
il_timeout_ms | long | 300000 | Duración máxima de una lectura SÍNCRONA: cinco minutos. Después, of_speak_sync devuelve -4 y la lectura se corta (is_last_error dice por qué) |
is_last_error | string | "" | Por qué el último of_speak_sync devolvió -4: voz no creada, fallo del motor, tiempo agotado |
Métodos #
| Método | Función |
|---|---|
of_open ( ) | Crea la voz. Opcional — of_speak lo hace — pero llamarlo al abrir la ventana paga el coste una vez, lejos de la primera frase. Devuelve un número positivo cuando la voz existe, 0 o menos si no pudo crearse |
of_is_open ( ) | Verdadero una vez creada la voz |
of_speak ( string as_text ) | Pone el texto y lo lee, desde la primera frase. Sin argumento, relee is_text. Devuelve 0 una vez enviado, -1 si la voz no pudo crearse |
of_speak_from ( long al_index ) | Retoma la lectura en una frase concreta. Devuelve 0 una vez enviado, -1 si la voz no pudo crearse |
of_pause ( ) | Suspende la lectura donde está. Devuelve 0 una vez enviado, -1 si la voz no pudo crearse |
of_resume ( ) | Retoma donde of_pause se detuvo. Devuelve 0 una vez enviado, -1 si la voz no pudo crearse |
of_stop ( ) | Detiene la lectura; of_speak vuelve a empezar por la primera frase |
of_is_speaking ( ) | Verdadero mientras se lee una frase — una lectura en pausa sigue contando. Preguntado al componente, nunca una copia caducada |
of_sentence_count ( ) | Devuelve cuántas frases ha hecho el componente del texto |
of_count ( ) → integer | Cuántas frases ha hecho el componente del texto — el mismo número que devuelve of_sentence_count. La biblioteca hace esta pregunta bajo un solo nombre en todas partes |
of_voices ( ref string as_names[] ) | Rellena la matriz con las voces instaladas en este equipo y devuelve cuántas hay |
of_languages ( ref string as_tags[] ) | Rellena la matriz con los idiomas que este equipo sabe pronunciar, sin duplicados, y devuelve cuántos hay |
of_voice_used ( ) | La voz que el componente entregará realmente al motor — no siempre la que pide is_lang: un equipo lleva las voces que alguien instaló y ninguna otra. Vacío = el componente no impone ninguna, y el motor toma la suya, la del idioma del sistema. Nombrarla sería adivinar. ue_error lo dice al leer; esto se lee antes |
of_speak_sync ( { string as_text } ) | Lee y ESPERA el final: la línea siguiente corre tras la última frase, la ventana sigue pintándose. Devuelve 0 al final, -4 en caso de fallo o tiempo agotado (is_last_error) |
of_enqueue ( string as_text ) | Pone un texto en COLA: leído enseguida si la voz está libre, tras la lectura en curso si no, sin cortarla nunca. Devuelve 0, -1 si la voz no pudo crearse |
of_clear_queue ( ) | Olvida los textos en espera, sin cortar el que se lee. Devuelve 0, -1 si la voz no pudo crearse |
of_queue_count ( ) | Devuelve el número de textos aún en espera (el que se lee no cuenta) |
of_add_replacement ( string as_from, string as_to ) | Una regla de pronunciación: cada palabra ENTERA as_from se lee as_to (PB → PowerBuilder). Aplicada antes de las etiquetas, guardada por el objeto. Devuelve 0, -5 si as_from está vacío |
of_clear_replacements ( ) | Vacía el diccionario. Devuelve 0 |
of_replacement_count ( ) | Devuelve el número de reglas del diccionario |
of_pick_voice ( string as_lang, string as_gender ) | Elige una voz INSTALADA para un idioma y, si existe, un género (GENDER_FEMALE, GENDER_MALE, GENDER_ANY): idioma exacto, luego su familia. La pone en is_voice y la devuelve; vacío si ninguna voz habla ese idioma |
of_duration ( ) | Devuelve la duración ESTIMADA de la lectura, en milisegundos (palabras por minuto a la velocidad pedida, pausas incluidas): para una barra de progreso, no para un cronómetro. El ritmo APRENDE la voz: cada frase leída hasta el final mide el real, recordado por voz en este equipo |
of_position ( ) | Devuelve la posición estimada de la lectura, en milisegundos, afinada por las palabras que el motor reporta; 0 cuando nada se lee |
of_progress ( ) | Devuelve el progreso estimado, de 0 a 100 |
of_spoken_text ( ) | Las frases TAL COMO la voz las recibe, una por línea (diccionario aplicado, etiquetas resueltas): el texto a mostrar para seguir palabra a palabra |
of_process_events ( ) | Vacía los eventos pendientes y los lanza sobre este objeto. El pump interno del componente lo llama por usted mientras la voz está abierta — usted nunca lo llama |
of_close ( ) | Libera la voz, deteniendo antes lo que estuviera diciendo. El destructor lo llama |
of_reset ( ) | Devuelve todas las propiedades a su valor original |
Eventos #
| Evento | Se dispara cuando |
|---|---|
ue_started (string as_lang) | La lectura comienza; as_lang recuerda en qué idioma |
ue_stopped ( ) | La última frase ha terminado, o se ha llamado a of_stop |
ue_paused ( ) | La lectura está suspendida |
ue_resumed ( ) | La lectura se reanuda |
ue_sentence (long al_index, string as_text) | Para cada frase, con su rango y su texto: así se sigue la lectura en otra parte de la ventana |
ue_error (string as_message) | El equipo no tiene motor ni voz alguna, o la voz falla. Una voz simplemente AUSENTE no es un error: es ue_voice_fallback |
ue_voices_ready (long al_count) | El motor ha llenado su lista de voces — llega tarde; of_voices, of_languages, of_voice_used y of_pick_voice la esperan por sí mismos (2,5 s como máximo), este evento solo dice CUÁNDO llegó; al_count dice cuántas tiene el equipo |
ue_word (long al_index, long al_start, long al_length) | La PALABRA en curso en la frase al_index: Mid(frase, al_start, al_length). Cuando el motor reporta las palabras (la mayoría de las voces de Windows) |
ue_queue_done ( ) | El último texto de la cola (of_enqueue) está leído |
ue_voice_fallback (string as_wanted, string as_used) | La voz o el idioma pedido no está en este equipo; as_used nombra la que lee en su lugar. Una información, no un error: la lectura continúa |
Pronunciación: pausas, deletreo, diccionario #
WebView2 no tiene SSML. El texto lleva por tanto tres etiquetas propias, resueltas antes del corte en frases, y un diccionario de palabras enteras aplicado antes. Una etiqueta desconocida se lee tal cual.
| Etiqueta | Efecto |
|---|---|
[pause=500] | Un silencio de 500 ms (10 s como máximo). La pausa termina la frase en curso |
[spell]ABC12[/spell] | Cada carácter dicho uno por uno: «A, B, C, 1, 2» |
[say-as=digits]4152[/say-as] | Los dígitos uno por uno, no «cuatro mil ciento cincuenta y dos» |
[say-as=characters]…[/say-as] | Como [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].")
Cola, lectura síncrona, progreso #
of_speakcorta,of_enqueueespera. Una aplicación que anuncia eventos (alerta, resultado, notificación) encola: dos anuncios cercanos se oyen ambos, yue_queue_donedice cuándo se leyó el último.of_clear_queueolvida lo que espera sin cortar;of_stophace ambas cosas.of_speak_syncdevuelve el control al final. El script espera la última frase (la ventana sigue pintándose), limitado poril_timeout_ms— para «lee esto, luego haz la pregunta».of_duration,of_position,of_progressson estimaciones: el motor no dice nada de la duración, cuentan palabras a la velocidad pedida, afinadas por las palabras que el motor reporta. Suficiente para una barra de progreso leída desde un timer, no para un cronómetro.of_pick_voice(idioma, género)elige una voz instalada por idioma y luego género, y la pone enis_voice. El género viene del nombre que Windows da a cada voz; una voz de nombre desconocido responde aGENDER_ANY.- Sin exportación de audio. La síntesis de WebView2 no entrega ningún flujo: la lectura no puede escribirse en un archivo. No es un ajuste que falte, es el motor.
Ejemplos #
Leer una notificación #
inv_voice.of_speak("Order 4152 has been shipped. It arrives on Thursday.")
Resaltar la frase en curso #
// 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)
Elegir la voz y la velocidad #
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()
Anunciar sin cortar: la cola #
// 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
Resaltar la palabra en curso #
// 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 voz por idioma y género, luego leer esperando el final #
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
Buenas prácticas #
- Una frase cada vez: el componente confía al motor una sola frase y encadena. Eso permite detenerse limpiamente entre dos frases, y evita el abandono silencioso de Chromium más allá de unos quince segundos.
- Resalte la frase leída en su texto, desde
ue_sentence— es la mitad de para lo que sirve una lectura en voz alta. - Llame a
of_languages()en lugar de suponer: un idioma instalado en su máquina puede no estarlo en la del cliente. - No llame a
of_speaken ráfaga sobre un conjunto de datos: cada llamada corta la anterior, y el usuario solo oye comienzos. Para eso estáof_enqueue.