[问题讨论] 【平头哥RVB2601创意应用开发】 八、RVB2601之声音识别代码分析

kit7828   2022-4-21 12:12 楼主

一直想完成评测项目的语音识别,但实际操作中不是这个困难,就是这个困难。尤其是每周一篇的评测文章,如果是自己掌握的技术感觉还好,要是学习新的技术,这个压力就太大了,毕竟还是有正式工作的。

网上看了平头哥OCC论坛的语音识别案例,云语音识别

https://occ.t-head.cn/community/post/detail?spm=a2c6h.12873639.article-detail.5.41b02ed0x0yJaQ&id=4018742358014234624

用的自建网站,在自建的网站中将RVB2601录制的PCM音频通过ffmep,调整大小端,然后转换WAV格式后,再通过腾讯云的API进行语音识别,转换成TXT文本。

作者开源了项目,再Github上,https://github.com/ha-zhuzhu/ch2601_speech_recognizer

核心代码主要是音频录制和语音识别

录音部分是根据ch2601_ft_demo修改的

static void cmd_mic_handler()
{
    //函数主要来源还是ch2601_ft_demo
    csi_error_t ret;
    csi_codec_input_config_t input_config;
    ret = csi_codec_init(&codec, 0);
​
    if (ret != CSI_OK)
    {
        printf("csi_codec_init error\n");
        return;
    }
​
    //一些设置codec相关的变量,以及录音的参数,采样率8000,宽度16
    codec_input_ch.ring_buf = &input_ring_buffer;
    csi_codec_input_open(&codec, &codec_input_ch, 0);
    /* input ch config */
    csi_codec_input_attach_callback(&codec_input_ch, codec_input_event_cb_fun, NULL);
    input_config.bit_width = 16;
    input_config.sample_rate = 8000;
    input_config.buffer = input_buf;
    input_config.buffer_size = INPUT_BUF_SIZE;
    input_config.period = 1024;
    input_config.mode = CODEC_INPUT_DIFFERENCE;
    csi_codec_input_config(&codec_input_ch, &input_config);
    csi_codec_input_analog_gain(&codec_input_ch, 0xbf);
    csi_codec_input_link_dma(&codec_input_ch, &dma_ch_input_handle);
​
    printf("start recoder\n");
    csi_codec_input_start(&codec_input_ch);
​
    // 麦克风录音写入数据 48x1024=49152
    //受限于repeater_data_addr的48K大小,每次1K数据,即1024,共48次
    while (new_data_flag < 48)
    {
        if (cb_input_transfer_flag)
        {
            csi_codec_input_read_async(&codec_input_ch, repeater_data_addr + (new_data_flag * 1024), 1024);
            cb_input_transfer_flag = 0U; // 回调函数将其置 1
            new_data_flag++;
        }
    }
​
    new_data_flag = 0;
​
    printf("stop recoder\n");
    csi_codec_input_stop(&codec_input_ch);
    csi_codec_input_link_dma(&codec_input_ch, NULL);
    csi_codec_input_detach_callback(&codec_input_ch);
    csi_codec_uninit(&codec);
​
    return;
}

 语音识别部分,是PHP代码完成的

<?php
// 调用腾讯云 SDK 省略
$uploads_dir = 'ch2601_recordings';
​
if ($_FILES['file']['error'] == UPLOAD_ERR_OK)
{
    $tmp_name = $_FILES['file']['tmp_name'];
    // $name = $_FILES['file']['name'];
    $date_str=date('YmdHis');
    move_uploaded_file($tmp_name, "$uploads_dir/$date_str".'-b2.pcm');
    // 用 ffmpeg 转码,先转大小端并压缩为 1 通道,再转为 wav 格式
    exec('ffmpeg -f s16be -ar 8000 -ac 2 -i '."$uploads_dir/$date_str".'-b2.pcm'.' -f s16le -ar 8000 -ac 1 '."$uploads_dir/$date_str".'-l1.pcm');
    exec('ffmpeg -f s16le -ar 8000 -ac 1 -i '."$uploads_dir/$date_str".'-l1.pcm '."$uploads_dir/$date_str".'-l1.wav');
​
    try {
        // api 调用部分,省略……
        $resp = $client->SentenceRecognition($req);
​
        $result_str=$resp->getResult();
        $result_file=fopen("$uploads_dir/$date_str".'.txt',"a");
        fwrite($result_file,$result_str);
        fclose($result_file);
        echo $result_str;
    }
    catch(TencentCloudSDKException $e) {
        echo $e;
    }
}

由于缺乏必要的http服务器,这个例程没法完成,于是又在开源网站上找了个基于百度的语音识别项目,SmartSpeaker

分析后,得知语音识别是通过百度API进行的

主要语音识别是调用语音文件,传输到百度openapi.baidu.com进行识别

 

#include "ff.h"
//语音文件的文件指针(不使用动态分配)
static FIL redAu_fp = {0};
//每次读取的文件大小
static uint32_t ulrByte = 0;
//语音的文件大小的字符串
static char pcConLen[16] = {0};


