wav音頻文件頭解析


wav概述

WAV為微軟公司(Microsoft)開發的一種聲音文件格式,它符合RIFF(Resource
Interchange File Format)文件規范,用於保存Windows平台的音頻信息資源,被Windows平台及其應用程序所廣泛支持,該格式也支持MSADPCM,CCITT
A LAW等多種壓縮運算法,支持多種音頻數字,取樣頻率和聲道,標准格式化的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,其大小不隨音量大小及清晰度的變化而變化。

支持WAV設計的手機主要為智能手機,如索尼愛立信P910和諾基亞N90以及采用Windows Moblie的多普達等手機還有微軟Windows
Phone系列手機,而其它一些非智能手機的產品,如果宣傳支持WAV格式則多半屬於只是支持單聲道的。


格式解析

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

標志符(RIFF)
余下所有數據的長度
格式類型("WAVE")
"fmt"
PCMWAVEFORMAT的長度
PCMWAVEFORMAT
"data"
聲音數據大小
聲音數據

wav頭結構體定義

[cpp]  view plain copy
 
  1. /* RIFF WAVE file struct. 
  2.  * For details see WAVE file format documentation  
  3.  * (for example at <a href="http://www.wotsit.org)." target="_blank">http://www.wotsit.org).</a>  */  
  4. typedef struct WAV_HEADER_S  
  5. {  
  6.     char            riffType[4];    //4byte,資源交換文件標志:RIFF     
  7.     unsigned int    riffSize;       //4byte,從下個地址到文件結尾的總字節數   
  8.     char            waveType[4];    //4byte,wav文件標志:WAVE      
  9.     char            formatType[4];  //4byte,波形文件標志:FMT(最后一位空格符)   
  10.     unsigned int    formatSize;     //4byte,音頻屬性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所占字節數  
  11.     unsigned short  compressionCode;//2byte,格式種類(1-線性pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM)  
  12.     unsigned short  numChannels;    //2byte,通道數  
  13.     unsigned int    sampleRate;     //4byte,采樣率  
  14.     unsigned int    bytesPerSecond; //4byte,傳輸速率  
  15.     unsigned short  blockAlign;     //2byte,數據塊的對齊,即DATA數據塊長度  
  16.     unsigned short  bitsPerSample;  //2byte,采樣精度-PCM位寬  
  17.     char            dataType[4];    //4byte,數據標志:data  
  18.     unsigned int    dataSize;       //4byte,從下個地址到文件結尾的總字節數,即除了wav header以外的pcm data length  
  19. }WAV_HEADER;  

 

頭解析程序示例


wav.h

#ifndef __WAV_H__  
#define __WAV_H__  
  
#define debug(fmt...) do \  
            { \  
                printf("[%s::%d] ", __func__, __LINE__);\  
                printf(fmt); \  
            }while(0)  
  
/* RIFF WAVE file struct. 
 * For details see WAVE file format documentation  
 * (for example at <a href="http://www.wotsit.org)." target="_blank">http://www.wotsit.org).</a>  */  
typedef struct WAV_HEADER_S  
{  
    char            riffType[4];    //4byte,資源交換文件標志:RIFF     
    unsigned int    riffSize;       //4byte,從下個地址到文件結尾的總字節數   
    char            waveType[4];    //4byte,wave文件標志:WAVE     
    char            formatType[4];  //4byte,波形文件標志:FMT    
    unsigned int        formatSize;     //4byte,音頻屬性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所占字節數  
    unsigned short  compressionCode;//2byte,編碼格式(1-線性pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM)  
    unsigned short  numChannels;    //2byte,通道數  
    unsigned int    sampleRate;     //4byte,采樣率  
    unsigned int    bytesPerSecond; //4byte,傳輸速率  
    unsigned short  blockAlign;     //2byte,數據塊的對齊  
    unsigned short  bitsPerSample;  //2byte,采樣精度  
    char            dataType[4];    //4byte,數據標志:data  
    unsigned int    dataSize;       //4byte,從下個地址到文件結尾的總字節數,即除了wav header以外的pcm data length  
}WAV_HEADER;  
  
typedef struct WAV_INFO_S  
{  
  WAV_HEADER    header;  
  FILE          *fp;  
  unsigned int  channelMask;  
}WAV_INFO;  
  
#endif  

 

 

wav.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wav.h"

/* func		: endian judge
 * return	: 0-big-endian othes-little-endian
 */
int IS_LITTLE_ENDIAN(void) 
{
	int __dummy = 1;
	return ( *( (unsigned char*)(&(__dummy) ) ) );
}

