Sistem Pentru Sinteza Vocala cu Sistemul de Dezvoltare Raspberry Pi
Cuprins
Lista figurilor și lista tabelelor
Lista acronimelor
ABP – Alternating Bit Protocol (Protocol de comunicație la nivel de date)
ADC – Analog Digital Converter (Convertor analog-digital)
Arborilor de clasificare și regresie (CARTs) – Metodă de modelare predictivă
BIOS – Basic Input/Output System (Interfață grafică folosită pentru a accesa software-ul plăcii de bază
DAC – Digital Analog Converter (Convertor digital-analog)
EEPROM – Electrically Erasable Programmable Read-Only Memory (Memorie de dimensiuni mici folosită pentru stocare)
Ethernet – Standard la nivel fizic ce permite interconectarea calculatoarelor
FireWire – Protocol de transmisie a datelor prin cablu
FULL DUPLEX – Transmiune a datelor în două direcții simultan.
GPIO – General-purpose input/output (Pini de intrare-iesire aparținând unui circuit integrat ce pot fi controlați de către utilizator)
GPU – Graphics Processing Unit (Unitate de procesare grafică)
HDMI – High-Definition Multimedia Interface (Interfață compactă audio/video pentru transferul datelor de tip audio/video)
JAR – Java Archive (Arhivă Java)
LAN – Local Area Network (Rețea ce interconectează mai multe calculatoare într-o arie limitată)
LED – Light-emitting diode (Diodă emițătoarea de lumină)
Low level – Programarea în limbajul de asamblare sau cod mașină.
MMC – MultiMediaCard (Card multimedia)
NTSC – National Television System Committee (Sistemul de televiziunea analog folosit în America de Sud)
PAL – Phase Alternating Line (Sistem de codare color pentru televiziunea analog folosit pentru transmiterea semnalelor TV)
PC – Personal Computer (Calculator Personal)
PID – Process ID (Identificator unic asociat de sistemul de operare unui process)
Port COM – Port de comunicație folosind o interfață serială
RAM – Random Access Memory (Memorie ce poate fi scrisă și citită)
RCA – Cablu electric ce poate transmite atât semnalul audio, cât și video
ROM – Read Only Memory (Memorie ce poate fi numai citită)
SD – Secure Digital (Card de memorie cu acces aleator)
SDIO – Secure Digital Input Output (Card de memorie special de tip SD)
Shell – Tip de interfață în linie de comandă
Socket – conexiune de date între calculatoare
SPI – Serial Peripheral Interface (Magistrala de comunicație full-duplex serial)
SSH – Secure Shell (Protocol de rețea pentru criptarea datelor)
TTS – Text to Speech (Sistem pentru sinteza vocală)
UART – Universal asynchronous receiver/transmitter (Receptor/Transmițător universal asincron)
URL – Universal Resource Locator (Adresă universală pentru o resursă)
USART – Universal Synchronous/Asynchronous Receiver/Transmitter (Convertor între interfața serială și cea paralelă)
USB – Universal Serial Bus (Magistrală de comunicare serială universal)
VGA – Video Graphics Array (Conector electric de tip D, cu 3 rânduri de pini)
VNC – Virtual Networking Computing (Sistem de distribuție grafică utilizat pentru a accesa un alt computer la distanța)
Introducere
Un sintetizator vocal, text-to-speech (TTS), reprezintă un sistem realizat cu ajutorul unui computer ce poate face posibilă redarea oricărui tip de text introdus în computer de către un operator. Distincția fundamentală între acest tip de sistem și orice alt tip de mașină/computer ce poate vorbi, de exemplu un casetofon, este aceea că suntem interesați de producerea automată a unor noi propoziții. În contextul sintezei de tip TTS este imposibil să înregistram întregul vocabular al unei limbi și să îl stocăm. Este de aceea mai convenabil să definim un sistem Text-To-Speech ca procesul automat de reproducere verbală a unui text, prin intermediul unei asocieri de tip grafeme-foneme.[1]
Un fonem reprezintă “cea mai mică unitate sonoră a unei limbi, care are funcțiunea de a diferenția cuvintele între ele, precum și formele gramaticale ale aceluiaș cuvânt”, iar grafemul reprezintă cea mai mică unitate semantică a unei limbi scrise, analog fonemelor in limba vorbită. [2]
În prima fază acest obiectiv nu pare greu de realizat, dat fiind faptul că oamenii au potențialul să pronunțe corect o propoziăie necunoscută lor, încă din copilărie. Cu toții avem, în mod inconștient mai mult, o ințelegere profundă a regulilor de citire. Aceste reguli ne-au fost oferite încă din clasele primare într-o formă simplificată, reguli pe care le-am perfecționat ulterior de-a lungul timpului. Însă, între un om și un computer există o discrepanță majoră din acest punct de vedere, deși progrese semnificative au avut loc în ultimul timp atât în domeniul procesării de semnal, cât si al inteligenței artificiale.
Fiecare sintetizator reprezintă rezultatul particular al imitării într-un mod original al capabilităților omului de a citi, constrâns de caracteristicile tehnologice și imaginative ale momentului creării sintetizatorului.[1]
Aplicații ale sintetizatoarelor vocale TTS se pot găsi în domenii precum: servicii de telecomunicații, educație, ajutarea persoanelor cu handicap, monitorizarea vocală, multimedia (interacțiunea/comunicarea între om și mașină).
Un sintetizator TTS prezintă două componente :
Procesarea digitală de semnal
Procesarea de limbaj natural.
Procesarea de limbaj natural este capabilă să producă un transcript fonetic al textului citit, împreuna cu intonația și ritmul dorit. Procesarea digitală de semnal transformă informația recepționată de la blocul ce se ocupă cu procesarea de limbaj natural în discurs.
Figura – Schema bloc a unui sistem pentru sinteza vocală de tip text-to-speech (TTS)
Prezenta lucrare are drept scop implementarea și evaluarea performanțelor algoritmilor texttospeech pe sisteme de tip SoC echipate cu microcontrolerul Broadcom BCM2835. Pentru implementarea algoritmilor de sinteză vocală va fi utilizat sistemul de dezvoltare Raspberry PI, rulând sistemul de operare Raspbian. Vor fi implementați algoritmii de sinteză vocală Festival, eSpeak și Google Text to Speech. Algoritmii Festival și eSpeak rulează în mod offline, utilizând resursele sistemului Raspberry PI. Google Text-to-speech rulează în mod online, necesitând existența unei conexiuni Internet. În vederea evaluării performanțelor algoritmilor va fi implementată o interfață unică de control a acestora, utilizând limbajul Java. Interfața Java permite atât redarea textului introdus de la tastatură, cât și redarea fișierelor text de pe mașina curentă de unde este pornită aplicația sau de la o adresă de Internet. Suplimentar vor fi efectuate comparații între performanțele obținute de aceeași algoritmi pe o platformă PC, utilizând o mașină virtuală ce rulează sistemul de operare Debian. Am ales ca mașina virtuală să folosească sistemului de operare Debian, deoarece Raspbian reprezintă o versiune de Debian adaptată să funcționeze corespunzător pe Raspberry PI.
Capitolul 1
– Raspberry PI –
Raspberry PI reprezintă un calculator de dimensiuni reduse la care se poate atașa un mouse sau o tastatură, având un sistem propriu de operare care îți oferă acces atât la Internet, cât și la o gamă largă de aplicații. Este un calculator multifuncțional, care poate fi folosit în proiecte de electronică, si pentru multe din funcțiile pe care le îndeplinește un calculator PC standard, cum ar fi procesarea de foi de calcul, de test și pentru jocuri. De asemenea, poate reda conținut video de calitate înaltă. Raspberry PI reprezintă un mod foarte accesibil de a învăța programarea. [3]
Figura 1.1. Raspberry PI model B
După achiziționare plăcuței este necesar un alimentator si un card SD pe care să se găseacă sistemul de operare Raspbian.
Placa Raspberry PI conține un procesor și un cip pentru procesare grafică, memorie de program (memorie RAM) și alte interfețe și conectori pentru dispozitive externe. Unele dintre aceste dispozitive sunt esențiale, altele opționale. Raspberry PI funcționeaza la fel ca un calculator standard, având nevoie de o tastatură pentru a introduce comenzi, de un monitor sau sistem de afișaj, și de o sursă de curent.
Un avantaj care îl are față de un calculator PC reprezintă conexiunea directă a plăcii Raspberry PI la alte dispozitive precum senzori de lumină, temperatură, presiune etc. cu ajutorul pinilor GPIO. Acești pini pot fi controlați din limbajele de programare ce ruleaza pe Raspberry PI precum Python, C++, JAVA, PHP, .NET, etc. [4]
Raspberry PI există în două variante: modelul A care are 256MB RAM, un port USB și nici un port Ethernet și Modelul B, prezentat în Figura 1.1, care are 512MB RAM, două porturi USB și un port Ethernet. [5]
Pentru pornirea plăcutei, trebuie realizată conexiunea prin intermediul socket-ului microUSB la o sursă de alimentare. Pentru oprire, mai întâi trebuie executată comanda "sudo halt -h" în fereastra de terminal. Se așteaptă apoi până când toate LED-urile în afară de cel de pornit/oprit sunt stinse, apoi înca o secundă pentru a fi siguri că s-a terminat transferul de date între plăcută și cardul SD, si apoi se poate deconecta plăcuța de cablul din socket-ul microUSB. Dacă nu se urmează acești pași, este posibil ca datele de pe cardul SD să fie corupte, ceea ce ar însemna că imaginea sistemului de operare Linux ar trebui rescrisă pe card. [6]
De asemenea, Raspberry PI are nevoie de o modalitate de stocare pe termen lung, dar un hard disk găsit într-un calculator de tip PC nu este agreat datorită dimensiunilor acestuia mai mari decât ale plăcuței. De aceea, se folosește un card de memorie SD Flash folosit adesea la camere digitale, configurat într-un asemenea mod încât să pară ca un hard disk pentru procesorul Raspberry PI. Plăcuța va boota, ceea ce înseamnă încarcarea sistemului de operare în memoria RAM, de pe acest card la fel cum un calculator booteaza Windows-ul de pe un hard disk.
Specificații
Compararea din punct de vedere al specificațiilor tehnice între modelul A și B al plăcuței Raspberry PI:
Tabel 1.1. Comparație între Raspberry PI modelul A si modelul B [7]
Componente Raspberry PI Model B:
Sistem de tip SoC (System on a chip) : Broadcom BCM2835;
LAN9512;
S1: Socket Micro USB de putere (5V – doar pentru putere);
S2: Interfața de afișaj serial (DSI). Conector cu 15 pini, oferind două canale de date, unul pentru ceas, 3.3V si GND (masă);
S3: Conector HDMI;
S4: Conector video compozit de tip RCA;
S5: Interfață serială pentru cameră (CSI-2). Conector cu 15 pini;
S6: Conector audio : Socket de 3.5mm stereo (ieșire doar);
S8: Slot pentru card de memorie SD/MMC/SDIO;
S7: 2 porturi USB 2.0;
P1: 26 pini (2×13), dintre care:
8 pini de intrare/ieșire de tip GPIO la 3.3V;
2 pini de tip Receptor/Transmițător universal asincron (UART); 2 pini de tip GPIO folosiți;
Interfața I²C; 2 pini de tip GPIO folosiți;
Interfața SPI 3v3; 5 pini de tip GPIO folosiți;
Pini pentru alimentare de 3.3V, 5V si GND (masă);
Conector ARM JTAG;
Interfața I²S;
P2: 8 pini pentru portul JTAG al unității de procesare grafică (GPU);
P3: 7 pini pentru portul JTAG al LAN9512;
P4: Socket Ethernet de tip RJ45 10/100Mb;
P5: 8 pini (2×4), pe partea de jos a plăcuței, dintre care:
4 pini de intrare/ieșire de tip GPIO la 3.3V;
Pini pentru alimentarea de 3.3V, 5V si GND (masa);
Interfața secundară I²C la 3.3V;
Interfața I²S;
Semnale de tip “handshake”(acord) catre UART de pe P1;
P6: 2 pini ce permit conectarea la buton de reset hardware (momentan Raspberry PI nu are un astfel de buton, plăcuța putând fi oprită doar cu comanda sudo halt –p);
TP1 și TP2: Puncte de test oferind acces la +5V și GND (masă);
5 LED-uri pentru statusul Raspberry-ului PI:
D5 (Verde) – Acces card SD prin intermediul pinului GPIO16 – etichetat ca “ACT” pentru Model B 2.0;
D6 (Roșu) – Putere de 3.3 V – etichetat ca “PWR”;
D7 (Verde) – Transmiterea si recepționarea datelor simulatană prin cablu LAN de tip “Full Duplex” – etichetat ca “FDX”;
D8 (Verde) – Activitate cablului LAN – etichetat ca “LNK”;
D9 (Galben) – 10/100Mbit (LAN) – etichetat ca “100 ” pentru Model B 2.0;
Dimensiune plăcuță: 85.0 x 56.0 mm x 17mm;
Greutatea: 40g. [7]
Componente periferice
ntru portul JTAG al unității de procesare grafică (GPU);
P3: 7 pini pentru portul JTAG al LAN9512;
P4: Socket Ethernet de tip RJ45 10/100Mb;
P5: 8 pini (2×4), pe partea de jos a plăcuței, dintre care:
4 pini de intrare/ieșire de tip GPIO la 3.3V;
Pini pentru alimentarea de 3.3V, 5V si GND (masa);
Interfața secundară I²C la 3.3V;
Interfața I²S;
Semnale de tip “handshake”(acord) catre UART de pe P1;
P6: 2 pini ce permit conectarea la buton de reset hardware (momentan Raspberry PI nu are un astfel de buton, plăcuța putând fi oprită doar cu comanda sudo halt –p);
TP1 și TP2: Puncte de test oferind acces la +5V și GND (masă);
5 LED-uri pentru statusul Raspberry-ului PI:
D5 (Verde) – Acces card SD prin intermediul pinului GPIO16 – etichetat ca “ACT” pentru Model B 2.0;
D6 (Roșu) – Putere de 3.3 V – etichetat ca “PWR”;
D7 (Verde) – Transmiterea si recepționarea datelor simulatană prin cablu LAN de tip “Full Duplex” – etichetat ca “FDX”;
D8 (Verde) – Activitate cablului LAN – etichetat ca “LNK”;
D9 (Galben) – 10/100Mbit (LAN) – etichetat ca “100 ” pentru Model B 2.0;
Dimensiune plăcuță: 85.0 x 56.0 mm x 17mm;
Greutatea: 40g. [7]
Componente periferice
Următoarele componente periferice sunt folosite:
Card de memorie SD conținând sistemul de operare Linux(Raspbian);
Tastatură USB;
Mouse USB;
Adaptor USB WiFi pentru conectarea la Internet;
Monitor;
Sursă de curent;
Cablu HDMI pentru conectarea monitorului;
HUB USB;
Boxe;
Cablu LAN pentru conectarea la Internet;
Modul de interconectare al componentelor periferice
Modul în care se conectează componentele între ele este următorul:
Se introduce cardul SD în plăcuță Raspberry PI.
Se introduce tastatura USB și mouse-ul în placută, prin intermediul hub-ului USB, daca este prezent.
Se conectează cablul video la monitor si apoi la plăcuță.
Se conectează restul componentelor la plăcuță (USB WiFi, cablu Ethernet, hard disk extern, etc.). Dacă nu mai sunt suficiente conexiuni USB, se poate apela la un hub USB.
Se verifică faptul că monitorul și hub-ul USB sunt pornite.
Cu monitorul pornit, se alimentează plăcuța prin intermediul socket-ului microUSB.
Raspberry PI ar trebui să booteze și să afișeze mesaje pe ecran.
Se recomandă întotdeauna conectarea microUSB la sursa de alimentare la final.
Timpul de bootare când plăcuța este pornită prima oară poate fi lung, de aceea nu se recomandă restartarea.
1.5 Descrierea detaliată a componentelor periferice
Cardul SD este folosit pentru stocarea sistemului de operare.
Deoarece Raspberry PI nu are o unitate de stocare de mare capacitate internă sau sistem de operare preconfigurat, are nevoie de un card SD încărcat cu o versiune a sistemului de operare Linux.
Acest card preîncărcat se poate crea de utilizator folosind orice card SD de dimensiuni peste 4GB minim. Se recomandă folosirea unui card nou sau un card formatat, dedicat special sistemului de operare Raspbian, cu o dimensiune de 8GB.
Carduri preîncărcate cu sistem de operare Raspbian se găsesc de asemenea la magazinul Raspberry PI.
Mouse și tastatura
Majoritatea tastaturilor si moușilor standard USB pot fi folosiți cu Raspberry PI. Mouși si tastaturi fără fir (wireless) funcționeaza de asemenea, și au nevoie de un singur port USB pentru antena RF. Pentru a folosi un mouse sau o tastatură Bluetooth este nevoie de un conector USB – Bluetooth, care de asemenea va folosi un singur port USB. Modelul de Raspberry PI tip A are un singur conector USB, iar modelul de tip B are doi conectori.
Monitor
Există două variante pentru conectarea display-ului la Raspberry PI: prin HDMI (calitate înaltă) și prin compozit (calitate standard).
Televizoarele HD și multe monitoare LCD pot fi conectate folosind un cablu HDMI cu toți pinii și cu un adaptor dacă se folosește DVI. Versiunile HDMI 1.3 si 1.4 sunt suportate, iar versiunea 1.4 este recomandată. Plăcuța Raspberry PI emite semnal audio si video prin intermediul HDMI, dar nu suportă intrare HDMI.
Televizoarele mai vechi pot fi conectate folosind ieșirea video compozită (un cablu galben RCA) sau prin intermediul SCART (folosind o ieșire video compozită și un adaptor SCART). Atât PAL, cât și NTSC, ca formate video, sunt suportate.
Atunci când se folosește o conexiune video compozită, semnalul audio este disponibil prin intermediul socket-ului de 3.5 mm și poate fi transmis la televizor, căști sau la un amplificator. Pentru transmiterea semnalului audio la televizor, este nevoie de un cablu care să adapteze de la 3.5 mm la conector dublu RCA (un cablu roșu si unul negru).
De notat că nu există ieșire video analogică VGA. Aceasta este conexiunea folosita de multe monitoare, în afară de cele mai noi. Nu se poate folosi un monitor cu conexiune de tip D. [8]
Unitatea de alimentare
Raspberry PI este alimentată prin intermediul unui conector microUSB (numai pinii de alimentare sunt conectați, deci nu se pot transfera date prin această conexiune). Se poate folosi un încărcător de telefon standard de tip microUSB, care să poată alimenta la 700mA la +5Vdc. Adaptori necesari se pot gasi la magazinul Raspberry Pi și sunt recomandați dacă nu sunteți convins ce alimentator să folosiți.
De notat ca porturile USB individuale ale unui hub sau ale unui calculator sunt de obicei cotate ca alimentând cu 500mA maxim. Dacă doriți să folosiți unul dintre aceste porturi ca sursă de alimentare, va trebui să folosiți un cablu care se conectează la două porturi și care oferă un curent combinat de 1000mA. [9]
Cabluri
Este nevoie de următoarele cabluri pentru a realiza conexiunile între plăcuța Raspberry PI și celălalte dispozitive:
Variante pentru cablul video: cablu HDMI-A, cablu HDMI-A și adaptor DVI, cablu video compozit sau cablu video compozit și adaptor SCART.
Cablu audio (nu este necesar dacă se folosește conectorul HDMI legat la un televizor).
Cablu Ethernet/LAN (doar model B). [10]
Hub USB
Pentru a conecta echipamente adiționale la plăcuța Raspberry PI, se poate achiziționa un hub USB, care va permite multiplicarea numărului de dispozitive folosite.
Este recomandat să se foloseasca un hub care să se alimenteze direct de la o sursă de curent. Se recomandă folosirea unui hub de tip USB 2.0. Pentru mouse și tastatură un hub 1.1 este suficient, dar poate să nu fie compatibil cu alte dispozitive. [11]
Carcasa
Deoarece Raspberry PI nu este livrat cu o carcasă, este important să nu fie folosit acolo unde poate intra în contact cu metale conductoare sau cu lichide. [12]
Periferice low-level
Dacă se dorește folosirea interfețelor low-level disponibile pe Raspberry PI, trebuie avut în vedere utilizarea unui conector cu pini GPIO.
De asemenea, este necesară protejarea plăcuței cu circuite de protecție dacă este utilizată în proiecte cu dispozitive low-level.
Conectarea la Internet
Se poate realiza prin intermediul unui cablu Ethernet/LAN (conector standard RJ45) sau cu un adaptor USB WiFi. Portul Ethernet pentru Raspberry PI model B poate auto-detecta tipul rețelei.
Aceasta înseamnă că se poate conecta atât la un router, cât și direct la un alt calculator, fără a fi nevoie de un cablu crossover. [13]
Conectarea si comunicarea cu Raspberry PI
Cea mai simplă metodă de conectare este prin intermediul conexiunii seriale. Comunicarea depinde de transmisia datelor octet cu octet, este ușor de configurat și este în general accesibilă chiar înainte de terminarea bootării sistemului de operare.
Pentru a realiza conexiunea serială, se folosește cablul serial conectat la portul COM al plăcuței Raspberry PI, cu celălalt capăt al cablului conectat la portul COM al adaptorului serial-USB legat la calculator.
Pentru conectarea la plăcuța Raspberry PI folosind un computer, se poate folosi Putty pentru execuția comenzilor și VNC (Virtual Network Computing) pentru a accesa interfața grafică a sistemului de operare a plăcii Raspberry PI.
Componenta audio
Semnalul audio poate fi difuzat prin intermediul boxelor sau căștilor folosind socket-ul de 3.5 mm.
Făra un cablu HDMI, este necesar folosirea unui socket de 3.5mm. Dacă se foloseste un cablu HDMI pentru conectarea la un monitor cu difuzoare, atunci nu mai este nevoie de socket de 3.5mm, deoarece semnalul audio poate fi redat direct prin intermediul monitorului; dar este posibilă conectarea la boxe sau căști dacă preferam să avem semnalul audio difuzat prin acestea, necesitând o configurare suplimentară.
Următoarea comandă: amixer cset numid=3 1 redirecționează semnalul audio către HDMI (valoare 1 de la finalul instrucțiunii). Dacă dorim ca ieșirea audio să fie redirecționată către analog (boxe, căști), se va înlocui valoare 1 cu valoare 2, iar pentru o detectarea automată se va folosi valoarea 0. [14]
Capitolul 2
– Broadcom BCM2835 –
Sisteme de tip System on a Chip (SoC)
Un sistem de tip System on a Chip (SoC) reprezintă un circuit integrat alcătuit din toate componentele unui computer sau ale unui sistem electronic într-un singur cip. Poate conține semnale digitale, analogice sau mixte și în mod uzual funcții de radio-frecvență – toate pe un singur substrat al cipului. Datorită consumului redus de putere, aceste sisteme de tip SoC reprezintă o soluție utilizată frecvent în industria telefoanelor mobile. Una din aplicațiile sistemelor SoC o reprezintă sistemele integrate “embedded”. [15]
Diferența dintre sistemele SoC și microcrontrolere o reprezintă ordinul de mărime al memoriei RAM de care dispun acestea. Microcontrolerul are de obicei o memorie RAM mai mică de 100 kB, pe când sistemele de tip SoC au procesoare mult mai puternice, ce permit rularea sistemelor de operare precum Windows sau Linux, care au nevoie de chip-uri de memorie externă și sunt folosite cu diverse periferice externe. Când nu este posibilă construcția unui SoC pentru o anumită aplicație, o alternativă o reprezintă sistemele în pachete (system in package, SiP), ce reprezintă un număr de cipuri într-un singur pachet. Pe scară largă, SoC se presupune a fi mult mai rentabil din punct de vedere financiar decât SiP.
Sistemele de tip SoC de obicei consumă mai puțină putere și costă mai puțin decât sistemele multi-chip pe care acestea le înlocuiesc. Asamblarea de asemenea costă mai puțin, deoarece avem mai puține pachete în sistem.
Un sistem de tip SoC conține în mod uzual:
Un microcontroller, microprocesor sau un procesor digital de semnal;
Blocuri de memorie de tip ROM, RAM, EEPROM sau memorie flash;
Oscilatoare;
Periferice precum timere, generatoare de resetare a puterii;
Interfețe externe precum USB, FireWire, Ethernet, USART sau SPI;
Interfețe analog incluzând convertoare analog-digitale, ADC, și convertoate digitale-analog, DAC;
Circuite de management al puterii si mentinere tensiunii.
Aceste blocuri sunt conectate printr-o magistrală standard precum magistrala AMBA de la firma ARM Holdings. Controlerele DMA (Direct Memory Acces, care permit accesul direct la date din memoria principala de către un dispozitiv fără a mai accesa unitatea centrala de prelucrare (CPU)) direcționeaza datele direct între interfața externă și memorie, făcând abstracție de procesor și astfel crește volumul de date transferat în cadrul unui sistem de tip SoC. [16]
Un sistem de tip SoC conține componentele hardware menționate mai sus, dar și software care controlează microcontrolerul, microprocesorul sau blocurile ce se ocupa de procesarea digitală de semnal.
Sistemele SoC sunt fabricate astfel încât să consume mai puțină putere, și cu un cost redus și o siguranță ridicată să poată înlocui sistemele multi-chip.
Sistemul de tip SoC Broadcom BCM2835
Caracteristici generale
Sistemul de tip SoC Broadcom BCM2835 prezent pe Raspberry PI are următoarele caracteristici:
Unitatea centrală de procesare (CPU): Procesor ARM1176JZF-S la frecvența de 700MHz; ARM 11 implementează o arhitectură de tip ARMv6.
Unitatea grafică de procesare (GPU): Broadcom VideoCore IV oferă acces la librariile grafice pentru sisteme integrate OpenGL ES 1.1, OpenGL ES 2.0, acceleratorul-hardware pentru 2D OpenVG 1.1, Open EGL, OpenMAX și o rezolutie FULL HD, 1080p.
512MB DRAM. Memoria RAM este localizata fizic deasupra procesorul media Broadcom.
Pipeline senzorial visual (ISP) util operării cu camere de până la 20 de megapixeli pentru o procesare de 220 megapixeli pe secundă. [17]
Figura 2.1. Schema bloc a sistemului SoC BCM2835
Perifericele accesibile de către ARM ale sistemului SoC BCM2835
Sistemul SoC BCM2835 conține următoarele periferice care pot fi accesate de către ARM:
Timere;
Controler de întreruperi;
Pini de intrare/ieșire (GPIO);
USB;
PCM / I2S Audio;
Controler DMA;
I2C master;
I2C / SPI slave;
SPI0, SPI1, SPI2;
PWM;
UART0, UART1. [18]
ARM reprezintă o familie de seturi de instrucțiuni pentru procesoarele computerelor bazată pe o arhitectură cu un număr redus de instrucțiuni (RISC) dezvoltate de compania britanică ARM Holdings.
Un design bazat pe RISC presupune că procesoarele ARM folosesc un număr mult mai redus de tranzistoare decât cele CISC (Complex Instruction Set Computer) x86, care predomină în computerele personale. Această abordare nu numai că reduce costurile, dar si căldura și puterea utilizată. [19]
Acest tip de arhitectură este preferabil pentru dispozitivele care sunt alimentate cu ajutorul unor baterii, cum ar fi: laptop-uri, tablete, smartphone-uri și alte sisteme integrate.
Un computer care are la bază CISC poate executa cu ajutorul unei instrucțiuni mai multe operații de nivel jos (low-level), precum încărcarea din memorie, operații aritmetice sau stocarea în memorie.
Adresele în ARM sunt :
emise ca adrese virtuale de către nucleul ARM-ului, pe urmă
mapate în adrese fizice de către unitatea de management al memoriei (MMU) al ARM-ului, pe urma
mapate într-o magistrală de adrese, iar în final
utilizate pentru a selecta perifericul dorit sau locația în RAM. [18]
Figura 2.2. Spațiul adreselor virtuale și fizice în BCM2835 [18]
Kernel-ul standard pentru BCM2835 oferă o asociere continuă pentru adresele virtuale asupra întregii memorii RAM disponibile. Kernel-ul este configurat astfel : 1GB pentru kernel și 3GB pentru memoria utilizatorului.
Adresele virtuale în modul kernel variază între 0xC0000000 și 0xEFFFFFFF. Adresele virtuale în modul utilizator variază între 0x00000000 și 0xBFFFFFFF. Perifericele sunt mapate în modul kernel începând cu adresa 0xF2000000. Adresele fizice încep de la 0x00000000 pentru RAM, iar pentru periferice de la 0x20000000 la 0x20FFFFFF. [18]
Adresele perifericelor utilizate de BCM2835 sunt adrese de magistrală. Software-ul care accesează aceste periferice trebuie să traducă aceste adrese în adrese fizice sau virtuale. Software-uri care accesează memoria RAM direct trebuie să folosească adrese fizice.
Controller extern pentru Mass Media
Controller-ul extern pentru Mass Media (EMMC) reprezintă o interfață către card-ul SD si sistemul MultiMedia integrat oferit de Arasan. Deoarece modulul EMMC folosește aceeași pini ca și alte funcționalități, trebuie selectat în interfața GPIO. Interfața către card folosește propriul semnal de ceas clk_emmc. Frecvența acestui ceas trebuie selectată între 50MHz si 100MHz. Folosind un semnal de ceas separat este permis accesul la card imediat, chiar dacă VideoCore funcționează la o frecvență de ceas redusă. [18]
VideoCore reprezintă o arhitectură de procesor multimedia de putere mica, ce aparține firmei Broadcom. Arhitectura procesorului digital de semnal bidimensională îi ofera flexibilitatea necesară pentru a decodifica și codifica un număr mare de codec-uri multimedia, în același timp folosind o putere mică.
BCM2835 folosește ca și unitate de procesare grafică VideoCore 4 care permite vizualizare conținutului multimedia Full HD, 1080p.
VideoCore funcționează la frecvență de ceas redusă. Modulul EMMC conține propriul divizor de timp pentru a genera semnalul de ceas al cardului din clk_emmc.
Modulul EMMC asigură funcționarea procesului de comun acord “handshaking” al unei comenzi automate. [18]
Software-ul este responsabil pentru verificare biti-lor de status ai cardului pentru a verifica dacă procesarea cardului a avut loc cu succes.
PCM / I2S Audio
Modulația Puls-Cod (PCM) este o metodă folosită pentru a reprezenta digital un semnal analogic eșantionat. Reprezintă forma standard a conținutului audio digital în computere, disc-uri compacte, telefonie digitală și alte aplicații audio. În procesul PCM, amplitudinea semnalului analogic este eșantionată la intervale uniforme, fiecare eșantion fiind cuantificat la cea mai apropriată valoare dintr-un pas digital. [20]
De exemplu să presupunem că avem un semnal sinusoidal eșantionat la intervale egale și cuantificat pentru PCM. Pentru fiecare eșantion este aleasă o valoare de pe axa Y după un anume algoritm. Aceasta produce o reprezentare discretă al semnalului de intrare care pot fi codificată ca și informație digitală ce poate fi stocată sau manipulată ulterior. În urmatorul exemplu putem verifica că valorile cuantizate pentru semnalul sinusoidal eșantionat sunt : 9,10,12,14,15,15,15,14 etc. Codarea acestor valori pe 4 biti este : 1001,1010,1100,1110,1111,1111,1111,1110 etc. Aceste valori pot fi procesate pe urmă de către un procesor digital de semnal.
Figura 2.3. Eșantionarea și cuantizarea unui semnal sinusoidal pentru PCM
Pentru ca să decodificăm un semnal, un demodulator poate realiza procesul inversul al unui modulator. După fiecare perioadă de eșantionare, demodulatorul citește următoarea valoare și mută semnalul de ieșire la noua valoare. Ca un rezultat al acestor tranziții, semnalul posedă o cantitate de energie mare furnizată de frecvențele înalte. Pentru a elimina aceste frecvențe nedorite și a păstra semnalul original, demodulatorul trimite semnalul printr-un filtru analogic care să înlăture energiile generate în afara intervalului de frecvență dorit (mai mari decât frecvența Nyquist Fs/2). Teorema de eșantionare dovedește faptul că dispozitivele PCM pot opera fără să introducă distorsiuni în afara benzii de frecvență dorit, dacă frecventa de eșantionare este de două ori mai mare decât cea a semnalului de intrare.
Interfața audio PCM reprezintă o magistrală periferică avansată (APB) care oferă intrări si ieșiri de tip telefonic sau pentru a emite semnal audio serial de înaltă calitate. Suportă mai multe tipuri de modulații PCM incluzând I2S. [18]
I2S (Inter-IC Sound) reprezintă o interfață electrică pentru magistrala serială folosită pentru conectarea dispozitivelor digitale audio. Este utilizat pentru a transmite date audio PCM între circuitele integrate ale unui dispozitiv electronic. [21]
Interfața audio PCM folosește patru semnale :
PCM_CLK – semnal de ceas.
PCM_FS – semnal pentru sincronizarea cadrelor.
PCM_DIN – date de intrare seriale.
PCM_DOUT – date de ieșire seriale.
PCM reprezintă un format serial cu un singur bit de intrare, data_in, și un singur bit de ieșire data_out. Informația este întotdeauna serializată.
Semnalul PCM_FS este utilizat pentru a delimita informația serială în cadre individuale. Lungimea cadrului, dimensiunea și poziționarea cadrului de sincronizare este programabilă. Cadrele pot conține unu sau două canale audio pentru fiecare direcție. Fiecare canal poate conține între 8 si 32 biți și poate fi poziționat oriunde în interiorul cadrului atât timp cât cele două canale nu se suprapun. Formatul canalului este separat programabil pentru transmiterea și recepționarea direcțiilor.
Figura2.4 Semnalele audio folosite de interfața audio PCM [18]
Semnalul PCM_CLK poate să nu fie sincronizat cu semnalul de ceas al magistralei APB si poate fi resetat dacă este nevoie. Direcția semnalalelor PCM_CLK și PCM_FS poate fi selectată individual, permițând astfel interfeței să poată acționa ca un dispozitiv master sau slave.
Semnalul de ieșire al unui PCM se schimbă pe frontul crescător al semnalului PCM_CLK și semnalele de intrare sunt eșantionate pe frontul descrescător. Cadrul de sincronizare este considerat ca un semnal de date și este eșantionat în același fel.
În modul master (CLKM=0), semnalul de ceas PCM_CLK reprezintă o ieșire si este derivat din semnalul de ceas de intrare PCM_MCLK.
În modul slave (CLKM=1), semnalul de ceas PCM_CLK reprezintă o intrare, furnizat de o sursă externă de ceas.
În modul sync master (FSM=0) semnalul PCM_FS este generat intern și este tratat ca un semnal de ieșire ce se modifică pe frontul crescător al ceasului. Lungimea si polaritatea cadrului de sincronizare este programabilă.
În modul sync slave (FSM=1), semnalul PCM_FS este tratat ca un semnal de intrare și este eșantionat pe frontul negativ al semnalului de ceas PCM_CLK. Primul semnal de ceas al unui cadru este setat în punctul în care semnalul PCM_FS are valoarea 1, după o perioadă în care semnalul a avut valoare 0.
Interfața PCM audio se folosește de semnalul de sincronizare a cadrelor pentru a indica unde sunt poziționate canalele de date.
Interfața PCM funcționeaza în mod asincron din punct de vedere al frecvenței semnalului PCM_CLK și transmite și recepționează date în mod automat către ceasul intern al APB-ului. Regiștrii de control nu sunt sincronizați și ar trebui programați până când dispozitivul este folosit și nu ar trebui schimbați în timp ce interfața funcționează.
Doar biții EN, RXON si TXON ai registrului PCMCS sunt sincronizați din punct de vedere al semnalului de ceas între PCM si APB și pot fi schimbați in timpul funcționării interfeței. [18]
Bitul EN este folosit pentru a permite salvarea de energie. Biții TXON si RXON permit transmiterea și recepționarea informațiilor, iar interfața funcționeaza atât timp cât unul din acești doi biți este activat.
Figura 2.5. Sincronizarea la inceperea unui nou cadru
Există un singur modul PCM în BCM2835. Adresa de baza a regiștriilor PCM este 0x7E203000. Adresele în PCM sunt descrise în Anexa 1.
Capitolul 3
– Sistem de operare –
Raspbian
Raspbian reprezintă un sistem de operare bazat pe Debian, optimizat pentru Raspberry PI. Un sistem de operare reprezintă ansamblul de programe și utilități pentru a putea face posibilă funcționarea sistemului Raspberry PI. Pe lângă sistemul de operare, Raspbian are integrat peste 35000 de pachete, pre-compilate, ușor de instalat pe Raspberry PI. [22]
Raspbian folosește o versiune gratuită de desktop, LXDE (Lightweight X11 Desktop Environment). LXDE este configurat pentru platforme Unix, precum Linux sau FreeBSD. În 2010, LXDE versiunea 0.5 a fost desemnată ca cel mai eficient mediu de desktop, având cel mai mic consum de memorie și cea mai mică energie consumată, ceea ce conduce la faptul că laptop-urile ce folosesc un sistem de operare bazat pe LXDE vor avea o rată de consum a bateriei mult mai mică decât alte laptop-uri ce folosesc un alt mediu de desktop. [23]
Raspbian folosește o arhitectură de tip ARM hard-float (armhf), ce a fost configurată pentru un procesor de tip ARMv7. Este o arhitectură ce folosește virgula mobilă pentru reprezentarea numerelor sub formă de șiruri de biți și o reprezentare a numerelor pe 32biti. [24]
Pentru instalare sistemului de operare Raspbian este suficient un card SD de 2GB, dar este recomandată folosirea unui card SD de minim 4 GB.
Am folosit software-ul NOOBS (New Out of Box Software) care reprezintă un manager de instalare de sisteme de operare pentru Raspberry PI
Am downloadat și am extras conținutul arhivei zip Noobs.zip de pe site-ul http://www.raspberrypi.org/downloads/ pe un card SD de 8GB formatat în prealabil. NOOBS include următoarele sisteme de operare :
Raspbian;
OpenELEC;
RISC OS;
Arch Linux;
RaspBMC;
Pidora.
Cardul trebuie introdus în spațiul alocat acestuia pe Raspberry PI. După ce am conectat Raspberry PI la alimentare, card-ul este recunoscut iar procesul de instalare a sistemului de operare începe.
După instalare, la prima bootare a sistemului de operare Raspbian, se deschide programul de configurare raspi-config. Pentru a ne conecta la interfața grafică, desktop, trebuie sa tastăm următoarea comandă în linia de comandă : startx. Pentru a reveni la raspi-config, trebuie sa deschidem un terminal si să tastăm: sudo raspi-config. Sudo este necesar deoarece fișierele de configurare nu aparțin utilizatorului pi (utilizatorul creat standard) având nevoie de privilegii de acces super user (su). Din raspi-config putem schimba parola, să verificăm că întregul spațiu al cardului SD este alocat sistemului de operare, să permitem să booteze în modul desktop, raspi-config sau linia de comanda, să setăm limba și regiunea, precum să ne adăugăm pe o harta virtuală cu toți utilizatorii de Raspberry PI din lume. Avem acces și la setări mai avansate precum Overclock (momentan, procesorul funcționeaza la 700MHz), configurări audio, împărțirea memoriei între unitatea centrală de procesarea și unitatea grafică sau permiterea conectării prin Secure Shell (SSH) de pe un alt dispozitiv la Raspberry PI sau schimbarea numelui asociat plăcutei Raspberry PI într-o rețea (Hostname). [25]
Pentru actualizarea sistemului de operare Raspbian sunt folosite două comenzi:
sudo apt-get update – pentru a sincroniza baza de date cu versiuni noi ale software-urile disponibile și ultima versiune a sistemului de operare.
sudo apt-get upgrade – pentru a actualiza software-urile cu versiunile noi (în caz că există).
Deoarece Raspberry PI este o platformă integrată, nu are un BIOS cum se gasește pe un computer personal. Parametrii ce sunt editați și stocați în BIOS pe un computer personal, pe un Raspberry PI sunt stocați în fișierul config.txt. Acest fișier este citit de unitatea grafică de procesare GPU înainte ca unitatea de procesare central, CPU, să fie inițializat. Fișierul se găsește în /boot/config.txt și poate fi editat doar cu utilizatorul root. Toate schimbările au loc după ce sistemul de operare este restartat. [26]
Debian
Debian reprezintă sistemul de operare pe baza căruia este construit Raspbian, fiind de asemenea ca și Raspbian, un sistem gratuit. Debian este una dintre cele mai cunoscute distribuții Linux la momentul actual pentru computere personale și servere de rețea.
Debian 6.0 suportă două kernel-uri, Linux si kFreeBSD. Cea mai actuală versiune, Debian 7.5, denumită “Wheezy”, suporta peste 11 arhitecturi actuale, printre care și armhf, utilizată de Raspbian. Debian oferă acces la peste 37500 de pachete momentan, pre-compilate și ușor de instalat.
Spre deosebire de Raspbian, Debian suportă mai multe moduri desktop: KDE Plasma Workspaces, Xfce si LXDE. [27]
Debian poate fi instalat accesând pagina https://www.debian.org/distrib/netinst, în funcție de arhitectura procesorului. Am instalat sistemul de operare Debian utilizâmd o mașină virtuală pe un computer personal cu sistemul de operare Windows cu ajutorul programului Oracle VM VirtualBox https://www.virtualbox.org/wiki/Downloads pentru a putea efectua comparații cu algoritmii pentru sinteza vocala configurați pe Raspberry PI. Am alocat mașinii virtuale 8GB spațiu de stocare ca să coincidă cu spațiul de pe cardul SD folosit de Raspberry PI. Pentru memoria RAM am alocat 4GB.
Capitolul 4
– Sinteza vocală Text-to-Speech –
Introducere
Scopul unui sintetizator vocal de tip Text-to-Speech (TTS) este de a converti text primit arbitrar într-un discurs inteligibil și natural din punct de vedere auditiv astfel încât să poți transmite informații de la o mașină la o persoană. De aceea, sistemele TTS realizează mai mult decât un simplu mecanism de decupare si lipire, de exemplu utilizat în aplicațiile telefonice pentru a citi înapoi utilizatorului un număr de telefon. Astfel de mecanisme simple îmbina cuvinte rostite individual, care sunt deja înregistrate. Metoda utilizată de către sistemele TTS este de a exploata reprezentările acustice ale discursului ce urmează a fi sintetizat, împreună cu o analiză lingvistică a textului pentru a extrage pronunția corectă (conținutul; ceea ce este spus) și metrica/prozodia din context (partea melodică a propoziției; cum este spus). [28]
Sistemele de sinteză vocală sunt de obicei evaluate din trei puncte de vedere:
Acuratețea textului de interpretat (pronunță sistemului TTS de exemplu acronime, nume, adrese email, URL-uri la fel cum le-ar pronunța un om?)
Claritatea mesajului audio produs (măsurat ca procentaj al unui sunet de test care e înțeles)
Naturalețea discursului (pronunță sistemul TTS ca o înregistrare a unui om?) [29]
Aplicații ale sistemelor TTS se regăsesc în servicii de telecomunicații (interpretarea numelor și adreselor), în asistența medicală oferită unui utilizatorului prin intermediul telefonului, sisteme de banking, în jocuri și în ajutorul persoanele cu un anume handicap ( un exemplu cunoscut îl reprezintă Steven Hawking).
Schema și explicarea unui sistem TTS
Schema bloc a unui sistem TTS este prezentat in Figura 8:
Figura 4.1. Schema bloc a unui sistem pentru sinteza vocală text-to-speech (TTS)
Se pot distinge două secțiuni, denumite “front-end” și “back-end”.”Front-end” reprezintă partea de sistem mai “apropriată” de textul intodus, pe când partea de “back-end” reprezinta partea sistemului mai aproape de rezultatul final, conținutul audio.
Textul introdus, opțional conținând etichete pentru prozodie și alte caracteristici, este procesat în “front-end” și “back-end” în urmatoarea ordine:
modulul de analiză a textului format din trei componente care:
detecteaza structura documentului (pauze intre paragrafe, intre propozitii, etc.)
normalizează textul (transcriere de acronime, abrevieri, valută, date, URL-uri, etc)
analizează din punct de vedere lingvistic.
modulul de analiză fonetică format din două componente:
diferențierea omografică (distincția între cuvinte care se scriu la fel, dar diferă din punct de vedere fonetic)
conversie de tip grafeme-foneme (o conversie de tip „literă-sunet”)
modulul de analiză prozodică prin care se determină durata, amplitudinea și frecvența fonemelor.
modulul de sinteză vocală unde este controlată vocea corespunzătoare textului introdus.
Aspecte Front-End
Analiza și normalizarea textului sunt două module ale front-end-ului care răspund la întrebări precum „ce” și „cum” are loc sinteza vocală. Punctuația („.”, „?”) nu este perfectă. De exemplu sistemul de sinteza vocala TTS nu trebuie să interpreteze greșit punctul după cuvântul „cm” ca în exemplul: „Dimensiunea ecranului este de 102 cm., de aproximativ 1.75 de ori mai mare decât lățimea ecranului” să îl considere ca finalul unei propoziții. De asemenea, punctuația și alte caractere speciale pot face parte din indicarea orei (exemplu: ora 7:30), a datei (exemplu: 28/01/1991) sau exprimarea valutei (exemplu: $50 = „50 de dolari”). Normalizarea textului este dificilă deoarece depinde de context (exemplu: $50.05 = „50 punct 05 dolari”). [29]
Abrevierile și acronimile pot face parte din ambele categorii menționate mai sus. Prima categorie conține un număr finit de mapări precum „Dr.” care poate însemna și „doctor” și „drum”. Alte prescurtări sunt create pe loc de oameni care sunt mult mai greu de depistat. De asemenea, o anumită abreviere poate avea sensuri diferite în funcție de domeniul de activitate unde este folosit. De aceea este necesar să fie tratată separat normalizarea în funcție de subiect/domeniul de activitate, printr-un filtru care să traducă abrevierea respectivă în funcție de context. Există aplicații care citesc conținutul unui e-mail sau al unei pagini de Internet folosesc un astfel de filtru prin care se elimină antetul sau alte informații referitoate la formatare. Până și citirea unor numere simple poate deveni dificilă, precum cea a numărului 370, unde 370 poate face parte dintr-un număr (exemplu: 0737000000, citit ca „zero șapte trei șapte..) sau parte a unui nume (exemplu: IBM370, citit ca „I B M trei sute șaptezeci”). Performantele modulelor de analiză și normalizare a textului afectează acuratețea cu care redă un sistem TTS mesajul.
Modulul de analiză lingvistică se ocupă de determinarea părților de vorbire, sensul cuvintelor, accente, stiluri adecvate de vorbire (exemplu: salutări, scuze, etc.)
Conversia de tip grafeme-foneme implică pronuntarea cuvintelor. Asocierea dintre ortografie și foneme poate fi dificilă din cauza dependențelor de context. De obicei aceste dependențe sunt clasificate cu ajutorul arborilor de clasificare și regresie (CARTs) care compară probabilitatea fiecare conversii al unui cuvant în funcție de context sau dacă cuvântul specificat este omograf. La nivel de cuvânt, există dicționare de pronunție care sunt utilizate în combinație cu descompuneri morfologice.
În final, reguli de tip literă-sunet sunt folosite în aproape toate sistemele de sinteză vocală Text-to-Speech actuale. O restricție ale acestor reguli de tip literă-sunet o reprezintă numele de oameni de exemplu, deoarece sunt pronunțate diferit în funcție de regiunea din care provin. De asemenea, identificarea unui nume într-un text este dificilă, deoarece unele nume, substantive proprii se pot confunda cu substantive comune dacă sunt folosite la începutul proproziției. O altă problemă o reprezintă pronunția care depinde foarte mult de accentul vorbitorului.
Dacă analiza și normalizarea textului sunt module care raspund la întrebări precum „ce” și „cum”, prozodia determină „cum” propoziția este vorbită în termeni melodici, frazare,ritm, accent local și emoții. Prozodia poate dicta și sensul în propoziții, precum: „Nu spun că nu ai dreptate” sau „Nu, spun că ai dreptate”, sensuri total opuse. De aceea, prozodia afectează naturalețea și inteligibilitatea unui sistem de tip TTS. Prozodia este dificil de adnotat în mod automat. Au fost realizate așa numitele dicționare ToBI („Tone and Break Indices”) pentru a putea preciza tonul și pauzele corespunzătoare pentru un anume text. În cazul pronunției, prozodia poate depinde de genul vorbitorului, acte ale vorbirii (salutări de exemplu), de obiectivul aplicației sau chiar la nivelul individual al vorbitorului. De aceea, cercetătorii din ziua de azi se mută spre o zonă unde este folosită o bază de date unică de dimensiuni mari, ce conține informații (audio) ce provin de la un singur vorbitor. [29]
Aspecte Back-End
O decizie importantă pe care un designer de sisteme TTS trebuie să o ia o reprezintă alegerea metodei pentru sinteză. În literatură găsim două mari categorii de metode: sinteza prin reguli și sinteza prin concatenare. Sinteza prin reguli exploatează cunoștințele experților în vorbire în domeniul producerii de cuvinte și al percepției prin punerea expertului uman în bucla de design a algoritmului. Sinteza prin concatenare implică înregistrări ale unui vorbitor uman, punând inerent mai mult accent pe date. Experții în general nu s-au pus de acord asupra căreia dintre cele două metode este mai utilă cu toate că, astăzi, metoda sintezei prin concatenare produce în mod clar cuvinte sintetice de o calitate mai înaltă. Sinteza articulatorie folosește modele computaționale ale articulatorului (limba, buze, etc) și ale glotisului (cutiei vocale) pentru a sintetiza vocea. În loc să descrie semnalul de sunet, folosește parametrii de control cum ar fi poziția și mișcarea limbii, a buzei și a deschiderii cutiei vocale, parametrii care au o însemnatate ridicată în reproducerea vocii umane. Prin urmare, sinteza articulatorie este favorizată de experții în domeniu care explorează teorii de producere a cuvintelor si subiecte aferente. Matematic, sinteza articulatorie poate fi la fel de simplă precum descrierea tractului vocal ca un tub drept de o secțiune transversală variabilă, sau la fel de complicat precum rezolvarea ecuațiilor complete Navier-Stokes. Sintetizatoarele articulatorii inițiale urmăreau îndeaproape metoda sintezei prin reguli; recent, cercetătorii au adoptat câteva concepte susținute de date. Cu toate că este metoda aleasă pentru câteva utilizări specifice, sinteza articulatorie nu a produs o sinteză vocală care să poată fi confundată cu o înregistrare a unui vorbitor uman. În plus, găsirea regulilor sau a sistemului dinamic și a parametrilor de control ai acestuia este computațional o sarcină intensivă sau/și are nevoie de măsurători ale semnalului produs care pot fi considerate oarecum invazive. Cu toate acestea, un sintetizator articulatoriu de o acuratețe ridicată aduce cu el avantajul producerii unei voci de înaltă calitate modificabilă (făcut să sune ca diferite persoane sau stiluri de vorbit), care poate fi folosit pentru a antrena alte sintetizatoare, mai practice.
Sinteza cu formanți
În categoria sintezei vocale prin reguli, sinteza cu formanți este exemplul cel mai des întâlnit. Un exemplu cunoscut include teoriile lui Dennis Flatt, al cărui sistem TTS a fost mai târziu comercializat ca "MITalk" și "DECTalk", și este acum întreținut și comercializat de Fonix. În forma sa cea mai simplă, sinteza cu formanți implică filtre de ordin doi și secțiuni formate din astfel de filtre în cascadă sau serie, sau in structuri paralele. Figura 4.2 prezintă aceste două variante.
Figura 4.2. Funcția de transfer a tractului vocal(partea stângă) poate fi aproximată cu filtre de ordin doi și secțiuni formate din filtre în serie sau paralel. [29]
Începând de la funcția de transfer a tractului vocal în stânga, care leagă fluxul de aer la nivelul buzelor cu fluxul de aer de la nivelul glotisului, sarcina este să se aproximeze toate rezonanțele tractului vocal (vârfuri în funcția de transfer, formanți) cu o rețea de filtre de ordin doi, prezentate în partea din dreapta. Se poate demonstra că reprezentarea serie a filtrelor aproximează un tract vocal non-nazal suficient de bine. De exemplu, dându-se intre trei si cinci secțiuni de filtru (fiecare detectând un formant), putem aproxima funcția de transfer a tractului vocal pentru vocale, chiar și între frecvențele formanților (vârfuri). Cu această metodă, trebuie specificate frecvențele formanților și lățimile de bandă, plus un factor de câștig general. Cu toate acestea, pentru sunete nazale, cât și pentru sunete amestecate, o reprezentare în serie folosind filtre de ordin doi nu poate produce rezultate bune. Filtrele paralel creeaza flexibilitatea aproximării oricărui spectru vocal, dar au nevoie de factori de câștig individuali, și în plus, de frecvențe ale formanților și ale lățimilor de bandă. Ca un efect secundar, filtrele paralel introduc de asemenea zerouri spectrale între frecvențele formanților care pot fi anulate de filtre speciale de corecție. [29]
Sursa de semnal poate fi o serie de impulsuri vocale (aproximând impulsuri glotale) sau zgomot (aproximând zgomotul aspirativ al surselor de zgomot din tractul vocal), sau un amestec al celor două. De exemplu, pentru cuvântul "doi", fonemul /d/ poate specifica o începere bruscă a sursei de zgomot a unui flitru cu o caracteristică plată urmată de modelul /oi/ al sursei de voce al celor trei filtre formante cu frecvențe de vârf la 300, 850 și 2250 Hz, reprezentând formații F1, F2 și F3. De notat că în acest caz se observă și o regiune de tranziție între începutul sunetului și vocala care este redată cu zgomot de aspirație, dar care deja prezintă structura de formant a următoarei vocale.
Deducerea de reguli pentru sintetizarea vorbirii cursive este principala problemă a sintezei cu formanți. Aceste reguli specifică intervalele de timp ale sursei (rostite sau nerostite) și valorile dinamice ale tuturor parametrilor filtrului. Este dificil de realizat acest lucru manual, chiar și pentru cuvinte simple, și mai mult, pentru propoziții întregi cu o inteligibilitate ridicată. Deduceri automate ale acestor reguli pot fi obținute prin metoda analizei prin sinteză care încearcă să mimeze semnalul vocal de intrare. Un sistem comercial care folosește cunoștințe despre articulația sunetelor și despre acustica și fonologie și este folosit ca semnal de intrare pentru sintetizatorul Klatt este HLSyn.
Sintetizatoarele cu formanți au necesități computaționale limitate. Anumite implementări permit unui sistem TTS să funcționeze folosind mai puțin de 2MB de memorie. În consecință, aplicațiile embedded, cum sunt cele folosite în sistemele handheld, de exemlu, dicționarele “vorbitoare” sau calendare, sau chiar aplicațiile folosite în telefoane mobile pot folosi sinteza cu formanți. Calitatea vocală poate fi controlată până la un grad înalt de fidelitate, dar este imposibil să se apropie de calitatea vocii unei persoane anume. Inteligibilitatea, cu toate acestea, este mare. În cele din urma, sinteza cu formanți este foarte utilă pentru a crea stimuli vocali pentru cercetarea în percepția vocii, având în vedere nivelul înalt de control pe care astfel de experimente îl necesită. [29]
Sinteza prin concatenare
Sinteza prin concatenare folosește segmente scurte de semnal vocal înregistrat care au fost tăiate din înregistrări și stocate într-un registru (bază de date de voce), fie forme de undă (necodate), fie codate cu o metodă potrivită pentru codarea vocală. O diagrama bloc a unui sistem tipic de sinteză prin concatenare este prezentată în Figura 4.3. După cum a fost descris în detaliu în capitolul anterior, și prezentat anterior în Figura 4.1, front-end-ul din stânga convertește un semnal de intrare dat într-o secvență fonetică de simboluri și de prozodie (frecvența fundamentală, durată și amplitudine). Front-end-ul folosește un set de reguli și/sau un dicționar de pronunție. Împreună cu un șir de simboluri fonetice, produce valori specifice pentru frecvența fundamentală (pitch), durate ale fonemelor și amplitudini. Blocul din centru din Figura 4.3 asamblează unitățile după lista de priorități setată de front-end. Aceste unități sunt selectate dintr-o librarie (centru-sus în Figura 4.3) care conține un inventar al unităților de sunet disponibile.
Figura 4.3. Diagrama bloc a unui sistem text-to-speech ce folosește metoda prin concatenare. În stânga, partea Front-End, iar în dreapta, partea Back-End, plus baza de date a sunetelor ce conține inventarul sunetelor. [29]
Diferite tipuri de unități de voce pot fi stocate în libraria unui sistem TTS concatenativ. Stocarea de cuvinte întregi este nepracticată în general pentru sisteme TTS, deoarece este necesar ca o persoană să citească câteva sute de mii de cuvinte într-o anumită manieră și cu o voce consistentă. Chiar dacă ar fi înregistrate cu succes în mai multe sesiuni desfășurate pe parcursul a mai multor săptămâni, o lipsă a coarticulației și a înregistrării fonetice la granițele dintre cuvinte ar putea rezulta într-un discurs care sună nenatural. La cealaltă extremă, folosind fonuri (aproximativ 50 pentru limba engleză) este de asemenea nesatisfăcător din cauza efectului mare de coarticulație care există între fonuri adiacente. Prin urmare, tranzițiile de la o unitate la următoarea pot fi auzite ca glitch-uri care introduc discontinuități perceptibile. Intuitiv, unitățile mai lungi produc o sinteză vocală mai bună, având în vedere ca rata de concatenări este mai mică decât în cazul unităților mai scurte. Pe de alta parte, avem nevoie de un set mai mare de unități lungi care să acopere orice domeniu de aplicare, de exemplu, turism (cu mesaje generate TTS pentru "De la ce aeroport doriți să plecați?"), din cauza multitudinii foarte mari a variantelor posibile. Având în vedere aceste cerințe contradictorii, cele mai practice implementări TTS până la mijlocul anilor '90 au făcut compromisul de a folos una dintre cele două metode de librărie de unități, difonul si demisilabele.
Un difon este partea din vorbire din mijlocul unui fon până la mijlocul următorului fon (de notat că lungimea medie a unui difon este identică cu cea a unui fon). Mijlocul unui fon tinde să fie regiunea acustică cea mai stabilă a acestuia. Prin urmare, difonii reprezintă tranziții acustice de la secțiunea stabilă din mijloc a uni fon până la următoarea. O librarie minimă de aproximativ 1000 de difonuri este necesară pentru a sintetiza text în limba engleză fără restricții. Deoarece sinteza prin concatenare păstrează detaliul acustic al limbajului natural, sinteza cu difoni are în general un grad de inteligibilitate ridicat. Un dezavantaj al sintezei folosind numai difoni este acela al coarticulării: aceasta este redată numai cu fonemele imediat care preced și urmează, în timp ce anumite foneme pot afecta articulația pentru mai multe foneme. Demisilabele sunt unități alternative pentru sinteza prin concatenare. O demisilabă reprezintă o jumătate de silabă; adică, ori silaba inițială în sus până la nucleul silabei, sau porțiunea finală începând de la nucleul silabei. Numărul de demisilabe în limba engleză este aproximativ egal cu numărul de difoni. Deoarece demisilabele sunt de obicei mai lungi decât difonii, si permit captarea mai bună a unui efect de coarticulație în comparație cu difonii, ar trebui să pună mai puține probleme la sinteza prin concatenare.
De notat că o bază de date tipică (o bază de date care cuprinde toți difonii posibili într-un număr minim de propoziții) poate conține chiar și numai 30 de minute de semnal vocal a unei persoane, având în vedere că unitățile trebuie să fie modificate prin procesare de semnal pentru a se potrivi predicțiilor de front-end și pentru a netezi punctele de concatenare. În cele ce urmează, vom urmări reprezentările de semnal folosite în TTS. De notat, cu toate acestea, că sistemele de ultimă generație TTS folosesc toate bazele de date de voce conținând multe ore de înregistrări, deoarece nemodificarea unui segment de voce va produce întotdeauna semnale vocale sintetizate de calitate mai înaltă. [29]
Concluzii
Acest capitol a scos în evidența anumite aspecte ale sistemului TTS (Text-to-Speech) folosit la sinteza vocală, prezentarea făcându-se cu o oarecare înclinare spre placul inginerilor electronisti. Multe aspecte, cum ar fi, de exemplu, generarea de prozodii, procesarea de limbaj natural, și altele, au fost lăsate deoparte din motive de spațiu. Este clar că sistemele TTS au avansat în direcția oferirii unui sunet de calitate înaltă ascultătorilor, care câteodată îi poate păcăli crezând că ascultă o înregistrare. Acestea fiind spuse, este de asemenea clar că suntem departe de a livra un sistem perfect de sinteză pentru toate aplicațiile posibile. Pe termen mai scurt, cea mai bună metoda pentru sinteza vocală de calitate pare să fie reglarea sistemului TTS specific pentru o aplicație anume. Atât front-end-ul cât și back-end-ul unui sistem TTS pot fi optimizate în această direcție. De exemplu, stocând și menținând un dicționar de pronunție a numelor pentru toate medicamentele pe bază de rețetă poate fi esențial pentru folosirea TTS în aplicații în domeniul sănătății. Generând caracteristicile vocale ale unei persoane anume pentru o bază de date poate fi esențial pentru clienții care acceptă un sistem de dialog automat care utilizează cu o voce TTS. În cele din urmă, alegeri inginerești tipice, cum ar fi balansarea memoriei pentru viteză, calitate vs. complexitate, și timp de dezvoltare vs. presiuni ale pieței, sunt de asemenea foarte relevante pentru sistemele TTS.
Capitolul 5
– Algoritmi pentru sinteza vocală –
Introducere
Sinteza vocală poate fi produsă prin intermediul mai multor metode, care sunt clasificate de obicei în trei categorii :
Sinteza articulară, care încearcă să modeleze în mod direct întregul sistem vocal uman.
Sinteza cu formanți, care modelează frecvențele pol ale semnalului de vorbire sau funcția de transfer a tractului vocal bazat pe modelul sursă-filtru.
Sinteza prin concatenare, care folosește eșantioane pre-înregistrate de lungimi diferite derivate din limbajul natural. [30]
Sinteza cu formanți și sinteza prin concatenare sunt cele mai folosite metode în sistemele curente de sinteză vocală Text-to-Speech (TTS). Sinteza cu formanți a dominat o perioadă lungă mediul sistemelor TTS, dar astăzi metoda prin concatenare devine din ce în ce mai populară. Sinteza articulară este înca prea complicată pentru implementări de calitate înaltă, dar poate fi folosită ca o metodă în viitor.
Festival
Festival reprezintă un sistem pentru sinteza vocală dezvoltat de Alan W. Black la Centrul pentru Cercetarea Tehnologică de Vorbire (CSTR) la universitatea din Edinburgh. La construcția sistemului de sinteză vocală TTS, Festival, au participat și membrii ai unitersității Carnegie Mellon. Festival este distribuit sub o licența de tip similară cu cea BSD, fiind un software gratis.
Sistemul text-to-speech Festival poate funcționa atât pe o platformă Linux, cât și Microsoft Windows și Mac OS X și folosește librăria Edinburgh Speech Tools.
Festival oferă un sistem complet de tip text-to-speech cu diverse API-uri (interfețe între program și sistemul de operare), dar și un mediu pentru dezvoltare și cercetarea tehnicilor de sinteză vocală. [31]
Sistemul Festival este scris în limbajul C++, cu un interpretor de comenzi (CLI) ce folosește limbajul Scheme pentru modificări generale și îmbunătățiri ale sistemului.
Festival suportă mai multe limbi, iar instalarea standard cuprinde pachete de limbi pentru Engleză (britanică), Galeză și Spaniolă. Momentan există pachete și pentru limbi precum Spaniolă Castiliană, Cehă, Finlandeză, Italiană, Poloneză, Rusă, Indiană (dialectele Telugu și Marathi). [31]
Ultima versiune, 2.1, a fost lansată în Noiembrie, 2010 și poate fi găsită pe site-ul oficial al sistemului Festival: http://www.cstr.ed.ac.uk/projects/festival/.
eSpeak
eSpeak este un sistem ce realizează sinteza vocală folosind metoda sintezei cu formanți, oferind pachete de voce pentru o multitudine de voci. Ca și Festival, eSpeak este un software gratis ce are ca suport mai multe platforme precum Linux, Windows, Mac OS X, RISC OS, Windows Mobile. Proiecte ce folosesc eSpeak: NVDA (NonVisual Desktop Access), Ubuntu sau OLPC (One Laptop per Child) și a fost de asemenea folosit de Google Translate.
Espeak folosește sistemul pentru sinteza vocală Speak folosit în computerele cu sistem de operare Acorn RISC OS pentru engleza britanică și a fost dezvoltat de către Jonathan Duddington în 1995. [32]
O versiune rescrisă pentru Linux a apărut în Februarie 2006. Din cauza dimensiunii sale reduse (aproximativ 1.6 MB) și a paletei diversificate de limbi disponibile este folosit ca sistemul de sinteza vocală standard pentru proiecte open source precum NVDA pentru Windows, Ubuntu și alte distribuții Linux. [32]
Calitatea vocii depinde foarte mult de limba selectată, unele limbi fiind avantajate datorită răspândirii pe glob și de răspunsul utilizatorilor. Majoritatea oamenilor care au ajutat la îmbunătățirea pronunțării diverselor limbi sunt utilizatori nevăzători.
Espeak oferă două metode de sinteză: sintetizatorul original eSpeak și sintetizatorul Klatt. Ambele sintetizatoare folosesc sinteza cu formanți.
Sintetizatorul eSpeak creează sunete precum vocale sau consoane “sonore” (precum l,r,m,n,y,w) prin adăugarea de semnale sinus pentru a crea vârfuri de tip formant (ce fac parte din spectrul unui sunet). Consoane “mute” precum “s” sunt realizate prin înregistrarea de sunete. Consoane precum “z” sunt compuse dintr-un sunet sintetizat și înregistrarea unei consoane “mute”.
Sintetizatorul Klatt folosește același procedeu ca și sintetizatorul eSpeak din punct de vedere al formanților. Produce sunete începând cu un semnal de undă bogat în armonice (simulând astfel vibrația corzii vocale) și apoi aplicând filtre digitale pentru a produce sunetele dorite.
Espeak oferă posibilitatea setării unei game mult mai largă de parametrii față de Festival sau Google Text-to-Speech precum: amplitudinea (volumul), setarea vitezei cu care se va reda fișierul audio, tonalitate, dacă un anume cuvânt începe cu literă mare, formatul caracterelor, lungimea pauzei dintre cuvinte sau dacă textul conține tag-uri XML. [33]
Espeak înglobează lista limbilor acceptate de Festival, plus limbi precum: româna, bulgara, norvegiana, mandarina sau vietnameza, dar si limbi mai populare precum franceza sau germana pe care Festival nu le suportă momentan.
Site-ul oficial al sistemului eSpeak: http://espeak.sourceforge.net/
Google Text-to-Speech
Algoritmul Google Text to Speech diferă de cei doi algoritmi prezentați mai sus, prin faptul că nu necesită nimic instalat, ci doar o conexiune permanentă la Internet. Acest algoritm, preia textul introdus de utilizator, îl transmite unui server google, care îl va procesa și converti într-un fișier audio, care va fi transmis înapoi sistemului și redat cu ajutorul player-ului audio disponibil.
sudo bash ./speak.sh “Salut.”,
unde speak.sh reprezintă un fișier ce face legătură între serverul google și player-ul audio și primește ca parametru($*) textul introdus :
speak.sh
#!/bin/bash
say() { local IFS=+;/usr/bin/mplayer -ao alsa -really-quiet -noconsolecontrols
“http://translate.google.com/translate_tts?tl=en&q=$*”; }
say $* [39]
Una din problemele utilizării metodei Google Text-to-Speech o reprezinta limitarea la 100 de caractere impuse de Google în modul standard. Astfel orice text ce depășește 100 de caractere și este transmis server-ului Google pentru a realiza transformarea din text în sunet, nu poate fi redat. Există însa un procedeu prin care se poate depăși această limitare, divizând textul in fragmente de 100 de caractere și trimiterea acestora în mod automat către serverul Google. Acest lucru este posibil dacă înlocuim scriptul speak.sh prezentat mai sus cu script-ul speak.sh din Anexa 2 care împarte textul introdus în cuvinte de lungime 100 și le transmite serverului Google pe rând pentru a le procesa.
Concluzii
eSpeak oferă avantajul utilizării metodei de sinteză cu formanți, având o paletă largă de limbi și o dimensiune redusă (1.6MB). Este foarte precis în exprimare și mai usor de înțeles decât Festival. Ca dezavantaje, aș putea spune că eSpeak are niște dependențe anormale legate de X11, uneori blocându-se când vreau să execut metoda eSpeak de pe mașina virtuală pe mașina fizică (computer-ul propriu) prin intermediul la X11 (X Window System) folosind aplicația Xming. Un alt dezavantaj este reprezentat de dimensiunea mare a librăriei în comparație cu Festival.
Festival reprezintă un sistem pentru sinteza vocală ce suportă, ca și eSpeak mai multe limbi. Avantajul care îl are față de eSpeak îl reprezintă proiectul Festvox, prin care îți poți crea singur noi voci sintetice perfecționate pentru o anume aplicație, mult mai bine documentată. Pe site-ul proiectului Festvox, poți găsi un tutorial despre cum să-ți creezi singur o voce sintetică, fiind detaliat atât procesul de creare, cât și de implementare și integrare noilor voci ce urmează a fi folosite. [35]
Ca dezavantaj al sistemului Festival îl reprezintă faptul că este scris în C++ și are un cod destul de greu de înțeles și de mutat pe alte sisteme în care trebuie ajustat ca să funcționeze convenabil.
Google Text-to-Speech oferă o calitate superioară celorlalte două sisteme, eSpeak si Festival, din punct de vedere al clarității sunetului și preciziei cu care redă, dar necesită o comunicație permanentă la Internet pentru a comunica cu server-ul Google, care procesează textul introdus, transformându-l din text în sunet.
Capitolul 6
– Proiectarea sistemului și realizarea practică –
Descrierea proiectului
Proiectul constă în realizarea unui sistem pentru sinteza vocală de tip text-to-speech (TTS) cu ajutorul sistemului de dezvoltare Raspberry PI și al unei mașini virtuale cu sistemul de operare Debian, având aceleași specificații tehnice (aceeași memorie RAM și același spațiu de stocare ca și Raspberry PI) pentru a efectua comparații între cele două sisteme. A fost ales sistemul de operare Debian pentru mașina virtuală, deoarece Raspberry PI folosește sistemul de operare Raspbian care reprezintă o variantă a sistemului de operare Debian ajustată pentru Raspberry PI. Cei trei algoritmi de realizare ai sintezei vocale pe care i-am folosit sunt: Festival, eSpeak și Google Text-to-Speech. Am realizat o interfață unică de control a celor trei algoritmi în Java. Cu ajutorul interfeței pot reda prin unul din cei trei algoritmi un text introdus de la tastatură, dar și un fisier text de pe sistemul unde rulează aplicația Java sau de la o adresă de Internet.
Am instalat pe computer-ul personal o mașina virtuală prin intermediul programului Oracle VM VirtualBox. Imaginea ISO (fișier arhivat al unui disc optic) pe baza căreia funcționează mașina virtuală este de tip Linux și simulează existența sistemului de operare Debian pe 64 de biți.
Mașina virtuală am configurat-o în modul următor:
8GB spațiu de stocare,
512MB de RAM, dintre care 12MB alocați memoriei video,
simulând astfel condițiile impuse pe Raspberry PI, model B.
Figura 6.1. Specificațiile mașinei virtuale instalate pe computer
Imaginea ISO este distribuită gratuit si poate fi descărcată după site-ul: https://www.debian.org/distrib/netinst în funcție de arhitectura procesorului.
Componente folosite
Componentele folosite pentru realizarea sistemului cu sinteza vocală sunt:
Raspberry PI, 512 MB RAM
Cutie Raspberry PI
Alimentator Raspberry PI
Conector WIFI pentru Raspberry PI
HUB USB
Tastatură, Mouse
Card SD 8GB
Casti, Boxe
Cablu HDMI / Cablu HDMI – DVI, Monitor TV
Figura 6.2. Componente folosite
Figura 6.3. Raspberry PI Model B
Instalarea sistemului de operare și conectarea remote la Raspberry PI
Sistemul de operare Raspbian se găsește în cadrul unei arhive denumită “NOOBS” si se poate descărca de la adresa: http://www.raspberrypi.org/downloads/. Am dezarhivat arhiva pe un card SD, formatat în prealabil, și am introdus cardul în slot-ul alocat acestuia pe Raspberry PI, ca în figura 6.4.
Figura 6.4. Slot-ul alocat cardului SD pe Raspberry PI
După ce am conectat Raspberry PI la sursa de alimentare, cardul a fost recunoscut și am realizat instalarea sistemului Raspbian.
Pentru apelarea interefeței grafice a sistemului de operare Raspbian, am folosit următoarea comandă din terminal: startx.
M-am conectat prin SSH (Secure Shell) de pe calculatorul personal la Raspberry, astfel având acces la shell-ul distribuției Linux, cu alte cuvinte voi putea executa comenzi direct de pe un alt calculator pe Raspberry PI.
Pentru a mă putea conecta prin SSH, trebuie să descarc aplicația PuTTY, de la adresa http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html.
Am identificat IP-ul generat pentru Raspberry PI de către router. Am folosit aplicația Angry IP Scanner, ce se poate descărca la adresa http://angryip.org/.
Cu IP-ul obtinut mă pot conecta la Raspberry PI prin SSH. Am bifat opțiunea Enable X11 forwarding din meniul Connection/SSH/X11, astfel de fiecare dată când mă voi conecta prin SSH, se va configura automat sesiunea X11 și va trimite înapoi la Windows partea grafică criptată prin SSH.
Figura 6.5. Configurare PuTTY
M-am conectat la Raspberry PI cu datele de conectare aferente acesteia :
Figura 6.6. Autentificare la Raspberry PI prin intermediul user-ului și parolei
Astfel pot executa comenzi Unix din fața calculatorului, comenzile fiind executate pe Raspberry PI, facilitând modul remote (de lucru la distanța prin intemediul unui calculator conectat la Internet), fără să fie nevoie să stau lângă Raspberry PI, atât timp cât este conectat la sursa de alimentare și la Internet.
Pentru a putea vizualiza interfața grafică a sistemului de operare Raspbian, trebuie să instalez un Virtual Networking Computing (VNC) ce îmi oferă acces la desktop-ul asociat sistemului de operare.
Pentru ca să pot instala VNC, trebuie să fiu conectat mai întâi prin SSH ca să pot executa anumite comenzi din terminal. Mai întâi voi instala TightVNC cu următoarea comandă :
sudo apt-get install tightvncserver
și îl voi executa cu comanda :
tightvncserver
Va trebui să setez o parolă ce o voi folosi la pasul următor.
După ce TightVNC a fost instalat pe Rasperry PI, trebuie descărcată și pe computer aplicația TightVNC Java Viewer pentru a putea realiza legătura dintre cele doua sisteme . Aplicația poate fi găsită la adresa: http://www.tightvnc.com/download.php.
Parola de la pasul anterior este folosită pentru autentificare ca în figura 6.8:
Figura 6.7. Conectarea prin TightVNC Figura 6.8. Autentificarea prin TightVNC
Acum am acces de la calculatorul personal și la interfața grafică (desktop) a sistemului de operare instalat pe Raspberry PI (prin VNC), nu doar la linia de comanda (prin SSH).
Figura 6.9. Sistemul de operare Raspbian accesat prin VNC de pe computer-ul personal
Implementarea sistemelor pentru sinteza vocală
Pentru realizarea sintezei vocale am folosit următoarele trei sisteme:
Festival
eSpeak
Google Text to Speech.
În primul rând trebuie să mă asigur că toate actualizarile sunt realizate și sistemul de operare este la zi. Acest lucru se poate realiza cu următoarele două comenzi:
sudo apt-get update – pentru a sincroniza baza de date cu versiuni noi ale software-urilor disponibile,
sudo apt-get upgrade – pentru a actualiza software-urile cu versiuni noi (în caz că exista).
Trebuie instalat Alsa-sound cu ajutorul comenzii următoare:
sudo apt-get install alsa-utils
ALSA (Advance Linux Sound Architecture) oferă funcționalități audio și MIDI sistemului de operare Linux (Raspbian în cazul meu). [36]
Voi edita fișierul /etc/modules ca să mă asigur că microcontrolerul BCM2835 este activ (nu este comentat, având # în față). Acest lucru se poate realiza cu următoarea comandă:
sudo nano /etc/modules
Figura 6.10. Conținutul fișierului /etc/modules
Am instalat player-ul audio mplayer pentru a putea reda rezultatul sintezei vocale:
sudo apt-get install mplayer
Trebuie editat fișierul mplayer.conf și adăugat la final următoarea linie: nolirc=yes, deoarece majoritatea sistemelor de distribuție Linux au integrat în mplayer opțiunea LIRC (Linux Infrared Remote Control). În cazul sistemului de operare Raspbian nu este cazul, așa că trebuie dezactivată această opțiune. [37]
Pentru a edita fișierul mplayer.conf este necesară următoarea comandă:
sudo nano /etc/mplayer/mplayer.conf
Sinteza vocală cu ajutorul sistemului Festival
Pentru a putea realiza sinteza vocală prin intermediul sistemului Festival, acesta trebuie instalat prin următoarea comandă:
sudo apt-get install festival
Cu ajutorul următoarei comenzi pot face ca sistemul Raspberry PI să poată reda prin intermediul sistemului Festival un text introdus, precum cel din exemplul de mai jos:
echo “Hello, I’m Octavian and this is my project.” | festival –tts
Caracterul “|” este folosit pentru a trimite textul “Hello, I’m Octavian and this is my project” sistemului Festival. Practic trimite ieșirea primei operații, intrării celei de a doua operație.
Figura 6.11. Exemplificarea utilizării sistemului Festival
În loc de un simplu text, acesta poate reda audio și IP-ul sau data curentă:
hostname -I | festival –tts
date ‘+%A, %B %e, %Y’ | festival –tts [38]
Sau chiar un fișier text:
festival –tts text.txt
Sinteza vocală cu ajutorul sistemului eSpeak
Pentru a putea realiza sinteza vocală prin intermediul sistemului Festival, acesta trebuie instalat prin următoarea comandă:
sudo apt-get install espeak
Cu ajutorul comenzii: espeak –stdout | aplay, cuvintele ce succed această comandă vor fi redate audio cu ajutorul sistemului eSpeak ca în figură 6.12:
Figura 6.12. Exemplificarea utilizarii sistemului eSpeak
Sistemul eSpeak folosește peste 50 de voci pentru redarea conținutului audio. Lista de voci poate fi accesată cu comanda următoare:
espeak –voices
Figura 6.13. Lista de voci disponibile
De exemplu, pentru redarea unui text în limba română (ro), se va folosi opțiunea –v urmat de identificatorul “ro” pentru română:
espeak -v ro –stdout ‘Salut, sunt Octavian și acesta este proiectul meu.’ | aplay
Figura 6.14. Exemplificare utilizării sistemului eSpeak în română
Se pot modifica de asemenea numărul de cuvinte rostite într-un minut, dar și tonalitatea vocii cu care sistemul poate să redea. Valoarea standard pentru numărul de cuvinte rostite într-un minut este de 160.
espeak -v ro -s 100 -stdout ‘Salut, sunt Octavian și acesta este proiectul meu.’ | aplay
(va fi redat la o viteză de 100 de cuvinte pe minut).
espeak -v ro+f3 -s 160 -stdout ‘Salut, sunt Octavian și acesta este proiectul meu.’ | aplay
(va fi redat într-o tonalitate diferită față de cea standard, ro+f3, care indică și utilizarea unei voci feminine în cazul de față).
Sinteza vocală cu ajutorul sistemului Google Text to Speech
Textul introdus de la tastatură nu mai este manipulate (convertit) de o aplicație instalată pe Raspbian, ca în cazul sistemului Festival sau eSpeak, ci este transmis printr-o comandă unui server Google care procesează textul introdus și răspunde cu un fișier audio, care va fi redat cu ajutorul player-ului audio mplayer instalat anterior. Dezavantajul consta în faptul că trebuie să ai o conexiune permanentă la internet.
Ca să poata fi folosit sistemului Google Text to Speech, se creează un fisier speak.sh:
sudo nano speak.sh
și se editează cu urmatorul cod :
#!/bin/bash
say() { local IFS=+;/usr/bin/mplayer -ao alsa -really-quiet -noconsolecontrols “http://translate.google.com/translate_tts?tl=en&q=$*”; }
say $* [39]
Pentru schimbarea limbii de redare se va înlocui tl=en de exemplu cu tl=ro (pentru romana). Pentru a folosi codare UTF-8 (ca să permit redarea și diacriticelor), trebuie adăugat în script-ul speak.sh, după translate_tts?, ie=UTF-8&. De exemplu link-ul din script va deveni: http://translate.google.com/translate_tts?ie=UTF-8&tl=en&q=$*
Se acordă permisiunea de execuție a fișierului speak.sh prin comanda:
sudo chmod u+x speak.sh
Se poate executa prin comanda: sudo bash ./speak.sh “Text de redat” ca în figura 6.15:
Figura 6.15. Exemplificarea utilizării sistemului Google Text to Speech
Analiza codului sursă utilizat pentru interfața Java
Interfața grafică am realizat-o cu ajutorul limbajul de programare Java. Pentru a scrie codul Java am folosit programul NetBeans IDE 8.0 versiunea EE (Enterprise Edition) descărcat de la adresa: https://netbeans.org/downloads/ .
Am folosit programul WinSCP pentru a transfera fișiere între mașina fizică (computer-ul personal) și mașina virtuală (pe care este instalat sistemul de operare Debian) și Raspberry PI (pe care este instalat sistemul de operare Raspbian).
Neavând posibilitatea să instalez momentan (deoarece nu există) o versiune de Netbeans 8.0 pe Raspberry PI, cât și pe mașina virtuală, am dezvoltat proiectul, aplicația, pe computer-ul personal, l-am compilat și transformat într-un fișier .JAR (Java Archive), ce reprezintă un pachet care conține mai multe clase Java, metadata (“date despre informație”), resursele associate (text, imagini) și librăriile folosite de proiect într-un singur fișier ce poate fi distribuit, în cazul meu, cu ajutorul programului WinSCP pe cele două medii (mașina virtuală și Raspberry PI) unde le-am putut executa din terminal cu următoarea comandă:
java -jar numeproiect.jar
în cadrul folder-ului “dist” asociat proiectului (/numeproiect/dist).
Versiunea pre-instalată atât pe sistemul Debian, cât și Raspbian de compiler Java este 1.6. Programele scrise pe o mașina ce urmează a fi executate de un compiler pe o alta mașină, trebuie sa aibă versiunea compiler-ului minim egală cu versiunea compiler-ului de pe mașina de pe care sunt mutate, așadar trebuie să instalez versiunea 1.8 (versiunea 8) pe Debian și Raspbian. Pașii sunt la fel, așa că mă voi referi doar la mașina virtuală pentru simplificarea detaliilor. Am urmat instrucțiunile din Anexa 3 pentru instalarea versiunii 1.8 a compiler-ului Java.
În final ne asigurăm că versiunea instalată este cea corespunzătoare executând următoarea comandă:
java -version
Pentru afișarea încărcării unității de procesare (CPU), precum și afișării memoriei disponibile, PID (id-ul procesului), timpulului de execuție, utilizatorului ce a executat comanda, timpului sistemului și procentajului de încărcare a CPU-ului din cauza aplicației scrise în Java am folosit SIGAR (System Information Gatherer And Reporter), ce reprezintă o librărie gratuită, cross-platform (ce poate fi folosită pe mai multe platform/sisteme de operare).
Pentru folosirea librăriei SIGAR, trebuie adăugate fișierele binare asociate limbajului Java în proiectul în care vreau să le folosesc:
Figura 6.16. Fișierele binare pentru folosirea librăriei SIGAR în Java
Am observat că deși funcționa pe sistemul de operare Windows când testam clasa Java ce folosea fișierele binare ale librăriei SIGAR, când mutam proiectul pe mașina virtuală nu funcționa, așa că am adăugat și fișierul libsigar-x86-linux.so în folderul librăriilor asociat proiectului de pe mașina virtuală: numeproiect/dist/lib.
Ultima librărie pe care am folosit-o în acest proiect este Absolute Layout, ce reprezintă un mod de asezare al ferestrei în care poți specifica locația exactă (coordonatele x/y) ale “copilului” într-o relație de tip părinte-copil. Modul oferit de “Absolute Layout” este mai puțin flexibil și mai greu de menținut decât celălalte moduri de afișare care nu oferă o poziționare absolută, ci relativă. [40]
Figura 6.17. Librăriile folosite în proiectul Java
Am realizat în Java patru clase, una pentru animația din începutul programului și afișare unei ferestre din care să poți selecta una din cele trei metode, iar celălalte trei clase fiind asociate fiecărui sistem de sinteză vocala în parte.
Am personalizat modul de afișare a ferestrelor, înlocuind bara de titlu, inclusiv butoanele de minimizare și inchidere a ferestrei, și fundalul programului.
Figura 6.18. Fereastra înainte de modificare (stânga); fereastra după modificare (dreapta)
Cele două butoane pentru minimizarea și închiderea ferestrei le-am desenat, după care am adăugat două componente de tip jLabel (etichetă) de dimensiunea fiecărui buton cu următoarele funcționalități:
private void jLabelXMouseClicked(java.awt.event.MouseEvent evt) {
System.exit(0);
}
private void jLabelMMouseClicked(java.awt.event.MouseEvent evt) {
this.setState(StartGUI.ICONIFIED);
}
jLabelX este eticheta asociată butonului de închidere a ferestrei în care la declanșarea evenimentului de click al Mouse-ului în zona corespunzătoare butonului, va închide execuția programului cu ajutorului comenzii System.exit(0) – 0 fiind folosit în cazul execuției fără erori.
JlabelM este eticheta asociată butonului de minimizare a ferestrei în care la declanșarea evenimentului de click al mouse-ului în zona corespunzătoare butonului, fereastra curentă (StartGUI) se va minimiza la nivelul unei iconițe în bara de start.
Fiecare din cele trei butoane din fereastra principală (StartGUI) are patru stări descrise în funcție de poziția mouse-ului față de buton:
mouse deasupra butonului – metoda jLabelButonMouseEntered()
mouse în afara butonului – metoda jLabelButonMouseExited()
mouse apăsat pe buton – metoda jLabelButonMousePressed()
mouse eliberat (inițial apăsat) pe buton – metoda jLabelButonMouseReleased().
Fiecare buton are trei imagini asociate, ce se schimbă în funcție de acțiunea mouse-ului asupra butonului. Procesul este similar pentru cele 3 stări (deasupra butonului, în afara butonului și apăsarea butonului). Se creeaza o variabilă locala de tip ImageIcon ce preia o imagine adăugată proiectului și se seteaza unei componente jLabel de dimensiunea butonului, astfel animând butonul. După ce mouse-ul a fost apăsat și eliberat, jLabel-ul își schimbă imaginea din cea a unui buton apăsat, în cea a unui buton deasupra căruia este ținut mouse-ul, iar o nouă fereastră (new Frame().setVisible(true)), FestivalFrame, apare pe ecran, fereastra curenta/inițială (StartGUI) fiind ascunsă (this.setVisible(false)).
Figura 6.19. Stările asociate fiecărui buton în Java
private void jLabelFestivalMouseEntered(java.awt.event.MouseEvent evt) {
ImageIcon FESTIVAL = new ImageIcon(getClass().getResource("/AppPackage/BUTON_FESTIVAL_HOVER.png"));
jLabelFestival.setIcon(FESTIVAL);
}
private void jLabelFestivalMouseExited(java.awt.event.MouseEvent evt) {
ImageIcon FESTIVAL = new ImageIcon(getClass().getResource("/AppPackage/BUTON_FESTIVAL_DEFAULT.png"));
jLabelFestival.setIcon(FESTIVAL);
}
private void jLabelFestivalMousePressed(java.awt.event.MouseEvent evt) {
ImageIcon FESTIVAL = new ImageIcon(getClass().getResource("/AppPackage/BUTON_FESTIVAL_PRESSED.png"));
jLabelFestival.setIcon(FESTIVAL);
}
private void jLabelFestivalMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon FESTIVAL = new ImageIcon(getClass().getResource("/AppPackage/BUTON_FESTIVAL_HOVER.png"));
jLabelFestival.setIcon(FESTIVAL);
new FestivalFrame().setVisible(true);
this.setVisible(false);
}
Când este lansată aplicația (java –jar aplicație.jar) se încarcă mai întâi o animație care durează 3.6 secunde, după care se deschide prima clasă Java, fereastra StartGUI. Animația este realizată din mai multe imagini ce sunt înlănțuite la intervale mici de timp, de ordinul 100ns, dând senzația unei animații. Animația este adăugată atât în proiect cât și în fișierul manifest.mf al proiectul pentru a putea fi redată la începutul aplicației.
Figura 6.20. Imaginile ce compun animația de la începutul aplicației Java
Conținutul fișierului manifest.mf:
Manifest-Version: 1.0
SplashScreen-Image: AppPackage/Splashx.gif
X-COMMENT: Main-Class will be added automatically by build,
unde Splashx.gif reprezintă animația și se găsește în pachetul AppPackage. Din proprietățile proiectului, categoria Run, la VM Options este adăugată următoarea comandă:
-splash:src/AppPackage/Splashx.gif.
Procesul este oprit timp de 3600ms (durata animației) pentru a putea reda animația :
java.awt.EventQueue.invokeLater(() -> {
try {
Thread.sleep(3600);
}
catch(InterruptedException e) {}
După această perioadă în care animația este executată, o variabilă de tip String este inițializată cu următoarea valoare:
String command = "sudo bash ./speechEN.sh Festival, e Speak and Google Text to Speech voice synthesis";
Acest string este folosit ulterior pentru apelul metodei Google Text to Speech, prin apelarea unui script speechEN.sh cu parametru “Festival, e Speak and Google Text to Speech voice synthesis.”
Pentru ca acest string să poata fi executat ca și când l-am scrie din linia de comandă, trebuie să formăm un vector de string-uri pe care să îl dăm ca parametru unui proces în momentul execuției în felul următor:
String[] cmd = {"/bin/sh", "-c", command}; // vector-ul de string-uri, unde command este //string-ul definit mai sus ce conține apelul metodei Google Text to Speech.
try {
Process pGoogleTTS = Runtime.getRuntime().exec(cmd);
} catch (IOException ex) {
Logger.getLogger(StartGUI.class.getName()).log(Level.SEVERE, null, ex);
}
Clasa Runtime este unică la nivelul aplicației Java pe care o dezvoltăm și ne permite comunicarea directă între aplicația Java și sistemul în care funcționeaza aplicația. Pentru a obține instanța curentă a clasei Runtime trebuie folosită metoda getRuntime(). Pentru execuția unei comenzi specificate prin intermediul unei variabile de tip String se folosește metoda exec() a metodei getRuntime() a clasei Runtime. Tipul returnat de această metodă este de tip Process. Blocul try-catch este util pentru a înregistra erorile de tip intrare/ieșire obținute în cazul unei funcționări necorespunzătoare (nu este primită o comanda sau o comanda incorectă este dată metodei exec() pentru a fi executată).
Metoda principală, main, a clasei StartGUI mai conține un apel al metodei setVisible pentru a face fereastra aferentă clasei StartGUI să devină vizibilă și statisticile obținute cu ajutorul librăriei SIGAR la un anume interval specificat prin parametrul SLEEP_TIME, codul fiind atașat in Anexa 4.
În metoda jLabelFestivalMouseReleased a fost adăugat un apel al metodei Google TTS:
private void jLabelFestivalMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon FESTIVAL = new ImageIcon(getClass().getResource("/AppPackage/BUTON_FESTIVAL_HOVER.png"));
jLabelFestival.setIcon(FESTIVAL);
String command = "sudo bash ./speechEN.sh Festival";
String[] cmd = {"/bin/sh", "-c",
command};
try {
Process pGoogleTTS = Runtime.getRuntime().exec(cmd);
} catch (IOException ex) {
Logger.getLogger(StartGUI.class.getName()).log(Level.SEVERE, null, ex);
}
new FestivalFrame().setVisible(true);
this.setVisible(false);
}
prin care la eliberarea mouse-ului de pe butonul Festival se execută comanda vocală cu ajutorul metodei Google Text-to-Speech a textului “Festival” înaintea deschiderii ferestrei FestivalFrame.
În continuare, voi prezenta clasa Java asociată metodei eSpeak folosită pentru sinteza vocală. Interfața grafică este prezentată în Anexa 5.
Toate cele trei butoane (“ÎNAPOI”, “ÎNCARCĂ TEXT”, “ÎNCARCĂ SITE”, “REDĂ”) au fost realizate în aceeași manieră ca butonul anterior “FESTIVAL“ având patru stări și trei imagini asociate fiecare.
Am adăugat o arie de text unde utilizatorul poate introduce un text de la tastatură, poate încărca un fișier text din memoria calculatorului sau de la o adresă de Internet, ce poate fi redat ulterior prin apăsarea butonului “REDĂ”. La eliberarea butonului “ÎNAPOI”, fereastra curentă (eSpeakFrame) este ascunsă, iar fereastra principală (StartGUI) este readusă în prim plan.
Când se apasă butonul de “ÎNCARCĂ TEXT”, aria de text este golită (în cazul în care există text anterior introdus). La eliberarea butonului “ÎNCARCĂ TEXT” următorul cod este executat:
ImageIcon UPLOAD = new ImageIcon(getClass().getResource(
"/AppPackage/BUTON_INCARCATEXT_DEFAULT.png"));
jLabelUpload.setIcon(UPLOAD);
JFileChooser FileChooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("Text Files", "txt");
FileChooser.setFileFilter(filter);
int returnVal = FileChooser.showOpenDialog(jLabelUpload);
if(returnVal == JFileChooser.APPROVE_OPTION) {
FileReader reader = null;
try {
File file = FileChooser.getSelectedFile();
reader = new FileReader( file );
try (BufferedReader br = new BufferedReader(reader)) {
jTextArea1.read( br, null );
}
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
reader.close();
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
Am folosit un JfileChooser care oferă un mecanism simplu utilizatorului de a alege un fișier.
Figura 6.21. JFileChooser cu opțiunea de alegere doar a unui fișier text
După care am filtrat, astfel încât doar fișiere cu extensia .txt să fie acceptate. La apăsarea butonului “Open” un fișier de tip File salvează fișierului ales, care îl transmite unui FileReader, care citește fișiere compuse din caractere, după care este transmis unui BufferedReader, făcând legătura între FileReader și zona de text jTextArea1 (aria de text unde este afișat mesajul ce urmează să fie redat).
În partea dreaptă există un ComboBox pentru alegerea limbii de redare. El funcționeaza foarte simplu, după cum urmează:
private void jComboBoxLanguageActionPerformed(java.awt.event.ActionEvent evt)
{
ImageIcon RO = new ImageIcon(getClass().getResource("/AppPackage/flag_RO.jpg"));
ImageIcon EN = new ImageIcon(getClass().getResource("/AppPackage/flag_en.jpg"));
if(jComboBoxLanguage.getSelectedIndex()==0) jLabelFlag.setIcon(RO);
if(jComboBoxLanguage.getSelectedIndex()==1) jLabelFlag.setIcon(EN);
}
Nu am adăugat toate opțiunile pentru selecția limbii, restul fiind similar. Dacă o anumită valoare din listă este selectată va influența imaginea steagului asociată.
În ferestră mai există și un cursor pentru setarea vitezei, care atunci când eliberăm mouse-ul valoarea cursorului este preluată și afișată lângă cursor.
private void jSliderSpeedMouseReleased(java.awt.event.MouseEvent evt)
{
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
jLabelSpeedValoare.setText(speedString);
}
La apăsarea butonului “REDA” următoarele lucruri sunt efectuate:
Se schimbă imaginea butonului:
ImageIcon REDA = new ImageIcon(getClass().getResource("/AppPackage/BUTON_REDA_DEFAULT.png")); jLabelReda.setIcon(REDA);
Textul din căsuța inferioara pentru afișarea parametrilor în urma execuției metodei este golită:
jTextAreaStats.setText("");
Este folosită o variabilă de tip Mem pentru a salva informații despre memoria sistemului:
Mem mem = null;
try {
mem = sigar.getMem();
} catch (SigarException se) {
}
Nivelul volumului este afișat și este setat în funcție de sistem de operare(Debian sau Raspbian) pe care funcționează:
int volum = jSliderVolum.getValue();
StringBuilder volumSB = new StringBuilder();
volumSB.append(volum);
String volumString = volumSB.toString();
jTextAreaStats.append("Nivelul volumului: "+volumString+"%\n");
String volumcommand = "amixer sset Master " + volumString + "%";
try {
Process pVolum = Runtime.getRuntime().exec(volumcommand);
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
Volumul este preluat cu ajutorul unui cursor de tip jSliDer și este atașat zonei de text jTextAreaStats din partea inferioară a ferestrei. Valoarea volumului este setată cu ajutorul comenzii “amixer sset Master X%”, unde X aparține intervalului [0,100].
Opțiuni precum valoarea vitezei, limba, tonalitatea sau vocea sunt valori preluate cu funcții precum getSelectedIndex(), getValue() sau getSelectedObjects() descrise în detaliu mai jos:
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
String limba="";
String limbaSB="";
if(jComboBoxLanguage.getSelectedIndex()==0) {
limba="ro";
limbaSB="romana";
}
Analog pentru restul limbilor disponibile pentru sinteza vocală.
String tonalitate="";
String tonalitateSB="";
if(jComboBoxTonalitate.getSelectedIndex()==0) {
tonalitate="10";
tonalitateSB="Joasa";
}
if(jComboBoxTonalitate.getSelectedIndex()==1) {
tonalitate="50";
tonalitateSB="Moderata";
}
if(jComboBoxTonalitate.getSelectedIndex()==2) {
tonalitate="90";
tonalitateSB="Ridicata";
}
String gen="";
String genSB="";
if(jRadioButtonMasculin.getSelectedObjects()!=null) {
gen="";
genSB="Masculin";
}
if(jRadioButtonFeminin.getSelectedObjects()!=null) {
gen="+f3";
genSB="Feminin";
}
Textul introdus fie de la tastatură, fie în urma apăsării butonului “ÎNCARCĂ TEXT” este divizat după separatorul final al unui rând și introdus într-un ArrayList denumit arrList.
String s[] = jTextArea1.getText().split("\\r?\\n");
ArrayList<String>arrList = new ArrayList<>(Arrays.asList(s)) ;
O nouă comandă este creată de tipul StringBuilder (am folosit StringBuilder. deoarece trebuie să concatenez mai multe String-uri). Am concatenat “espeak -v limbagen -s speedString -p tonalitate –stdout arrList.get(0) … arrList.get(n) | aplay”, unde valoarea n din arrList.get(n) reprezintă valoarea maximă a rândurilor din textul introdus. La execuție, prin setarea valorilor pentru variabilele limbă, gen, speedString, tonalitate și text va rezulta de exemplu în:
“espeak -v ro+f3 -s 170 -p 50 –stdout Hello | aplay”
StringBuilder command = new StringBuilder();
command.append("espeak -v ").append(limba).append(gen).append(" -s "). append(speedString).append(" -p ").append(tonalitate).append(" –stdout \"");
int i=0;
for(String x: arrList) {
command.append(arrList.get(i)).append(" ");
i++;
}
command.append("\" | aplay");
String totalcommand = command.toString();
String[] cmd = {
"/bin/sh",
"-c",
totalcommand
};
Sunt inițializate 6 variabile de tip long (pentru precizie) ce conțin memoria disponibilă și timpul înainte și după execuția comenzii plus alte două variabile pentru efectuare diferenței (calculul memoriei și timpului alocat operațiunii):
long startMem = mem.getActualUsed();
long startTime = System.nanoTime();
try {
Process peSpeak = Runtime.getRuntime().exec(cmd);
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
long endTime = System.nanoTime();
long endMem = mem.getActualUsed();
long totalTime = endTime – startTime;
long totalMem = endMem – startMem;
informații referitoare la execuție sunt afișate în partea inferioară a ferestrei, în caseta text JtextAreaStats:
jTextAreaStats.append("Timpul de executie al comenzii: "+totalTime+"ns \n");
jTextAreaStats.append("Memoria consumata de comanda: "+ totalMem + "B\n");
jTextAreaStats.append("Memorie sistemului disponibila: "+ mem.getActualFree() /1024 /1024 + " MB\n");
jTextAreaStats.append("Limba: "+limbaSB+" | Viteza: "+speedString+"\nTonalitatea: "+tonalitateSB+" | Gen: "+genSB+"\n");
if(jTextArea1.getText().length()>50)
jTextAreaStats.append("Textul redat: "+jTextArea1.getText().substring(0,50)+"…");
else jTextAreaStats.append("Textul redat: "+jTextArea1.getText()) ;
Dacă valoarea textului este mai mare de 50 de caractere, se vor afișa doar primele 50 de caractere în caseta text JtextAreaStats, dacă nu (lungimea este mai mică de 50 de caractere) afișez întregul text.
Ultima funcționalitate redă un text preluat de la o adresă de Internet. Adresa este introdusă într-o casetă text, iar la apăsare butonului “ÎNCARCĂ SITE” textul regăsit pe pagina de Internet este preluat și afișat în zona text, jTextArea1, urmând să fie redat. Primul pas este de a goli caseta text jTextArea1, după care am folosit o variabilă de tip URL (Uniform Resource Locator), care indică spre o resursă de pe Internet, pentru a stoca adresa de Internet introdusă. Am folosit o variabilă Scanner pentru a citi informația din variabila URL.
Ulterior am afișat informația/textul obținut de Scanner în aria de text jTextArea1. Scanner-ul preia toate cuvintele găsite la adresa de Internet fără să mai conțină delimitatorii inițiali, de exemplu dacă textul găsit la adresa de Internet este pe mai multe rânduri, când voi citi textul din variabila Scanner, va afișa tot continutul pe un singur rând. Astfel a trebuit să aranjez conținutul afișat în așa fel încât textul de la adresa de Internet să fie împărțit în mod egal pe rânduri în caseta text jTextArea1. Am folosit variabila lenprev, ce conține lungimea textului înainte ca următorul cuvânt să fie afișat și variabila len, ce conține diferența dintre lungimea textului după ce cuvântul a fost adăugat și lungimea textului înainte ca și cuvântul să fie adăugat, plus lungimea cuvintelor de până atunci, cu alte cuvinte numărul de caractere al textului introdus, inclusiv ultimul cuvânt adăugat. Presupunând că în caseta text jTextArea1 încap în medie 58 de caractere, am testat dacă lungimea modulo 58 este mai mare sau egală cu valoarea 44, în acest caz introducandu-se “\n” pentru a trece pe rândul următor, adică toate lungimile multiplu de 44÷57 (în apropierea lungimii optime de 58 de caractere) vor conține un “\n”, fiecare rând având maxim între 44 si 57 de caractere. Nu am folosit o valoare fixă, deoarece variabila Scanner îmi preia cu metoda next() câte un cuvânt, lungimea neincrementându-se doar cu o unitate de fiecare data, ci cu lungimea totală a cuvântului de adăugat. Când lungimea este multiplu de 44÷57 de caractere, după ce este inserat “\n”, la lungimea totală a textului este adăugat diferența până la 58 de caractere prin expresia (58-len)%58, unde len reprezintă lungimea textului. Această ultimă modificare își are scopul dacă de exemplu după cele 44 de caractere urmează un cuvânt de zece caractere (majoritatea cuvintelor), la o nouă iterație lungimea modulo 58 >=44 ar fi introdus “\n” (un nou enter) deși rândul al doilea ar conține doar 10 caractere. Nu am setat limita în loc de 44, 56 sau o valoare mai apropiată de 58, deoarece dacă variabila Scanner îmi aduce un cuvânt de peste 5 caractere și lungimea precedenta adăugarii noului cuvânt ar fi de 55 de caractere, rezultatul 55+5=60 ar depăși 58, 60 modulo 58=2<44, astfel nici un spatiu nu ar fi introdus după mai mult de 58 de caractere.
jTextArea1.setText("");
URL url = null;
try {
url = new URL(jTextFieldSite.getText());
} catch (MalformedURLException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
Scanner s = null;
try {
s = new Scanner(url.openStream());
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
int len=0;
int lenprev=0;
while(s.hasNext()){
lenprev=jTextArea1.getText().length();
jTextArea1.append(s.next()+" ");
len=len+jTextArea1.getText().length()-lenprev;
if(len%58>=44) {jTextArea1.append("\n");
len=len+(58-len)%58;
}
}
Celălalte două ferestre FestivalFrame și GoogleTTSFrame sunt realizate în mod similar cu cea prezentată anterior, eSpeakFrame, cu mici diferențe (de exemplu la Google text-to-speech, nu mai există un ComboBox pentru alegerea limbii, ci sunt afișate șase steaguri la apăsarea cărora este selectată limba și este redat cu ajutorul metodei Google Text-to-Speech limba aleasă).
Pentru Raspberry PI am creat două variante ale aplicației, una exact la fel ca cea menționată mai sus (utilizată pe mașina virtuală) și o variantă mai compactă cu o singura fereastra din care pot apela cei trei algoritmi și fără apelarea animației pentru un consum mai mic al resurselor disponibile sau modificarea barei de meniu.
Figura 6.22. Varianta simplificată utilizată ca alternativă pe Rasberry PI.
Concluzii
În concluzie, sinteza vocală este un domeniu fascinant reprezentând un pas important în automatizarea servicilor de telecomunicații, îmbunătățirea sistemelor educaționale, ajutorarea persoanelor cu handicap, monitorizare audio, comunicația directă între om și dispozitivile cu care interacționează, în domeniul cercetării și multe altele. Spun că este un domeniu fascinant, deoarece deși pentru om pare extrem de simplu pe baza unor reguli învățate în copilărie să poată comunica, în cazul calculatoarelor situația stă altfel. Sunetele sunt guvernate de ecuații diferențiale ale mecanicii fluidelor, aplicate într-un context dinamic din momemnt ce presiunea la nivelul plămânilor, tensiunea glotei și configurarea tractului nazal și vocal evoluează în timp. În cazul omului toate aceste sunt controlate de cortex care datorită structurii sale paralele poate extrage esența unui text: înțelesul. Deși din punct de vedere tehnologic în momentul actual se pot construi sisteme care să poată simula sistemul uman, există un compromis între complexitatea și bugetul alocat pentru construcția unui asemnea sistem.Un sintetizator vocal de tip TTS (Text-to-Speech) nu doar redă anumite cuvinte pre-înregistrate ca în cazul anunțării unui tren, ci se bazează pe anumite reguli pentru a oferi inteles oricărui tip de text pe care utilizatorul vrea să îl introducă, fiind imposibilă înregistrarea tuturor cuvintelor și propozițiilor ce pot fi introduse de un utilizator. Un astfel de sistem complex conține două module: cel de procesare naturală de limbaj și cel de procesare digitală de semnal. Modulul pentru procesarea naturală de limbaj oferă o transcriere fonetică împreună cu intonația și ritmul dorit, iar modulul pentru procesarea digitală, transformă simbolurile primite de la modulul de procesarea naturală în discurs. Unele din problemele actuale a celor doua module ce formează un sistem TTS sunt descrise in Capitolul 4.
În aplicația dezvoltată am integrat trei sisteme TTS (Festival, eSpeak si Google Text-to-Speech) având fiecare avantajele și dezvantajele sale. Aplicația nu este dependentă de platforma pe care este executată, fiind mici modificări de sintaxă (de exemplu setarea volumului sau schimbarea librariei utilizata de SIGAR) pentru a muta pe alt system de operare. Sistemul de operare pe care am integrat cele trei sisteme pentru sinteza vocala TTS este Raspbian, care reprezintă sistemul de operare standard pentru Raspberry PI fiind o variantă modificată a sistemului de operare Debian. Aplicația are o interfață plăcuta și este ușor de accesat, având butoane pentru toate funcționalitățile implementate, precum: redarea unui text introdus de la tastatură, redarea unui text salvat pe calculator sau redarea unui text aflat la o adresă de Internet.
Raspberry PI, în ciuda dimensiunilor sale reduse reprezintă un calculator în adevaratul sens al
cuvântului, având posibilitatea conectării unui mouse sau a unei tastaturi, conectării la Internet, precum
și dispunerea de 512MB RAM, utili pentru a putea porni majoritatea aplicațiilor disponibile pe un
computer personal. Poate reda și conținut video de calitate înaltă, FULL HD și se poate conecta prin
pinii GPIO la senzori pentru măsurea temperaturii, presiunii aerului, etc. Prețul avantajos (aproximativ
50 de dolari momentan) face din Raspberry PI un competitor puternic pentru computerele personale
actuale, fiind vândute peste trei milioane de exemplare momentan, conform unor statistici neoficiale un
Raspberry PI la 17 secunde, fiind prezentat până și reginei Angliei într-un program de îmbunătățire a învățării programării în sistemului educațional.
În următoarea perioadă am să continui dezvoltarea aplicației integrând și partea de recunoaștere
vocală încercând să creez un sistem ce poate controla instalația electrică într-o locuință.
Bibliografie
[1] Thierry Dutoit – An Introduction to Text-to-Speech Synthesis
[2] http://www.webdex.ro/online/dictionar/fonem, accesat la 10.04.2014
[3] http://www.raspberrypi.org/, accesat la 15.04.2014
[4] http://www.raspberrypi.org/documentation/hardware/raspberrypi/gpio.md, accesat la 15.04.2014
[5] http://downloads.element14.com/raspberryPi1.html, accesat la 15.04.2014
[6] http://www.raspberrypi.org/help/faqs/#generalOnOff, accesat la 24.04.2014
[7] http://elinux.org/RPi_Hardware, accesat la 28.04.2014
[8] http://elinux.org/RPi_Hardware_Basic_Setup#Display, accesat la 28.04.2014
[9] http://elinux.org/RPi_Hardware_Basic_Setup#Power_Supply, accesat la 28.04.2014
[10] http://elinux.org/RPi_Hardware_Basic_Setup#Cables, accesat la 28.04.2014
[11] http://elinux.org/RPi_Hardware_Basic_Setup#USB-Hub, accesat la 28.04.2014
[12] http://elinux.org/RPi_Hardware_Basic_Setup#Case, accesat la 28.04.2014
[13] http://elinux.org/RPi_Hardware_Basic_Setup#Network_.2F_Internet_Connectivity, accesat la 28.04.2014
[14] http://www.raspberrypi.org/documentation/configuration/audio-config.md, accesat la 3.05.2014
[15] http://whatis.techtarget.com/definition/system-on-a-chip-SoC, accesat la 4.05.2014
[16] A. F. Harvey – DMA Fundamentals on Various PC Platforms
[17] https://www.broadcom.com/products/BCM2835, accesat la 6.05.2014
[18] Broadcom BCM2835 ARM Peripherals – http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf, accesat la 6.05.2014
[19] http://www.arm.com/products/processors/instruction-set-architectures/index.php, accesat la 10.05.2014
[20] https://www.princeton.edu/~achaney/tmve/wiki100k/docs/Pulse-code_modulation.html, accesat la 12.05.2014
[21] Philips Semiconductors – I2S bus – specification – https://web.archive.org/web/20060702004954/http://www.semiconductors.philips.com/acrobat_download/various/I2SBUS.pdf, accesat la 14.05.2014
[22] http://www.raspbian.org/, accesat la 28.05.2014
[23] http://lxde.org/lxde, accesat la 28.05.2014
[24] Arhitectura ARMv8 – http://www.arm.com/files/downloads/ARMv8_Architecture.pdf , accesat la 3.06.2014
[25] http://elinux.org/RPi_raspi-config, accesat la 4.06.2014
[26] http://www.raspberrypi.org/documentation/configuration/config-txt.md, accesat la 4.06.2014
[27] https://www.debian.org/, accesat la 4.06.2014
[28] http://www.istnetworks.com/download_pdf.php?file=resource_35.pdf, accesat la 5.06.2014
[29] Juergen Schroeter – Text to-Speech (TTS) Synthesis
[30] http://www.acoustics.hut.fi/publications/files/theses/lemmetty_mst/chap5.html, accesat la 5.06.2014
[31] http://www.cstr.ed.ac.uk/projects/festival/, accesat la 6.06.2014
[32] http://espeak.sourceforge.net/, accesat la 6.06.2014
[33] http://espeak.sourceforge.net/commands.html, accesat la 6.06.2014
[34] http://danfountain.com/2013/03/raspberry-pi-text-to-speech/, accesat la 10.06.2014
[35] http://festvox.org/, accesat la 10.06.2014
[36] http://www.alsa-project.org/main/index.php/Main_Page, accesat la 15.06.2014
[37] http://overshoot.tv/node/348, accesat la 15.06.2014
[38] Robofun.ro – Raspberry PI pentru începători – Sinteza și recunoașterea de voce – Utilizarea Festival
[39] Robofun.ro – Raspberry PI pentru începători – Sinteza și recunoașterea de voce – Utilizarea Google Text to Speech
[40] http://developer.android.com/reference/android/widget/AbsoluteLayout.html, accesat la 15.06.2014
[41] http://www.webupd8.org/2014/03/how-to-install-oracle-java-8-in-debian.html, accesat la 15.06.2014
Anexe
Anexa 1. Adresele în PCM [18]
Anexa 2. Modificarea sintaxei apelării metodei Google Text-to-Speech pentru a permite redarea textelor mai mari de 100 de caractere. [34]
#!/bin/bash
INPUT=$*
STRINGNUM=0
ary=($INPUT)
for key in "${!ary[@]}"
do
SHORTTMP[$STRINGNUM]="${SHORTTMP[$STRINGNUM]} ${ary[$key]}"
LENGTH=$(echo ${#SHORTTMP[$STRINGNUM]})
if [[ "$LENGTH" -lt "100" ]]; then
SHORT[$STRINGNUM]=${SHORTTMP[$STRINGNUM]}
else
STRINGNUM=$(($STRINGNUM+1))
SHORTTMP[$STRINGNUM]="${ary[$key]}"
SHORT[$STRINGNUM]="${ary[$key]}"
fi
done
for key in "${!SHORT[@]}"
do
echo "Playing line: $(($key+1)) of $(($STRINGNUM+1))"
mpg123 -q "http://translate.google.com/translate_tts?tl=en&q=${SHORT[$key]}"
done
Anexa 3. Instalarea versiunii 1.8 a compiler-ului Java pe o masina ce ruleaza cu sistemul de operare Debian. [41]
su –
echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list
echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list
apt-key adv –keyserver keyserver.ubuntu.com –recv-keys EEA14886
apt-get update
apt-get install oracle-java8-installer
exit
Anexa 4. Codul statisticilor utilizate in proiectul Java cu ajutorul librariei SIGAR
private static final int SLEEP_TIME = 1000 * 1;
private static final String HEADER = "PID\tUSER\tSTIME\tSIZE\tRSS\tSHARE\tSTATE\tTIME\t%CPU\tCOMMAND";
Sigar sigarImpl = new Sigar();
SigarProxy sigar = SigarProxyCache.newInstance(sigarImpl, SLEEP_TIME);
while(true) {
try {
Shell.clearScreen();
System.out.println(sigar.getCpuPerc()+ "\t" + sigar.getMem());
System.out.println();
System.out.println(HEADER);
long[] pids = Shell.getPids(sigar, args);
for (int i=0; i<pids.length; i++) {
long pid = pids[i];
String cpuPerc = "?";
List info;
try {
info = Ps.getInfo(sigar, pid);
} catch (SigarException e) {
continue; //process may have gone away
}
try {
ProcCpu cpu = sigar.getProcCpu(pid);
cpuPerc = CpuPerc.format(cpu.getPercent());
} catch (SigarException e) {
}
info.add(info.size()-1, cpuPerc);
System.out.println(Ps.join(info));
}
Thread.sleep(SLEEP_TIME);
SigarProxyCache.clear(sigar);
} catch (InterruptedException | SigarException ex) {
}
}
Anexa 5. Interfata grafica a clasei Java asociata sistemului eSpeak pentru sinteza vocala.
Anexa 6. Cod sursa pentru fereastra eSpeakFrame (similar pentru fereastra FestivalFrame, GoogleTTSFrame și StartGUI cu mici modificări în funcție de metoda folosită)
public class eSpeakFrame extends javax.swing.JFrame {
private String inputString;
private static final Sigar sigar = new Sigar();
public eSpeakFrame() {
initComponents();
ImageIcon RO = new ImageIcon(getClass().getResource("/AppPackage/flag_RO.jpg"));
jLabelFlag.setIcon(RO);
jSliderSpeed.setValue(170);
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
jLabelSpeedValoare.setText(speedString);
jRadioButtonMasculin.setSelected(true);
}
private void jLabelXMouseClicked(java.awt.event.MouseEvent evt) {
System.exit(0);
}
private void jLabelMMouseClicked(java.awt.event.MouseEvent evt) {
this.setState(StartGUI.ICONIFIED);
}
private void jLabelBackMouseEntered(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_HOVER.png"));
jLabelBack.setIcon(BACK);
}
private void jLabelBackMouseExited(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_DEFAULT.png"));
jLabelBack.setIcon(BACK);
}
private void jLabelBackMousePressed(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_PRESSED.png"));
jLabelBack.setIcon(BACK);
}
private void jLabelBackMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_HOVER.png"));
jLabelBack.setIcon(BACK);
new StartGUI().setVisible(true);
this.setVisible(false);
}
private void jLabelRedaMouseEntered(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_HOVER.png"));
jLabelReda.setIcon(REDA);
}
private void jLabelRedaMouseExited(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_DEFAULT.png"));
jLabelReda.setIcon(REDA);
}
private void jLabelRedaMousePressed(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_PRESSED.png"));
jLabelReda.setIcon(REDA);
}
private void jLabelRedaMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_DEFAULT.png"));
jLabelReda.setIcon(REDA);
if(evt!=null) {
jTextAreaStats.setText("");
Mem mem = null;
try {
mem = sigar.getMem();
} catch (SigarException se) {
}
int volum = jSliderVolum.getValue();
StringBuilder volumSB = new StringBuilder();
volumSB.append(volum);
String volumString = volumSB.toString();
jTextAreaStats.append("Nivelul volumului: "+volumString+"%\n");
String volumcommand = "amixer sset Master " + volumString + "%";
try {
Process pVolum = Runtime.getRuntime().exec(volumcommand);
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
String limba="";
String limbaSB="";
if(jComboBoxLanguage.getSelectedIndex()==0) {
limba="ro";
limbaSB="romana";
}
if(jComboBoxLanguage.getSelectedIndex()==1) {
limba="en";
limbaSB="engleza";
}
String tonalitate="";
String tonalitateSB="";
if(jComboBoxTonalitate.getSelectedIndex()==0) {
tonalitate="10";
tonalitateSB="Joasa";
}
if(jComboBoxTonalitate.getSelectedIndex()==1) {
tonalitate="50";
tonalitateSB="Moderata";
}
if(jComboBoxTonalitate.getSelectedIndex()==2) {
tonalitate="90";
tonalitateSB="Ridicata";
}
String gen="";
String genSB="";
if(jRadioButtonMasculin.getSelectedObjects()!=null) {
gen="";
genSB="Masculin";
}
if(jRadioButtonFeminin.getSelectedObjects()!=null) {
gen="+f3";
genSB="Feminin";
}
String s[] = jTextArea1.getText().split("\\r?\\n");
ArrayList<String>arrList = new ArrayList<>(Arrays.asList(s)) ;
StringBuilder command = new StringBuilder();
command.append("espeak -v ").append(limba).append(gen).append(" -s ").append(speedString).append(" -p ").append(tonalitate).append(" –stdout \"");
int i=0;
for(String x: arrList) {
command.append(arrList.get(i)).append(" ");
i++;
}
command.append("\" | aplay");
String totalcommand = command.toString();
String[] cmd = {
"/bin/sh",
"-c",
totalcommand
};
long startMem = mem.getActualUsed();
long startTime = System.nanoTime();
try {
Process peSpeak = Runtime.getRuntime().exec(cmd);
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
long endTime = System.nanoTime();
long endMem = mem.getActualUsed();
long totalTime = endTime – startTime;
long totalMem = endMem – startMem;
jTextAreaStats.append("Timpul de executie al comenzii: "+totalTime+"ns \n");
jTextAreaStats.append("Memoria consumata de comanda: "+ totalMem + "B\n");
jTextAreaStats.append("Memorie sistemului disponibila: "+ mem.getActualFree() /1024 /1024 + " MB\n");
jTextAreaStats.append("Limba: "+limbaSB+" | Viteza: "+speedString+"\nTonalitatea: "+tonalitateSB+" | Gen: "+genSB+"\n");
if(jTextArea1.getText().length()>50)
jTextAreaStats.append("Textul redat: "+jTextArea1.getText().substring(0,50)+"…");
else jTextAreaStats.append("Textul redat: "+jTextArea1.getText()) ;
}
}
private void jLabelUploadMousePressed(java.awt.event.MouseEvent evt) {
ImageIcon UPLOAD = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INCARCATEXT_PRESSED.png"));
jLabelUpload.setIcon(UPLOAD);
jTextArea1.setText("");
}
private void jLabelUploadMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon UPLOAD = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INCARCATEXT_DEFAULT.png"));
jLabelUpload.setIcon(UPLOAD);
JFileChooser FileChooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("Text Files", "txt");
FileChooser.setFileFilter(filter);
int returnVal = FileChooser.showOpenDialog(jLabelUpload);
if(returnVal == JFileChooser.APPROVE_OPTION) {
FileReader reader = null;
try {
File file = FileChooser.getSelectedFile();
reader = new FileReader( file );
try (BufferedReader br = new BufferedReader(reader)) {
jTextArea1.read( br, null );
}
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
reader.close();
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
private void jSliderSpeedMouseReleased(java.awt.event.MouseEvent evt) {
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
jLabelSpeedValoare.setText(speedString);
}
private void jComboBoxLanguageActionPerformed(java.awt.event.ActionEvent evt) {
ImageIcon RO = new ImageIcon(getClass().getResource("/AppPackage/flag_RO.jpg"));
ImageIcon EN = new ImageIcon(getClass().getResource("/AppPackage/flag_en.jpg"));
if(jComboBoxLanguage.getSelectedIndex()==0) jLabelFlag.setIcon(RO);
if(jComboBoxLanguage.getSelectedIndex()==1) jLabelFlag.setIcon(EN);
}
private void jLabel1MousePressed(java.awt.event.MouseEvent evt) {
ImageIcon SITE = new ImageIcon(getClass().getResource("/AppPackage/BUTON_INCARCASITE_PRESSED.png"));
jLabel1.setIcon(SITE);
}
private void jLabel1MouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon SITE = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INCARCASITE_DEFAULT.png"));
jLabel1.setIcon(SITE);
jTextArea1.setText("");
if(jTextFieldSite.getText().length()>0){
URL url = null;
try {
url = new URL(jTextFieldSite.getText());
} catch (MalformedURLException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
Scanner s = null;
try {
s = new Scanner(url.openStream());
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
int len=0;
int lenprev=0;
while(s.hasNext()){
lenprev=jTextArea1.getText().length();
jTextArea1.append(s.next()+" ");
len=len+jTextArea1.getText().length()-lenprev;
if(len%58>=44) {jTextArea1.append("\n");
len=len+58-len%58;
}
}
}
}
private void jTextFieldSiteMouseClicked(java.awt.event.MouseEvent evt) {
jTextFieldSite.setText("");
}
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Windows".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(StartGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(() -> {
new eSpeakFrame().setVisible(true);
});
}
Bibliografie
[1] Thierry Dutoit – An Introduction to Text-to-Speech Synthesis
[2] http://www.webdex.ro/online/dictionar/fonem, accesat la 10.04.2014
[3] http://www.raspberrypi.org/, accesat la 15.04.2014
[4] http://www.raspberrypi.org/documentation/hardware/raspberrypi/gpio.md, accesat la 15.04.2014
[5] http://downloads.element14.com/raspberryPi1.html, accesat la 15.04.2014
[6] http://www.raspberrypi.org/help/faqs/#generalOnOff, accesat la 24.04.2014
[7] http://elinux.org/RPi_Hardware, accesat la 28.04.2014
[8] http://elinux.org/RPi_Hardware_Basic_Setup#Display, accesat la 28.04.2014
[9] http://elinux.org/RPi_Hardware_Basic_Setup#Power_Supply, accesat la 28.04.2014
[10] http://elinux.org/RPi_Hardware_Basic_Setup#Cables, accesat la 28.04.2014
[11] http://elinux.org/RPi_Hardware_Basic_Setup#USB-Hub, accesat la 28.04.2014
[12] http://elinux.org/RPi_Hardware_Basic_Setup#Case, accesat la 28.04.2014
[13] http://elinux.org/RPi_Hardware_Basic_Setup#Network_.2F_Internet_Connectivity, accesat la 28.04.2014
[14] http://www.raspberrypi.org/documentation/configuration/audio-config.md, accesat la 3.05.2014
[15] http://whatis.techtarget.com/definition/system-on-a-chip-SoC, accesat la 4.05.2014
[16] A. F. Harvey – DMA Fundamentals on Various PC Platforms
[17] https://www.broadcom.com/products/BCM2835, accesat la 6.05.2014
[18] Broadcom BCM2835 ARM Peripherals – http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf, accesat la 6.05.2014
[19] http://www.arm.com/products/processors/instruction-set-architectures/index.php, accesat la 10.05.2014
[20] https://www.princeton.edu/~achaney/tmve/wiki100k/docs/Pulse-code_modulation.html, accesat la 12.05.2014
[21] Philips Semiconductors – I2S bus – specification – https://web.archive.org/web/20060702004954/http://www.semiconductors.philips.com/acrobat_download/various/I2SBUS.pdf, accesat la 14.05.2014
[22] http://www.raspbian.org/, accesat la 28.05.2014
[23] http://lxde.org/lxde, accesat la 28.05.2014
[24] Arhitectura ARMv8 – http://www.arm.com/files/downloads/ARMv8_Architecture.pdf , accesat la 3.06.2014
[25] http://elinux.org/RPi_raspi-config, accesat la 4.06.2014
[26] http://www.raspberrypi.org/documentation/configuration/config-txt.md, accesat la 4.06.2014
[27] https://www.debian.org/, accesat la 4.06.2014
[28] http://www.istnetworks.com/download_pdf.php?file=resource_35.pdf, accesat la 5.06.2014
[29] Juergen Schroeter – Text to-Speech (TTS) Synthesis
[30] http://www.acoustics.hut.fi/publications/files/theses/lemmetty_mst/chap5.html, accesat la 5.06.2014
[31] http://www.cstr.ed.ac.uk/projects/festival/, accesat la 6.06.2014
[32] http://espeak.sourceforge.net/, accesat la 6.06.2014
[33] http://espeak.sourceforge.net/commands.html, accesat la 6.06.2014
[34] http://danfountain.com/2013/03/raspberry-pi-text-to-speech/, accesat la 10.06.2014
[35] http://festvox.org/, accesat la 10.06.2014
[36] http://www.alsa-project.org/main/index.php/Main_Page, accesat la 15.06.2014
[37] http://overshoot.tv/node/348, accesat la 15.06.2014
[38] Robofun.ro – Raspberry PI pentru începători – Sinteza și recunoașterea de voce – Utilizarea Festival
[39] Robofun.ro – Raspberry PI pentru începători – Sinteza și recunoașterea de voce – Utilizarea Google Text to Speech
[40] http://developer.android.com/reference/android/widget/AbsoluteLayout.html, accesat la 15.06.2014
[41] http://www.webupd8.org/2014/03/how-to-install-oracle-java-8-in-debian.html, accesat la 15.06.2014
Anexe
Anexa 1. Adresele în PCM [18]
Anexa 2. Modificarea sintaxei apelării metodei Google Text-to-Speech pentru a permite redarea textelor mai mari de 100 de caractere. [34]
#!/bin/bash
INPUT=$*
STRINGNUM=0
ary=($INPUT)
for key in "${!ary[@]}"
do
SHORTTMP[$STRINGNUM]="${SHORTTMP[$STRINGNUM]} ${ary[$key]}"
LENGTH=$(echo ${#SHORTTMP[$STRINGNUM]})
if [[ "$LENGTH" -lt "100" ]]; then
SHORT[$STRINGNUM]=${SHORTTMP[$STRINGNUM]}
else
STRINGNUM=$(($STRINGNUM+1))
SHORTTMP[$STRINGNUM]="${ary[$key]}"
SHORT[$STRINGNUM]="${ary[$key]}"
fi
done
for key in "${!SHORT[@]}"
do
echo "Playing line: $(($key+1)) of $(($STRINGNUM+1))"
mpg123 -q "http://translate.google.com/translate_tts?tl=en&q=${SHORT[$key]}"
done
Anexa 3. Instalarea versiunii 1.8 a compiler-ului Java pe o masina ce ruleaza cu sistemul de operare Debian. [41]
su –
echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list
echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list
apt-key adv –keyserver keyserver.ubuntu.com –recv-keys EEA14886
apt-get update
apt-get install oracle-java8-installer
exit
Anexa 4. Codul statisticilor utilizate in proiectul Java cu ajutorul librariei SIGAR
private static final int SLEEP_TIME = 1000 * 1;
private static final String HEADER = "PID\tUSER\tSTIME\tSIZE\tRSS\tSHARE\tSTATE\tTIME\t%CPU\tCOMMAND";
Sigar sigarImpl = new Sigar();
SigarProxy sigar = SigarProxyCache.newInstance(sigarImpl, SLEEP_TIME);
while(true) {
try {
Shell.clearScreen();
System.out.println(sigar.getCpuPerc()+ "\t" + sigar.getMem());
System.out.println();
System.out.println(HEADER);
long[] pids = Shell.getPids(sigar, args);
for (int i=0; i<pids.length; i++) {
long pid = pids[i];
String cpuPerc = "?";
List info;
try {
info = Ps.getInfo(sigar, pid);
} catch (SigarException e) {
continue; //process may have gone away
}
try {
ProcCpu cpu = sigar.getProcCpu(pid);
cpuPerc = CpuPerc.format(cpu.getPercent());
} catch (SigarException e) {
}
info.add(info.size()-1, cpuPerc);
System.out.println(Ps.join(info));
}
Thread.sleep(SLEEP_TIME);
SigarProxyCache.clear(sigar);
} catch (InterruptedException | SigarException ex) {
}
}
Anexa 5. Interfata grafica a clasei Java asociata sistemului eSpeak pentru sinteza vocala.
Anexa 6. Cod sursa pentru fereastra eSpeakFrame (similar pentru fereastra FestivalFrame, GoogleTTSFrame și StartGUI cu mici modificări în funcție de metoda folosită)
public class eSpeakFrame extends javax.swing.JFrame {
private String inputString;
private static final Sigar sigar = new Sigar();
public eSpeakFrame() {
initComponents();
ImageIcon RO = new ImageIcon(getClass().getResource("/AppPackage/flag_RO.jpg"));
jLabelFlag.setIcon(RO);
jSliderSpeed.setValue(170);
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
jLabelSpeedValoare.setText(speedString);
jRadioButtonMasculin.setSelected(true);
}
private void jLabelXMouseClicked(java.awt.event.MouseEvent evt) {
System.exit(0);
}
private void jLabelMMouseClicked(java.awt.event.MouseEvent evt) {
this.setState(StartGUI.ICONIFIED);
}
private void jLabelBackMouseEntered(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_HOVER.png"));
jLabelBack.setIcon(BACK);
}
private void jLabelBackMouseExited(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_DEFAULT.png"));
jLabelBack.setIcon(BACK);
}
private void jLabelBackMousePressed(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_PRESSED.png"));
jLabelBack.setIcon(BACK);
}
private void jLabelBackMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon BACK = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INAPOI_HOVER.png"));
jLabelBack.setIcon(BACK);
new StartGUI().setVisible(true);
this.setVisible(false);
}
private void jLabelRedaMouseEntered(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_HOVER.png"));
jLabelReda.setIcon(REDA);
}
private void jLabelRedaMouseExited(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_DEFAULT.png"));
jLabelReda.setIcon(REDA);
}
private void jLabelRedaMousePressed(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_PRESSED.png"));
jLabelReda.setIcon(REDA);
}
private void jLabelRedaMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon REDA = new ImageIcon (getClass().getResource("/AppPackage/BUTON_REDA_DEFAULT.png"));
jLabelReda.setIcon(REDA);
if(evt!=null) {
jTextAreaStats.setText("");
Mem mem = null;
try {
mem = sigar.getMem();
} catch (SigarException se) {
}
int volum = jSliderVolum.getValue();
StringBuilder volumSB = new StringBuilder();
volumSB.append(volum);
String volumString = volumSB.toString();
jTextAreaStats.append("Nivelul volumului: "+volumString+"%\n");
String volumcommand = "amixer sset Master " + volumString + "%";
try {
Process pVolum = Runtime.getRuntime().exec(volumcommand);
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
String limba="";
String limbaSB="";
if(jComboBoxLanguage.getSelectedIndex()==0) {
limba="ro";
limbaSB="romana";
}
if(jComboBoxLanguage.getSelectedIndex()==1) {
limba="en";
limbaSB="engleza";
}
String tonalitate="";
String tonalitateSB="";
if(jComboBoxTonalitate.getSelectedIndex()==0) {
tonalitate="10";
tonalitateSB="Joasa";
}
if(jComboBoxTonalitate.getSelectedIndex()==1) {
tonalitate="50";
tonalitateSB="Moderata";
}
if(jComboBoxTonalitate.getSelectedIndex()==2) {
tonalitate="90";
tonalitateSB="Ridicata";
}
String gen="";
String genSB="";
if(jRadioButtonMasculin.getSelectedObjects()!=null) {
gen="";
genSB="Masculin";
}
if(jRadioButtonFeminin.getSelectedObjects()!=null) {
gen="+f3";
genSB="Feminin";
}
String s[] = jTextArea1.getText().split("\\r?\\n");
ArrayList<String>arrList = new ArrayList<>(Arrays.asList(s)) ;
StringBuilder command = new StringBuilder();
command.append("espeak -v ").append(limba).append(gen).append(" -s ").append(speedString).append(" -p ").append(tonalitate).append(" –stdout \"");
int i=0;
for(String x: arrList) {
command.append(arrList.get(i)).append(" ");
i++;
}
command.append("\" | aplay");
String totalcommand = command.toString();
String[] cmd = {
"/bin/sh",
"-c",
totalcommand
};
long startMem = mem.getActualUsed();
long startTime = System.nanoTime();
try {
Process peSpeak = Runtime.getRuntime().exec(cmd);
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
long endTime = System.nanoTime();
long endMem = mem.getActualUsed();
long totalTime = endTime – startTime;
long totalMem = endMem – startMem;
jTextAreaStats.append("Timpul de executie al comenzii: "+totalTime+"ns \n");
jTextAreaStats.append("Memoria consumata de comanda: "+ totalMem + "B\n");
jTextAreaStats.append("Memorie sistemului disponibila: "+ mem.getActualFree() /1024 /1024 + " MB\n");
jTextAreaStats.append("Limba: "+limbaSB+" | Viteza: "+speedString+"\nTonalitatea: "+tonalitateSB+" | Gen: "+genSB+"\n");
if(jTextArea1.getText().length()>50)
jTextAreaStats.append("Textul redat: "+jTextArea1.getText().substring(0,50)+"…");
else jTextAreaStats.append("Textul redat: "+jTextArea1.getText()) ;
}
}
private void jLabelUploadMousePressed(java.awt.event.MouseEvent evt) {
ImageIcon UPLOAD = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INCARCATEXT_PRESSED.png"));
jLabelUpload.setIcon(UPLOAD);
jTextArea1.setText("");
}
private void jLabelUploadMouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon UPLOAD = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INCARCATEXT_DEFAULT.png"));
jLabelUpload.setIcon(UPLOAD);
JFileChooser FileChooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("Text Files", "txt");
FileChooser.setFileFilter(filter);
int returnVal = FileChooser.showOpenDialog(jLabelUpload);
if(returnVal == JFileChooser.APPROVE_OPTION) {
FileReader reader = null;
try {
File file = FileChooser.getSelectedFile();
reader = new FileReader( file );
try (BufferedReader br = new BufferedReader(reader)) {
jTextArea1.read( br, null );
}
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
reader.close();
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
private void jSliderSpeedMouseReleased(java.awt.event.MouseEvent evt) {
int speed = jSliderSpeed.getValue();
String speedString = new StringBuilder().append(speed).toString();
jLabelSpeedValoare.setText(speedString);
}
private void jComboBoxLanguageActionPerformed(java.awt.event.ActionEvent evt) {
ImageIcon RO = new ImageIcon(getClass().getResource("/AppPackage/flag_RO.jpg"));
ImageIcon EN = new ImageIcon(getClass().getResource("/AppPackage/flag_en.jpg"));
if(jComboBoxLanguage.getSelectedIndex()==0) jLabelFlag.setIcon(RO);
if(jComboBoxLanguage.getSelectedIndex()==1) jLabelFlag.setIcon(EN);
}
private void jLabel1MousePressed(java.awt.event.MouseEvent evt) {
ImageIcon SITE = new ImageIcon(getClass().getResource("/AppPackage/BUTON_INCARCASITE_PRESSED.png"));
jLabel1.setIcon(SITE);
}
private void jLabel1MouseReleased(java.awt.event.MouseEvent evt) {
ImageIcon SITE = new ImageIcon (getClass().getResource("/AppPackage/BUTON_INCARCASITE_DEFAULT.png"));
jLabel1.setIcon(SITE);
jTextArea1.setText("");
if(jTextFieldSite.getText().length()>0){
URL url = null;
try {
url = new URL(jTextFieldSite.getText());
} catch (MalformedURLException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
Scanner s = null;
try {
s = new Scanner(url.openStream());
} catch (IOException ex) {
Logger.getLogger(FestivalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
int len=0;
int lenprev=0;
while(s.hasNext()){
lenprev=jTextArea1.getText().length();
jTextArea1.append(s.next()+" ");
len=len+jTextArea1.getText().length()-lenprev;
if(len%58>=44) {jTextArea1.append("\n");
len=len+58-len%58;
}
}
}
}
private void jTextFieldSiteMouseClicked(java.awt.event.MouseEvent evt) {
jTextFieldSite.setText("");
}
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Windows".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(StartGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(() -> {
new eSpeakFrame().setVisible(true);
});
}
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Sistem Pentru Sinteza Vocala cu Sistemul de Dezvoltare Raspberry Pi (ID: 163565)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
