pocketsphinx应用编程
Jiangdg_VIP
http://blog.csdn.net/u012637501
译自:http://cmusphinx.sourceforge.net/wiki/tutorialpocketsphinx
Pocketsphinx是一个库,它依赖于另一个库-SphinxBase。在整个CMUSphinx工程项中,SphinxBase库为项目提供所需的常用功能。在正常使用PocketSphinx,需要同时安装Pocketsphinx和Sphinxbase库,通过安装这两个库我们可以实现在linux系统或windows系统中使用Pocketsphinx进行语音开发。
首先,我们需要将这两个库同时解压到同一目录下,如果是在windows中,还需要将'sphinxbase-X.Y"或者"pocketsphinx-X.Y"(其中X.Y为sphinxbase的版本号)改名为"sphinxbase"或"pocketsphinx"。
一.linux下环境搭建
即为Linux, Solaris, FreeBSD等系统安装 Pocketsphinx库、sphinxbase库。
1.安装sphinxbase库
(1)./autogen.sh:生成configure文件
(2)./configure
(3)make
(4)make install
通过上面4步,
sphinxbase库会被安装到/usr/local目录。这里需要注意的是,并不是每个系统都是自动从这个目录加载库。为了能够正确加载
这些库,我们可能需要配置路径去寻找共享库。这里提供两种方法:
a.修改/etc/ld.so.conf文件(永久有效)
$vim
/etc/ld.so.conf
添加:/usr/local/lib
/usr/local/lib/pkconfig
$ldconfig :使其生效。ldconfig将 /etc/ld.so.conf 列出的路径下的库文件缓存到
/etc/ld.so.cache以供使用系统是通过读取这个库列表文件来加快搜索
链接路径的速度的
b.修改环境变量(环境变量只对当前终端有效)
export LD_LIBRARY_PATH=/usr/local/lib
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
2.安装pocketsphinx库
(1)$./configure
(2)$make
(3)$make install
3.测试安装结果
为了测试是否安装成功,我们可以运行pocketsphinx_continuous,然后观察其是否能识别从麦克风输入的语音。
$
pocketsphinx_continuous
二、Pocketsphinx API核心概念
Pocketsphinx API能给让你很容易的使用语音识别的基本功能来开发应用程序,主要有以下特点:
1.由于使用抽象类,所以在源代码和二进制文件兼容方面,更能保持稳定。
2.因为它完全可重入,所以在同一进程中拥有多个编码器也不会出现问题。
3.在运行时,新的语言模型的接口(在sphinxbase)支持线性多模型插值。
4.它能大幅度减少代码量而且能明显减少内存消耗。
详见: http://cmusphinx.sourceforge.net/api/pocketsphinx/
三、基本用法-Hello World例子
关于如何使用API接口,这里有几个关键点需要知道:
(1)命令行通过外部<cmd_ln.h>来解析;
(2)一切都需要一个ps_decoder_t*作为第一个参数.
我们将通过一个简单的"
Hello World
"例子来说明新的API接口,这个例子为unix源文件和汇编程序。
1.建立一个C源文件hello_ps.c,下面是它的编译命令.
$gcc -o hello_ps hello_ps.c -DMODELDIR=\"`pkg-config --variable=modeldir pocketsphinx`\" `pkg-config --cflags --libs pocketsphinx sphinxbase`(编译源码,gcc的-D选项,指定宏定义)
使用上述命令对该C文件在Linxu平台上进行编译。如果编译出错,则说明你没有仔细阅读本教程或者没有正确安装pocketsphinx。比如说,pocketsphinx需要通过pkg-config系统来正确安装。为了检查pocketsphinx正确安装与否,只要在命令行运行 pkg-config --cflags --libs pocketsphinx sphinxbase 就可以从输出结果看出。即,运行命令:
$ pkg-config --cflags --libs pocketsphinx sphinxbase
pkg-config工具可以获得一个库的编译和连接等信息,如果输出:
-I /usr/local/include -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
2.初始化-创建配置对象cmd_ln_t
首先我们需要建立一个配置对象cmd_ln_t,可以通过一个标准模版(方法)来创建该配置对象,代码如下:
#include <pocketsphinx.h>
int
main(int argc, char *argv[])
{
ps_decoder_t *ps;
cmd_ln_t *config; //建立配置对象
cmd_ln_t
config = cmd_ln_init(NULL, ps_args(), TRUE, //cmd_ln_init() 函数
"-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;
}
源代码解析:
(1)cmd_ln_init() 函数接受字符串类型的参数,且参数的个数是可变,其作用是返回一个配置对象类型指针。第一个参数是之前要更新的cmd_ln_t *;第二个参数是
一个
参数定义的
数组—通过调用ps_args()来进行设置;第三个参数是一个标识,用来告诉参数解析器将是“严格”的。当参数设置为"TRUE"时,重复的参数或未知参数会导致解析失败。
(2)
MODELDIR宏被定义在GCC命令行中,主要是通过使用pkg-config 从PocketSphinx配置中获取modeldir变量来实现。
在Windows中,我们可以通过在源代码中添加一个预定义,
如:#define MODELDIR "c:/sphinx/model",(注意:
c:/sphinx/model为你安装models位置路径
)
3.初始化解码器
ps = ps_init(config);
if (ps == NULL)
return 1;
即使用ps_init(config)函数,将配置对象指针变量为传入参数来初始化解码器。
4.解码一个音频文件
解码的作用就是将音频文件转换为字符串。
因为现场音频输入或多或少会与平台有关,所以我们在这里我们只解码已经录制好的音频文件。现有的"turtle"语言模型只能识别非常简单的"机器人控制"指令,比如识别"go forward ten meters"短语。实际上,在PocketSphinx源码中已经包含了一个非常有用的音频文件,其包含已经包含了这个句
子。这个文件在
test/data/goforward.raw,我们可以将它拷贝到当下(项目)目录下。
当然,如果你想创建自己的版本,在录制音频文件的时候需要设置文件属性为:单声道、小端、16位PCM格式音频文件、频率16000HZ。音频文件录制(goforward.raw)好后,
为了正确使用它,需要做以下几步:
(1)打开文件,代码实现如下:
FILE *fh;
fh = fopen("goforward.raw", "rb");
if (fh == NULL) {
perror("Failed to open goforward.raw");
return 1;
}
(2)解码音频文件,调用ps_decode_raw()函数。
rv = ps_decode_raw(ps, fh, "goforward", -1);
if (rv < 0)
return 1;
(3)得到解码的结果(概率最大的字串),调用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);
5.解码内存中的音频数据
现在我们将再次解码相同的
test/data/goforward.raw音频文件,
这步同第四步作用一样,目的是解码音频文件。但是,
解码内存中的音频数据使用的是PocketSphinx
API接口,为此,我们使用ps_start_utt()函数开始说话。
(1)调用
ps_start_utt()函数开始说话
fseek(fh, 0, SEEK_SET);
rv = ps_start_utt(ps, "goforward");
if (rv < 0)
return 1;
(2)我们将每次从文件中读取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);
}
(3)使用ps_end_utt()方法标识说话结束
rv = ps_end_utt(ps);
if (rv < 0)
return 1;
(4)以相同精确的方式运行来检索假设的字符串:
hyp = ps_get_hyp(ps, &score, &uttid);
if (hyp == NULL)
return 1;
printf("Recognized: %s\n", hyp);
6.清理工作
使用ps_free()释放使用ps_init()返回的对象,不用释放配置对象。
四、源代码(hello_ps.c)
#include <pocketsphinx.h>
int main(int argc, char *argv[])
{
ps_decoder_t *ps;
cmd_ln_t *config;
FILE *fh;
char const *hyp, *uttid;
int16 buf[512];
int rv;
int32 score;
//1、初始化:创建一个配置对象 cmd_ln_t *
//cmd_ln_init函数第一个参数是我们需要更新的上一个配置,因为这里是初次创建,所以传入NULL;
//第二个参数是一个定义参数的数组,如果使用的是标准配置的参数集的话可以通过调用ps_args()去获得。
//第三个参数是是一个标志,它决定了参数的解释是否严格,如果为TRUE,那么遇到重复的或者未知的参
//数,将会导致解释失败;
//MODELDIR这个宏,指定了模型的路径,包括声学模型,语言模型和字典三个文件,是由gcc命令行传入,
//我们通过pkg-config工具从PocketSphinx的配置中去获得这个modeldir变量
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;
//2、初始化解码器(语言识别就是一个解码过程,通俗的将就是将你说的话解码成对应的文字串)
ps = ps_init(config);
if (ps == NULL)
return 1;
//3、解码文件流
//因为音频输入接口(麦克风)受到一些特定平台的影响,不利用我们演示,所以我们通过解码音频文件流
//来演示PocketSphinx API的用法,goforward.raw是一个包含了一些诸如“go forward ten meters”等用来
//控制机器人的短语(指令)的音频文件,其在test/data/goforward.raw。把它复制到当前目录
fh = fopen("/dev/input/event14", "rb");
if (fh == NULL) {
perror("Failed to open goforward.raw");
return 1;
}
//4、使用ps_decode_raw()进行解码
rv = ps_decode_raw(ps, fh, NULL, -1);
if (rv < 0)
return 1;
//5、得到解码的结果(概率最大的字串) hypothesis
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, NULL);
if (rv < 0)
return 1;
while (!feof(fh)) {
rv = ps_start_utt(ps, NULL);
if (rv < 0)
return 1;
printf("ready:\n");
size_t nsamp;
nsamp = fread(buf, 2, 512, fh);
printf("read:\n");
//我们将每次从文件中读取512大小的样本,使用ps_process_raw()把它们放到解码器中:
rv = ps_process_raw(ps, buf, nsamp, FALSE, FALSE);
printf("process:\n");
}
//我们需要使用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);
}
//6、清理工作:使用ps_free()释放使用ps_init()返回的对象,不用释放配置对象。
fclose(fh);
ps_free(ps);
return 0;
}