一、什么是wav
wav是一种无损的音频文件格式,wav文件有两部分,第一部分是文件头,记录一些重要的参数信息,如音频的采样率,通道数,数据位宽,第二部分是数据部分,数据部分可以是PCM,也可以是其它的编码格式的数据
二、为什么要将音频存储wav格式
存储为该格式,音乐播放器可以通过读取wav头,识别出它是音频文件,从而进行播放。
因为后缀名是可以任意修改的,不能简单的通过后缀名来判断该文件是否是音频文件
三、wav与pcm的区别
pcm是一种未经压缩的编码方式
wav是一种无损的音频文件格式
四、wav文件结构说明
五、存储wav格式文件
WaveHeader代码
public class WavFileHeader {
public static final int WAV_FILE_HEADER_SIZE = 44;
public static final int WAV_CHUNKSIZE_EXCLUDE_DATA = 36;
public static final int WAV_CHUNKSIZE_OFFSET = 4;
public static final int WAV_SUB_CHUNKSIZE1_OFFSET = 16;
public static final int WAV_SUB_CHUNKSIZE2_OFFSET = 40;
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 channels, int bitsPerSample){
mSampleRate = sampleRateInHz;
mNumChannel = (short) channels;
mBitsPerSample = (short) bitsPerSample;
mByteRate = mSampleRate * mNumChannel * mBitsPerSample / 8;
mBlockAlign = (short) (mNumChannel * mBitsPerSample / 8);
}
}
public class WavFileWriter {
private static final String TAG = "WavFileWriter";
private String mFilePath;
private int mDataSize = 0;
private DataOutputStream dos;
/**
*
* @param filePath
* @param sampleRateInHz 采样率 44100
* @param channels 声道数 1单声道 2双声道
* @param bitsPerSample 每个样点对应的位数 16
* @return
*/
public boolean openFile(String filePath, int sampleRateInHz, int channels, int bitsPerSample) {
if (dos != null) {
closeFile();
}
mFilePath = filePath;
try {
dos = new DataOutputStream(new FileOutputStream(mFilePath));
return writeHeader(sampleRateInHz, channels, bitsPerSample);
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
}
}
public boolean closeFile() {
boolean result=false;
if (dos != null) {
try {
result=writeDataSize();
dos.close();
dos=null;
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public boolean writeData(byte[] buffer, int offset, int count) {
if (dos == null) {
return false;
}
try {
dos.write(buffer, offset, count);
mDataSize += count;
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 将一些需要计算出来的字段重新赋值
* mChunkSize 位置4-8,值=36+原始音频数据大小
* mSubChunk1Size 固定值16
* mSubChunk2Size 位置40-44 值=原始音频数据大小
*/
private boolean writeDataSize() {
if (dos == null) {
return false;
}
try {
RandomAccessFile waveAccessFile = new RandomAccessFile(mFilePath, "rw");
waveAccessFile.seek(WavFileHeader.WAV_CHUNKSIZE_OFFSET);
waveAccessFile.write(intToByteArray(WavFileHeader.WAV_CHUNKSIZE_EXCLUDE_DATA + mDataSize), 0, 4);
waveAccessFile.seek(WavFileHeader.WAV_SUB_CHUNKSIZE2_OFFSET);
waveAccessFile.write(intToByteArray(mDataSize), 0, 4);
waveAccessFile.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
private boolean writeHeader(int sampleRateInHz, int channels, int bitsPerSample) {
if (dos == null) {
return false;
}
WavFileHeader header = new WavFileHeader(sampleRateInHz, channels, bitsPerSample);
//按照wav文件结构依次写入
try {
dos.writeBytes(header.mChunkID);
//这里不直接用writeInt的原因是它采用的大端法存储
dos.write(intToByteArray(header.mChunkSize), 0, 4);
dos.writeBytes(header.mFormat);
dos.writeBytes(header.mSubChunk1ID);
dos.write(intToByteArray(header.mSubChunk1Size), 0, 4);
dos.write(shortToByteArray(header.mAudioFormat), 0, 2);
dos.write(shortToByteArray(header.mNumChannel), 0, 2);
dos.write(intToByteArray(header.mSampleRate), 0, 4);
dos.write(intToByteArray(header.mByteRate), 0, 4);
dos.write(shortToByteArray(header.mBlockAlign), 0, 2);
dos.write(shortToByteArray(header.mBitsPerSample), 0, 2);
dos.writeBytes(header.mSubChunk2ID);
dos.write(intToByteArray(header.mSubChunk2Size), 0, 4);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
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();
}
}