Android音頻播放AudioTrack詳解

PS: 鴻蒙2.0還是可以的,期待。
前面幾篇文章介紹了MediaCodec、MediaMuxer、AudioRecord等音視頻相關(guān)知識,這些都是 Android 音視頻開發(fā)中必須掌握的,相關(guān)文章鏈接如下:
如何正確編譯ijkplayer 音視頻開發(fā)基礎(chǔ)知識 音頻幀、視頻幀及其同步 Camera2、MediaCodec錄制mp4 MediaCodec詳解 音頻基礎(chǔ)知識 AudioRecord采集音頻數(shù)據(jù)及合成
Android 中常用的播放音頻的接口有MediaPlayer、AudioTrack和SoundPool,音頻的渲染最常用的是AudioTrack和OpenSL ES ,下面將介紹下AudioTrack相關(guān)知識,主要內(nèi)容如下:
AudioTrack介紹 AudioTrack的創(chuàng)建 AudioTrack音頻數(shù)據(jù)寫入 AudioTrack生命周期 AudioTrack的使用
AudioTrack介紹
AudioTrack用來點播放原始 pcm格式的音頻數(shù)據(jù),AudioTrack有兩種播放模式:
MODE_STATIC:這種模式會將音頻數(shù)據(jù)一次寫入音頻緩沖區(qū),適合處理內(nèi)存少及盡可能小的延遲播放的短聲音場景,如播放的游戲音效、鈴聲、系統(tǒng)提示音等,此時這種模式開銷最小。MODE_STREAM:這種模式會不斷的寫入音頻數(shù)據(jù),適用于需要不斷接受音頻數(shù)據(jù)的場景,這種模式主要是由于某些音頻數(shù)據(jù)持續(xù)時間長、或者音頻特性(高采樣率、更高位深等)導(dǎo)致不能一次性寫入內(nèi)存而出現(xiàn)的,正常播放PCM原始音頻數(shù)據(jù)就選擇這種模式。
與MediaPlayer相比較,MediaPlayer可以播放不同類型、不同格式的聲音文件,會在底層創(chuàng)建與之對應(yīng)的音頻解碼器,而AudioTrack只接收PCM原始音頻數(shù)據(jù),MediaPlayer在底層還是會創(chuàng)建AudioTrack,把解碼后的PCM數(shù)流傳遞給AudioTrack,AudioTrack再傳遞給AudioFlinger進行混音,然后才傳遞給硬件播放。
AudioTrack的創(chuàng)建
AudioTrack的創(chuàng)建使用如下方式:
// Android5.0開始
AudioTrack(
attributes: AudioAttributes!,
format: AudioFormat!,
bufferSizeInBytes: Int,
mode: Int,
sessionId: Int)
上面構(gòu)造方法對應(yīng)的參數(shù)含義如下:
attributes:表示音頻流信息的屬性集合,自從 Android5.0 開始使用 AudioAttributes來取代流類型的設(shè)置,可以比流類型設(shè)置傳達更多信息,常用來設(shè)置音頻的用途、音頻的內(nèi)容等。format:表示 AudioTrack接受的音頻格式,對于線性PCM來說,反應(yīng)每個樣本大小(8、16、32位)及表現(xiàn)形式(整型、浮點型),音頻格式定義在AudioFormat中,常見的音頻數(shù)據(jù)格式中只有AudioFormat.ENCODING_PCM_16BIT可以保證在所有的設(shè)備上正常使用,像AudioFormat.ENCODING_PCM_8BIT不能保證在所有設(shè)備上正常使用。bufferSizeInBytes:表示音頻數(shù)據(jù)緩沖區(qū)的大小,單位是字節(jié),其大小一般是音頻幀大小的非零倍數(shù),如果播放模式是 MODE_STATIC,則緩沖區(qū)大小是本次播放的音頻的大小,如果播放模式是MODE_STREAM,則緩沖區(qū)大小不能小于最小緩沖區(qū)大小,也就是不能小于getMinBufferSize返回的大小。mode:表示播放模式, AudioTrack提供了MODE_STATIC和MODE_STREAM兩種方式,MODE_STATIC會將音頻資源一次性寫入音頻緩沖區(qū),適用于鈴聲、系統(tǒng)提示音等延時小、音頻資源內(nèi)存占用少的場景,,MODE_STREAM則適用于需要不斷通過write方法寫入數(shù)據(jù)的場景,相較MODE_STATIC會有一定延時,但是可以持續(xù)不斷的接收音頻數(shù)據(jù)。sessionId:音頻會話 Id,這里使用 AudioManager.AUDIO_SESSION_ID_GENERATE有底層音頻框架自己生成sessionId。
AudioTrack音頻數(shù)據(jù)寫入
無論是流模式(STREAM_MODE)還是靜態(tài)緩沖模式(STATIC_MODE)模式,都需通過write方式寫入音頻數(shù)據(jù)來進行播放,主要的write方式如下:
// AudioTrack構(gòu)造函數(shù)中指定的格式應(yīng)為AudioFormat#ENCODING_PCM_8BIT
open fun write(audioData: ByteArray, offsetInBytes: Int, sizeInBytes: Int): Int
// AudioTrack構(gòu)造函數(shù)中指定的格式應(yīng)為AudioFormat#ENCODING_PCM_16BIT
open fun write(audioData: ShortArray, offsetInShorts: Int, sizeInShorts: Int): Int
// AudioTrack構(gòu)造函數(shù)中指定的格式應(yīng)為AudioFormat#ENCODING_PCM_FLOAT
open fun write(audioData: FloatArray, offsetInFloats: Int, sizeInFloats: Int, writeMode: Int): Int
寫入音頻數(shù)據(jù)的返回值大于等于 0,讀取音頻數(shù)據(jù)常見異常如下:
ERROR_INVALID_OPERATION:表示 AudioTrack未初始化。ERROR_BAD_VALUE:表示參數(shù)無效。 ERROR_DEAD_OBJECT:表示已經(jīng)傳輸了一些音頻數(shù)據(jù)的情況下不返回錯誤碼,將在下次 write返回處返回錯誤碼。
這個跟AudioRecord中的 read 函數(shù)有點類似,具體細節(jié)查看官方文檔。
AudioTrack生命周期
AudioTrack的生命周期主要是STATE_UNINITIALIZED、STATE_INITIALIZED和STATE_NO_STATIC_DATA,其中STATE_INITIALIZED對應(yīng)STREAM_MODE,STATE_NO_STATIC_DATA對應(yīng)STATIC_MODE,至于播放狀態(tài)不怎么重要,如下圖所示:

AudioTrack的使用
AudioTrack的使用主要就是從PCM文件中讀取數(shù)據(jù),然后將讀取到的音頻寫入AudioTrack進行播放,其關(guān)鍵代碼如下:
// 初始化AudioTrack
private fun initAudioTrack() {
bufferSize = AudioTrack
.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT)
attributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) // 設(shè)置音頻的用途
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) // 設(shè)置音頻的內(nèi)容類型
.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寫入音頻數(shù)據(jù)
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播放音頻的相關(guān)代碼可以在回復(fù)關(guān)鍵字【audiotrack】關(guān)鍵字獲取,案例中用到的本地PCM文件可以回復(fù)關(guān)鍵字【pcm】獲取。
推薦閱讀:
