您现在的位置是:首页 >其他 >ESP32开发路程I2S篇——音频播放,自定义分区网站首页其他

ESP32开发路程I2S篇——音频播放,自定义分区

_做个辣妹 2023-06-11 16:00:02
简介esp32 i2s


自定义分区

官方给出的分区表介绍: esp-idf 编程指南

相关操作:

  • 在 VSCODE 的 ESP-IDF 环境中点击底层菜单栏的 menuconfig 图标
  • 修改 Serial flasher config 的 Flash size 大小(我的为 4MB)
  • 将 Partition Table 选择为 Custom partition table,其余默认,save保存
  • 查看->命令面板(Ctrl + Shift + P) 搜索 partition table,选择 Open Partition Table Editor UI
  • 通过 add new row 添加分区,并参考下图编辑分区表,编写后 save 保存自定义分区表

音频文件写入自定义分区

音频文件编译为二进制文件

将 wav 形式的音频文件放进 audio 文件夹中

音频二进制文件制作

在 spiffsgen.py 所在的文件夹下打开命令行,输入以下命令(示例):python spiffsgen.py 0x200000 audio audio.bin

即可生成 audio.bin

  • python spiffsgen.py A B C
  • A:系统的空间大小,使用十六进制(预设的 audio 分区为 2M,所以为 0x200000,注意不是 audio 分区的偏移地址)
  • B:文件夹名称
  • C:生成的bin文件名称

二进制文件烧录到自定义分区

使用 ESP32 FLASH DOWNLOAD TOOL 对 audio.bin 文件进行烧录

按照上面步骤对 audio 分区的设定,将 audio.bin 烧录在 @ 0x110000 ,选择相应 COM 口,点击 START 烧录

ps:如果报错 Permission Denied ,将 ESP32 FLASH DOWNLOAD TOOL 文件夹里的 dl-temp 删掉,重新烧录

音频播放代码

几个需要根据自身情况修改的地方:

  • BCK、WS、DATA 引脚根据 ESP32 情况去修改
  • esp_vfs_spiffs_conf_t 结构体里的 max_files 的值根据音频文件数量去修改
  • fopen(“/audio/what_edit.wav”, “rb”) 根据要播放的文件名称去修改

完整代码如下:

#include <stdio.h>
#include <string.h>
#include "driver/i2s.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_spiffs.h"

typedef struct 
{
    //   RIFF Section    
    char RIFFSectionID[4];      // Letters "RIFF"
    uint32_t Size;              // Size of entire file less 8
    char RiffFormat[4];         // Letters "WAVE"
    
    //   Format Section    
    char FormatSectionID[4];    // letters "fmt"
    uint32_t FormatSize;        // Size of format section less 8
    uint16_t FormatID;          // 1=uncompressed PCM
    uint16_t NumChannels;       // 1=mono,2=stereo
    uint32_t SampleRate;        // 44100, 16000, 8000 etc.
    uint32_t ByteRate;          // =SampleRate * Channels * (BitsPerSample/8)
    uint16_t BlockAlign;        // =Channels * (BitsPerSample/8)
    uint16_t BitsPerSample;     // 8,16,24 or 32
    
    // Data Section
    char DataSectionID[4];      // The letters "data"
    uint32_t DataSize;          // Size of the data that follows
}WavHeader_Struct;

WavHeader_Struct WavHeader;

bool ValidWavData(WavHeader_Struct* Wav);
void sound_terminate(void);

