【esp32-s3】7.2 I2S——播放wav文件

该博客介绍了如何在ESP32上实现I2S数字麦克风录音和播放功能。通过示例代码展示了如何配置硬件接口、设置I2S参数、读写SD卡上的WAV文件,并提供了文件操作和目录遍历的实现。代码中包含了播放指定WAV文件的功能,以及记录指定时长音频到SD卡的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 前言

本章实现了播放和录制功能。

2 硬件

在这里插入图片描述

3 代码

/* I2S Digital Microphone Recording Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "sdkconfig.h"

#include <dirent.h>     // dir struct
#include "esp_vfs.h" 
#define DIR_NUM_MAX 10 

static const char* TAG = "pdm_rec_example";

#define SPI_DMA_CHAN        SPI_DMA_CH_AUTO
#define SD_MOUNT_POINT      "/sdcard"

#define I2S_REC_CHANNEL        (1) // For mono recording only!
#define I2S_REC_BIT (16)
#define I2S_REC_SAMPLE_RATE (36000)
#define I2S_SAMPLE_SIZE         (I2S_REC_BIT * 1024)
#define I2S_BYTE_RATE           (I2S_REC_SAMPLE_RATE * (I2S_REC_BIT / 8)) * I2S_REC_CHANNEL

#define I2S_NUM 0

#define I2S_REC_TIME 5  //set record time seconds

#define I2S_PIN_BCK_GPIO 41
#define I2S_PIN_WS_GPIO 40
#define I2S_PIN_DATA_RECORD 42
#define I2S_PIN_DATA_PLAYBACK 45

// When testing SD and SPI modes, keep in mind that once the card has been
// initialized in SPI mode, it can not be reinitialized in SD mode without
// toggling power to the card.
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
sdmmc_card_t* card;

#define SPI_MOSI_GPIO 35
#define SPI_MISO_GPIO 37
#define SPI_SCLK_GPIO 36
#define SPI_CS_GPIO 34

static int16_t i2s_readraw_buff[I2S_SAMPLE_SIZE];
size_t bytes_read;
const int WAVE_HEADER_SIZE = 44;

// char wav_list[10];
struct wavinfo
{
    char *name;       // wav file path
    bool status;      // playing is 1, stop is 0
    int current_time;    // last stop time

    int bit;          // bit
    int channel;        
    int sample_rate;
    int while_time;   // sample num
    int format;
    struct wavinfo * next;
};
#define WAV_LIST_MAX 10
struct wavinfo *wav_list = NULL;
struct wavinfo list[WAV_LIST_MAX];
int current_list_num = 0;
struct wavinfo current_wav_msg;
struct wavinfo temp_wavinfo;

#define DIR_NAM_LEN_MAX 50
char dir_temp[DIR_NAM_LEN_MAX];

struct wavinfo get_wav_msg(char * wav_name)
{
    for(int i=0;i<current_list_num;i++)
    {
        if(strstr(list[i].name,wav_name))
        {
            printf("has searched wav\n");
            printf("name: %s\n",list[i].name);
            return list[i];
        }
    }
    printf("not find wav %s\n",wav_name);
    return list[0];
}

char* char_change_A2a(char *raw_char)
{
    char *temp = raw_char ;
    int len = strlen(raw_char);
    // printf("str = %s, len = %d\n",raw_char,len);
    for(int a = 0; a<len; a++)
    {
        // printf("str: %c\t",temp[a]);
        if('A'<=temp[a]&&temp[a]<='Z')
            temp[a]=temp[a]+32;
    }
    // printf("temp = %s\n",temp);
    // printf("raw_char = %s\n",raw_char);
    return temp;
}

void dir_list(char *path)
{
    char *dir_name;
    if(path != NULL)
        dir_name = path;
    else
        dir_name = SD_MOUNT_POINT;//"/t1";
        
    ESP_LOGI(TAG, "Opening dir %s", dir_name);

    DIR *dir_t1 = opendir(dir_name);

    int count = 0;
    char* names[DIR_NUM_MAX];

    printf("DIR: %s\n",dir_name);
    while(1) {
        struct dirent* de = readdir(dir_t1);
        if (!de) {
            break;
        }
        
        names[count] = char_change_A2a(de->d_name);

        printf("\t%s\n", de->d_name);
        // printf("\t%s type = %d\n", de->d_name, de->d_type);

        // dir
        if(de->d_type == 2)
        {
            strcpy(dir_temp,dir_name);
            strcat(dir_temp,"/");
            strcat(dir_temp,de->d_name);
            printf("this is dir:%s\n",dir_temp);

            char * temp_cpr;
            temp_cpr = "system~1";
            // printf("%s,%s",de->d_name , temp_cpr);
            if(strcmp(de->d_name,temp_cpr)==0)
                printf("ignore this dir\n");
            else
            {
                // strcpy(dir_temp,"");
                dir_list(dir_temp);
            }
        }


        // search wav file 
        char* search_key = ".wav";
        // printf("search wav = %s\n",strstr(de->d_name,search_key));
        if(strstr(de->d_name,search_key) != NULL)
        {
            // printf("this is wav file: %s\n",de->d_name);
            int file_name_len = strlen(dir_name)+strlen(de->d_name);
            // printf("file name len = %d\n",file_name_len);
            char *temp_path = (char *)malloc(sizeof(char) * (file_name_len+2));
            strcpy(temp_path,dir_name);
            strcat(temp_path,"/");
            strcat(temp_path,de->d_name);
            printf("file path = %s\n",temp_path);

            struct wavinfo *p1 ;
            // // p1 = (struct wavinfo*)malloc(sizeof(struct wavinfo));
            // printf("1\n");
            p1 = &list[current_list_num];
            current_list_num++;
            
            // printf("1\n");
            p1->name = temp_path;

            // printf("1\n");
            printf("p1->name = %s\n",p1->name);
            // printf("1\n");


            // struct wavinfo * p1 ;
            // p1 = (struct wavinfo*)malloc(sizeof(struct wavinfo));
            // printf("1\n");
            // p1->name = temp_path;
            // printf("1\n");
            // list[current_list_num]=*p1;
            // printf("1\n");
            // current_list_num++;
            // printf("1\n");
            // printf("p1->name = %s\n",p1->name);
            // printf("1\n");
        }

        ++count;
    }

    closedir(dir_t1);
}

void print_wav_list()
{
    printf("--------- wav list --------%d\n",current_list_num);

    for(int i=0;i<current_list_num;i++)
    {
        printf("\twav%d: %s\n",i,list[i].name);
    }
}

void test_sd_list()
{
    char *temp_path = SD_MOUNT_POINT;
    dir_list(temp_path);
    // temp_path = SD_MOUNT_POINT"/t1";
    // dir_list(temp_path);
    print_wav_list();

    // printf("wav1: %s \nwav2: %s\n",list[0].name,list[1].name);
}

void mount_sdcard(void)
{
    esp_err_t ret;
    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        .max_files = 5,
        .allocation_unit_size = 8 * 1024
    };
    ESP_LOGI(TAG, "Initializing SD card");

    spi_bus_config_t bus_cfg = {
        .mosi_io_num = SPI_MOSI_GPIO,
        .miso_io_num = SPI_MISO_GPIO,
        .sclk_io_num = SPI_SCLK_GPIO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000,
    };
    ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize bus.");
        return;
    }

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = SPI_CS_GPIO;
    slot_config.host_id = host.slot;

    ret = esp_vfs_fat_sdspi_mount(SD_MOUNT_POINT, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return;
    }

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);
}

void generate_wav_header(char* wav_header, uint32_t wav_size, uint32_t sample_rate){

    // See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
    uint32_t file_size = wav_size + WAVE_HEADER_SIZE - 8;
    uint32_t byte_rate = I2S_BYTE_RATE;

    const char set_wav_header[] = {
        'R','I','F','F', // ChunkID
        file_size, file_size >> 8, file_size >> 16, file_size >> 24, // ChunkSize
        'W','A','V','E', // Format
        'f','m','t',' ', // Subchunk1ID
        0x10, 0x00, 0x00, 0x00, // Subchunk1Size (16 for PCM)
        0x01, 0x00, // AudioFormat (1 for PCM)
        0x01, 0x00, // NumChannels (1 channel)
        sample_rate, sample_rate >> 8, sample_rate >> 16, sample_rate >> 24, // SampleRate
        byte_rate, byte_rate >> 8, byte_rate >> 16, byte_rate >> 24, // ByteRate
        0x02, 0x00, // BlockAlign
        0x10, 0x00, // BitsPerSample (16 bits)
        'd','a','t','a', // Subchunk2ID
        wav_size, wav_size >> 8, wav_size >> 16, wav_size >> 24, // Subchunk2Size
    };

    memcpy(wav_header, set_wav_header, sizeof(set_wav_header));
}

void playback_wav(char *wav_name)
{
    printf("wav name is %s\n",wav_name);
    FILE* f_wav_play = fopen(wav_name, "r");
    if (f_wav_play == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }

    char wav_header_fmt_play[WAVE_HEADER_SIZE];

    fread(wav_header_fmt_play, 1, WAVE_HEADER_SIZE, f_wav_play);

    // printf("wav play head: %2x\n",wav_header_fmt_play[0]);

    char temp_cpr[8];
    strncpy(temp_cpr,wav_header_fmt_play,4);
    if(strstr(temp_cpr , "RIFF"))
    {
        // printf("this is wav file\n");
        strncpy(temp_cpr,wav_header_fmt_play+8,8);
        if(strstr(temp_cpr , "WAVEfmt "))
        {
            printf("this is wav file \n");

            current_wav_msg = get_wav_msg(wav_name);

            // wav size
            printf("wav size = %02x, %02x, %02x, %02x\n",wav_header_fmt_play[40],wav_header_fmt_play[41],wav_header_fmt_play[42],wav_header_fmt_play[43]);

            current_wav_msg.while_time = wav_header_fmt_play[40] + wav_header_fmt_play[41]* (1<<8) + wav_header_fmt_play[42]* (1<<16) + wav_header_fmt_play[43] * (1<<24);
            printf("while_time = %d\n",current_wav_msg.while_time);

            // wav sample rate
            current_wav_msg.sample_rate = wav_header_fmt_play[24] + wav_header_fmt_play[25]* (1<<8) + wav_header_fmt_play[26]* (1<<16) + wav_header_fmt_play[27] * (1<<24);
            printf("sample_rate = %d\n",current_wav_msg.sample_rate);

            int temp_current_time = 0;
            bytes_read = 0;

            // Start playback
            while (temp_current_time < current_wav_msg.while_time) {
                // Read the RAW samples from the microphone (char *)

                fread(i2s_readraw_buff, 1, bytes_read, f_wav_play);

                i2s_write(I2S_NUM, i2s_readraw_buff, I2S_SAMPLE_SIZE, &bytes_read, 100);

                temp_current_time += bytes_read;
                // Write the samples to the WAV file
                // printf("%ls\n",i2s_readraw_buff);
                printf("temp_current_time = %d\n",temp_current_time);
            }
        }
    }

    // ESP_LOGI(TAG, "Recording done!");
    fclose(f_wav_play);
    ESP_LOGI(TAG, "File read from SDCard");

}

void record_wav(uint32_t rec_time)
{
    // Use POSIX and C standard library functions to work with files.
    int flash_wr_size = 0;
    ESP_LOGI(TAG, "Opening file");

    char wav_header_fmt[WAVE_HEADER_SIZE];

    uint32_t flash_rec_time = I2S_BYTE_RATE * rec_time;
    generate_wav_header(wav_header_fmt, flash_rec_time, I2S_REC_SAMPLE_RATE);

    // First check if file exists before creating a new file.
    struct stat st;
    if (stat(SD_MOUNT_POINT"/record.wav", &st) == 0) {
        // Delete it if it exists
        unlink(SD_MOUNT_POINT"/record.wav");
    }

    // Create new WAV file
    FILE* f = fopen(SD_MOUNT_POINT"/record.wav", "a");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }

    // Write the header to the WAV file
    fwrite(wav_header_fmt, 1, WAVE_HEADER_SIZE, f);

    // Start recording
    while (flash_wr_size < flash_rec_time) {
        // Read the RAW samples from the microphone (char *)
        i2s_read(I2S_NUM, i2s_readraw_buff, I2S_SAMPLE_SIZE, &bytes_read, 100);
        // Write the samples to the WAV file
        // printf("%ls\n",i2s_readraw_buff);
        fwrite(i2s_readraw_buff, 1, bytes_read, f);
        flash_wr_size += bytes_read;

        printf("flash_wr_size = %d\n",flash_wr_size);
    }

    ESP_LOGI(TAG, "Recording done!");
    fclose(f);
    ESP_LOGI(TAG, "File written on SDCard");
}

void init_i2s(void)
{
    // Set the I2S configuration as PDM and 16bits per sample
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_RX  | I2S_MODE_TX , // | I2S_MODE_PDM,
        .sample_rate = I2S_REC_SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
        .dma_buf_count = 8,
        .dma_buf_len = 1024,
        .use_apll = 0,
    };

    // Set the pinout configuration (set using menuconfig)
    i2s_pin_config_t pin_config = {
        .mck_io_num = I2S_PIN_NO_CHANGE,
        .bck_io_num = I2S_PIN_BCK_GPIO,
        .ws_io_num = I2S_PIN_WS_GPIO,
        .data_out_num = I2S_PIN_DATA_PLAYBACK,
        .data_in_num = I2S_PIN_DATA_RECORD,
    };

    // Call driver installation function before any I2S R/W operation.
    ESP_ERROR_CHECK( i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL) );
    ESP_ERROR_CHECK( i2s_set_pin(I2S_NUM, &pin_config) );
    ESP_ERROR_CHECK( i2s_set_clk(I2S_NUM, I2S_REC_SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO) );
}

void app_main(void)
{
    ESP_LOGI(TAG, "PDM microphone recording Example start");
    // Mount the SDCard for recording the audio file
    mount_sdcard();

    test_sd_list();

    // Init the PDM digital microphone
    init_i2s();
    ESP_LOGI(TAG, "Starting recording for %d seconds!", I2S_REC_TIME);
    // Start Recording
    // record_wav(I2S_REC_TIME);

    char * wav_play = "/sdcard/music60.wav";
    // playback_wav(wav_play);
    // wav_play = "/sdcard/music70.wav";
    // playback_wav(wav_play);
    wav_play = "/sdcard/music120.wav";
    playback_wav(wav_play);

    // All done, unmount partition and disable SPI peripheral
    esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, card);
    ESP_LOGI(TAG, "Card unmounted");
    // Deinitialize the bus after all devices are removed
    spi_bus_free(host.slot);

    // Stop I2S driver and destroy
    ESP_ERROR_CHECK( i2s_driver_uninstall(I2S_NUM) );
}

4 结果

在这里插入图片描述

### 关于ESP32-S3在Arduino环境下的I2C通信 #### 使用ESP32-S3进行I2C通信的基础配置 为了使ESP32-S3能够在Arduino环境中实现I2C通信,需先安装对应的板级支持包。这可以通过Arduino IDE的首选项页面完成,在附加开发板管理器网址中加入ESP32的支持链接[^1]。 一旦完成了上述准备工作,则可以利用Wire库来编写程序以操作I2C总线上的设备。对于Seeed XIAO ESP32S3 Sense而言,其丰富的外设接口使得它非常适合用于构建各种依赖I2C协议的应用场景,比如连接温湿度传感器或是其他类型的数字传感器等[^2]。 下面是一个简单的例子,展示了如何初始化并使用I2C总线与一个假设地址为0x76的器件交换数据: ```cpp #include <Wire.h> #define SDA_PIN 21 // 定义SDA引脚编号 #define SCL_PIN 22 // 定义SCL引脚编号 #define DEVICE_ADDRESS 0x76 // 器件I2C地址 void setup() { Serial.begin(115200); Wire.begin(SDA_PIN, SCL_PIN); // 初始化I2C总线 delay(100); } void loop() { uint8_t data_to_send[] = {0xAA}; // 准备发送的数据 int bytes_received; // 向指定地址写入命令字节 Wire.beginTransmission(DEVICE_ADDRESS); Wire.write(data_to_send, sizeof(data_to_send)); if (Wire.endTransmission(true) != 0){ Serial.println("Failed to send command."); } // 请求接收来自该地址的数据 Wire.requestFrom((int)DEVICE_ADDRESS, 1); while(Wire.available()){ char c = Wire.read(); Serial.print(c, HEX); Serial.print(&#39; &#39;); } } ``` 此代码片段实现了向特定I2C地址发送一条消息以及请求回传响应的功能。实际应用时应根据具体硬件调整`DEVICE_ADDRESS`和其他参数值。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值