# wav_to_c_array **Repository Path**: AllenMaa/wav_to_c_array ## Basic Information - **Project Name**: wav_to_c_array - **Description**: 将WAV音频提取为C语言数组 在测试MCU的I2S时,一般需要通过读取SD卡中的WAV或MP3格式的音乐进行播放测试,考虑到快速独立验证和使用的简洁性,这里将WAV 格式的音乐直接转换为C数组的形式编译到源代码中直接进行播放使用。 - **Primary Language**: C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-03-26 - **Last Updated**: 2025-03-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # WAV音频数据提取 **--将WAV音频提取为C语言数组** ## 引言 在测试MCU的I2S时,一般需要通过读取SD卡中的WAV或MP3格式的音乐进行播放测试,考虑到快速独立验证和使用的简洁性,这里将WAV 格式的音乐直接转换为C数组的形式编译到源代码中直接进行播放使用。 ## 转换工具 通过搜索有部分现成工具,但考虑到并不是一个复杂的工具直接用Python实现即可。 这里使用python进行代码转换源码如下: ```python def wav_to_c_array(wav_file, output_file): # 以二进制模式读取WAV文件 with open(wav_file, 'rb') as wav: # 读取所有二进制数据 binary_data = wav.read() # 打开输出文件 with open(output_file, 'w') as f: # 写入C数组的声明 f.write(f"const uint8_t wav_data[{len(binary_data)}] = {{\n") # 每行显示16个字节 for i in range(0, len(binary_data), 16): chunk = binary_data[i:i+16] hex_values = ', '.join(f'0x{byte:02X}' for byte in chunk) f.write(f" {hex_values},\n") # 写入C数组的结束 f.write("};\n") print(f"WAV文件已成功转换为C数组并保存到 {output_file}") # 使用示例 wav_to_c_array('ding.wav', 'output.c') ``` ## 使用介绍 将上述代码保存为如`wav_to_c_array_tool.py`文件的形式,在Python代码最后一行中的`daing.wav`改为需要转换的对应`WAV`格式的文件名即可,同时将对应的`wav`文件复制到和该Python文件同一目录位置下,点击该文件直接运行即可,当件目录下即可生产`output.c`文件将该源码打开后便可看到转换后的C代码数组,如下: ```c const uint8_t wav_data[70060] = { 0x52, 0x49, 0x46, 0x46, 0xA4, 0x11, 0x01, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x10, 0xB1, 0x02, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x80, 0x11, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFD, 0xFF, ``` 将此数组复制到需要使用的源码中使用即可。 ## 实例演示 这里以电脑 Win10 自带的wav音乐`C:\Windows\Media\ding.wav`为例,转换后通过二进制查看软件对比可以验证数据的一致性。 ![image-20250319105135511](image/image-20250319105135511.png) 这里提供一个使用灵动型号为MM32L0180的32位 Cortex-M0+内核的MCU 进行实例演示: ### `main.c` ```c #include "bsp_i2s_audio.h" int main(void) { PLATFORM_InitDelay(); board_i2s_init(); while(1) { app_i2s_wav_play_task(); delay_ms(1000); } } ``` ### `bsp_i2s_audio.c` ```` #include "bsp_i2s_audio.h" #include "board.h" static volatile uint32_t audio_status = 0u; static volatile uint32_t record_status = 0u; uint8_t WAV_DataBuffer[2][WAV_BUFFER_SIZE]; uint8_t WAV_NextIndex = 0; uint8_t WAV_PlayEnded = 0; uint8_t WAV_PlayState = 0; uint32_t WAV_Offset = 0; uint32_t WAV_DataSize = 0; uint32_t WAV_TxLength = 0; uint32_t SPI_FLASH_RMEM[2]; uint32_t SPI_FLASH_TMEM[2]; void board_i2s_init(void); /* WAV C code data, Song:Ding.wav 16bit */ /* SampleRate:44.1kHz, BitRate:1411200. */ const uint8_t wav_data[70060] = { 0x52, 0x49, 0x46, 0x46, 0xA4, 0x11, 0x01, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x10, 0xB1, 0x02, 0x00, ``` 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; uint8_t WAV_DecodeFile(WAV_TypeDef *pWav, uint32_t Address) { ChunkRIFF_TypeDef *WAV_RIFF; ChunkFMT_TypeDef *WAV_FMT; ChunkFACT_TypeDef *WAV_FACT; ChunkDATA_TypeDef *WAV_DATA; uint8_t * WAV_HeadBuffer = (uint8_t *)wav_data; /* 获取RIFF块 */ WAV_RIFF = (ChunkRIFF_TypeDef *)WAV_HeadBuffer; /* 是WAV格式文件 */ if (WAV_RIFF->Format == 0x45564157) { /* 获取FMT块 */ WAV_FMT = (ChunkFMT_TypeDef *)(WAV_HeadBuffer + 12); /* 读取FACT块 */ WAV_FACT = (ChunkFACT_TypeDef *)(WAV_HeadBuffer + 12 + 8 + WAV_FMT->ChunkSize); if ((WAV_FACT->ChunkID == 0x74636166) || (WAV_FACT->ChunkID == 0x5453494C)) { /* 具有FACT/LIST块的时候(未测试) */ pWav->DataStart = 12 + 8 + WAV_FMT->ChunkSize + 8 + WAV_FACT->ChunkSize; } else { pWav->DataStart = 12 + 8 + WAV_FMT->ChunkSize; } /* 读取DATA块 */ WAV_DATA = (ChunkDATA_TypeDef *)(WAV_HeadBuffer + pWav->DataStart); /* 解析成功 */ if (WAV_DATA->ChunkID == 0x61746164) { pWav->AudioFormat = WAV_FMT->AudioFormat; /* 音频格式 */ pWav->nChannels = WAV_FMT->NumOfChannels; /* 通道数 */ pWav->SampleRate = WAV_FMT->SampleRate; /* 采样率 */ pWav->BitRate = WAV_FMT->ByteRate * 8; /* 得到位速 */ pWav->BlockAlign = WAV_FMT->BlockAlign; /* 块对齐 */ pWav->BitsPerSample = WAV_FMT->BitsPerSample; /* 位数,16/24/32位 */ pWav->DataSize = WAV_DATA->ChunkSize; /* 数据块大小 */ pWav->DataStart = pWav->DataStart + 8; /* 数据流开始的地方 */ printf("\r\npWav->AudioFormat : %d", pWav->AudioFormat); printf("\r\npWav->nChannels : %d", pWav->nChannels); printf("\r\npWav->SampleRate : %d", pWav->SampleRate); printf("\r\npWav->BitRate : %d", pWav->BitRate); printf("\r\npWav->BlockAlign : %d", pWav->BlockAlign); printf("\r\npWav->BitsPerSample : %d", pWav->BitsPerSample); printf("\r\npWav->DataSize : %d", pWav->DataSize); printf("\r\npWav->DataStart : %d", pWav->DataStart); WAV_Offset = pWav->DataStart + Address; WAV_DataSize = pWav->DataSize; } } return (0); } void my_memcpy(const void *src, void *dest, size_t len) { uint8_t *d = (uint8_t *)dest; const uint8_t *s = (const uint8_t *)src; for (size_t i = 0; i < len; i++) { d[i] = s[i]; } } void WAV_PrepareData(void) { if (WAV_NextIndex == 0) { my_memcpy((uint8_t *)(WAV_Offset + WAV_TxLength), WAV_DataBuffer[0], WAV_BUFFER_SIZE); } else { my_memcpy((uint8_t *)(WAV_Offset + WAV_TxLength), WAV_DataBuffer[1], WAV_BUFFER_SIZE); } WAV_TxLength += WAV_BUFFER_SIZE; if (WAV_TxLength > WAV_DataSize) { WAV_PlayEnded = 1; } } void I2S_DMA_Transfer(uint16_t *Buffer, uint32_t BufferSize) { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; DMA_DeInit(DMA_Channel1); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA, ENABLE); DMA_StructInit(&DMA_InitStructure); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->TXREG); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BufferSize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_InitStructure.DMA_Auto_Reload = DMA_Auto_Reload_Disable; DMA_Init(DMA_Channel1, &DMA_InitStructure); DMA_SetChannelMuxSource(DMA_Channel1, DMA_MUX_SPI1_TX); NVIC_InitStructure.NVIC_IRQChannel = DMA_Channel1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA_Channel1, DMA_IT_TC, ENABLE); DMA_ITConfig(DMA_Channel1, DMA_IT_HT, ENABLE); DMA_Cmd(DMA_Channel1, ENABLE); } void WAV_PlayHandler(void) { if (WAV_PlayEnded == 0) { I2S_DMA_Transfer((uint16_t *)&WAV_DataBuffer[WAV_NextIndex][0], (WAV_BUFFER_SIZE / 2)); } else { if (WAV_PlayState != 0) { WAV_PlayState = 0; DMA_Cmd(DMA_Channel1, DISABLE); printf("\r\nWAV Play Finish!\r\n"); } } if (WAV_NextIndex == 0) { WAV_NextIndex = 1; } else { WAV_NextIndex = 0; } } void DMA_Channel1_IRQHandler(void) { if (DMA_GetITStatus(DMA_IT_TC1) != RESET) { DMA_ClearITPendingBit(DMA_IT_TC1); WAV_PlayHandler(); } if (DMA_GetITStatus(DMA_IT_HT1) != RESET) { DMA_ClearITPendingBit(DMA_IT_HT1); WAV_PrepareData(); } } /* Init I2s for audio. */ void board_i2s_init(void) { GPIO_InitTypeDef GPIO_InitStruct; SPI_InitTypeDef SPI_InitStruct; I2S_InitTypeDef I2S_InitStruct; DMA_InitTypeDef DMA_InitStruct; SPI_Cmd(SPI1, DISABLE); SPI_DeInit(SPI1); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); /* I2S Init ------------------------------------------ */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0); //I2S2_CK(BCLK) GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_0); //I2S2_MCK(extSD) GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_0); //I2S2_SD(Serial Data) GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_6); //I2S2_WS(Word Select)/ FS(Frame Sync)/(Left/Right Clock Select) /* PC6 - I2S1_WS */ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOC, &GPIO_InitStruct); /* PA5 - I2S1_CK */ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStruct); /* PA6 - I2S1_MCK(extSD) */ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStruct); /* PD5 - I2S1_SD (DATA out)*/ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOD, &GPIO_InitStruct); I2S_InitStruct.I2S_Mode = I2S_Mode_MasterTx; I2S_InitStruct.I2S_Standard = I2S_Standard_Philips; I2S_InitStruct.I2S_DataFormat = I2S_DataFormat_16b; I2S_InitStruct.I2S_MCKOutput = I2S_MCKOutput_Enable; I2S_InitStruct.I2S_AudioFreq = I2S_AudioFreq_44k; I2S_InitStruct.I2S_CPOL = I2S_CPOL_Low; I2S_Init(SPI1, &I2S_InitStruct); SPI_DMACmd(SPI1, ENABLE); I2S_Cmd(SPI1, ENABLE); SPI_Cmd(SPI1, ENABLE); /* PD8 - I2S1_Standby*/ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOD, &GPIO_InitStruct); GPIO_WriteBit(GPIOD, GPIO_Pin_8, Bit_RESET); // Turn on audio. } void app_i2s_wav_play_task(void) { WAV_TypeDef WaveFile; if (WAV_PlayState == 0) { if (WAV_DecodeFile(&WaveFile, (uint32_t)wav_data) == 0) { if ((WaveFile.BitsPerSample == 16) && (WaveFile.nChannels == 2) && (WaveFile.SampleRate > 44000) && (WaveFile.SampleRate < 48100)) { WAV_NextIndex = 0; WAV_PlayEnded = 0; WAV_TxLength = 0; WAV_PlayState = 1; printf("\r\n"); printf("\r\nWAV Data Size : %d, Data Start : 0x%08x", WAV_DataSize, WAV_Offset); printf("\r\n"); WAV_PrepareData(); WAV_PlayHandler(); } else { printf("\r\nWAV File Format Error!\r\n"); return; } } else { printf("\r\nNo WAV File!\r\n"); return; } } else { printf("\r\nThe Song Is Not Over Yet!\r\n"); } } /* EOF. */ ```` ### `bsp_i2s_audio.h` ```h #ifndef __AUDIO_PORT_H__ #define __AUDIO_PORT_H__ #include #include #define AUDIO_STATUS_XFER_HALF (1u << 0u) #define AUDIO_STATUS_XFER_DONE (1u << 1u) #define RECORD_STATUS_XFER_HALF (1u << 0u) #define RECORD_STATUS_XFER_DONE (1u << 1u) #define WAV_BUFFER_SIZE (2 * 1024) /* Exported types *****************************************************************************************************/ typedef __packed struct { uint32_t ChunkID; /* 这里固定为"RIFF",即0x46464952 */ uint32_t ChunkSize ; /* 集合大小;文件总大小-8 */ uint32_t Format; /* 格式;WAVE,即0x45564157 */ } ChunkRIFF_TypeDef; typedef __packed struct { uint32_t ChunkID; /* 这里固定为"fmt ",即0x20746D66 */ uint32_t ChunkSize ; /* 子集合大小(不包括ID和Size);这里为:20 */ uint16_t AudioFormat; /* 音频格式;0x01,表示线性PCM;0x11表示IMA ADPCM */ uint16_t NumOfChannels; /* 通道数量;1,表示单声道;2,表示双声道 */ uint32_t SampleRate; /* 采样率;0x1F40,表示8Khz */ uint32_t ByteRate; /* 字节速率 */ uint16_t BlockAlign; /* 块对齐(字节) */ uint16_t BitsPerSample; /* 单个采样数据大小;4位ADPCM,设置为4 */ // uint16_t ByteExtraData; /* 附加的数据字节;2个; 线性PCM,没有这个参数 */ }ChunkFMT_TypeDef; typedef __packed struct { uint32_t ChunkID; /* 这里固定为"fact",即0x74636166 */ uint32_t ChunkSize; /* 子集合大小(不包括ID和Size);这里为:4 */ uint32_t NumOfSamples; /* 采样的数量; */ }ChunkFACT_TypeDef; typedef __packed struct { uint32_t ChunkID; /* 这里固定为"LIST",即0x74636166 */ uint32_t ChunkSize; /* 子集合大小(不包括ID和Size);这里为:4 */ }ChunkLIST_TypeDef; typedef __packed struct { uint32_t ChunkID; /* 这里固定为"data",即0x5453494C */ uint32_t ChunkSize; /* 子集合大小(不包括ID和Size) */ }ChunkDATA_TypeDef; typedef __packed struct { ChunkRIFF_TypeDef ChunkRIFF; /* RIFF块 */ ChunkFMT_TypeDef ChunkFMT; /* FMT块 */ // ChunkFACT_TypeDef ChunkFACT; /* FACT块 线性PCM,没有这个结构体 */ ChunkDATA_TypeDef ChunkDATA; /* DATA块 */ }WaveHeader_TypeDef; typedef __packed struct { uint16_t AudioFormat; /* 音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM */ uint16_t nChannels; /* 通道数量;1,表示单声道;2,表示双声道 */ uint16_t BlockAlign; /* 块对齐(字节) */ uint32_t DataSize; /* WAV数据大小 */ uint32_t Totalsecond; /* 整首歌时长,单位:秒 */ uint32_t CurrentSecond; /* 当前播放时长 */ uint32_t BitRate; /* 比特率(位速) */ uint32_t SampleRate; /* 采样率 */ uint16_t BitsPerSample; /* 位数,bsp,比如16bit,24bit,32bit */ uint32_t DataStart; /* 数据帧开始的位置(在文件里面的偏移) */ } WAV_TypeDef; /* Audio API */ void Audio_SetConf(uint32_t sample_rate, uint16_t * buf, uint32_t buf_len); void Audio_Enable(bool enable); uint32_t Audio_GetStatus(void); void Audio_ClearStatus(uint32_t status); /* Record API */ void Record_SetConf(uint16_t * buf, uint32_t buf_len); void Record_Enable(bool enable); uint32_t Record_GetStatus(void); void Record_ClearStatus(uint32_t status); void BOARD_I2S_Audio_Stop(void); void board_i2s_init(void); void app_i2s_wav_play_task(void); #endif /* __AUDIO_PORT_H__ */ ```