当前位置: 首页 > news >正文

播放本地音频的代码

首先要在芯片存储区建立存储区域,这个加到partitions.csvz最后

spiffs,   data, spiffs,  0xD00000,  1M

下来就是在CMakeLists.txt中增加自动烧录配置

# 配置 SPIFFS 自动烧录(关键代码)
spiffs_create_partition_image(spiffs data FLASH_IN_PROJECT)

音频文件放在data文件夹下面

这个文件夹是我置顶的烧录到映像区域的文件

下面这个是头文件和配置硬件,我用的是ns416

8

// *************************************************************/#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_vfs.h"
#include "esp_spiffs.h"  // 添加SPIFFS支持// -------------------------- 配置参数(根据硬件修改) --------------------------
#define I2S_BCLK        GPIO_NUM_15  // I2S时钟线引脚
#define I2S_WS          GPIO_NUM_16  // I2S声道选择线(LRCK)引脚
#define I2S_DOUT        GPIO_NUM_7   // I2S数据输出引脚
#define PLAY_BUFFER_SIZE 2048        // 播放缓冲区大小(2KB,可按需调整)
#define WAV_SAMPLE_RATE 16000        // 目标WAV采样率(16kHz,需与播放文件匹配)
#define WAV_BIT_DEPTH   16           // 目标WAV位深(16bit,需与播放文件匹配)/***************************************************************/

下面是播放和测试分区文件的代码

头文件

/*******************************************************/void list_spiffs_files();int init_ok = 0;esp_err_t spiffs_init();// 新增:WAV播放相关配置(需与你的WAV文件匹配)static const int WAV_BUFFER_SIZE = 2048;  // 播放缓冲区大小(2KB,可调整)static const int WAV_HEADER_SIZE = 44;    // 标准WAV文件头大小(固定44字节)// 新增:核心函数声明(读取WAV并播放)bool read_wav_and_play(const char* wav_path);  // 读取WAV文件并调用Write播放/*******************************************************/

类文件

void AudioCodec::OutputData(std::vector<int16_t>& data) {//Write(data.data(), data.size());ESP_LOGE(TAG, "++++++++++++OutputData+++++++++++++");// 确保先初始化(只在首次调用时执行)if (this->spiffs_init() != ESP_OK) {return;  // 初始化失败则退出}// 2. 调用核心函数:读取WAV并播放(路径替换为你的WAV文件名)const char* wav_path = "/spiffs/hi_idf_audio.wav";  // SPIFFS中的WAV路径this->read_wav_and_play(wav_path);
}
/****************************************************************************/
// -------------------------- SPIFFS初始化 --------------------------
esp_err_t AudioCodec::spiffs_init(void) {if (this->init_ok) return ESP_OK;  // 已初始化直接返回// SPIFFS配置(指定spiffs分区)esp_vfs_spiffs_conf_t conf = {.base_path = "/spiffs",.partition_label = "spiffs",.max_files = 5,.format_if_mount_failed = true};// 执行初始化esp_err_t ret = esp_vfs_spiffs_register(&conf);if (ret != ESP_OK) {ESP_LOGE(TAG, "SPIFFS初始化失败: %s", esp_err_to_name(ret));return ret;}this->init_ok = 1;  // 标记为已初始化ESP_LOGI(TAG, "SPIFFS初始化成功");// 每次调用都打印文件清单DIR *dir = opendir("/spiffs");if (!dir) {ESP_LOGE(TAG, "无法打开SPIFFS目录");return ret;}struct dirent *entry;int count = 0;ESP_LOGI(TAG, "=== 文件清单 ===");while ((entry = readdir(dir))) {if (entry->d_name[0] == '.') continue;  // 跳过.和..ESP_LOGI(TAG, "文件: %s", entry->d_name);count++;}closedir(dir);ESP_LOGI(TAG, "共%d个文件 ====", count);return ESP_OK;
}// 核心函数:读取SPIFFS中的WAV文件,调用Write播放
bool AudioCodec::read_wav_and_play(const char* wav_path) {// 1. 打开WAV文件(SPIFFS路径,只读模式)FILE* wav_fp = fopen(wav_path, "rb");if (wav_fp == NULL) {ESP_LOGE(TAG, "打开WAV失败: %s", wav_path);return false;}// 2. 跳过WAV文件头(前44字节是格式信息,不是PCM数据)if (fseek(wav_fp, WAV_HEADER_SIZE, SEEK_SET) != 0) {ESP_LOGE(TAG, "跳过WAV头失败");fclose(wav_fp);return false;}// 3. 分配缓冲区(栈外分配,避免栈溢出)int16_t* wav_buffer = (int16_t*)malloc(WAV_BUFFER_SIZE);if (wav_buffer == NULL) {ESP_LOGE(TAG, "分配WAV缓冲区失败");fclose(wav_fp);return false;}// 4. 循环读取WAV数据并播放(核心逻辑)size_t read_samples = 0;  // 每次读取的PCM样本数(16bit=1个样本)bool play_success = true;while (1) {// 读取数据:fread返回字节数,转成样本数(1样本=2字节)read_samples = fread(wav_buffer, sizeof(int16_t), WAV_BUFFER_SIZE / sizeof(int16_t), wav_fp);if (read_samples == 0) {// 读取到文件末尾,播放完成ESP_LOGI(TAG, "WAV播放完成");break;}// 调用Write函数,将PCM数据写入I2S(播放)int written_samples = Write(wav_buffer, read_samples);if (written_samples != read_samples) {ESP_LOGE(TAG, "播放数据丢失:读取%d样本,写入%d样本", read_samples, written_samples);play_success = false;break;}// (可选)打印播放进度(调试用)ESP_LOGD(TAG, "播放进度:已读%d样本", read_samples);}// 5. 释放资源free(wav_buffer);fclose(wav_fp);return play_success;
}/****************************************************************************/

