wav音頻文件格式解析【個人筆記】(自用)


1. WAV格式

wav是微軟開發的一種音頻文件格式,注意,wav文件格式是無損音頻文件格式,相對於其他音頻格式文件數據是沒有經過壓縮的,通常文件也相對比較大些。、

支持多種音頻數字,取樣頻率和聲道,標准格式化的WAV文件和CD格式一樣,也是44.1K的取樣頻率,16位量化數字,因此在聲音文件質量和CD相差無幾! WAV打開工具是WINDOWS的媒體播放器
通常使用三個參數來表示聲音量化位數,取樣頻率和采樣點振幅量化位數分為8位,16位,24位三種,聲道單聲道和立體聲之分,單聲道振幅數據為n*1矩陣點,立體聲為n*2矩陣點,取樣頻率一般有11025Hz(11kHz) ,22050Hz(22kHz)和44100Hz(44kHz) 三種,不過盡管音質出色,但在壓縮后的文件體積過大!相對其他音頻格式而言是一個缺點,其 文件大小的計算方式為:WAV格式文件所占容量(B) = (取樣頻率 X量化位數X 聲道) X 時間 / 8 (字節= 8bit) 每一分鍾WAV格式的音頻文件的大小為10MB,其大小不隨音量大小及清晰度的變化而變化

注:專業名詞(取樣頻率、量化位數、聲道)解釋:https://blog.csdn.net/eric88/article/details/17098603 

  • 采樣位數:也叫量化位數(單位:比特),是存儲每個采樣值所用的二進制位數。采樣值反應了聲音的波動狀態。采樣位數決定了量化精度。采樣位數越長,量化的精度就越高,還原的波形曲線越真實,產生的量化噪聲越小,回放的效果就越逼真。常用的量化位數有4、8、12、16、24。量化位數與聲卡的位數和編碼有關。

  • 采樣頻率:采樣頻率是指錄音設備在一秒鍾內對聲音信號的采樣次數,采樣頻率越高聲音的還原就越真實越自然。越高所能描述的聲波頻率就越高。采樣率決定聲音頻率的范圍(相當於音調),可以用數字波形表示。以波形表示的頻率范圍通常被稱為帶寬。要正確理解音頻采樣可以分為采樣的位數和采樣的頻率。 

  • 聲道數:  使用的聲音通道的個數,也是采樣時所產生的聲音波形的個數。播放聲音時,單聲道的WAV一般使用一個喇叭發聲,立體聲的WAV可以使兩個喇叭發聲。記錄聲音時,單聲道,每次產生一個波形的數據,雙聲道,每次產生兩個波形的數據,所占的存儲空間增加一倍。
  • WAV格式大小:采樣率一般是44.1K,16bit采樣精度,存儲成WAV格式大小 = 44.1KHz(采樣率) X 16bit(采樣位數) X 2(雙聲道) X  播放時間

  • WAV格式是沒有壓縮無損的,MP3格式是按1:12壓縮保存的,所以MP3格式大小等於上式的1/12。

為什么還要用wav來做示例呢?這是因為WAV本質上是無壓縮的原始音頻文件,而且他的文件結構不算非常復雜,因此可以作為我們初學者的學習示例格式。

2. WAV的二進制格式解析

WAV文件格式分析:https://blog.csdn.net/zhihu008/article/details/7854529

  • 大部分的多媒體文件都依循着一種結構來存放信息,這種結構稱為"資源互換文件格式"(Resources lnterchange File Format),簡稱RIFF。例如聲音的WAV文件

  • RIFF:可以看做是一種樹狀結構,其基本構成單位為chunk,猶如樹狀結構中的節點,每個chunk由 "辨別碼"、"數據大小"及"數據" 所組成。

2.1 WAV文件格式

  • WAVE文件是非常簡單的一種RIFF文件,它的格式類型為"WAVE"。RIFF塊包含兩個子塊,這兩個子塊的ID分別是 "fmt" 和 "data",其中 "fmt" 子塊由結構PcmWaveFormat所組成,其子塊的大小就是sizeof(PcmWaveFormat),數據組成就是PcmWaveFormat結構中的數據。

圖三、WAVE文件結構 

typedef struct WAV_RIFF {
    /* chunk "riff" */
    char ChunkID[4];     /* "RIFF" */
    /* sub-chunk-size */
    uint32_t ChunkSize;  /* 36 + Subchunk2Size */
    /* sub-chunk-data */
    char Format[4];      /* "WAVE" */
} RIFF_t;

