View Javadoc

1   package org.wcb.plugins.speech;
2   /***
3    * Copyright (C) 1999  Walter Bogaardt
4    *
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
18   *
19   *  Project:   Home Automation Interface
20   *  Filename:  $Id: Talker.java,v 1.2 2004/07/22 03:17:56 wbogaardt Exp $
21   *
22   */
23  import javax.sound.sampled.SourceDataLine;
24  import javax.sound.sampled.AudioSystem;
25  import javax.sound.sampled.DataLine;
26  import javax.sound.sampled.Clip;
27  import javax.sound.sampled.AudioFormat;
28  import javax.sound.sampled.AudioInputStream;
29  import java.util.StringTokenizer;
30  import java.net.URL;
31   
32   public class Talker {
33       private SourceDataLine line = null;
34  
35       /***
36        * This method speaks a phonetic word specified on the command line.
37        */
38       public static void main(String args[]) {
39           Talker player=new Talker();
40           if (args.length>0) player.sayPhoneWord(args[0]);
41           System.exit(0);
42       }
43       /***
44        * This method speaks the given phonetic word.
45        */
46       public void sayPhoneWord(String word) {
47           // -- Set up a dummy byte array for the previous sound --
48           byte[] previousSound=null;
49           // -- Split the input string into separate allophones --
50           StringTokenizer st=new StringTokenizer(word,"|",false);
51           while (st.hasMoreTokens()) {
52               // -- Construct a file name for the allophone --
53               String thisPhoneFile=st.nextToken();
54               thisPhoneFile="/allophones/"+thisPhoneFile+".au";
55               // -- Get the data from the file --
56               byte[] thisSound=getSound(thisPhoneFile);
57               if (previousSound!=null) {
58                   // -- Merge the previous allophone with this one, if we can --
59                   int mergeCount=0;
60                   if (previousSound.length>=500 && thisSound.length>=500)
61                       mergeCount=500;
62                   for (int i=0; i<mergeCount;i++) {
63                       previousSound[previousSound.length-mergeCount+i]
64                       =(byte)((previousSound[previousSound.length
65                       -mergeCount+i]+thisSound[i])/2);
66                   }
67                   // -- Play the previous allophone --
68                   playSound(previousSound);
69                   // -- Set the truncated current allophone as previous --
70                   byte[] newSound=new byte[thisSound.length-mergeCount];
71                   for (int ii=0; ii<newSound.length; ii++)
72                       newSound[ii]=thisSound[ii+mergeCount];
73                   previousSound=newSound;
74               }
75               else
76                   previousSound=thisSound;
77           }
78           // -- Play the final sound and drain the sound channel --
79           playSound(previousSound);
80           drain();
81       }
82       /***
83        * This method plays a sound sample.
84        */
85       private void playSound(byte[] data) {
86           if (data.length>0) line.write(data, 0, data.length);
87       }
88       
89  /***
90   * This method flushes the sound channel.
91   */
92       private void drain() {
93           if (line!=null) line.drain();
94           try {Thread.sleep(100);} catch (Exception e) {}
95       }
96  /***
97   * This method reads the file for a single allophone and
98   * constructs a byte vector.
99   */
100      private byte[] getSound(String fileName) {
101          try {
102              URL url=Talker.class.getResource(fileName);
103              AudioInputStream stream = AudioSystem.getAudioInputStream(url);
104              AudioFormat format = stream.getFormat();
105              // -- Convert an ALAW/ULAW sound to PCM for playback --
106              if ((format.getEncoding() == AudioFormat.Encoding.ULAW) ||
107              (format.getEncoding() == AudioFormat.Encoding.ALAW)) {
108                  AudioFormat tmpFormat = new AudioFormat(
109                  AudioFormat.Encoding.PCM_SIGNED,
110                  format.getSampleRate(),
111                  format.getSampleSizeInBits() * 2,
112                  format.getChannels(),
113                  format.getFrameSize() * 2,
114                  format.getFrameRate(),
115                  true);
116                  stream = AudioSystem.getAudioInputStream(tmpFormat, stream);
117                  format = tmpFormat;
118              }
119              DataLine.Info info = new DataLine.Info(
120              Clip.class,
121              format,
122              ((int) stream.getFrameLength() * format.getFrameSize()));
123              if (line==null) {
124                  // -- Output line not instantiated yet --
125                  // -- Can we find a suitable kind of line? --
126                  DataLine.Info outInfo = new DataLine.Info(SourceDataLine.class,
127                  format);
128                  if (!AudioSystem.isLineSupported(outInfo)) {
129                      System.out.println("Line matching " + outInfo + " not supported.");
130                      throw new Exception("Line matching " + outInfo + " not supported.");
131                  }
132                  // -- Open the source data line (the output line) --
133                  line = (SourceDataLine) AudioSystem.getLine(outInfo);
134                  line.open(format, 50000);
135                  line.start();
136              }
137              // -- Some size calculations --
138              int frameSizeInBytes = format.getFrameSize();
139              int bufferLengthInFrames = line.getBufferSize() / 8;
140              int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
141              byte[] data=new byte[bufferLengthInBytes];
142              // -- Read the data bytes and count them --
143              int numBytesRead = 0;
144              if ((numBytesRead = stream.read(data)) != -1) {
145                  int numBytesRemaining = numBytesRead;
146              }
147              // -- Truncate the byte array to the correct size --
148              byte[] newData=new byte[numBytesRead];
149              for (int i=0; i<numBytesRead;i++)
150                  newData[i]=data[i];
151              return newData;
152          }catch (Exception e) {
153              return new byte[0];
154          }
155      }
156  }