在搭建应用之前需要以下准备工作:
Pocketsphinx API旨在简化应用程序中语音识别器功能的使用,它具有一下特点:
1.由于使用抽象类型,它在源代码和二进制兼容性方面很可能保持稳定;
2.它是完全可重入的,因此在同一个过程中有多个解码器是没有问题的;
3.它允许大幅减少代码占用空间,而且支持适度但显著减少内存消耗。
这个小程序的基本功能是:读取一个wav语音文件,利用pocketSphinx API将该语音文件转化为文字。
由于我们需要用Visual Studio 2017编写c++程序,因此需要为VS安装C++语言包。
新工程创建完毕,在正式开始写代码之前,我们需要配置include路径以及linker(链接)路径,从而使得我们的程序可以引用pocketsphinx。
#include
#define MODELDIR "D:/sphinx/pocketsphinx/model"
int main(int argc, char *argv[]) {
ps_decoder_t *ps = NULL;
cmd_ln_t *config = NULL;
FILE *fh;
char const *hyp, *uttid;
int16 buf[512];
int rv;
int32 score;
config = cmd_ln_init(NULL, ps_args(), TRUE,
"-hmm", MODELDIR "/en-us/en-us",
"-lm", MODELDIR "/en-us/en-us.lm.bin",
"-dict", MODELDIR "/en-us/cmudict-en-us.dict",
NULL);
if (config == NULL) {
fprintf(stderr, "Failed to create config object, see log for details\n");
return -1;
}
ps = ps_init(config);
if (ps == NULL) {
fprintf(stderr, "Failed to create recognizer, see log for details\n");
return -1;
}
fh = fopen("goforward.raw", "rb");
if (fh == NULL) {
fprintf(stderr, "Unable to open input file goforward.raw\n");
return -1;
}
rv = ps_start_utt(ps);
while (!feof(fh)) {
size_t nsamp;
nsamp = fread(buf, 2, 512, fh);
rv = ps_process_raw(ps, buf, nsamp, FALSE, FALSE);
}
rv = ps_end_utt(ps);
hyp = ps_get_hyp(ps, &score);
printf("Recognized: %s\n", hyp);
fclose(fh);
ps_free(ps);
cmd_ln_free_r(config);
return 0;
}
当你在Main.cpp中输入以上代码时,你会发现pocketsphinx.h找不到:
这是因为要想找到相关的头文件,需要修改项目中的include path。每个动态链接库编译成功后,都会附带一个include folder,用于给调用该动态链接库的项目使用,include文件夹里面包含了需要用到的头文件。所以我们的第一步是添加include path到该项目中。
pocketsphinx的include文件夹路径是:
sphinxbase的include文件夹路径d:\sphinx\sphinxbase\include,进入该文件夹,还有几个更细节的子文件夹:
右击项目名称,选择properties:
在properties窗口中的VC++ Directories里面,选择include directories,并点击下拉菜单,选择edit:
添加d:\sphinx\pocketsphinx\include到include directories中,点击ok关闭该对话框。
之后查看代码,就可以发现,pocketsphinx.h可以找到了,但是还是有提示某些类找不到,比如cmd_ln_t,这是由于cmd_ln_t的头文件不是在pocketsphinx的include文件夹下,而是在sphinxbase include文件夹下,所以需要添加sphinxbase include文件夹。
添加sphinxbase的include文件夹,在这一步中不能只包含根目录的include文件夹,而且还要包含具体的子文件夹,因为我们是在window平台上使用,所以我们包含下面两个文件夹:
为什么我们要包含根目录,而不是直接包含子目录就可以了呢,那是因为在pocketsphinx的头文件引用sphinxbase下的include文件时,使用了相对路径,例如在pocketsphinx.h包含了这样一段代码:
#include
#include
#include
#include
它使用了某个路径下的sphinxbase相对路径,所以必须包含sphinxbase下的include根目录,编译时才能找到该文件。
添加之后,我们就可以看到,我们的代码没有语法错误了。
那么是否意味着我们的代码可以编译了呢,那么让我们来试一下:
结果我们遇到了这样一个错误:
这是由于secure的规则不同造成的,为了避免修改代码,我们将在项目中定义_CRT_SECURE_NO_WARNINGS,从而让项目忽略这个规则的错误。现在继续打开项目的properties,添加宏变量_CRT_SECURE_NO_WARNINGS:
重新编译,现在之前的错误没有了,又遇到了新的编译错误:
这个错误是为什么呢?这个跟c++生成一个可执行文件的过程有关,下面简单介绍一下这个过程:
首先我们需要找到pocketsphinx的lib文件在哪儿呢?在D:\sphinx\pocketsphinx\bin\Release\Win32文件夹下,由于该处已经包含pocketsphinx.lib和sphinxbase.lib,所以只需要添加这一个文件夹就可以了:
打开项目的properties,在VC++ Directories下面配置或者在Linker下面配置,我们将在linker下面配置,在配置时需要配置两个位置,一个是配置Additional Library Directories,也就是说lib的文件夹位置,另一个是配置需要用到哪些lib,完成后关闭对话框:
配置完成后重新编译该项目:
Yeah,成功!
那是否意味着我们完成了呢?当然不是。。。我们还需要最重要的一步。
在程序运行时,需要引用响应的动态链接库,那么我们需要将程序用到的动态链接库在编译时拷贝到最终的可执行文件所在的文件夹,否则就会遇到执行时的错误。
我们需要拷贝两个dll文件,一个是pocketsphinx.dll,另一个是spinxbase.dll。
输入命令:xcopy /y /d “D:\sphinx\pocketsphinx\bin\Release\Win32*” “$(OutDir)”
点击ok关闭对话框。
代码的基本逻辑是: