speechout — n_pbt_speechout #
← Referência dos componentes · Índice do guia
Leitura em voz alta: a aplicação lê um texto frase a frase e diz-lhe onde vai. Sem serviço de terceiros, sem chave de API — a síntese é a do posto de trabalho.
▶ Ver ao vivo — Aplicação de demonstração, mosaico Speech out: a pré-visualização, o código que a produz e esta página, lado a lado.
Em resumo #
| Objeto não visual | n_pbt_speechout |
| Serve para | Fazer ouvir um texto: acessibilidade, mãos ocupadas, um aviso que ninguém olha |
| Princípio | Dá o texto; o componente corta-o em frases e diz-lhe qual está a ler |
| Dependência | A síntese de voz do posto — sem serviço de terceiros, sem chave de API |
Início 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.")
Não visual: a ligação dos eventos #
Uma voz não tem nada para mostrar. O componente não desenha portanto nada: nenhuma barra de leitura para arranjar espaço, nenhum lugar tirado àquilo que serve.
E para isso não tem de ligar nada: um objeto não visual não tem janela, logo não há campainha, mas o componente recolhe os seus eventos no ciclo do PowerBuilder enquanto a voz está aberta e dispara-os no objeto. Só escreve os tratadores ue_* (sem recetor, sem 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 a leitura no SEU texto #
ue_sentence transporta o índice e o texto da frase que está a ser lida. É daí que vem o realce — no seu mle_, na sua datawindow ou no seu statictext: você sabe onde está o seu texto, nós nunca.
O caminho inverso também existe: of_speak_from() retoma numa frase precisa, o que se liga ao clique num parágrafo. Os números vêm de ue_sentence, logo apontam sempre para o que foi realmente lido.
O corte é o do componente, não o seu:
of_sentence_count()devolve a contagem dele. Não volte a contar do seu lado, as duas divergiriam.
O que o posto sabe realmente dizer #
of_languages() devolve os idiomas que este posto sabe efetivamente pronunciar, sem duplicados. É a pergunta que um utilizador faz: não «que vozes existem», mas «o meu idioma está lá».
of_voices() desce um degrau e nomeia as próprias vozes. Ambas as listas vêm da máquina, não de nós: nunca escreva um nome fixo no código.
O reconhecimento de voz não tem equivalente, e não é um esquecimento: o reconhecimento não traz qualquer lista dos idiomas que aceita.
Sem qualquer componente da sua parte, gnv_utils.of_speech_languages(as_tags[]) dá a mesma lista — a DLL consulta uma voz oculta e depois descarta-a: é o que uma janela pergunta antes de a voz existir, que idioma propor, em qual ler. A chamada é síncrona e pode demorar até dois segundos da primeira vez: a lista de vozes chega tarde, e a chamada espera por ela. E gnv_utils.of_speech_voices(as_names[], as_langs[]) dá as vozes em si com o seu idioma, para propor «Hortense» ou «Julie» em vez de uma etiqueta; gnv_utils.of_locale_name(as_tag) dá a uma etiqueta o seu nome legível — «francês (França)» para fr-FR.
string ls_tags[]
if inv_voice.of_languages(ls_tags) > 0 then inv_voice.is_lang = ls_tags[1]
Propriedades #
| Propriedade | Tipo | Predefinição | Função |
|---|---|---|---|
is_lang | string | en-US | Idioma lido, em BCP-47 (constantes LANG_*). Decide que voz é escolhida — enquanto is_voice estiver vazia, pois um nome de voz fixa a sua própria língua. Sem voz para essa língua o posto lê com a que tem, e ue_voice_fallback nomeia as duas |
is_text | string | "" | O texto a ler. O componente corta-o em frases; três etiquetas dizem COMO ler um pedaço: [pause=500], [spell]…[/spell], [say-as=digits]…[/say-as] (ver abaixo) |
is_voice | string | "" | Nome da voz, tirado de of_voices(). Vazio = a primeira que fala is_lang |
ii_rate | integer | 100 | Velocidade, em POR CENTO da normal (10 a 400). Alterada durante a leitura, aplica-se à frase seguinte |
ii_pitch | integer | 100 | Altura da voz, em POR CENTO da altura normal (0 a 200) |
ii_volume | integer | 100 | Volume em percentagem (0 mudo a 100), a escala do leitor de vídeo |
il_timeout_ms | long | 300000 | Duração máxima de uma leitura SÍNCRONA: cinco minutos. Depois, of_speak_sync devolve -4 e a leitura é cortada (is_last_error diz porquê) |
is_last_error | string | "" | Porque o último of_speak_sync devolveu -4: voz não criada, falha do motor, tempo esgotado |
Métodos #
| Método | Função |
|---|---|
of_open ( ) | Cria a voz. Facultativo — of_speak fá-lo — mas chamá-lo ao abrir a janela paga o custo uma vez, longe da primeira frase. Devolve um número positivo quando a voz existe, 0 ou menos se não pôde ser criada |
of_is_open ( ) | Verdadeiro assim que a voz existe |
of_speak ( string as_text ) | Define o texto e lê-o, desde a primeira frase. Sem argumento, relê is_text. Devolve 0 uma vez enviado, -1 se a voz não pôde ser criada |
of_speak_from ( long al_index ) | Retoma a leitura numa frase precisa. Devolve 0 uma vez enviado, -1 se a voz não pôde ser criada |
of_pause ( ) | Suspende a leitura onde está. Devolve 0 uma vez enviado, -1 se a voz não pôde ser criada |
of_resume ( ) | Retoma onde of_pause parou. Devolve 0 uma vez enviado, -1 se a voz não pôde ser criada |
of_stop ( ) | Para a leitura; of_speak recomeça pela primeira frase |
of_is_speaking ( ) | Verdadeiro enquanto uma frase é lida — uma leitura em pausa ainda conta. Perguntado ao componente, nunca uma cópia desatualizada |
of_sentence_count ( ) | Devolve quantas frases o componente fez do texto |
of_count ( ) → integer | Quantas frases o componente fez do texto — o mesmo número que of_sentence_count devolve. A biblioteca faz esta pergunta sob um único nome em todo o lado |
of_voices ( ref string as_names[] ) | Preenche o vetor com as vozes instaladas neste posto e devolve quantas são |
of_languages ( ref string as_tags[] ) | Preenche o vetor com os idiomas que este posto sabe pronunciar, sem duplicados, e devolve quantos são |
of_voice_used ( ) | A voz que o componente entregará realmente ao motor — nem sempre a que is_lang pede: um posto carrega as vozes que alguém lá instalou e nenhuma outra. Vazio = o componente não impõe nenhuma, e o motor toma a sua, a da língua do sistema. Nomeá-la seria adivinhar. ue_error diz o mesmo ao ler; isto lê-se antes |
of_speak_sync ( { string as_text } ) | Lê e ESPERA o fim: a linha seguinte corre após a última frase, a janela continua a pintar-se. Devolve 0 no fim, -4 em caso de falha ou tempo esgotado (is_last_error) |
of_enqueue ( string as_text ) | Põe um texto em FILA: lido de imediato se a voz está livre, após a leitura em curso caso contrário, sem nunca a cortar. Devolve 0, -1 se a voz não pôde ser criada |
of_clear_queue ( ) | Esquece os textos em espera, sem cortar o que se lê. Devolve 0, -1 se a voz não pôde ser criada |
of_queue_count ( ) | Devolve o número de textos ainda em espera (o que se lê não conta) |
of_add_replacement ( string as_from, string as_to ) | Uma regra de pronúncia: cada palavra INTEIRA as_from é lida as_to (PB → PowerBuilder). Aplicada antes das etiquetas, guardada pelo objeto. Devolve 0, -5 se as_from estiver vazio |
of_clear_replacements ( ) | Esvazia o dicionário. Devolve 0 |
of_replacement_count ( ) | Devolve o número de regras do dicionário |
of_pick_voice ( string as_lang, string as_gender ) | Escolhe uma voz INSTALADA para um idioma e, se existir, um género (GENDER_FEMALE, GENDER_MALE, GENDER_ANY): idioma exato, depois a sua família. Põe-na em is_voice e devolve-a; vazio se nenhuma voz fala esse idioma |
of_duration ( ) | Devolve a duração ESTIMADA da leitura, em milissegundos (palavras por minuto ao ritmo pedido, pausas incluídas): para uma barra de progresso, não para um cronómetro. O ritmo APRENDE a voz: cada frase lida até ao fim mede o real, guardado por voz neste posto |
of_position ( ) | Devolve a posição estimada da leitura, em milissegundos, afinada pelas palavras que o motor reporta; 0 quando nada se lê |
of_progress ( ) | Devolve o progresso estimado, de 0 a 100 |
of_spoken_text ( ) | As frases TAL COMO a voz as recebe, uma por linha (dicionário aplicado, etiquetas resolvidas): o texto a mostrar para seguir palavra a palavra |
of_process_events ( ) | Esvazia os eventos pendentes e levanta-os neste objeto. O pump interno do componente chama-o por si enquanto a voz está aberta — nunca o chama |
of_close ( ) | Liberta a voz, parando antes o que estivesse a dizer. O destrutor chama-o |
of_reset ( ) | Repõe todas as propriedades no valor inicial |
Eventos #
| Evento | Disparado quando |
|---|---|
ue_started (string as_lang) | A leitura começa; as_lang recorda em que idioma |
ue_stopped ( ) | A última frase terminou, ou of_stop foi chamado |
ue_paused ( ) | A leitura está suspensa |
ue_resumed ( ) | A leitura recomeça |
ue_sentence (long al_index, string as_text) | Para cada frase, com o seu lugar e o seu texto: é assim que se segue a leitura noutro ponto da janela |
ue_error (string as_message) | O posto não tem motor nem voz alguma, ou a voz falha. Uma voz simplesmente AUSENTE não é um erro: é ue_voice_fallback |
ue_voices_ready (long al_count) | O motor preencheu a sua lista de vozes — chega tarde; of_voices, of_languages, of_voice_used e of_pick_voice esperam por ela sozinhos (2,5 s no máximo), este evento só diz QUANDO chegou; al_count diz quantas o posto tem |
ue_word (long al_index, long al_start, long al_length) | A PALAVRA em curso na frase al_index: Mid(frase, al_start, al_length). Quando o motor reporta as palavras (a maioria das vozes Windows) |
ue_queue_done ( ) | O último texto da fila (of_enqueue) está lido |
ue_voice_fallback (string as_wanted, string as_used) | A voz ou o idioma pedido não está neste posto; as_used nomeia a que lê em seu lugar. Uma informação, não um erro: a leitura continua |
Pronúncia: pausas, soletração, dicionário #
O WebView2 não tem SSML. O texto traz por isso três etiquetas próprias, resolvidas antes do corte em frases, e um dicionário de palavras inteiras aplicado antes delas. Uma etiqueta desconhecida é lida tal como está.
| Etiqueta | Efeito |
|---|---|
[pause=500] | Um silêncio de 500 ms (10 s no máximo). A pausa termina a frase em curso |
[spell]ABC12[/spell] | Cada carácter dito um a um: «A, B, C, 1, 2» |
[say-as=digits]4152[/say-as] | Os dígitos um a um, não «quatro mil cento e cinquenta e dois» |
[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].")
Fila, leitura síncrona, progresso #
of_speakcorta,of_enqueueespera. Uma aplicação que anuncia eventos (alerta, resultado, notificação) põe em fila: dois anúncios próximos são ambos ouvidos, eue_queue_donediz quando o último foi lido.of_clear_queueesquece o que espera sem cortar;of_stopfaz as duas coisas.of_speak_syncdevolve o controlo no fim. O script espera a última frase (a janela continua a pintar-se), limitado poril_timeout_ms— para «lê isto, depois faz a pergunta».of_duration,of_position,of_progresssão estimativas: o motor nada diz da duração, contam palavras ao ritmo pedido, afinadas pelas palavras que o motor reporta. Suficiente para uma barra de progresso lida a partir de um timer, não para um cronómetro.of_pick_voice(idioma, género)escolhe uma voz instalada por idioma e depois género, e põe-na emis_voice. O género vem do nome próprio que o Windows dá a cada voz; uma voz de nome desconhecido responde aGENDER_ANY.- Sem exportação áudio. A síntese do WebView2 não devolve nenhum fluxo: a leitura não pode ser escrita num ficheiro. Não é uma definição em falta, é o motor.
Exemplos #
Ler uma notificação #
inv_voice.of_speak("Order 4152 has been shipped. It arrives on Thursday.")
Realçar a frase em 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)
Escolher a voz e a velocidade #
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 sem cortar: a fila #
// 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
Realçar a palavra em 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)
Uma voz por idioma e género, depois ler esperando o fim #
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
Boas práticas #
- Uma frase de cada vez: o componente entrega ao motor uma só frase e encadeia. É isso que permite parar limpamente entre duas frases, e evita a desistência silenciosa do Chromium para lá de uns quinze segundos.
- Realce a frase lida no seu texto, a partir de
ue_sentence— é metade daquilo para que serve ler em voz alta. - Chame
of_languages()em vez de supor: um idioma instalado na sua máquina pode não estar na do cliente. - Não chame
of_speakem rajada sobre um conjunto de dados: cada chamada corta a anterior, e o utilizador só ouve inícios. Para isso existeof_enqueue.