关于安卓录音的实现,原生安卓给我们提供了两个实现方法,一个是MediaRecorder,另外一个则是AudioRecorder,两个区别可以理解为MediaRecorder更加便捷,实现起来更加方便,api更加高级,但生成的数据是已经处理好的。而AudioRecorder,实现起来比MediaRecorder繁琐一点,生成的原始数据为pcm数据,为无损数据。
下面介绍一下AudioRecorder的用法:
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes)
第一个参数,就是音频源,一般设置为麦克风。
第二个参数,就是频率,一般都是44100KHZ,该频率为普遍设备都适用,其次,还有22050KHZ,16000KHZ,11025KHZ,但这三种设备不能保证设备通用性,下图为源码解析:
第三个参数,就是音频参数,一般可以是单声道或者立体声。
第四个参数,就是音频的格式,可以是8位或者是16位,一般更高的位数,保存的细节将会越多
第五个参数,就是指定缓存区大小。
所有参数初始化如下图:
然后调用构造方法,即可创建一个AudioRecorder对象。
(一)录音:
调用AudioRecorder的startRecording()方法开始录音。
调用方法后,使用AudioRecorder的Read()方法进行音频数据的读取,获取到这些字节数组后,我们即可进行后续的操作。
注意,获取数据方法需要在循环中不断获取,核心代码如下图(录制音频并且保存到本地):
通过相关标识控制录制,若要退出,则把该标识取反即可退出。
(二)停止:
调用AudioRecorder的stop()方法停止录音。
(三)释放:
调用AudioRecorder的release()释放录音。
备注:记得io操作或者录制完成以后,释放相关的变量避免造成内存泄漏。
示例代码:
package com.north.light.libaudio.voicerecorder
import android.content.Context
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import com.north.light.libaudio.thread.AudioHandlerManager
import java.io.BufferedOutputStream
import java.io.DataOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.CopyOnWriteArrayList
/**
* Created by lzt
* time 2021/6/8 16:51
*
* @author lizhengting
* 描述:音频录取manager
*/
class VoiceRecordManager : VoiceRecordManagerApi {
//指定音频源 这个和MediaRecorder是相同的 MediaRecorder.AudioSource.MIC指的是麦克风
private val mAudioSource = MediaRecorder.AudioSource.MIC
//指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
private val mSampleRateInHz =44100
//指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量
private val mChannelConfig: Int = AudioFormat.CHANNEL_IN_STEREO //立体声
//指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。
//因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。
private val mAudioFormat: Int = AudioFormat.ENCODING_PCM_16BIT
//指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。
private val mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz, mChannelConfig, mAudioFormat)//计算最小缓冲区
//全局context
private var mContext: Context? =null
//监听
private val mListener = CopyOnWriteArrayList()
//录音对象标识集合
private var mAudioRecordMap: ConcurrentMap = ConcurrentHashMap()
private var mAudioCancelMap: ConcurrentMap = ConcurrentHashMap()
private var mAudioTimeMap: ConcurrentMap = ConcurrentHashMap()
private var mRecordMap: ConcurrentMap = ConcurrentHashMap()
override fun init(context: Context?) {
mContext = context?.applicationContext
}
@Synchronized
override fun startRecord(path: String, fileName: String, duration: Long) {
if (getAudioRecorder(path + fileName)?.state == AudioRecord.STATE_UNINITIALIZED) {
mListener.forEach {
it.noInit()
}
return
}
//已经初始化了,开始录音
AudioHandlerManager.getMessageExecutor().execute(Runnable {
try {
mListener.forEach {
it.startRecord(path, fileName)
}
mAudioRecordMap[path + fileName] =true
mAudioCancelMap[path + fileName] =false
mAudioTimeMap[path + fileName] = System.currentTimeMillis() + duration
var mRecordFile = File(path, fileName)
if (mRecordFile.exists()) {
mRecordFile.delete()
}
File(path).mkdirs()
mRecordFile.createNewFile()
var mDataOutputStream = DataOutputStream(BufferedOutputStream(FileOutputStream(mRecordFile)))
var buffer = ByteArray(mBufferSizeInBytes)
//开始录音
getAudioRecorder(path + fileName)?.startRecording()
//getRecordingState获取当前AudioReroding是否正在采集数据的状态
while (mAudioRecordMap[path + fileName] ==true &&
(mAudioTimeMap[path + fileName] ?:0) > System.currentTimeMillis() &&
getAudioRecorder(path + fileName)?.recordingState == AudioRecord.RECORDSTATE_RECORDING) {
var bufferReadResult = getAudioRecorder(path + fileName)?.read(buffer, 0, mBufferSizeInBytes)
if (bufferReadResult == -1) {
bufferReadResult =0
}
mDataOutputStream.write(buffer, 0, bufferReadResult ?:0)
}
mDataOutputStream.close()
if (mAudioCancelMap[path + fileName] ==true) {
mListener.forEach {
it.cancel(path, fileName)
}
}else {
mListener.forEach {
it.result(path + fileName)
}
mListener.forEach {
it.finish(path, fileName)
}
}
stopRecord(path, fileName)
}catch (e: Exception) {
mListener.forEach {
it.error(e.message ?:"")
}
stopRecord(path, fileName)
}
})
}
/**
* 停止录音
* */
override fun stopRecord(path: String, fileName: String) {
mAudioRecordMap.remove(path + fileName)
releaseAudioRecorder(path + fileName)
}
/**
* 取消录音
* */
override fun cancelRecord(path: String, fileName: String) {
mAudioCancelMap[path + fileName] =true
stopRecord(path, fileName)
}
override fun release() {
mListener.clear()
//释放所有录音对象
for (keyin mRecordMap.entries) {
releaseAudioRecorder(key.key)
}
}
override fun setOnRecordListener(listener: VoiceRecordListener?) {
if (listener ==null)return
mListener.add(listener)
}
override fun removeOnRecordListener(listener: VoiceRecordListener?) {
if (listener ==null)return
mListener.remove(listener)
}
/**
* 获取audio recorder对象
* */
private fun getAudioRecorder(key: String): AudioRecord? {
if (mRecordMap[key] ==null) {
mRecordMap[key] = AudioRecord(mAudioSource, mSampleRateInHz, mChannelConfig,
mAudioFormat, mBufferSizeInBytes)
}
return mRecordMap[key]
}
/**
* 释放audio recorder 对象
* */
private fun releaseAudioRecorder(key: String) {
if (mRecordMap[key] !=null) {
var recordObj =mRecordMap[key]
try {
if (recordObj?.recordingState == AudioRecord.RECORDSTATE_RECORDING) {
recordObj.stop()
}
}catch (e: Exception) {
}
try {
if (recordObj?.state == AudioRecord.STATE_INITIALIZED) {
recordObj.release()
}
}catch (e: Exception) {
}
mRecordMap.remove(key)
}
}
}