https://www.jianshu.com/p/125b94af7c08

音频采集(AudioRecorder)

安仔夏天勤奋

2018.05.08 21:50* 字数 1426 阅读 2134评论 0喜欢 2

音频采集

想更好地了解音频采集,首先要去了解一些音频入门基础知识。关于一些音频开发的一些基础知识,这里就不一一讲解了,可以去了解Android音频技术开发的一些基础知识

Android音频采集(捕获)

android平台上的音频采集一般就两种方式:

  1. 使用MediaRecorder进行音频采集。

MediaRecorder 是基于 AudioRecorder 的 API(最终还是会创建AudioRecord用来与AudioFlinger进行交互) ,它可以直接将采集到的音频数据转化为执行的编码格式,并保存。这种方案相较于调用系统内置的用用程序,便于开发者在UI界面上布局,而且系统封装的很好,便于使用,唯一的缺点是使用它录下来的音频是经过编码的,没有办法的得到原始的音频。同时MediaRecorder即可用于音频的捕获也可以用于视频的捕获相当的强大。实际开发中没有特殊需求的话,用的是比较多的!

  1. 使用AudioRecord进行音频采集。

AudioRecord 是一个比较偏底层的API,它可以获取到一帧帧PCM数据,之后可以对这些数据进行处理。AudioRecord这种方式采集最为灵活,使开发者最大限度的处理采集的音频,同时它捕获到的音频是原始音频PCM格式的!像做变声处理的需要就必须要用它收集音频。

在实际开发中,它也是最常用来采集音频的手段。如直播技术采用的就是AudioRecorder采集音频数据。AudioRecorder采集也是本文介绍的重点。

AudioRecord的基本参数

  • audioResource:音频采集的来源
  • audioSampleRate:音频采样率
  • channelConfig:声道
  • audioFormat:音频采样精度,指定采样的数据的格式和每次采样的大小。
  • bufferSizeInBytes:AudioRecord 采集到的音频数据所存放的缓冲区大小。
  • 获取最小的缓冲区大小,用于存放AudioRecord采集到的音频数据。
//指定音频源 这个和MediaRecorder是相同的 MediaRecorder.AudioSource.MIC指的是麦克风private static final int mAudioSource = MediaRecorder.AudioSource.MIC;//指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)private static final int mSampleRateInHz=44100 ;//指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量private static final int mChannelConfig= AudioFormat.CHANNEL_CONFIGURATION_MONO; //单声道//指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。//因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。private static final int mAudioFormat=AudioFormat.ENCODING_PCM_16BIT;//指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。private int mBufferSizeInBytes= AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig, mAudioFormat);//计算最小缓冲区//创建AudioRecord。AudioRecord类实际上不会保存捕获的音频,因此需要手动创建文件并保存下载。 private AudioRecord mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,mSampleRateInHz,mChannelConfig,mAudioFormat, mBufferSizeInBytes);//创建AudioRecorder对象

以上的基本参数对于基本的音频采集已经够用了,但参数还有很多就不一一说明了,对于参数值的设置也要看项目设计而设置。我们了解了基本参数,那就来看看AudioRecord的一些状态。

AudioRecord的状态用意

AudioRecord的状态对于我们做业务逻辑时会起到很好的作用。下面说来说说一些常用的状态。

  • 获取AudioRecord的状态

用于检测AudioRecord是否确保了获得适当的硬件资源。在AudioRecord对象实例化之后调用getState()。

STATE_INITIALIZED 初始完毕

STATE_UNINITIALIZED 未初始化

  • 开始采集时调用mAudioRecord.startRecording()开始录音。

开始采集之后,状态自动变为RECORDSTATE_RECORDING。

  • 停止采集时调用mAudioRecord.stop()停止录音。

停止采集之后,状态自动变为 RECORDSTATE_STOPPED 。

  • 读取录制内容,将采集到的数据读取到缓冲区,方法调用的返回值的状态码。

情况异常:

  1. ERROR_INVALID_OPERATION if the object wasn't properly initialized。
  2. ERROR_BAD_VALUE if the parameters don't resolve to valid data and indexes。

情况正常:the number of bytes that were read。

AudioRecord音频采集的基本流程

  • 构造一个 AudioRecord 对象。
  • 开始采集。
  • 读取采集的数据。
  • 停止采集。

