简介
AudioRecord是安卓多媒体框架中用于录制音频的工具。它支持录制原始音频数据,即PCM数据,PCM数据不能被播放器直接播放,需要编码压缩成常见音频格式才能被播放器识别。而原生api也提供了AudioTrack播放PCM数据。
谷歌Api文档
录音流程
AudioRecord是通过read方式不断读取来自音源输入的数据流(字节流),进而把数据流保存成PCM数据。
开始录音的时候,AudioRecord需要创建一个缓冲区, 这个缓冲区主要是用来保存新的音频数据,它用于标识一个AudioRecord对象还没有被读取(同步)声音数据前能录多长的音(即一次可以录制的声音容量)。声音数据不断从音频硬件中被读出,每次读取的数据大小不超过初始化缓冲区的容量(录音数据的大小)。
流程如下:
- 构造一个AudioRecord对象。其中最小录音数据缓存的缓冲区大小可以通过getMinBufferSize方法得到,如果缓冲区容量过小,将导致对象构造的失败。
- 初始化一个缓冲区,该缓冲区大小大于等于AudioRecord对象用于写声音数据的缓冲区大小,用于缓存读取的音频数据。
- startRecording开始录音
- 创建一个数据流,不断地从AudioRecord中读取声音数据到初始化的缓冲区,然后将缓冲区中的数据输出。
- 关闭数据流
- 停止录音
示例
下面使用Kotlin代码展示AudioRecord如何录制音频数据:
class AudioActivity : AppCompatActivity() {
//音频录制
private var audioRecord: AudioRecord? = null
//缓冲区大小,缓冲区用于保存音频数据流
private var bufferSize: Int = 0
//记录是否正在录制音频
@Volatile private var isRecording = false
//录音线程
private var recordThread: Thread? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_media)
initRecoder()
}
/**
* 初始化
*/
private fun initRecoder() {
/*
getMinBufferSize用于获取成功创建AudioRecord对象所需的最小缓冲区大小,
此大小不能保证在负载下能顺利录制,应根据预期的频率选择更高的值,
在该频率下,将对AudioRecord实例进行轮询以获取新数据
参数介绍:(具体看官网api介绍)
sampleRateInHz:采样率,以赫兹为单位
channelConfig:音频通道的配置
audioFormat:音频数据的格式
*/
bufferSize = AudioRecord.getMinBufferSize(
44100,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT
)
/*
构建AudioRecord对象。
参数介绍:
audioSource:音频来源
sampleRateInHz:采样率,以赫兹为单位。目前,只有44100Hz是保证在所有设备上都可以使用的速率(最适合人耳的),但是其他速率(例如22050、16000和11025)可能在某些设备上可以使用
channelConfig:音频通道的配置
audioFormat:音频数据的格式
bufferSizeInBytes:在录制期间写入音频数据的缓冲区的总大小(以字节为单位)
*/
audioRecord = AudioRecord(
MediaRecorder.AudioSource.MIC,
44100,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize * 2
)
}
/**
* 开始录制
*/
fun startRecord(view: View) {
if (isRecording) {
return
}
isRecording = true
if (recordThread == null) {
recordThread = Thread(recordRunnable)
}
recordThread!!.start()
}
/**
* 停止录制
*/
fun stopRecord(view: View) {
//置为false,表示线程循环就结束了,线程也执行完毕了
//也可以直接中断线程
isRecording = false
audioRecord = null
recordThread = null
}
/**
* 录音线程
*
* 由于需要不断读取音频数据,所以放在子线程操作
*/
private val recordRunnable = Runnable {
//设置线程优先级
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO)
//创建文件
val tmpFile: File? = FileUtil.createFile("${System.currentTimeMillis()}.pcm")
//文件输出流
var fos: FileOutputStream = FileOutputStream(tmpFile?.getAbsoluteFile())
try {
if (audioRecord?.getState() !== AudioRecord.STATE_INITIALIZED) {
//没有初始化成功
return@Runnable
}
//开始录制
audioRecord?.startRecording()
var buffer = 0
val bytes = ByteArray(bufferSize)
//轮询读取数据
while (isRecording) {
if (audioRecord != null) {
buffer = audioRecord!!.read(bytes, 0, bufferSize)
if (buffer == AudioRecord.ERROR_INVALID_OPERATION || buffer == AudioRecord.ERROR_BAD_VALUE) {
continue
}
if (buffer == 0 || buffer == -1) {
break
}
//在此可以对录制音频的数据进行二次处理 如变声,压缩,降噪等操作
//也可以直接发送至服务器(实时语音传输) 对方可采用AudioTrack进行播放
//这里直接将pcm音频数据写入文件
fos.write(bytes)
}
}
} catch (e: Exception) {
Log.e("Test", "出错了", e)
} finally {
try {
fos?.close()
} catch (ex: IOException) {
}
audioRecord?.stop()
audioRecord?.release()
}
}
}