朗誦控制程式
曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 October 2025
這是一篇以語音輸出為題,展示如何將一個很基本的語音輸出功能,發展成能讓系統僅只需操作 READ
1. 程式特性
這種應用範例,就如同電腦繪圖,看起來功能非凡,但通常都與電腦作業系統的基本性能高度相關,因此,發展出來的程式有時效性,而且只能配合硬體既有的能力來設計,程式移往另外一套電腦或系統,就不能使用。
此類程式也因此而不可能規劃出標準指令,最終發展出來的應用程式,都得因時、因地制宜,僅安排特別指令,或只有幾個簡單規則指令。
語音輸出本身,是一項很大的課題,我們不可能從最基本的硬體操控來設計程式,現行作業系統的設計觀念,提供了發展者可以叫用程式來使用的基本運用方法, Win32Forth 系統因此亦跟著建立起叫用作業系統現成程式的功能,前人詳細讀完作業系統叫用程式的說明資料後,才設計出相關的叫用程式,我們的程式才能夠跟著使用。
語音輸出的基本功能已經存在於 Win32Forth V6.14 版本的系統中。換句話說,語音輸出的功能要求,已經不勞我們傷神。但原始程式提供的功能,只能操作系統,讓它一句一句的講人話。我們的目標,則是憑此功能,設計出能夠一次就唸出整篇文章的程式來。
『原始功能』與『我們的目標』間的差異,從 FORTH 系統的傳統觀念來看,主要就是被唸的文字,應該放置在那裡?有所不同而已。因此,我們打算設計的程式,就得設計出文字處理功能。另外,它牽扯到檔案操作、字串處理、與一些簡單的語音輸出固定規則。
想設計出這個應用程式之前,必須先行了解已有程式的性能。由於沒有任可可以參考的書面文件,所有性能都要靠操作電腦、實測、記錄後,才能完成,要耗不少時間。詳細的測試過程,作者將其寫成一篇網文,張貼在作者的個人網頁上,供大家參考,是 2010-8-2 貼出的『因子分解法』。(補註 : 該文已從本網頁撤除)
該文只作經驗記錄,沒有刊出程式,本文才刊出實際可用程式,為了節省本書篇幅,僅節錄該文部份相關敘述於此,以便讓讀者明白,在程式中為何出現一些將待講文字進行特殊處理的修改設計?然後才交給電腦程式執行語音輸出。
『因子分解法』文內,是這樣寫的:
這半個月,正式開始了語音輸出的程式設計,經過長期的積極準備,心中早已有了如何進行這件工作的具體腹案,因此,進展順利,截至昨天為止,『唸文章』的功能幾乎全部實現了,唸的是英文,想唸出中文,以後再說。
我每天在住家附近散步時,都會經過一條名叫 Rothesay 的街道,我實在是不知道街名的正確發音為何?街的名稱只是一個名字,字典上查不到,因此,長期迷惑於這條街名的唸法。我們散步時,也曾經有外來訪客,拿著地址字條問過我們,這條街在那裡?我告訴他方向時,曾試著想聽對方如何唸這條街名,但經我一問,他反而猶豫不敢發音了,只笑一笑回答說:『第一次來,也不知道該如何唸』。換句話說,我以一個中國人的身份問問題,所有天生講英文的朋友,都可能失去回答此一問題的信心,英文名字確實是可以隨著不同地方的人而唸成不同發音的,我們現在居住的都市 Hamilton 的唸法,英式發音就與美式發音不同。台灣宜蘭五結有一個『利澤簡』,會講台語的同胞,也不見得能用台語直接講對這個地名,不信?您現在就試試看,然後打電話到利澤簡派出所或消防隊,驗證一下發音。
現在,我找到一個充滿信心、敢於直接回答,永遠都可以請教的對象了,它就是我設計出來的軟體。即時性控制(Real time control)是 FORTH 的專長,自從大系統不能直接執行P@、P! 來進行硬體實質控制之後,我們對 FORTH 的感覺,可以說是心都涼了半截,此次設計電腦語音輸出時,卻可以排除這種遺憾。目前我所辦到的功能是:執行 READ filename ,可以得到名為 filename 的文字檔案,並自動唸完整篇文章,執行期間始終呈現即時性響應的感覺。
作者戲稱這種程式設計的方法,是一種『吸功大法』,性質確實如此。因為實際的語音輸出功能,我們根本不需要設計,是別種 C 式程式語言,先行設計好的動態連結程式,幫了大忙。而 Win32Forth 系統,只設計了吸功大法所需要的指令,將別人辛苦練成的功夫,全面吸取來用,完成了語音輸出的所有功能,『因子分解法』中,是這樣寫的:
現代 FORTH 系統內的功能,還真像金庸的小說:『天龍八部』裡面,那位來自大理國的段譽,身懷北冥神功的絕技,具有化功大法,有武功的人一旦被他沾上,立刻吸來自用。您若精研 Win32Forth 裡面的特異功能,發現它可以呼叫 C 式語言設計出來之 .dll 程式庫內所有 API 功能程式時,可以了解它簡直就是這種吸功大法,此法還不可逆,暫時是別的程式語言難以辦到的,要不然別的程式語言,只好將整個 FORTH 系統全請去用。
我這次進行的語音輸出程式設計,正在全面使用此一吸功大法,是借力使力進行程式設計。練得這項吸功大法,確實耗費不少時間,我完整的練成過用 C 設計程式,然後由 FORTH 『吸用』的詳實步驟,確實像北冥神功,而且這種功夫練會一次就夠了,以後都是別人設計程式,我只需借力使力叫來使用,這種功夫好用得很。
關於利用 FORTH 直接測試語音輸出性能的部份,『因子分解法』中,是這樣寫的:
我在利用 SAPI(Sound Application Program Interface) 程式設計語音輸出時,首先要做的事情就是因子分解,『講人話,唸文章,配合視障操作而反應』就將整個打算設計的系統一分為三了,接下來繼續像樹狀分支一般的再將問題分解。講人話是別人的作品,我不用傷腦筋,我只需善用吸功大法,搞清楚吸來使用時該有的規矩就夠了。
即時性控制功能的妙用,就是能讓使用者即時的獲得執行結果,只要花一點點的時間,您就能體會這一套軟體講人話的效果到底如何了?憑良心講,如果不是利用 FORTH 具有直接操作,能立即得到結果的特性,我根本就不想設計這種系統,換成不用 FORTH 來進行這種發展,情況可以想像,絕對是笨透了。直接操作時,我將測試因子分成數個,先大略分成講中文與講英文,再細分成亂講、講單字、講連體字、講符號、與以上皆有的混講,還得把整個鍵盤全面試透,產生出來的效果完全不同,就算有說明書描述這些細節,都絕對不如我用手直接操作,用耳直接聽取來得乾脆,這豈不就是視障者的程式設計方式?有超出原本預料之處,立刻留下記錄,後續發展需要知道這些特性。
試完之後,發現光是講單字,配上少數幾個不同符號,音調的高低就可不同,因此,讓電腦講中文的五音就也可行了。我將希望測試出來的效果,再度因子分解成一份固定的表格,經過兩天的摸索,就把表格填妥了,這就是 FORTH 。我絕不相信現行世界上其他的任何程式語言,能這樣子完成測試, Python 不知道會不會講話? Java 呢? Matlab 呢?我的嬰兒期啟明 FORTH ,開機點兩點後就會講人話,還是不用花錢買的東西。
接著,我規劃出要系統講整句話的測試,先以直接鍵入(Type-in)的方式來測,隨後就又發現,句中、句尾使用不同標準符號時,又出現了不同效果,必須繼續因子分解,列出所有可能的符號與句子組合,然後,操作 FORTH 唸出句子,發現電腦裡面的這個美國人確實聰明,唸得真像英文,該問則問,該嘆就嘆,該輕必輕,但中文講得不太好,有點生硬,還需要更多時間來揣摩。
為了集中精力完成『唸文章』的要求,我停止了講中文的測試與發展,以免干擾接下來更為複雜一些的程式設計。我再度將發展項目因子分解,先將唸文章的方式分解成兩個,一是全面翻譯本文成可唸文,然後直接唸全篇文章,另一方式是逐句翻譯,逐句講。前者的程式較容易設計,可是系統耗用大量的記憶體,講出之話,段落不太分明,是其缺點。後者的程式要考慮之處較多,解決所遭遇的問題時,必須自己動腦筋想辦法,很有挑戰性,但系統耗用記憶體較少,將來與別的功能程式,存在同一 FORTH 系統中時,影響較小,而且,最重要的是,要電腦以這種方式講話時,才比較像真人在講話,段落分明,正確的設計應該要採用後者。另外,測試程式時,發現了無法追蹤下去的不合理問題,如果每次講完話後,不強行將講話區域的記憶體內容清理乾淨,前面留下來的東西,會自動縮黏到新話語的尾部,繼續發音講話,沒有 FORTH 的性能,想得知此事,難啊!
程式設計完成到這個階段時,前述的兩個指令就已經設計出來了,基本上,在 FORTH 系統內自行打轉,都沒有問題,我指的是:文章用 FORTH 系統的編輯程式自己準備,然後全面唸出來,變得輕而易舉。我休息了幾天,建了幾篇『伊索寓言』(The Fables of Aesop)中的短文來欣賞,剛開始聽講時不太習慣,必須調整講話速度,系統以設定負數值來調慢速度,我需要用 -3 的設定才能完全聽得清楚,聽過幾篇後,就完全能用正常速度聽講了。
我自學英文的過程中,一直強迫自己聽英文時不要看文字,才能加速適應不同人講的英文,對這套系統而言,情況相同。我為視障朋友發展系統,系統設計出來後,我當然必須假想自己就是一名視障者,根本無書可看,完全要用聽力,要把狀況想像成,我現在比視障者更可憐,連點字都摸不出來時該怎麼辦?讀者您讀到這裡時,您實在該慶幸自己還有這麼健全的視力來看我的文章,但也不要忽略我們還有許多視障同胞,需要我們的幫助。
唸文章的軟體工程到此並未結束,我想唸的文章,不能只是使用 FORTH 系統自行建立的檔案文章才能唸,必須接受所有可能呈現在這個系統裡面的各種檔案格式才行。這項要求是件新的挑戰,一般程式語言更難處理,因為不同編輯軟體,編寫出來的純文字檔案全都不同,我又得進行因子分解了。
平日,我除了搞 FORTH 與打網文外,只上網看新聞與搜索資料,我不是聖人,偶爾欣賞一下清涼的美女照片也是難免的,經常抓資料儲存的方式,都用微軟的編輯軟體 Word ,現在正在打網文,用的也是 Word 系統,至於其他的編輯軟體,沒有使用經驗,也不想勉強去接觸。因此,這次的因子分解,就將問題分解成四個,一是用 FORTH 編輯程式,就是那片綠葉子 WinEd 建的文字檔,二是用記事本建的文字檔,三是用文件檔案編輯程式(WordPad)建的文字檔,四是用文書編輯軟體(Word)建的文字檔。
以前,讀別人寫的英文電腦書籍時,讀到過強調這些編輯程式編出的文字檔案,內容會有所不同的特別聲明,自己卻不曾仔細的看個明白,到底其中有甚麼不同?現在,為了程式設計需要,不得不碰了,趁機讀個仔細,然後從中尋找規則,最後才能設計出適用於各種編輯軟體,所編寫出來之文字檔案的程式。
能看透別人的規格,是件痛快的事情, FORTH 才能輕而易舉的辦到,因為系統中早就有現成的指令可用,我已用了 30 年,但現在才拿來看所有的文字檔,看完了立刻發展程式解決問題,也把原來發展成功的唸文章程式,改寫成更具有彈性的設計,可以輕鬆插入新面臨的不同格式文字檔閱讀要求之指令,如此一來,程式就可以適用於所有可能出現的狀況了。
讀過了四種不同的文字檔結構內容,還都分別為它們設計完適應性的程式,現在已經不怕再有新規格文字檔案出現了,因為頭腦已經被磨練過幾次,知道如何適應新問題,換句話說,將來應該沒有我的啟明 FORTH 系統讀不成的文字檔案,這些文字檔案暫時還須要根據圖示後人工識別檔案的格式,將來,我希望能做到全由程式自己識別,這一部份的發展就可以全部告一段落了。
比較上述四種文字檔案的規格後,發現由 WordPad 建成的文字檔比由 Word 建成的文字檔還難處理,遭遇困難時,我仍是採用因子分解法,將問題因子化成一個一個的問題,然後逐個解決,這半個月,我不停的採用這種解決問題的方式進行程式設計,效果非凡,程式設計得非常順利。
我自己也急於想享受設計出來的成果,好像已經有許久沒見到純用 FORTH 控制東西的成果了,現在又重拾這種愉快,還是講人話的東西,我很喜歡。我知道這樣的設計,可以幫助台灣健康的同胞學習英文。
仔細追究這套方法,能用到的語音輸出指令,總共只有三個,首要的第一個,是一個前引指令 VOICE ,它的用法,如同必須與後續指令形成片語,後續有限的幾個語音輸出控制指令,才得以被執行。第二個是設定講話速度的指令,名為 SETRATE ,執行前賦予數值,就能改變講話速度,給負值就變慢,給正值就變快,執行讓速度變慢的操作範例如下:
-3 VOICE SETRATE
我們打算設計的程式,不需要動用到設定講話速度的 SETRATE 指令。讀者只須知其功能,而在需要調整講話速度時,直接操作使用。
結果,最後僅剩第三個唯一的指令,是我們想要設計程式時,可以使用的指令了。指令的名稱是 SPEAK ,在原始源程式檔案 EX_SAPI.F 中,所記錄的範例用法為:
0 0 u" Hello World!" drop voice speak
一個 FORTH 程式老手,可以僅憑對上述指令執行時,指令間參數傳遞狀況的分析,了解自己可以如何運用這些指令。上列程式對大家可能造成的困擾,來自於由原設計人狄湯瑪(Thomas Dixon)自行添加的 u” ………” 指令,在系統中可以透過直接操作 SEE u” 來看清它的詳細設計內容,它還是一個立即執行性的指令。
事實上,經過仔細追究後,想利用 SPEAK 指令的方式,沒有那麼複雜, SPEAK 指令僅只需要先行提供一個安放字串處的起始記憶體位址就夠了。原作者純粹只是為了讓 SPEAK 能夠直接接受就地處理出來的字串,才會那樣設計 u” 指令。
分析結果告訴我們,想要操控 SPEAK 指令,下命令的方式可以簡化成:
0 0 address voice speak
其中的 address 代表待講之話的字串所安放的起始位址,而上述指令執行完畢之後,堆疊上會留下一個代表執行順利成功或失敗的旗號值。
僅僅就是這麼一丁點的分析,就足夠讓我們進行自己打算設計之程式的規劃了。想讓系統自動的唸出整篇檔案文章,就得設計檔案讀取程式。讀到的文章要放在那裡?得自行規劃。
檔案內存在了會妨礙 SPEAK 指令執行或影響效果的符號,得設計程式先行將其處理掉。考慮到人機界面問題,試成程式之後,就改良內容,讓它顯得友善。這些要求,全由純粹傳統的 FORTH 程式設計方式來完成,非常簡單,因為, FORTH 就擅長於搞自動控制,而上述這些要求,就是最典型的自動控制問題,只不過控制對象是字串與語音輸出而已。
2. 唸檔案文章的程式
沒有留言:
張貼留言