Differenze tra sintesi vocali, problematiche,  e indicazioni per la creazione di un dizionario specifico.

Di Adriano Barbieri.

Ogni sintesi vocale pu gestire la punteggiatura, ma non sempre allo stesso modo;  importante quindi sapere come poter interagire rispettando il loro modus operandi.

La punteggiatura serve a creare una pausa pi o meno lunga, durante la lettura di un testo, il punto fermo  quello pi delicato. Infatti, se  seguito da almeno uno spazio, si trovi a fine riga, o inserito in un acronimo del tipo "s.p.a.", la sintesi vocale si comporter in modo diverso a seconda dei casi; in effetti non sempre il punto fermo indica una pausa.

Per far s che la pausa venga rispettata occorre che il punto fermo sia seguito da almeno uno spazio, o sia a fine riga e seguito da il carattere di ritorno a capo.

Queste condizioni sono sufficienti per alcune sintesi vocali, mentre per altre come e-speak, non bastano, occorre che anche l'iniziale che segue sia maiuscola; ma non solo; che prima del punto fermo vi siano almeno due caratteri.

Quest'ultima condizione  comune a molte sintesi vocali, se ne avete pi d'una, provate questi semplici test:

::

  a. b. (due minuscole separate da punto e spazio)
  a.b. (due minuscole separate da un punto).
  A. B. (due maiuscole separate da punto e spazio).
  A.B. (due maiuscole separate da un punto).
  a.b.c. (tre minuscole separate da un punto).
  A.B.C. (tre maiuscole come sopra).
  a. b. c. (tre minuscole separate da un punto pi uno spazio).
  A. B. C. (tre maiuscole come sopra).
  ab. c. (due minuscole e una minuscola separata da un punto pi uno spazio).
  ab. C. (due minuscole e una maiuscola separata come sopra).
  AB. C. (due maiuscole e una maiuscola separata come sopra).

Noterete che ogni sintesi interagir inserendo le pause in modi diversi come ho spiegato sopra.

E-Speak richiede almeno due caratteri prima del punto, uno spazio, e una lettera maiuscola, quindi,  neccessario creare un dizionario ad hoc, che permetta di conservare il formato dell'iniziale di ogni parola e nei limiti del possibile anche tutto il resto di quest'ultima.

Prima occorre, per, spiegare alcune problematiche, perch capendo il loro meccanismo saremo in grado di aggirarle.

Prendiamo ancora ad esempio il nome Sophia. Il metodo per processare e successivamente correggere il nome Sophia, in Sopha, con l'ultima "" accentata al posto della "i" normale, potrebbe essere cos:

``"SOPHIA|[sS]ophia(\W)", parola in sostituzione "sopha\1".``

Questo approccio  funzionale solamente nel matching, per non permette di preservare l'iniziale corretta, ma si limita a correggere la parola, utilizzando una forma minuscola e fissa nella parola in sostituzione, compresa l'iniziale "s".

::

  Test. sophia, con iniziale minuscola.
  Test. Sophia, con iniziale maiuscola.

Se si prova le due righe di test appena descritte utilizzando una sintesi della Loquendo, si potr notare che la pausa che precede il nome "Sophia" verr rispettata a prescindere dalla iniziale della parola.

Cosa diversa se si provasse con Silvia della Nuance Real Speak, o con e-speak di Nvda.

La soluzione di usare sistematicamente tutte le iniziali delle parole nel campo di sostituzione in maiuscolo avrebbe degli ulteriori inconvenienti.

Nota bene, dalla snapshot 2493 sono stati apportati alcuni cambiamenti:

- Il file di configurazione nvda.ini e SpeechDicts sono ora salvati nella cartella "dati applicazioni" dell'utente,questo, se nvda viene installato tramite la versione installer.

- La modifica  stata neccessaria per  Vista e anche altri sistemi multiutente, in modo che   ogni utente potr avere nvda con la propria configurazione individuale.

