在介绍Wav格式之前先聊聊RIFF。
1.RIFF是什么?
RIFF(Resources Interchange File Format)资源互换文件格式。在Windows中有很多多媒体文件都遵循RIFF的文件结构。以RIFF格式存储的数据有:
Audio/visual interleaved data (.AVI)
Waveform data (.WAV)
Bitmapped data (.RDI)
MIDI information (.RMI)
Color palette (.PAL)
Multimedia movie (.RMN)
Animated cursor (.ANI)
A bundle of other RIFF files (.BND)
2. RIFF的结构?
chunk是RIFF文件的基本单元。通常情况下,一个chunk是指多媒体数据的一个基本逻辑单元,比如视频的一帧数据,音频的一帧数据等。
chunk的结构是:
id 4字节,用于标识块中所包含的数据。如:RIFF,LIST,fmt,data,wav,avi等。
size 块大小,存储在data域中数据长度,不包含id和size的大小。
data 包含数据,如果数据长度为奇书,则最后添加一个空字节。
3.chunk嵌套?
chunk是可以嵌套的,只有块id为RIFF或LIST的chunk才能包含其他的chunk。
每个RIFF文件首先存放的必须是RIFF chunk,并且只能有一个标志为RIFF的chunk。RIFF的数据域的起始位置是一个4字节码(ROURCC),用于标识其数据域的格式,比如 WAV,AVI等 ;接着数据域的内容是包含的subChunk。
如上图所示,RIFF的数据域的初始位置是 form Type,接着是嵌套了两个chunk,每个chunk都有字节的标识,数据域大小和数据域。
RIFF chunk的数据域下嵌套了一个 LIST chunk 和一个 chunk。LIST chunk嵌套了两个chunk。
LIST chunk的数据域的起始位置也有一个四字节码( List Type)用于说明LIST数据域的数据内容。
4.Wav 是什么?
Wav是一种音频文件格式,它符合RIFF文件格式。一个Wav文件通常有三个chunk和一个可选的chunk组成。
在文件中排列循序是:RIFF chunk ,Format chunk ,Fact chunk(可选),Data chunk。
在这个Wav文件中,首先是一个RIFF chunk, RIFF chunk的数据域中包含了Format chunk ,Data chunk 和Fact chunk(可选)。
大致的意思是
RIFF chunk 的格式是 WAVE,包含了 fmt 和 data 两个subchunk。
fmt chunk 描述了 data chunk 音频的信息。
data chunk 描述 音频的大小,包含音频数据。
Chunk ID 包含 'R' , 'I' , 'F' , 'F' 字母;
ChunkSize 36 + SubChunk2Size 或者 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size);
Format 包含'W' , 'A' , 'V' , 'E';
Subchunk1ID 包含'f' , 'm' , 't' ;
Subchunk1Size Subchunk的数据域大小。如果是PCM的话是16
AudioFormat 1 PCM格式, 2 音频数据格式
NumChannels 1为 单声道, 2 为双声道
SampleRate 采样率
ByteRate 音频的码率(每秒播放的字节数) SampleRate * NumChannels * BitsPerSample / 8
BlockAlign 一次采样的大小 NumChannels * BitsPerSample / 8
BitsPerSample 位宽 8bit,16bit
ExtraParamSize 扩展长度
ExtraParams 扩展内容(22字节)
Subchunk2ID 包含 'd' , 'a' , 't' , 'a' 字母
Subchunk2Size NumSamples* NumChannels * BitsPerSample / 8
Data 音频数据
5. PCM转Wav
5.1 写Wav头部信息
private byte[]writeWavHeader(FileOutputStream dataOutputStream, long totalAudioLen, long totalDataLen, long longSampleRate,
int channels, long byteRate)throws IOException {
byte[] header =new byte[44];
//RIFF WAVE Chunk
// RIFF标记占据四个字节
header[0] ='R';
header[1] ='I';
header[2] ='F';
header[3] ='F';
//数据大小表示,由于原始数据为long型,通过四次计算得到长度
header[4] = (byte) (totalDataLen &0xff);
header[5] = (byte) ((totalDataLen >>8) &0xff);
header[6] = (byte) ((totalDataLen >>16) &0xff);
header[7] = (byte) ((totalDataLen >>24) &0xff);
//WAVE标记占据四个字节
header[8] ='W';
header[9] ='A';
header[10] ='V';
header[11] ='E';
//FMT Chunk
header[12] ='f';
// 'fmt '标记符占据四个字节
header[13] ='m';
header[14] ='t';
header[15] =' ';//过渡字节
//数据大小
header[16] =16; // 4 bytes: size of 'fmt ' chunk
header[17] =0;
header[18] =0;
header[19] =0;
//编码方式 10H为PCM编码格式
header[20] =1; // format = 1
header[21] =0;
//通道数
header[22] = (byte) channels;
header[23] =0;
//采样率,每个通道的播放速度
header[24] = (byte) (longSampleRate &0xff);
header[25] = (byte) ((longSampleRate >>8) &0xff);
header[26] = (byte) ((longSampleRate >>16) &0xff);
header[27] = (byte) ((longSampleRate >>24) &0xff);
//音频数据传送速率,采样率*通道数*采样深度/8
header[28] = (byte) (byteRate &0xff);
header[29] = (byte) ((byteRate >>8) &0xff);
header[30] = (byte) ((byteRate >>16) &0xff);
header[31] = (byte) ((byteRate >>24) &0xff);
// 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数
header[32] = (byte) (1 *16 /8);
header[33] =0;
//每个样本的数据位数
header[34] =16;
header[35] =0;
//Data chunk
header[36] ='d';//data标记符
header[37] ='a';
header[38] ='t';
header[39] ='a';
//数据长度
header[40] = (byte) (totalAudioLen &0xff);
header[41] = (byte) ((totalAudioLen >>8) &0xff);
header[42] = (byte) ((totalAudioLen >>16) &0xff);
header[43] = (byte) ((totalAudioLen >>24) &0xff);
dataOutputStream.write(header, 0, 44);
return header;
}
5.2 转换
fileTarget =new File(rootFile, "test.pcm");
fileWav =new File(rootFile, "test.wav");
DataInputStream dataInputStream =null;
DataOutputStream dataOutputStream =null;
if (!fileWav.exists()) {
try {
fileWav.createNewFile();
}catch (IOException e) {
e.printStackTrace();
}
}
convertPcmToWav(fileTarget.getPath(),fileWav.getPath(),mSampleRate,1,mAudioFormat);
public void convertPcmToWav(String inPcmFilePath, String outWavFilePath, int sampleRate,
int channels, int bitNum) {
FileInputStream in =null;
FileOutputStream out =null;
byte[] data =new byte[1024];
try {
long byteRate = sampleRate * channels * bitNum /8; //码率
in =new FileInputStream(inPcmFilePath);
out =new FileOutputStream(outWavFilePath);
long totalAudioLen = in.getChannel().size(); //PCM文件大小
//不包括'RIFF'和'WAV',所以是44 - 8 = 36,在加上PCM文件大小
long totalDataLen = totalAudioLen +36;
writeWavHeader(out, totalAudioLen, totalDataLen, sampleRate, channels, byteRate);
int length =0;
while ((length = in.read(data)) >0) {
out.write(data, 0, length);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
if (in !=null) {
try {
in.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if (out !=null) {
try {
out.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
参考:感谢!
Microsoft WAVE soundfile format
网友评论