关于 AudioRecord

Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压缩(如AMR、MP3等)并存成文件,而后者则更接近底层,能够更加自由灵活地控制,可以得到原始的一帧帧PCM音频数据。

如果想简单地做一个录音机,录制成音频文件,则推荐使用 MediaRecorder,而如果需要对音频做进一步的算法处理、或者采用第三方的编码库进行压缩、以及网络传输等应用,则建议使用 AudioRecord,其实 MediaRecorder 底层也是调用了 AudioRecord 与 Android Framework 层的 AudioFlinger 进行交互的。

音频的开发,更广泛地应用不仅仅局限于本地录音,因此,我们需要重点掌握如何利用更加底层的 AudioRecord API 来采集音频数据(注意,使用它采集到的音频数据是原始的PCM格式,想压缩为mp3,aac等格式的话,还需要专门调用编码器进行编码)。

AudioRecord 的参数配置

  • audioSource:音频采集的输入源

    可选的值以常量的形式定义在 MediaRecorder.AudioSource 类中,常用的值包括:

    • 1. DEFAULT (默认)
    • 2. VOICE_RECOGNITION (用于语音识别,等同于 DEFAULT )
    • 3. MIC (由手机麦克风输入)
    • 4. VOICE_COMMUNICATION (用于 VoIP 应用)等等
  • sampleRateInHz: 采样率

    目前 44100Hz 是唯一可以保证兼容所有 Android 手机的采样率。

  • channelConfig: 通道数

    可选的值以常量的形式定义在 AudioFormat 类中,常用的是

    • 1. CHANNEL_IN_MONO (单通道)
    • 2. CHANNEL_IN_STEREO(双通道)
  • audioFormat: 数据位宽

    可选的值以常量的形式定义在 AudioFormat 类中,常用的是( 1 是可以保证兼容所有Android手机的)

    • 1. ENCODING_PCM_16BIT(16bit)
    • 2. ENCODING_PCM_8BIT(8bit)
  • bufferSizeInBytes:AudioRecord 内部的音频缓冲区的大小

    该缓冲区的值不能低于一帧“音频帧”(Frame)的大小:

    int size = 采样率 x 位宽 x 采样时间 x 通道数

    采样时间一般取 2.5ms~120ms 之间,由厂商或者具体的应用决定,我们其实可以推断,每一帧的采样时间取得越短,产生的延时就应该会越小,当然,碎片化的数据也就会越多。

    在Android开发中,AudioRecord 类提供了一个帮助你确定这个 bufferSizeInBytes 的函数,原型如下:

    int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);

    不同的厂商的底层实现是不一样的,但无外乎就是根据上面的计算公式得到一帧的大小,音频缓冲区的大小则必须是一帧大小的2~N倍。实际开发中,强烈建议由该函数计算出需要传入的 bufferSizeInBytes,而不是自己手动计算。