void app_main()
{
    // 初始化I2S引脚
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX,              // 使用主模式并设置为发送数据
        .sample_rate = 44100,                               // 设置采样率为44100Hz
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,       // 设置每个采样点的位数为16位
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,       // 只使用右声道
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,  // I2S通信格式
        .dma_buf_count = 8,                                 // 设置DMA缓冲区数量为8
        .dma_buf_len = 1024,                                // 每个DMA缓冲区的长度为1024字节
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,           // 分配中断标志
    };
    i2s_pin_config_t pin_config = {
        .bck_io_num = 26,            // BCLK引脚号
        .ws_io_num = 27,             // LRCK引脚号
        .data_out_num = GPIO_NUM_25, // DATA引脚号
        .data_in_num = -1,           // DATA_IN引脚号
    };
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM_0, &pin_config);

    // 使用 SPIFFS 进行文件系统的初始化
    esp_vfs_spiffs_conf_t conf = {
    .base_path = "/audio",
    .partition_label = "audio",
    .max_files = 5,                // 包含的最大文件数
    .format_if_mount_failed = true
    };
    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    // 打开文件进行读取
    FILE* f = fopen("/audio/what_edit.wav", "rb");

    if (f == NULL) 
    {
        ESP_LOGI("AUDIO", "Failed to open file for reading");
    } 
    else 
    {
        char wavBuffer[1024];   // 接收缓冲区

        // 读取wav文件信息
        fseek(f, 0, SEEK_SET);  // 重新将指针指向文件首部
        fread(wavBuffer, sizeof(char), 44, f);
        memcpy(&WavHeader, wavBuffer, 44);
        if(ValidWavData(&WavHeader))
        {
            i2s_set_sample_rates(I2S_NUM_0, WavHeader.SampleRate);
        }
        else
        {
            ESP_LOGI("AUDIO", "Failed to read wav file");
        }
        uint32_t wavData_size = WavHeader.DataSize; // 保存文件字符数
        uint16_t readTimes = 0;                     // 需要读的次数
        size_t BytesWritten;

        for (readTimes = 0; readTimes < (wavData_size / 1024); readTimes++)
        {
            fread(wavBuffer, sizeof(char), 1024, f); // 读文件
            i2s_write(I2S_NUM_0, wavBuffer, 1024, &BytesWritten, portMAX_DELAY);
        }

        memset(wavBuffer, 0, 1024);                               // 清空,准备读少于1024的字节
        fread(wavBuffer, sizeof(char), (wavData_size % 1024), f); // 读文件

        i2s_write(I2S_NUM_0, wavBuffer, (wavData_size % 1024) + 20, &BytesWritten, portMAX_DELAY);

        printf("readtimes=%d,last read len = %d
", readTimes, (wavData_size % 1024));

        printf("sound play over
");
        sound_terminate(); //清空缓存
    }

    // 关闭文件
    fclose(f);

    // 卸载 SPIFFS 文件系统
    esp_vfs_spiffs_unregister(NULL);
    // 卸载 I2S 驱动
    i2s_driver_uninstall(I2S_NUM_0); 
}

bool ValidWavData(WavHeader_Struct* Wav)
{
    if(memcmp(Wav->RIFFSectionID,"RIFF",4)!=0) 
    {    
        printf("Invlaid data - Not RIFF format");
        return false;        
    }
    if(memcmp(Wav->RiffFormat,"WAVE",4)!=0)
    {
        printf("Invlaid data - Not Wave file");
        return false;           
    }
    if(memcmp(Wav->FormatSectionID,"fmt",3)!=0) 
    {
        printf("Invlaid data - No format section found");
        return false;       
    }
    if(memcmp(Wav->DataSectionID,"data",4)!=0) 
    {
        printf("Invlaid data - data section not found");
        return false;      
    }
    if(Wav->FormatID!=1) 
    {
        printf("Invlaid data - format Id must be 1");
        return false;                          
    }
    if(Wav->FormatSize!=16) 
    {
        printf("Invlaid data - format section size must be 16.");
        return false;                          
    }
    if((Wav->NumChannels!=1)&(Wav->NumChannels!=2))
    {
        printf("Invlaid data - only mono or stereo permitted.");
        return false;   
    }
    if(Wav->SampleRate>48000) 
    {
        printf("Invlaid data - Sample rate cannot be greater than 48000");
        return false;                       
    }
    if((Wav->BitsPerSample!=8)& (Wav->BitsPerSample!=16)) 
    {
        printf("Invlaid data - Only 8 or 16 bits per sample permitted.");
        return false;                        
    }
    return true;
}

void sound_terminate(void)
{
    vTaskDelay(pdMS_TO_TICKS(200)); //延时200ms
    i2s_zero_dma_buffer(I2S_NUM_0);   // Clean the DMA buffer
    i2s_stop(I2S_NUM_0);
    i2s_start(I2S_NUM_0);//增加测试
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。