当前位置: 代码网 > 服务器>服务器>Linux > Linux下基于QT的智能家居语音识别

Linux下基于QT的智能家居语音识别

2024年08月01日 Linux 我要评论
因为只需要接受数据所以采用了UDP通讯协议,在单纯的接受数据方面使用UDP协议比起TCP协议更加高效。检验通讯连接是否建立,是否可以对语音文件进行处理,在一切准备就绪后在QT上进行实物的操作。在voicectl.c中调用common.c的函数模块实现与.XML文件的交互。在mainwindow.cpp中进行通信的链接和函数的实现。设置了开门,关门,开灯,关灯,开窗关窗的QLable模块。此上就可以在Linux上实现语音识别并且与QT的交互了。也会在文章的末尾附上本人使用的文件。QT界面和运行结果如下。

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的交互了

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com