uint8_t ucNet_Audio2Text(const char *pcfile)
{
	if (f_open(&redAu_fp, pcfile, FA_READ) == FR_OK)
	{
		memset(pcConLen, 0, sizeof(pcConLen));
		snprintf(pcConLen, sizeof(pcConLen)-1, "%lld", f_size(&redAu_fp));
		
		memset((char *)pNET_SEND_BUF0, 0, NET_BUFFER_SIZE);
		sprintf((char *)pNET_SEND_BUF0, __audio2text, pcTokenBuf, pcConLen);
		xSemaphoreTake(xNetLsn_RecBinary, 0);
		ucEsp_CIPSTART("TCP", "vop.baidu.com", 80);
		
		//因为涉及到大文件的传输,使用透传会发生数据丢失
		ucEsp_CIPMODE(0);
		ucEsp_CIPSEND(0xFF, strlen((char *)pNET_SEND_BUF0));
		vNet_SendBuf((char *)pNET_SEND_BUF0, strlen((char *)pNET_SEND_BUF0));
		if (ucEsp_WaitSendOK())
		{
			f_close(&redAu_fp);
			ucEsp_CIPCLOSE();
			return 2;
		}
		for (;;)
		{
			memset(pNET_SEND_BUF0, 0, NET_BUFFER_SIZE);
			f_read(&redAu_fp, pNET_SEND_BUF0, NET_BUFFER_SIZE, &ulrByte);
			ucEsp_CIPSENDBUF(0xFF, ulrByte);
			vNet_SendBuf((char *)pNET_SEND_BUF0, ulrByte);
			if (ucEsp_WaitSendOK())
			{
				f_close(&redAu_fp);
				ucEsp_CIPCLOSE();
				return 2;
			}
			if(ulrByte < NET_BUFFER_SIZE)
			{
				break;
			}
		}
		vNet_LsnStart(10000);
		f_close(&redAu_fp);
		xSemaphoreTake(xNetLsn_RecBinary, portMAX_DELAY);
		ucEsp_CIPCLOSE();
		return 0;
	}
	else
	{
		ts_printf("文件系统异常,请检查!");
		f_close(&redAu_fp);
		return 1;
	}
}

前提是需要开通百度语音识别账号

//请注册百度语音开发者账号,将下面的xxxxxxxx替换为自己申请的ID。

static const char __get_token[] = 
{
"GET /oauth/2.0/token?\
grant_type=client_credentials\
&client_id=xxxxxxxxxxxxxx\
&client_secret=xxxxxxxxxxxxxxxx \
HTTP/1.1\r\nHost: openapi.baidu.com\r\n\r\n"
};


static const char __audio2text[] = 
{
"POST /server_api?\
lan=zh\
&cuid=xxxxxxxx\
&token=%s \
HTTP/1.1\r\nHost: vop.baidu.com\r\n\
Content-Type: wav;rate=8000\r\nContent-Length: %s\r\n\r\n"
};

转换令牌还需要一定 的步骤

void vNet_ReacquireToken(void)
{
	//连接证书服务器
	ucEsp_CIPSTART("TCP", "openapi.baidu.com", 80);
	//开启透传模式
	ucEsp_CIPMODE(1);
	//准备发送数据
	ucEsp_CIPSEND(0,0);
	//把信号量置零
	xSemaphoreTake(xNetLsn_RecBinary, 0);
	vNet_SendBuf(__get_token, sizeof (__get_token));
	vNet_LsnStart(10000);
	//等待数据接收完毕
	xSemaphoreTake(xNetLsn_RecBinary, portMAX_DELAY);
	//发送关闭监听信号量
	vNetLsn_Close();
	//退出透传模式
	ucEsp_BreakSEND();
	//关闭透传
	ucEsp_CIPMODE(0);
	//断开和服务器的连接
	ucEsp_CIPCLOSE();
}

原代码用的是ESP8266进行WiFi数据传输的

本来想尝试这个识别的,到百度控制台一看,免费资格应为前面项目申请但未进行,期限过了。

目前打算在讯飞平台或者阿里云平台学习后再进行语音识别项目了。由于评测的每周发表测文要求,就先应付一番了,后面看看是不是先评测GUI方面的?只是这个GUI不在自己提交的评测计划中。

回复评论 (3)

“每周一篇的评测文章”

这个是建议进度,至少大家每周能抽空推进作品完成进度,不要一忙起来把它忘记了

 

可根据自己的完成进度进行调整,不是非要每周一篇,最终保证作品截止日期搞定作品、并提交即可。

点赞  2022-4-21 14:45

还得搞php吗?

默认摸鱼,再摸鱼。2022、9、28
点赞  2022-4-21 22:35
引用: freebsder 发表于 2022-4-21 22:35 还得搞php吗?

如果用PHP的话,干脆就集成识别代码了,不仅仅是声音的处理

点赞  2022-4-22 14:03
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复