typedef struct WAV_FMT {
    /* sub-chunk "fmt" */
    char Subchunk1ID[4];    /* "fmt " */
    /* sub-chunk-size */
    uint32_t Subchunk1Size; /* 16 for PCM */
    /* sub-chunk-data */
    uint16_t AudioFormat;   /* PCM = 1*/
    uint16_t NumChannels;   /* Mono = 1, Stereo = 2, etc. */
    uint32_t SampleRate;    /* 8000, 44100, etc. */
    uint32_t ByteRate;      /* = SampleRate * NumChannels * BitsPerSample/8 */
    uint16_t BlockAlign;    /* = NumChannels * BitsPerSample/8 */
    uint16_t BitsPerSample; /* 8bits, 16bits, etc. */
} FMT_t;

typedef struct WAV_data {
    /* sub-chunk "data" */
    char Subchunk2ID[4];    /* "data" */
    /* sub-chunk-size */
    uint32_t Subchunk2Size; /* data size */
    /* sub-chunk-data */
//    Data_block_t block;
} Data_t;

//typedef struct WAV_data_block {
//} Data_block_t;

typedef struct WAV_fotmat {
    RIFF_t riff;
    FMT_t fmt;
    Data_t data;
} Wav;
  • "data"子塊包含WAVE文件的數字化波形聲音數據,其存放格式依賴於"fmt"子塊中SubchunkID,  SubchunkSize ,AudioFormat 成員指定的格式種類,在多聲道WAVE文件中,樣本是交替出現的。如16bit的單聲道WAVE文件和雙聲道WAVE文件的數據采樣格式分別如圖四所示: 

 圖四、WAVE文件數據采樣格式

2.2 C語言解析wav文件

wave.h

//
// Created by douzi on 19-3-26.
//

#ifndef RESOLVE_WAV_WAVE_H
#define RESOLVE_WAV_WAVE_H

#include <cstdint>

typedef struct WAV_RIFF {
    /* chunk "riff" */
    char ChunkID[4];     /* "RIFF" */
    /* sub-chunk-size */
    uint32_t ChunkSize;  /* 36 + Subchunk2Size */
    /* sub-chunk-data */
    char Format[4];      /* "WAVE" */
} RIFF_t;

typedef struct WAV_FMT {
    /* sub-chunk "fmt" */
    char Subchunk1ID[4];    /* "fmt " */
    /* sub-chunk-size */
    uint32_t Subchunk1Size; /* 16 for PCM */
    /* sub-chunk-data */
    uint16_t AudioFormat;   /* PCM = 1*/
    uint16_t NumChannels;   /* Mono = 1, Stereo = 2, etc. */
    uint32_t SampleRate;    /* 8000, 44100, etc. */
    uint32_t ByteRate;      /* = SampleRate * NumChannels * BitsPerSample/8 */
    uint16_t BlockAlign;    /* = NumChannels * BitsPerSample/8 */
    uint16_t BitsPerSample; /* 8bits, 16bits, etc. */
} FMT_t;

typedef struct WAV_data {
    /* sub-chunk "data" */
    char Subchunk2ID[4];    /* "data" */
    /* sub-chunk-size */
    uint32_t Subchunk2Size; /* data size */
    /* sub-chunk-data */
//    Data_block_t block;
} Data_t;

//typedef struct WAV_data_block {
//} Data_block_t;

typedef struct WAV_fotmat {
    RIFF_t riff;
    FMT_t fmt;
    Data_t data;
} Wav;


#endif //RESOLVE_WAV_WAVE_H
  • uint8_t:  unsigned char

  • uint16_t:unsigned short int

  • uint32_t:unsigned int

  • uint64_t: unsigned long int

wave.cpp

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "wave.h"

int main()
{
    FILE *fp = NULL;

    Wav wav;
    RIFF_t riff;
    FMT_t fmt;
    Data_t data;

    fp = fopen("/home/douzi/Douzi_qdreamer/resolve_wav/mbfio_mic3.wav", "rb");
    if (!fp) {
        printf("can't open audio file\n");
        exit(1);
    }

    fread(&wav, 1, sizeof(wav), fp);

    riff = wav.riff;
    fmt = wav.fmt;
    data = wav.data;

    /**
     *  RIFF
     */
    printf("ChunkID \t\t%c%c%c%c\n", riff.ChunkID[0], riff.ChunkID[1], riff.ChunkID[2], riff.ChunkID[3]);
    printf("ChunkSize \t\t%d\n", riff.ChunkSize);
    printf("Format \t\t\t%c%c%c%c\n", riff.Format[0], riff.Format[1], riff.Format[2], riff.Format[3]);

    printf("\n");

    /**
     *  fmt
     */
    printf("Subchunk1ID \t%c%c%c%c\n", fmt.Subchunk1ID[0], fmt.Subchunk1ID[1], fmt.Subchunk1ID[2], fmt.Subchunk1ID[3]);
    printf("Subchunk1Size \t%d\n", fmt.Subchunk1Size);
    printf("AudioFormat \t%d\n", fmt.AudioFormat);
    printf("NumChannels \t%d\n", fmt.NumChannels);
    printf("SampleRate \t\t%d\n", fmt.SampleRate);
    printf("ByteRate \t\t%d\n", fmt.ByteRate);
    printf("BlockAlign \t\t%d\n", fmt.BlockAlign);
    printf("BitsPerSample \t%d\n", fmt.BitsPerSample);

    printf("\n");

    /**
     *  data
     */
    printf("blockID \t\t%c%c%c%c\n", data.Subchunk2ID[0], data.Subchunk2ID[1], data.Subchunk2ID[2], data.Subchunk2ID[3]);
    printf("blockSize \t\t%d\n", data.Subchunk2Size);

    printf("\n");

//    duration = Subchunk2Size / ByteRate
    printf("duration \t\t%d\n", data.Subchunk2Size / fmt.ByteRate);

}

