Pocketsphinx API 核心理念
Pocketsphinx API 被設計是為了減輕編寫語音識別功能應用程序。
- 由於使用抽象類,所以在源代碼和二進制文件兼容方面,更能保持穩定。
- 因為它完全可重入,所以在同一進程中擁有多個編碼器也不會出現問題。
- 在運行時,新的語言模型的接口(在sphinxbase)支持線性多模型插值。
- 它能大幅度減少代碼量而且能明顯減少內存消耗。
相關文檔見:http://cmusphinx.sourceforge.net/api/pocketsphinx/
基本用法(hello world)
你需要知道幾個關鍵的點如何使用應用程序接口(API):
- 命令行通過外部<cmd_ln.h>來解析;
- 一切都需要一個ps_decoder_t*作為第一個參數。
為了說明新的接口,我們將通過一個簡單的“hello world”的例子。這個例子為unix源文件和匯編程序。我們將創建一個叫hello_ps.c的C源文件。我們使用如下命令來編譯:
gcc -o hello_ps hello_ps.c \
-DMODELDIR=\"`pkg-config --variable=modeldir pocketsphinx`\"
`pkg-config --cflags --libs pocketsphinx sphinxbase`
請注意,編譯錯誤在這里意味着你沒有仔細閱讀教程和不遵循上述安裝指南。例如pocketsphinx需要通過pkg-config系統來正確安裝。為了檢查pocketsphinx正確安裝與否,只要在命令行運行 pkg-config --cflags --libs pocketsphinx sphinxbase 就可以從輸出結果看出。
-I/usr/local/include/sphinxbase -I/usr/local/include/pocketsphinx
-L/usr/local/lib -lpocketsphinx -lsphinxbase –lsphinxad
pkg-config --variable=modeldir pocketsphinx
顯示結果輸出:/usr/local/share/pocketsphinx/model
補充:gcc -o recog recog.c \-DMODELDIR=\"/usr/local/share/pocketsphinx/model\" -I/usr/local/include/sphinxbase -I/usr/local/include/pocketsphinx -L/usr/local/lib -lpocketsphinx -lsphinxbase -lsphinxad
初始化
首先我們需要做的是創建一個配置對象,由於某種原因叫作cmd_ln_t。我們將按如下程序這樣做:
#include <pocketsphinx.h>
int main(int argc, char *argv[])
{
ps_decoder_t *ps;
cmd_ln_t *config;
config = cmd_ln_init(NULL, ps_args(), TRUE,
"-hmm",MODELDIR "/hmm/en_US/ hub4wsj_sc_8k",
"-lm", MODELDIR "/lm/en/turtle.DMP",
"-dict", MODELDIR "/lm/en/turtle.dic",
NULL);
if (config == NULL)
return 1;
return 0;
}
該cmd_ln_init()功能采用可變數目的空結束NULL的字符串參數。第一個參數是之前要更新的cmd_ln_t *。第二參數是一個數組參數定義—通過調用ps_args()來進行設置。第三個參數是一個標志,告訴參數解析器將是“嚴格”的—如果這是真的,然后重復的參數或未知參數會導致解析失敗。
該MODELDIR宏定義是在GCC命令行中使用pkg-config從PocketSphinx配置中獲得modeldir變量。在windows系統中,你可以簡單的增加一個預處理宏定義到代碼中,像這樣:
#define MODELDIR "c:/sphinx/model"
(無論你的模型安裝在哪個目錄下),現在,為了初始化解碼器,使用ps_init如下:
ps = ps_init(config);
if (ps == NULL)
return 1;
解碼文件流
現在現場音頻輸入受到一些特定平台的影響,這將限制我們自已去解碼音頻文件。“turtle”語言模式識別的一個非常簡單的“robot control”語言,其中包括一些諸如“go forward ten meters”的短語。事實上,一個音頻文件包含在PocketSphinx源代碼的句子中。你可以在test/data/goforward.raw中找到。把它復制到當前目錄。如果你想創建自己的版本,需要一個單聲道,小端,無頭16-bit PCM有符號,采樣率為16KHZ的音頻文件。
為了這樣做,首先我們打開文件:
FILE *fh;
fh = fopen("goforward.raw", "rb");
if (fh == NULL) {
perror("Failed to open goforward.raw");
return 1;
}
然后使用ps_decode_raw()進行解碼:
rv = ps_decode_raw(ps, fh, "goforward", -1);
if (rv < 0)
return 1;
現在,為了假設,我們可以使用ps_get_hyp():
char const *hyp, *uttid;
int rv;
int32 score;
hyp = ps_get_hyp(ps, &score, &uttid);
if (hyp == NULL)
return 1;
printf("Recognized: %s\n", hyp);
從內存中解碼音頻數據
現在我們將再次解碼相同的文件,但是使用API從內存塊中解碼音頻數據。在這種情況下,首先我們需要使用ps_start_utt()開始說話:
fseek(fh, 0, SEEK_SET);
rv = ps_start_utt(ps, "goforward");
if (rv < 0)
return 1;
我們將每次從文件中讀取512大小的樣本,使用ps_process_raw()把它們放到解碼器中:
int16 buf[512];
while (!feof(fh)) {
size_t nsamp;
nsamp = fread(buf, 2, 512, fh);
rv = ps_process_raw(ps, buf, nsamp, FALSE, FALSE);
}
然后我們需要使用ps_end_utt()去標記說話的結尾處:
rv = ps_end_utt(ps);
if (rv < 0)
return 1;
以相同精確的方式運行來檢索假設的字符串:
hyp = ps_get_hyp(ps, &score, &uttid);
if (hyp == NULL)
return 1;
printf("Recognized: %s\n", hyp);
清理
使用ps_free()對對象進行清理,返回使用ps_init()。你不應該釋放配置對象。
代碼清單
1 #include <pocketsphinx.h> 2 3 int main(int argc, char *argv[]) 4 { 5 ps_decoder_t *ps; 6 cmd_ln_t *config; 7 FILE *fh; 8 char const *hyp, *uttid; 9 int16 buf[512]; 10 int rv; 11 int32 score; 12 13 config = cmd_ln_init(NULL, ps_args(), TRUE, 14 "-hmm", MODELDIR "/hmm/en_US/hub4wsj_sc_8k", 15 "-lm", MODELDIR "/lm/en/turtle.DMP", 16 "-dict", MODELDIR "/lm/en/turtle.dic", 17 NULL); 18 if (config == NULL) 19 return 1; 20 ps = ps_init(config); 21 if (ps == NULL) 22 return 1; 23 24 fh = fopen("goforward.raw", "rb"); 25 if (fh == NULL) { 26 perror("Failed to open goforward.raw"); 27 return 1; 28 } 29 30 rv = ps_decode_raw(ps, fh, "goforward", -1); 31 if (rv < 0) 32 return 1; 33 hyp = ps_get_hyp(ps, &score, &uttid); 34 if (hyp == NULL) 35 return 1; 36 printf("Recognized: %s\n", hyp); 37 38 fseek(fh, 0, SEEK_SET); 39 rv = ps_start_utt(ps, "goforward"); 40 if (rv < 0) 41 return 1; 42 while (!feof(fh)) { 43 size_t nsamp; 44 nsamp = fread(buf, 2, 512, fh); 45 rv = ps_process_raw(ps, buf, nsamp, FALSE, FALSE); 46 } 47 rv = ps_end_utt(ps); 48 if (rv < 0) 49 return 1; 50 hyp = ps_get_hyp(ps, &score, &uttid); 51 if (hyp == NULL) 52 return 1; 53 printf("Recognized: %s\n", hyp); 54 55 fclose(fh); 56 ps_free(ps); 57 return 0; 58 }
高級用法
對於使用更復雜的老API,有一些明顯的不同地點:
- 得到部分和全部假設不再有單獨的功能;
- 詞語切分是通過訪問迭代器而不是返回的數組或列表;
- 語言模型的轉換是通過外部(<ngram_model.h>)。
首先這些都是簡單的。在這之前,你必須使用uttproc_partial_result()得到部分結果(例如在uttproc_end_utt()前),使用uttproc_result()得到全部結果。現在,ps_get_hyp()能工作在兩種方式下。
對於字的分割,API提供了一個遍歷所有字序列的迭代器對象。這個迭代器對象是一個抽象類,這個抽象類為一些訪問器提供獲取每個字的時間點,分數和(最有趣的)后驗概率。
最后,語言模型切換是完全不同的。解碼器總是與一個語言模型集對象聯系在一起。切換語言模型是通過以下:
- 得到一個句柄然后給語言模型集對象:ps_get_lmset()
- 選擇一個新的語言模型:ngram_model_set_select()
- 告訴解碼器語言模型集已更新:ps_update_lmset()
翻譯於2011年9月21日
參考官網:http://cmusphinx.sourceforge.net/wiki/tuturialpocketsphinx
