用Java实现语音引擎(2)
2008-02-23 09:57:51来源:互联网 阅读 ()
// 把输入字符串分割成单独的音素
StringTokenizer st=new StringTokenizer(word,"|",false);
while (st.hasMoreTokens())
{
// 为音素构造相应的文件名字
String thisPhoneFile=st.nextToken();
thisPhoneFile="/allophones/" thisPhoneFile ".au";
// 从声音文件读取数据
byte[] thisSound=getSound(thisPhoneFile);
if (previousSound!=null)
{
// 如果可能的话,把前一个音素和当前音素合并
int mergeCount=0;
if (previousSound.length>=500 && thisSound.length>=500)
mergeCount=500;
for (int i=0; i
{
previousSound[previousSound.length-mergeCount i]
=(byte)((previousSound[previousSound.length
-mergeCount i] thisSound[i])/2);
}
// 播放前一个音素
playSound(previousSound);
// 把经过截短的当前音素作为前一个音素
byte[] newSound=new byte[thisSound.length-mergeCount];
for (int ii=0; ii
newSound[ii]=thisSound[ii mergeCount];
previousSound=newSound;
}
else
previousSound=thisSound;
}
// 播放最后一个音素,清理声音通道
playSound(previousSound);
drain();
}
在sayPhoneWord()的后面,你可以看到它调用playSound()输出单个声音样本(即一个音
素),然后调用drain()清理声音通道。下面是playSound()的代码:
/*
* 该方法播放一个声音样本
*/
private void playSound(byte[] data)
{
if (data.length>0) line.write(data, 0, data.length);
}
下面是drain()的代码:
/*
* 该方法清理声音通道
*/
private void drain()
{
if (line!=null) line.drain();
try {Thread.sleep(100);} catch (Exception e) {}
}
现在回过头来看sayPhoneWord(),这里还有一个方法我们没有分析,即getSound()方法。
getSound()方法从一个au文件以字节数据的形式读入预先录制的声音样本。要了解读取数
据、转换音频格式、初始化声音输出行(SouceDataLine)以及构造字节数据的详细过
程,请参考下面代码中的注释:
/*
* 该方法从文件读取一个音素,
* 并把它转换成byte数组
*/
private byte[] getSound(String fileName)
{
try
{
URL url=Talker.class.getResource(fileName);
AudioInputStream stream = AudioSystem.getAudioInputStream(url);
AudioFormat format = stream.getFormat();
// 把一个ALAW/ULAW声音转换成PCM以便回放
if ((format.getEncoding() == AudioFormat.Encoding.ULAW) ||
(format.getEncoding() == AudioFormat.Encoding.ALAW))
{
AudioFormat tmpFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
format.getSampleRate(), format.getSampleSizeInBits() * 2,
format.getChannels(), format.getFrameSize() * 2,
format.getFrameRate(), true);
stream = AudioSystem.getAudioInputStream(tmpFormat, stream);
format = tmpFormat;
}
DataLine.Info info = new DataLine.Info(
Clip.class, format,
((int) stream.getFrameLength() * format.getFrameSize()));
if (line==null)
{
// 输出线还没有实例化
// 是否能够找到合适的输出线类型?
DataLine.Info outInfo = new DataLine.Info(SourceDataLine.class,
format);
if (!AudioSystem.isLineSupported(outInfo))
{
System.out.println("不支持匹配" outInfo "的输出线");
throw new Exception("不支持匹配" outInfo "的输出线");
}
// 打开输出线
line = (SourceDataLine) AudioSystem.getLine(outInfo);
line.open(format, 50000);
line.start();
}
int frameSizeInBytes = format.getFrameSize();
int bufferLengthInFrames = line.getBufferSize() / 8;
int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
byte[] data=new byte[bufferLengthInBytes];
// 读取字节数据,并计数
int numBytesRead = 0;
if ((numBytesRead = stream.read(data)) != -1)
{
int numBytesRmaining = numBytesRead;
}
// 把字节数据切割成合适的大小
byte[] newData=new byte[numBytesRead];
for (int i=0; i
newData[i]=data[i];
return newData;
}
catch (Exception e)
{
return new byte[0];
}
}
这就是全部的代码,包括注释在内,一个大约150行代码的语音合成器。
三、文本-语音转换
以语音元素的格式指定待朗读的单词似乎过于复杂,如果要构造一个能够朗读文本(比如
Web页面或Email)的应用,我们希望能够直接指定原始的文本。
深入分析这个问题之后,我在本文后面的ZIP文件中提供了一个试验性的文本-语音转换
类。运行这个类,它将显示出分析结果。文本-语音转换类可以从命令行执行,如下所示:
java com.lotontech.speech.Converter "hello there"
输出结果类如:
hello -> h|e|l|oo
there -> dth|aer
如果运行下面这个命令:
java com.lotontech.speech.Converter "I like to read JavaWorld"
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash
