Android 音频(一) _ 采样量化编码 & AudioRecord 录制音频

本文介绍了一个在Android中实现音频录制的抽象接口`Recorder`,以及使用`AudioRecord`和`MediaRecorder`的实现。通过解耦抽象,提供了一种线程安全、易于管理音频录制的方法。`AudioRecorder`类中包含了音频录制的核心逻辑,包括实时计算录音时长、检查录制状态等。`MediaRecord`类则作为`AudioRecord`的替代方案,用于生成不同格式的音频文件。上层类`AudioManager`负责音频录制的控制,包括开始、停止、释放资源以及状态回调。
摘要由CSDN通过智能技术生成

public inline fun File.outputStream(): FileOutputStream {
return FileOutputStream(this)
}

  • use()是一个 Closeable 的扩展方法,不管发生了什么,最终use()都会调用close()来关闭资源。这就避免了流操作的模板代码,降低了代码的复杂度:

public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
var exception: Throwable? = null
try {
// 在 try 代码块中执行传入的 lambda
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
// 在 finally 中执行 close()
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {}
}
}
}

  • IO操作时耗时的,读写音频数据的代码应该在非UI线程中执行。而是否继续录制应该由用户动作触发,即UI线程触发。这里有多线程安全问题,需要一个线程安全的布尔值来控制音频录制:

var isRecording = AtomicBoolean(false) // 线程安全的布尔变量
val audioRecord: AudioRecord

// 是否继续录制
fun continueRecord(): Boolean {
return isRecording.get() &&
audioRecord.recordingState == AudioRecord.RECORDSTATE_RECORDING
}

// 停止录制音频(供业务层调用以停止录音的 while 循环)
fun stop() {
isRecording.set(false)
}

解耦抽象

将对 AudioRecord 的所有操作都抽象在一个接口中:

interface Recorder {
var outputFormat: String // 输出音频格式
fun isRecording(): Boolean // 是否正在录制
fun getDuration(): Long // 获取音频时长
fun start(outputFile: File, maxDuration: Int) // 开始录制
fun stop() // 停止录制
fun release() // 释放录制资源
}

这个接口提供了录制音频的抽象能力。当上层类和这组接口打交道时,不需要关心录制音频的实现细节,即不和 AudioRecord 耦合。

为啥要多一层这样的抽象?因为具体实现总是易变的,万一哪天业务层需要直接生成 AAC 文件,就可以通过添加一个Recorder的实例方便地地替换原有实现。

给出 AudioRecord 对于Recorder接口的实现:

class AudioRecorder(override var outputFormat: String) : Recorder {
private var bufferSize: Int = 0 // 音频字节缓冲区大小
private var isRecording = AtomicBoolean(false) // 用于控制音频录制的线程安全布尔值
private var startTime = 0L // 记录音频开始录制时间
private var duration = 0L // 音频时长
// AudioRecord 实例
private val audioRecord by lazy {
bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_IN_MONO, ENCODING_PCM_16BIT)
AudioRecord(SOURCE, SAMPLE_RATE, CHANNEL_IN_MONO, ENCODING_PCM_16BIT, bufferSize)
}
// 是否正在录制
override fun isRecording(): Boolean = isRecording.get()
// 获取音频时长
override fun getDuration(): Long = duration
// 开始音频录制
override fun start(outputFile: File, maxDuration: Int) {
if (audioRecord.state == AudioRecord.STATE_UNINITIALIZED) return
isRecording.set(true) // 在异步线程中标记开始录制
startTime.set(SystemClock.elapsedRealtime()) // 在异步线程中记录开始时间
// 创建文件输出流
outputFile.outputStream().use { outputStream ->
// 开始录制
audioRecord.startRecording()
val audioData = ByteArray(bufferSize)
// 持续读取音频数据到字节数组, 再将字节数组写入文件
while (continueRecord(maxDuration)) {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值