无论是文字、图像还是声音,都必须以一定的格式来组织和存储起来,这样播放器才知道以怎样的方式去解析这一段数据,例如,对于原始的图像数据,我们常见的格式有 YUV、Bitmap,而对于音频来说,最简单常见的格式就是 wav 格式了。
文件头首先,我们了解一下 wav 格式的“文件头”: 我们可以简单地分析一下这个 wav 格式头,它主要分为三个部分:
- 第一部分,属于最“顶层”的信息块,通过“ChunkID”来表示这是一个 “RIFF”格式的文件,通过“Format”填入“WAVE”来标识这是一个 wav 文件。而“ChunkSize”则记录了整个 wav 文件的字节数。
- 第二部分,属于“fmt”信息块,主要记录了本 wav 音频文件的详细音频参数信息,例如:通道数、采样率、位宽等
- 第三部分,属于“data”信息块,由“Subchunk2Size”这个字段来记录后面存储的二进制原始音频数据的长度。
Java 代码:
public class WavFileHeader {
public String mChunkID = "RIFF";
public int mChunkSize = 0;
public String mFormat = "WAVE";
public String mSubChunk1ID = "fmt ";
public int mSubChunk1Size = 16;
public short mAudioFormat = 1;
public short mNumChannel = 1;
public int mSampleRate = 8000;
public int mByteRate = 0;
public short mBlockAlign = 0;
public short mBitsPerSample = 8;
public String mSubChunk2ID = "data";
public int mSubChunk2Size = 0;
public WavFileHeader() {
}
public WavFileHeader(int sampleRateInHz, int bitsPerSample, int channels) {
mSampleRate = sampleRateInHz;
mBitsPerSample = (short)bitsPerSample;
mNumChannel = (short)channels;
mByteRate = mSampleRate*mNumChannel*mBitsPerSample/8;
mBlockAlign = (short)(mNumChannel*mBitsPerSample/8);
}
}
读写 wav 文件
wav 文件就是一段“文件头”+“音频二进制数据”,因此:
(1)写 wav 文件,其实就是先写入一个 wav 文件头,然后再继续写入音频二进制数据即可
(2)读 wav 文件,其实也就是先读一个 wav 文件头,然后再继续读出音频二进制数据即可
1. wav 文件头中,有哪些是“变化的”,哪些是“不变的”? 比如:文件头开头的“RIFF”字符串就是“不变的”部分,而用来记录音频数据总长度的“Subchunk2Size”变量就是属于“变化的”部分,因为,再音频数据没有彻底全部写完之前,你是无法知道一共写入了多少字节的音频数据的,因此,这个部分,需要用一个变量记录起来,到全部写完之后,再使用 Java 的“RandomAccessFile”类,将文件指针跳转到“Subchunk2Size”字段,改写一下默认值即可。
2. 如何把 int、short 变量与 byte[] 的转换 因为 wav 文件都是二进制的方式读写,因此,“WavFileHeader”类中定义的变量都需要转换为byte字节流,具体转换方法如下:
private static byte[] intToByteArray(int data) {
return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(data).array();
}
private static byte[] shortToByteArray(short data) {
return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(data).array();
}
private static short byteArrayToShort(byte[] b) {
return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getShort();
}
private static int byteArrayToInt(byte[] b) {
return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getInt();
}