什么是wav

wav是一种无损的音频文件格式,wav文件有两部分,第一部分是文件头,记录一些重要的参数信息,如音频的采样率,通道数,数据位宽,第二部分是数据部分,数据部分可以是PCM,也可以是其它的编码格式的数据

为什么要将音频存储wav格式

存储为该格式,音乐播放器可以通过读取wav头,识别出它是音频文件,从而进行播放。 因为后缀名是可以任意修改的,不能简单的通过后缀名来判断该文件是否是音频文件

wav与pcm的区别

pcm是一种未经压缩的编码方式

wav是一种无损的音频文件格式

wav文件结构说明

[摘]音视频学习系列第(三)篇---wav文件的存储和解析

little

小端法,低位字节放在内存的低地址端

big

大端法,低位字节放在内存的高地址端

write(int)和writeInt(int)区别

write只写入最低的8位

writeInt会按大端法写

WaveHeader代码
  1. public class WavFileHeader {
  2.  
  3. public static final int WAV_FILE_HEADER_SIZE = 44;
  4. public static final int WAV_CHUNKSIZE_EXCLUDE_DATA = 36;
  5.  
  6. public static final int WAV_CHUNKSIZE_OFFSET = 4;
  7. public static final int WAV_SUB_CHUNKSIZE1_OFFSET = 16;
  8. public static final int WAV_SUB_CHUNKSIZE2_OFFSET = 40;
  9.  
  10. public String mChunkID="RIFF";
  11. public int mChunkSize=0;
  12. public String mFormat="WAVE";
  13.  
  14. public String mSubChunk1ID="fmt ";
  15. public int mSubChunk1Size = 16;
  16. public short mAudioFormat = 1;
  17. public short mNumChannel = 1;
  18. public int mSampleRate = 8000;
  19. public int mByteRate = 0;
  20. public short mBlockAlign = 0;
  21. public short mBitsPerSample = 8;
  22.  
  23. public String mSubChunk2ID = "data";
  24. public int mSubChunk2Size = 0;
  25.  
  26.  
  27. public WavFileHeader(){
  28.  
  29. }
  30.  
  31. public WavFileHeader(int sampleRateInHz, int channels, int bitsPerSample){
  32. mSampleRate = sampleRateInHz;
  33. mNumChannel = (short) channels;
  34. mBitsPerSample = (short) bitsPerSample;
  35. mByteRate = mSampleRate * mNumChannel * mBitsPerSample / 8;
  36. mBlockAlign = (short) (mNumChannel * mBitsPerSample / 8);
  37. }
  38. }
复制

将录音存储为wav文件

  1. public class WavFileWriter {
  2.  
  3. private static final String TAG = "WavFileWriter";
  4.  
  5. private String mFilePath;
  6. private int mDataSize = 0;
  7. private DataOutputStream dos;
  8.  
  9.  
  10. /**
  11. *
  12. * @param filePath
  13. * @param sampleRateInHz 采样率 44100
  14. * @param channels 声道数 1单声道 2双声道
  15. * @param bitsPerSample 每个样点对应的位数 16
  16. * @return
  17. */
  18. public boolean openFile(String filePath, int sampleRateInHz, int channels, int bitsPerSample) {
  19. if (dos != null) {
  20. closeFile();
  21. }
  22.  
  23. mFilePath = filePath;
  24. try {
  25. dos = new DataOutputStream(new FileOutputStream(mFilePath));
  26. return writeHeader(sampleRateInHz, channels, bitsPerSample);
  27. } catch (FileNotFoundException e) {
  28. e.printStackTrace();
  29. return false;
  30. }
  31. }
  32.  
  33.  
  34. public boolean closeFile() {
  35. boolean result=false;
  36. if (dos != null) {
  37. try {
  38. result=writeDataSize();
  39. dos.close();
  40. dos=null;
  41. } catch (IOException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. return result;
  46. }
  47.  
  48. public boolean writeData(byte[] buffer, int offset, int count) {
  49. if (dos == null) {
  50. return false;
  51. }
  52. try {
  53. dos.write(buffer, offset, count);
  54. mDataSize += count;
  55. } catch (IOException e) {
  56. e.printStackTrace();
  57. return false;
  58. }
  59. return true;
  60. }
  61.  
  62. /**
  63. * 将一些需要计算出来的字段重新赋值
  64. * mChunkSize 位置4-8,值=36+原始音频数据大小
  65. * mSubChunk1Size 固定值16
  66. * mSubChunk2Size 位置40-44 值=原始音频数据大小
  67. */
  68. private boolean writeDataSize() {
  69. if (dos == null) {
  70. return false;
  71. }
  72. try {
  73. RandomAccessFile waveAccessFile = new RandomAccessFile(mFilePath, "rw");
  74. waveAccessFile.seek(WavFileHeader.WAV_CHUNKSIZE_OFFSET);
  75. waveAccessFile.write(intToByteArray(WavFileHeader.WAV_CHUNKSIZE_EXCLUDE_DATA + mDataSize), 0, 4);
  76. waveAccessFile.seek(WavFileHeader.WAV_SUB_CHUNKSIZE2_OFFSET);
  77. waveAccessFile.write(intToByteArray(mDataSize), 0, 4);
  78. waveAccessFile.close();
  79. } catch (FileNotFoundException e) {
  80. e.printStackTrace();
  81. return false;
  82. } catch (IOException e) {
  83. e.printStackTrace();
  84. return false;
  85. }
  86. return true;
  87. }
  88.  
  89. private boolean writeHeader(int sampleRateInHz, int channels, int bitsPerSample) {
  90. if (dos == null) {
  91. return false;
  92. }
  93.  
  94. WavFileHeader header = new WavFileHeader(sampleRateInHz, channels, bitsPerSample);
  95.  
  96. //按照wav文件结构依次写入
  97. try {
  98. dos.writeBytes(header.mChunkID);
  99. //这里不直接用writeInt的原因是它采用的大端法存储
  100. dos.write(intToByteArray(header.mChunkSize), 0, 4);
  101. dos.writeBytes(header.mFormat);
  102. dos.writeBytes(header.mSubChunk1ID);
  103. dos.write(intToByteArray(header.mSubChunk1Size), 0, 4);
  104. dos.write(shortToByteArray(header.mAudioFormat), 0, 2);
  105. dos.write(shortToByteArray(header.mNumChannel), 0, 2);
  106. dos.write(intToByteArray(header.mSampleRate), 0, 4);
  107. dos.write(intToByteArray(header.mByteRate), 0, 4);
  108. dos.write(shortToByteArray(header.mBlockAlign), 0, 2);
  109. dos.write(shortToByteArray(header.mBitsPerSample), 0, 2);
  110. dos.writeBytes(header.mSubChunk2ID);
  111. dos.write(intToByteArray(header.mSubChunk2Size), 0, 4);
  112. } catch (IOException e) {
  113. e.printStackTrace();
  114. return false;
  115. }
  116. return true;
  117. }
  118.  
  119.  
  120. private static byte[] intToByteArray(int data) {
  121. return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(data).array();
  122. }
  123.  
  124. private static byte[] shortToByteArray(short data) {
  125. return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(data).array();
  126. }
  127. }
复制

解析wav文件并播放

  1. public class WavFileReader {
  2. private static final String TAG="WavFileReader";
  3.  
  4. private DataInputStream dis;
  5. private WavFileHeader mWavFileHeader;
  6.  
  7.  
  8. public WavFileHeader getWavFileHeader(){
  9. return mWavFileHeader;
  10. }
  11.  
  12. public boolean openFile(String filePath){
  13. if(dis!=null){
  14. closeFile();
  15. }
  16. try {
  17. dis=new DataInputStream(new FileInputStream(filePath));
  18. } catch (FileNotFoundException e) {
  19. e.printStackTrace();
  20. }
  21.  
  22. return readHeader();
  23. }
  24.  
  25. public void closeFile(){
  26. if(dis!=null){
  27. try {
  28. dis.close();
  29. dis=null;
  30. } catch (IOException e) {
  31. e.printStackTrace();
  32. }
  33.  
  34. }
  35. }
  36.  
  37. public int readData(byte[] buffer, int offset, int count) {
  38. if (dis == null || mWavFileHeader == null) {
  39. return -1;
  40. }
  41.  
  42. try {
  43. int nbytes = dis.read(buffer, offset, count);
  44. if (nbytes == -1) {
  45. return 0;
  46. }
  47. return nbytes;
  48. } catch (IOException e) {
  49. e.printStackTrace();
  50. }
  51.  
  52. return -1;
  53. }
  54.  
  55.  
  56. /**
  57. *read和read(byte b[])
  58. * read每次读取一个字节,返回0-255的int字节值
  59. * read(byte b[])读取一定数量的字节,返回实际读取的字节的数量
  60. */
  61. private boolean readHeader(){
  62. if(dis==null){
  63. return false;
  64. }
  65.  
  66. WavFileHeader header=new WavFileHeader();
  67. byte[] intValue = new byte[4];
  68. byte[] shortValue = new byte[2];
  69.  
  70. try {
  71. header.mChunkID = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
  72. Log.d(TAG, "Read file chunkID:" + header.mChunkID);
  73.  
  74. dis.read(intValue);
  75. header.mChunkSize=byteArrayToInt(intValue);
  76. Log.d(TAG, "Read file chunkSize:" + header.mChunkSize);
  77.  
  78. header.mFormat = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
  79. Log.d(TAG, "Read file format:" + header.mFormat);
  80.  
  81. header.mSubChunk1ID = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
  82. Log.d(TAG, "Read fmt chunkID:" + header.mSubChunk1ID);
  83.  
  84. dis.read(intValue);
  85. header.mSubChunk1Size = byteArrayToInt(intValue);
  86. Log.d(TAG, "Read fmt chunkSize:" + header.mSubChunk1Size);
  87.  
  88. dis.read(shortValue);
  89. header.mAudioFormat = byteArrayToShort(shortValue);
  90. Log.d(TAG, "Read audioFormat:" + header.mAudioFormat);
  91.  
  92. dis.read(shortValue);
  93. header.mNumChannel = byteArrayToShort(shortValue);
  94. Log.d(TAG, "Read channel number:" + header.mNumChannel);
  95.  
  96. dis.read(intValue);
  97. header.mSampleRate = byteArrayToInt(intValue);
  98. Log.d(TAG, "Read samplerate:" + header.mSampleRate);
  99.  
  100. dis.read(intValue);
  101. header.mByteRate = byteArrayToInt(intValue);
  102. Log.d(TAG, "Read byterate:" + header.mByteRate);
  103.  
  104. dis.read(shortValue);
  105. header.mBlockAlign = byteArrayToShort(shortValue);
  106. Log.d(TAG, "Read blockalign:" + header.mBlockAlign);
  107.  
  108. dis.read(shortValue);
  109. header.mBitsPerSample = byteArrayToShort(shortValue);
  110. Log.d(TAG, "Read bitspersample:" + header.mBitsPerSample);
  111.  
  112. header.mSubChunk2ID = "" + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte() + (char) dis.readByte();
  113. Log.d(TAG, "Read data chunkID:" + header.mSubChunk2ID);
  114.  
  115. dis.read(intValue);
  116. header.mSubChunk2Size = byteArrayToInt(intValue);
  117. Log.d(TAG, "Read data chunkSize:" + header.mSubChunk2Size);
  118.  
  119. Log.d(TAG, "Read wav file success !");
  120. } catch (IOException e) {
  121. e.printStackTrace();
  122. return false;
  123. }
  124.  
  125. mWavFileHeader=header;
  126. return true;
  127. }
  128.  
  129.  
  130. private int byteArrayToInt(byte[] b){
  131. return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getInt();
  132. }
  133.  
  134. private short byteArrayToShort(byte[] b){
  135. return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getShort();
  136. }
  137. }
复制
  1. public class AudioWavActivity extends UIRootActivity {
  2.  
  3. private Button btn_audio_record;
  4. private Button btn_audio_record_play;
  5.  
  6.  
  7. private AudioCapture audioCapture;
  8. private AudioPlayer audioPlayer;
  9.  
  10. private WavFileWriter wavFileWriter;
  11. private WavFileReader wavFileReader;
  12. private boolean isReading;
  13.  
  14. private String path="";
  15.  
  16.  
  17. @Override
  18. protected int getLayoutId() {
  19. return R.layout.activity_media_audio;
  20. }
  21.  
  22. @Override
  23. protected void initTitle() {
  24. head_title.setText("wav音频文件的存储和解析");
  25. }
  26.  
  27. @Override
  28. public void initView() {
  29. btn_audio_record=findViewById(R.id.btn_audio_record);
  30. btn_audio_record_play=findViewById(R.id.btn_audio_record_play);
  31. }
  32.  
  33. @Override
  34. public void initData() {
  35. path=FileUtil.getAudioDir(this)+"/audioTest.wav";
  36. audioCapture=new AudioCapture();
  37. audioPlayer=new AudioPlayer();
  38. wavFileReader=new WavFileReader();
  39. wavFileWriter=new WavFileWriter();
  40.  
  41. String des = "录音权限被禁止,我们需要打开录音权限";
  42. String[] permissions = new String[]{Manifest.permission.RECORD_AUDIO};
  43. baseAt.requestPermissions(des, permissions, 100, new PermissionsResultListener() {
  44. @Override
  45. public void onPermissionGranted() {
  46.  
  47. }
  48. @Override
  49. public void onPermissionDenied() {
  50. finish();
  51.  
  52. }
  53. });
  54.  
  55. }
  56.  
  57. @Override
  58. public void initEvent() {
  59. btn_audio_record.setOnTouchListener(new View.OnTouchListener() {
  60. @Override
  61. public boolean onTouch(View v, MotionEvent event) {
  62. if(event.getAction()==MotionEvent.ACTION_DOWN){
  63. Log.d("TAG","按住");
  64. start();
  65. }else if(event.getAction()==MotionEvent.ACTION_UP){
  66. Log.d("TAG","松开");
  67. stop();
  68. }
  69. return false;
  70. }
  71. });
  72.  
  73. btn_audio_record_play.setOnClickListener(new View.OnClickListener() {
  74. @Override
  75. public void onClick(View v) {
  76. play();
  77.  
  78. }
  79. });
  80.  
  81. }
  82.  
  83. //播放录音
  84. private void play(){
  85. isReading=true;
  86. wavFileReader.openFile(path);
  87. audioPlayer.startPlay();
  88. new AudioTrackThread().start();
  89. }
  90.  
  91. private class AudioTrackThread extends Thread{
  92. @Override
  93. public void run() {
  94. byte[] buffer = new byte[1024];
  95. while (isReading && wavFileReader.readData(buffer,0,buffer.length)>0){
  96. audioPlayer.play(buffer,0,buffer.length);
  97. }
  98. audioPlayer.stopPlay();
  99. wavFileReader.closeFile();
  100. }
  101. }
  102.  
  103.  
  104. //开始录音
  105. private void start(){
  106. wavFileWriter.openFile(path,44100,2,16);
  107. btn_audio_record.setText("松开 结束");
  108. audioCapture.startRecord();
  109. audioCapture.setOnAudioFrameCaptureListener(new AudioCapture.onAudioFrameCaptureListener() {
  110. @Override
  111. public void onAudioFrameCapture(byte[] audioData) {
  112. wavFileWriter.writeData(audioData,0,audioData.length);
  113. }
  114. });
  115. }
  116.  
  117. //结束录音
  118. private void stop(){
  119. btn_audio_record.setText("按住 录音");
  120. audioCapture.stopRecord();
  121. wavFileWriter.closeFile();
  122. }
  123. }
复制

摘抄

1.《音视频学习系列第(二)篇—音频采集和播放

相关文章

暂无评论

none
暂无评论...