前言

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

实现流程

  1. 获取权限
  2. 初始化获取每一帧流的Size
  3. 初始化音频录制AudioRecord
  4. 开始录制与保存录制音频文件
  5. 停止录制
  6. 给音频文件添加头部信息,并且转换格式成wav
  7. 释放AudioRecord,录制流程完毕

获取权限

    <!--音频录制权限 --><uses-permission android:name="android.permission.RECORD_AUDIO" /><!--读取和写入存储权限--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

如果是Android5.0以上,以上3个权限需要动态授权

初始化获取每一帧流的Size

private Integer mRecordBufferSize;
private void initMinBufferSize(){//获取每一帧的字节流大小mRecordBufferSize = AudioRecord.getMinBufferSize(8000 , AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);}
 

第一个参数sampleRateInHz 采样率(赫兹),方法注释里有说明

只能在4000到192000的范围内取值

在AudioFormat类里
public static final int SAMPLE_RATE_HZ_MIN = 4000; 最小4000
public static final int SAMPLE_RATE_HZ_MAX = 192000; 最大192000

第二个参数channelConfig 声道配置 描述音频声道的配置,例如左声道/右声道/前声道/后声道。

在AudioFormat类录
public static final int CHANNEL_IN_LEFT = 0x4;//左声道
public static final int CHANNEL_IN_RIGHT = 0x8;//右声道
public static final int CHANNEL_IN_FRONT = 0x10;//前声道
public static final int CHANNEL_IN_BACK = 0x20;//后声道
public static final int CHANNEL_IN_LEFT_PROCESSED = 0x40;
public static final int CHANNEL_IN_RIGHT_PROCESSED = 0x80;
public static final int CHANNEL_IN_FRONT_PROCESSED = 0x100;
public static final int CHANNEL_IN_BACK_PROCESSED = 0x200;
public static final int CHANNEL_IN_PRESSURE = 0x400;
public static final int CHANNEL_IN_X_AXIS = 0x800;
public static final int CHANNEL_IN_Y_AXIS = 0x1000;
public static final int CHANNEL_IN_Z_AXIS = 0x2000;
public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;
public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;
public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;//单声道
public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);//立体声道(左右声道)

第三个参数audioFormat 音频格式 表示音频数据的格式。

注意!一般的手机设备可能只支持 16位PCM编码,如果其他的都会报错为坏值.

public static final int ENCODING_PCM_16BIT = 2; //16位PCM编码
public static final int ENCODING_PCM_8BIT = 3; //8位PCM编码
public static final int ENCODING_PCM_FLOAT = 4; //4位PCM编码
public static final int ENCODING_AC3 = 5;
public static final int ENCODING_E_AC3 = 6;
public static final int ENCODING_DTS = 7;
public static final int ENCODING_DTS_HD = 8;
public static final int ENCODING_MP3 = 9; //MP3编码 此格式可能会因为不设备不支持报错
public static final int ENCODING_AAC_LC = 10;
public static final int ENCODING_AAC_HE_V1 = 11;
public static final int ENCODING_AAC_HE_V2 = 12;

初始化音频录制AudioRecord

private AudioRecord mAudioRecord;
private void initAudioRecord(){mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mRecordBufferSize);}
  • 第一个参数audioSource 音频源   这里选择使用麦克风:MediaRecorder.AudioSource.MIC
  • 第二个参数sampleRateInHz 采样率(赫兹)  与前面初始化获取每一帧流的Size保持一致
  • 第三个参数channelConfig 声道配置 描述音频声道的配置,例如左声道/右声道/前声道/后声道。   与前面初始化获取每一帧流的Size保持一致
  • 第四个参数audioFormat 音频格式  表示音频数据的格式。  与前面初始化获取每一帧流的Size保持一致
  • 第五个参数缓存区大小,就是上面我们配置的AudioRecord.getMinBufferSize

开始录制与保存录制音频文件

