android音频学习笔记之wav头文件

如何存储和解析wav文件
定义:wav格式,就是微软开发的一种文件格式规范,文件分为两部分
(1)第一部分:文件头,记录重要的参数信息,对于音频而言,包括:采样率,通道数,位宽等等
(2)第二部分:数据块,也就是一帧一帧的二进制数据,对于音频而言,就是原始的PCM数据

(一)将采集的PCM音频数据保存到wav文件

文件头
在这里插入图片描述
wav格式头,主要分为三个部分
(1)属于最顶层的信息块
ChunkID 来表示这是一个RIFF格式文件
Format 填入WAVE 来标识这是一个wav文件
ChunkSize 记录了整个wav文件的字节数

(2)第二部分 属于fmt信息块
主要记录了本wav音频文件的详细音频参数信息,例如通道数,采样率,位宽

(3)第三部分,数据data信息块,由Subchunk2Size 这个字段来记录后面存储的二进制原始音频数据的长度

其实,格式就是一种规范,告诉你二进制数据是怎么存储的,你应该按照什么样的方式解析
下图就是数据怎么进行二进制存储的
在这里插入图片描述

所以可以写一个java类来抽象和描述wav文件头

public class WavFileHeader {

    public static final int WAV_FILE_HEADER_SIZE = 44;
    public static final int WAV_CHUNKSIZE_EXCLUDE_DATA = 36;//第一部分"顶层"和第二部分"fmt"信息块共占36位

    public static final int WAV_CHUNKSIZE_OFFSET = 4; //ChunkSize是第4位开始,占4个byte
    public static final int WAV_SUB_CHUNKSIZE1_OFFSET = 16;//SubChunk1Size是第16位开始,占4个byte
    public static final int WAV_SUB_CHUNKSIZE2_OFFSET = 40;//SubChunk2Size是第40位开始,占4个byte

    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;
        mBitsPerSample = (short) bitsPerSample;
        mNumChannel = (short) channels;
        mByteRate = mSampleRate * mNumChannel * mBitsPerSample / 8;
        mBlockAlign = (short) (mNumChannel * mBitsPerSample / 8);
    }
}

也就是将上图描述的用java代码进行抽象化

读写wav文件
wav文件就是一段“文件头+音频二进制数据”,因此:
(1)写wav文件,其实就是先写入一个wav文件头,然后再继续写入音频二进制数据
(2)读wav文件,其实也就是先读一个wav文件,然后再继续读出音频二进制数据即可

值得注意的是:
(1)wav文件头中,有哪些是变化的,哪些是不变的: 文件头开头的“RIFF”字符串就是“不变的”部分,而用来记录音频数据总长度的“Subchunk2Size”变量就是属于“变化的”部分,因为,再音频数据没有彻底全部写完之前,你是无法知道一共写入了多少字节的音频数据的,因此,这个部分,需要用一个变量记录起来,到全部写完之后,再使用 Java 的“RandomAccessFile”类,将文件指针跳转到“Subchunk2Size”字段,改写一下默认值即可。
(2)如何将int ,short变量与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();
}

参考文献:Jhuster的博客

猜你喜欢

转载自blog.csdn.net/qq_42447739/article/details/127695600