Rozpoznawanie oraz synteza mowy z wykorzystaniem JSAPI

lukashm

glowa.jpg

<font size="4">Rozpoznawanie oraz synteza mowy z wykorzystaniem
Java Speech API.
</span>

<font size="4">1. Instalacja</span>

<font size="3">1.1 Sprzęt</span>

Pentium 200 MHz, 64 MB RAM,
zalecane powyżej Pentium 400 MHz, 256 MB RAM.

<font size="3">1.2 System operacyjny</span>

Microsoft Windows 98, Me , NT4, 2000 lub XP

<font size="3">1.3 Niezbędne składniki </span>

Aby możliwe było kompilowanie oraz uruchamianie programu niezbędne będzie zainstalowanie w systemie następujących składników:
*J2SDK 1.4.2.02 windows-i586-p
*Microsoft Speech SDK 5.1
*Java Media Framework 2.1.1e
*TalkingJava SDK 1.6.2
<font size="3">1.4 Przebieg instalacji</span>

Instalację należy rozpocząć od zainstalowania w systemie J2SDK 1.4.2.02 windows-i586-p, po której możemy zainstalować środowiska programistyczne (np. JCreator Pro, JBuilder, RealJava czy FreeJava ) w celu rekompilacji programu.

Następnie instalujemy niezbędne silniki (speech recognition and synthesis engine) rozpoznawania oraz syntezy mowy - Microsoft Speech SDK 5.1.
Po tych czynnościach instalujemy pakiet odpowiedzialny za przechwytywanie i odtwarzanie mediów (audio - video) Java Media Framework 2.1.1e.

Po instalacji wyżej wymienionych pakietów instalujemy składnik TalkingJava SDK 1.6.2.
W "Zmiennych środowiskowych? systemu Windows (lub w pliku "autoexec.bat? dla Windows98) ustawiamy zmienne CLASSPATH (wartosc rejestru "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Java VM\Classpath") - podając scieżkę do pliku "cgjsapi.jar? oraz PATH ? podając scieżkę do pliku "cgjsapi162.dll? znajdujących sie w katalogu, w którym zainstalowaliśmy składnik TalkingJava SDK1.6.2 lub skopiowac plik "cgjsapi162.dll" do folderu systemowego (np. "c:\windows") .

Nastepnie kopiujemy pliki "cgjsapi.jar" oraz "cgjsapi162.dll" do katalogów lib\ext oraz bin środowiska JRE znajdującego się w zainstalowanym składniku J2SDK 1.4.2.02 windows-i586-p.

W celu rekompilacji gdy korzystamy ze środowiska programistycznego (jak np. JCreator Pro) należy dodać pakiet packet.jar znajdujący się w katalogu instalacyjnym składnika TalkingJava SDK1.6.2 do profili SDK.

Postępujemy następująco: Configure -> Options -> JDK Profiles, następnie klikając dwukrotnie na dołączonym do środowiska JDK( np. j2sdk 1.4.2_02) wybieramy Add -> Add Archive i odnajdujemy na dysku pakiet packet.jar. Podobnie postępujemy z pakietem cgjsapi.jar.

JDKProfiles.jpg

<font size="3">2. Uruchamianie</span>

Aby móc korzystać w pełni sprawnie z programu potrzebny będzie mikrofon. Najlepiej wykorzystać słuchawki z mikrofonem typu headset, co zapewnia optymalne ustawienie odległości mikrofonu od ust i pozwala na uzyskanie lepszych wyników rozpoznawania mowy przez system, ponieważ siła głosu nie zmienia się tak drastycznie (o ile użytkownik jej nie zmieni) jak przy użyciu standardowego mikrofonu trzymanego w ręce gdzie odległość od ust może się niezauważalnie w trakcie wypowiadania komend zmieniać i pogarszać wyniki działania programu.

sluchawki.jpg

Po uruchomieniu programu ujrzymy okno konfiguracji JSAPI Speech Engines, w którym należy wybrać odpowiednie profile rozpoznawania oraz syntezy mowy.

JSAPSpeechEngines.jpg

Po przejściu na konkretny profil (syntezatora lub rozpoznawcę) uaktywni się przycisk jego właściwości (Recognizer Properties lub Synthesizer Properties ) co umożliwi nam w przypadku syntezatora ustawienie barwy dźwięku oraz szybkości czytania,

SynthProp.jpg

natomiast dla modułu rozpoznawania mowy możliwe jest ustawienie szybkości rozpoznawania., pewności (trafności) oraz czasu trwania wypowiedzi.

RecoProp.jpg

Jako zaawansowane opcje modułu rozpoznającego mowę należy wyróżnić:

*trening silnika dla użytkownika (User Training), który składa się z kilkunastu zdań, po przeczytaniu których, system uczy się lepiej rozpoznawać głos danego użytkownika .

UserTraning.jpg

*ustawienia mikrofonu, które umożliwiają optymalne ustawienia poziomu sygnału wejściowego.

MicroTraning.jpg

<font size="4">3. Opis programu (prosty edytor graficzny sterowany glosem).</span>

<font size="3">3.1 Sprzęt</span>
*Program testowany, napisany, kompilowany oraz uruchamiany na sprzęcie:
*procesor: Celeron 562 MHz,
*pamięć: 320 MB RAM,
*karta muzyczna: Sound Blaster Live 1024,