大体就是这样,因为嵌入别的代码的一部分,不能直接使用,可以借鉴。

http://www.dtcms.com/a/544625.html

相关文章:

  • cefsharp139-H264-X86升级测试(MP4)-支持PDF预览-chromium7258定制浏览器
  • pandoc导出markdown为PDF,同时解决中文内容报乱码的错误
  • 【printpdf】生成PDF的全能Rust库printpdf
  • 小技巧:ipynb转pdf
  • 计算机网络自顶向下方法16——应用层 因特网视频 HTTP流和DASH
  • 摄像头选型与对应采集工具方案
  • 免费的行情软件下载安装佛山网站优化指导
  • 仓颉尾递归优化:从编译器实现到函数式编程实践
  • 小智机器人连接抖音直播间教程
  • webhooks
  • 基于Springboot + vue3实现的亚运会志愿者管理系统
  • 绥中做网站百度如何网站
  • 双碳主题互动装置-低碳环保互动游戏-VR环保展厅方案
  • AI重构兴趣内容与营销生态,驱动消费全链路升级
  • 【数据结构】从线性表到排序算法详解
  • 网站家建设培训学校设计科技公司官网
  • SPIR-V后端稳定性的推进工作报告总结
  • MySQL逗号分隔字段-历史遗留原因兼容方案
  • Bun.js + Elysia 框架实现基于 SQLITE3 的简单 CURD 后端服务
  • 做网站 怎么赚钱吗网站数据分析课程
  • Rust——迭代器适配器深度解析:函数式编程的优雅实践
  • 理解PostgreSQL中的映射表
  • Java1029 抽象类:构造方法
  • 类和对象(中)——日期类的实现取地址运算符重载
  • Linux系统编程—线程同步与互斥
  • 【笔试真题】- 百度第一套-2025.09.23
  • notion模板 | 小胡的第二大脑[特殊字符]- 使用案例
  • notion模版 | 小胡的第二大脑[特殊字符]-介绍
  • 公司网站被百度转码了银川网站建设设计
  • 链式二叉树算法精讲:前中后序、层序与完全二叉树判断