当前位置: 首页 > news >正文

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

转载于:https://www.cnblogs.com/Ph-one/p/6839892.html

相关文章:

  • fseek
  • signal( SIGINT, SigIntHandler )
  • Linux下直接读写物理地址内存
  • 数学2考点
  • 2018考研全年备考规划,你还在等什么?
  • 复旦软件工程专业课
  • src源dst目标
  • linux 中mmap的用法
  • platform_set_drvdata 和 platform_get_drvdata
  • 内核Alsa之pcm
  • MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
  • 双链表的初始化,建立,插入,查找,删除。
  • 顺序栈的初始化,建立,插入,查找,删除。
  • 顺序队列的初始化,建立,插入,查找,删除。
  • 链队列的初始化,建立,插入,查找,删除。
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Asm.js的简单介绍
  • CSS中外联样式表代表的含义
  • Facebook AccountKit 接入的坑点
  • learning koa2.x
  • Leetcode 27 Remove Element
  • Spring声明式事务管理之一:五大属性分析
  • Vue--数据传输
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 半理解系列--Promise的进化史
  • 对超线程几个不同角度的解释
  • 解决iview多表头动态更改列元素发生的错误
  • 浏览器缓存机制分析
  • 每天10道Java面试题,跟我走,offer有!
  • 我的面试准备过程--容器(更新中)
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • #、%和$符号在OGNL表达式中经常出现
  • #Lua:Lua调用C++生成的DLL库
  • #大学#套接字
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (C语言)球球大作战
  • (day 12)JavaScript学习笔记(数组3)
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (二)pulsar安装在独立的docker中,python测试
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (四)Android布局类型(线性布局LinearLayout)
  • (循环依赖问题)学习spring的第九天
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)大型网站架构演变和知识体系
  • ***通过什么方式***网吧
  • .CSS-hover 的解释
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net Application的目录
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET Core中的去虚