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

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 结果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38091632/article/details/124580249