unsigned int readHeader(void *dst, signed int size, signed int nmemb, FILE *fp) 
{
	unsigned int n, s0, s1, err;
	unsigned char tmp, *ptr;
	
	if ((err = fread(dst, size, nmemb, fp)) != nmemb) 
	{
		return err;
	}
	if (!IS_LITTLE_ENDIAN() && size > 1) 
	{
		//debug("big-endian \n");
		ptr = (unsigned char*)dst;
		for (n=0; n<nmemb; n++) 
		{
	  		for (s0=0, s1=size-1; s0 < s1; s0++, s1--) 
	  		{
	    		tmp = ptr[s0];
	    		ptr[s0] = ptr[s1];
	    		ptr[s1] = tmp;
	  		}
	  		ptr += size;
		}
	}
	else
	{
		//debug("little-endian \n");
	}
	
  	return err;
}

void dumpWavInfo(WAV_INFO wavInfo)
{
	debug("compressionCode:%d \n",wavInfo.header.compressionCode);
	debug("numChannels:%d \n",wavInfo.header.numChannels);
	debug("sampleRate:%d \n",wavInfo.header.sampleRate);
	debug("bytesPerSecond:%d \n",wavInfo.header.bytesPerSecond);
	debug("blockAlign:%d \n",wavInfo.header.blockAlign);
	debug("bitsPerSample:%d \n",wavInfo.header.bitsPerSample);

}

int wavInputOpen(WAV_INFO *pWav, const char *filename)
{
    signed int offset;
    WAV_INFO *wav = pWav ;

    if (wav == NULL) 
    {
      debug("Unable to allocate WAV struct.\n");
      goto error;
    }
    wav->fp = fopen(filename, "rb");
    if (wav->fp == NULL) 
    {
      debug("Unable to open wav file. %s\n", filename);
      goto error;
    }

	/* RIFF標志符判斷 */
	if (fread(&(wav->header.riffType), 1, 4, wav->fp) != 4) 
	{
	  debug("couldn't read RIFF_ID\n");
	  goto error;  /* bad error "couldn't read RIFF_ID" */
	}
	if (strncmp("RIFF", wav->header.riffType, 4)) 
	{
	  	debug("RIFF descriptor not found.\n") ;
	  	goto error;
	}
	debug("Find RIFF \n");
	
	/* Read RIFF size. Ignored. */
    readHeader(&(wav->header.riffSize), 4, 1, wav->fp);
    debug("wav->header.riffSize:%d \n",wav->header.riffSize);

    /* WAVE標志符判斷 */
    if (fread(&wav->header.waveType, 1, 4, wav->fp) !=4) 
    {
      	debug("couldn't read format\n");
      	goto error;  /* bad error "couldn't read format" */
    }
    if (strncmp("WAVE", wav->header.waveType, 4)) 
    {
      	debug("WAVE chunk ID not found.\n") ;
      	goto error;
    }
    debug("Find WAVE \n");

	/* fmt標志符判斷 */
    if (fread(&(wav->header.formatType), 1, 4, wav->fp) != 4) 
    {
      	debug("couldn't read format_ID\n");
      	goto error;  /* bad error "couldn't read format_ID" */
    }
    if (strncmp("fmt", wav->header.formatType, 3)) 
    {
      	debug("fmt chunk format not found.\n") ;
     	goto error;
    }
    debug("Find fmt \n");

   	readHeader(&wav->header.formatSize, 4, 1, wav->fp);  // Ignored
   	debug("wav->header.formatSize:%d \n",wav->header.formatSize);

	/* read  info */
	readHeader(&(wav->header.compressionCode), 2, 1, wav->fp);
	readHeader(&(wav->header.numChannels), 2, 1, wav->fp);
	readHeader(&(wav->header.sampleRate), 4, 1, wav->fp);
	readHeader(&(wav->header.bytesPerSecond), 4, 1, wav->fp);
	readHeader(&(wav->header.blockAlign), 2, 1, wav->fp);
	readHeader(&(wav->header.bitsPerSample), 2, 1, wav->fp);

	offset = wav->header.formatSize - 16;

   	/* Wav format extensible */
   	if (wav->header.compressionCode == 0xFFFE) 
	{
	 	static const unsigned char guidPCM[16] = {
	 		0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
		 	0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
	 	};
	 	unsigned short extraFormatBytes, validBitsPerSample;
	 	unsigned char guid[16];
	 	signed int i;

	 	/* read extra bytes */
	 	readHeader(&(extraFormatBytes), 2, 1, wav->fp);
	 	offset -= 2;

		if (extraFormatBytes >= 22) 
		{
			readHeader(&(validBitsPerSample), 2, 1, wav->fp);
			readHeader(&(wav->channelMask), 4, 1, wav->fp);
			readHeader(&(guid), 16, 1, wav->fp);

			/* check for PCM GUID */
			for (i = 0; i < 16; i++) if (guid[i] != guidPCM[i]) break;
			if (i == 16) wav->header.compressionCode = 0x01;

			offset -= 22;
		}
	}
	debug("wav->header.compressionCode:%d \n",wav->header.compressionCode);

    /* Skip rest of fmt header if any. */
    for (;offset > 0; offset--) 
    {
      	fread(&wav->header.formatSize, 1, 1, wav->fp);
    }

	#if 1
    do 
    {
      	/* Read data chunk ID */
		if (fread(wav->header.dataType, 1, 4, wav->fp) != 4) 
		{
			debug("Unable to read data chunk ID.\n");
			free(wav);
			goto error;
		}
      	/* Read chunk length. */
     	readHeader(&offset, 4, 1, wav->fp);

		/* Check for data chunk signature. */
		if (strncmp("data", wav->header.dataType, 4) == 0) 
		{
			debug("Find data \n");
			wav->header.dataSize = offset;
			break;
		}
		
		/* Jump over non data chunk. */
		for (;offset > 0; offset--) 
		{
			fread(&(wav->header.dataSize), 1, 1, wav->fp);
		}
    } while (!feof(wav->fp));
    debug("wav->header.dataSize:%d \n",wav->header.dataSize);
    #endif	
    
    /* return success */
    return 0;

/* Error path */
error:
    if (wav) 
    {
      if (wav->fp) 
      {
        fclose(wav->fp);
        wav->fp = NULL;
      }
      //free(wav);
    }
    return -1; 
}