<font size="3">3.2 Narzędzia</span>
*system operacyjny: Windowws XP Pro,
*jdk: J2SDK 1.4.2.02 windows-i586-p,
*środowisko: JCreator Pro version 2.5,

<font size="3">3.3 Opis kodu </span>

Aby możliwe było skorzystanie z mechanizmów JSAPI Engine, niezbędne jest importowanie następujących pakietów

<font name="Courier New"> ```java import javax.speech.*; import javax.speech.recognition.*; import javax.speech.synthesis.*;
</span>

<font name="Courier New">
```java
import com.cloudgarden.speech.userinterface.*;
import com.cloudgarden.speech.CGEngineProperties;
import com.cloudgarden.speech.CGEngineCentral;

</span>

Pozostałe pakiety są standardowo instalowane z JDK
<font name="Courier New">

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*; 
</span>

Program składa się z dwóch klas:

public class RozpoznawanieOrazSynteza extends Thread {}

Jest to klasa główna ( posiadająca nazwę pliku źródłowego *.java ), w której to umieszczona jest metoda main(), oraz dwa główne obiekty silników rozpoznawania oraz syntezy mowy należące do klas uwidocznionych poniżej, inicjowane wartością null..:

<font name="Courier New"> ```java public static Recognizer Rozpoznawca = null; public static Synthesizer Syntezer = null;
</span>

<b>Pozostałe obiekty i pola klasy </b>

Okno klasy Frame służące do wyświetlania informacji (autor, przeznaczenie projektu) na początku programu, oraz napisu "Program zakończony" po wychwyceniu z wypowiedzianych komend słowa "exit". 

```java
public static Frame OknoInformacji = null;

Etykieta label1 umieszczona na OknieInformacji, na ktorej to zostają wyświetlane wszystkie łańcuchy tekstowe

public static Label label1 = null; 

Trzy zmienne boolowskie służące do sterowania mechanizmami otwierania, rysowania figur oraz zmiany rozmiaru płótna

<font name="Courier New"> ```java public static boolean IsOpen = false; public static boolean IsExist = false; public static boolean IsResizing = false;
</span>

Trzy obiekty klasy Font wykorzystane zostały w celu ustawienia w obiekcie label1 różnych rodzajów czcionek podczas wyświetlania informacji. 

<font name="Courier New">
```java
public static Font czcionka1 = new Font( "Times New Roman", Font.ROMAN_BASELINE, 24 );
public static Font czcionka2 = new Font( "Verdana", Font.ROMAN_BASELINE, 30 );
public static Font czcionka3 = new Font( "Dialog", Font.ROMAN_BASELINE, 32 ); 

</span>

Metody klasy

Dwie poniżej są to metody odpowiedzialne za wyświetlanie informacji podczas startu programu oraz w trakcie jego zakończenia.

<font name="Courier New"> ```java public static void Informacje(int czas){} public static void Wyjscie(int czas){}
</span>

Metoda Wait() korzystająca z funkcji sleep(), dziedziczonej z klasy Thread, w celu opóźniania niektórych sekwencji wykonywanego kodu 

```java
public static boolean Wait(int milisec){}

Metoda znajdująca się wewnątrz metody main(), odpowiedzialna za obsługę zdarzeń

public void resultAccepted(ResultEvent e) {}

Metoda main()

Inicjalizacja obiektów odpowiedzialnych za wyświetlanie figur geometrycznych

<font name="Courier New"> ```java final SterowaneOkno Plotno = new SterowaneOkno(); final Graphics graph = null;
</span>

Zawarte w konstrukcji try...catch utworzenie obiektu wybieracz, służącego do konfiguracji silników rozpoznawania oraz syntezy mowy 

```java
final SpeechEngineChooser wybieracz = SpeechEngineChooser.getAllEnginesDialog()

Po zebraniu informacji na temat dostępnych silników w systemie następuje uwidocznienie okna JSAPI Speech Engines (omówione w zakładce "Instalacja")

wybieracz.show();

Główny blok programy umieszczony w konstrukcji try...catch (ponieważ mogą w nim występować sytuacje wyjątkowe), w którym ustalone reguły gramatyczne zostają porównywane z otrzymanymi po rozpoznaniu komendami słownymi

<font name="Courier New"> ```java

try{
FinalResult r =(FinalResult)(e.getSource());
Grammar gram = r.getGrammar();

if(gram instanceof RuleGrammar) {
   String tags[] = ((FinalRuleResult)r).getTags();
         
         //ciąg_warunków if()
		.
		.
		.
         if( tags[0].equals("ustalony_TAG") )
		{
                     //reakcje_systemu
                    }

catch(Exception e1)
{
//reakcja_na_wystąpienie_wyjątku
}

</span>
 
<b>Obiekty odpowiedzialne za nasłuchiwanie wejścia mikrofonowego oraz jego przetwarzanie:</b>

<font name="Courier New">
```java
RecognizerAudioAdapter raud = new TestAudioListener();
Rozpoznawca.getAudioManager().addAudioListener(raud);
Rozpoznawca.addEngineListener(new TestEngineListener());

</span>

Alokacja głównego obiektu Rozpoznawca, oraz ustawienie profili mówcy, wcześniej wybranych w oknie konfiguracji dla rozpoznawania

