網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
wav文件通常會(huì)使用PCM格式數(shù)據(jù)存儲(chǔ)音頻,這種格式的數(shù)據(jù)讀取出來(lái)直接就可以播放,要在wav文件中讀取數(shù)據(jù),我們首先要獲取頭部信息,wav的文件結(jié)構(gòu)里面分為多個(gè)chunk,我們要做的就是識(shí)別這些chunk的信息,獲取音頻的格式以及數(shù)據(jù)。
一、如何實(shí)現(xiàn)?
首先需要構(gòu)造wav頭部,wav文件音頻信息全部保存在頭部,我們要做的就是讀取wav頭部信息,并且記錄PCM的相關(guān)參數(shù)。
1.定義頭結(jié)構(gòu)
只定義PCM格式的wav文件頭,對(duì)于PCM格式的數(shù)據(jù)只需要下面3個(gè)結(jié)構(gòu)體即可。
struct WaveRIFF; struct WaveFormat; struct WaveData;
2.讀取頭部信息
打開(kāi)文件后需要讀取頭部信息,需要獲取聲音的格式以及數(shù)據(jù)長(zhǎng)度。
WaveRIFF riff; WaveFormat format; WaveData data; int userDataSize; f= fopen(fileName.c_str(), "rb+"); //讀取頭部信息 fread(&riff, 1, sizeof(riff), f); fread(&format, 1, sizeof(format),f); //判讀頭部信息是否正確 //略 //查找data chunk //略 //記錄數(shù)據(jù)起始位置
3.讀取數(shù)據(jù)
獲取頭部信息后,就知道數(shù)據(jù)在位置及長(zhǎng)度了,只需要直接讀文件即可。
//跳到數(shù)據(jù)起始位置 seek(f, _dataOffset, SEEK_SET);
//讀取數(shù)據(jù) fread(buf, 1, bufLength, f);
二、完整代碼
完整代碼總用有3部分,頭結(jié)構(gòu)、WavFileReader.h、WavFileReader.cpp。
1.頭結(jié)構(gòu)
#pragma pack(push,1) ?? ?struct WaveRIFF { ?? ??? ?const?? ?char id[4] = { 'R','I', 'F', 'F' }; ?? ??? ?uint32_t fileLength; ?? ??? ?const?? ?char waveFlag[4] = { 'W','A', 'V', 'E' }; ?? ?}; ?? ?struct WaveFormat ?? ?{ ?? ??? ?const?? ?char id[4] = { 'f','m', 't', ' ' }; ?? ??? ?uint32_t blockSize = 16; ?? ??? ?uint16_t formatTag; ?? ??? ?uint16_t channels; ?? ??? ?uint32_t samplesPerSec; ?? ??? ?uint32_t avgBytesPerSec; ?? ??? ?uint16_t blockAlign; ?? ??? ?uint16_t ?bitsPerSample; ?? ?}; ?? ?struct ?WaveData ?? ?{ ?? ??? ?const?? ?char id[4] = { 'd','a', 't', 'a' }; ?? ??? ?uint32_t dataLength; ?? ?}; #pragma pack(pop)
2.WavFileReader.h
#pragma once #include<string> /************************************************************************ * @Project: ??? ?AC.WavFileWriter * @Decription: ?wav文件讀取工具 * 本版本只支持pcm讀取,且未處理字節(jié)順序。?? ?riff文件是小端,通常在intel的設(shè)備上是沒(méi)問(wèn)題的,在java虛擬機(jī)上則需要處理。 * @Verision: ??? ?v1.0.0.0 * @Author: ??? ?Xin Nie * @Create: ??? ?2019/4/10 11:10:17 * @LastUpdate: ?2019/4/16 10:45:00 ************************************************************************ * Copyright @ 2019. All rights reserved. ************************************************************************/ namespace AC { ?? ?/// <summary> ?? ?/// wav文件讀取對(duì)象 ?? ?/// </summary> ?? ?class WavFileReader { ?? ?public: ?? ??? ?/// <summary> ?? ??? ?/// 構(gòu)造方法 ?? ??? ?/// </summary> ?? ??? ?WavFileReader(); ?? ??? ?/// <summary> ?? ? ? ?/// 析構(gòu)方法 ?? ? ? ?/// </summary> ?? ??? ?~WavFileReader(); ?? ??? ?/// <summary> ?? ??? ?/// 打開(kāi)wav文件 ?? ??? ?/// </summary> ?? ??? ?/// <param name="fileName">文件名</param> ?? ??? ?/// <returns>是否打開(kāi)成功</returns> ?? ??? ?bool OpenWavFile(const std::string& fileName); ?? ??? ?/// <summary> ?? ??? ?/// 關(guān)閉文件 ?? ??? ?/// </summary> ?? ??? ?void CloseFlie(); ?? ??? ?/// <summary> ?? ??? ?/// 讀取音頻數(shù)據(jù) ?? ??? ?/// </summary> ?? ??? ?/// <param name="buf">外部緩存</param> ?? ??? ?/// <param name="bufLength">緩存長(zhǎng)度</param> ?? ??? ?/// <returns>讀取長(zhǎng)度</returns> ?? ??? ?int ReadData(unsigned char* buf, int bufLength); ?? ??? ?/// <summary> ?? ??? ?/// 設(shè)置讀取位置 ?? ??? ?/// </summary> ?? ??? ?/// <param name="position"> 讀取位置</param> ?? ??? ?void SetPosition(int position); ?? ??? ?/// <summary> ?? ??? ?/// 獲取讀取位置 ?? ??? ?/// </summary> ?? ??? ?/// <returns>讀取位置</returns> ?? ??? ?int GetPosition(); ?? ??? ?/// <summary> ?? ??? ?/// 獲取文件長(zhǎng)度 ?? ??? ?/// </summary> ?? ??? ?/// <returns>文件長(zhǎng)度</returns> ?? ??? ?int GetFileLength(); ?? ??? ?/// <summary> ?? ??? ?/// 獲取音頻數(shù)據(jù)長(zhǎng)度 ?? ??? ?/// </summary> ?? ??? ?/// <returns>音頻數(shù)據(jù)長(zhǎng)度</returns> ?? ??? ?int GetDataLength(); ?? ??? ?/// <summary> ?? ??? ?/// 獲取聲道數(shù) ?? ??? ?/// </summary> ?? ??? ?/// <returns>聲道數(shù)</returns> ?? ??? ?int GetChannels(); ?? ??? ?/// <summary> ?? ??? ?/// 獲取采樣率 ?? ??? ?/// </summary> ?? ??? ?/// <returns>采樣率,單位:hz</returns> ?? ??? ?int GetSampleRate(); ?? ??? ?/// <summary> ?? ??? ?/// 獲取位深 ?? ??? ?/// </summary> ?? ??? ?/// <returns>位深,單位:bits</returns> ?? ??? ?int GetBitsPerSample(); ?? ?private: ?? ??? ?void* _file = nullptr; ?? ??? ?uint32_t _fileLength = 0; ?? ??? ?uint32_t _dataLength = 0; ?? ??? ?int _channels = 0; ?? ??? ?int ?_sampleRate = 0; ?? ??? ?int ?_bitsPerSample = 0; ?? ??? ?int _dataOffset = 0; ?? ?}; }
3.WavFileReader.cpp
#include"WavFileReader.h" namespace AC { ?? ?WavFileReader::WavFileReader() ?? ?{ ?? ?} ?? ?WavFileReader::~WavFileReader() ?? ?{ ?? ??? ?CloseFlie(); ?? ?} ?? ?bool WavFileReader::OpenWavFile(const std::string& fileName) ?? ?{ ?? ??? ?if (_file) ?? ??? ?{ ?? ??? ??? ?printf("已經(jīng)打開(kāi)了文件!\n"); ?? ??? ??? ?return false; ?? ??? ?} ?? ??? ?WaveRIFF riff; ?? ??? ?WaveFormat format; ?? ??? ?WaveData data; ?? ??? ?int userDataSize; ?? ??? ?_file = fopen(fileName.c_str(), "rb+"); ?? ??? ?if (!_file) ?? ??? ?{ ?? ??? ??? ?printf("打開(kāi)文件失敗!\n"); ?? ??? ??? ?return false; ?? ??? ?} ?? ??? ?//讀取頭部信息 ?? ??? ?if (fread(&riff, 1, sizeof(riff), static_cast<FILE*>(_file)) != sizeof(riff)) ?? ??? ?{ ?? ??? ??? ?printf("文件讀取錯(cuò)誤,讀取riff失敗!\n"); ?? ??? ??? ?goto error; ?? ??? ?} ?? ??? ?if (std::string(riff.id, 4) != "RIFF" || std::string(riff.waveFlag, 4) != "WAVE") ?? ??? ?{ ?? ??? ??? ?printf("頭部信息不正確,不是wav文件!\n"); ?? ??? ??? ?goto error; ?? ??? ?} ?? ??? ?if (fread(&format, 1, sizeof(format), static_cast<FILE*>(_file)) != sizeof(format)) ?? ??? ?{ ?? ??? ??? ?printf("文件讀取錯(cuò)誤,讀取format失敗!\n"); ?? ??? ??? ?goto error; ?? ??? ?} ?? ??? ?if (std::string(format.id, 4) != "fmt ") ?? ??? ?{ ?? ??? ??? ?printf("頭部信息不正確,缺少fmt!\n"); ?? ??? ??? ?goto error; ?? ??? ?} ?? ??? ?if (format.formatTag != 1) ?? ??? ?{ ?? ??? ??? ?printf("程序不支持,數(shù)據(jù)格式非pcm,只支持pcm格式的數(shù)據(jù)!\n"); ?? ??? ??? ?goto error; ?? ??? ?} ?? ??? ?userDataSize = format.blockSize - sizeof(format) + 8; ?? ??? ?if (userDataSize < 0) ?? ??? ?{ ?? ??? ??? ?printf("頭部信息不正確,blockSize大小異常!\n"); ?? ??? ??? ?goto error; ?? ??? ?} ?? ??? ?else if (userDataSize > 0) ?? ??? ?{ ?? ??? ??? ?if (fseek(static_cast<FILE*>(_file), userDataSize, SEEK_CUR) != 0) ?? ??? ??? ?{ ?? ??? ??? ??? ?printf("文件讀取錯(cuò)誤!\n"); ?? ??? ??? ??? ?goto error; ?? ??? ??? ?} ?? ??? ?} ?? ??? ?while (1) ?? ??? ?{ ?? ??? ??? ?if (fread(&data, 1, sizeof(data), static_cast<FILE*>(_file)) != sizeof(data)) ?? ??? ??? ?{ ?? ??? ??? ??? ?printf("文件讀取錯(cuò)誤!\n"); ?? ??? ??? ??? ?goto error; ?? ??? ??? ?}; ?? ??? ??? ?if (std::string(data.id, 4) != "data") ?? ??? ??? ?{ ?? ??? ??? ??? ?if (fseek(static_cast<FILE*>(_file), data.dataLength, SEEK_CUR) != 0) ?? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ?printf("文件讀取錯(cuò)誤!\n"); ?? ??? ??? ??? ??? ?goto error; ?? ??? ??? ??? ?} ?? ??? ??? ??? ?continue; ?? ??? ??? ?} ?? ??? ??? ?break; ?? ??? ?} ?? ??? ?_dataOffset = ftell(static_cast<FILE*>(_file)); ?? ??? ?_fileLength = riff.fileLength+8; ?? ??? ?_dataLength = data.dataLength; ?? ??? ?_channels = format.channels; ?? ??? ?_sampleRate = format.samplesPerSec; ?? ??? ?_bitsPerSample = format.bitsPerSample; ?? ??? ?return true; ?? ?error: ?? ??? ?if (fclose(static_cast<FILE*>(_file)) == EOF) ?? ??? ?{ ?? ??? ??? ?printf("文件關(guān)閉失敗!\n"); ?? ??? ?} ?? ??? ?_file = nullptr; ?? ??? ?return false; ?? ?} ?? ?void WavFileReader::CloseFlie() ?? ?{ ?? ??? ?if (_file) ?? ??? ?{ ?? ??? ??? ?if (fclose(static_cast<FILE*>(_file)) == EOF) ?? ??? ??? ?{ ?? ??? ??? ??? ?printf("文件關(guān)閉失敗!\n"); ?? ??? ??? ?} ?? ??? ??? ?_file = nullptr; ?? ??? ?} ?? ?} ?? ?int WavFileReader::ReadData(unsigned char* buf, int bufLength) ?? ?{ ?? ??? ?if (ftell(static_cast<FILE*>(_file)) >= _dataOffset + _dataLength) ?? ??? ??? ?return 0; ?? ??? ?return fread(buf, 1, bufLength, static_cast<FILE*>(_file)); ?? ?} ?? ?void WavFileReader::SetPosition(int postion) ?? ?{ ?? ??? ?if (fseek(static_cast<FILE*>(_file), _dataOffset + postion, SEEK_SET) != 0) ?? ??? ?{ ?? ??? ??? ?printf("定位失敗!\n"); ?? ??? ?} ?? ?} ?? ?int WavFileReader::GetPosition() ?? ?{ ?? ??? ?return ftell(static_cast<FILE*>(_file)) - _dataOffset; ?? ?} ?? ?int WavFileReader::GetFileLength() ?? ?{ ?? ??? ?return _fileLength; ?? ?} ?? ?int WavFileReader::GetDataLength() ?? ?{ ?? ??? ?return _dataLength; ?? ?} ?? ?int WavFileReader::GetChannels() ?? ?{ ?? ??? ?return _channels; ?? ?} ?? ?int WavFileReader::GetSampleRate() ?? ?{ ?? ??? ?return _sampleRate; ?? ?} ?? ?int WavFileReader::GetBitsPerSample() ?? ?{ ?? ??? ?return _bitsPerSample; ?? ?} }
三、使用示例
1、播放
#include "WavFileReader.h" int main(int argc, char** argv) { ?? ?AC::WavFileReader read; ?? ?unsigned char buf[1024]; ?? ?if (read.OpenWavFile("test_music.wav")) ?? ?{ ?? ??? ?int channels, sampleRate, bitsPerSample; ?? ??? ?//獲取聲音格式 ?? ??? ?channels = read.GetChannels(); ?? ??? ?sampleRate = read.GetSampleRate(); ?? ??? ?bitsPerSample = read.GetBitsPerSample(); ?? ??? ?//打開(kāi)聲音設(shè)備(channels,sampleRate,bitsPerSample) ?? ??? ?int size; ?? ??? ?do? ?? ??? ?{ ?? ??? ? ? ?//讀取音頻數(shù)據(jù) ?? ??? ??? ?size = read.ReadData(buf,1024); ?? ??? ??? ?if (size > 0) ?? ??? ??? ?{ ?? ??? ??? ??? ?//播放(buf,1024) ?? ??? ??? ?} ?? ??? ??? ? ?? ??? ?} while (size); ?? ??? ?read.CloseFlie(); ?? ?} ?? ?return 0; }
2、循環(huán)播放
#include "WavFileReader.h" int main(int argc, char** argv) { ?? ?AC::WavFileReader read; ?? ?unsigned char buf[1024]; ? ? bool exitFlag = false; ?? ?if (read.OpenWavFile("test_music.wav")) ?? ?{ ?? ??? ?int channels, sampleRate, bitsPerSample; ?? ??? ?//獲取聲音格式 ?? ??? ?channels = read.GetChannels(); ?? ??? ?sampleRate = read.GetSampleRate(); ?? ??? ?bitsPerSample = read.GetBitsPerSample(); ?? ??? ?//打開(kāi)聲音設(shè)備(channels,sampleRate,bitsPerSample) ?? ??? ?int size; ?? ??? ?while (!exitFlag) ?? ??? ?{ ?? ??? ? ? ?//讀取音頻數(shù)據(jù) ?? ??? ??? ?size = read.ReadData(buf, 1024); ?? ??? ??? ?if (size > 0) ?? ??? ??? ?{ ?? ??? ??? ??? ?//播放(buf,1024) ?? ??? ??? ?} ?? ??? ??? ?else ?? ??? ??? ?{ ?? ?? ??? ??? ? ? ?//回到數(shù)據(jù)起始位置 ?? ??? ??? ??? ?read.SetPosition(0);?? ??? ? ?? ??? ??? ?} ?? ??? ?}? ?? ??? ?read.CloseFlie(); ?? ?} ?? ?return 0; }
總結(jié)
以上就是今天要講的內(nèi)容,wav文件中讀取PCM還是相對(duì)較簡(jiǎn)單的,只要了解wav頭結(jié)構(gòu),然后自定義其頭結(jié)構(gòu),讀取頭部信息,校驗(yàn)頭部信息,然后再讀取數(shù)據(jù)所在的chunk,就可以實(shí)現(xiàn)這樣一個(gè)功能。
原文鏈接:https://blog.csdn.net/u013113678/article/details/122388555
相關(guān)推薦
- 2022-05-29 簡(jiǎn)單聊聊Golang中defer預(yù)計(jì)算參數(shù)_Golang
- 2022-09-20 Android實(shí)現(xiàn)簡(jiǎn)單實(shí)用的垂直進(jìn)度條_Android
- 2022-03-03 關(guān)于使用iview table 組件中使用 tooltip 樣式覆蓋的問(wèn)題
- 2022-09-16 Android12四大組件之Activity生命周期變化詳解_Android
- 2022-02-07 ifconfig顯示沒(méi)有網(wǎng)卡,nmtui運(yùn)行提示NetworkManaer 未運(yùn)行
- 2023-04-10 pytest?用例執(zhí)行失敗后其他不再執(zhí)行_python
- 2023-01-10 Docker調(diào)度器Kubernetes使用過(guò)程_docker
- 2022-10-17 C++STL教程之vector模板的使用_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支