构造一个 AudioRecord 对象

mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,mSampleRateInHz,mChannelConfig,
mAudioFormat, mBufferSizeInBytes);//创建AudioRecorder对象

获取 getMinBufferSize 值

mBufferSizeInBytes 是 AudioRecord 采集到的音频数据所存放的缓冲区大小。

mBufferSizeInBytes = AudioRecord.getMinBufferSize(audioSampleRate, channelConfig, audioFormat);

特别注意:这个大小不能随便设置,AudioRecord 提供对应的 API 来获取这个值。

通过 mBufferSizeInBytes 返回就可以知道传入给AudioRecord.getMinBufferSize的参数是否支持当前的硬件设备。

if (AudioRecord.ERROR_BAD_VALUE == mBufferSizeInBytes || AudioRecord.ERROR == mBufferSizeInBytes) {throw new RuntimeException("Unable to getMinBufferSize");}//bufferSizeInBytes is available...//或者检测AudioRecord是否确保了获得适当的硬件资源。if (mAudioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {throw new RuntimeException("The AudioRecord is not uninitialized");}

开始采集

我们在开始录音之前,首先要判断一下AudioRecord的状态是否已经初始化完毕了,然后再去调用audioRecord.startRecording()。

//判断AudioRecord的状态是否初始化完毕//在AudioRecord对象构造完毕之后,就处于AudioRecord.STATE_INITIALIZED状态了。if (mAudioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {throw new RuntimeException("The AudioRecord is not uninitialized");}

读取采集的数据

由于AudioRecord 在采集数据时会将数据存放到缓冲区中,因此我们只需要创建一个数据流去从缓冲区中将采集的数据读取出来即可。

创建一个数据流,一边从 AudioRecord 中读取音频数据到缓冲区,一边将缓冲区 中数据写入到数据流。
因为需要使用IO操作,因此读取数据的过程应该在子线程中执行。

