Incorporating speech in a Swing application

1) First you need to download a speech engine. For example, you can download for free the Microsoft’s Speech SDK here. (Beware: version 5.1 is 68MB). Use the tools available to make sure the SDK is properly installed and trained to your voice.

2) Then get an implementation of Sun’s Java Speech API (JSAPI). The one I used in this example is from the CloudGarden. Follow the instructions on this page to find out how to install: copy the files jsapi.dll and jsapi.jar to JRE_HOME/lib/ext and adjust your classpath so that it includes jsapi.jar, necessary to compile following sample program.

3) This sample program was written by looking at the Cloud Garden examples. I dictated the following text to the app: “Last night, after a productive day of work, I joined my girlfriend in bed and quoted a very famous poet, who’s name escapes me. I said: ‘I feel great sexual desire for you right now.’. She said she’s not into poetry.”. The result (see window) was better than I expected.

Main.java:

import javax.speech.recognition.*;
import javax.speech.synthesis.*;
import javax.speech.*;
 
import javax.swing.text.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame
{ 
   TextComponentSpeechEnabler tcse;
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            tcse.deallocate();
            System.exit(1);
         }
      });
 
      getContentPane().setLayout(new BorderLayout(10, 10));
      JTextArea textArea = new JTextArea();
      textArea.setLineWrap(true);
 
      try {
         tcse = new TextComponentSpeechEnabler(textArea);
         getContentPane().add(BorderLayout.CENTER, tcse.getPanel());
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(500, 500);
      main.setVisible(true);
   }
}
 
class TextComponentSpeechEnabler
{
   static Recognizer recognizer;
 
   JPanel         textComponentPanel;
   JTextComponent textComponent;
   JTextField     speakerName;
   JTextArea      statusArea;
 
   public TextComponentSpeechEnabler(JTextComponent textComponent) throws Exception {
      this.textComponent = textComponent;
      
      createPanel();
      initializeRecognizer();
   }
  
   public void initializeRecognizer() throws Exception {
      recognizer = Central.createRecognizer(null);
      recognizer.addResultListener(new MyResultListener());
      recognizer.allocate();
      recognizer.waitEngineState(Recognizer.ALLOCATED);
 
      SpeakerManager speakerManager = recognizer.getSpeakerManager();
      SpeakerProfile[] speakers = speakerManager.listKnownSpeakers();
      for (int i=0; i<speakers.length; i++) {
         addStatusText("Found profile of " + speakers[i].getName());
      }
 
      addStatusText("Current profile is " +
                                   speakerManager.getCurrentSpeaker().getName());
      speakerName.setText(speakerManager.getCurrentSpeaker().getName());
      speakerManager.setCurrentSpeaker(speakers[0]);
 
      DictationGrammar dictation = recognizer.getDictationGrammar("dictation");
      dictation.setEnabled(true);
 
      recognizer.commitChanges();
 
      recognizer.requestFocus();
      recognizer.resume(); 
   }
 
   public void deallocate() {
      System.out.println("deallocated!");
      try {
         recognizer.deallocate();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   }
 
   private void createPanel() {
      textComponentPanel = new JPanel(new BorderLayout(10, 10));
      JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
      northPanel.add(new JLabel("Speaker name:  "));
      speakerName = new JTextField(25);
      northPanel.add(speakerName);
      textComponentPanel.add(BorderLayout.NORTH, northPanel);
      textComponentPanel.add(BorderLayout.CENTER, new JScrollPane(textComponent));
 
      statusArea = new JTextArea(10, 50);
      textComponentPanel.add(BorderLayout.SOUTH, new JScrollPane(statusArea));   
   }
  
   public JPanel getPanel() {
      return textComponentPanel;
   }
 
   public void addStatusText(final String s) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            statusArea.append(s + "n");
            statusArea.setCaretPosition(statusArea.getDocument().getLength());
         }
      });
   }
 
   public void addText(final String s) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            textComponent.setText(textComponent.getText() + s);
            textComponent.setCaretPosition(textComponent.getDocument().getLength());
         }
      });
   }
  
   class MyResultListener extends ResultAdapter {
      public void resultRejected(ResultEvent e) {
      }
 
      public void resultCreated(ResultEvent e) {
      }
 
      public void resultUpdated(ResultEvent e) {
      }
 
      public void resultAccepted(ResultEvent e) {
         FinalResult finalResult = (FinalResult)(e.getSource());
         ResultToken tokens[] = null;
         tokens = finalResult.getBestTokens();
 
         StringBuffer sb = new StringBuffer();
         for (int i=0; i<tokens.length; i++) {
            sb.append(tokens[i].getSpokenText() + " ");  
         }
         addText(sb.toString());
         addStatusText("" + finalResult);
      }
   }
}

