C++解析wav文件方法介紹
一、前言
一開始本來在網(wǎng)上找代碼,不過改了好幾個都不是很好用。因?yàn)楹芏鄔av文件的fmt塊后面并不是data塊,經(jīng)常還帶有其他塊,正確的方法應(yīng)該是按MSDN的方法,找到data塊再讀取。
二、接口
最后接口如下:
class AudioReader { public: struct PCM { int _numChannel;//通道數(shù) 1,2 AL_FORMAT_MONO8,AL_FORMAT_STEREO8 int _bitPerSample;//采樣數(shù) 8,16 byte* _data; size_t _size; size_t _freq;//采樣率 void Delete() { delete[] _data; } }; static bool ReadWAV(string_view path_name, PCM& pcm); };
三、具體步驟
打開文件,這里就是普通的文件流,按二進(jìn)制、只讀打開文件即可:
ifstream ifs; if (!g_file->GetFile(path_name, ifs)) { debug_err(format("打開文件失?。簕}", path_name)); return false; }
查找riff塊:
uint32_t dwChunkSize; uint32_t dwChunkPosition; //查找riff塊 FindChunk(ifs, fourccRIFF, dwChunkSize, dwChunkPosition); uint32_t filetype; ReadChunkData(ifs, &filetype, sizeof(uint32_t), dwChunkPosition); if (filetype != fourccWAVE) { debug_err(format("匹配標(biāo)記失敗(fourccWAVE):{}", path_name)); return false; }
其中fourccRIFF和fourccWAVE是我們定義的標(biāo)記,也就是處理了下大小端,如下:
#ifdef DND_ENDIAN_BIG #define fourccRIFF 'RIFF' #define fourccDATA 'data' #define fourccFMT 'fmt ' #define fourccWAVE 'WAVE' #define fourccXWMA 'XWMA' #define fourccDPDS 'dpds' #endif #ifdef DND_ENDIAN_LITTLE #define fourccRIFF 'FFIR' #define fourccDATA 'atad' #define fourccFMT ' tmf' #define fourccWAVE 'EVAW' #define fourccXWMA 'AMWX' #define fourccDPDS 'sdpd' #endif
而FindChunk和ReadChunkData兩個函數(shù),分別是查找一個塊,和讀取一個塊。代碼實(shí)現(xiàn)有點(diǎn)長,可以參考后面我給出的完整源碼。
接著,查找并讀取fmt塊,這個塊描述了wav文件的音頻屬性,結(jié)構(gòu)如下(部分字段會用到):
//16字節(jié) struct WAVEFormat { int16_t audioFormat; int16_t numChannels; int32_t sampleRate; int32_t byteRate; int16_t blockAlign; int16_t bitsPerSample; };
//查找fmt塊 if (!FindChunk(ifs, fourccFMT, dwChunkSize, dwChunkPosition)) { debug_err(format("查找塊失敗(fourccFMT):{}", path_name)); return false; } //讀wave信息 WAVEFormat wave_format; if (!ReadChunkData(ifs, &wave_format, dwChunkSize, dwChunkPosition)) { debug_err(format("讀取塊失?。╳ave_format):{}", path_name)); return false; };
接下來查找data塊,根據(jù)返回的大小分配內(nèi)存:
//查找音頻數(shù)據(jù) if (!FindChunk(ifs, fourccDATA, dwChunkSize, dwChunkPosition)) { debug_err(format("查找塊失?。╢ourccDATA):{}", path_name)); return false; }; pcm._data = new byte[dwChunkSize];
然后讀取data塊,將數(shù)據(jù)讀取到我們分配的內(nèi)存pcm._data。然后記錄下一些重要的字段。由于OpenaAL不能直接播放32位(只8、16)的數(shù)據(jù),這里簡單返回失敗。
if (!ReadChunkData(ifs, pcm._data, dwChunkSize, dwChunkPosition)) { debug_err(format("讀取塊失敗(pcm數(shù)據(jù)):{}", path_name)); pcm.Delete(); return false; }; pcm._size = dwChunkSize; pcm._numChannel = wave_format.numChannels; pcm._bitPerSample = wave_format.bitsPerSample; pcm._freq = wave_format.sampleRate; if (pcm._bitPerSample == 32) { debug_err(format("不支持32位:{}", path_name)); pcm.Delete(); return false; } return true;
四、完整源碼
可以此處獲取最新的源碼(我將來會添加ogg格式的解析),也可以用下面的:傳送門
//.h class AudioReader { public: struct PCM { int _numChannel;//通道數(shù) 1,2 AL_FORMAT_MONO8,AL_FORMAT_STEREO8 int _bitPerSample;//采樣數(shù) 8,16 byte* _data; size_t _size; size_t _freq;//采樣率 void Delete() { delete[] _data; } }; static bool ReadWAV(string_view path_name, PCM& pcm); }; //16字節(jié) struct WAVEFormat { int16_t audioFormat; int16_t numChannels; int32_t sampleRate; int32_t byteRate; int16_t blockAlign; int16_t bitsPerSample; }; //.cpp #ifdef DND_ENDIAN_BIG #define fourccRIFF 'RIFF' #define fourccDATA 'data' #define fourccFMT 'fmt ' #define fourccWAVE 'WAVE' #define fourccXWMA 'XWMA' #define fourccDPDS 'dpds' #endif #ifdef DND_ENDIAN_LITTLE #define fourccRIFF 'FFIR' #define fourccDATA 'atad' #define fourccFMT ' tmf' #define fourccWAVE 'EVAW' #define fourccXWMA 'AMWX' #define fourccDPDS 'sdpd' #endif bool FindChunk(ifstream& ifs, uint32_t fourcc, uint32_t& size, uint32_t& pos) { bool ret = true; ifs.seekg(0); if (ifs.fail()) return false; uint32_t dwChunkType; uint32_t dwChunkDataSize; uint32_t dwRIFFDataSize = 0; uint32_t dwFileType; uint32_t bytesRead = 0; uint32_t dwOffset = 0; while (ret) { ifs.read((char*)&dwChunkType, sizeof(uint32_t)); ifs.read((char*)&dwChunkDataSize, sizeof(uint32_t)); switch (dwChunkType) { case fourccRIFF: dwRIFFDataSize = dwChunkDataSize; dwChunkDataSize = 4; ifs.read((char*)&dwFileType, sizeof(uint32_t)); break; default: ifs.seekg(dwChunkDataSize, std::ios::cur); if (ifs.fail()) return false; break; } dwOffset += sizeof(uint32_t) * 2; if (dwChunkType == fourcc) { size = dwChunkDataSize; pos = dwOffset; return true; } dwOffset += dwChunkDataSize; if (bytesRead >= dwRIFFDataSize) return false; } return true; } bool ReadChunkData(ifstream& ifs, void* buffer, uint32_t size, uint32_t pos) { ifs.seekg(pos); if (ifs.fail()) return false; ifs.read((char*)buffer, size); return true; } bool AudioReader::ReadWAV(string_view path_name, PCM& pcm) { ifstream ifs; if (!g_file->GetFile(path_name, ifs)) { debug_err(format("打開文件失?。簕}", path_name)); return false; } uint32_t dwChunkSize; uint32_t dwChunkPosition; //查找riff塊 FindChunk(ifs, fourccRIFF, dwChunkSize, dwChunkPosition); uint32_t filetype; ReadChunkData(ifs, &filetype, sizeof(uint32_t), dwChunkPosition); if (filetype != fourccWAVE) { debug_err(format("匹配標(biāo)記失?。╢ourccWAVE):{}", path_name)); return false; } //查找fmt塊 if (!FindChunk(ifs, fourccFMT, dwChunkSize, dwChunkPosition)) { debug_err(format("查找塊失?。╢ourccFMT):{}", path_name)); return false; } //讀wave信息 WAVEFormat wave_format; if (!ReadChunkData(ifs, &wave_format, dwChunkSize, dwChunkPosition)) { debug_err(format("讀取塊失敗(wave_format):{}", path_name)); return false; }; //查找音頻數(shù)據(jù) if (!FindChunk(ifs, fourccDATA, dwChunkSize, dwChunkPosition)) { debug_err(format("查找塊失?。╢ourccDATA):{}", path_name)); return false; }; pcm._data = new byte[dwChunkSize]; if (!ReadChunkData(ifs, pcm._data, dwChunkSize, dwChunkPosition)) { debug_err(format("讀取塊失敗(pcm數(shù)據(jù)):{}", path_name)); pcm.Delete(); return false; }; pcm._size = dwChunkSize; pcm._numChannel = wave_format.numChannels; pcm._bitPerSample = wave_format.bitsPerSample; pcm._freq = wave_format.sampleRate; if (pcm._bitPerSample == 32) { debug_err(format("不支持32位:{}", path_name)); pcm.Delete(); return false; } return true; }
到此這篇關(guān)于C++解析wav文件方法介紹的文章就介紹到這了,更多相關(guān)C++解析wav內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++11中std::function與std::bind的用法實(shí)例
大家都知道C++11中增加了許多的新特性,下面這篇文章主要給大家介紹了關(guān)于C++11中std::function與std::bind的用法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05C++實(shí)現(xiàn)LeetCode(108.將有序數(shù)組轉(zhuǎn)為二叉搜索樹)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(108.將有序數(shù)組轉(zhuǎn)為二叉搜索樹),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C++實(shí)現(xiàn)反轉(zhuǎn)鏈表的兩種方法
本文主要介紹了C++實(shí)現(xiàn)反轉(zhuǎn)鏈表的兩種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02C語言實(shí)現(xiàn)掃雷小游戲(擴(kuò)展版)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)擴(kuò)展版的掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05C++詳細(xì)講解繼承與虛繼承實(shí)現(xiàn)
這篇文章主要介紹了Java中的繼承詳情,繼承是面向?qū)ο笕筇卣髦唬梢允沟米宇惥哂懈割惖膶傩院头椒?,還可以在子類中重新定義,以及追加屬性和方法,下文介紹需要的朋友可以參考下2022-04-04C++超詳細(xì)分析講解內(nèi)聯(lián)函數(shù)
為了消除函數(shù)調(diào)用的時空開銷,C++ 提供一種提高效率的方法,即在編譯時將函數(shù)調(diào)用處用函數(shù)體替換,類似于C語言中的宏展開。這種在函數(shù)調(diào)用處直接嵌入函數(shù)體的函數(shù)稱為內(nèi)聯(lián)函數(shù)(Inline Function),又稱內(nèi)嵌函數(shù)或者內(nèi)置函數(shù)2022-06-06