zoukankan      html  css  js  c++  java
  • 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文件是很easy的一种RIFF文件,它的格式类型为"WAVE"。RIFF块包括两个子块,这两个子块的ID各自是"fmt"和"data",当中"fmt"子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeofof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。
    整个头长度44byte.

    标志符(RIFF)

    余下全部数据的长度

    格式类型("WAVE")

    "fmt"

    PCMWAVEFORMAT的长度

    PCMWAVEFORMAT

    "data"

    声音数据大小

    声音数据


    wav头结构体定义

    /* RIFF WAVE file struct.
     * For details see WAVE file format documentation 
     * (for example at http://www.wotsit.org).
     */
    typedef struct WAV_HEADER_S
    {
    	char   			riffType[4];	//4byte,资源交换文件标志:RIFF	
    	unsigned int   	riffSize;		//4byte,从下个地址到文件结尾的总字节数	
    	char   			waveType[4];	//4byte,wav文件标志: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,数据块的对齐,即DATA数据块长度
    	unsigned short 	bitsPerSample;	//2byte,採样精度-PCM位宽
    	char   			dataType[4];	//4byte,数据标志:data
    	unsigned int   	dataSize;		//4byte,从下个地址到文件结尾的总字节数。即除了wav header以外的pcm data length
    }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 http://www.wotsit.org).
     */
    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 
    ");
    		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 
    ");
    	}
    	
      	return err;
    }
    
    void dumpWavInfo(WAV_INFO wavInfo)
    {
    	debug("compressionCode:%d 
    ",wavInfo.header.compressionCode);
    	debug("numChannels:%d 
    ",wavInfo.header.numChannels);
    	debug("sampleRate:%d 
    ",wavInfo.header.sampleRate);
    	debug("bytesPerSecond:%d 
    ",wavInfo.header.bytesPerSecond);
    	debug("blockAlign:%d 
    ",wavInfo.header.blockAlign);
    	debug("bitsPerSample:%d 
    ",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.
    ");
          goto error;
        }
        wav->fp = fopen(filename, "rb");
        if (wav->fp == NULL) 
        {
          debug("Unable to open wav file. %s
    ", filename);
          goto error;
        }
    
    	/* RIFF标志符推断 */
    	if (fread(&(wav->header.riffType), 1, 4, wav->fp) != 4) 
    	{
    	  debug("couldn't read RIFF_ID
    ");
    	  goto error;  /* bad error "couldn't read RIFF_ID" */
    	}
    	if (strncmp("RIFF", wav->header.riffType, 4)) 
    	{
    	  	debug("RIFF descriptor not found.
    ") ;
    	  	goto error;
    	}
    	debug("Find RIFF 
    ");
    	
    	/* Read RIFF size. Ignored. */
        readHeader(&(wav->header.riffSize), 4, 1, wav->fp);
        debug("wav->header.riffSize:%d 
    ",wav->header.riffSize);
    
        /* WAVE标志符推断 */
        if (fread(&wav->header.waveType, 1, 4, wav->fp) !=4) 
        {
          	debug("couldn't read format
    ");
          	goto error;  /* bad error "couldn't read format" */
        }
        if (strncmp("WAVE", wav->header.waveType, 4)) 
        {
          	debug("WAVE chunk ID not found.
    ") ;
          	goto error;
        }
        debug("Find WAVE 
    ");
    
    	/* fmt标志符推断 */
        if (fread(&(wav->header.formatType), 1, 4, wav->fp) != 4) 
        {
          	debug("couldn't read format_ID
    ");
          	goto error;  /* bad error "couldn't read format_ID" */
        }
        if (strncmp("fmt", wav->header.formatType, 3)) 
        {
          	debug("fmt chunk format not found.
    ") ;
         	goto error;
        }
        debug("Find fmt 
    ");
    
       	readHeader(&wav->header.formatSize, 4, 1, wav->fp);  // Ignored
       	debug("wav->header.formatSize:%d 
    ",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 
    ",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.
    ");
    			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 
    ");
    			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 
    ",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 !!! 
    ");
    		return -1 ;
    	}
    	debug("size : %d 
    ",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




  • 相关阅读:
    在桌面建立快捷方式 Mr
    在窗口中显示一幅 JPG 图象 Mr
    【转载】COM组件设计与应用(一)——起源及复合文件 Mr
    【转载】COM组件设计与应用(二)——GUID和接口 Mr
    关于在Win7 SP1操作系统下编译的ADO应用程序不能在比Win7 SP1版本低的操作系统上运行,微软给出的解释和解决方案 Mr
    Vue UI图形化项目管理使用
    vue+eldialog提交校验
    获取当前日期前一个月日期与天数数组
    element输入框只能输入数字或小数保留两位小数
    vue+ DatePicker日期的加减
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/7116139.html
Copyright © 2011-2022 走看看