Feel free to mail me any improvements!

Updating a row in the resultset

You can always execute an UPDATE statement with the new changes. JDBC2.0 provides another way. It allows you to make changes to a row from ResultSet and reflect the changes in the database.

before:

+---------+--------+---------------------+--------+
| orderid | custid | timestamp           | status |
+---------+--------+---------------------+--------+
|       1 |      1 | 2001-10-13 17:33:11 |      0 |
|       2 |      1 | 1972-12-12 00:00:00 |      1 |
|       3 |      1 | 1999-02-01 00:00:00 |      2 |
|       4 |      2 | 2001-05-01 00:00:00 |      0 |
+---------+--------+---------------------+--------+

Main.java:

import java.util.*;
import java.text.*;
import java.sql.*;
  
public class Main {
   public static void main(String []args) throws Exception {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                                               ResultSet.CONCUR_UPDATABLE);
 
         ResultSet rs = stmt.executeQuery("SELECT * FROM orders WHERE orderid = 1"); 
 
         if (rs.next()) {
            // change the status column
            rs.updateInt("status", 1);
            // store changes in database
            rs.updateRow();
         }
 
         stmt.close();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

after:

+---------+--------+---------------------+--------+
| orderid | custid | timestamp           | status |
+---------+--------+---------------------+--------+
|       1 |      1 | 2001-10-13 17:33:11 |      1 |
|       2 |      1 | 1972-12-12 00:00:00 |      1 |
|       3 |      1 | 1999-02-01 00:00:00 |      2 |
|       4 |      2 | 2001-05-01 00:00:00 |      0 |
+---------+--------+---------------------+--------+

Storing and retrieving a serialized object in a database table

As an example, I created the following table:

mysql> create table objects (objectid int(10) not null auto_increment, 
name varchar(30), data blob, length int(6), primary key(objectid));
Query OK, 0 rows affected (0.12 sec)
 
mysql> desc images;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| objectid | int(10)     |      | PRI | NULL    | auto_increment |
| name     | varchar(30) | YES  |     | NULL    |                |
| length   | int(6)      | YES  |     | NULL    |                |
| data     | blob        | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+

In the following code, a string array is serialized and stored in the objects table.

Main.java:

import java.sql.*;
import java.io.*;  
 
public class Main {
   public static void main(String []args) {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         String stringArray[] = { "this", "is", "a", "test" };       
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos);
         oos.writeObject(stringArray);
 
         PreparedStatement insertStmt = conn.prepareStatement(
                                       "INSERT INTO objects (name, length, data) " +
                                       "VALUES (?, ?, ?)");
         insertStmt.setString(1, "stringarray");
         byte[] buffer = baos.toByteArray();
         insertStmt.setInt(2, buffer.length);
         insertStmt.setBytes(3, buffer);
         insertStmt.execute();
 
         oos.close();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

To read it back in:
Main.java:

import java.sql.*;
import java.io.*;  
 
public class Main {
   public static void main(String []args) {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery("SELECT name, length, data FROM objects");
      
         if (rs.next()) {
            String name = rs.getString("name");
            int length = rs.getInt("length");
            byte[] buf = rs.getBytes("data");
 
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(buf));
            String[] stringArray = (String[]) ois.readObject();
            ois.close();
 
            for (int i=0; i<stringArray.length; i++) {
               System.out.println(stringArray[i]);
            }
         }
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

outputs:

this
is
a
test

Moving the database resultset cursor backward and forward

With JDBC 2.0, you can use scrollable resultsets.

The following example operates on this table and will read the resultset backwards.

mysql> select * from customers;
+--------+-----------------------+-------------------------------+
| custid | name                  | email                         |
+--------+-----------------------+-------------------------------+
|      1 | Joris Van den Bogaert | joris_vandenbogaert@yahoo.com |
|      2 | Alicia Kolesnikova    | alicia@esus.com               |
+--------+-----------------------+-------------------------------+

Main.java:

import java.util.*;
import java.text.*;
import java.sql.*;
  
public class Main {
   public static void main(String []args) throws Exception {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
                                               ResultSet.CONCUR_READ_ONLY);
 