- Il dizionario di default.dic non essendo pi presente la cartella SpeechDicts nei pacchetti di Nvda,  stato incorporato e renominato con "Builtin.dic". Ad ogni aggiornamento dello ScreenReader non ci sar pi pericolo di sovrascrittura, ma il dizionario di default  comunque sempre a disposizione all'utente,, mantiene la priorit sui "specifici", ed  secondario al "Builtin".

Inserendo una regex come questa:

``# guaina.``
``"GUAINA|[gG]uaina(\W)", e in sostituzione "Guina\1".``

La quale si limita a sostituire l'iniziale con una analoga maiuscola e la prima "a", con la " accentata".

L'iniziale fissa maiuscola consente il rispetto della pausa con sintesi come silvia, ed e-speak ; ma la parola "Guaina",  contenuta ad esempio anche nella parola "Sguaina", "Inguaina", "Sguainala", eccetera.

Incontrando tale parola, notare l'iniziale "s" maiuscola, la nostra regex la correggerebbe sostituendo "guaina" in "Guina".

La correzione formerebbe una parola cos: "SGuina", notare le prime due lettere "SG" maiuscole.

Come mai la s risulta "staccata"?

Questa interferenza  generata dal dizionario "builtin.dic" di Nvda, la seguente regex ne  la causa:

``#Break away a word starting with a capital from a fully uppercase word``
``"([A-Z])([A-Z][a-z])	\1 \2	1	1".``

Come si pu notare per chi mastica inglese, la regex si incarica di staccare quelle parole composte da una completamente maiuscola seguita da un'altra con l'iniziale maiuscola, come ad esempio: "WORDPad", "FIREFox", "OPENOffice", eccetera.

L'uso di un'iniziale fissa maiuscola limita la versatilit delle Regex; la Regex builtin ci obbligherebbe a inserire una nuova voce al nostro dizionario per la parola "Sguaina", dovendo inserire alla sua sinistra anche il metacarattere ``"\b"`` (barra rovesciata diagonale b) per evitare un conflitto tra le due, e limitando il pi possibile ogni contatto di ulteriori parole, cos:

``# Guaina.``
``"(\b)(GUAINA|[gG]uaina)(\W)", e in sostituzione "\1Guina\3".``

il problema per si pu aggirare sfruttando la capacit di memorizzazione dei gruppi tra parentesi tonde, e l'aiuto di una classe; salviamo quindi l'iniziale di "guaina", in questo modo: ``"([gG])".``

La classe "[gG]" permette di scegliere uno dei due caratteri predefiniti, mentre il gruppo serve a memorizzarlo. Il contenuto memorizzato lo si pu richiamare nel campo della parola in sostituzione con ``"\1"`` (barra diagonale rovesciata uno), come nell'esempio che segue:

``"([gG])UAINA|uaina(\W)", parola in sostituzione "\1uina\2".``

Abbiamo fatto s che l'iniziale della nostra parola sia variabile e non pi costante; se si incapper nuovamente nella parola "Sguaina", la nostra Regex si adatter  al resto della parola in sostituzione, ottenendo "Sguina", notare che sta volta di maiuscola c' rimasta solo la "S". Abbiamo cos aggirato un problema, ma non sar l'unico, vediamo perch.

La nostra regex funziona bene solo se incontra una parola nella forma in minuscolo, anche se in realt  in grado di riconoscere la parola nei due formati; il motivo  che abbiamo lasciato una parte fissa in minuscolo nel campo di sostituzione. Ecco come viene rielaborata la parola incontrata nei due formati:

``Guina. (in minuscolo; la prima "" accentata ci salva). ``
``SGuina. (in maiuscolo; sta volta no, perch?).``

Per l'ennesima volta il dizionario "Builtin",  la causa di questo secondo problema:

``#break up words that use a capital letter to denote another word "([a-z])([A-Z])	\1 \2	1	1".``

Analogamente alla precedente, questa si occupa di "staccare" le parole, quelle per che hanno l'iniziale maiuscola da quelle in minuscolo che la precedono, come WinFax, WordPad, AdrianoBarbieri, eccetera.

 un'altra regex utile, ma limitante perch impedisce l'uso di regex pi complesse, preposte a processare pi ricorrenze di una stessa parola, come ad esempio questa:

