讓Java說話! (轉)
讓說話!
為你的Java 1.3 應用和Applet新增說話能力
概要
這篇文章中,Tony Loton展示了不使用和本地的,少於150行Java程式碼實現一個簡單的語音引擎。此外,他提供了一個小zip,裡面包含了使Java應用程式說話說需要的東西—僅僅用來娛樂或別的真正的應用程式。如果你剛剛接觸Java Sound ,這篇文章將是一個很好的介紹。(1800字)
作者:Tony Loton
譯者:Cocia Lin
為什麼要使你的程式說話呢?首先,為了娛樂,這很適合象遊戲這樣的娛樂程式。並且還有很多嚴肅的應用領域。我想這雖然不是視覺化介面的天生缺點,也是可用之處-- 或者過分一點 – 可以使你的眼睛離開你正在做的事情。
最近,我曾經應用一些技術在上獲得HTML和資訊的工作[請看 "Access the World's Biggest Database with Web DataBase Connectivity" (JavaWorld, March 2001)]。這讓我將那個工作和我的這個想法結合來建立一個說話的Web。這樣的一個瀏覽器可以使你聽到你喜歡的網站上的資訊摘錄 – 新聞標題,例如 – 就象在外邊溜狗或開車上班的途中收聽收音機一樣。當然,以現在的科技水平,你必須帶上你的筆記本和行動電話,但這些不切實際的設想在不久的將來,隨著應用Java技術的智慧電話的出現而變成現實,例如Nokia 9210(在美國叫9290).
也許對現在來說,能用的到的是一個e朗讀器,這也得謝謝JavaMail API.這樣的程式將定期的檢查你的電子,並且你的注意被一個聲音“你有新的,你要我給你朗讀嗎?”吸引。相近的,考慮語音提醒 – 當連線到你的日常管理程式時 –- 電腦大喊“不要忘了10分鐘後你和老闆的會議!”
回到這些想法,或者你有更好的自己的想法,我們繼續。我將演示怎樣將我提供的zip檔案新增的我們的工作中,這樣,如果你覺得這些東西太難了,你就可以直接執行而跳過實現細節。
測試語音引擎
為了使用這個語音引擎,你需要將jw-0817-javatalk.zip新增到你的classpath,在命令列方式或Java程式中使用com.lotontech.speech.Talker。
命令列方式,象下面這樣執行,輸入:
java com.lotontech.speech.Talker "h|e|l|oo"
在Java程式中,簡單的包含著兩行程式碼:
com.lotontech.speech.Talker talker=new com.lotontech.speech.Talker();
talker.sayPhone("h|e|l|oo");
這裡,你可能想知道命令列方式或sayPhoneWord(..)方法中的字串格式”h|e|l|oo”的含義。讓我來解釋。
語音引擎依靠聯結人的最小的語音單位的短聲音例子來工作 – 在這裡是英語。這些聲音例子,叫做音體變位(allophone),是一個,兩個,或三個字母識別符號的標誌。有些識別符號是明顯的,有些是不明顯的,你能從語音學裡看到這樣的”hello”的表示。
h --發音你能想到
e --發音你能想到
l --發音你能想到,但注意,我將兩個 “l” 變為一個”l”
oo -- “hello”的發音,不是”bot”的,也不是”too”的
這裡列出了能用到的音體變(allophone):
a -- 例如 cat
b -- 例如 cab
c -- 例如 cat
d -- 例如 dot
e -- 例如 bet
f -- 例如 frog
g -- 例如 frog
h -- 例如 hog
i -- 例如 pig
j -- 例如 jig
k -- 例如 keg
l -- 例如 leg
m -- 例如 met
n -- 例如 begin
o -- 例如 not
p -- 例如 pot
r -- 例如 rot
s -- 例如 sat
t -- 例如 sat
u -- 例如 put
v -- 例如 have
w -- 例如 wet
y -- 例如 yet
z -- 例如 zoo
aa -- 例如 fake
ay -- 例如 hay
ee -- 例如 bee
ii -- 例如 high
oo -- 例如 go
bb -- 變調b
dd --變調d
ggg -- 變調g
hh --變調h
ll --變調l
nn --變調n
rr -- 變調r
tt -- 變調t
yy --變調y
ar -- 例如 car
aer -- 例如 care
ch -- 例如 which
ck -- 例如 check
ear -- 例如 beer
er -- 例如 later
err -- 例如 later (longer sound)
ng -- 例如 feeding
or -- 例如 law
ou -- 例如 zoo
ouu -- 例如 zoo (longer sound)
ow -- 例如 cow
oy -- 例如 boy
sh -- 例如 shut
th -- 例如 thing
dth -- 例如 this
uh -- 變調 u
wh -- 例如 where
zh -- 例如 Asian
人說話的每一個句子都有單詞的升調和降調的變化。這個音調使說話聽起來自然,富有感情,並且可以從句子語調確定這是疑問句。如果你聽過Stephen Hawking的人造聲音,你就能夠理解我所說的了。考慮這兩個句子:
It is fake -- f|aa|k
Is it fake? -- f|AA|k
你也許猜想,使用升調的方法是用大寫字母。你要實際感受一下,我的提示是你要注意聽母音字母
這是你使用這個需要知道的全部了,但是如果你對引擎罩下面的東西感興趣,那麼繼續往下讀。
實現語音引擎
語音引擎僅僅需要一個類來實現,包含四個方法。它使用1.3的Java Sound API。我不想提供一個全面的Java Sound API教程,你將透過例子學習。你將發現不是有很多需要你來做,並且說明能告訴你需要知道的。
這裡是Talker類的基本定義:
package com.lotontech.speech;
import javax.sound.sampled.*;
import java.io.*;
import java.util.*;
import java.*;
public class Talker
{
private DataLine line=null;
}
如果你從命令列執行程式,下面的main(..)方法將作為一個入口服務。它取得命令列的第一個引數,如果有一個引數,將傳遞給sayPhoneWord(…)方法:
/*
*這個方法在命令列對一個指定的單詞發音
*/
public static void main(String args[])
{
Talker =new Talker();
if (args.length>0) player.sayPhoneWord(args[0]);
System.exit(0);
}
上面,SayPhoneWord(…)方法被main(…)方法呼叫,或者它被Java程式或Applet直接呼叫。它看起來比它本身難理解。本質上,它簡單的一步一步解釋單詞的語音變位allophone – 被”|”標誌分割的輸入文字 – 在把他們一個一個透過聲音輸出通道輸出。為了讓它聽起來更自然,合併每一個聲音的結尾到下一個聲音的開頭:
/*
*這個方法使輸入的單詞發音
*/
public void sayPhoneWord(String word)
{
// 為上一個聲音設定一個位元組陣列
byte[] previousSound=null;
//分割輸入字串
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
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檔案中讀出聲音的位元組資料。當我說檔案時,指的是我提供的zip檔案裡的資源。我強調這點差別,因為你得到JAR資源控制 – 使用getResource(..)方法 – 這不同於得到一個普通檔案的控制權。
為了有一個語音一個語音的讀出資料,轉換到聲音格式,例項化一個聲音輸出行(為什麼他們叫它SourceDateLine,我不知道),組合這些位元組資料,我在下面程式碼中提供給你說明:
/*
*這個方法從檔案中讀出單獨的語音並且構造一個位元組向量
*/
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)
{
// -- Output line not instantiated yet –
// -- Can we find a suitable kind of line? --
DataLine.Info outInfo = new DataLine.Info(SourceDataLine.class,
format);
if (!AudioSystem.isLineSupported(outInfo))
{
System.out.println("Line matching " + outInfo + " not supported.");
throw new Exception("Line matching " + outInfo + " not supported.");
}
開啟資源資料行(輸出行output line)
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 numBytesRemaining = numBytesRead;
}
//裁剪位元組陣列到正確尺寸
byte[] newData=new byte[numBytesRead];
for (int i=0; i
return newData;
}
catch (Exception e)
{
return new byte[0];
}
}
好了,就這麼多。一個150行的語音合成器程式碼,包括說明。但這沒有完全結束。
文字到語音(Text-to-speech)的轉換
用語音學方法表示單詞可能太乏味,所以,如果你想建立一個象我介紹一樣的應用程式,你要提供原始文字。
研究過這個題目後,我在zip檔案中提供一個實驗性的文字到語音的轉換類。當你執行它後,將輸出給你你想要的語音表示。
在命令列,執行text-to-speech轉換器:
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"
看到(並且聽到)這些:
i -> ii
like -> l|ii|k
to -> t|ouu
read -> r|ee|a|d
java -> j|a|v|a
world -> w|err|l|d
如果你想知道它是怎樣工作的,我將告訴你我的方法很簡單,應用通常的順序的一套文字替換規則。有幾個例子規則,你可能喜歡應用精神上的,順序的方式,這些例子是”ant”,”want”,”wanted”,”unwanted”,”unique”:
替換 "*unique*"使用 "|y|ou|n|ee|k|"
替換"*want*"使用"|w|o|n|t|"
替換"*a*"使用"|a|"
替換"*e*"使用"|e|"
替換"*d*"使用"|d|"
替換"*n*" 使用"|n|"
替換"*u*"使用"|u|"
替換"*t*" 使用"|t|"
”unwanted”的順序將是這樣:
unwanted
un[|w|o|n|t|]ed (rule 2)
[|u|][|n|][|w|o|n|t|][|e|][|d|] (rules 4, 5, 6, 7)
u|n|w|o|n|t|e|d (with surplus characters removed)
你應該看到,包含ant的want被用幾種不同的方式朗讀。你也應該看到對於unique來說的特殊情況,應該被讀為y|ou..而不是u|n….
電腦裡的精靈,對你說話
這篇文章提供一個可以使用Java 1.3執行的簡便的語音引擎。如果你研究這些程式碼,你可以得到一些關於JavaSound API播放片斷的有用方法。要想使這個引擎真的能用,你要思考文字到語音的轉換方法,這真的是我的一個主要想法。在這個引擎中,你要想出大量的文字轉換規則,還要應用一些好的優先順序。我希望你有比我強的毅力。
最後,你可能還記得我說過的Nokia 9210。我有一部,它支援Java,我決定用Java使它說話。我也想使applet(Java2的以前版本)在瀏覽器中說話。這些技術依靠J2SE 1.3聲音引擎,現在是可用的。一個不同的方法是需要的,依靠簡單的Java AudioCl介面。不像你想象的那樣簡單,但我在其上工作。
關於作者
Tony Loton為他的公司工作 – LOTONTech Limited – 提供軟體解決方案,顧問,培訓和技術寫作服務。寫作的小蟲好像在這一年裡始終叮咬著他,他為John Wiley & Sons 和 Wrox Press出版社寫書。
關於譯者
Cocia Lin()是程式設計師。它擁有學士學位,現在專攻Java相關技術,剛剛開始在領域折騰。
相關資源
You'll find the speech engine and related source code in the jw-0817-javatalk.file:
Go to java.sun.com's "Java Sound API" page for documentation, information, and FAQ:
To see how the speech engine will combine with Webpages to build my talking browser, read "Access the World's Biggest Database with Web Database Connectivity," Tony Loton (JavaWorld, March 2001):
.html">
In "Make an from Any Java Class with Java Reflection" (JavaWorld, December 2000), Tony Loton explains how to turn any Java class into an EJB, and any single-tier Java application into an enterprise application:
"Program Multimedia with JMF," Budi Kurniawan (JavaWorld):
Part 1: Go multimedia by learning how the Java Media compares to your stereo system (April 2001)
Part 2: Jump into Java Media Framework's important classes and interfaces (May 2001)
"Add capabilities to Java Sound with SPI," Dan Becker (JavaWorld, November 2000):
Sign up for the JavaWorld This Week free weekly email newsletter to learn what's new at JavaWorld:
You'll find a wealth of IT-related articles from our sister publications at IDG.net
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990043/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 讓Windows XP系統開口說話(轉)Windows
- 讓你的Windows XP系統開口說話(TTS)(轉)WindowsTTS
- 為Java說句公道話Java
- 小技巧194:讓蘋果電腦“說話”蘋果
- 10個說話技巧讓你的語言有魅力
- 什麼樣的技術,能讓黃河開口說話?
- 關於性的問題,讓大資料來說話大資料
- Elasticsearch 創始人 Shay Banon:讓資料自己說話Elasticsearch
- 個性化啟動之語音篇--讓你的 Linux 開口和你說話(轉)Linux
- 大白話說java併發工具類-Semaphore,ExchangerJava
- 大白話說java併發工具類-CountDownLatch,CyclicBarrierJavaCountDownLatch
- 專訪Elasticsearch創始人ShayBanon:讓資料自己說話Elasticsearch
- 深度學習的可解釋性研究(一):讓模型「說人話」深度學習模型
- Tracy智慧狗帶竟然能讓狗狗說人話?千萬別嚇到
- 白話說框架框架
- 話說快取快取
- Python文字轉換語音,讓你的文字會「說話」,摳腳大漢秒變撒嬌萌妹Python
- 想轉行人工智慧?哈佛博士後有話說...人工智慧
- 智雲通CRM:掌握信任公式:如何讓客戶願意聽你說話?公式
- 讓大資料十三五說話行業新時代真的來了大資料行業
- 告別生硬翻譯!AI谷歌翻譯:讓你的文字"說人話"AI谷歌
- 準確率97%,將大腦訊號轉化為語音,新型腦機介面讓失語者「說話」腦機介面
- 跟UI好好說話UI
- AI來實現程式碼轉換!Python轉Java,Java轉Go不在話下?AIPythonJavaGo
- 老話新說,RedHat 公司 推薦編譯核心方法(轉)Redhat編譯
- 《最後期限》:人怎樣對軟體工程說話 (轉)軟體工程
- ERP專案實施方 可曾有話要說 (轉)
- 華為雲大資料輕量級解決方案,讓資料“慧”說話大資料
- Up主已經開始鬼畜,騰訊開源「AniPortrait」讓照片唱歌說話AI
- 如果沒有Java 人類就像不會說話的嬰兒Java
- 轉:不可不說的Java“鎖”事Java
- 聽,是版本在說話
- ERP專案實施方 可曾有話要說(1)(轉)
- ERP專案實施方 可曾有話要說(2)(轉)
- 首個支援普通話和方言混說的TTS大模型:河南話、上海話、粵語說得溜TTS大模型
- 頭條面試歸來,有些話想和Java程式設計師說!面試Java程式設計師
- GraphPad Prism 10:讓資料說話,揭示科學奧秘 mac/win啟用版PHPMac
- 說說java的反射Java反射