C++將音頻PCM數(shù)據(jù)封裝成wav文件的方法
前言
使用聲音設(shè)備采集的聲音數(shù)據(jù)通常是PCM數(shù)據(jù),直接寫(xiě)入文件是無(wú)法播放的,通常的做法是將其封裝成wav格式,這樣播放器就能夠識(shí)別且播放了。本文將介紹如何將PCM封裝成wav的方法。
一、如何實(shí)現(xiàn)?
首先需要構(gòu)造wav頭部,wav文件音頻信息全部保存在頭部,我們要做的就是在PCM數(shù)據(jù)的前面加入wav頭,并且記錄PCM的相關(guān)參數(shù)。
1.定義頭結(jié)構(gòu)
只定義PCM格式的wav文件頭
//WAV頭部結(jié)構(gòu)-PCM格式 struct WavPCMFileHeader;
2.預(yù)留頭部空間
創(chuàng)建文件時(shí)預(yù)留頭部空間
FILE*f = fopen(fileName.c_str(), "wb+"); //預(yù)留頭部位置 fseek(f, sizeof(WavPCMFileHeader), SEEK_SET);
3.寫(xiě)入PCM數(shù)據(jù)
寫(xiě)入數(shù)據(jù),并記錄數(shù)據(jù)總長(zhǎng)度。
fwrite(data, 1, dataLength, f); //錄數(shù)據(jù)總長(zhǎng)度 _totalDataLength += dataLength;
4.寫(xiě)入頭部信息
關(guān)閉文件時(shí),回到起始位置寫(xiě)入頭部信息
//寫(xiě)入頭部信息 fseek(f, 0, SEEK_SET); WavPCMFileHeader h(_channels, _sampleRate, _bitsPerSample, _totalDataLength); fwrite(&h, 1, sizeof(h), f); fclose(f);
二、完整代碼
WavWapper.h
#pragma once #include<string> namespace AC { ?? ?class ?WavWapper { ?? ?public: ?? ??? ?WavWapper(); ?? ??? ?~WavWapper(); ?? ??? ?/// <summary> ?? ??? ?/// 創(chuàng)建wav文件 ?? ??? ?/// </summary> ?? ??? ?/// <param name="fileName">文件名</param> ?? ??? ?/// <param name="channels">聲道數(shù)</param> ?? ??? ?/// <param name="sampleRate">采樣率,單位hz</param> ?? ??? ?/// <param name="bitsPerSample">位深</param> ?? ??? ?void CreateWavFile(const std::string &fileName, int channels, int ?sampleRate, int ?bitsPerSample); ?? ??? ?/// <summary> ?? ??? ?/// 寫(xiě)入PCM數(shù)據(jù) ?? ??? ?/// </summary> ?? ??? ?/// <param name="data">PCM數(shù)據(jù)</param> ?? ??? ?/// <param name="dataLength">數(shù)據(jù)長(zhǎng)度</param> ?? ??? ?void WriteToFile(unsigned char* data, int dataLength); ?? ??? ?/// <summary> ?? ??? ?/// 關(guān)閉文件 ?? ??? ?/// </summary> ?? ??? ?void CloseFile(); ?? ?private: ?? ??? ?void* _file=nullptr; ?? ??? ?uint32_t _totalDataLength=0; ?? ?}; }
WavWapper.cpp
#include"WavWapper.h" #include<stdio.h> namespace AC { ?? ?//WAV頭部結(jié)構(gòu)-PCM格式 ?? ?struct WavPCMFileHeader ?? ?{ ?? ??? ?struct RIFF { ?? ??? ??? ?const?? ?char rift[4] = { 'R','I', 'F', 'F' }; ?? ??? ??? ?uint32_t fileLength; ?? ??? ??? ?const?? ?char wave[4] = { 'W','A', 'V', 'E' }; ?? ??? ?}riff; ?? ??? ?struct Format ?? ??? ?{ ?? ??? ??? ?const?? ?char fmt[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; ?? ??? ?}format; ?? ??? ?struct ?Data ?? ??? ?{ ?? ??? ??? ?const?? ?char data[4] = { 'd','a', 't', 'a' }; ?? ??? ??? ?uint32_t dataLength; ?? ??? ?}data; ?? ??? ?WavPCMFileHeader() {} ?? ??? ?WavPCMFileHeader(int nCh, int ?nSampleRate, int ?bitsPerSample, int dataSize) { ?? ??? ??? ?riff.fileLength = 36 + dataSize; ?? ??? ??? ?format.formatTag = 1; ?? ??? ??? ?format.channels = nCh; ?? ??? ??? ?format.samplesPerSec = nSampleRate; ?? ??? ??? ?format.avgBytesPerSec = nSampleRate * nCh * bitsPerSample / 8; ?? ??? ??? ?format.blockAlign = nCh * bitsPerSample / 8; ?? ??? ??? ?format.bitsPerSample = bitsPerSample; ?? ??? ??? ?data.dataLength = dataSize; ?? ??? ?} ?? ?}; ?? ?WavWapper::WavWapper() ?? ?{ ?? ?} ?? ?WavWapper::~WavWapper() ?? ?{ ?? ??? ?CloseFile(); ?? ?} ?? ?int _channels; ?? ?int _sampleRate; ?? ?int _bitsPerSample; ?? ?void WavWapper::CreateWavFile(const std::string& fileName, int channels, int sampleRate, int bitsPerSample) ?? ?{ ?? ??? ?if (!_file) ?? ??? ?{ ?? ??? ??? ?_channels = channels; ?? ??? ??? ?_sampleRate = sampleRate; ?? ??? ??? ?_bitsPerSample = bitsPerSample; ?? ??? ??? ?_totalDataLength = 0; ?? ??? ??? ?_file = fopen(fileName.c_str(), "wb+"); ?? ??? ??? ?//預(yù)留頭部位置 ?? ??? ??? ?fseek(static_cast<FILE*>(_file), sizeof(WavPCMFileHeader), SEEK_SET); ?? ??? ?} ?? ?} ?? ?void WavWapper::WriteToFile(unsigned char* data, int dataLength) ?? ?{ ?? ??? ?fwrite(data, 1, dataLength, static_cast<FILE*>(_file)); ?? ??? ?_totalDataLength += dataLength; ?? ?} ?? ?void WavWapper::CloseFile() ?? ?{ ?? ??? ?if (_file) ?? ??? ?{ ?? ??? ??? ?if (_totalDataLength > 0) ?? ??? ??? ?{ ?? ??? ??? ??? ?//寫(xiě)入頭部信息 ?? ??? ??? ??? ?fseek(static_cast<FILE*>(_file), 0, SEEK_SET); ?? ??? ??? ??? ?WavPCMFileHeader h(_channels, _sampleRate, _bitsPerSample, _totalDataLength); ?? ??? ??? ??? ?fwrite(&h, 1, sizeof(h), static_cast<FILE*>(_file)); ?? ??? ??? ?} ?? ??? ??? ?fclose(static_cast<FILE*>(_file)); ?? ??? ??? ?_file = nullptr; ?? ??? ?} ?? ?} }
三、使用示例
#include "WavWapper.h" #include<Windows.h> int main() {?? ? ?? ?AC::WavWapper ww; ?? ?//創(chuàng)建wav文件,確保pcm聲音格式與參數(shù)一致 ?? ?ww.CreateWavFile("sound.wav",2, 44100, 16); ?? ?while (flag) ?? ?{ ?? ??? ?//獲取PCM數(shù)據(jù) ?? ??? ?//略 ?? ??? ?//獲取PCM數(shù)據(jù)-end ?? ? ? ?//寫(xiě)入PCM數(shù)據(jù) ?? ??? ?ww.WriteToFile(data, dataLength); ?? ?} ?? ?//關(guān)閉文件 ?? ?ww.CloseFile();?? ? }
總結(jié)
以上就是今天要講的內(nèi)容,PCM封裝成wav還是相對(duì)較簡(jiǎn)單的,只要了解wav頭結(jié)構(gòu),然后自定義其頭結(jié)構(gòu),然后再進(jìn)行一定的測(cè)試,就可以實(shí)現(xiàn)這樣一個(gè)功能。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C語(yǔ)言判定一棵二叉樹(shù)是否為二叉搜索樹(shù)的方法分析
這篇文章主要介紹了C語(yǔ)言判定一棵二叉樹(shù)是否為二叉搜索樹(shù)的方法,結(jié)合實(shí)例形式綜合對(duì)比分析了C語(yǔ)言針對(duì)二叉搜索樹(shù)判定的原理、算法、效率及相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-08-08C語(yǔ)言進(jìn)階之字符串查找?guī)旌瘮?shù)詳解
字符串是一種非常重要的數(shù)據(jù)類型,但是C語(yǔ)言不存在顯式的字符串類型,C語(yǔ)言中的字符串都以字符串常量的形式出現(xiàn)或存儲(chǔ)在字符數(shù)組中,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言進(jìn)階之字符串查找?guī)旌瘮?shù)的相關(guān)資料,需要的朋友可以參考下2023-01-01C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)
這篇文章主要介紹了C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)的相關(guān)資料,這里主要是利用數(shù)據(jù)數(shù)據(jù)結(jié)果中循環(huán)鏈表來(lái)實(shí)現(xiàn),需要的朋友可以參考下2017-08-08C++使用htslib庫(kù)讀入和寫(xiě)出bam文件的實(shí)例
下面小編就為大家分享一篇C++使用htslib庫(kù)讀入和寫(xiě)出bam文件的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11C語(yǔ)言 詳細(xì)講解接續(xù)符和轉(zhuǎn)義符的使用
接續(xù)符是用來(lái)告訴編譯器行為的符號(hào),那編譯器遇到接續(xù)符是什么行為呢,就是去掉接續(xù)符,然后把下一行連接到現(xiàn)在這行上面,轉(zhuǎn)義符是主要用于表示無(wú)回顯字符,也用于表示常規(guī)字符,轉(zhuǎn)義符必須放在單引號(hào)或者雙引號(hào)里面2022-04-04