<font name="Courier New"> ```java Rozpoznawca.allocate(); SpeakerProfile profil = wybieracz.getSpeakerProfile(); Rozpoznawca.getSpeakerManager().setCurrentSpeaker(profil); Rozpoznawca.waitEngineState(Recognizer.ALLOCATED);
</span>

Alokacja obiektu syntezatora mowy Syntezer

<font name="Courier New">
```java
Syntezer.allocate();
Syntezer.waitEngineState(Synthesizer.ALLOCATED);
Syntezer.resume();

</span>

Przypisanie odpowiedniego głosu syntezatora, wcześniej wybranego w oknie konfiguracji syntezy

<font name="Courier New"> ```java SynthesizerProperties syth_prop = Syntezer.getSynthesizerProperties(); syth_prop.setVoice(wybieracz.getVoice());
</span>

Utworzenie obowiązującej gramatyki rozpocznie się utworzeniem sekwencji reguł z podanymi poniżej TAG-ami

<font name="Courier New">
```java
RuleSequence rs1 = new RuleSequence();
RuleTag regula_1 = new RuleTag(new RuleToken("komenda_1"),"TAG_1");
. 
. 
. 
RuleTag regula_n = new RuleTag(new RuleToken("komenda_n"),"TAG_N");

</span>

Po tej czynności każda z reguł zostaję dołączona do możliwych wariantów komend rozpoznawanych

<font name="Courier New"><font name="Courier New">

RuleAlternatives wariant = new 
RuleAlternatives();
wariant.append(regula_1); 
. 
. 
. 
wariant.append(regula_n);

</span> </span>

Następnie odbywa się utworzenie obiektu przechowującego naszą gramatykę, ustawienie reguł oraz uaktywnienie jej

