1、选择合适的语音识别处理文件.XML(可从各大官网获取)
也会在文章的末尾附上本人使用的文件
2、在Linux上编写voicectl.c文件与编译后的XML文件进行交互
为了是代码更加清晰且模块化,讲所需函数写入了common.c文件中
放入common几个重要的函数模块
//进行TCP通讯套接字配置
int Setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen)
{
int retval = setsockopt(sockfd, level, optname, optval, optlen);
if(retval == -1)
{
perror("setsockopt() error");
}
return retval;
}
int Select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)
{
int n = select(nfds, readfds, writefds, exceptfds, timeout);
if(n == -1)
{
perror("select() failed");
exit(0);
}
return n;
}
//初始化套接字的封装函数
int init_sock(const char *ubuntu_ip)
{
struct sockaddr_in sin;
int sockfd = Socket(AF_INET, SOCK_STREAM, 0);
//进行端口复用和协议组,通讯模式的配置
int on = 1;
Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(1));
//连接到虚拟机中
bzero((char *) &sin, sizeof(sin));
sin.sin_family = AF_INET;
inet_pton(AF_INET, ubuntu_ip, &sin.sin_addr);
sin.sin_port = htons(DEF_PORT);
Connect(sockfd, (struct sockaddr *)&sin, sizeof(sin));
printf("connected.\n");
return sockfd;
}
void send_pcm(int sockfd, char *pcmfile)
{
// 打开PCM文件
int fd = Open(pcmfile, O_RDONLY);
// 取得PCM数据的大小
off_t pcm_size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
// 读取PCM数据
char *pcm = calloc(1, pcm_size);
Read(fd, pcm, pcm_size);
// 将PCM发送给语音识别引擎系统
int m = Write(sockfd, pcm, pcm_size);
printf("%d bytes has been write into socket!\n", m);
free(pcm);
}
xmlChar *wait4id(int sockfd)
{
char *xml = calloc(1, XMLSIZE);
printf("wait4id: 1\n");
// 从ubuntu接收XML结果
int n = Read(sockfd, xml, XMLSIZE);
printf("%d bytes has been recv from ubuntu.\n", n);
// 将XML写入本地文件 XMLFILE 中
FILE *fp = fopen(XMLFILE, "w");
if(fp == NULL)
{
perror("fopen() failed");
exit(0);
}
size_t m = fwrite(xml, 1, n, fp);
if(m != n)
{
perror("fwrite() failed");
exit(0);
}
fflush(fp);
return parse_xml(XMLFILE);
}
/********************* XML函数列表 *******************/
xmlChar *__get_cmd_id(xmlDocPtr doc, xmlNodePtr cur)
{
xmlChar *key, *id;
cur = cur->xmlChildrenNode;
while (cur != NULL)
{
if ((!xmlStrcmp(cur->name, (const xmlChar *)"cmd")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("cmd: %s\n", key);
xmlFree(key);
id = xmlGetProp(cur, (const xmlChar *)"id");
printf("id: %s\n", id);
xmlFree(doc);
return id;
}
cur = cur->next;
}
xmlFree(doc);
return NULL;
}
xmlChar *parse_xml(char *xmlfile)
{
xmlDocPtr doc;
xmlNodePtr cur1, cur2;
doc = xmlParseFile(xmlfile);
if (doc == NULL)
{
fprintf(stderr,"Document not parsed successfully. \n");
return NULL;
}
cur1 = xmlDocGetRootElement(doc);
if(cur1 == NULL)
{
fprintf(stderr,"empty document\n");
xmlFreeDoc(doc);
return NULL;
}
if(xmlStrcmp(cur1->name, (const xmlChar *)"nlp"))
{
fprintf(stderr,"document of the wrong type, root node != nlp");
xmlFreeDoc(doc);
return NULL;
}
cur1 = cur1->xmlChildrenNode;
while (cur1 != NULL)
{
if ((!xmlStrcmp(cur1->name, (const xmlChar *)"result")))
{
cur2 = cur1->xmlChildrenNode;
while(cur2 != NULL)
{
if((!xmlStrcmp(cur2->name, (const xmlChar *)"confidence")))
{
xmlChar *key = xmlNodeListGetString(doc, cur2->xmlChildrenNode, 1);
if(atoi((char *)key) < 30)
{
xmlFree(doc);
fprintf(stderr, "sorry, I'm NOT sure what you say.\n");
return NULL;
}
}
if((!xmlStrcmp(cur2->name, (const xmlChar *)"object")))
{
return __get_cmd_id(doc, cur2);
}
cur2 = cur2->next;
}
}
cur1 = cur1->next;
}
xmlFreeDoc(doc);
return NULL;
}
void send_data_to_8266(int id_num)
{
int sockfd;
/*客户程序开始建立sockfd描述符*/
if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) ==-1)
{
printf("socket error\n");
exit(1);
}
struct sockaddr_in r_addr;
bzero(&r_addr,sizeof(struct sockaddr_in)); /*将结构体里数据全部置0*/
r_addr.sin_family = AF_INET; /*结构体成员赋初值*/
r_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//广播发送信息到这个网段
r_addr.sin_port = htons(10010); // 端口号
int on=1;
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
//设置socket为可广播模式。
//通过设置不同的命令,发送给XML文件进行判断分析
switch(id_num)
{
case 1:sendto(sockfd,"open windows",strlen("open windows"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
printf("open windows\n");
break;
case 2:sendto(sockfd,"open door",strlen("open door"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
printf("open door\n");
break;
case 3:sendto(sockfd,"open led",strlen("open led"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
printf("open led\n");
break;
case 4:sendto(sockfd,"close windows",strlen("close windows"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
printf("close windows\n");
break;
case 5:sendto(sockfd,"close door",strlen("close door"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
printf("close door\n");
break;
case 6:sendto(sockfd,"close led",strlen("close led"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
printf("close led\n");
break;
default:
break;
}
}
在voicectl.c中调用common.c的函数模块实现与.XML文件的交互
//
//
// Copyright(C), 2013-2017, GEC Tech. Co., Ltd.
//
// File name: GPLE/voicectl.c
//
// Author: GEC
//
// Date: 2017-01
//
// Description: 获取语音指令,根据指令完成相应动作
//
//
//
#include "common.h"
#include <sys/ioctl.h>
#define TEST_MAGIC 'x' //定义幻数
#define TEST_MAX_NR 2 //定义命令的最大序数
//定义LED的魔幻数
#define LED1 _IO(TEST_MAGIC, 0)
#define LED2 _IO(TEST_MAGIC, 1)
#define LED3 _IO(TEST_MAGIC, 2)
#define LED4 _IO(TEST_MAGIC, 3)
//#define REC_CMD "./arecord -d4 -c1 -r16000 -traw -fS16_LE cmd.pcm"
#define REC_CMD "arecord -d3 -c1 -r16000 -traw -fS16_LE cmd.pcm"
#define PCM_FILE "./cmd.pcm"
/* -d:录音时长(duration)
-c:音轨(channels)
-r:采样频率(rate)
-t:封装格式(type)
-f:量化位数(format) */
int said=0;
void catch(int sig)
{
if(sig == SIGPIPE)
{
printf("killed by SIGPIPE\n");
exit(0);
}
}
int main(int argc, char const *argv[]) // ./wav2pcm ubuntu-IP
{
signal(SIGPIPE, catch);
int id_num=0;
if(argc != 2)
{
printf("Usage: %s <ubuntu-IP>\n", argv[0]);
exit(0);
}
int sockfd = init_sock(argv[1]); //tcp
while(1)
{
// 1,调用arecord来录一段音频
printf("please to start REC in 3s...\n");
// 在程序中执行一条命令 “录音的命令”
system(REC_CMD);
// 2,将录制好的PCM音频发送给语音识别引擎
send_pcm(sockfd, PCM_FILE);
// 3,等待对方回送识别结果(字符串ID)
xmlChar *id = wait4id(sockfd);
if(id == NULL)
{
said = 0;
continue;
}
id_num=atoi((char *)id);
if(id_num == 999)
{
printf("bye-bye!\n");
goto exit;
}
if(id_num == 100 )
{
printf("你好,主人!有什么吩咐\n");
said = 1; //唤醒它
system("madplay ../mp3/我在.mp3"); // 放一个语音文件,MP3文件放入上一级目录
}
if( id_num != 100 )
{
send_data_to_8266(id_num);//处理芯片
printf("id: %d\n",id_num);
//调用Linux命令播放MP3文件
if(id_num == 1)
{
system("madplay ../mp3/开窗.mp3");
}
if(id_num == 2)
{
system("madplay ../mp3/开门.mp3");
}
if(id_num == 3)
{
system("madplay ../mp3/开灯.mp3");
}
if(id_num == 4)
{
system("madplay ../mp3/关窗.mp3");
}
if(id_num == 5)
{
system("madplay ../mp3/关门.mp3");
}
if(id_num == 6)
{
system("madplay ../mp3/关灯.mp3");
}
if(id_num == 7)
{
system("madplay ../mp3/天气.mp3");
}
}
}
exit:
close(sockfd);
return 0;
}
3、在Linux下运行.xml文件与voicectl.c文件
检验通讯连接是否建立,是否可以对语音文件进行处理,在一切准备就绪后在QT上进行实物的操作
QT界面和运行结果如下
设置了开门,关门,开灯,关灯,开窗关窗的QLable模块
通过按钮或者语音命令实现相关操作
在mainwindow.h文件中加入#include <QUdpSocket>头文件确保可以进行通信
因为只需要接受数据所以采用了UDP通讯协议,在单纯的接受数据方面使用UDP协议比起TCP协议更加高效
附上mainwindow.h的代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QUdpSocket>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void slotrecv();
void on_btnOpenWin_clicked();
void on_btnCloseWin_clicked();
void on_btnOpenDoor_clicked();
void on_btnCloseDoor_clicked();
void on_btnOpenLight_clicked();
void on_btnCloseLight_clicked();
private:
Ui::MainWindow *ui;
QUdpSocket *udp;
};
#endif // MAINWINDOW_H
在mainwindow.cpp中进行通信的链接和函数的实现
废话不多说,上代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QNetworkDatagram>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
udp = new QUdpSocket(this);//创建新的QUDPSocket对象
udp->bind(QHostAddress::Any,10010);//连接服务器
connect(udp,SIGNAL(readyRead()),this,SLOT(slotrecv()));//转到槽函数
}
void MainWindow::slotrecv()
{
QNetworkDatagram datagram ;
while (udp->hasPendingDatagrams()) {
datagram = udp->receiveDatagram();//接受数据
}
if(datagram.data()=="open windows")
{
on_btnOpenWin_clicked();//进行对应的处理
}
else if(datagram.data()=="close windows")
{
on_btnCloseWin_clicked();
}
else if(datagram.data()=="open door")
{
on_btnOpenDoor_clicked();
}
else if(datagram.data()=="close door")
{
on_btnCloseDoor_clicked();
}
else if(datagram.data()=="open led")
{
on_btnOpenLight_clicked();
}
else if(datagram.data()=="close led")
{
on_btnCloseLight_clicked();
}
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btnOpenWin_clicked()
{
QPixmap pix(":/pic/开窗.jpg");//接受图片资源
ui->lbWin->setPixmap(pix);//QLabel上显示图片在显示图片
}
void MainWindow::on_btnCloseWin_clicked()
{
QPixmap pix(":/pic/关窗.jpg");
ui->lbWin->setPixmap(pix);
}
void MainWindow::on_btnOpenDoor_clicked()
{
QPixmap pix(":/pic/开门.jpg");
ui->lbDoor->setPixmap(pix);
}
void MainWindow::on_btnCloseDoor_clicked()
{
QPixmap pix(":/pic/关门.jpg");
ui->lbDoor->setPixmap(pix);
}
void MainWindow::on_btnOpenLight_clicked()
{
QPixmap pix(":/pic/openLight.jpg");
ui->lbLight->setPixmap(pix);
}
void MainWindow::on_btnCloseLight_clicked()
{
QPixmap pix(":/pic/closeLight.jpg");
ui->lbLight->setPixmap(pix);
}
此上就可以在Linux上实现语音识别并且与QT的交互了