欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++標(biāo)準(zhǔn)庫實(shí)現(xiàn)WAV文件讀寫的操作

 更新時(shí)間:2022年01月26日 13:01:47   作者:Brook_icv  
本文將使用標(biāo)準(zhǔn)C++庫實(shí)現(xiàn)對數(shù)據(jù)為PCM格式的WAV文件的讀寫操作,只使用標(biāo)準(zhǔn)C++庫函數(shù),不依賴于其他的庫,對C++標(biāo)準(zhǔn)庫實(shí)現(xiàn)WAV文件讀寫相關(guān)知識(shí)感興趣的朋友一起看看吧

在上一篇文章RIFF和WAVE音頻文件格式中對WAV的文件格式做了介紹,本文將使用標(biāo)準(zhǔn)C++庫實(shí)現(xiàn)對數(shù)據(jù)為PCM格式的WAV文件的讀寫操作,只使用標(biāo)準(zhǔn)C++庫函數(shù),不依賴于其他的庫。

WAV文件結(jié)構(gòu)

WAV是符合RIFF標(biāo)準(zhǔn)的多媒體文件,其文件結(jié)構(gòu)可以如下:

WAV 文件結(jié)構(gòu)
RIFF塊
WAVE FOURCC
fmt 塊
fact 塊(可選)
data塊(包含PCM數(shù)據(jù))

首先是一個(gè)RIFF塊,有塊標(biāo)識(shí)RIFF,指明該文件是符合RIFF標(biāo)準(zhǔn)的文件;接著是一個(gè)FourCC,WAVE,該文件為WAV文件;fmt塊包含了音頻的一些屬性:采樣率、碼率、聲道等;fact 塊是一個(gè)可選塊,不是PCM數(shù)據(jù)格式的需要該塊;最后data塊,則包含了音頻的PCM數(shù)據(jù)。實(shí)際上,可以將一個(gè)WAV文件看著由兩部分組成:文件頭和PCM數(shù)據(jù),則WAV文件頭各字段的意義如下:

本文實(shí)現(xiàn)的是一個(gè)能夠讀取PCM數(shù)據(jù)格式的單聲道或者雙聲道的WAV文件,是沒有fact塊以及擴(kuò)展塊。

結(jié)構(gòu)體定義

通過上面的介紹發(fā)現(xiàn),WAV的頭文件所包含的內(nèi)容有兩種:RIFF文件格式標(biāo)準(zhǔn)中需要的數(shù)據(jù)和關(guān)于音頻格式的信息。對于RIFF文件格式所需的信息,聲明結(jié)構(gòu)體如下:

// The basic chunk of RIFF file format
struct Base_chunk{

	FOURCC fcc;    // FourCC id
	uint32_t cb_size; // 數(shù)據(jù)域的大小
	Base_chunk(FOURCC fourcc)
		: fcc(fourcc)
	{
		cb_size = 0;
	}
};

chunk是RIFF文件的基本單元,首先一個(gè)4字節(jié)的標(biāo)識(shí)FOURCC,用來指出該塊的類型;cb_size則是改塊數(shù)據(jù)域中數(shù)據(jù)的大小。

文件頭中另一個(gè)信息則是音頻的格式信息,實(shí)際上是frm chunk的數(shù)據(jù)域信息,其聲明如下:

// Format chunk data field
struct Wave_format{

	uint16_t format_tag;      // WAVE的數(shù)據(jù)格式,PCM數(shù)據(jù)該值為1
	uint16_t channels;        // 聲道數(shù)
	uint32_t sample_per_sec;  // 采樣率
	uint32_t bytes_per_sec;   // 碼率,channels * sample_per_sec * bits_per_sample / 8
	uint16_t block_align;     // 音頻數(shù)據(jù)塊,每次采樣處理的數(shù)據(jù)大小,channels * bits_per_sample / 8
	uint16_t bits_per_sample; // 量化位數(shù),8、16、32等
	uint16_t ex_size;         // 擴(kuò)展塊的大小,附加塊的大小
	Wave_format()
	{
		format_tag      = 1; // PCM format data
		ex_size         = 0; // don't use extesion field
		channels        = 0;
		sample_per_sec  = 0;
		bytes_per_sec   = 0;
		block_align     = 0;
		bits_per_sample = 0;
	}
	Wave_format(uint16_t nb_channel, uint32_t sample_rate, uint16_t sample_bits)
		:channels(nb_channel), sample_per_sec(sample_rate), bits_per_sample(sample_bits)
		format_tag    = 0x01;                                            // PCM format data
		bytes_per_sec = channels * sample_per_sec * bits_per_sample / 8; // 碼率
		block_align   = channels * bits_per_sample / 8;
		ex_size       = 0;                                               // don't use extension field
};