``# Leggici/mi/ti/la/le/li/lo. "([lL])EGGI|eggi([cCmMtT][iI]|[lL].)(\W)", voce in sostituzione "\1ggi\2\3".``

Che pu processare le seguenti varianti:

::

  eggici
  leggimi
  leggiti
  leggila
  leggile
  leggili
  leggilo

La regex mette la giusta iniziale  nel gruppo uno, e sostituisce la prima vocale "e", con la " accentata", aggiungendo il valore processato nei gruppi due e tre, sfruttando una parte fissa in minuscolo della parola "ggi", ottenendo una cosa del genere:

::

  leggimi = lggimi; tutto in minuscolo va bene.
  LEGGIMI = LggiMI; in maiuscolo, la "MI" alla fine viene staccata dal resto della parola in minuscolo a causa della Regex del dizionario "builtin" di Nvda.

Ovviamente l'effetto  ancora pi pronunciato con parole pi lunghe.

Quelle "due" ci attaccano da due fronti, ed hanno sempre la priorit sui nostri dizionari. Non vogliamo e non conviene eliminarle, perch, dopotutto, hanno la loro utilit.

Una soluzione  di aggirare queste due regex "builtin" sfruttando il loro "tallone d'Achille", le vocali accentate, che per nostra fortuna non vengono considerate, per ora!

Sfruttando appieno i "gruppi" che permettono di memorizzare il risultato di un processo in esso contenuto, per salvare e recuperare l'iniziale, ma anche altre parti di una parola, combinati opportunamente con le vocali accentate, ecco pi o meno come sar la nostra nuova Regex:

``"([lL])[eE](GGI|ggi)([cCmMtT][iI]|[lL].)(\W)", voce in sostituzione "\1\2\3\4".``

Qui, ogni singolo gruppo processa e memorizza la parte di parola ad esso assegnata, escludendo l'unica classe esterna ai gruppi, i quali, richiamati,  come pezzi di un puzzle si occupano di aggiungere le varie parti che completano la nostra parola, mantenendo il formato dell'originale. In tal modo le ricorrenze di prima vengono convertite cos:

::

  leggimi = lggimi. (in minuscolo).
  Leggimi = Lggimi. (iniziale maiuscola e resto minuscolo).
  LEGGIMI = LGGIMI. (tutto maiuscolo).

E cos via per le altre ricorrenze precedentemente elencate. Tutta la parola viene preservata e ricomposta nella sua forma originale, a parte la vocale nella classe ``"[eE]" ``, che nel nostro caso viene sostituita dalla " accentata".

Nel tentativo di rendere pi chiara possibile la comprensione della sintassi delle Regex, non ho usato una forma "nidificata" che solitamente preferisco adottare, altrimenti ecco come sarebbe stata:

``"([lL])[eE]((GGI|ggi)([cCmMtT][iI]|[lL].)\W)", voce in sostituzione "\1\2".``

La forma nidificata la trovo pi comoda nel caso di modifica. Vi ricordo che i "gruppi" vanno contati da sinistra verso destra, e nei gruppi nidificati ha priorit il primo gruppo pi esterno che ne contiene; negli esempiche seguono user una classe vuota indicante la vocale da accentare, e nel campo di sostituzione user un punto esclamativo per indicarne il punto d'inserimento:

::

  "(1)[](2)(3)" = "\1!\2\3".
  "(1(2))[](3)" = "\1!\3".
  "(1(2)(3))[](4)(5)" = "\1!\4\5".
  "(1(2))[](3(4)(5)(6))" = "\1!\3".

La struttura della nostra nuova Regex si adatterebbe a quasi tutte le sintesi vocali, ma E-speak ha un ulteriore problema...non vocalizza allo stesso modo le vocali accentate, c' una lieve differenza di inflessione da una minuscola a una maiuscola. Quindi, non  sempre possibile sfruttare le Regex a struttura "multipla" che processano la parola nei due formati ``maiuscolo/minuscolo.``

