音频处理
问题
Android 音频 API 有哪些?如何实现音频录制和播放?
答案
音频 API 层级
| API | 层级 | 延迟 | 适用场景 |
|---|---|---|---|
| MediaPlayer | 高层 | 高 | 简单音频/视频播放 |
| SoundPool | 高层 | 低 | 短音效(游戏音效、提示音) |
| AudioTrack | 底层 | 低 | PCM 数据播放、实时音频 |
| AudioRecord | 底层 | 低 | PCM 录音 |
| Oboe (C++) | 底层 | 最低 | 实时音频(乐器、音效处理) |
AudioTrack 播放 PCM
// PCM 播放
val sampleRate = 44100
val bufferSize = AudioTrack.getMinBufferSize(
sampleRate,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT
)
val audioTrack = AudioTrack.Builder()
.setAudioAttributes(AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(AudioFormat.Builder()
.setSampleRate(sampleRate)
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.build())
.setBufferSizeInBytes(bufferSize)
.setTransferMode(AudioTrack.MODE_STREAM)
.build()
audioTrack.play()
// 在子线程中持续写入 PCM 数据
thread {
val buffer = ByteArray(bufferSize)
while (isPlaying) {
val bytesRead = inputStream.read(buffer)
if (bytesRead > 0) {
audioTrack.write(buffer, 0, bytesRead)
}
}
}
AudioRecord 录音
val sampleRate = 44100
val bufferSize = AudioRecord.getMinBufferSize(
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT
)
val audioRecord = AudioRecord(
MediaRecorder.AudioSource.MIC,
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize
)
audioRecord.startRecording()
thread {
val buffer = ByteArray(bufferSize)
while (isRecording) {
val bytesRead = audioRecord.read(buffer, 0, bufferSize)
if (bytesRead > 0) {
// 处理 PCM 数据:保存、编码、发送
outputStream.write(buffer, 0, bytesRead)
}
}
}
音频焦点
// 请求音频焦点(播放前必须)
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.build())
.setOnAudioFocusChangeListener { focusChange ->
when (focusChange) {
AudioManager.AUDIOFOCUS_LOSS -> {
// 永久失去焦点:暂停播放
player.pause()
}
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
// 暂时失去焦点:暂停
player.pause()
}
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
// 可降低音量继续播放
player.setVolume(0.3f)
}
AudioManager.AUDIOFOCUS_GAIN -> {
// 恢复焦点:恢复播放和音量
player.setVolume(1.0f)
player.play()
}
}
}
.build()
audioManager.requestAudioFocus(focusRequest)
常见面试问题
Q1: PCM、WAV、AAC、MP3 的区别?
答案:
| 格式 | 类型 | 压缩 | 说明 |
|---|---|---|---|
| PCM | 原始数据 | 无 | 未编码的音频采样数据 |
| WAV | 容器格式 | 无 | PCM + 文件头(采样率、位深等) |
| AAC | 编码格式 | 有损 | 比 MP3 更高效,Android 推荐 |
| MP3 | 编码格式 | 有损 | 兼容性最好 |
PCM 是最原始的音频数据,AudioRecord 录制的就是 PCM,需要编码为 AAC/MP3 才方便存储和传输。
Q2: 什么是音频焦点?为什么要管理它?
答案:
音频焦点是 Android 的音频优先级管理机制。多个应用可能同时需要播放音频(音乐 App、导航、电话),音频焦点确保只有一个应用在前台播放,其他应用根据焦点变化暂停或降低音量。不管理音频焦点会导致多个声音同时播放,用户体验差。