那試試這個如何?
若電腦上已安裝 Autoit 可直接複製以下代碼,存成 .au3 之後便可立即執行體驗。
建議選擇可閱讀中文的 Sapi5 語音,從控制台找到文字轉語音,並進行設定。
我在幾年前完成它時,只有在 Win7 測試過,至少已經可以很穩定的在記事本閱讀長篇小說了。
代碼開始:
; 定義初始化函數
func init()
; 建立 Sapi 語音物件
global $sapi = ObjCreate("Sapi.SpVoice")
; 為了避免與其他正在使用 Sapi 的程式搶主要輸出管道,所以我們另外跟系統要一個新的輸出管道。
$sapi_audio = objCreate("sapi.spMMAudioOut")
; 然後將輸出管道設定成我們新建的管道
$sapi.audioOutputStream = $sapi_audio
; 然後這個新建的管道實力就可以不要了
$sapi_audio = null
; 設定尋找視窗標題的模式,目的等我們用到的時候再講
opt("WinTitleMatchMode", 4)
; 註冊 shift+esc 這個快速鍵來執行 onExit 這個函數,已關閉導讀軟體。 + 是 shift 的意思
hotKeySet("+{ESC}", "onExit")
; 讓語音說出軟體已啟動的提示
output("實驗型螢幕導讀軟體已啟動!若要關閉導讀軟體,請按 Shift+ESC 鍵。", 0)
endFunc
; 執行初始化動作
init()
; 主程式無窮迴圈開始
while 1
; 回圈內不斷執行 eventProcess 這個函數
eventProcess()
; 讓每一次都稍微休息一下,不要轉太快
sleep(20)
wEnd
; 定義 eventProcess 這個函數,函數內將檢查焦點的改變與控件資訊,最後通知對應事件的函數
func eventProcess()
; 定義 4 個靜態變數,以記錄當前視窗標題、焦點指向的控件,以及如果控件是編輯區,則記錄游標所在的行列。
static $window, $focus, $line, $col
; 收集要讓語音朗讀的文字
$output = ""
; 抓取當前視窗的視窗標題,那個 "active" 是目前顯示在畫面上視窗的意思,是設定視窗標題搜尋第 4 模式的功能
$gWindow = WinGetTitle("active", "")
; 檢查抓取的視窗標題與上一圈抓到的是否相同,若不同就表示視窗改變了,需通知對應函數
if $gWindow <> $window then
$window = $gWindow
$output &= onActiveWindowChange($window)
endIf
; 接著是視窗內焦點的位置,做法與視窗標題相同
$gFocus = ControlGetFocus("active", "")
if $gFocus <> $focus then
$focus = $gFocus
$output &= onFocusChange($focus)
endIf
; 接著檢查 $focus
select
; 如果是編輯區就抓取編輯區內的游標所在蘭列位置
case StringInStr($focus, "Edit")
$gLine = ControlCommand("active", "", $focus, "GetCurrentLine")
$gCol = ControlCommand("active", "", $focus, "GetCurrentCol")
; 接著依然跟上一圈的欄與列相比,若不同則通知對應函數
select
case $gLine <> $line
$output &= onLineChange($focus, $gLine)
case $gCol <> $col
$output &= onColChange($focus, $line, $gCol)
endSelect
$line = $gLine
$col = $gCol
endSelect
; 接著該輸出文字了,先檢查是否有累積到文字,如果沒有就不用輸出了,因為空字串是強制停止語音朗讀的意思
if $output then output($output)
endFunc
; 定義將文字傳給語音並輸出的函數,那個 $flag 不大記得具體意義了,只記得 3 是非同步輸出,也就是不等語音說完就離開這個函數,讓程式繼續執行的意思,前面有用到 0 可能是同步輸出,必須等語音閱讀完畢才會繼續程式的意思
func output($str, $flag = 3)
; 這裡用 execute 包起來,其實是因為懶得做 $sapi 萬一出意外的處理xd
execute($sapi.speak($str, $flag))
endFunc
; 當視窗改變的處理函數
func onActiveWindowChange($title)
return $title & " 視窗 "
endFunc
; 當視窗內焦點改變時的處理函數
func onFocusChange($focus)
select
; 這裡根據 $focus 的值,猜測可能的控件,並將中文名稱回傳
case StringInStr($focus, "Edit")
return " 編輯區 "
case StringInStr($focus, "List")
return " 清單 "
case StringInStr($focus, "Button")
return ControlGetText("active", "", $focus) & " 按鈕 "
case else
return "不明"
endSelect
endFunc
; 當編輯區游標所在行改變時的處理函數
func onLineChange($focus, $line)
; 抓取該編輯區內的所有文字
$text = ControlGetText("active", "", $focus)
; 抓取當前行的文字
$lineText = StringSplit($text, @crlf, 1)[$line]
if not $lineText then $lineText = "空行"
return $lineText
endFunc
; 當編輯區游標所在欄改變時的處理函數
func onColChange($focus, $line, $col)
$text = ControlGetText("active", "", $focus)
$lineText = StringSplit($text, @crlf, 1)[$line]
; 當游標到達行尾時,位置將會超過當前行的字串長度
if $col > StringLen($lineText) then return "行委"
return StringSplit($lineText, "")[$col]
endFunc
; 導讀軟體結束之前的處理函數
func onExit()
output("正在關閉實驗型螢幕導讀軟體 ...", 0)
$sapi = null
exit
endFunc