Con E-Speak occorrono due Regex, una per ogni formato della parola; questo, almeno per quelle espressioni regolari preposte a gestire pi ricorrenze di una stessa parola.

Per ognuna occorre usare una vocale maiuscola o minuscola a seconda del formato da processare, quindi  partendo dall'esempio dell'ultima Regex, ecco come deve essere convertita nel formato esteso:

``# Leggici/mi/ti/la/e/i/o minuscolo.``
``"([lL])e(ggi)([cmt]i|l.)(\W)", voce in sostituzione "\1\2\3\4".``

``# Leggici/mi/ti/la/e/i/o, maiuscolo.``
``"(L)E(GGI)([CMT]I|L.)(\W)", voce in sostituzione "\1\2\3\4".``

Ecco la stessa coppia di Espressioni regolari ma nella versione nidificata:

``# Leggici/mi/ti/la/e/i/o, minuscolo.``
``([lL])e(ggi([cmt]i|l.)\W)", voce in sostituzione "\1\2".``

``# Leggici/mi/ti/la/e/i/o, maiuscolo.``
``(L)E(GGI([CMT]I|L.)\W)", voce in sostituzione "\1\2".``

Questo sistema a doppia Regex si pu evitarlo nelle parole uniche che non hanno ricorrenze come ad esempio "Sophia":

``"([sS])(OPHIA|ophia)(\W)", voce in sostituzione "\1opha\3".``

In ogni caso, qualsiasi metodo si adotti, bisogna sempre stare attenti ai possibili conflitti tra Regex con parole che potrebero essere contenute all'interno di altre. In tal caso si deve fare in modo di "chiudere" queste regex utilizzando il giusto metacarattere escaped, nella maggior parte dei casi si pu usare ``"\b"`` (barra diagonale rovesciata b minuscola) all'inizio, mentre alla fine ``"\W"`` (barra diagonale rovesciata W maiuscola) alla fine dell'espressione regolare; in alcuni casi la ``"\b"`` pu essere anche usata alla fine in combinazione con una classe che neghi, ad esempio, anche le vocali: ``"(\b[^Ԑ녊])".``

Le vocali accentate "filtrano", quindi l'uso dei soli metacaratteri escaped a volte va affiancato alla negazione sistematica delle vocali accentate, prendete ad esempio questa parola: "url". Essa  contenuta anche in "urlo", o "url", o "urlo", e tante altre.

Chiudere questa parola con ``"\W"`` funziona bene con "urlo", ma non con "url", o urlo". Le ultime due parole, infatti, hanno una vocale accentata  che nessun metacarattere pu escludere; quello pi efficace  ``"\b",`` ma anche questo lascia filtrare le vocali accentate. Ma con una classe di negazione come ``"[^Ԑ녊]"`` di rinforzo si pu.

``# Url. ``

``"\b[uU])([rR])([lL])(\b[^])", voce in sostituzione "\1 \2 \3\4".``

Per essere sicuro che una nuova Regex inserita nel mio dizionario, funzioni senza conflitti, aggiungo temporaneamente un", ok" al termine della stringa di sostituzione.

Pu succedere benissimo che la nuova Regex non funzioni, non perch sbagliata, ma perch un'altra molto simile "non chiusa" interviene prima perch essendo pi in cima ne assume la priorit.

In tal caso se quest'ultima la si "chiude", o la si sposta subito sotto, tutto funziona. Tenetelo sempre in mente.

Ecco un valido motivo del perch il modulo dizionario non ordini alfabeticamente le voci in modo automatico, come ad esempio invecee fa Jaws che al momento non usa le Regex.

La cosa che invece manca  un campo di ricerca e la possibilit di inserire una voce nel punto desiderato, mentre attualmente il modulo si limita ad "accodare" le nuove immissioni, obbligando a sistemare manualmente i conflitti con un editor esterno come il blocco note, che purtroppo non  affidabile con files molto grossi.

Ecco perch io uso ``NotePad++``.

`********`

Per ulteriori spiegazioni, scrivere a: `Adriano Barbieri. <barbadriano@libero.it>`_