         ResultSet rs = stmt.executeQuery("SELECT * FROM customers"); 
         rs.afterLast();
         while (rs.previous()) {
            System.out.print(rs.getInt("custid"));
            System.out.print("  " + rs.getString("name"));
            System.out.println("  " + rs.getString("email"));
         }
 
         stmt.close();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

outputs:

2  Alicia Kolesnikova  alicia@esus.com
1  Joris Van den Bogaert  joris_vandenbogaert@yahoo.com

Programmatically get information about your database driver

Main.java:

import java.util.*;
import java.sql.*;
  
public class Main {
   public static void main(String []args) throws Exception {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
         DatabaseMetaData dbMeta = conn.getMetaData(); 
 
         System.out.println ("Database Product Name : " +
                                 dbMeta.getDatabaseProductName());
         System.out.println ("Database Product Version: " +
                                 dbMeta.getDatabaseProductVersion());
         System.out.println ("Database Driver Name : " +
                                 dbMeta.getDriverName());
         System.out.println ("Database Driver Version : " +
                                 dbMeta.getDriverVersion());
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

outputs:

Database Product Name : MySQL
Database Product Version: 3.23.42
Database Driver Name : Mark Matthews' MySQL Driver
Database Driver Version : 2.0.4

Retrieving binary data from a database table

This example will read images stored in a table.

Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.text.*;
import java.sql.*;
import java.io.*;  
 
public class Main extends JFrame {
   public Main() {
      getContentPane().setLayout(new FlowLayout(FlowLayout.LEFT));
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      ArrayList al = getImagesFromDB();
      Iterator iter = al.iterator();
      while (iter.hasNext()) {
         getContentPane().add((JLabel) iter.next());
      }
   }
 
   public ArrayList getImagesFromDB() {
      ArrayList al = new ArrayList();
 
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery("SELECT name, length, data FROM images");
      
         while(rs.next()) {
            String name = rs.getString("name");
            int length = rs.getInt("length");
            byte[] buf = rs.getBytes("data");
 
            ImageIcon ii = new ImageIcon(buf);
            JLabel label = new JLabel(name, ii, SwingConstants.CENTER); 
            al.add(label);
         }
      }
      catch(Exception e) {
         e.printStackTrace();
      }
 
      return al;
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(300, 300);
      main.setVisible(true);
   } 
}

Saving binary data (eg. images) in a database table

As an example, I created the following table:

mysql> create table images (imageid int(10) not null auto_increment, 
name varchar(30), data blob, length int(6), primary key(imageid));
Query OK, 0 rows affected (0.12 sec)
 
mysql> desc images;
+---------+-------------+------+-----+---------+----------------+
| Field   | Type        | Null | Key | Default | Extra          |
+---------+-------------+------+-----+---------+----------------+
| imageid | int(10)     |      | PRI | NULL    | auto_increment |
| name    | varchar(30) | YES  |     | NULL    |                |
| length  | int(6)      | YES  |     | NULL    |                |
| data    | blob        | YES  |     | NULL    |                |
+---------+-------------+------+-----+---------+----------------+

Main.java:

import java.util.*;
import java.text.*;
import java.sql.*;
import java.io.*;  
 
public class Main {
   public static void main(String []args) throws Exception {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         File[] files = new File(".").listFiles(new FilenameFilter() {
            public boolean accept(File dir, String name) {
               if (name.endsWith(".gif")) {
                  return true;
               }
               return false;
            }
         });
 
         for (int i=0; i<files.length; i++) {
            FileInputStream fis=new FileInputStream(files[i]);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte buf[]=new byte[1024];
            int length=-1;
            while((length=fis.read(buf))!=-1) {
               baos.write(buf,0,length);
            }
 
            PreparedStatement insertStmt = conn.prepareStatement(
                                          "INSERT INTO images (name, length, data) " +
                                          "VALUES (?, ?, ?)");
            insertStmt.setString(1, files[i].getName());
            insertStmt.setInt(2, (int) files[i].length());
            insertStmt.setBytes(3, baos.toByteArray());
            insertStmt.execute();
 
            fis.close();
            baos.close();
        }
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

Programmatically getting column information of a database table in Java

You can get meta information on a table by invoking getMetaData on a connection. With the method getColumns, you get back a number of rows containing information about each column of a particular table. Check the api to see what you can query.

Main.java:

import java.util.*;
import java.text.*;
import java.sql.*;
  
public class Main {
   public static void main(String []args) throws Exception {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         DatabaseMetaData dbMeta = conn.getMetaData();
         ResultSet rs = dbMeta.getColumns(null, "", "orders", "%");
 
         while (rs.next()) {
            String column_name      = rs.getString(4);
            String sql_type         = rs.getString(5);
            String type_name        = rs.getString(6);
            String column_size      = rs.getString(7);
            String ordinal_position = rs.getString(17);
            String is_nullable      = rs.getString(18);
 
            System.out.println("Column " + column_name);
            System.out.println("tSQL_TYPE          = " + sql_type);
            System.out.println("tTYPE_NAME         = " + type_name);
            System.out.println("tCOLUMN_SIZE       = " + column_size);
            System.out.println("tORDINAL_POSITION  = " + ordinal_position);
            System.out.println("tIS_NULLABLE       = " + is_nullable);
         } 
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

outputs:

Column orderid
	SQL_TYPE          = 4
	TYPE_NAME         = int
	COLUMN_SIZE       = 10
	ORDINAL_POSITION  = 1
	IS_NULLABLE       = NO
Column custid
	SQL_TYPE          = 4
	TYPE_NAME         = int
	COLUMN_SIZE       = 10
	ORDINAL_POSITION  = 2
	IS_NULLABLE       = YES
Column timestamp
	SQL_TYPE          = 93
	TYPE_NAME         = datetime
	COLUMN_SIZE       = 19
	ORDINAL_POSITION  = 3
	IS_NULLABLE       = YES
Column status
	SQL_TYPE          = -6
	TYPE_NAME         = tinyint
	COLUMN_SIZE       = 4
	ORDINAL_POSITION  = 4
	IS_NULLABLE       = YES

Inserting a value with type datetime in a column using JDBC

This example assumes that you have set up a table orders.

orders looks like this:

mysql> desc orders;
+-----------+------------+------+-----+---------+----------------+
| Field     | Type       | Null | Key | Default | Extra          |
+-----------+------------+------+-----+---------+----------------+
| orderid   | int(10)    |      | PRI | NULL    | auto_increment |
| custid    | int(10)    | YES  | MUL | NULL    |                |
| timestamp | datetime   | YES  |     | NULL    |                |
| status    | tinyint(4) | YES  |     | NULL    |                |
+-----------+------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

The following example will populate this table with 4 rows, date is passed in as a java.util.Date and converted to a java.sql.Timestamp.

Main.java:

import java.util.*;
import java.text.*;
import java.sql.*;
  
public class Main {
   public static void main(String []args) throws Exception {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         Statement stmt = conn.createStatement();
         DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
         createOrder(stmt, 1, new java.util.Date(), 0);
         createOrder(stmt, 1, df.parse("12/12/1972"), 1);
         createOrder(stmt, 1, df.parse("2/1/1999"), 2);
         createOrder(stmt, 2, df.parse("5/01/2001"), 0);
         stmt.close();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
 
   public static void createOrder(Statement stmt, 
                                  int custid, java.util.Date timestamp, int status) 
                                           throws SQLException, ParseException {
      java.sql.Timestamp sqlDateTime = new java.sql.Timestamp(timestamp.getTime());
      stmt.executeUpdate("INSERT INTO orders (custid, timestamp, status) " + 
                         "VALUES (" + custid + ", '" +
                                  sqlDateTime + "', " + status + ")");

   }
}

This is the result:

mysql> select * from orders;
+---------+--------+---------------------+--------+
| orderid | custid | timestamp           | status |
+---------+--------+---------------------+--------+
|       1 |      1 | 2001-10-13 17:33:11 |      0 | 
|       2 |      1 | 1972-12-12 00:00:00 |      1 | 
|       3 |      1 | 1999-02-01 00:00:00 |      2 | 
|       4 |      2 | 2001-05-01 00:00:00 |      0 | 
+---------+--------+---------------------+--------+
4 rows in set (0.00 sec)

Create a database using JDBC

JDBC is not intended to manage databases. It’s used to connect to them. Creating a database is not standard, every DMBS has another way of doing it. If you’re willing to give up portability accross databases, there are ways. For example, in mysql, you can connect to an existing database and invoke executeUpdate to create a new database.

Main.java:

import java.util.*;
import java.sql.*;
  
public class Main {
   public static void main(String []args) throws Exception {
      try {
         Class.forName("org.gjt.mm.mysql.Driver").newInstance();
         Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.0.1/esus", 
                                                       "joris",
                                                       "mypass");
 
         Statement stmt = conn.createStatement();
         stmt.executeUpdate("create database testdb");
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}