網站首頁 編程語言 正文
一、前言
一開始本來在網上找代碼,不過改了好幾個都不是很好用。因為很多wav文件的fmt塊后面并不是data塊,經常還帶有其他塊,正確的方法應該是按MSDN的方法,找到data塊再讀取。
二、接口
最后接口如下:
class AudioReader
{
public:
struct PCM
{
int _numChannel;//通道數 1,2 AL_FORMAT_MONO8,AL_FORMAT_STEREO8
int _bitPerSample;//采樣數 8,16
byte* _data;
size_t _size;
size_t _freq;//采樣率
void Delete() { delete[] _data; }
};
static bool ReadWAV(string_view path_name, PCM& pcm);
};
三、具體步驟
打開文件,這里就是普通的文件流,按二進制、只讀打開文件即可:
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("匹配標記失敗(fourccWAVE):{}", path_name));
return false;
}
其中fourccRIFF和fourccWAVE是我們定義的標記,也就是處理了下大小端,如下:
#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兩個函數,分別是查找一個塊,和讀取一個塊。代碼實現有點長,可以參考后面我給出的完整源碼。
接著,查找并讀取fmt塊,這個塊描述了wav文件的音頻屬性,結構如下(部分字段會用到):
//16字節
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("讀取塊失敗(wave_format):{}", path_name));
return false;
};
接下來查找data塊,根據返回的大小分配內存:
//查找音頻數據
if (!FindChunk(ifs, fourccDATA, dwChunkSize, dwChunkPosition))
{
debug_err(format("查找塊失敗(fourccDATA):{}", path_name));
return false;
};
pcm._data = new byte[dwChunkSize];
然后讀取data塊,將數據讀取到我們分配的內存pcm._data。然后記錄下一些重要的字段。由于OpenaAL不能直接播放32位(只8、16)的數據,這里簡單返回失敗。
if (!ReadChunkData(ifs, pcm._data, dwChunkSize, dwChunkPosition))
{
debug_err(format("讀取塊失敗(pcm數據):{}", 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;//通道數 1,2 AL_FORMAT_MONO8,AL_FORMAT_STEREO8
int _bitPerSample;//采樣數 8,16
byte* _data;
size_t _size;
size_t _freq;//采樣率
void Delete() { delete[] _data; }
};
static bool ReadWAV(string_view path_name, PCM& pcm);
};
//16字節
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("匹配標記失敗(fourccWAVE):{}", path_name));
return false;
}
//查找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("讀取塊失敗(wave_format):{}", path_name));
return false;
};
//查找音頻數據
if (!FindChunk(ifs, fourccDATA, dwChunkSize, dwChunkPosition))
{
debug_err(format("查找塊失敗(fourccDATA):{}", path_name));
return false;
};
pcm._data = new byte[dwChunkSize];
if (!ReadChunkData(ifs, pcm._data, dwChunkSize, dwChunkPosition))
{
debug_err(format("讀取塊失敗(pcm數據):{}", 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;
}
原文鏈接:https://blog.csdn.net/u014629601/article/details/126627528
相關推薦
- 2024-03-10 【Redis】Redis的持久化(備份)
- 2022-06-13 Python標準庫之time庫的使用教程詳解_python
- 2023-01-10 Flutter圖片緩存管理ImageCache原理分析_Android
- 2022-07-25 View事件分發原理和ViewPager+ListView嵌套滑動沖突_Android
- 2022-05-01 Entity?Framework系統架構與原理介紹_基礎應用
- 2023-09-12 springboot將jar改成war
- 2024-04-07 linux系統中防火墻(firewall)的操作(開啟端口,查看端口)
- 2023-03-04 C語言模擬實現字符串庫函數的示例講解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支