private boolean mWhetherRecord;
private File pcmFile;
private void startRecord(){pcmFile = new File(AudioRecordActivity.this.getExternalCacheDir().getPath(),"audioRecord.pcm");mWhetherRecord = true;new Thread(new Runnable() {@Overridepublic void run() {mAudioRecord.startRecording();//开始录制FileOutputStream fileOutputStream = null;try {fileOutputStream = new FileOutputStream(pcmFile);byte[] bytes = new byte[mRecordBufferSize];while (mWhetherRecord){mAudioRecord.read(bytes, 0, bytes.length);//读取流fileOutputStream.write(bytes);fileOutputStream.flush();}Log.e(TAG, "run: 暂停录制" );mAudioRecord.stop();//停止录制fileOutputStream.flush();fileOutputStream.close();addHeadData();//添加音频头部信息并且转成wav格式} catch (FileNotFoundException e) {e.printStackTrace();mAudioRecord.stop();} catch (IOException e) {e.printStackTrace();}}}).start();}

这里说明一下为什么用布尔值,来关闭录制.有些小伙伴会发现AudioRecord是可以获取到录制状态的.那么肯定有人会用状态来判断while是否还需要处理流.这种是错误的做法.因为MIC属于硬件层任何硬件的东西都是异步的而且会有很大的延时.所以回调的状态也是有延时的,有时候流没了,但是状态还是显示为正在录制.

停止录制

就是调用mAudioRecord.stop();方法来停止录制,但是因为我在上面的保存流后做了调用停止视频录制,所以我这里只需要切换布尔值就可以关闭音频录制

private void stopRecord(){mWhetherRecord = false;}

给音频文件添加头部信息,并且转换格式成wav

音频录制完成后,这个时候去存储目录找到音频文件部分,会提示无法播放文件.其实是因为没有加入音频头部信息.一般通过麦克风采集的录音数据都是PCM格式的,即不包含头部信息,播放器无法知道音频采样率、位宽等参数,导致无法播放,显然是非常不方便的。pcm转换成wav,我们只需要在pcm的文件起始位置加上至少44个字节的WAV头信息即可。

偏移地址   命名       内容

00-03   ChunkId       "RIFF"

04-07   ChunkSize      下个地址开始到文件尾的总字节数(此Chunk的数据大小)

08-11   fccType       "WAVE"

12-15   SubChunkId1       "fmt ",最后一位空格。

16-19   SubChunkSize1    一般为16,表示fmt Chunk的数据块大小为16字节

20-21   FormatTag      1:表示是PCM 编码

22-23   Channels         声道数,单声道为1,双声道为2

24-27   SamplesPerSec      采样率

28-31   BytesPerSec     码率 :采样率 * 采样位数 * 声道个数,bytePerSecond = sampleRate * (bitsPerSample / 8) * channels

32-33   BlockAlign       每次采样的大小:位宽*声道数/8

34-35   BitsPerSample     位宽

36-39   SubChunkId2     "data"

40-43   SubChunkSize2      音频数据的长度

44-...   data         音频数据

private void addHeadData(){pcmFile = new File(AudioRecordActivity.this.getExternalCacheDir().getPath(),"audioRecord.pcm");handlerWavFile = new File(AudioRecordActivity.this.getExternalCacheDir().getPath(),"audioRecord_handler.wav");PcmToWavUtil pcmToWavUtil = new PcmToWavUtil(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);pcmToWavUtil.pcmToWav(pcmFile.toString(),handlerWavFile.toString());}

写入头部信息的工具类

注意输入File和输出File不能同一个,因为没有做缓存.

public class PcmToWavUtil {private static final String TAG = "PcmToWavUtil";/*** 缓存的音频大小*/private int mBufferSize;/*** 采样率*/private int mSampleRate;/*** 声道数*/private int mChannel;/*** @param sampleRate sample rate、采样率* @param channel channel、声道* @param encoding Audio data format、音频格式*/PcmToWavUtil(int sampleRate, int channel, int encoding) {this.mSampleRate = sampleRate;this.mChannel = channel;this.mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, encoding);}/*** pcm文件转wav文件** @param inFilename 源文件路径* @param outFilename 目标文件路径*/public void pcmToWav(String inFilename, String outFilename) {FileInputStream in;FileOutputStream out;long totalAudioLen;//总录音长度long totalDataLen;//总数据长度long longSampleRate = mSampleRate;int channels = mChannel == AudioFormat.CHANNEL_IN_MONO ? 1 : 2;long byteRate = 16 * mSampleRate * channels / 8;byte[] data = new byte[mBufferSize];try {in = new FileInputStream(inFilename);out = new FileOutputStream(outFilename);totalAudioLen = in.getChannel().size();totalDataLen = totalAudioLen + 36;writeWaveFileHeader(out, totalAudioLen, totalDataLen,longSampleRate, channels, byteRate);while (in.read(data) != -1) {out.write(data);out.flush();}Log.e(TAG, "pcmToWav: 停止处理");in.close();out.close();} catch (IOException e) {e.printStackTrace();}}/*** 加入wav文件头*/private void writeWaveFileHeader(FileOutputStream out, long totalAudioLen,long totalDataLen, long longSampleRate, int channels, long byteRate)throws IOException {byte[] header = new byte[44];// RIFF/WAVE headerheader[0] = 'R';header[1] = 'I';header[2] = 'F';header[3] = 'F';header[4] = (byte) (totalDataLen & 0xff);header[5] = (byte) ((totalDataLen >> 8) & 0xff);header[6] = (byte) ((totalDataLen >> 16) & 0xff);header[7] = (byte) ((totalDataLen >> 24) & 0xff);//WAVEheader[8] = 'W';header[9] = 'A';header[10] = 'V';header[11] = 'E';// 'fmt ' chunkheader[12] = 'f';header[13] = 'm';header[14] = 't';header[15] = ' ';// 4 bytes: size of 'fmt ' chunkheader[16] = 16;header[17] = 0;header[18] = 0;header[19] = 0;// format = 1header[20] = 1;header[21] = 0;header[22] = (byte) channels;header[23] = 0;header[24] = (byte) (longSampleRate & 0xff);header[25] = (byte) ((longSampleRate >> 8) & 0xff);header[26] = (byte) ((longSampleRate >> 16) & 0xff);header[27] = (byte) ((longSampleRate >> 24) & 0xff);header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);header[30] = (byte) ((byteRate >> 16) & 0xff);header[31] = (byte) ((byteRate >> 24) & 0xff);// block alignheader[32] = (byte) (2 * 16 / 8);header[33] = 0;// bits per sampleheader[34] = 16;header[35] = 0;//dataheader[36] = 'd';header[37] = 'a';header[38] = 't';header[39] = 'a';header[40] = (byte) (totalAudioLen & 0xff);header[41] = (byte) ((totalAudioLen >> 8) & 0xff);header[42] = (byte) ((totalAudioLen >> 16) & 0xff);header[43] = (byte) ((totalAudioLen >> 24) & 0xff);out.write(header, 0, 44);}
}

释放AudioRecord,录制流程完毕

调用release()方法释放资源

mAudioRecord.release();

最后你就可以在指定目录下找到音频文件播放了

最后介绍下其他API

获取AudioRecord初始化状态

public int getState() {return mState;
}

注意!这里是初始化状态,不是录制状态,它只会返回2个状态

  • AudioRecord#STATE_INITIALIZED    //已经初始化
  • AudioRecord#STATE_UNINITIALIZED  //没有初始化

获取AudioRecord录制状态

public int getRecordingState() {synchronized (mRecordingStateLock) {return mRecordingState;}}
 

返回录制状态,它只返回2个状态

  • AudioRecord#RECORDSTATE_STOPPED    //停止录制
  • AudioRecord#RECORDSTATE_RECORDING    //正在录制

Android 开发 AudioRecord音频录制相关推荐

  1. audiorecord怎么释放_Android 开发 AudioRecord音频录制

    前言 Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压 ...

  2. Android音视频开发之音频录制和播放

    Android音视频开发之音频录制和播放 1.封装音频录制工具类: public class RecorderAudioManagerUtils {private static volatile Re ...

  3. android AudioRecord 音频录制 噪音消除

    android AudioRecord 音频录制 噪音消除  因为公司APP做适配,一些低端机的噪音比较严重,所以再一些低端机上做了简单除噪音功能, 1,由于APP使用场景的限制,所以一般噪音基本上都 ...

  4. Android开发音效中录制WAV音频和录制MP3音频的讲解及实战(超详细 附源码)

    需要源码请点赞关注收藏后评论区留下QQ~~~ 一.录制WAV音频 无论是MediaRecoredr录制的AMR和AAC音频,还是AudioRecord录制的PCM音频,都不能在计算机上直接播放,因为它 ...

  5. android开发之音频拼接

    第一种情况:不同压缩格式音频拼接,不同的压缩格式拼接需要解码为采样数据然后拼接,然后再编码为统一的压缩格式. 方法一:FFmpeg命令拼接,ffmpeg -I 'concat:0.mp3|1.wav| ...

  6. android录屏音频录制

    android9和9以下,音源使用MediaRecorder.AudioSource.DEFAULT 1.外放 只有手机麦克风声音,麦克风声音可以听到比较大的游戏声音,堵住麦克风孔就听不见游戏声音了 ...

  7. android开发,静音录制视频,在一般清晰度的前提下保证文件大小越小越好

    public void startRecord() {mediarecorder = new MediaRecorder();// 创建mediarecorder对象mCamera = getCame ...

  8. Android多媒体功能开发(11)——使用AudioRecord类录制音频

    AudioRecord类优点是能录制到缓冲区,能够实现边录边播(AudioRecord + AudioTrack)以及对音频的实时处理(如QQ电话).缺点是输出是PCM格式的原始采集数据,如果直接保存 ...

  9. Android音视频开发:MediaRecorder录制音频

    Android 多媒体框架针对音频录制提供了两种方法:MediaRecorder和AudioRecord. 区别 MediaRecorder:录制的音频文件是经过压缩后的,需要设置编码器,并且录制的音 ...

最新文章

  1. 【组队学习】【23期】Datawhale集成学习(上)
  2. 视频演示:高颜值免费在线绘图ImageGP
  3. reduce_sum() got an unexpected keyword argument 'keep_dims'
  4. leetcode算法题--爬楼梯
  5. 值得阅读的C语言开源项目代码
  6. php函数计算加法,JavaScript_javascript实现一个数值加法函数,废话不多说,直接奉上代码 JS - phpStudy...
  7. ActiveMQ点对点消息通信demo
  8. 使用string.Format需要注意的一个性能问题
  9. ExtJs2.0学习系列(12)--Ext.TreePanel之第一式
  10. 现男友代言!最强自拍手机荣耀20S正式发布:售价1899元起
  11. hibernate中标识字段不能保存
  12. 负载均衡策略_高负荷小区负载均衡策略建议
  13. 脚本之家python专题_一个简单的python读写文件脚本
  14. Java定时自动锁屏小程序_小程序 番茄时钟如何实现锁屏持续运行功能
  15. Verilog编程网站学习——门电路、组合电路、时序电路
  16. java gmail邮箱_Java - 谷歌邮箱发送邮件详解
  17. 电影票(水题 杭电排位赛-6)
  18. c语言1GB转成B,2018职称计算机考试WPS_Office精选习题9
  19. Easymock十分钟入门
  20. 非对称加密和对称加密的优缺点

热门文章

  1. 微软IT如何规划和构建的企业私有云基础设施
  2. java类编来那个初始化顺序_Java类及对象的初始化顺序
  3. Unity3D中场景烘培步骤分享
  4. Linux 下 screenfetch 安装
  5. 1学习react 第一章创建react项目
  6. 【分享】beta技术沙龙:大型网站的Lucene应用
  7. Educational Codeforces Round 95 (Rated for Div. 2)D. Trash Problem(权值线段树+离散化)
  8. webpack中处理css文件
  9. python基础教程Day04
  10. 春节停车难?用Python找空车位