[ESP32]:百度语音识别

[ESP32]:百度语音识别

1.开发环境:

  • esp-idf 5.1
  • esp32 s3
  • microphone:MSM261S4030H0

百度文档:短语音识别标准版API

这里请求我们选择RAW方式

Content-Type: audio/pcm;rate=16000

请求格式如下:

POST http://vop.baidu.com/server_api?dev_pid=1537&cuid=******&token=1.a6b7dbd428f731035f771b8d********.86400.1292922000-2346678-124328

2.ESP32

1.整体流程:

  1. 录音
  2. 转成wav格式,保存至spiffs
  3. 读取保存的wav文件到buffer
  4. 调用http client

关于这里为什么要保存到spiffs,主要是考虑到录制的时间太长的,malloc一块大buffer容易失败

2.spiffs创建

我的分区表如下

# Name,   Type, SubType, Offset,  Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,      data, nvs,     0x9000,  0x6000,
phy_init, data, phy,     0xf000,  0x1000,
factory,  app,  factory, 0x10000, 1M,
storage,  data, spiffs,  ,        2000K,

关于spiffs可以参考idf的例子,我的如下

#include "app_spiffs.h"static const char *TAG = "SPIFFS";esp_err_t app_spiffs_init(char *mount_path)
{esp_err_t ret;esp_vfs_spiffs_conf_t conf = {.base_path = mount_path,.partition_label = NULL,.max_files = 5,.format_if_mount_failed = true, // 如果挂载失败,将格式化文件系统};ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf));// 检查spiffsret = esp_spiffs_check(conf.partition_label);if (ret != ESP_OK){ESP_LOGI(TAG, "SPIFFS Check failed:%s", esp_err_to_name(ret));}else{ESP_LOGI(TAG, "SPIFFS Check success");}// 获取spiffs的信息size_t total = 0, used = 0;ret = esp_spiffs_info(conf.partition_label, &total, &used);if (ret != ESP_OK){ESP_LOGI(TAG, "Failed to get spiffs partition info:%s", esp_err_to_name(ret));return ESP_FAIL;}else{ESP_LOGI(TAG, "Partition size: total:%d,used:%d ", total, used);}// 如果used > total,再次检查if (used > total){ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check().");ret = esp_spiffs_check(conf.partition_label);if (ret != ESP_OK){ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret));return ESP_FAIL;}else{ESP_LOGI(TAG, "SPIFFS_check() successful");}}return ret;
}

3.i2s初始化

i2s的录音主要参考idf的例子:IDF I2S录音

#include "hal_i2s.h"
#include "app_spiffs.h"
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_log.h"
#include "esp_heap_caps.h"static const char *TAG = "AUDIO";
i2s_chan_handle_t rx_handle = NULL;
record_info_t record_info = {};esp_err_t hal_i2s_microphone_init(i2s_microphone_config_t config)
{esp_err_t ret_val = ESP_OK;i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(config.i2s_num, I2S_ROLE_MASTER);ret_val |= i2s_new_channel(&chan_cfg, NULL, &rx_handle);i2s_std_config_t std_cfg = {.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(config.sample_rate),.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(config.bits_per_sample, I2S_SLOT_MODE_MONO),.gpio_cfg = {.mclk = GPIO_NUM_NC,.bclk = config.bclk_pin,.ws = config.ws_pin,.dout = GPIO_NUM_NC,.din = config.din_pin,.invert_flags = {.mclk_inv = false,.bclk_inv = false,.ws_inv = false,},},};std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;ret_val |= i2s_channel_init_std_mode(rx_handle, &std_cfg);ret_val |= i2s_channel_enable(rx_handle);record_info.i2s_config = config;return ret_val;
}void hal_i2s_record(char *file_path, int record_time)
{ESP_LOGI(TAG, "Start Record");record_info.flash_wr_size = 0;record_info.byte_rate = 1 * record_info.i2s_config.sample_rate * record_info.i2s_config.bits_per_sample / 8; // 声道数×采样频率×每样本的数据位数/8。播放软件利用此值可以估计缓冲区的大小。record_info.bytes_all = record_info.byte_rate * record_time;                                                 // 设定时间下的所有数据大小record_info.sample_size = record_info.i2s_config.bits_per_sample * 1024;                                     // 每一次采样的带下const wav_header_t wav_header = WAV_HEADER_PCM_DEFAULT(record_info.bytes_all, record_info.i2s_config.bits_per_sample, record_info.i2s_config.sample_rate, 1);// 判断文件是否存在struct stat st;if (stat(file_path, &st) == 0){ESP_LOGI(TAG, "%s exit", file_path);unlink(file_path); // 如果存在就删除}// 创建WAV文件FILE *f = fopen(file_path, "a");if (f == NULL){ESP_LOGI(TAG, "Failed to open file");return;}fwrite(&wav_header, sizeof(wav_header), 1, f);while (record_info.flash_wr_size < record_info.bytes_all){char *i2s_raw_buffer = heap_caps_calloc(1, record_info.sample_size, MALLOC_CAP_DMA);if (i2s_raw_buffer == NULL){continue;}// Malloc successif (i2s_channel_read(rx_handle, i2s_raw_buffer, record_info.sample_size, &record_info.read_size, 100) == ESP_OK){fwrite(i2s_raw_buffer, record_info.read_size, 1, f);record_info.flash_wr_size += record_info.read_size;}else{ESP_LOGI(TAG, "Read Failed!\n");}free(i2s_raw_buffer);}ESP_LOGI(TAG, "Recording done!");fclose(f);ESP_LOGI(TAG, "File written on SDCard");
}
#pragma once#include "driver/i2s_common.h"
#include "driver/i2s_std.h"
#include "driver/i2s_tdm.h"
#include "driver/gpio.h"
#include "driver/i2s_pdm.h"
#include "wav_formate.h"typedef struct
{uint16_t sample_rate;uint16_t bits_per_sample;gpio_num_t ws_pin;gpio_num_t bclk_pin;gpio_num_t din_pin;i2s_port_t i2s_num;
} i2s_microphone_config_t;typedef struct
{i2s_microphone_config_t i2s_config; // i2s的配置信息int byte_rate;                      // 1s下的采样数据int bytes_all;                      // 录音时间下的所有数据大小int sample_size;                    // 每一次采样的大小int flash_wr_size;                  // 当前录音的大小size_t read_size;                   // i2s读出的长度
} record_info_t;extern i2s_chan_handle_t rx_handle;esp_err_t hal_i2s_microphone_init(i2s_microphone_config_t config);
void hal_i2s_record(char *file_path, int record_time);

4.http请求

首先我们把保存的wav文件读取出来

wav_file = fopen("/spiffs/record.wav", "r");
if (wav_file == NULL)
{ESP_LOGI(TAG, "Read audio file failed");
}
fseek(wav_file, 0, SEEK_END);
wav_file_size = ftell(wav_file);
fseek(wav_file, 0, SEEK_SET);
ESP_LOGI(TAG, "WAV File size:%zu", wav_file_size);
wav_raw_buffer = heap_caps_malloc(wav_file_size + 1, MALLOC_CAP_DMA);
if (wav_raw_buffer == NULL)
{ESP_LOGI(TAG, "Malloc wav raw buffer fail");return;
}
fread(wav_raw_buffer, 1, wav_file_size, wav_file);
fclose(wav_file);
1.设置esp http client的url
char *access_token = "xxx";
char *url_formate = "http://vop.baidu.com/server_api?dev_pid=1537&cuid=dPKArKm9yCGIOwPoCSjTDzmIIj4cBsEV&token=%s";

这里的access_token需要自己去api控制台获取

2.然后设置header
esp_http_client_set_header(client, "Content-Type", "audio/pcm;rate=16000");
esp_http_client_set_header(client, "Accept", "application/json");
3.填写post_field
esp_http_client_set_post_field(client, wav_raw_buffer, wav_file_size);
4.整体调用
void app_main(void)
{// Init NVSesp_err_t ret = nvs_flash_init();if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase());ret = nvs_flash_init();}ESP_ERROR_CHECK(ret);// Connect WIFIapp_wifi_init("MERCURY_5B00", "tzyjy12345678");// Init spiffsESP_ERROR_CHECK(app_spiffs_init("/spiffs"));// Init i2s microphoneESP_ERROR_CHECK(hal_i2s_microphone_init(i2s_microphone_config));hal_i2s_record("/spiffs/record.wav", 5);wav_file = fopen("/spiffs/record.wav", "r");if (wav_file == NULL){ESP_LOGI(TAG, "Read audio file failed");}fseek(wav_file, 0, SEEK_END);wav_file_size = ftell(wav_file);fseek(wav_file, 0, SEEK_SET);ESP_LOGI(TAG, "WAV File size:%zu", wav_file_size);wav_raw_buffer = heap_caps_malloc(wav_file_size + 1, MALLOC_CAP_DMA);if (wav_raw_buffer == NULL){ESP_LOGI(TAG, "Malloc wav raw buffer fail");return;}fread(wav_raw_buffer, 1, wav_file_size, wav_file);fclose(wav_file);// HTTPesp_http_client_config_t config = {.method = HTTP_METHOD_POST,.event_handler = app_http_baidu_speech_recognition_event_handler,.buffer_size = 4 * 1024,};char *url = heap_caps_malloc(strlen(url_formate) + strlen(access_token) + 1, MALLOC_CAP_DMA);sprintf(url, url_formate, access_token);config.url = url;client = esp_http_client_init(&config);esp_http_client_set_method(client, HTTP_METHOD_POST);esp_http_client_set_header(client, "Content-Type", "audio/pcm;rate=16000");esp_http_client_set_header(client, "Accept", "application/json");esp_http_client_set_post_field(client, wav_raw_buffer, wav_file_size);esp_err_t err = esp_http_client_perform(client);if (err == ESP_OK){ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d", esp_http_client_get_status_code(client), (int)esp_http_client_get_content_length(client));}else{ESP_LOGI(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));}esp_http_client_cleanup(client);free(url);
}

3.实验效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/623502.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux学习:进程(新建+运行某文件+退出处理函数+等待)

目录 api 创建新进程 注意 运行某文件 例子 注意 例子&#xff0c;等待进程 进程是由进程控制块、程序段、数据段三部分组成 进程有都有一个父进程&#xff0c;除了init&#xff0c;父进程可以创建子进程 每个进程都有一个PID&#xff0c;可以用ps来查看&#xff0c;等…

FebHost:瑞士.CH域名和.RE域名如何选择

.ch和.re域名的区别主要在于它们代表的地区不同。.ch是瑞士的顶级域名&#xff0c;代表着瑞士的精细、创新和可靠&#xff1b;而.re则是留尼汪岛的顶级域名&#xff0c;展示着留尼汪岛的多元化和温馨。 从历史角度看&#xff0c;.ch域名的历史更悠久&#xff0c;反映了瑞士长久…

双轴测径仪兼顾铜排、铜棒、铜丝等多产品品质检测!

关键词&#xff1a;双轴测径仪,铜排测径仪,铜棒测径仪,铜丝测径仪, 测宽测厚仪, 铜排测宽测厚仪 在现代工业生产中&#xff0c;铜排作为一种重要的导电材料&#xff0c;广泛应用于电力、电子、通信等领域。为了确保铜排的质量和性能符合要求&#xff0c;铜排测宽测厚仪成为了生…

如何在CentOS安装Firefox并结合内网穿透工具实现公网访问本地火狐浏览器

文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器&#xff0c;由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…

二叉树——存储结构

二叉树的存储结构 二叉树一般可以使用两种结构存储&#xff0c;一种是顺序结构&#xff0c;另一种是链式结构。 一、顺序存储 二叉树的顺序存储是指用一组连续的存储单元依次自上而下、自左至右存储完全二叉树上的结点元素&#xff0c;即将完全二叉树上编号为i的结点元素存储…

双电机增程系统工作原理

问界M7的双电机增程系统是一项技术革新&#xff0c;它的核心在于高度集成的动力系统和先进的电机控制技术。 这种系统不仅提升了车辆的性能&#xff0c;还在保证高效能源利用的同时&#xff0c;为驾驶者带来了更舒适、更平稳的驾驶体验。 首先&#xff0c;让我们了解一下双电机…

Linux --- 高级IO

目录 1. 什么是IO 2. 阻塞的本质 3. 五种IO模型 3.1. 通过故事认识五种IO模型 3.2. 上述故事的总结 3.3. 具体的五种IO模型 3.3.1. 阻塞IO 3.3.2. 非阻塞轮询式IO 3.3.3. 信号驱动IO 3.3.4. 多路转接IO 3.3.5. 异步IO 4. 非阻塞IO 4.1. fcntl 系统调用 1. 什么是I…

2024最新软件测试【测试理论+ 数据库】面试题(内附答案)

一、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段&#xff1a;需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的 SE 会把需求文档给我们自己先去了解一到两天这样&#xff0c;之后我们会有一个需求澄清会议&#xff0c; …

图像处理相关知识 —— 椒盐噪声

椒盐噪声是一种常见的图像噪声类型&#xff0c;它会在图像中随机地添加黑色&#xff08;椒&#xff09;和白色&#xff08;盐&#xff09;的像素点&#xff0c;使图像的质量降低。这种噪声模拟了在图像传感器中可能遇到的问题&#xff0c;例如损坏的像素或传输过程中的干扰。 椒…

每日两题 / 15. 三数之和 73. 矩阵置零(LeetCode热题100)

15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 先确定一个数t&#xff0c;对于剩下的两个数&#xff0c;要求两数之和为t的负数 三数之和就退化成了两数之和&#xff0c;两数之和可以用双指针 先排序&#xff0c;左右两个指针&#xff0c;指向的数之和大于目标值&…

llama_factory微调QWen1.5

GitHub - hiyouga/LLaMA-Factory: Unify Efficient Fine-Tuning of 100 LLMsUnify Efficient Fine-Tuning of 100 LLMs. Contribute to hiyouga/LLaMA-Factory development by creating an account on GitHub.https://github.com/hiyouga/LLaMA-FactoryQwen1.5 介绍 | QwenGITH…

考研数学|基础阶段做什么题,1800/1000/880/660?

基础阶段关键的不是做什么题&#xff0c;关键的是做题 不管你是做1800题还是做87-08年的老真题&#xff0c;任选一个都不会错&#xff0c;只要你静下心去做就行&#xff0c;不要朝三暮四&#xff0c;听别人说1800题好&#xff0c;就去做1800题&#xff0c;听别人说660题好&…