废话

权限、权限、权限,必须要先获取了录音权限,其他的事情晚点再说。

另外,新版本的Android 10系统会对录音有调整,引入了一个录音焦点的概念,也就是说以前的麦克风只能一个APP使用,必须要等它断开了别人才能用,现在换成可以抢的形式,也就是如果没有音焦,代码有可能不会报错,但是是录不进声音的。

Android系统API提供的录音方式就两种:MediaRecorder、AudioRecord

MediaRecorder:简易模式,调用简单,只有开始、结束,录音之后的文件也是指定编码格式,系统播放器可以直接播放。

AudioRecord:原始模式,可以暂停、继续,可以实时获取到录音录制的数据,然后进行一些骚操作,然后录出来的东西是最原始的pcm数据,系统播放器不能直接播放。

MediaRecorder

话不多说,直接上代码,具体用法,直接将需要保存文件的路径通过构造方法传进去,然后调用开始和结束方法即可:


import android.media.MediaRecorder;
import android.os.Handler;import java.io.File;
import java.io.IOException;/*** 录音功能*/public class MediaRecordingUtils {//文件路径private String filePath;private MediaRecorder mMediaRecorder;private final String TAG = "fan";public static final int MAX_LENGTH = 1000 * 60 * 200;// 最大录音时长,单位毫秒,1000*60*10;private OnAudioStatusUpdateListener audioStatusUpdateListener;/*** 文件存储默认sdcard/record*/public MediaRecordingUtils() {}public MediaRecordingUtils(String filePath) {this.filePath=filePath;
//        File path = new File(filePath);
//        if (!path.exists())
//            path.mkdirs();
//        this.FolderPath = filePath;}private long startTime;private long endTime;/*** 开始录音 使用aac格式* 录音文件** @return*/public void startRecord() {// 开始录音/* ①Initial:实例化MediaRecorder对象 */if (mMediaRecorder == null)mMediaRecorder = new MediaRecorder();try {/* ②setAudioSource/setVedioSource */mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风/* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);/** ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式* ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)*/mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//            filePath = FolderPath + DateUtil.getTimeForLong() + ".aac";/* ③准备 */mMediaRecorder.setOutputFile(filePath);mMediaRecorder.setMaxDuration(MAX_LENGTH);mMediaRecorder.prepare();/* ④开始 */mMediaRecorder.start();// AudioRecord audioRecord./* 获取开始时间* */startTime = System.currentTimeMillis();updateMicStatus();ALog.e("fan", "startTime" + startTime);} catch (IllegalStateException e) {ALog.e(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());} catch (IOException e) {ALog.e(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());}}/*** 停止录音*/public long stopRecord() {if (mMediaRecorder == null)return 0L;endTime = System.currentTimeMillis();//有一些网友反应在5.0以上在调用stop的时候会报错,翻阅了一下谷歌文档发现上面确实写的有可能会报错的情况,捕获异常清理一下就行了,感谢大家反馈!try {mMediaRecorder.stop();mMediaRecorder.reset();mMediaRecorder.release();mMediaRecorder = null;audioStatusUpdateListener.onStop(filePath);filePath = "";} catch (RuntimeException e) {try {mMediaRecorder.reset();mMediaRecorder.release();mMediaRecorder = null;File file = new File(filePath);if (file.exists())file.delete();filePath = "";} catch (Exception e1) {}}return endTime - startTime;}/*** 取消录音*/public void cancelRecord() {try {mMediaRecorder.stop();mMediaRecorder.reset();mMediaRecorder.release();mMediaRecorder = null;} catch (RuntimeException e) {mMediaRecorder.reset();mMediaRecorder.release();mMediaRecorder = null;}File file = new File(filePath);if (file.exists())file.delete();filePath = "";}private final Handler mHandler = new Handler();private Runnable mUpdateMicStatusTimer = new Runnable() {public void run() {updateMicStatus();}};private int BASE = 1;private int SPACE = 100;// 间隔取样时间public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {this.audioStatusUpdateListener = audioStatusUpdateListener;}/*** 更新麦克状态*/private void updateMicStatus() {if (mMediaRecorder != null) {double ratio = (double) mMediaRecorder.getMaxAmplitude() / BASE;double db = 0;// 分贝if (ratio > 1) {db = 20 * Math.log10(ratio);if (null != audioStatusUpdateListener) {audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime);}}mHandler.postDelayed(mUpdateMicStatusTimer, SPACE);}}public String getFilePath() {return filePath;}public interface OnAudioStatusUpdateListener {/*** 录音中...** @param db   当前声音分贝* @param time 录音时长*/public void onUpdate(double db, long time);/*** 停止录音** @param filePath 保存路径*/public void onStop(String filePath);}}

AudioRecord


/*** 录音* 用法:1-init,filePath文件的后缀为.pcm 2-start  3-stop* stop之后,所有的音频数据会以pcm的格式写入到filePath这个文件内,并且是末尾添加的方式,而非覆盖(以达到暂停录音继续录音的效果),需要转换为其他格式才能让系统播放器直接播放*/
public class AudioRecordingUtils {//指定音频源 这个和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_IN_STEREO; //立体声//指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。//因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。private static final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;//指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。private AudioRecord audioRecord = null;  // 声明 AudioRecord 对象private int recordBufSize = 0; // 声明recoordBufffer的大小字段private boolean isRecording = false;private String saveFilePath;//    private FileOutputStream os = null;private File mRecordingFile;private OnAudioRecordingListener onAudioRecordingListener;public void init(String filePath, OnAudioRecordingListener onAudioRecordingListener) {this.onAudioRecordingListener = onAudioRecordingListener;saveFilePath = filePath;recordBufSize = AudioRecord.getMinBufferSize(mSampleRateInHz, mChannelConfig, mAudioFormat);//计算最小缓冲区audioRecord = new AudioRecord(mAudioSource, mSampleRateInHz, mChannelConfig,mAudioFormat, recordBufSize);//创建AudioRecorder对象//创建一个流,存放从AudioRecord读取的数据mRecordingFile = new File(saveFilePath);if (mRecordingFile.exists()) {//音频文件保存过了删除mRecordingFile.delete();}try {mRecordingFile.createNewFile();//创建新文件} catch (IOException e) {e.printStackTrace();ALog.e("lu", "创建储存音频文件出错");}}public static double bytes2Double(byte[] arr) {long value = 0;for (int i = 0; i < 8; i++) {value |= ((long) (arr[i] & 0xff)) << (8 * i);}return Double.longBitsToDouble(value);}public void startRecording() {//判断AudioRecord的状态是否初始化完毕//在AudioRecord对象构造完毕之后,就处于AudioRecord.STATE_INITIALIZED状态了。if (audioRecord == null || audioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {ALog.e("尚未初始化完成");return;}XyObservable.addTask(new XyCallBack() {//开一个子线程的意思private double volume = 0;@Overridepublic void run() {//标记为开始采集状态isRecording = true;try {//获取到文件的数据流DataOutputStream mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile, true)));byte[] buffer = new byte[recordBufSize];audioRecord.startRecording();//开始录音//getRecordingState获取当前AudioReroding是否正在采集数据的状态while (isRecording && audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {int bufferReadResult = audioRecord.read(buffer, 0, recordBufSize);for (int i = 0; i < bufferReadResult; i++) {mDataOutputStream.write(buffer[i]);}setFinish();//这里会调到下面的finish()方法,finish()方法处于UI线程中}mDataOutputStream.close();} catch (Throwable t) {ALog.e("lu", "Recording Failed");stopRecording();}}@Overridepublic void finish() {if (onAudioRecordingListener != null) {onAudioRecordingListener.onChange(volume);}}});}/*** 暂停录音*/public void pauseRecording() {isRecording = false;if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {audioRecord.stop();}}//停止录音public void stopRecording() {isRecording = false;//停止录音,回收AudioRecord对象,释放内存if (audioRecord != null) {if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {audioRecord.stop();}if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {audioRecord.release();}}}public interface OnAudioRecordingListener {public void onChange(double volume);}
}

然后再附带一个将原始pcm转换为wav格式的方法:


public class Pcm2WavUtils {/*** PCM文件转WAV文件** @param inPcmFilePath  输入PCM文件路径* @param outWavFilePath 输出WAV文件路径* @param sampleRate     采样率,例如44100* @param channels       声道数 单声道:1或双声道:2* @param bitNum         采样位数,8或16*/public void convertPcm2Wav(String inPcmFilePath, String outWavFilePath, int sampleRate,int channels, int bitNum) {FileInputStream in = null;FileOutputStream out = null;byte[] data = new byte[1024];try {//采样字节byte率long byteRate = sampleRate * channels * bitNum / 8;in = new FileInputStream(inPcmFilePath);out = new FileOutputStream(outWavFilePath);//PCM文件大小long totalAudioLen = in.getChannel().size();//总大小,由于不包括RIFF和WAV,所以是44 - 8 = 36,在加上PCM文件大小long totalDataLen = totalAudioLen + 36;writeWaveFileHeader(out, totalAudioLen, totalDataLen, sampleRate, channels, byteRate);int length = 0;while ((length = in.read(data)) > 0) {out.write(data, 0, length);}} catch (Exception e) {e.printStackTrace();} finally {if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}if (out != null) {try {out.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 输出WAV文件** @param out           WAV输出文件流* @param totalAudioLen 整个音频PCM数据大小* @param totalDataLen  整个数据大小* @param sampleRate    采样率* @param channels      声道数* @param byteRate      采样字节byte率* @throws IOException*/private void writeWaveFileHeader(FileOutputStream out, long totalAudioLen,long totalDataLen, int sampleRate, int channels, long byteRate) throws IOException {byte[] header = new byte[44];header[0] = 'R'; // RIFFheader[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);header[8] = 'W';//WAVEheader[9] = 'A';header[10] = 'V';header[11] = 'E';//FMT Chunkheader[12] = 'f'; // 'fmt 'header[13] = 'm';header[14] = 't';header[15] = ' ';//过渡字节//数据大小header[16] = 16; // 4 bytes: size of 'fmt ' chunkheader[17] = 0;header[18] = 0;header[19] = 0;//编码方式 10H为PCM编码格式header[20] = 1; // format = 1header[21] = 0;//通道数header[22] = (byte) channels;header[23] = 0;//采样率,每个通道的播放速度header[24] = (byte) (sampleRate & 0xff);header[25] = (byte) ((sampleRate >> 8) & 0xff);header[26] = (byte) ((sampleRate >> 16) & 0xff);header[27] = (byte) ((sampleRate >> 24) & 0xff);//音频数据传送速率,采样率*通道数*采样深度/8header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);header[30] = (byte) ((byteRate >> 16) & 0xff);header[31] = (byte) ((byteRate >> 24) & 0xff);// 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数header[32] = (byte) (channels * 16 / 8);header[33] = 0;//每个样本的数据位数header[34] = 16;header[35] = 0;//Data chunkheader[36] = 'd';//dataheader[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);}
}

Android 调用系统api录音的两种方式(MediaRecorder、AudioRecord)相关推荐

  1. python rest api_Python调用REST API接口的几种方式汇总

    相信做过自动化运维的同学都用过REST API接口来完成某些动作.API是一套成熟系统所必需的接口,可以被其他系统或脚本来调用,这也是自动化运维的必修课. 本文主要介绍python中调用REST AP ...

  2. Py之qrcode:调用python的qrcode库两种方式生成二维码、带logo的二维码

    Py之qrcode:调用python的qrcode库两种方式生成二维码.带logo的二维码 目录 python编程实现生成二维码 1.第一种方式-纯文本 2.第二种方式-带logo

  3. 服务器主机GHOST装系统,大神请绕道!科普电脑装系统最基本的两种方式,萌新千万不要错过...

    原标题:大神请绕道!科普电脑装系统最基本的两种方式,萌新千万不要错过 Hello大家好,我是兼容机之家的小牛! 电脑装系统用GHOST好,还是原装版本好?这是很多小伙伴疑惑的问题,那么让我们来具体分析 ...

  4. java native函数库_Java 层调用 Native 层函数的两种方式

    概述 Java 层如何调用Native层函数,大家都应该知道使用JNI(Java 本地接口). 通过在java层声明native方法,然后遵守JNI规范命名Native函数,即可建立Java层nati ...

  5. Android 实现图片的高斯模糊(两种方式)

    做项目时遇到要做图片模糊的需求,和微信语音通话的背景效果一样.网上有很多不错的博客,记录一下使用的两种方式. 1. 先来看看第一种,博客原文 首先创建一个工具类 /*** 快速模糊化工具*/ publ ...

  6. Android加载GIF图片的两种方式

    飞哥语录:得到一件东西最好的方式是让自己配得上它. 方式一:使用第三开源框架直接在布局文件中加载gif 1.在工程的build.gradle中添加如下 buildscript {repositorie ...

  7. Android中模拟点击的两种方式

    目前这两种方式,后面有的再继续添加. 1.当我们知道View的时候可以采用 view. performClick()方法 是使用代码主动去调用控件的点击事件(模拟人手去触摸控件) 2.点击也就是相当于 ...

  8. C语言调用so动态库的两种方式

    方式1:类似静态库的调用(使用头文件) 这种方式生成的程序会在启动时候就加载so动态库. add.h int add(int x, int y); add.c #include "add.h ...

  9. Android之发送短信的两种方式

    SMS涉及的主要类SmsManager 实现SMS主要用到SmsManager类,该类继承自java.lang.Object类,下面我们介绍一下该类的主要成员. 公有方法: ArrayList< ...

最新文章

  1. 我国数字出版发展尚存三大难题
  2. php中引入shiro,基于shiro的自定义注解的扩展-图文详解
  3. 深入理解PHP的运行模式
  4. 测试常用——linux 基础命令
  5. zlib-1.2.11手册
  6. linux 7 network fail,CentOs7 网卡出现问题Job for network.service failed
  7. 马云将于阿里巴巴功成身退?
  8. Node.js入门(含NVM、NPM、NVM的安装)-(转载)
  9. 工具篇:Navicat-12版本破解安装
  10. JavaNIO读取文件
  11. 每次离开总是 装做轻松的样子 微笑着说回去吧 转身泪湿眼底
  12. 数据分析基础-假设检验原理详解
  13. Django 链接数据库错误 Strick Mode 解决
  14. 微信小程序校园论坛系统丨安卓也可以用
  15. Kotlin - 改良构建者模式
  16. 笔记本计算机排名2015,2015性价比高的笔记本电脑排行榜
  17. 转:Ubuntu常用命令速查
  18. 【数字经济】概念、技术、实践:中国 500强泰康集团背后的数据驱动
  19. 【怀旧】80后,难忘童年----100部最经典的电视连续剧
  20. 压缩图片大小(根据比例,或直接给定宽高)

热门文章

  1. 【python】python制作 连连看 游戏脚本(三)
  2. datagridview固定列宽_[C#] winform中的DataGridView的列宽设置(自动调整列宽)
  3. 女生玩游戏专属神器,华为平板M5青釉版高颜值、强性能
  4. 获取Spring IOC容器对象:
  5. sql中的列变行、行变列操作
  6. windows10:GTX GeForce 1070+更新nvidia显卡驱动+CUDA+CUDNN+tensorflow_gpu深度学习GPU环境搭建(史上排雷最多版本)
  7. 关于JavaWeb图书商城答辩问题总结
  8. 实习日记07/08 day1
  9. 教程向|3D建模之面部雕刻,详细教程带给大家(上)
  10. 创意小游戏《蛇它虫》团队专访:玩法画面均是新意