Android音频播放AudioTrick详解
Android 中常用的播放音频的接口有MediaPlayer、AudioTrack和SoundPool,音频的渲染最常用的是AudioTrack和OpenSL ES ,下面将介绍下AudioTrack相关知识,主要内容如下:
- AudioTrack介绍
- AudioTrack的创建
- AudioTrack音频数据写入
- AudioTrack生命周期
- AudioTrack的使用
AudioTrack介绍
AudioTrack用来点播放原始 pcm格式的音频数据,AudioTrack有两种播放模式:
MODE_STATIC:这种模式会将音频数据一次写入音频缓冲区,适合处理内存少及尽可能小的延迟播放的短声音场景,如播放的游戏音效、铃声、系统提示音等,此时这种模式开销最小。MODE_STREAM:这种模式会不断的写入音频数据,适用于需要不断接受音频数据的场景,这种模式主要是由于某些音频数据持续时间长、或者音频特性(高采样率、更高位深等)导致不能一次性写入内存而出现的,正常播放PCM原始音频数据就选择这种模式。
与MediaPlayer相比较,MediaPlayer可以播放不同类型、不同格式的声音文件,会在底层创建与之对应的音频解码器,而AudioTrack只接收PCM原始音频数据,MediaPlayer在底层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放。
AudioTrack的创建
AudioTrack的创建使用如下方式:
// Android5.0开始
AudioTrack(
attributes: AudioAttributes!,
format: AudioFormat!,
bufferSizeInBytes: Int,
mode: Int,
sessionId: Int)上面构造方法对应的参数含义如下:
- attributes:表示音频流信息的属性集合,自从 Android5.0 开始使用
AudioAttributes来取代流类型的设置,可以比流类型设置传达更多信息,常用来设置音频的用途、音频的内容等。 - format:表示
AudioTrack接受的音频格式,对于线性PCM来说,反应每个样本大小(8、16、32位)及表现形式(整型、浮点型),音频格式定义在AudioFormat中,常见的音频数据格式中只有AudioFormat.ENCODING_PCM_16BIT可以保证在所有的设备上正常使用,像AudioFormat.ENCODING_PCM_8BIT不能保证在所有设备上正常使用。 - bufferSizeInBytes:表示音频数据缓冲区的大小,单位事字节,其大小一般是音频帧大小的非零倍数,如果播放模式是
MODE_STATIC,则缓冲区大小是本次播放的音频的大小,如果播放模式是MODE_STREAM,则缓冲区大小不能小于最小缓冲区大小,也就是不能小于getMinBufferSize返回的大小。 - mode:表示播放模式,
AudioTrack提供了MODE_STATIC和MODE_STREAM两种方式,MODE_STATIC会将音频资源一次性写入音频缓冲区,适用于铃声、系统提示音等延时小、音频资源内存占用少的场景,,MODE_STREAM则适用于需要不断通过write方法写入数据的场景,相较MODE_STATIC会有一定延时,但是可以持续不断的接收音频数据。 - sessionId:音频会话 Id,这里使用
AudioManager.AUDIO_SESSION_ID_GENERATE有底层音频框架自己生成sessionId。
AudioTrack音频数据写入
无论是流模式(STREAM_MODE)还是静态缓冲模式(STATIC_MODE)模式,都需通过write方式写入音频数据来进行播放,主要的write方式如下:
// AudioTrack构造函数中指定的格式应为AudioFormat#ENCODING_PCM_8BIT
open fun write(audioData: ByteArray, offsetInBytes: Int, sizeInBytes: Int): Int
// AudioTrack构造函数中指定的格式应为AudioFormat#ENCODING_PCM_16BIT
open fun write(audioData: ShortArray, offsetInShorts: Int, sizeInShorts: Int): Int
// AudioTrack构造函数中指定的格式应为AudioFormat#ENCODING_PCM_FLOAT
open fun write(audioData: FloatArray, offsetInFloats: Int, sizeInFloats: Int, writeMode: Int): Int写入音频数据的返回值大于等于 0,读取音频数据常见异常如下:
- ERROR_INVALID_OPERATION:表示
AudioTrack未初始化。 - ERROR_BAD_VALUE:表示参数无效。
- ERROR_DEAD_OBJECT:表示已经传输了一些音频数据的情况下不返回错误码,将在下次
write返回处返回错误码。
这个跟AudioRecord中的 read 函数有点类似,具体细节查看官方文档。
AudioTrack生命周期
AudioTrack的生命周期主要是STATE_UNINITIALIZED、STATE_INITIALIZED和STATE_NO_STATIC_DATA,其中STATE_INITIALIZED对应STREAM_MODE,STATE_NO_STATIC_DATA对应STATIC_MODE,至于播放状态不怎么重要,如下图所示:
AudioTrack的使用
AudioTrack的使用主要就是从PCM文件中读取数据,然后将读取到的音频写入AudioTrack进行播放,其关键代码如下:
// 初始化AudioTrack
private fun initAudioTrack() {
bufferSize = AudioTrack
.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT)
attributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) // 设置音频的用途
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) // 设置音频的内容类型
.build()
audioFormat = AudioFormat.Builder()
.setSampleRate(SAMPLE_RATE)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.build()
audioTrack = AudioTrack(
attributes, audioFormat, bufferSize,
AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE
)
}
// AudioTrack写入音频数据
private fun writeAudioData(){
scope.launch(Dispatchers.IO){
val pcmFile = File(pcmFilePath)
val ins = FileInputStream(pcmFile)
val bytes = ByteArray(bufferSize)
var len: Int
while (ins.read(bytes).also { len = it } > 0){
audioTrack.write(bytes, 0, len)
}
audioTrack.stop()
}
}
// 开始播放
private fun start(){
audioTrack.play()
writeAudioData()
}AudioTrack的使用基本如上,AudioTrack播放音频的相关代码可以在回复关键字【audiotrack】关键字获取,案例中用到的本地PCM文件可以回复关键字【pcm】获取。
链接:https://juejin.cn/post/7204485453570867259
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。