diff --git a/examples/peripherals/i2s/common/format_wav.h b/examples/peripherals/i2s/common/format_wav.h new file mode 100644 index 0000000000..b84345eabc --- /dev/null +++ b/examples/peripherals/i2s/common/format_wav.h @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Header structure for WAV file with only one data chunk + * + * @note See this for reference: http://soundfile.sapp.org/doc/WaveFormat/ + * + * @note Assignment to variables in this struct directely is only possible for little endian architectures + * (including Xtensa & RISC-V) + */ +typedef struct { + struct { + char chunk_id[4]; /*!< Contains the letters "RIFF" in ASCII form */ + uint32_t chunk_size; /*!< This is the size of the rest of the chunk following this number */ + char chunk_format[4]; /*!< Contains the letters "WAVE" */ + } descriptor_chunk; /*!< Canonical WAVE format starts with the RIFF header */ + struct { + char subchunk_id[4]; /*!< Contains the letters "fmt " */ + uint32_t subchunk_size; /*!< This is the size of the rest of the Subchunk which follows this number */ + uint16_t audio_format; /*!< PCM = 1, values other than 1 indicate some form of compression */ + uint16_t num_of_channels; /*!< Mono = 1, Stereo = 2, etc. */ + uint32_t sample_rate; /*!< 8000, 44100, etc. */ + uint32_t byte_rate; /*!< ==SampleRate * NumChannels * BitsPerSample s/ 8 */ + uint16_t block_align; /*!< ==NumChannels * BitsPerSample / 8 */ + uint16_t bits_per_sample; /*!< 8 bits = 8, 16 bits = 16, etc. */ + } fmt_chunk; /*!< The "fmt " subchunk describes the sound data's format */ + struct { + char subchunk_id[4]; /*!< Contains the letters "data" */ + uint32_t subchunk_size; /*!< ==NumSamples * NumChannels * BitsPerSample / 8 */ + int16_t data[0]; /*!< Holds raw audio data */ + } data_chunk; /*!< The "data" subchunk contains the size of the data and the actual sound */ +} wav_header_t; + +/** + * @brief Default header for PCM format WAV files + * + */ +#define WAV_HEADER_PCM_DEFAULT(wav_sample_size, wav_sample_bits, wav_sample_rate, wav_channel_num) { \ + .descriptor_chunk = { \ + .chunk_id = {'R', 'I', 'F', 'F'}, \ + .chunk_size = (wav_sample_size) + sizeof(wav_header_t) - 8, \ + .chunk_format = {'W', 'A', 'V', 'E'} \ + }, \ + .fmt_chunk = { \ + .subchunk_id = {'f', 'm', 't', ' '}, \ + .subchunk_size = 16, /* 16 for PCM */ \ + .audio_format = 1, /* 1 for PCM */ \ + .num_of_channels = (wav_channel_num), \ + .sample_rate = (wav_sample_rate), \ + .byte_rate = (wav_sample_bits) * (wav_sample_rate) * (wav_channel_num) / 8, \ + .block_align = (wav_sample_bits) * (wav_channel_num) / 8, \ + .bits_per_sample = (wav_sample_bits)\ + }, \ + .data_chunk = { \ + .subchunk_id = {'d', 'a', 't', 'a'}, \ + .subchunk_size = (wav_sample_size) \ + } \ +} + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt b/examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt index 5ffc0c8df5..44a620a03e 100644 --- a/examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt +++ b/examples/peripherals/i2s/i2s_recorder/main/CMakeLists.txt @@ -1,2 +1,2 @@ idf_component_register(SRCS "i2s_recorder_main.c" - INCLUDE_DIRS ".") + INCLUDE_DIRS "../../common") diff --git a/examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c b/examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c index dbb17482f5..cf881e10b0 100644 --- a/examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c +++ b/examples/peripherals/i2s/i2s_recorder/main/i2s_recorder_main.c @@ -10,6 +10,7 @@ #include #include #include +#include "sdkconfig.h" #include "esp_log.h" #include "esp_err.h" #include "esp_system.h" @@ -20,7 +21,7 @@ #include "driver/gpio.h" #include "driver/spi_common.h" #include "sdmmc_cmd.h" -#include "sdkconfig.h" +#include "format_wav.h" static const char *TAG = "pdm_rec_example"; @@ -90,40 +91,15 @@ void mount_sdcard(void) 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 = 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 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 = BYTE_RATE * rec_time; - generate_wav_header(wav_header_fmt, flash_rec_time, CONFIG_EXAMPLE_SAMPLE_RATE); + const wav_header_t wav_header = + WAV_HEADER_PCM_DEFAULT(flash_rec_time, 16, CONFIG_EXAMPLE_SAMPLE_RATE, 1); // First check if file exists before creating a new file. struct stat st; @@ -140,7 +116,7 @@ void record_wav(uint32_t rec_time) } // Write the header to the WAV file - fwrite(wav_header_fmt, 1, WAVE_HEADER_SIZE, f); + fwrite(&wav_header, sizeof(wav_header), 1, f); // Start recording while (flash_wr_size < flash_rec_time) { @@ -148,7 +124,7 @@ void record_wav(uint32_t rec_time) if (i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 1000) == ESP_OK) { printf("[0] %d [1] %d [2] %d [3]%d ...\n", i2s_readraw_buff[0], i2s_readraw_buff[1], i2s_readraw_buff[2], i2s_readraw_buff[3]); // Write the samples to the WAV file - fwrite(i2s_readraw_buff, 1, bytes_read, f); + fwrite(i2s_readraw_buff, bytes_read, 1, f); flash_wr_size += bytes_read; } else { printf("Read Failed!\n");