關(guān)于各個(gè)字段的信息,在上面圖中有介紹,這里主要說明兩個(gè)字段:

  • format_tag表示以何種數(shù)據(jù)格式存儲(chǔ)音頻的sample值,這里設(shè)置為0x01表示用PCM格式,非壓縮格式,不需要fact塊。
  • ex_size表示的是擴(kuò)展塊的大小。有兩種方法來設(shè)置不使用擴(kuò)展塊,一種是設(shè)置fmt中的size字段為16(無ex_size字段);或者,有ex_size,設(shè)置其值為0.在本文中,使用第二種方法,設(shè)置ex_size的值為0,不使用擴(kuò)展塊。

有了上面兩個(gè)結(jié)構(gòu)體的定義,對于WAV的文件頭,可以表示如下:

/*

	數(shù)據(jù)格式為PCM的WAV文件頭
	--------------------------------
	| Base_chunk | RIFF |
	---------------------
    | WAVE              |
	| Base_chunk | fmt  |	Header
	| Wave_format|      |
	| Base_chunk | data |
*/
struct Wave_header{
	shared_ptr<Base_chunk> riff;
	FOURCC wave_fcc;
	shared_ptr<Base_chunk> fmt;
	shared_ptr<Wave_format>  fmt_data;
	shared_ptr<Base_chunk> data;
	Wave_header(uint16_t nb_channel, uint32_t sample_rate, uint16_t sample_bits)
	{
		riff      = make_shared<Base_chunk>(MakeFOURCC<'R', 'I', 'F', 'F'>::value);
		fmt       = make_shared<Base_chunk>(MakeFOURCC<'f', 'm', 't', ' '>::value);
		fmt->cb_size = 18;
		fmt_data  = make_shared<Wave_format>(nb_channel, sample_rate, sample_bits);
		data      = make_shared<Base_chunk>(MakeFOURCC<'d', 'a', 't', 'a'>::value);
		wave_fcc = MakeFOURCC<'W', 'A', 'V', 'E'>::value;
	}
	Wave_header()
		riff         = nullptr;
		fmt          = nullptr;
		fmt_data     = nullptr;
		data         = nullptr;
		wave_fcc     = 0;
};

在WAV的文件頭中有三種chunk,分別為:RIFF,fmt,data,然后是音頻的格式信息Wave_format。在RIFF chunk的后面是一個(gè)4字節(jié)非FOURCC:WAVE,表示該文件為WAV文件。另外,Wave_format的構(gòu)造函數(shù)只需要三個(gè)參數(shù):聲道數(shù)、采樣率和量化精度,關(guān)于音頻的其他信息都可以使用這三個(gè)數(shù)值計(jì)算得到。注意,這里設(shè)置fmt chunk的size為18。

實(shí)現(xiàn)

有了上面結(jié)構(gòu)體后,再對WAV文件進(jìn)行讀寫就比較簡單了。由于RIFF文件中使用FOURCC老標(biāo)識(shí)chunk的類型,這里有兩個(gè)FOURCC的實(shí)現(xiàn)方法:使用宏和使用模板,具體如下:

#define FOURCC uint32_t	

#define MAKE_FOURCC(a,b,c,d) \
( ((uint32_t)d) | ( ((uint32_t)c) << 8 ) | ( ((uint32_t)b) << 16 ) | ( ((uint32_t)a) << 24 ) )
template <char ch0, char ch1, char ch2, char ch3> struct MakeFOURCC{ enum { value = (ch0 << 0) + (ch1 << 8) + (ch2 << 16) + (ch3 << 24) }; };