流程

  • 设置所有 AudioRecord 参数

        // 音频输入-麦克风private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;// 采样频率 一般共分为 22.05KHz、44.1KHz、48KHz 三个等级// 44100 是目前的标准,但是某些设备仍然支持 22050,16000,11025private final static int AUDIO_SAMPLE_RATE = 44100;// 声道 单声道private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO;// 编码private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;// 缓冲区字节大小private int bufferSizeInBytes = 0;// 录音对象private AudioRecord audioRecord;
    
  • 创建 AudioRecord 并且获取缓冲区字节大小

        /*** 创建默认的录音对象** @param fileName 文件名*/public void createDefaultAudio(String fileName) {// 获得缓冲区字节大小,AudioRecord 能接受的最小的 buffer 大小bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING);// 创建默认的录音对象并且设置一系列配置audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING, bufferSizeInBytes);this.fileName = fileName;}
    
  • 创建 buffer (用于保存新的声音数据),设置 buffer 大小(录制声音数据容量大小)

        // new 一个 byte 数组用来存一些字节数据,大小为缓冲区大小byte[] audiodata = new byte[bufferSizeInBytes];
    
  • 开始录音

    audioRecord.startRecording();
    // 标志位, 用于控制停止数据流读写循环
    isRecording = true;
    
  • 开始采集,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流

        // 开个子线程将录音的数据放入pcm文件new Thread(new Runnable() {@Overridepublic void run() {writeDataTOFile(listener);}}).start();// 如何将音频写入文件是重点,我写个伪代码,说明这个代码运行顺序// 首先创建 pcm 文件,得到他的 FileOutputStream,然后不断循环 AudioRecord 通过 read 将录音的数据放入字节数组里,// 当录音结束的时候要记得停止这个循环FileOutputStream fos = null;int readsize = 0;try {File file = new File(currentFileName);if (file.exists()) {file.delete();}// 建立一个可存取字节的文件fos = new FileOutputStream(file);} catch (IllegalStateException e) {Log.e("AudioRecorder", e.getMessage());throw new IllegalStateException(e.getMessage());} catch (FileNotFoundException e) {Log.e("AudioRecorder", e.getMessage());}while (isRecording) {readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) {try {fos.write(audiodata);} catch (IOException e) {Log.e("AudioRecorder", e.getMessage());}}}
    
  • 关闭数据流

    修改标志位:isRecording 为false,上面的while循环就自动停止了,数据流也就停止流动了,Stream也就被关闭了。

    isRecording = false;
    
  • 停止录音

    只要AudioRecord.stop就可以暂停录音了,然后当继续录音时在AudioRecord.start就好了,但是要另创建一个pcm记录,当录音结束时我们要将这些pcm一起转换为一个wav,

    至于pcm转换为wav的代码是固定的
    停止录音之后,注意要释放资源。

    if (null != audioRecord) {audioRecord.stop();audioRecord.release();audioRecord = null;recordingThread = null;
    }
    

流程走向

  • buffer 从 AudioRecord 中拉取声音数据
可分多次读取声音数据
通过 read 方法拉取声音数据
音频硬件
AudioRecord
buffer
  • 创建一个数据流,开启一个线程,一边从 AudioRecord 中读取数据,一边将数据导入到数据流中
读取大小等于 buffer 容量的声音数据
导入
AudioRecord
buffer
数据流

参考文献

  • Android音频开发(2):如何采集一帧音频
  • Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件
  • Android 音视频深入

Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件(学习笔记)相关推荐

  1. android pcm文件大小_Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件...

    一.AudioRecord API详解 AudioRecord是Android系统提供的用于实现录音的功能类. 要想了解这个类的具体的说明和用法,我们可以去看一下官方的文档: AndioRecord类 ...

  2. Android 音视频开发(一) -- 使用AudioRecord 录制PCM(录音);AudioTrack播放音频

    前言,音视频这块,确实比较难入门,本着学习的态度,我这边也跟着 Android 音视频开发入门指南 打怪升级,留下个脚印,大家共勉. 音视频 系列文章 Android 音视频开发(一) – 使用Aud ...

  3. Android 音视频开发(二) -- Camera1 实现预览、拍照功能

    音视频 系列文章 Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍 ...

  4. Android音视频开发(二)SurfaceView

    简介 官方API文档介绍:SurfaceView是View的子类,它内嵌了一个专门用于绘制的Surface,你可以控制这个Surface的格式和尺寸,Surfaceview控制这个Surface的绘制 ...

  5. Android 音视频开发学习思路

    Android 音视频开发这块目前的确没有比较系统的教程或者书籍,网上的博客文章也都是比较零散的.只能通过一点点的学习和积累把这块的知识串联积累起来. 初级入门篇: Android 音视频开发(一) ...

  6. android音频开发6,Android 音视频开发(一) : 通过三种方式绘制图片

    想要逐步入门音视频开发,就需要一步步的去学习整理,并积累.本文是音视频开发积累的第一篇. 对应的要学习的内容是:在 Android 平台绘制一张图片,使用至少 3 种不同的 API,ImageView ...

  7. Android 音视频开发(三) -- Camera2 实现预览、拍照功能

    音视频 系列文章 Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍 ...

  8. Android 音视频开发(六) -- Android Mediaprojection 截屏和录屏

    Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍照功能 Andro ...

  9. Android音视频开发基础(七):视频采集-系统API基础

    前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了<Android 音视频从入门到提高 - 任务列表>.本文是Android音视 ...

最新文章

  1. ORA-01031: insufficient privileges的解决方法
  2. 你的神经网络不起作用的37个理由
  3. P5431 【模板】乘法逆元2(小学数学题,毒瘤鱼,卡常之王yyds)
  4. 【每日提高之声明式事物】spring声明式事务 同一类内方法调用事务失效
  5. Linux centosVMware shell中的函数、shell中的数组、
  6. SAP SF打印次数统计
  7. 通信 / HTTP Host 请求头的作用
  8. 介绍一些平时用得到的服务/组件
  9. CF590E-Birthday【AC自动机,最大独立集】
  10. 因HTTP的Header长度过长导致下载文件名出现乱码的问题
  11. 47 SD配置-销售凭证设置-激活项目类别的定价
  12. 安川伺服调试软件_2020南昌三菱伺服回收广东收购价
  13. win7c盘空间越来越小_电脑一分钟小技巧:如何更改电脑桌面路径?
  14. python一般的基础代码-Python入门经典练习题
  15. mysql自定义序号_mysql序列号生成器 mysql自定义函数生成序列号的例子
  16. 百度地图API详解之公交路线规划
  17. 7张图让你看懂互联网营销思维与传统思维本质区别
  18. WebShell --冰蝎
  19. c 当前程序的语言,c语言实现获取macos当前的系统语言
  20. 车托帮APP的顽固残留清除

热门文章

  1. c语言kbhit函数6,C语言中kbhit()函数怎么复位
  2. 【小米手环7】使用 Zeus + 表盘自定义工具 为小米手环7开发和安装小程序
  3. 计算机access分数比例,[access查询]关于分数段统计问题
  4. 伦敦 quant_伦敦统一用户组8
  5. ArcMAP实现矢量数据平移
  6. ANTLR4(二) Vistor Listener
  7. 模拟外线O口呼入时,看不到来电显示号码?
  8. 《Python 数据科学实践指南》读书笔记
  9. 【懒懒的Python学习笔记一】
  10. 哥谭第一季/全集Gotham迅雷下载