ESP32实现Wave(.wav)音频文件输出

目录


一.实现原理        

二.了解Wave(.wav)文件格式

三.Wave(.wav)文件转换

四.代码实现


一.实现原理

  ESP32有两个DAC通道,通道1链接GPIO25, 通道2链接GPIO26。对应着GPIO25可以输出左声道,GPIO26可以输出右声道。我们知道,通过dac_output_voltage()可以设置DAC输出不同的电压。我们要做的就是将wav文件转换为C语言数组,同时通过设置一个高分辨率定时器循环输出数组(定时器频率应该与音频文件采样率一致),即可达到输出音频的效果。


二.了解Wave(.wav)文件格式

   Wave文件采用的是RIFF文件结构。RIFF文件是由一个一个的chunk(块)组成的,并且chunk之间可以嵌套。总体来看Wave文件是由多个chunk嵌套组成的。文件头包括了标识符,数据大小,格式判别码,fmt子块和data子块等非音频数据。如果直接引用数组,会输出一端噪音。所以要剔除头文件处数组。

  如果想详细了解Wave(.wav)文件格式,可以参考其他博主的文章。


三.wav文件转换

  第一步我们应该准备一段音频,将其格式转换为wav文件,并配置其采样率及声道。我这里使用的是格式工厂 官方主页 - 免费多功能的多媒体文件转换工具

  第二步.将我们转换好格式的wav文件转换为C语言数组。我这里使用的是WinHex。当然其他博主那里也有生成数组的代码,我在这不多做阐述。

WinHex下载地址:WinHex: Hex Editor & Disk Editor, Computer Forensics & Data Recovery Software

  导入文件之后---->点击编辑---->复制全部---->C源码---->复制到文本文件中代用


四.代码实现

#include "driver/dac.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <stdio.h>
#include "esp_timer.h"
extern unsigned char rawData[];

void test_timer_periodic_cb();
// 定义定时器句柄
esp_timer_handle_t test_p_handle = 0;

// 定义一个周期重复运行的定时器结构体
esp_timer_create_args_t test_periodic_arg = 
{ 
    .callback = &test_timer_periodic_cb, // 设置回调函数
    .arg = NULL,                         // 不携带参数
    .name = "TestPeriodicTimer"          // 定时器名字
};
unsigned int i=1;
void test_timer_periodic_cb(void *arg) 
{   
        dac_output_voltage(DAC_CHANNEL_1,  rawData[i]+0x80);
/*为什么将提取出来的数据+0x8000,原因就是16的WAV编码采用的就是 -32767~ 32767
在数轴上就是一个x轴上下波动的波形图,可是单片机的DAC里可没有负信号,所以统一加上0x8000;*/
        i+=2;//每隔一个数值读取一个
        if(i>=N)
        {
        i=1+128;//跳过wav文件开头非音频数据的类型块标识部分,从头重新播放
        }
}
void app_main(void)
{
    esp_timer_init(); // 使用定时器API函数,先调用接口初始化
    dac_output_enable(DAC_CHANNEL_1);
    // 开始创建一个重复周期的定时器并且执行
    esp_err_t err = esp_timer_create(&test_periodic_arg, &test_p_handle);
    err = esp_timer_start_periodic(test_p_handle,90);//我的.wav文件采样率为11025
    printf("重复周期运行的定时器创建状态码: %s", err == ESP_OK ? "ok!\r\n" : "failed!\r\n");
         while(1) 
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}


unsigned char rawData[N] = {};//第二步wav文件转换得到的数组

 

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!要实现ESP32S3读取SD卡音频文件并通过I2S输出,您可以按照以下步骤进行: 1. 首先,您需要在ESP32S3上连接一个SD卡模块,并使用SD卡库来读取音频文件。您可以使用ESP32官方的SD卡库或者其他第三方库来实现。 2. 接下来,您需要配置I2S输出。您可以使用ESP32官方的I2S库来设置I2S输出参数,如采样率、位深度等。 3. 一旦您成功读取了音频文件并设置了I2S输出参数,您就可以开始将音频数据写入I2S缓冲区并输出音频信号了。您可以使用I2S库的write函数来将音频数据写入I2S缓冲区。 下面是一个示例代码,用于读取SD卡中的音频文件,并通过I2S输出: ```c #include <Arduino.h> #include <SD.h> #include <driver/i2s.h> #define SD_CS 5 #define I2S_BCK 26 #define I2S_WS 25 #define I2S_DO 27 File audioFile; void setup() { Serial.begin(115200); // 初始化SD卡 if (!SD.begin(SD_CS)) { Serial.println("SD Card initialization failed!"); return; } // 配置I2S输出 i2s_config_t i2sConfig = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), .sample_rate = 44100, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_I2S_MSB, .dma_buf_count = 8, .dma_buf_len = 64, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .tx_desc_auto_clear = true, }; i2s_pin_config_t pinConfig = { .bck_io_num = I2S_BCK, .ws_io_num = I2S_WS, .data_out_num = I2S_DO, .data_in_num = I2S_PIN_NO_CHANGE }; i2s_driver_install(I2S_NUM_0, &i2sConfig, 0, NULL); i2s_set_pin(I2S_NUM_0, &pinConfig); // 打开音频文件 audioFile = SD.open("/test.wav"); if (!audioFile) { Serial.println("Failed to open audio file!"); return; } Serial.println("Begin playing audio..."); } void loop() { static uint8_t buffer[1024]; static int bytesRemaining = audioFile.size(); static int bytesRead = 0; // 从SD卡中读取音频数据 int bytesToRead = min(sizeof(buffer), bytesRemaining); int bytesRead = audioFile.read(buffer, bytesToRead); bytesRemaining -= bytesRead; // 将音频数据写入I2S缓冲区并输出 i2s_write(I2S_NUM_0, buffer, bytesRead, portMAX_DELAY); // 如果音频文件已经播放完毕,则关闭文件并停止I2S输出 if (bytesRemaining <= 0) { audioFile.close(); i2s_driver_uninstall(I2S_NUM_0); Serial.println("Audio playback complete!"); while (1) delay(1000); // 停止执行 } } ``` 需要注意的是,以上示例代码仅供参考,实际使用时您需要根据自己的需求进行修改。另外,由于ESP32S3的I2S输出引脚与ESP32不同,请根据实际情况修改示例代码中的引脚定义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值