Write WAVE file

寫WAV文件過程,首先是填充文件頭信息,對于Wave_format只需要三個(gè)參數(shù):聲道數(shù)、采樣率和量化精度,將文件頭信息寫入后,緊接這寫入PCM數(shù)據(jù)就完成了WAV文件的寫入。其過程如下:

Wave_header header(1, 48000, 16);

	uint32_t length = header.fmt_data->sample_per_sec * 10 * header.fmt_data->bits_per_sample / 8;
	uint8_t *data = new uint8_t[length];
	memset(data, 0x80, length);
	CWaveFile::write("e:\\test1.wav", header, data, length);

首先夠著WAV文件頭,然后寫入文件即可。將數(shù)據(jù)寫入的實(shí)現(xiàn)也比較簡單,按照WAv的文件結(jié)構(gòu),依次將數(shù)據(jù)寫入文件。在設(shè)置各個(gè)chunk的size值時(shí)要注意其不同的意義:

  • RIFF chunk 的size表示的是其數(shù)據(jù)的大小,其包含各個(gè)chunk的大小以及PCM數(shù)據(jù)的長度。該值 + 8 就是整個(gè)WAV文件的大小。
  • fmt chunk 的size是Wave_format的大小,這里為18
  • data chunk 的size 是寫入的PCM數(shù)據(jù)的長度

Read WAVE file

知道了WAV的文件結(jié)構(gòu)后,讀取其數(shù)據(jù)就更為簡單了。有一種直接的方法,按照PCM相對于文件起始的位置的偏移位置,直接讀取PCM數(shù)據(jù);或者是按照其文件結(jié)構(gòu)依次讀取信息,本文的將依次讀取WAV文件的信息填充到相應(yīng)的結(jié)構(gòu)體中,其實(shí)現(xiàn)代碼片段如下:

 header = make_unique<Wave_header>();

    // Read RIFF chunk
    FOURCC fourcc;
    ifs.read((char*)&fourcc, sizeof(FOURCC));

    if (fourcc != MakeFOURCC<'R', 'I', 'F', 'F'>::value) // 判斷是不是RIFF
        return false;
    Base_chunk riff_chunk(fourcc);
    ifs.read((char*)&riff_chunk.cb_size, sizeof(uint32_t));

    header->riff = make_shared<Base_chunk>(riff_chunk);

    // Read WAVE FOURCC
    ifs.read((char*)&fourcc, sizeof(FOURCC));
    if (fourcc != MakeFOURCC<'W', 'A', 'V', 'E'>::value)
        return false;
    header->wave_fcc = fourcc;
    ...

實(shí)例

調(diào)用本文的實(shí)現(xiàn),寫入一個(gè)單聲道,16位量化精度,采樣率為48000Hz的10秒鐘WAV文件,代碼如下:

Wave_header header(1, 48000, 16);

	uint32_t length = header.fmt_data->sample_per_sec * 10 * header.fmt_data->bits_per_sample / 8;
	uint8_t *data = new uint8_t[length];
	memset(data, 0x80, length);
	CWaveFile::write("e:\\test1.wav", header, data, length);

這里將所有的sample按字節(jié)填充為0x80,以16進(jìn)制打開該wav文件,結(jié)果如下:

可以參照上圖給出的WAV文件頭信息,看看各個(gè)字節(jié)的意義。音頻的格式信息在FOURCC fmt后面

  • 4字節(jié) 00000012 fmt數(shù)據(jù)的長度 18字節(jié)
  • 2字節(jié) 0001 數(shù)據(jù)的存儲(chǔ)格式為PCM
  • 2字節(jié) 0001 聲道個(gè)數(shù)
  • 4字節(jié) 0000BB80 采樣率 48000Hz
  • 4字節(jié) 00017700 碼率 96000bps
  • 2字節(jié) 0002 數(shù)據(jù)塊大小
  • 2字節(jié) 0010 量化精度 16位
  • 2字節(jié) 0000 擴(kuò)展塊的大小
  • 4字節(jié) FOURCC data
  • 4字節(jié) 數(shù)據(jù)長度 0x000EA600

