概述
Waveform Audio File Format(WAVE,又或者是因为WAV后缀而被大众所知的)是最常见的声音文件格式之一,是微软公司专门为Windows开发的一种标准数字音频文件,该文件能记录各种单声道或立体声的声音信息,并能保证声音不失真。
它采用RIFF(Resource Interchange File Format)文件格式结构。通常用来保存PCM格式的原始音频数据,所以通常被称为无损音频。但是严格意义上来讲,WAV也可以存储其它压缩格式的音频数据。
文件结构
WAV文件遵循RIFF规则,其内容以区块(chunk)为最小单位进行存储。WAV文件一般由3个区块组成:RIFF chunk、Format chunk和Data chunk。另外,文件中还可能包含一些可选的区块,如:Fact chunk、Cue points chunk、Playlist chunk、Associated data list chunk等。
这里只介绍RIFF chunk、Format chunk和Data chunk。
RIFF区块
名称 | 偏移地址 | 字节数 | 端序 | 内容 |
---|---|---|---|---|
riffType | 0x00 | 4Byte | 大端 | ‘RIFF’ (0x52494646) |
riffSize | 0x04 | 4Byte | 小端 | fileSize - 8 |
waveType | 0x08 | 4Byte | 大端 | ‘WAVE’(0x57415645) |
- 以’RIFF’为标识
- riffSize是整个文件的长度减去ID和Size的长度
- waveType是WAVE表示后面需要两个子块:Format区块和Data区块
FORMAT区块
名称 | 偏移地址 | 字节数 | 端序 | 内容 |
---|---|---|---|---|
formatType | 0x00 | 4Byte | 大端 | 'fmt ’ (0x666D7420) |
formatSize | 0x04 | 4Byte | 小端 | 16 |
compressionCode | 0x08 | 2Byte | 小端 | 音频格式 |
numChannels | 0x0A | 2Byte | 小端 | 声道数 |
sampleRate | 0x0C | 4Byte | 小端 | 采样率 |
bytesPerSecond | 0x10 | 4Byte | 小端 | 每秒数据字节数 |
blockAlign | 0x14 | 2Byte | 小端 | 每个采样所需字节数 |
bitsPerSample | 0x16 | 2Byte | 小端 | 采样位数 |
- 以’fmt '为标识
- formatSize表示该区块数据的长度(不包含ID和Size的长度)
- compressionCode表示Data区块存储的音频数据的格式,PCM音频数据的值为1
- numChannels表示音频数据的声道数,1:单声道,2:双声道
- sampleRate 表示音频数据的采样率
- bytesPerSecond每秒数据字节数 = SampleRate * NumChannels * BitsPerSample / 8
- blockAlign每个采样所需的字节数 = NumChannels * BitsPerSample / 8
- bitsPerSample每个采样存储的bit数,8:8bit,16:16bit,32:32bit
[注意] 这个区域只需要关心 NumChannels SampleRate BitsPerSample 三个参数就可以了,其它的都是依据这三个计算出来的。
DATA区块
名称 | 偏移地址 | 字节数 | 端序 | 内容 |
---|---|---|---|---|
dataType | 0x00 | 4Byte | 大端 | ‘data’ (0x64617461) |
dataSize | 0x04 | 4Byte | 小端 | N |
Data | 0x08 | NByte | 小端 | 音频数据 |
demo
import java.io.FileInputStream;
/**
* @author 作者 luhua
*/
public class WavTest {
public static void main(String[] args) throws Exception {
WavHeard wavHeard = new WavHeard();
FileInputStream fis = new FileInputStream("F:\\testFile\\audio\\b.wav");
byte[] b = new byte[4];
/**RIFF部分*/
fis.read(wavHeard.riffType);
/**riffSize*/
fis.read(b);
wavHeard.riffSize = (b[3]&0xff)<<24|(b[2]&0xff)<<16|(b[1]&0xff)<<8|(b[0]&0xff);
/**wave*/
fis.read(wavHeard.waveType);
/**FMT */
fis.read(wavHeard.formatType);
/**表示该区块数据的长度(不包含FMT 和fSize的长度)*/
fis.read(b);
wavHeard.formatSize = (b[3]&0xff)<<24 | (b[2]&0xff)<<16 | (b[1]&0xff)<<8 | (b[0]&0xff);
/**PCM音频数据的值为1*/
b = new byte[2];
fis.read(b);
wavHeard.compressionCode = (short) ((b[1]&0xff)<<8 | (b[0]&0xff));
/**音频数据的声道数,1:单声道,2:双声道*/
fis.read(b);
wavHeard.numChannels = (short) ((b[1]&0xff)<<8 | (b[0]&0xff));
/**采样率*/
b = new byte[4];
fis.read(b);
wavHeard.sampleRate = (b[3]&0xff) <<24 | (b[2]&0xff)<<16 | (b[1]&0xff)<<8 | (b[0]&0xff);
/**每秒数据字节数*/
fis.read(b);
wavHeard.bytesPerSecond = (b[3]&0xff) <<24 | (b[2]&0xff)<<16 | (b[1]&0xff)<<8 | (b[0]&0xff);
/**每个采样所需字节数*/
b = new byte[2];
fis.read(b);
wavHeard.blockAlign = (short) ((b[1]&0xff)<<8 | (b[0]&0xff));
/**每个采样存储的bit数*/
fis.read(b);
wavHeard.bitsPerSample = (short) ((b[1]&0xff)<<8 | (b[0]&0xff));
/**data*/
fis.read(wavHeard.dataType);
/**dataSize*/
b = new byte[4];
fis.read(b);
wavHeard.dataSize = (b[3]&0xff) <<24 | (b[2]&0xff)<<16 | (b[1]&0xff)<<8 | (b[0]&0xff);
fis.close();
System.out.println(wavHeard);
}
}
class WavHeard{
public byte[] riffType = new byte[4]; //4byte,资源交换文件标志:RIFF
public int riffSize; //4byte,从下个地址到文件结尾的总字节数
public byte[] waveType = new byte[4]; //4byte,wav文件标志:WAVE
public byte[] formatType = new byte[4]; //4byte,波形文件标志:FMT(最后一位空格符)
public int formatSize; //4byte,音频属性(compressionCode,numChannels,sampleRate,bytesPerSecond,blockAlign,bitsPerSample)所占字节数
public short compressionCode; //2byte,格式种类(1-线性pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM)
public short numChannels; //2byte,通道数
public int sampleRate; //4byte,采样率
public int bytesPerSecond; //4byte,传输速率
public short blockAlign; //2byte,数据块的对齐,即DATA数据块长度
public short bitsPerSample; //2byte,采样精度-PCM位宽
public byte[] dataType = new byte[4]; //4byte,数据标志:data
public int dataSize; //4byte,从下个地址到文件结尾的总字节数,即除了wav header以外的pcm data length
@Override
public String toString() {
return "------------------RIFF------------------\n"+
"\n riffType\t:"+new String(riffType)+
"\n riffSize\t:"+riffSize+
"\n waveType\t:"+new String(waveType)+
"\n\n------------------FORMAT----------------\n"+
"\n formatType\t:"+ new String(formatType) +
"\n formatSize\t:"+ formatSize +
"\n compressionCode:"+ compressionCode +
"\n numChannels\t:" + numChannels +
"\n sampleRate\t:" + sampleRate +
"\n bytesPerSecond\t:" + bytesPerSecond +
"\n blockAlign\t:" + blockAlign +
"\n bitsPerSample\t:" + bitsPerSample +
"\n dataType\t:" + new String(dataType) +
"\n dataSize\t:" + dataSize +
"\n\n-----------------------------------------";
}
}
解析结果
测试文件
csdn下载 : https://download.csdn.net/download/qq_41054313/18704149
百度网盘 : https://pan.baidu.com/s/1U93rkCs-jqrPBE-Zgd_W7A 提取码:xhn8