3. WAV文件語音數據的組織結構

參考:https://www.cnblogs.com/ranson7zop/p/7657874.html

WAV文件的聲音數據保存在數據塊中。塊標識符為“data”, 塊長度值為聲音數據的長度

從數據塊的第9個字符開始是聲音波形采樣數據。每個樣本按采樣的時間先后順序寫入。樣本的字節數取決於采樣位數。對於多字節樣本, 低位字節數據 放在低地址單元,相鄰的高位字節數據放在高地址單元。多聲道樣本數據采用交替方式存儲。例如: 立體聲(雙聲道)采樣值的存儲順序為:

  • 通道1第1采樣值, 通道2第1采樣值;通道1第2采樣值, 通道2第2采樣值;以此類推。基於PCM編碼的樣本數據排列方式。

(1)“52 49 46 46”這個是Ascii字符“RIFF”,這部分是固定格式,表明這是一個WAVE文件頭。
(2)“22 60 28 00”,這個是我這個WAV文件的數據大小,這個大小包括除了前面4個字節的所有字節,也就等於文件總字節數減去8。16進制的“22 60 28 00”對應是十進制的“2646050”
(3)“57 41 56 45 66 6D 74 20”,也是Ascii字符“WAVEfmt”,這部分是固定格式。
以后是PCMWAVEFORMAT部分

(4)“12 00 00 00”,這是一個DWORD,對應數字18,這個對應定義中的PCMWAVEFORMAT部分的大小,可以看到后面的這個段內容正好是18個字節。一般情況下大小為16,此時最后附加信息沒有,上面這個文件多了兩個字節的附加信息。
(5)“01 00”,這是一個WORD,對應定義為編碼格式(WAVE_FORMAT_PCM格式一般用的是這個)。
(6)“01 00”,這是一個WORD,對應數字1,表示聲道數為1,是個單聲道Wav
(7)“22 56 00 00”對應數字22050,代表的是采樣頻率22050,采樣率(每秒樣本數),表示每個通道的播放速度
(8)“44 AC 00 00”對應數字44100,代表的是每秒的數據量,波形音頻數據傳送速率,其值為:通道數×每秒樣本數×每樣本的數據位數/8(1*22050*16/8。播放軟件利用此值可以估計緩沖區的大小。
(9)“02 00”對應數字是2,表示塊對齊的內容。數據塊的調整數(按字節算的),其值為通道數×每樣本的數據位值/8。播放軟件需要一次處理多個該值大小的字節數據,以便將其值用於緩沖區的調整。
(10)“10 00”數值為16,采樣大小為16Bits,每樣本的數據位數,表示每個聲道中各個樣本的數據位數。如果有多個聲道,對每個聲道而言,樣本大小都一樣。
(11)“00 00”此處為附加信息(可選),和(4)中的size對應。

(12)“66 61 73 74” Fact是可選字段,一般當wav文件由某些軟件轉化而成,則包含該項,“04 00 00 00”Fact字段的大小為4字節,“F8 2F 14 00”是fact數據。

(13)“64 61 74 61”,這個是Ascii字符“data”,標示頭結束,開始數據區域。
(14)“F0 5F 28 00”十六進制數是“0x285ff0”,對應十進制2646000,是數據區的開頭,以后數據總數,看一下前面正好可以看到,文件大小是2646050,從(2)到(13)包括(13)正好是2646050-2646000=50字節。

4. 總結

對WAV格式影響最大的參數是編碼格式。采用不同的編碼的WAV格式是不同的,PCM是最常見的編碼格式,其它的為壓縮編碼格式,一般很少使用,有的已經廢棄。隨着人們認識的進步可能還會有新的編碼格式出現。今后對WAV文件格式的更多的研究是壓縮編碼格式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM