本文着重介绍 AudioRecord 的使用
将设备收录到的音频信息,保存为 pcm 文件
1、针对高版本的文件写入位置
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath
这个路径在 /sdcard/Android/data/[package_name]/files/Download
2、文件的输入输出流处理,使用 Kotlin 的操作符,可以方便处理流的关闭逻辑
使用 Kotlin 的 use 表达式,可以无需关心流的关闭逻辑,内部会自行处理输入输出流的关闭,只需专注自身的逻辑即可
@SuppressLint("MissingPermission")
class AudioRecordActivity : AppCompatActivity() {
companion object {
const val TAG: String = "AudioRecordActivity"
const val AUDIO_RATE = 44100
}
var mAudioThread: AudioThread? = null
var mAudioTrackThread: AudioTrackThread? = null
lateinit var PATH: String
lateinit var toolbar: MaterialToolbar
lateinit var audio_record: Button
lateinit var play_pcm: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_audio_record)
toolbar = findViewById(R.id.toolbar)
toolbar.setNavigationOnClickListener {
this@AudioRecordActivity.finish()
}
audio_record = findViewById(R.id.audio_record)
audio_record.setOnTouchListener { view, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
startRecord()
} else if (motionEvent.action == MotionEvent.ACTION_UP
|| motionEvent.action == MotionEvent.ACTION_CANCEL) {
mAudioThread?.let {
mAudioThread!!.done()
}
}
return@setOnTouchListener false
}
play_pcm = findViewById(R.id.play_pcm)
play_pcm.setOnClickListener {
playPcm()
}
PATH = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath + "/VideoDemo"
}
private fun startRecord() {
mAudioThread?.let { mAudioThread?.done() }
// 开始录制
mAudioThread = AudioThread(PATH)
mAudioThread?.start()
Log.d(TAG, "开始录制音频")
}
// 音频录制线程
class AudioThread(path: String) : Thread() {
val PATH: String
lateinit var record: AudioRecord
var minBufferSize: Int = 0
var isDone: Boolean = false
init {
PATH = path
/**
* 获取最小 buffer 大小
* 采样率 44100,双声道,采样位数为 16bit
*/
minBufferSize = AudioRecord.getMinBufferSize(
AUDIO_RATE,
AudioFormat.CHANNEL_IN_STEREO,
AudioFormat.ENCODING_PCM_16BIT
)
}
override fun run() {
super.run()
try {
// 先创建文件夹
var dir = File(PATH)
if (!dir.exists()) {
val result = dir.mkdirs()
Log.d(TAG, "create path result: ${result}")
}
// 创建 pcm 文件
val pcmFile = Utils.getFile(PATH, "test.pcm")
Log.d(TAG, "pcmFile: ${pcmFile.isFile}")
/**
* 使用 AudioRecord 去录音
*/
if (!::record.isInitialized) {
record = AudioRecord(
MediaRecorder.AudioSource.MIC,
AUDIO_RATE,
AudioFormat.CHANNEL_IN_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
minBufferSize)
Log.d(TAG, "初始化 record ${record}")
}
record.startRecording()
var buffer = ByteArray(size = minBufferSize)
FileOutputStream(pcmFile).use { outputStream ->
while (!isDone) {
val read = record.read(buffer, 0, buffer.size)
Log.d(TAG, "音频数据 ${read}")
if (AudioRecord.ERROR_INVALID_OPERATION != read) {
outputStream.write(buffer, 0, read)
}
}
}
// 录制结束
record.stop()
record.release()
} catch (e: IOException) {
e.printStackTrace()
Log.d(TAG, "audio record exception: ${e.message}")
}
}
fun done() {
interrupt()
isDone = true
Log.d(TAG, "停止录制音频")
}
}
fun playPcm() {
mAudioTrackThread = AudioTrackThread(PATH)
mAudioTrackThread?.start()
}
class AudioTrackThread(path: String) : Thread() {
val PATH: String
var audioTrack: AudioTrack
val bufferSize: Int
var isDone: Boolean = false
init {
PATH = path
val channelConfig = AudioFormat.CHANNEL_IN_STEREO
/**
* 设置音频信息属性
* 1.设置支持多媒体属性,如 audio、video
* 2.设置音频格式,如 music
*/
val attributes: AudioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
/**
* 设置音频特色
* 1.设置采样率
* 2.设置采样位数
* 3.设置声道
*/
val format: AudioFormat = AudioFormat.Builder()
.setSampleRate(AUDIO_RATE)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(channelConfig)
.build()
bufferSize = AudioTrack.getMinBufferSize(AUDIO_RATE, channelConfig, AudioFormat.ENCODING_PCM_16BIT)
audioTrack = AudioTrack(
attributes,
format,
bufferSize,
AudioTrack.MODE_STREAM,
AudioManager.AUDIO_SESSION_ID_GENERATE
)
audioTrack.play()
}
override fun run() {
super.run()
val pcmFile = File(PATH, "test.pcm")
Log.d(TAG, "pcm 文件是否存在:${pcmFile.exists()} isDone: ${isDone}")
if (pcmFile.exists()) {
FileInputStream(pcmFile).use { inputStream ->
val buffer = ByteArray(size = bufferSize)
var bytesRead = 0
while (!isDone && inputStream.read(buffer).also { bytesRead = it } != -1) {
Log.d(TAG, "pcm 数据:${bytesRead}")
audioTrack.write(buffer, 0, bytesRead)
}
}
audioTrack.stop()
audioTrack.release()
}
}
fun done() {
isDone = true
}
}
}