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

您所在的位置:网站首页 安卓手机的麦克风权限怎么设置 Android 调用系统api录音的两种方式(MediaRecorder、AudioRecord)

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

2024-07-09 09:36| 来源: 网络整理| 查看: 265

废话

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

另外,新版本的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)) 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'; // RIFF 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); header[8] = 'W';//WAVE header[9] = 'A'; header[10] = 'V'; header[11] = 'E'; //FMT Chunk header[12] = 'f'; // 'fmt ' header[13] = 'm'; header[14] = 't'; header[15] = ' ';//过渡字节 //数据大小 header[16] = 16; // 4 bytes: size of 'fmt ' chunk header[17] = 0; header[18] = 0; header[19] = 0; //编码方式 10H为PCM编码格式 header[20] = 1; // format = 1 header[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); //音频数据传送速率,采样率*通道数*采样深度/8 header[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 chunk header[36] = 'd';//data 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); } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3