代碼

最后將本文的代碼封裝在了類CWaveFile中,使用簡單。

寫WAV文件

Wave_header header(1, 48000, 16);

	uint32_t length = header.fmt_data->sample_per_sec * 10 * header.fmt_data->bits_per_sample / 8;
	uint8_t *data = new uint8_t[length];
	memset(data, 0x80, length);
	CWaveFile::write("e:\\test1.wav", header, data, length);

讀取WAV文件

CWaveFile wave;
	wave.read("e:\\test1.wav");
	wave.data // PCM數(shù)據(jù)

源代碼只有一個(gè)不到300行的cpp文件,腳本之家下載

到此這篇關(guān)于C++標(biāo)準(zhǔn)庫實(shí)現(xiàn)WAV文件讀寫的文章就介紹到這了,更多相關(guān)C++ WAV文件讀寫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言實(shí)現(xiàn)簡單通訊錄功能

    C語言實(shí)現(xiàn)簡單通訊錄功能

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)簡單通訊錄功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C++實(shí)現(xiàn)八皇后問題的方法

    C++實(shí)現(xiàn)八皇后問題的方法

    這篇文章主要介紹了C++實(shí)現(xiàn)八皇后問題的方法,是數(shù)據(jù)結(jié)構(gòu)與算法中常見的一個(gè)經(jīng)典算法,需要的朋友可以參考下
    2014-09-09
  • 異步http listener 完全并發(fā)處理懲罰http懇求的小例子

    異步http listener 完全并發(fā)處理懲罰http懇求的小例子

    異步http listener 完全并發(fā)處理懲罰http懇求的小例子,需要的朋友可以參考一下
    2013-05-05
  • C++實(shí)現(xiàn)圖書管理系統(tǒng)課程設(shè)計(jì)

    C++實(shí)現(xiàn)圖書管理系統(tǒng)課程設(shè)計(jì)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)圖書管理系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • c/c++ 奇技淫巧(一些c語言的技巧)

    c/c++ 奇技淫巧(一些c語言的技巧)

    這篇文章主要介紹了c/c++ 奇技淫巧,需要的朋友可以參考下
    2017-03-03
  • 深入了解C語言字符函數(shù)和字符串函數(shù)

    深入了解C語言字符函數(shù)和字符串函數(shù)

    這篇文章主要給大家介紹了關(guān)于C語言字符/字符串的相關(guān)函數(shù),文中通過示例代碼總結(jié)的非常詳細(xì),對大家學(xué)習(xí)或者使用C語言具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • c語言通過opencv實(shí)現(xiàn)輪廓處理與切割

    c語言通過opencv實(shí)現(xiàn)輪廓處理與切割

    這篇文章主要介紹了c語言通過opencv實(shí)現(xiàn)輪廓處理與切割,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • C++中的整型

    C++中的整型

    這篇文章我們來聊聊C++中的整型,整型即整數(shù),與小數(shù)對應(yīng)。許多語言只能表示一種整型(如Python),而在C++當(dāng)中根據(jù)整數(shù)的范圍提供了好幾種不同的整型,下面文章我們就來看看具體是哪幾種,需要的朋友也可以參考一下
    2021-11-11
  • C語言系統(tǒng)日期和時(shí)間實(shí)例詳解

    C語言系統(tǒng)日期和時(shí)間實(shí)例詳解

    我們在寫C語言程序的時(shí)候,有的時(shí)候會(huì)用到讀取本機(jī)的時(shí)間和日期,下面這篇文章主要給大家介紹了關(guān)于C語言系統(tǒng)日期和時(shí)間的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • C語言 深入淺出講解指針的使用

    C語言 深入淺出講解指針的使用

    指針是C語言中一個(gè)非常重要的概念,也是C語言的特色之一。使用指針可以對復(fù)雜數(shù)據(jù)進(jìn)行處理,能對計(jì)算機(jī)的內(nèi)存分配進(jìn)行控制,在函數(shù)調(diào)用中使用指針還可以返回多個(gè)值
    2022-03-03

最新評論