音頻文件算法
音頻文件是對聲音進行數字轉換以後存放的數據文件,瞭解音頻數據必須先知道幾個重要概念。ide
1. 採樣:對聲音信息錄入時,行進的最小操做單位,通常一次採樣具備左右2個聲道,每一個聲道用1或2個字節來存儲;this
這樣採樣的量化位數是8位,或16位(樣本位寬),量化位數越高聲音音質越好;就像11位電話號碼錶示的號碼比7位要多得多;編碼
2. 採樣頻率:每秒採樣次數,單位Hz,通常的音頻文件有11.025kHz、22.05kHz、44.10kHz等;顯然,這種模-數信息的轉換,每秒採樣次數越多,聲音就越精確;spa
3. 碼率:每秒編碼的bit數,單位是kb/s;計算方式:位寬×聲道數×採樣頻率;(單位是bit不是字節)指針
4. 聲道數,固定值爲1-單聲道,或者2-雙聲道,雙聲道時,每一個採樣樣本中包含左聲道、右聲道的音頻數據,所以二者的數據是交錯排列的;code
(一)Wave 格式orm
WAVE是微軟開發的聲音文件格式,用於保存Windows平臺的音頻信息資源,文件後綴名*.wav;支持多種壓縮算法、多種音頻位數、採樣頻率和聲道;blog
標準的wav文件採用44.1kHz採樣頻率,16位量化位數,聲音文件質量幾與CD至關;Wave格式不對源數據作任何處理,若是源數據是無損的,編碼後的Wav文件也是無損的;若是源數據是有損的,編碼後的Wav文件也是有損的;資源
1. Wave文件的構成:
RIFF | 標 志 4B | "RIFF" |
數據大小 4B | - | |
格式 4B | "WAVE" | |
fmt | 標誌 4B | "fmt " |
結構體大小 4B | 16/18 | |
結構體 16B/18B | ||
data | 標誌 4B | "data" |
聲音數據大小 4B | - | |
data | - |
2. Wave文件的詳細結構:
// RIFF 標準媒體流文件頭
struct Riff_Header {
char szRiffId[4]; // 'R','I','F','F'
DWORD dwRiffSize; // Size, 除了這 8 個字節以外,文件剩餘大小,等於文件總字節數-8
char szRiffFormat[4]; // 'W','A','V','E'
}; struct Fmt_Block { char szFmtId[4]; // 'f', 'm', 't',' '
DWORD dwFmtSize; // Size 爲 16 或 18
WORD wFormatTag; // 編碼方式,通常爲 0x0001
WORD wChannels; // 聲道數 1--單聲道 2--雙聲道
DWORD dwSamplesPerSec; // 採樣頻率 /Hz
DWORD dwAvgBytesPerSec; // 每秒字節數
WORD wBlockAlign; // 數據塊對齊單位(每一個採樣須要的字節數)
WORD wBitsPerSample; // 每一個採樣須要的 bit // WORD wBits; // 可能有可能沒有,由dwFmtSize字段決定
}; //Fact_Block 塊,有些 wav 文件中沒有
struct Fact_Block { char szFactId[4]; // 'f','a','c','t'
DWORD dwFactSize; // }; //數據塊
struct Data_Block { char szDataId[4]; //'d','a,','t','a'
DWORD dwDataSize; // 音頻數據大小 //data ...
};
說明:
(1) RIFF塊裏面的 dwRiffSize 表示的是整個文件除開頭8個字節以外的大小,0x24 0xCD 0x01 0x00,即 118,052 Byte,經過文件屬性查得文件大小是118,060Byte;
(2) dwFmtSize 爲 0x10 0x00 0x00 0x00,即爲16;fmt塊的剩餘部分是一個波形信息結構,是微軟定義的:
/* * extended waveform format structure used for all non-PCM formats. this * structure is common to all non-PCM formats. */ typedef struct tWAVEFORMATEX { WORD wFormatTag; /* format type */ WORD nChannels; /* number of channels (i.e. mono, stereo...) */ DWORD nSamplesPerSec; /* sample rate */ DWORD nAvgBytesPerSec; /* for buffer estimation */ WORD nBlockAlign; /* block size of data */ WORD wBitsPerSample; /* number of bits per sample of mono data */ WORD cbSize; /* the count in bytes of the size of */ /* extra information (after cbSize) */ } WAVEFORMATEX, *PWAVEFORMATEX, NEAR *NPWAVEFORMATEX, FAR *LPWAVEFORMATEX;
(3) Data塊:dwDataSize表示音頻數據的大小,0x00 0x01 0xCD 0x00,即118,016,略小於118,052,說明文件末有部分無效數據;
一個示例:
// // 讀取Wav文件頭,並驗證文件格式 // 成功返回文件句柄,並重定位文件指針到數據區 // 失敗返回NULL // HANDLE ReadHeader(char* path) { HANDLE hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Unable to Open File!"); return NULL; } char buffer[512]; DWORD readByte; if (ReadFile(hFile, buffer, sizeof(buffer), &readByte, NULL)) { Riff_Header header; Fmt_Block fmt; Data_Block data; memcpy(&header, buffer, sizeof(Riff_Header)); if (strncmp(header.szRiffId, "RIFF", 4) != 0) {CloseHandle(hFile); return NULL;} memcpy(&fmt, buffer + sizeof(Riff_Header), sizeof(Fmt_Block)); if (strncmp(fmt.szFmtId, "fmt ", 4) != 0) {CloseHandle(hFile);return NULL;} memcpy(&data, buffer + sizeof(Riff_Header)+fmt.dwFmtSize+8, sizeof(Data_Block)); if (strncmp(data.szDataId, "data", 4) != 0) {CloseHandle(hFile);return NULL;} memcpy(&wfx, &fmt.wFormatTag, sizeof(WAVEFORMATEX) - 2); wfx.cbSize = 0; // 重定位文件指針,到數據起始位置 int headSize = sizeof(Riff_Header) + fmt.dwFmtSize + 8 + sizeof(Data_Block);
headSize = (headSize/8 + (headSize%8?1:0))*8; SetFilePointer(hFile, headSize, 0, FILE_BEGIN); return hFile; } return NULL; }
說明:1. 這裏沒有考慮fact結構存在的狀況;2. 調用ReadHeader()以後wfx結構同時也填充完畢,能夠用於打開音頻設備,進行wav音頻播放;
其調用以下:
#include "stdafx.h" #include <Windows.h> #include <mmsystem.h> #include "WavStruct.h" #pragma comment(lib, "winmm.lib")
const char testWave = "C:/Windows/Media/Ring02.wav"; WAVEFORMATEX wfx; int main(int argc, char* argv[]) { HANDLE hFile = ReadHeader((char*)testWave); if (hFile == NULL) return 0; CloseHandle(hFile); return 0; }
結果:
(二) MP3格式
MP3格式(待續 ...)