什么是wav
wav是一种无损的音频文件格式,wav文件有两部分,第一部分是文件头,记录一些重要的参数信息,如音频的采样率,通道数,数据位宽,第二部分是数据部分,数据部分可以是PCM,也可以是其它的编码格式的数据
为什么要将音频存储wav格式
存储为该格式,音乐播放器可以通过读取wav头,识别出它是音频文件,从而进行播放。 因为后缀名是可以任意修改的,不能简单的通过后缀名来判断该文件是否是音频文件
wav与pcm的区别
pcm是一种未经压缩的编码方式
wav是一种无损的音频文件格式
wav文件结构说明
little
小端法,低位字节放在内存的低地址端
big
大端法,低位字节放在内存的高地址端
write(int)和writeInt(int)区别
write只写入最低的8位
writeInt会按大端法写
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);
- }
- }
将录音存储为wav文件
- 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();
- }
- }
解析wav文件并播放
- public class WavFileReader {
- private static final String TAG="WavFileReader";
- private DataInputStream dis;
- private WavFileHeader mWavFileHeader;
- public WavFileHeader getWavFileHeader(){
- return mWavFileHeader;
- }
- public boolean openFile(String filePath){
- if(dis!=null){
- closeFile();
- }
- try {
- dis=new DataInputStream(new FileInputStream(filePath));
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- return readHeader();
- }
- public void closeFile(){
- if(dis!=null){
- try {
- dis.close();
- dis=null;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- public int readData(byte[] buffer, int offset, int count) {
- if (dis == null || mWavFileHeader == null) {
- return -1;
- }
- try {
- int nbytes = dis.read(buffer, offset, count);
- if (nbytes == -1) {
- return 0;
- }
- return nbytes;
- } catch (IOException e) {
- e.printStackTrace();
- }
- return -1;
- }
- /**
- *read和read(byte b[])
- * read每次读取一个字节,返回0-255的int字节值
- * read(byte b[])读取一定数量的字节,返回实际读取的字节的数量
- */
- private boolean readHeader(){
- if(dis==null){
- return false;
- }
- WavFileHeader header=new WavFileHeader();
- byte[] intValue = new byte[4];
- byte[] shortValue = new byte[2];
- try {
- header.mChunkID = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
- Log.d(TAG, "Read file chunkID:" + header.mChunkID);
- dis.read(intValue);
- header.mChunkSize=byteArrayToInt(intValue);
- Log.d(TAG, "Read file chunkSize:" + header.mChunkSize);
- header.mFormat = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
- Log.d(TAG, "Read file format:" + header.mFormat);
- header.mSubChunk1ID = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
- Log.d(TAG, "Read fmt chunkID:" + header.mSubChunk1ID);
- dis.read(intValue);
- header.mSubChunk1Size = byteArrayToInt(intValue);
- Log.d(TAG, "Read fmt chunkSize:" + header.mSubChunk1Size);
- dis.read(shortValue);
- header.mAudioFormat = byteArrayToShort(shortValue);
- Log.d(TAG, "Read audioFormat:" + header.mAudioFormat);
- dis.read(shortValue);
- header.mNumChannel = byteArrayToShort(shortValue);
- Log.d(TAG, "Read channel number:" + header.mNumChannel);
- dis.read(intValue);
- header.mSampleRate = byteArrayToInt(intValue);
- Log.d(TAG, "Read samplerate:" + header.mSampleRate);
- dis.read(intValue);
- header.mByteRate = byteArrayToInt(intValue);
- Log.d(TAG, "Read byterate:" + header.mByteRate);
- dis.read(shortValue);
- header.mBlockAlign = byteArrayToShort(shortValue);
- Log.d(TAG, "Read blockalign:" + header.mBlockAlign);
- dis.read(shortValue);
- header.mBitsPerSample = byteArrayToShort(shortValue);
- Log.d(TAG, "Read bitspersample:" + header.mBitsPerSample);
- header.mSubChunk2ID = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
- Log.d(TAG, "Read data chunkID:" + header.mSubChunk2ID);
- dis.read(intValue);
- header.mSubChunk2Size = byteArrayToInt(intValue);
- Log.d(TAG, "Read data chunkSize:" + header.mSubChunk2Size);
- Log.d(TAG, "Read wav file success !");
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- mWavFileHeader=header;
- return true;
- }
- private int byteArrayToInt(byte[] b){
- return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getInt();
- }
- private short byteArrayToShort(byte[] b){
- return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getShort();
- }
- }
- public class AudioWavActivity extends UIRootActivity {
- private Button btn_audio_record;
- private Button btn_audio_record_play;
- private AudioCapture audioCapture;
- private AudioPlayer audioPlayer;
- private WavFileWriter wavFileWriter;
- private WavFileReader wavFileReader;
- private boolean isReading;
- private String path="";
- @Override
- protected int getLayoutId() {
- return R.layout.activity_media_audio;
- }
- @Override
- protected void initTitle() {
- head_title.setText("wav音频文件的存储和解析");
- }
- @Override
- public void initView() {
- btn_audio_record=findViewById(R.id.btn_audio_record);
- btn_audio_record_play=findViewById(R.id.btn_audio_record_play);
- }
- @Override
- public void initData() {
- path=FileUtil.getAudioDir(this)+"/audioTest.wav";
- audioCapture=new AudioCapture();
- audioPlayer=new AudioPlayer();
- wavFileReader=new WavFileReader();
- wavFileWriter=new WavFileWriter();
- String des = "录音权限被禁止,我们需要打开录音权限";
- String[] permissions = new String[]{Manifest.permission.RECORD_AUDIO};
- baseAt.requestPermissions(des, permissions, 100, new PermissionsResultListener() {
- @Override
- public void onPermissionGranted() {
- }
- @Override
- public void onPermissionDenied() {
- finish();
- }
- });
- }
- @Override
- public void initEvent() {
- btn_audio_record.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if(event.getAction()==MotionEvent.ACTION_DOWN){
- Log.d("TAG","按住");
- start();
- }else if(event.getAction()==MotionEvent.ACTION_UP){
- Log.d("TAG","松开");
- stop();
- }
- return false;
- }
- });
- btn_audio_record_play.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- play();
- }
- });
- }
- //播放录音
- private void play(){
- isReading=true;
- wavFileReader.openFile(path);
- audioPlayer.startPlay();
- new AudioTrackThread().start();
- }
- private class AudioTrackThread extends Thread{
- @Override
- public void run() {
- byte[] buffer = new byte[1024];
- while (isReading && wavFileReader.readData(buffer,0,buffer.length)>0){
- audioPlayer.play(buffer,0,buffer.length);
- }
- audioPlayer.stopPlay();
- wavFileReader.closeFile();
- }
- }
- //开始录音
- private void start(){
- wavFileWriter.openFile(path,44100,2,16);
- btn_audio_record.setText("松开 结束");
- audioCapture.startRecord();
- audioCapture.setOnAudioFrameCaptureListener(new AudioCapture.onAudioFrameCaptureListener() {
- @Override
- public void onAudioFrameCapture(byte[] audioData) {
- wavFileWriter.writeData(audioData,0,audioData.length);
- }
- });
- }
- //结束录音
- private void stop(){
- btn_audio_record.setText("按住 录音");
- audioCapture.stopRecord();
- wavFileWriter.closeFile();
- }
- }
摘抄
© 版权声明