July 31, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Processing Speech with Java

  • September 26, 2002
  • By Sams Publishing
  • Send Email »
  • More Articles »

This architecture reminds us of the way that Java itself is organized. For our programs to be portable, they must not have any concrete connection to the underlying hardware. This is the reason that abstraction layers such as the Java Speech APIs are so valuable. The same programming interface can support a number of underlying products in a very similar and sometimes identical manner.

The output of this program is audible. You should hear the phrase "Hello Unleashed" in your speakers or headphone. In addition, you will see the following line on the console for this application:

You should be hearing Hello, Unleashed Reader now.

Engine States

The engine itself moves through certain states. These states keep the engine behaving properly by allowing it to change its behavior when conditions change, as well as in response to user actions.

Four different allocation states exist that the engine passes through while doing its work: DEALLOCATED, ALLOCATED, ALLOCATING_RESOURCES, and DEALLOCATING_RESOURCES. The ALLOCATED state is the only one that allows any voice synthesis to be performed.

While the engine is in the allocated state, it can also have several substates. One substate is PAUSED, and its converse is RESUMED.

Another set of substates that are independent of the paused/resumed pair is the QUEUE_EMPTY and the QUEUE_NOT_EMPTY state. While there is data to be processed on the queue, the state is QUEUE_NOT_EMPTY. Otherwise, the state is QUEUE_EMPTY. These two states apply to speech synthesizing but not to recognition. Listing 12.2 shows a simple speech-synthesizing example with the states printed out.

Listing 12.2 The HelloUnleashedStates Class

/*
 * HelloUnleashedStates.java
 *
 * Created on March 5, 2002, 3:32 PM
 */

package unleashed.ch12;

/**
 *
 * @author Stephen Potts
 * @version
 */
import javax.speech.*;
import javax.speech.synthesis.*;
import java.util.Locale;

public class HelloUnleashedStates
{
  private static void printState(Synthesizer synth)
  {
    System.out.println("The current States are:");
    if(synth.testEngineState(Synthesizer.QUEUE_EMPTY))
      System.out.println("State = QUEUE_EMPTY");
    if(synth.testEngineState(Synthesizer.QUEUE_NOT_EMPTY))
      System.out.println("State = QUEUE_NOT_EMPTY");
    if(synth.testEngineState(Engine.ALLOCATED))
      System.out.println("State = ALLOCATED");
    if(synth.testEngineState(Engine.DEALLOCATED))
      System.out.println("State = DEALLOCATED");
    if(synth.testEngineState(Engine.ALLOCATING_RESOURCES))
      System.out.println("State = ALLOCATING_RESOURCES");
    if(synth.testEngineState(Engine.DEALLOCATING_RESOURCES))
      System.out.println("State = DEALLOCATING_RESOURCES");



    if(synth.testEngineState(Engine.RESUMED))
      System.out.println("State = RESUMED");
    if(synth.testEngineState(Engine.PAUSED))
      System.out.println("State = PAUSED");

  }

  public static void main(String args[])
  {
    try
    {
      // Create a synthesizer for English
      Synthesizer synth = Central.createSynthesizer(
      new SynthesizerModeDesc(Locale.ENGLISH));
      printState(synth);
      synth.allocate();
      printState(synth);
      synth.resume();
      printState(synth);

      // Speak the "Hello, Unleashed States" string
      synth.speakPlainText("Hello, Unleashed States!", null);
      printState(synth);

      // Wait till speaking is done
      synth.waitEngineState(Synthesizer.QUEUE_EMPTY);
      printState(synth);

      // release the resources
      synth.deallocate();
      printState(synth);
    } catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}

The output from running this example is shown in the following. We can see the progression of states as the synthesizer allocates the resources, reads in the phrase, places it on the queue, processes it off the queue, and deallocates the resources:

The current States are:
State = QUEUE_EMPTY
State = DEALLOCATED
State = RESUMED
The current States are:
State = QUEUE_EMPTY
State = ALLOCATED
State = RESUMED
The current States are:
State = QUEUE_EMPTY
State = ALLOCATED
State = RESUMED
The current States are:
State = QUEUE_NOT_EMPTY
State = ALLOCATED
State = RESUMED
The current States are:
State = QUEUE_EMPTY
State = ALLOCATED
State = RESUMED
The current States are:
State = QUEUE_EMPTY
State = DEALLOCATED
State = RESUMED

In addition to the previously printed lines, the phrase "Hello, Unleashed States" is audible. From this example, it is clear to see how the different states are changed independently of each other.

Allocating in a Thread

Speech programs require quite a bit of processing power. This might present a problem in the perceived performance of your program. One approach that can be used to address this performance is to allocate the engine in a thread separate from the main program, thereby improving perceived performance. All you have to do is declare an anonymous inner class that runs the allocate() method, trap any exceptions, and execute the start() method as shown here in Listing 12.3.

Listing 12.3 The HelloThread Example

/*
 * HelloThread.java
 *
 * Created on March 5, 2002, 3:32 PM
 */

package unleashed.ch12;

/**
 *
 * @author Stephen Potts
 * @version
 */
import javax.speech.*;
import javax.speech.synthesis.*;
import java.util.Locale;

public class HelloThread
{

  public static void main(String args[])
  {
    Engine eng;
    try
    {
      // Create a synthesizer for English
      final Synthesizer synth = Central.createSynthesizer(
      new SynthesizerModeDesc(Locale.ENGLISH));

      new Thread(new Runnable()
      {
        public void run()
        {
          try
          {
            synth.allocate();
          }catch (Exception e)
          {
            System.out.println("Exception " + e);
          }
        }
      }).start();

      //Add the rest of you initialization code here


      //wait for the engine to get ready
      synth.waitEngineState(Engine.ALLOCATED);
      synth.resume();

      // Speak the "Hello, Thread" string
      synth.speakPlainText("Hello, Thread!", null);
      System.out.println(
      "You should be hearing Hello, Thread now.");


      // Wait till speaking is done
      synth.waitEngineState(Synthesizer.QUEUE_EMPTY);

      // release the resources
      synth.deallocate();
    } catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}

There is no need to name this inner class, so we just begin the statement by declaring it with the keyword new:

      new Thread(new Runnable()
      {
        public void run()

We perform the allocate on the synthesizer handle just as we did before, only this is done inside the new thread:

            synth.allocate();

Because we are accessing synth from within another class, we have to declare it final. This is okay because we never need to reassign its value:

      final Synthesizer synth = Central.createSynthesizer(
      new SynthesizerModeDesc(Locale.ENGLISH));

We must coordinate the timing of the two threads for the example to work. Specifically, we must tell the main thread to wait until there is an allocated synthesizer to make calls to:

      synth.waitEngineState(Engine.ALLOCATED);

The output from this application is an audible voice in your speaker or headphone, along with the following line on the console:

You should be hearing Hello, Thread now.

In an example this trivial, a separate thread is obviously overkill. In many applications though, a complex GUI could be created in the time it takes to allocate the resources needed to support the speech engine.





Page 2 of 5



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel