2019独角兽企业重金招聘Python工程师标准>>>

无论是文字、图像还是声音,都必须以一定的格式来组织和存储起来,这样播放器才知道以怎样的方式去解析这一段数据,例如,对于原始的图像数据,我们常见的格式有 YUV、Bitmap,而对于音频来说,最简单常见的格式就是 wav 格式了。

wav 格式,与 bitmap 一样,都是微软开发的一种文件格式规范,它们都有一个相似之处,就是整个文件分为两部分,第一部分是“文件头”,记录重要的参数信息,对于音频而言,就包括:采样率、通道数、位宽等等,对于图像而言,就包括:图像的宽高、色彩位数等等;第二部分是“数据块”,即一帧一帧的二进制数据,对于音频而言,就是原始的 PCM 数据;对于图像而言,就是 RGB 数据。

前面几篇文章讲了如何利用 Android 平台的 API 完成原始音频信号的采集和播放,而本文则重点关注如何在 Android 平台上,将采集到的 PCM 音频数据保存到 wav 文件,同时,也介绍如何读取和解析 wav 文件。

而文章最后,我还会给出一段 AudioDemo 程序,该程序将最近的几篇文章涉及到的代码综合起来了,演示了一个完整的 Android 音频从采集到播放的全过程。

下面言归正传,讲讲如何读写 wav 文件格式。

1. 文件头

首先,我们了解一下 wav 格式的“文件头”,可以参考这篇文章:《WAVE PCM soundfile format》

我们可以简单地分析一下这个 wav 格式头,它主要分为三个部分:

第一部分,属于最“顶层”的信息块,通过“ChunkID”来表示这是一个 “RIFF”格式的文件,通过“Format”填入“WAVE”来标识这是一个 wav 文件。而“ChunkSize”则记录了整个 wav 文件的字节数。

第二部分,属于“fmt”信息块,主要记录了本 wav 音频文件的详细音频参数信息,例如:通道数、采样率、位宽等等(含义请参考我的第一篇文章《Android音频开发(1):基础知识》)

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

分析到这里,我想大家应该就明白了,其实,做一种多媒体格式的解析,也不是一件特别复杂的事,说白了,格式就是一种规范,告诉你,我的二进制数据是怎么存储的,你应该按照什么样的方式来解析。

具体而言,我们可以定义一个如下的 Java 类来抽象和描述 wav 文件头:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

/*

 *  COPYRIGHT NOTICE  

 *  Copyright (C) 2016, Jhuster <lujun.hust@gmail.com>

 *  https://github.com/Jhuster/AudioDemo

 *   

 *  @license under the Apache License, Version 2.0 

 *

 *  @file    WavFileHeader.java

 *  

 *  @version 1.0     

 *  @author  Jhuster

 *  @date    2016/03/19

 */

package com.jhuster.audiodemo.api;

public class WavFileHeader {    

   

    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 bitsPerSample, int channels) {          

        mSampleRate = sampleRateInHz;

        mBitsPerSample = (short)bitsPerSample;

        mNumChannel = (short)channels;                

        mByteRate = mSampleRate*mNumChannel*mBitsPerSample/8;

        mBlockAlign = (short)(mNumChannel*mBitsPerSample/8);

    }

}

具体每一个字段的含义,可以参考我上面给出的链接,下面我们再看看如何读写 wav 文件。

2. 读写 wav 文件

文章开头已经说过,其实说白了,wav 文件就是一段“文件头”+“音频二进制数据”,因此:

(1)写 wav 文件,其实就是先写入一个 wav 文件头,然后再继续写入音频二进制数据即可

(2)读 wav 文件,其实也就是先读一个 wav 文件头,然后再继续读出音频二进制数据即可

那么,在动手写代码之前,有两点你需要搞清楚:

(1) wav 文件头中,有哪些是“变化的”,哪些是“不变的”?

比如:文件头开头的“RIFF”字符串就是“不变的”部分,而用来记录音频数据总长度的“Subchunk2Size”变量就是属于“变化的”部分,因为,再音频数据没有彻底全部写完之前,你是无法知道一共写入了多少字节的音频数据的,因此,这个部分,需要用一个变量记录起来,到全部写完之后,再使用 Java 的“RandomAccessFile”类,将文件指针跳转到“Subchunk2Size”字段,改写一下默认值即可。

(2) 如何把 int、short 变量与 byte[] 的转换

因为 wav 文件都是二进制的方式读写,因此,“WavFileHeader”类中定义的变量都需要转换为byte字节流,具体转换方法如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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();

}

关于 wav 文件读写的类我已经帮大家“封装”好了,并且结合着前面几篇文章给出的音频采集和播放的代码,完成了一个 AudioDemo 程序,放在我的 Github 上了,欢迎大家下载运行测试,然后结合着代码具体学习 Android 音频相关技术,代码地址:

https://github.com/Jhuster/AudioDemo

注:本系列文章的所有代码,以后都会并入到该 demo 项目中。

3. 小结

免费学习更多精品课程,登录乐搏学院官网http://h.learnbo.cn/

或关注我们的官方微博微信,还有更多惊喜哦~

本文出自 “Jhuster的专栏” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/1750593

转载于:https://my.oschina.net/learnbo/blog/761182

Android音频开发(4):如何存储和解析wav文件相关推荐

  1. Android音频开发(五)如何存储和解析最简单的音频wav文件

    我们大家都知道,无论是文字.图像还是声音,都必须以一定的格式来组织和存储起来,然后其它的软件再以相同的协议规则,相应的格式才能去打开解析这一段数据,例如,对于原始的图像数据,我们常见的格式有 YUV. ...

  2. Android 音频开发(一) 基础入门篇

    今天主要讲解下Android音频开发的入门知识,希望对想入门却不知如何下手的朋友有所帮助,同时希望能得到高手的指点和帮助. 深入细化基础技能知识点 大致细化如下10个知识点. 音频开发的主要应用有哪些 ...

  3. Android应用开发:数据存储和界面展现-1

    1. 相对布局RelativeLayout 特点:相对布局所有组件可以叠加在一起:各个组件的布局是独立的,互不影响:所有组件的默认位置都是在左上角(顶部.左部对齐) 属性 功能描述 android:l ...

  4. 一次搞懂 Android 音频开发

    在接触Android音频开发后,陆陆续续的看了不少的文章,如果说查缺补漏把这些文章梳理清楚,然后逐个整合,那么确实也能完整的推导出音频开发需要掌握的技术.但是对于初学者来说,可能在开发中产生很多障碍以 ...

  5. Android音频开发(3):如何播放一帧音频

    本文重点关注如何在Android平台上播放一帧音频数据.阅读本文之前,建议先读一下<Android音频开发(1):基础知识>,因为音频开发过程中,经常要涉及到这些基础知识,掌握了这些重要的 ...

  6. Android音频开发(六)音频编解码之初识MediaCodec上

    前五篇我们介绍了关于音频的基础知识,今天来讲述一下音频的重点和难点部分,这一节依然是分上下俩个节点,深入讲述Android音频开发的编解码技术. 1.MediaCodec 介绍 MediaCodeC是 ...

  7. Android 音频开发(四) 如何播放一帧音频数据下

    再看这一篇文章前,如果你是小白,我建议你先看一下Android 音频开发(一) 基础入门篇这一篇.今天继续讲解如何通过Android SDK自带API实现播放一帧音频数据. 我们都知道,Android ...

  8. Android 音频开发(二) 采集一帧音频数据

    这一节主要介绍如何采集一帧音频数据,如果你对音频的基础概念比较陌生,建议看我的上一篇Android 音频开发(一) 基础入门篇.因为音频开发过程中,经常要涉及到这些基础知识,掌握了这些重要的基础知识后 ...

  9. Android音频开发(2):如何采集一帧音频

    本文重点关注如何在Android平台上采集一帧音频数据.阅读本文之前,建议先读一下我的上一篇文章<Android音频开发(1):基础知识>,因为音频开发过程中,经常要涉及到这些基础知识,掌 ...

最新文章

  1. 跨还是不跨?这是一个问题
  2. 软件工程-东北师大站-第九次作业(PSP)
  3. 接收端收到数据包以后的处理过程
  4. MPEG(mpeg1,mpeg2,mpeg4) 与H264 QP值间 关系
  5. 选课_ctsc1997_ssl1606_树形dp
  6. python目录和文件的基本操作_python学习笔记(七)——文件和目录操作
  7. 驰骋工作流引擎-自由表单
  8. 电商网站交易记录设计
  9. 在 Delphi 下使用 DirectSound (1): 枚举播放设备
  10. RE-Base64编码分析
  11. SkeyeLive开源流媒体同屏直播软件源码功能框架解析
  12. 将加密的pdf转化成word
  13. MIMO系统模型构建
  14. 台式计算机如何定时关机,台式电脑设置定时关机
  15. 【Unity3D】制作进度条——让Image同时具有Filled和Sliced的功能
  16. 【仿美团点餐App】—— 首页(一)
  17. 微信小程序 关于下载文件、打开文件预览文件(wx.downloadFile和wx.openDocument)
  18. 前端常用面试题2020
  19. Android 文件系统目录分析(手机系统目录分析)
  20. js点击空白关闭div

热门文章

  1. GDCM:创建ex-nihilo图像,创建“派生”图像的测试程序
  2. VTK:小部件之ContourWidget
  3. VTK:可视化算法之CutWithCutFunction
  4. VTK:图片之ImageDilateErode3D
  5. Qt Creator创建UI
  6. OpenGL多光源Multiple lights
  7. C++bead sort珠排序的实现算法(附完整源码)
  8. QT的QDBusPendingReply类的使用
  9. C++头文件引入的一种小问题
  10. docker 返回错误信息与本地不一致_Docker基本配置及常用命令