#if 0
int main(int argc,char **argv)

{
	WAV_INFO wavInfo;
	char fileName[128];
	if(argc<2 || strlen(&argv[1][0])>=sizeof(fileName))
	{
		debug("argument error !!! \n");
		return -1 ;
	}
	debug("size : %d \n",sizeof(WAV_HEADER));
	strcpy(fileName,argv[1]);
	wavInputOpen(&wavInfo, fileName);
	return 0;
}
#endif



附:FIFF文件知識點

1. 簡介RIFF全稱為資源互換文件格式ResourcesInterchange FileFormat),RIFF文件是windows環境下大部分多媒體文件遵循的一種文件結構,RIFF文件所包含的數據類型由該文件的擴展名來標識,能以RIFF文件存儲的數據包括:音頻視頻交錯格式數據(.AVI)
波形格式數據(.WAV) 位圖格式數據(.RDI) MIDI格式數據(.RMI)調色板格式(.PAL)多媒體電影(.RMN)動畫光標(.ANI)其它RIFF文件(.BND)

2. CHUNK

chunk是組成RIFF文件的基本單元,它的基本結構如下:

struct chunk{

u32 id; /* 塊標志 */

u32 size; /* 塊大小 */

u8 dat[size]; /* 塊內容 */

};

id 由4個ASCII字符組成,用以識別塊中所包含的數據。如:'RIFF','LIST','fmt','data','WAV','AVI'等等,由於這種文件結構最初是由Microsoft和IBM為PC機所定義,RIFF文件是按照little-endian[2] 字節順序寫入的。

size(塊大小) 是存儲在data域中數據的長度,id與size域的大小則不包括在該值內。

dat(塊內容) 中所包含的數據是以字(WORD)為單位排列的,如果該數據結構長度是奇數,則在最后添加一個空(NULL)字節。

chunk塊中有且僅有兩種類型塊:'RIFF'和'LIST'類型可以包含其他塊,而其它塊僅能含有數據。

'RIFF'和'LIST'類型的chunk結構如下

structchunk{

u32 id; /* 塊標志 */

u32 size; /* 塊大小 */

/*此時的dat = type + restdat */

u32 type ; /* 類型 */

u8 restdat[size] /* dat中除type4個字節后剩余的數據*/

};

可以看出,'RIFF'和'LIST'也是chunk,只是它的dat由兩部分組成type和restdat。

type,由4個ASCII字符組成,代表RIFF文件的類型,如'WAV','AVI ';或者'LIST'塊的類型,如avi文件中的列表'hdrl','movi'。

restdat,dat中除type4個字節后剩余的數據,包括塊內容,包含若干chunk和'LIST'

2.1 FOURCC 一個FOURCC(fourcharacter
code
)是一個占4個字節的數據,一般表示4個ASCII字符。在RIFF文件格式中,FOURCC非常普遍,structchunk
中的id成員,'LIST','RIFF'的type成員,起始標識等信息都是用FOURCC表示的。FOURCC一般是四個字符,如'abcd'這樣的形式,也可以三個字符包含一個空格,如'abc'這樣的形式。

RIFF文件的FileData部分由若干個'LIST'和chunk組成,而'LIST'的ListData又可以由若干個'LIST'和chunk組成,即'LIST'是可以嵌套的。

'RIFF',FileType,'LIST',ListType,ChunkID都是FOURCC,即使用4字節的ASIIC字符標識類型。

FileSize,ListSize,ChunkSize為little-endian32-bit正整數,表示Type(只有'RIFF','LIST'chunk有Type)+Data一起的大小,注意它是little-endian表示,如:0x00123456,存儲地址由低到高,在little-endian系統中的存儲表示為0x56341200(字節由低位到高位存儲),而在big-endian為0x00123456(字節由高位到低位存儲)。32bit整數0x00123456存儲地址低--------->;高little-endian(字節由低位到高位存儲)56341200big-endian(字節由高位到低位存儲)00123456


免責聲明!

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



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