<font name="Courier New"> ```java RuleGrammar gramatyka = Rozpoznawca.newRuleGrammar("Gramatyka_1"); gramatyka.setRule("Reguły_1",wariant,true); gramatyka.setEnabled(true);
</span>

Ostatecznie w konstrukcji finally...try...catch zostają zwolnione obiekty rozpoznawania oraz syntezy:
 
```java
finally {
	try { Syntezer.deallocate();
	      Rozpoznawca.deallocate();
	      Rozpoznawca.waitEngineState(Synthesizer.DEALLOCATED);
	      Syntezer.waitEngineState(Synthesizer.DEALLOCATED);
	      } 

	catch(Exception e2) {
	      e2.printStackTrace(System.out);
	     }

class SterowaneOkno extends Frame {}

Klasa dziedzicząca z klasy Frame, która posłuży nam jako płótno edytora do wyświetlania zachodzących zdarzeń w programie

Pola klasy

public static int RysowanyElement = 0;
public static int KolorPlotna = 0;

Zmienne odpowiedzialne za ustalenie punktu początkowego, rozmiaru oraz przesunięcia, po modyfikacji których w trakcie działania programu możemy dowolnie manipulować figurami

<font name="Courier New"> ```java public int x0 = 50; public int y0 = 50;

public int Rozmiar = 0;
public int szerokosc = 400;
public int wysokosc = 400;

public int Xoffset = 0;
public int Yoffset = 0;

</span>

<b>Konstruktor</b>

Odpowiada za ustawienie wybranych wartości początkowych nowo tworzonego obiektu
<font name="Courier New">

```java
public SterowaneOkno()
	{
          setTitle("Plotno");
          setSize(500, 500);
          setLocation(200,200);
          setBackground(Color.gray);
	}

</span>

Metody klasy:

Główną metodą znajdującą się w klasie SterowaneOkno jest paint(), która przykrywa metodę klasy Frame. Odpowiedzialna za malowanie odpowiednich figur geometrycznych oraz zmianę koloru tła formularza Plotno podczas reakcji systemu na rozpoznanie wypowiadanej komendy słownej.

<font name="Courier New"> ```java public void paint(Graphics powierzchnia){}
</span>

Realizują to dwa mechnizmy switch():

<font name="Courier New">
```java
switch(KolorPlotna){}{
           //warianty_zmiany_tla
         }

switch(RysowanyElement){}{
           //warianty_rysowania_figur
         }

</span>

<font size="4">4. Sterowanie</span>

<font size="2">Opis komend głosowych akceptowanych przez aplikacje </span>

<font size="3">4.1. Sterowanie aplikacją</span>
*Exit ? wyjście z programu,
*Open ? otwarcie płótna (formatki),
*Close- schowanie płótna (formatki),
<font size="3">4.2. Powtorzenie sekwencji słów przez syntezator mowy</span>
*One ? wypowiedziane przez syntezator zostaną słowa - ?You speak One?.
*Two - wypowiedziane przez syntezator zostaną słowa - ?You speak Two?.
*Three - wypowiedziane przez syntezator zostaną słowa - ?You speak Three?.
*Four - wypowiedziane przez syntezator zostaną słowa - ?You speak Four?.
<font size="3">4.4. Sterowanie wewnątrz aplikacji</span>
*Size? zmiana rozmiaru płótna,
*Remove ? usuniecie figury oraz ustawienie białego koloru tła,
<font size="3">4.1 Ustawienie kolorów tła płótna</span>
*White- ustawiony zostanie kolor biały,
*Gray - ustawiony zostanie kolor szary,
*Blue - ustawiony zostanie kolor niebieski,
*Green - ustawiony zostanie kolor zielony,
*Yellow - ustawiony zostanie kolor żółty,
<font size="3">4.2 Rysowanie podstawowych figur geometrycznych</span>
*Line ? na płótnie narysowana zostanie linia,
*Circle - na płótnie narysowane zostanie kolo,
*Rect- na płótnie narysowany zostanie prostokąt,
*Fill ? wypelnienie aktualnie narysowanej figury.
<font size="3">4.3 Manipulacja figurami geometrycznymi</span>
*Grow ? powiększenie figury,
*Reduce ? zmniejszenie figury,
*Left ? przesuniecie w lewo,
*Right - przesuniecie w prawo,
*Up - przesuniecie w górę,
*Down - przesuniecie w dół,

<font size="4">5.Przebieg działania programu </span>

<font size="3">5.1. Po skompilowaniu oraz uruchomieniu programu</span>

Ukazuje się okno konfiguracji JSAPI Speech Engines (omówione dokładniej w zakładce "Instalacja"), w którym dokonujemy ustawienia odpowiednich profili syntezy oraz rozpoznawania mowy. Gdy ukończymy etap konfiguracji zatwierdzając przyciskiem "OK", okno zostanie zamknięte a program uruchomi się w konsoli.

wystartowanie.jpg

W pierwszych liniach oznaczonych symboliką

com.cloudgarden.speech.CGxxxxxxxxxxxxxx

następuje inicjalizacja oraz alokacja silników rozpoznawania i syntezy mowy
W ostatniej linii startowej programu możemy ujrzeć wpis

com.cloudgarden.speech.CGRecognizer@19a029e recognizerListening

co oznacza, iż program przeszedł w tryb nasłuchiwania mikrofonu. Od tej pory wychwytuje sygnał z wejścia mikrofonowego cyklicznie odpytując port karty muzycznej o czym świadczą pojawiające się (kilka razy/sek) komunikaty AudioLevel x.xx , przedstawiające poziom sygnału pobranego z mikrofonu.

RecognizerProces.jpg

Gdy poziom sygnału przekroczy pewną wartość graniczną system wychwyci to i podda klasyfikacji w celu rozpoznania co uwidocznione wyżej na screenie konsoli w trakcie działania poleceniami:

AudioLevel 0.49 
Speech started 
recognizerProcessing 
Speech stopped

Po czym program przechodzi dalej w tryb nasłuchiwania recognizerListening

<font size="3">5.2. Synteza</span>

W trakcie działania możemy także ujrzeć znajdujące się w lewym górnym rogu ekranu usta położone na formatce, obrazujące wypowiedzi syntezatora podczas jego działania, które są obiektem klasy Mouth należącej do silnika syntezy mowy umieszczone w pakiecie

com.cloudgarden.speech.userinterface.Mouth; 

usta.jpg

<font size="3">5.3. Rezultaty</span>

Poniżej przedstawiono prosty przykład reakcji formatki programu na wypowiedzenie następującej sekwencji komend głosowych. Po każdej syntezator wypowiada także stosowne komentarze oznaczające prawidłowe rozpoznanie polecenia.

<font size="3"> ```ada > open - circle - fill - blue < ``` </span>

reakcja_systemu.jpg

Ostatnia komenda zostaje uwidoczniona na pasku tytułu formularza Płótno.

<font size="4">6. Listing omawianego projektu</span>

Jest w fazie rozwoju więc trzeba go traktowac jako propozycja do rozbudowy i popracowania nad nim.

<font size="1"> ```java

import package examples;

//zaimportowane pakiety z potrzebnymi klasami
import examples.;
import examples.synthesis.
;

//podstawowe pakiety Javy
import java.awt.;
import java.awt.event.
;
import java.io.;
import java.net.
;
import java.util.;
import java.text.
;

//pakiety z modulami syntezy oraz rozpoznawania -------------------------------
import javax.speech.;
import javax.speech.recognition.
;
import javax.speech.synthesis.*;

import com.cloudgarden.speech.userinterface.*;
import com.cloudgarden.speech.CGEngineProperties;
import com.cloudgarden.speech.CGEngineCentral;

//-----klasy pomocnicze---

class SterowaneOkno extends Frame {

public static int RysowanyElement = 0;
public static int KolorPlotna = 0;
public int Rozmiar = 0;
public int Xoffset = 0;
public int Yoffset = 0;
public int x0 = 50;
public int y0 = 50;
public int szerokosc = 400;
public int wysokosc = 400;

//konstruktor
public SterowaneOkno(){
setTitle("Plotno");
setSize(500, 500);
setLocation(200,200);
setBackground(Color.gray);
}

//przykryta metoda paint klasy Frame
public void paint(Graphics powierzchnia){
switch(KolorPlotna){
case 0 : setBackground(Color.white);
break;
case 1 : setBackground(Color.gray);
break;
case 2 : setBackground(Color.blue);
break;
case 3 : setBackground(Color.green);
break;
case 4 : setBackground(Color.yellow);
break;
}//!switch

if(RysowanyElement == 0) return;

else{
   switch(RysowanyElement){
     case  1 : powierzchnia.drawLine(50 + Xoffset, 50 + Yoffset,
               400+ Rozmiar+ Xoffset, 400+ Rozmiar + Yoffset);
               break;
     case  2 : powierzchnia.drawOval(50 + Xoffset, 50 + Yoffset,
               400+ Rozmiar, 400+ Rozmiar);
               break;
     case  3 : powierzchnia.drawRect(x0 + Xoffset,y0 +
               Yoffset,szerokosc + Rozmiar,wysokosc+ Rozmiar);
               break;        
     case  4 : powierzchnia.fillRect(x0 + Xoffset,y0 + 
               Yoffset,szerokosc+ Rozmiar,wysokosc+ Rozmiar);
               break;
     case  5 : powierzchnia.fillOval(50 + Xoffset, 50 + Yoffset,
               400+ Rozmiar, 400+ Rozmiar);
               break;        
               }//!switch
      }//!else

}//!paint
}//!SterowaneOkno

//------------------------------------------------------------------------------

//główna klasa ----------------------------------------------------------------

public class RozpoznawanieOrazSynteza extends Thread {
//główne obiekty programu (speech syntezier & speech recognition)
public static Recognizer Rozpoznawca = null;
public static Synthesizer Syntezer = null;

//obiekty służące do sterowania
public static Frame OknoInformacji = null;
public static Label label1 = null;

//pomocnicze zmienne
public static boolean IsOpen = false;
public static boolean IsExist = false;
public static boolean IsResizing = false;
public static int OnClose = 0;

//czcionki
public static Font czcionka1 = new Font( "Times New Roman", Font.ROMAN_BASELINE, 24 );
public static Font czcionka2 = new Font( "Verdana", Font.ROMAN_BASELINE, 30 );
public static Font czcionka3 = new Font( "Dialog", Font.ROMAN_BASELINE, 32 );

//metody --------------------------------------------------------------------

//-------------------------------------
public static void Informacje(int czas){
OknoInformacji = new Frame("Program demonstrujacy sterowanie komputerem za pomoca głosu.");
label1 = new Label("", Label.CENTER);
label1.setBackground(Color.blue);
label1.setForeground(Color.white);
label1.setFont(czcionka3);
OknoInformacji.setSize(1024,768);
OknoInformacji.add(label1);
OknoInformacji.setVisible(true);
label1.setText("Pomysł dla serwisu 4programmers.net");
Wait(czas + 2000);
label1.setFont(czcionka3);
label1.setText("Program napisany przy użyciu pakietów Java Speech API");
Wait(czas);
label1.setFont(czcionka2);
label1.setText("Autor: Łukasz Mazur");
Wait(czas);
label1.setText("lukashm@wp.pl");
Wait(czas);
label1.setFont(czcionka1);
label1.setText("W oknie konfiguracji JSAPI Speech Engines proszę o ustawienie profili");
Wait(czas);
}//!Informacje

//-------------------------------------
public static void Wyjscie(int czas){
OknoInformacji.setVisible(true);
label1.setFont(czcionka3);
label1.setText("Pragram zakończony");
Wait(czas);
}//!Wyjscie

//-------------------------------------
public static boolean Wait(int milisec)
{
try{
sleep(milisec);
return true;
}//try
catch(InterruptedException e)
{
System.out.println("Wyjątek !");
return false;
}//catch
}//!Wait

//matoda main -----------------------------------------------------------------
public static void main(String[] args){
final SterowaneOkno Plotno = new SterowaneOkno();
final Graphics graph = null;
//Informacje(4000);

try{
   final SpeechEngineChooser wybieracz = SpeechEngineChooser.getAllEnginesDialog();
   //OknoInformacji.hide();
                  
   wybieracz.show();
   RecognizerModeDesc desc = wybieracz.getRecognizerModeDesc();
   Rozpoznawca = Central.createRecognizer(desc);
   Rozpoznawca.addResultListener(new ResultAdapter() {
   
   //wykonywanie akcji po klasyfikacji wypowiedzi -----------------------
   public void resultAccepted(ResultEvent e) {

   try{
       FinalResult r =(FinalResult)(e.getSource());
       Grammar gram = r.getGrammar();
                    
       if(gram instanceof RuleGrammar) {
          String tags[] = ((FinalRuleResult)r).getTags();
          if(tags == null || tags.length == 0) return;
          
          //--------------------------------------------------
          if(tags[0].equals("OPEN") && IsOpen ==false){
            Syntezer.speak("open window",null);
            Plotno.setVisible(true);
            IsOpen = true;}                              

          if(tags[0].equals("CLOSE") && IsOpen == true){
            Syntezer.speak("close window",null);
            Plotno.hide();
            IsOpen = false;}

          if(tags[0].equals("EXIT")){
            Syntezer.speak("Goodbyee !!!",null);
            Plotno.setTitle("Goodbyee !!!");
            Wyjscie(1000);
            System.exit(0);}
          
          //----------------------------------------------------------------   
          if(tags[0].equals("ONE"))   Syntezer.speak("You speak One",null);
          if(tags[0].equals("TWO"))   Syntezer.speak("You speak Two",null);
          if(tags[0].equals("THREE")) Syntezer.speak("You speak Three",null);
          if(tags[0].equals("FOUR"))  Syntezer.speak("You speak four",null);
          //----------------------------------------------------------------                               
          
          if(tags[0].equals("SIZE") && IsOpen == true ){   
            Syntezer.speak("I'am resizing ",null);                                                                                        if(IsResizing == false){
            Plotno.setLocation(50, 50);
            Plotno.setSize(800, 800);
            Plotno.setTitle("Plotno   -   resizing");
            IsResizing = true;
            }
          else{
            Plotno.setLocation(200, 200);
            Plotno.setSize(500, 500);
            Plotno.setTitle("Plotno   -   resizing");
            IsResizing = false;}}        

          if(tags[0].equals("REMOVE") && IsOpen == true ){   
            Syntezer.speak("I'am removing ",null);
            Plotno.setTitle("Plotno");
            Plotno.RysowanyElement = 0;
            Plotno.KolorPlotna = 0;
            IsExist = false;
            Plotno.repaint();                                  
            Plotno.removeAll();}

          //----------------------------------------------------------------   
          if(tags[0].equals("WHITE") && IsOpen == true ){   
            Syntezer.speak("Set white background ",null);
            Plotno.KolorPlotna = 0;;
            Plotno.setTitle("Plotno   -   white");
            Plotno.setVisible(true);
            Plotno.repaint();}    
                            
          if(tags[0].equals("GRAY") && IsOpen == true ){   
            Syntezer.speak("Set gray background ",null);
            Plotno.KolorPlotna = 1;;
            Plotno.setTitle("Plotno   -   gray");
            Plotno.setVisible(true);
            Plotno.repaint();}     
                           
          if(tags[0].equals("BLUE") && IsOpen == true ){   
            Syntezer.speak("Set blue background ",null);
            Plotno.KolorPlotna = 2;
            Plotno.setTitle("Plotno   -   blue");
            Plotno.setVisible(true);                        
            Plotno.repaint();}

          if(tags[0].equals("GREEN") && IsOpen == true ){   
            Syntezer.speak("Set gren background ",null);
            Plotno.KolorPlotna = 3;
            Plotno.setTitle("Plotno   -   green");
            Plotno.setVisible(true);                        
            Plotno.repaint();}

          if(tags[0].equals("YELLOW") && IsOpen == true ){   
            Syntezer.speak("Set yellow background ",null);                     
            Plotno.KolorPlotna = 4;
            Plotno.setTitle("Plotno   -   yellow");
            Plotno.setVisible(true);                        
            Plotno.repaint();

          //--------------------------------------------------
          if(tags[0].equals("LINE") && IsOpen == true ){   
            Syntezer.speak("I draw line",null);                                     
            Plotno.RysowanyElement = 1;
            Plotno.setTitle("Plotno   -   line");
            Plotno.setVisible(true);                        
            IsExist = true;
            Plotno.repaint();}

          if(tags[0].equals("CIRCLE") && IsOpen == true ){   
            Syntezer.speak("I draw circle",null);                                       
            Plotno.RysowanyElement = 2;
            Plotno.setVisible(true);                        
            IsExist = true;
            Plotno.repaint();}

          if(tags[0].equals("RECT") && IsOpen == true ){   
            Syntezer.speak("I draw rectangle",null);                         
            Plotno.RysowanyElement = 3;
            Plotno.setTitle("Plotno   -   rect");
            Plotno.setVisible(true);                        
            IsExist = true;                                     
            Plotno.repaint();}
    
          if(tags[0].equals("FILL") && IsOpen == true ){
            if(Plotno.RysowanyElement != 2 && Plotno.RysowanyElement != 3)
              Syntezer.speak("There is nothing to fill",null);

            if(Plotno.RysowanyElement == 3){
              Syntezer.speak("I fill rectangle",null);
              Plotno.RysowanyElement = 4;
              Plotno.setTitle("Plotno   -   fill rect");
              Plotno.setVisible(true);                        
              Plotno.repaint();}

            if(Plotno.RysowanyElement == 2){
              Syntezer.speak("I fill circle",null);
              Plotno.RysowanyElement = 5;
              Plotno.setTitle("Plotno   -   fill circle");
              Plotno.setVisible(true);                        
              Plotno.repaint();}
            }

          if((IsExist == false) && (tags[0].equals("LEFT") || 
            tags[0].equals("RIGHT") ||tags[0].equals("UP") || 
            tags[0].equals("DOWN") || tags[0].equals("GROW")|| 
            tags[0].equals("REDUCE"))) 
            Syntezer.speak("There is nothing to control",null);;                                                        

          if(tags[0].equals("GROW") && IsOpen == true && IsExist == true ){   
            Syntezer.speak("I'am growing figure",null);                          
            Plotno.Rozmiar += 50;
            Plotno.setTitle("Plotno   -   grow");
            Plotno.setVisible(true);                        
            Plotno.repaint();}

          if(tags[0].equals("REDUCE") && IsOpen == true&& IsExist == true ){ 
            Syntezer.speak("I'am reduce figure",null);                          
            Plotno.Rozmiar -= 50;
            Plotno.setTitle("Plotno   -   reduce");
            Plotno.setVisible(true);                        
            Plotno.repaint();}

          if(tags[0].equals("LEFT") && IsOpen == true&& IsExist == true  ){   
            Syntezer.speak("Moving left",null);                                          
            Plotno.Xoffset -= 50;
            Plotno.setTitle("Plotno   -   left");
            Plotno.setVisible(true);                        
            Plotno.repaint();}

          if(tags[0].equals("RIGHT") && IsOpen == true && IsExist == true ){   
            Syntezer.speak("Moving right",null);                                     
            Plotno.Xoffset += 50;
            Plotno.setTitle("Plotno   -   right");
            Plotno.setVisible(true);                        
            Plotno.repaint();}

          if(tags[0].equals("UP") && IsOpen == true&& IsExist == true  ){   
            Syntezer.speak("Moving up",null);                                         
            Plotno.Yoffset -= 50;
            Plotno.setTitle("Plotno   -   up");
            Plotno.setVisible(true);                        
            Plotno.repaint();}

          if(tags[0].equals("DOWN") && IsOpen == true && IsExist == true ){   
            Syntezer.speak("Moving down",null);
            Plotno.setTitle("Plotno   -   down");
            Plotno.Yoffset += 50;
            Plotno.setVisible(true);                        
            Plotno.repaint();}
            //Recognizer.deallocate();
          } 
        else{
          ResultToken[] toks = r.getBestTokens();
          String result = "";
          for(int i = 0; i < toks.length; i++) result+= " " +toks[i].getSpokenText();
          Syntezer.speak(result,null);
          }//!else
                    
        }//!try 
      catch(Exception e1) {
        e1.printStackTrace(System.out);
        }//!catch
      }
    }
  );

RecognizerAudioAdapter raud = new TestAudioListener();
Rozpoznawca.getAudioManager().addAudioListener(raud);
Rozpoznawca.addEngineListener(new TestEngineListener());
Rozpoznawca.allocate();
SpeakerProfile profil = wybieracz.getSpeakerProfile();
Rozpoznawca.getSpeakerManager().setCurrentSpeaker(profil);
Rozpoznawca.waitEngineState(Recognizer.ALLOCATED);

//----------Reguly gramatyczne-------------------
RuleSequence rs1 = new RuleSequence();

//sterowanie aplikacją
RuleTag regula1 = new RuleTag(new RuleToken("exit"),"EXIT");
RuleTag regula2 = new RuleTag(new RuleToken("open"),"OPEN");
RuleTag regula3 = new RuleTag(new RuleToken("close"),"CLOSE");

//po wypowiedzeniu zostanie powtorzone przez syntezator mowy
RuleTag regula4 = new RuleTag(new RuleToken("one"),"ONE");
RuleTag regula5 = new RuleTag(new RuleToken("two"),"TWO");
RuleTag regula6 = new RuleTag(new RuleToken("three"),"THREE");
RuleTag regula7 = new RuleTag(new RuleToken("four"),"FOUR");

//sterowanie wewnątrz aplikacji
RuleTag regula8 = new RuleTag(new RuleToken("size"),"SIZE");
RuleTag regula9 = new RuleTag(new RuleToken("remove"),"REMOVE");

//kolory tla plotna
RuleTag regula10 = new RuleTag(new RuleToken("white"),"WHITE");
RuleTag regula11 = new RuleTag(new RuleToken("gray"),"GRAY");
RuleTag regula12 = new RuleTag(new RuleToken("blue"),"BLUE");
RuleTag regula13 = new RuleTag(new RuleToken("green"),"GREEN");
RuleTag regula14 = new RuleTag(new RuleToken("yellow"),"YELLOW");

//rysowanie podstawowych figur geometrycznych
RuleTag regula15 = new RuleTag(new RuleToken("line"),"LINE");
RuleTag regula16 = new RuleTag(new RuleToken("circle"),"CIRCLE");
RuleTag regula17 = new RuleTag(new RuleToken("rect"),"RECT");
RuleTag regula18 = new RuleTag(new RuleToken("fill"),"FILL");

//manipulacja figuraimi
RuleTag regula19 = new RuleTag(new RuleToken("grow"),"GROW");
RuleTag regula20 = new RuleTag(new RuleToken("reduce"),"REDUCE");
RuleTag regula21 = new RuleTag(new RuleToken("left"),"LEFT");
RuleTag regula22 = new RuleTag(new RuleToken("right"),"RIGHT");
RuleTag regula23 = new RuleTag(new RuleToken("up"),"UP");
RuleTag regula24 = new RuleTag(new RuleToken("down"),"DOWN");
RuleAlternatives wariant = new RuleAlternatives();
wariant.append(regula2);
wariant.append(regula3);
wariant.append(regula4);
wariant.append(regula5);
wariant.append(regula6);
wariant.append(regula7);
wariant.append(regula8);
wariant.append(regula9);
wariant.append(regula10);
wariant.append(regula11);
wariant.append(regula12);
wariant.append(regula13);
wariant.append(regula14);
wariant.append(regula15);
wariant.append(regula16);
wariant.append(regula17);
wariant.append(regula18);
wariant.append(regula19);
wariant.append(regula20);
wariant.append(regula21);
wariant.append(regula22);
wariant.append(regula23);
wariant.append(regula24);

RuleGrammar gramatyka = Rozpoznawca.newRuleGrammar("Gramatyka_1");
gramatyka.setRule("Reguły_1",wariant,true);
gramatyka.setEnabled(true);

//-------------------------------------------------------------
//Aby komputer po rozpoznaniu wypowiedzianego slowa przetworzyl
//i wypowiedzial poprzez syntezator najbardziej zbliżone słowo
//do tego co rozpoznał należy odznaczyć poniższe dwie linie
//DictationGrammar dictGram = rec.getDictationGrammar(null);
//dictGram.setEnabled(true);
//-------------------------------------------------------------

Syntezer = Central.createSynthesizer(null);
Syntezer.addEngineListener(new TestEngineListener());
Syntezer.allocate();
Syntezer.waitEngineState(Synthesizer.ALLOCATED);
Syntezer.resume();
SynthesizerProperties syth_prop = Syntezer.getSynthesizerProperties();
syth_prop.setVoice(wybieracz.getVoice());

SpeakableListener spList = new TestSpeakableListener();
Syntezer.addSpeakableListener(spList);

SynthesizerProperties props = Syntezer.getSynthesizerProperties();

syth_prop.setVolume(1.0f);
syth_prop.setSpeakingRate(200.0f);

Rozpoznawca.commitChanges();
Rozpoznawca.requestFocus();
Rozpoznawca.resume();
Rozpoznawca.waitEngineState(Recognizer.DEALLOCATED);

Syntezer.waitEngineState(Synthesizer.QUEUE_EMPTY);
}catch(Exception e) {
e.printStackTrace(System.out);
}catch(Error e1) {
e1.printStackTrace(System.out);
}finally {
try{
Syntezer.deallocate();
Rozpoznawca.deallocate();
Rozpoznawca.waitEngineState(Synthesizer.DEALLOCATED);
Syntezer.waitEngineState(Synthesizer.DEALLOCATED);
}
catch(Exception e2) {
e2.printStackTrace(System.out);
}
System.exit(0);
}
}
}//!RozpoznawanieOrazSynteza

</span>


<font size="4"><b>7. Rozbudowa oraz możliwości dalszej modyfikacji kodu</b></span> 

Program ma stosunkowo prostą budowę pod względem składni kodu więc istnieje w miarę łatwe rozbudowanie tego prostego edytora graficznego o nowe funkcje, ponieważ ideą tego projektu było pokazanie zasady sterowania aplikacją za pomocą głosu i syntezy mowy toteż możliwości edytora są niewielkie.

<font size="3">7.1. Możliwości modyfikacji</span>

Aby powiększyć zbiór komend głosowych akceptowanych przez aplikację w trakcie działania musimy dodać nowe reguły gramatyczne

Umożliwia nam to klasa RuleTag, a dokonujemy tego w sposób następujący: 

*tworzymy nowy obiekt klasy RuleTag

```java
RuleTag nowe_reguła = new RuleTag(new RuleToken("komenda"),"TAG");

*po utworzeniu tego obiektu musimy uaktywnić w danej gramatyce istnienie tej reguły

RuleAlternatives ra = new RuleAlternatives();
ra.append(nowa_regula); 

*gdy system zostanie już powiadomiony o możliwości wystąpienia po rozpoznaniu mowy takiej komendy należy przypisać odpowiednią akcję do tego zdarzenia, które znajduje się w ciele funkcji

public void resultAccepted(ResulyEvent e)
  { 
    //akcja_systemu
  }

co demonstruję poniżej:

Grammar gram = r.getGrammar();//pobranie utworzonej gramatyki

jeżeli wypowiedziane słowo poddane klasyfikacji przez system jest instancją aktualnie zdefiniowanej gramatyki

if(gram instanceof RuleGrammar)

to przypisujemy TAG słowa ze zbioru komend ( gramatyki ) do zdefiniowanego indntyfikatora, który jest łańcuchem (klasa String)

String identyfikator[] = ((FinalRuleResult)r).getTags();

Pozostaje tylko napisanie kodu jaki się wykona po przypisaniu TAG-u do zmiennej identyfikator, której klasa posiada metode equals(), co ułatwi porównywanie łańcuchów.

if( identyfikator[0].equals("TAG"))
  {
    //kod_reakcji_systemu
  }

Po dokonaniu zmian podczas rekompilacji kodu proponuje użycia bardziej rozbudowanych i zaawansowanych środowisk programistycznych z możliwością dołączania wielu pakietów rozszerzających możliwości języka (JavaExtension), w opcjach jdk
np: JCreator Pro, JBuilder, RealJava czy FreeJava

<font size="3">7.2. Propozycje rozbudowy</span>

*zwiększenie zbioru rysowanych figur geometrycznych,
*wzbogacenie możliwości wypełniania tła oraz figur w pełnej palecie kolorów,
*więcej możliwości manipulacji na płótnie obiektami,
*większy zbiór komend dotyczących sterowania przebiegiem działania aplikacji
*możliwość wyboru konkretnej figury (ustawienie skupienia) przy większej ilości obiektów na płótnie.


all rights reserved
artykuł napisany dla serwisu 4programmers.net
wszelkie pytania i uwagi na: lukashm@wp.pl

... miłego experymentowania.
korekty nie wykluczone

lu_small.JPG

Łukasz M.

2 komentarzy

Tutorial jest po prostu świetny :-)
Pozdrawiam!

  1. Zapowiada się ciekawie :)

  2. Mała uwaga: tam gdzie masz kody, szczególnie ten jeden długi, fajnie by było jakbyś pozamieniał tabulatory na spacje (preferuję: 2 spacje == 1 tabulacja), dlatego, że kod staje bardzo nieczytelny na stronie.

  3. Jestem za zmianą nazwy tego artykułu z "JSAPI" na "Rozpoznawanie oraz synteza mowy z wykorzystaniem JSAPI".