@Overridepublic void run() {//标记为开始采集状态isRecording = true;//创建一个流,存放从AudioRecord读取的数据mRecordingFile = new File(mFileRoot,mFileName);if(mRecordingFile.exists()){//音频文件保存过了删除mRecordingFile.delete();}try {mRecordingFile.createNewFile();//创建新文件} catch (IOException e) {e.printStackTrace();Log.e("lu","创建储存音频文件出错");}try {//获取到文件的数据流mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile)));byte[] buffer = new byte[mBufferSizeInBytes];mAudioRecord.startRecording();//开始录音//getRecordingState获取当前AudioReroding是否正在采集数据的状态while (isRecording && mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {int bufferReadResult = mAudioRecord.read(buffer,0,mBufferSizeInBytes);for (int i = 0; i < bufferReadResult; i++){mDataOutputStream.write(buffer[i]);}}mDataOutputStream.close();} catch (Throwable t) {Log.e("lu", "Recording Failed");stopRecord();}}

停止采集

//停止录音public void stopRecord() {isRecording = false;//停止录音,回收AudioRecord对象,释放内存if (mAudioRecord != null) {if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {mAudioRecord.stop();}if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {mAudioRecord.release();}}}

注意添加权限

有时候代码写完了就行动了,很容易忽略在AndroidManifest.xml中添加权限。

<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

总结

  • 直播技术采用的就是AudioRecorder采集音频数据。
  • mBufferSizeInBytes这个大小不能随便设置,AudioRecord 提供对应的 API 来获取这个值。
  • 一些AudioRecord状态判断是否支持当前的硬件设备。
  • 注意AndroidManifest.xml中添加权限。
  • AudioRecord保存的音频文件为.pcm格式,不能直接播放。它是一个原始的文件,没有任何播放格式,因此就无法被播放器识别并播放。

播放PCM文件的方案

  • 使用 AudioTrack 播放PCM格式的音频数据。
  • 将PCM数据转化为wav格式的数据,这样就可以被播放器识别。

贴出AudioRecord源代码,写得不好的,请大家多多指正。

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;public class AudioRecordActivity extends AppCompatActivity implements Runnable {private Button mBtnStartRecord,mBtnStopRecord;//指定音频源 这个和MediaRecorder是相同的 MediaRecorder.AudioSource.MIC指的是麦克风private static final int mAudioSource = MediaRecorder.AudioSource.MIC;//指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)private static final int mSampleRateInHz=44100 ;//指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量private static final int mChannelConfig= AudioFormat.CHANNEL_CONFIGURATION_MONO; //单声道//指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。//因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。private static final int mAudioFormat=AudioFormat.ENCODING_PCM_16BIT;//指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。private int mBufferSizeInBytes;private File mRecordingFile;//储存AudioRecord录下来的文件private boolean isRecording = false; //true表示正在录音private AudioRecord mAudioRecord=null;private File mFileRoot=null;//文件目录//存放的目录路径名称private static final String mPathName = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/AudiioRecordFile";//保存的音频文件名private static final String mFileName = "audiorecordtest.pcm";//缓冲区中数据写入到数据,因为需要使用IO操作,因此读取数据的过程应该在子线程中执行。private Thread mThread;private DataOutputStream mDataOutputStream;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_audio_record);initDatas();initUI();}//初始化数据private void initDatas() {mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig, mAudioFormat);//计算最小缓冲区mAudioRecord = new AudioRecord(mAudioSource,mSampleRateInHz,mChannelConfig,mAudioFormat, mBufferSizeInBytes);//创建AudioRecorder对象mFileRoot = new File(mPathName);if(!mFileRoot.exists())mFileRoot.mkdirs();//创建文件夹}//初始化UIprivate void initUI() {mBtnStartRecord = findViewById(R.id.btn_start_record);mBtnStopRecord = findViewById(R.id.btn_stop_record);mBtnStartRecord.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startRecord();}});mBtnStopRecord.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {stopRecord();}});}//开始录音public void startRecord() {//AudioRecord.getMinBufferSize的参数是否支持当前的硬件设备if (AudioRecord.ERROR_BAD_VALUE == mBufferSizeInBytes || AudioRecord.ERROR == mBufferSizeInBytes) {throw new RuntimeException("Unable to getMinBufferSize");}else{destroyThread();isRecording = true;if(mThread == null){mThread = new Thread(this);mThread.start();//开启线程}}}/*** 销毁线程方法*/private void destroyThread() {try {isRecording = false;if (null != mThread && Thread.State.RUNNABLE == mThread.getState()) {try {Thread.sleep(500);mThread.interrupt();} catch (Exception e) {mThread = null;}}mThread = null;} catch (Exception e) {e.printStackTrace();} finally {mThread = null;}}//停止录音public void stopRecord() {isRecording = false;//停止录音,回收AudioRecord对象,释放内存if (mAudioRecord != null) {if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {//初始化成功mAudioRecord.stop();}if (mAudioRecord  !=null ) {mAudioRecord.release();}}}@Overridepublic void run() {//标记为开始采集状态isRecording = true;//创建一个流,存放从AudioRecord读取的数据mRecordingFile = new File(mFileRoot,mFileName);if(mRecordingFile.exists()){//音频文件保存过了删除mRecordingFile.delete();}try {mRecordingFile.createNewFile();//创建新文件} catch (IOException e) {e.printStackTrace();Log.e("lu","创建储存音频文件出错");}try {//获取到文件的数据流mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile)));byte[] buffer = new byte[mBufferSizeInBytes];//判断AudioRecord未初始化,停止录音的时候释放了,状态就为STATE_UNINITIALIZEDif(mAudioRecord.getState() == mAudioRecord.STATE_UNINITIALIZED){initDatas();}mAudioRecord.startRecording();//开始录音//getRecordingState获取当前AudioReroding是否正在采集数据的状态while (isRecording && mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {int bufferReadResult = mAudioRecord.read(buffer,0,mBufferSizeInBytes);for (int i = 0; i < bufferReadResult; i++){mDataOutputStream.write(buffer[i]);}}mDataOutputStream.close();} catch (Throwable t) {Log.e("lu", "Recording Failed");stopRecord();}}@Overrideprotected void onDestroy() {super.onDestroy();destroyThread();stopRecord();}
}

小礼物走一走,来简书关注我

音频采集(AudioRecorder)相关推荐

  1. android触屏音文件地址,Android音视频-音频采集

    Android的音视频开发是我暂定的一个职业发展的一个方向,通过自学记录一些记了又忘记的知识. 音频基础知识 采样率(samplerate) 蓝色代表模拟音频信号,红色的点代表采样得到的量化数值. 采 ...

  2. Android多个音频源采集,Android音频开发之音频采集的实现示例

    在 Android 系统中,一般使用 AudioRecord 或者 MediaRecord 来采集音频. AudioRecord 是一个比较偏底层的API,它可以获取到一帧帧 PCM 数据,之后可以对 ...

  3. Android 音频采集(原始音频)

    Android 音频简介 常见的音频编解码的类型:AAC  OPUS MP3  AMR  Ogg  PCM AAC: 高级音频编码  对应  .m4a(audio/m4a)或者.3pg(audio/3 ...

  4. android下音频采集功能,音频采集:Android基于AudioRecord的实现

    前言 这篇文章简单详情下手机端Android系统下利使用AudioRecord进行音频采集方法. 开始前先提供一份源码 AudioRecordLib . AudioRecord采集的核心实现在于 Au ...

  5. Windows上的音频采集技术

    在制作发布端的时候,需要采集到声卡的输出信号,以便与麦克风的输入信号进行混音,对于音频处理的技术,主要有如下几种: 采集麦克风输入 采集声卡输出 将音频数据送入声卡进行播放 对多路音频输入进行混音处理 ...

  6. android 录音原始文件_音频采集:Android基于AudioRecord的实现

    前言 这篇文章简单介绍下移动端Android系统下利用AudioRecord进行音频采集方法. 按照惯例开始前先提供一份源码 AudioRecordLib . AudioRecord采集的核心实现在于 ...

  7. Linux下Qt使用QAudio相关类进行音频采集,使用Windows下的Matlab软件播放

    Qt集成的QAudio相关类可以很方便的进行音频采集,主要涉及到以下几个类: #include <QAudioInput> #include <QAudioDeviceInfo> ...

  8. voip 音频采集时间_树莓派3 音频配置及其应用场景(录音、VoIP 电话等)(锁定重发)...

    从网上看到一本关于树莓派的电子书 Raspberry Pi for Secret Agents,利用树莓派打造"特工装备". 其中有一章讲到音频设备的配置和几个相关的应用场景(比如 ...

  9. java语言实现视频音频采集_详解js的视频和音频采集

    今天要写的,不是大家平时会用到的东西.因为兼容性实在不行,只是为了说明下前端原来还能干这些事. 大家能想象前端是能将摄像头和麦克风的视频流和音频流提取出来,再为所欲为的么.或者说我想把我canvas画 ...

最新文章

  1. google breakpad native crash分析工具
  2. careercup-高等难度 18.5
  3. Java中的异常 Exception
  4. Introduction to Financial Management
  5. python单词反转_python文本 字符串逐字符反转以及逐单词反转
  6. GitHub使用指南——如何删除存储库
  7. python 从大到小循环_python算法(3) 插入排序
  8. android系统性能优化---(9)Android 绿色应用公约
  9. 六年级计算机应用计划,六年级信息工作计划
  10. Java 9 ← 2017,2019 → Java 13 ,Java 两年来都经历了什么?| CSDN 博文精选
  11. 亲测可用小米刷旧版开发版固件,刷入华硕、潘多拉固件
  12. wpsa4排版_WPS2000如何快速排版
  13. lisp 标注螺纹孔_cad中螺纹孔怎么标注
  14. Java技术体系简介
  15. java打印两个小人_Swing多线程实现奔跑的小人动画代码实现 | 彬菌
  16. 博客园申请理由写这1句话,居然12分钟就能开通成功!
  17. 爬取分析拉勾网招聘信息
  18. Nebula Graph 招募社区布道师
  19. 《分布式JAVA应用 基础与实践》 第六章 构建高可用的系统
  20. 开机出现“Disk I/O error”的故障解决

热门文章

  1. maven项目的groupId和artifactId什么意思?
  2. 七层协议功能及对应协议
  3. python getattr_python中getattr()的用法
  4. linux基础命令netstat,Linux netstat命令详解与常用选项
  5. Suse11sp3系统安装asm+oracle11G+rac
  6. 详解箭头函数和普通函数的区别以及箭头函数的注意事项、不适用场景
  7. 服务器无线桥接,服务器无线桥接设置方法
  8. 打家劫舍DP-算法题
  9. 服务器重启后没有启动网卡
  10. [转]虚拟机安装详细图解教程及使用教程