本人是基于粤嵌的GEC210和ubuntu14.04开发的,源代码过长,需要的可以去这里下载:
https://download.csdn.net/download/weixin_42116930/10807763
语音交互平台框架:
平台:Linux+armA8+科大讯飞语音识别平台
功能:
1、通过文件检索可以将固定的目录下的三种类型的图片和音乐给检索出来,然后再利用libjpeg库和libpng库来对jpeg图片和png图片进行解码,
再通过直接操作framebuffer来将图片显示在LCD屏上,还可以使用触摸屏来切换图片。而播放音乐就要移植madplay库并使用当中的命令来播放音乐,
也可以使用触摸屏来切换音乐。
2、拍照功能,利用V4L2来实现采集一帧的图像并把它显示在LCD屏上。
3、语言交互功能,首先在客户端实现录音功能,并将录制的音频数据通过socket传输到服务端中,服务端就先进行语法构建然后再进行语法识别,
最后将识别的结果保存在xml文件中,再通过socket将xml文件传输到客户端中,客户端再对这个文件进行解析,并得到识别的id号,
然后再根据id进行相应的操作,如操作上述两个功能。
遇到问题的解决方法:通过debug来调试和上网查找一些资料来分析。
流程图:
打开LCD屏
刷屏刷成白色
眨眼
socket初始化
摄像头初始化
while循环里
触摸
进行语音识别
根据语音识别做出相关操作
退出循环
关闭LCD
客户端:
开机先显示几幅BMP图片,然后建立SOCKET连接,之后调用system函数来开始录音,并将录音好的音频保存为cmd.pcm文件,
向服务器发送我们录好的音频文件cmd.pcm(因为在Linux系统中,一切皆是文件,所以在发送函数里我们是用open打开cmd.pcm文件,
然后读取该文件数据,并将数据保存在我们申请的动态内存中,然后再将之发送给服务端),然后等待服务端那边发送数据过来,
发送过来的是xml结果,然后再将这个数据保存在我们的本地文件中(result.xml),之后就是分析xml文件获得ID号并返回ID号。然后就在根据ID号进行一些其他的工作。
服务端:
先设置登录参数,然后再建立SOCKET连接,然后在while循环里先进行登录,服务端每识别一次都要先登录,识别完后要登出,
所以每次识别的时候都要重复这个过程。(因为识别完一次语音,onResult都会被占用,需要通过登出再登陆才能释放占用资源)。
然后再构建语法网络,获取语法ID。之后进行语法识别,识别完之后最后登出。
其中遇到的困难:
之前进行语音识别的时候识别错误,总是不能够识别,一开始我认为是我们录制的音频的问题,
因为录制的音频不能够播放出来,所以我就往这方面排查,先单独进行录制音频,然后把音频利用socket连接传输到我们的ubuntu上。
一开始录制的是wav音频,利用socket传输到ubuntu上然后使其保存在另一个音频文件,但是奇怪的是服务器虽然接收到了同样大小的数据,
但是却不能播放保存的音频文件,打开音频文件来看看,发现数据非常少,跟原版的不一样,于是发现这里有问题,通过排查,
原因是我在服务器的程序中,用数组来接收数据,这是不行的,具体原因我也不清楚,改成用动态内存来保存发送过来的数据就ok了。
于是这个问题就解决了,音频能够传输了。最后在对比下这个成功的例子,发现原来是我进行语言识别的客户端的发送的程序漏了一行代码,
就是在计算音频文件大小完的时候,忘记把指针移到一开始的地方,所以导致我们发送的数据是空,也就不能识别了。
语音交互平台总结:
1、显示图片
2、要让开发板能够播放音乐,首先开发板得要有声卡驱动,这个取决于内核。由于我是用九鼎制作的内核的,该内核已经有声卡驱动了,
所以不需要我们去弄。所以我们只需在开发板上安装一个播放器madplay,进行移植,具体步骤如下:
首先,我们移植madplay需要三个库文件:
madplay-0.15.2b.tar.gz
libmad-0.15.1b.tar.gz
libid3tag-0.15.1b.tar.g
下载地址为: http://sourceforge.net/project/showfiles.php?group_id=12349
下载完之后,我们开始进行移植
①:在root目录上创建一个文件夹madplayer,然后将这三个压缩文件放在这个文件夹里,然后再进行解压。如下所示:
tar -xvf madplay-0.15.2b.tar.gz
tar -xvf libmad-0.15.1b.tar.gz
tar -xvf libid3tag-0.15.1b.tar.g
②:解压完之后,我们先检查下自己的ubuntu里面有没有zlib库,如果没有的话就要安装下,(新手)建议都安装下(不管有没有)。
首先也是从网上找到zlib库(http://www.zlib.net/),然后也将其放置到文件夹madplayer中,解压:
tar -xvf zlib-1.2.8.tar.gz
然后进行配置:
export CC=arm-linux-gcc
./configure -shared --prefix=/opt/libdecode
(这个是我们指定安装的目录,可以自己随意指定。因为我们ubuntu中的/opt目录中是没有libcode这个文件夹的,所以我们得要先在opt目录下创建这个文件夹)
之后进行编译和安装:
make 和 make install
安装完之后,我们还得在ubuntu上敲如下三个命令:
export LDFLAGS="-L/opt/libdecode/lib"
export CFLAGS="-I/opt/libdecode/include"
export CPPFLAGS="-I/opt/libdecode/include"
这是用来导出环境变量的,如果我们不导出,那么等下我们在配置其他库时会出错,提示找不到这个头文件等等。
③:进入到libid3tag-0.15.1b.tar.g解压完之后的目录libid3tag-0.15.1b中,
然后进行配置:
./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libdecode
配置完之后会得到一个Makefile,检查下Makefile,看看交叉编译设置是否正确(一般默认的都不是交叉编译,所以我们得在Makefile中做修改):
CC=gcc 改为 CC=arm-linux-gcc
AR=ar 改为 AR=arm-linux-ar
RANLIB=ranlib 改为 RANLIB=arm-linux-ranlib
之后进行编译和安装:
make 和 make install
④:进入到libmad-0.15.1b.tar.gz解压完之后的目录libmad-0.15.1b中,
然后进行配置:
./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libdecode
配置完之后会得到一个Makefile,检查下Makefile,看看交叉编译设置是否正确(一般默认的都不是交叉编译,所以我们得在Makefile中做修改):
CC=gcc 改为 CC=arm-linux-gcc
AR=ar 改为 AR=arm-linux-ar
RANLIB=ranlib 改为 RANLIB=arm-linux-ranlib
之后进行编译和安装:
make 和 make install
⑤:进入到madplay-0.15.2b.tar.gz解压完之后的目录madplay-0.15.2b中,
然后进行配置:
./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libdecode
配置完之后会得到一个Makefile,检查下Makefile,看看交叉编译设置是否正确(一般默认的都不是交叉编译,所以我们得在Makefile中做修改):
CC=gcc 改为 CC=arm-linux-gcc
AR=ar 改为 AR=arm-linux-ar
RANLIB=ranlib 改为 RANLIB=arm-linux-ranlib
之后进行编译和安装:
make 和 make install
这时会生成两个文件:madplay和abxtest,放在/opt/libdecode/bin中。
⑥:接下来就是部署我们的动态文件了,由于我们生成的动态文件都是生成在/opt/libdecode/lib中,
所以我们要把这里面的.so文件都部署到我们的根文件系统中去(我的根文件系统是/root/rootfs/rootfs),部署到/root/rootfs/rootfs/usr/lib中,
命令如下:cp /opt/libdecode/lib/*so* /root/rootfs/rootfs/usr/lib 。然后将生成在/opt/libdecode/bin中的madplay复制到我们的/root/rootfs/rootfs/usr/bin中。
这样就部署完成了,接下来我们可以把一个音乐文件部署到/root/rootfs/rootfs/usr/bin中,然后让我们的开发板开机进入到系统中,然后进入到/bin目录中,执行如下命令:
./madplay 123.mp3
就可以听到音乐啦。
注意:根文件系统是自己制作的,然后我是通过nfs来挂载根文件系统的,因为这样调试比较方便。
3、录音播放
要进行录音播放,我们得需要安装两个库alsa-lib-1.0.22.tar.bz2和alsa-utils-1.0.22.tar.bz2。接下来我们就进行这两个库的配置和安装。
首先我们在~目录下创建一个文件夹放这两个压缩文件,然后分别进行解压:tar -jxvf alsaxxxxxxx
①:alsa-lib-1.0.22.tar.bz2
解压后会得到:alsa-lib-1.0.22
cd alsa-lib-1.0.22
配置:
./configure --prefix=/root/luyin/alsa-1.0.22 \
--host=arm-none-linux-gnueabi \
--disable-python
注意:--prefix是自己指定的目录,将来我们编译和安装的文件都是放在这个目录下。
编译和安装:
make
make install
至此这个库安装完成,进入到/root/luyin/alsa-1.0.22会看到生成了一些文件夹和文件等。
②:alsa-utils-1.0.22.tar.bz2
解压后会得到:alsa-utils-1.0.22
cd alsa-utils-1.0.22
配置:
./configure --prefix=/root/luyin/alsa-1.0.22/ \
--host=arm-none-linux-gnueabi \
--with-alsa-prefix=/root/luyin/alsa-1.0.22/lib/ \
--with-alsa-inc-prefix=/root/luyin/alsa-1.0.22/include/ \
--disable-alsamixer \
--disable-xmlto
./configure --prefix=/opt/alsa-1.0.22/ \
--host=arm-none-linux-gnueabi \
--with-alsa-prefix=/opt/alsa-1.0.22/lib/ \
--with-alsa-inc-prefix=/opt/alsa-1.0.22/include/ \
--disable-alsamixer \
--disable-xmlto
编译和安装:
make
make install
③:接下来就是部署了
进入到/root/luyin/alsa-1.0.22/lib,将所有的so文件拷贝到我们自己制作的根文件系统中的/usr/lib中。我自己制作的根文件系统的目录是:/root/rootfs/rootfs。如下所示:
cp /root/luyin/alsa-1.0.22/lib/*so* /root/rootfs/rootfs/usr/lib
进入到/root/luyin/alsa-1.0.22/bin,将其中所有的文件拷贝到我们自己制作的根文件系统中的/usr/bin中。命令如下所示:
cp /root/luyin/alsa-1.0.22/bin/* /root/rootfs/rootfs/usr/bin
进入到/root/luyin/alsa-1.0.22/share,将其中的alsa文件(因为这个文件执行的目录跟我们之前配置的要相同,所以我们要先在我们的根文件系统中创建root/luyin/alsa-1.0.22/share),
所以复制到的目录是:/root/rootfs/rootfs/root/luyin/alsa-1.0.22/share。命令如下:
cd /root/rootfs/rootfs/
mkdir root/luyin/alsa-1.0.22/share -p
cp /root/luyin/alsa-1.0.22/share/alsa /root/rootfs/rootfs/root/luyin/alsa-1.0.22/share/
至此,这两个文件就安装完成,我们可以在开发板上测试了。
④:配置声卡
在开发板中依次运行如下命令:
mkdir /dev/snd
cd /dev/snd
mknod dsp c 14 3
mknod audio c 14 4
mknod mixer c 14 0
mknod controlC0 c 116 0
mknod seq c 116 1
mknod pcmC0D0c c 116 24
mknod pcmC0D0p c 116 16
mknod pcmC0D1c c 116 25
mknod pcmC0D1p c 116 17
mknod timer c 116 33
如果开发板上本身就有了,可以不用执行这个,但还是建议执行下,反正也没什么损失。
⑤:录音和播放
录音:
arecord -d3 -c1 -r16000 -twav -fS16_LE example.wav
说明:
-d:录音时长(duration)
-c:音轨(channels)
-r:采样频率(rate)
-t:封装格式(type)
-f:量化位数(format)
执行这条命令时,可能到时候播放录音时会有噪音,这是由于我们设置的音轨为1的原因,所以把它设置为2就可以了。具体命令如下:
arecord -d3 -c2 -r16000 -twav -fS16_LE example.wav
播放:
aplay example.wav
4、Linux下的科大讯飞语音离线识别
①:首先得要上科大讯飞官网( https://www.xfyun.cn/?ch=bdtg),注册一个账号
②:选择 资料库 ---> SDK下载 ----> 输入登录的用户名 ----> 首次使用需要添加应用 ---> 添加描述(应用名称:xxxx),其他描述自定义
---> 回到SDK下载
---> 选择刚刚的应用,点击添加 AI功能 (离线命令词识别),并选中
---> 最后确定SDK下载,选择 Linux 平台
③:将下载好的 SDK拷贝到Ubuntu系统的工作区
mkdir /root/AI
cp /mnt/hgfs/windows_share/Linux_aitalk_expxxxxxx.zip /root/AI -----> 注意 Linux_aitalk_expxxxxxx.zip 为自己的实际包名
④:编译SDK
cd /root/AI
unzip Linux_aitalk_expxxxxxx.zip
/**********************从现有语音中识别命令************************/
首先,进行编译
cd samples
cd asr_sample/
chmod 777 32bit_make.sh
./32bit_make.sh
其次,将so库移植到我们的/usr/lib库中
cp /root/AI/libs/x86/libmsc.so /usr/lib/
最后,测试
cd /root/AI/bin
./asr_sample
/*****************************************************************/
/*********************从现现场录音中识别命令**********************/
首先,进行编译
cd samples
cd asr_record_sample
chmod 777 32bit_make.sh
./32bit_make.sh
这时会出现错误,提示找不到动态库和头文件等,所以我们要安装两个库alsa-lib-1.0.22.tar.bz2和alsa-utils-1.0.22.tar.bz2。然后将其生成的文件部署到相应的地方,比如把生成的头文件拷贝到/usr/include中,将.so文件拷贝到/usr/lib中就行。反正就是把相应的放到ubuntu中相应的地方去,bin文件就放到/usr/bin中等等。
弄完之后还是会出现错误:/usr/bin/ld: cannot find -lasound
这时我们得要执行系统更新命令:
sudo apt-get update
sudo apt-get remove libasound2
sudo apt-get install alsa-base alsa-utils alsa-source
sudo apt-get install libasound2*
sudo apt-get install fglrx
sudo apt-get install --reinstall ubuntu-desktop
然后执行命令:
./32bit_make.sh
其次,将so库移植到我们的/usr/lib库中
cp /root/AI/libs/x86/libmsc.so /usr/lib/
最后,测试
cd /root/AI/bin
./asr_record_sample
/************************************************************************************/
这样就可以了。
利用socket将语音传送给主机:
首先得将libxml2-sources-2.9.3.tar库安装
安装步骤:
1、配置:
./configure --prefix=/opt/libdecode --host=arm-none-linux-gnueabi --without-python
2、编译和安装:
make、make install
完成这两个步骤之后,去编译我们的程序可能会出现这个问题:
fatal error: libxml/xmlmemory.h: No such file or directory
解决办法:
ln -s /opt/libdecode/include/libxml2/libxml /opt/libdecode/include/libxml
可能还会遇到的问题:
/mnt/hgfs/windows_share/yuyin/include/common.h:27: error: expected '=', ',', ';', 'asm' or '__attribute__' before '*' token
/mnt/hgfs/windows_share/yuyin/include/common.h:29: error: expected '=', ',', ';', 'asm' or '__attribute__' before '*' token
/mnt/hgfs/windows_share/yuyin/include/common.h:30: error: expected ')' before 'doc'
原因:那是因为没有包含相应的头文件,包含相应的头文件就行了。
视频:
VIDIOC_S_FMT failed (-1)
运行V4L2编写的camera测试程序出现如下错误:
Capability Informations:
driver: uvcvideo
card: USB2.0 PC Camera
bus_info: usb-0000:02:03.0-1
version: 3.5.7
capabilities: 04000001
VIDIOC_S_FMT failed (-1)
在开发板上运行camera程序时总是出现VIDIOC_S_FMT failed (-1)错误,查看源码发现是在 ret = ioctl(fd, VIDIOC_S_FMT, &fmt);时出错,用源码在PC机上调试运行却没问题,可以通过此条语句,goolgle也没查到什么有用的信息。后来在PC机上调试时发现在有一个调试程序的情况下再对该程序重启一个进程调试也出现这个问题,怀疑出现这个问题的原因是video0设备被别的程序占用了,所以无法再次设置FMT,
实验:杀断所有占用视频设备的程序或重启,在一个终端上运行正常,再开个终端运行时也出现相同情况,说明确实是因为被占用的原因。
以这个思路在开发板上排除了USB CAMERA设备节点被占用后,依然无法设置FMT,偿试了下先获取设备支持的视频格式,以此设置OK了,同一个摄像头在PC上的格式是YUV但在开发板上获取却变成MJPG了,所以以PC机上的格式设置无法在开发板上运行,格式不同的根本原因应该是UVC驱动不同造成。
检查了下源码发现退出时都没有close相应的设备文件,这样也会导致重新插拔USB摄像头时设备结点的次设备号增加变成video1,video2....videoN等。
服务端程序流程:
1、先执行以下两条命令:
system("rm msc -rf");
system("cp .msc msc -rf");
2、设置登陆参数
const char *login_config = "appid = 583467f7"; //登录参数
3、建立socket连接
init_sock();//服务端初始化一次socket,等待客户端的连接就好,socket不要重复初始化,不让资源被占用会直接失败只能识别一次
4、进入while循环中,该循环的主要作用:等待客户端的语音数据,如果客户端没有语音数据传过来,服务器就会陷入不可控的死循环(不能循环识别了)
5、循环里面:
①:ret = MSPLogin(NULL, NULL, login_config); //第一个参数为用户名,第二个参数为密码,传NULL即可,第三个参数是登录参数
注意:服务端每次识别一次语音,都要先登录;识别完之后要登出;需要重复这个过程;因为识别完一次语音,onResult都会被占用,需要通过登出再登陆才能释放占用资源。
②:第一次使用某语法进行识别,需要先构建语法网络,获取语法ID,之后使用此语法进行识别,无需再次构建
memset(&asr_data, 0, sizeof(UserData));
build_grammar(&asr_data); //构建语法网络
asr_data是UserData定义的一个结构体:
typedef struct _UserData
{
int build_fini; //标识语法构建是否完成
int update_fini;//标识更新词典是否完成
int errcode; //记录语法构建或更新词典回调错误码
char grammar_id[MAX_GRAMMARID_LEN]; //保存语法构建返回的语法ID
}UserData;
③:build_grammar函数解析
函数原型
int build_grammar(UserData *udata)
参数
udata : 用户数据
返回值: ret
先进行各种定义:
FILE *grm_file = NULL;
char *grm_content = NULL;
unsigned int grm_cnt_len = 0;
int ret = 0;
char *grm_build_params = calloc(1, MAX_PARAMS_LEN);
打开GRM_FILE文件(构建离线识别语法网络所用的语法文件)(const char * GRM_FILE = "cmd.bnf";)
grm_file = fopen(GRM_FILE, "rb");
if(NULL == grm_file) {
printf("打开\"%s\"文件失败![%s]\n", GRM_FILE, strerror(errno));
return -1;
}
计算GRM_FILE文件的大小长度
fseek(grm_file, 0, SEEK_END);
grm_cnt_len = ftell(grm_file);
fseek(grm_file, 0, SEEK_SET);
向内存申请GRM_FILE文件的大小+1长度的内存,即(grm_cnt_len + 1)
grm_content = (char *)malloc(grm_cnt_len + 1);
if (NULL == grm_content)
{
printf("内存分配失败!\n");
fclose(grm_file);
grm_file = NULL;
return -1;
}
将GRM_FILE文件中的内容读取到刚刚申请的内存grm_content中,最后以‘\0'为结束符号
fread((void*)grm_content, 1, grm_cnt_len, grm_file);
grm_content[grm_cnt_len] = '\0';
关闭GRM_FILE文件
fclose(grm_file);
grm_file = NULL;
将相关信息存储到grm_build_params中
snprintf(grm_build_params, MAX_PARAMS_LEN - 1,
"engine_type = local, \
asr_res_path = %s, sample_rate = %d, \
grm_build_path = %s, ",e'w'we
ASR_RES_PATH,
SAMPLE_RATE_16K,
GRM_BUILD_PATH
);
构建语法,生成语法ID
ret = QISRBuildGrammar("bnf", grm_content, grm_cnt_len, grm_build_params, build_grm_cb, udata);
函数原型
QISRBuildGrammar()
int MSPAPI QISRBuildGrammar ( const char * grammarType,
const char * grammarContent,
unsigned int grammarLength,
const char * params,
GrammarCallBack callback,
void * userData
)
参数
grammarType[in] 语法类型,在线识别采用abnf 格式语法,离线识别采用bnf 格式语法。
grammarContent[in] 语法内容。
grammarLength[in] 语法长度。
params[in] 包含sample_rate(音频采样率)、asr_res_path(离线识别资源路径)、grm_build_path(离线语法生成路径)
callback[in] 构建语法回调接口。typedef int ( GrammarCallBack)( int errorCode, const char info, void* userData);
userData[in/out] 用户数据。
返回
函数调用成功返回MSP_SUCCESS,否则返回错误代码,详见错误码列表。
该语句调用了回调函数build_grm_cb
释放内存
free(grm_content);
free(grm_build_params);
grm_content = NULL;
返回值
返回ret
④:build_grm_cb回调函数解析
函数原型
int build_grm_cb(int ecode, const char *info, void *udata)
参数
ecode:记录语法构建或更新词典回调错误码
info: 语法ID
udata: 用户数据
返回值
返回0
定义一个结构体指针,并初始化为我们传进来的数据
UserData *grm_data = (UserData *)udata;
将grm_data指针赋值
if (NULL != grm_data) {
grm_data->build_fini = 1;
grm_data->errcode = ecode;
}
判断语法是否构建成功
if (MSP_SUCCESS == ecode && NULL != info) {
printf("构建语法成功! 语法ID:%s\n", info);
if (NULL != grm_data)
snprintf(grm_data->grammar_id, MAX_GRAMMARID_LEN - 1, "%s", info);
//将info的值赋值给grm_data->grammar_id
}
else
printf("构建语法失败!%d\n", ecode);
⑤:回到主函数里面继续分析
判断构建语法调用是否成功
printf("构建离线识别语法网络...\n");
if(build_grammar(&asr_data) != MSP_SUCCESS)
{
printf("构建语法调用失败!\n");
goto exit;
}
判断语法构建完成标识
while (1 != asr_data.build_fini)
usleep(200 * 1000);
若为1,则证明语法构建完成
判断语法构建是否成功
if (MSP_SUCCESS != asr_data.errcode)
goto exit;
打印以下语句
printf("离线识别语法网络构建完成,开始识别...\n");
离线语法识别
run_asr(&asr_data)
⑥:run_asr函数解析
函数原型
run_asr(&asr_data)
参数
asr_data :结构体数据
返回值
0
先定义两个指针
char *asr_params = calloc(1, MAX_PARAMS_LEN);
const char *asr_audiof = NULL;
离线语法识别参数设置
snprintf(asr_params, MAX_PARAMS_LEN - 1,
"engine_type = local, "
"asr_res_path = %s, sample_rate = %d, "
"grm_build_path = %s, local_grammar = %s, "
"result_type = xml, result_encoding = UTF-8, ",
ASR_RES_PATH,
SAMPLE_RATE_16K,
GRM_BUILD_PATH,
udata->grammar_id
);
选择进行离线语法识别的语音文件
asr_audiof = get_audio_file();
打印get_audio_file()函数返回的数据
printf("%c \n",*asr_audiof);
进行语言识别
demo_file(asr_audiof, asr_params);
释放内存
free(asr_params);
⑦:get_audio_file()函数解析
函数原型
const char *get_audio_file(void)
参数
无
返回值
return "pcm/cmd.pcm";
申请内存,打印调试信息,初始化
char *pcm = calloc(1, 96000);
printf("waiting for data...\n");
gec210_ip = calloc(1, 32);
int n, total_bytes = 96000, m=0;
char *tmp = pcm;
进入一个循环中,直到total_bytes<0才退出,循环内容为:
读取通过socket由客户端传过来的数据
n = read(sockfd_asr, tmp, total_bytes);
判断是否读取成功
if(n == -1)
{
perror("read() failed");
exit(0);
}
if(n == 0)
{
printf("[%d]recv finished. get %d bytes\n", __LINE__, m);
break;
}
总体来说这个循环体的作用是读取通过socket由客户端传过来的数据
打印调试信息
printf("[%d]recv finished. get %d bytes\n", __LINE__, m);
打开./pcm/cmd.pcm文件
int pcmfd = open("./pcm/cmd.pcm", O_WRONLY|O_CREAT|O_TRUNC, 0644);
将pcm里的内容写入到./pcm/cmd.pcm文件中
m = write(pcmfd, pcm, 96000);
打印调试信息
printf("%d bytes has been wirtten into cmd.pcm\n", m);
关闭文件描述符和返回一个值
close(pcmfd);
return "pcm/cmd.pcm";
⑧:demo_file函数解析
函数原型
static void demo_file(const char* audio_file, const char* session_begin_params)
参数
audio_file :传递进来的文件
session_begin_params : 传递进来的文件的参数
返回值
无
先进行各种定义
int errcode = 0;
FILE* f_pcm = NULL;
char* p_pcm = NULL;
unsigned long pcm_count = 0;
unsigned long pcm_size = 0;
unsigned long read_size = 0;
struct speech_rec iat;
struct speech_rec_notifier recnotifier =
{
on_result,
on_speech_begin,
on_speech_end
};
判断客户端传过来的文件是否为空
if (NULL == audio_file)
goto iat_exit;
打开文件
f_pcm = fopen(audio_file, "rb");
if (NULL == f_pcm)
{
printf("\nopen [%s] failed: %s\n",
audio_file, strerror(errno));
goto iat_exit;
}
计算文件大小
fseek(f_pcm, 0, SEEK_END);
pcm_size = ftell(f_pcm);
fseek(f_pcm, 0, SEEK_SET);
申请内存
p_pcm = (char *)malloc(pcm_size);
if (NULL == p_pcm)
{
printf("\nout of memory! \n");
goto iat_exit;
}
将文件内容读取到p_pcm中(即刚刚申请的内存)
read_size = fread((void *)p_pcm, 1, pcm_size, f_pcm);
if (read_size != pcm_size)
{
printf("\nread [%s] error!\n", audio_file);
goto iat_exit;
}
内容初始化到iat中
errcode = sr_init(&iat, session_begin_params, SR_USER, &recnotifier);
if(errcode != 0)
{
printf("speech recognizer init failed : %d\n", errcode);
goto iat_exit;
}
语音识别函数
errcode = sr_start_listening(&iat);
if(errcode != 0)
{
printf("\nsr_start_listening failed! error code:%d\n", errcode);
goto iat_exit;
}
进入while循环里
进行一些判断
if (pcm_size < 2 * len)
len = pcm_size;
if (len <= 0)
break;
写数据
ret = sr_write_audio_data(&iat, &p_pcm[pcm_count], len);
if(0 != ret)
{
printf("\nwrite audio data failed! error code:%d\n", ret);
goto iat_exit;
}
停止语言识别
errcode = sr_stop_listening(&iat);
if(errcode)
{
printf("\nsr_stop_listening failed! error code:%d \n", errcode);
goto iat_exit;
}
回到主函数中
判断语法识别是否出错
if((ret=run_asr(&asr_data)) != MSP_SUCCESS)
{
printf("离线语法识别出错: %d \n", ret);
goto exit;
}
进行后续工作
printf("语音识别完毕.\n");
system("rm msc -rf");//清楚发送过来的云隐数据
system("cp .msc msc -rf");
MSPLogout();
sleep(1);
socket传输音频总结:
客户端传送音频步骤:
在执行下列步骤之前,你已经把socket连接建立好了
1、用open把音频文件打开
fd = open("ddhgdw.pcm", O_RDONLY);
2、获取音频文件数据大小
off_t pcm_size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
3、申请一块动态内存,将音频文件数据读取到这块内存上
char *pcm = calloc(1, pcm_size);
read(fd, pcm, pcm_size);
4、发送音频数据
int m = write(sockfd, pcm, pcm_size);
printf("%d bytes has been write into socket!\n", m);
5、释放内存
free(pcm);
服务端接收音频步骤:
1、先创建一个音频文件用来最后保存客户端传送过来的数据
int fd = -1;
fd = open("cmd.pcm",O_RDWR | O_CREAT | O_APPEND);
2、申请一块动态内存,用来接收客户端传送过来的数据
char *pcm = calloc(1, 192000);
3、建立一个循环,在循环里不断的接收客户端发过来的数据,每次接收9600字节大小,直到客户端发送完,然后再把数据写入到我们刚刚创建的音频文件中。
while(1)
{
ret = recv(clifd, pcm, 96000, 0);
printf("成功接收了%d个字节\n", ret);
write(fd,pcm,ret);
if(ret == 0)
{
break;
}
}
4、释放内存
free(pcm);
注意:不能用数组来保存数据。