Android wav文件的存储

一、什么是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();
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网小熊猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值