AudioTrack是什麼?
官方釋義:管理和播放單個音頻資源。將PCM音頻緩衝區流傳輸到音頻接收器以進行播放。
AudioTrack和其他播放器的區別
播放器 | 區別/關係 |
---|---|
AudioTrack | 只能播放PCM(.wav格式的音頻文件)數據流 |
MediaPlayer | 本質是先將.mp3等格式解碼成PCM流,然後底層會調用AudioTrack進行播放 |
SoundPool | 短音頻,播放提示音,按鍵音之類的音頻 |
AudioTrack的使用流程
1.初始化AudioTrack。
2.將數據從流或者文件中不斷的寫入緩存。(靜態模式會一次性全部寫入緩存)
2.不斷的將音頻流從緩存中拿出來(靜態模式會一次性全部拿出來)
3.將拿到的緩存數據,不斷的播放出來。
4.停止或者銷燬。
使用AudioTrack前的基本概念
1.AudioTrack的兩種傳輸模式(TransferMode):
傳輸模式(TransferMode) | 區別 | 操作 |
---|---|---|
AudioTrack.MODE_STATIC | 長(流)音頻,多次讀取,可能存在延遲,需要將數據一次一次拷貝到AudioTrack的緩存中。 | 先play,然後不停的write。 |
AudioTrack.MODE_STREAM | 短音頻,一次讀完。如果數據過長,可能會直接奔掉,只需將數據一次性全部拷貝到AudioTrack的緩存中。 | 必須先write 將數據一次加載完成,再play |
2.AudioTrack的基本構造
audioTrack = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(8000)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build())
.setTransferMode(AudioTrack.MODE_STREAM)
.setSessionId(0)
.build();
構造中各種參數的含義
1.setAudioAttributes()
主要用來設置音頻屬性
- setUsage()
音頻的用途:
比如是要作爲音樂播放,
還是手機導航語音播放,
還是作爲收到消息的提示音使用,等。
如:(還有很多,這裏只列舉3種)
AudioAttributes.USAGE_MEDIA
AudioAttributes.USAGE_ALARM
AudioAttributes.USAGE_VOICE_COMMUNICATION
- setContentType()
音頻的內容:
比如當前要播放的音頻內容是聊天語音,
還是一段音樂,還是按鍵音效,等。
如:(還有很多,這裏只列舉3種)
AudioAttributes.CONTENT_TYPE_SPEECH
AudioAttributes.CONTENT_TYPE_MUSIC
AudioAttributes.CONTENT_TYPE_MOVIE
設置這些屬性有到底有什麼用?
這取決於Android系統的運行策略。
不知道大夥有沒有這種情況:
正在聽音樂,聽的很high。突然電話來了,你會發現你的音樂聲音會自動變小或者關閉。當你結束電話,音頻又會開始播放。
這是就是setAudioAttributes()所起到的作用。幫你判斷目前音頻的使用環境,並做出相應處理。
2.setAudioFormat()
- setEncoding(音頻採樣大小)
指一秒內傳輸的數據大小。採樣大小越高聲音越飽滿。
目前只有兩種模式:
ENCODING_PCM_8BIT
ENCODING_PCM_16BIT
- setSampleRate(採樣率)
指錄音設備在一秒鐘內對聲音信號的採樣次數,採樣率越高聲音越細膩。
我目前項目裏是16000Hz。
官方說44100Hz可以兼容所有值。(目前大部分app用的應該也是44100Hz)
- setChannelMask(通道數)
通道,通俗點就是耳機的聲道,聽歌的雙聲道,打遊戲的5.1聲道,7.1聲道,就是這個聲道。
AudioFormat.CHANNEL_OUT_MONO
AudioFormat.CHANNEL_OUT_STEREO
AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
3.setTransferMode()
傳輸模式,上面講到的 靜態 和 流式。
AudioTrack.MODE_STREAM
AudioTrack.MODE_STATIC
4.setBufferSizeInBytes(最小緩存區大小)
還有個概念,就是minBufferSize,它是幹嘛的?
應用層能分配多大的數據Buffer。也就是保障AudioTrack正常工作的最小緩衝區大小。
也就是播放當前音頻每一幀需要的緩存大小。
它的計算方式
AudioTrack.getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)
舉例:
int minBufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
也就是上面構造中setAudioFormat()方法裏的三個參數。
構造中有.setBufferSizeInBytes()這個方法,就是用來設置這個值的。
3.AudioTrack的其他構造
- new AudioTrack.Builder().setAudioAttributes().setAudioFormat().setBufferSizeInBytes().setTransferMode().build(); [最推薦的用法]
- new AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int mode, int sessionId); [和Builder類似,但是需要自行調用getMinBufferSize計算一下緩存大小]
- new AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) [早期API3-9的方法,官方已不推薦使用]
4.開始播放音頻
不管哪種構造方法,播放還是一樣的。
播放前的判空
audioTrack == null //是否爲空
audioTrack.getState() != AudioTrack.STATE_INITIALIZED //是否已經初始化
audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) //是否正在播放(這一條根據自己業務需求而定)
初始化狀態 和 播放狀態
1.初始化狀態
AudioTrack.STATE_UNINITIALIZED 未初始化(這個時候直接播放,會奔潰)
AudioTrack.STATE_INITIALIZED 已初始化
AudioTrack.STATE_NO_STATIC_DATA 靜態播放流已加載
2.播放狀態
AudioTrack.PLAYSTATE_STOPPED 播放停止
AudioTrack.PLAYSTATE_PAUSED 播放暫停
AudioTrack.PLAYSTATE_PLAYING 播放中
靜態播放
先說靜態播放,前面說了,一次性全部讀取。
public void staticPlay() {
//這裏以raw資源文件爲例:因爲是短資源,所以直接放在資源文件中了。
byte[] staticData = new byte[0];
InputStream in = getResources().openRawResource(R.raw.msg);
try {
int sizeOfInputStram = in.available();
staticData = new byte[sizeOfInputStram];
Log.i("ddd", "文件大小:" + sizeOfInputStram);
ByteArrayOutputStream out = new ByteArrayOutputStream(sizeOfInputStram);
// for (int b; (b = in.read()) != -1; ) {
// out.write(b);
// }
int rc;
while ((rc = in.read(staticData, 0, sizeOfInputStram)) != -1) {
out.write(staticData, 0, rc);
}
staticData = out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
staticAudioTrack = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(staticData.length)//由於短音頻一般會在一秒內播完,所以推薦使用資源的大小作爲參數
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build())
.setBufferSizeInBytes(staticData.length)//這裏設置資源的長度,因爲是靜態的短音頻
.setTransferMode(AudioTrack.MODE_STATIC)//這裏是靜態模式
.setSessionId(0)
.build();
Log.i("ddd", "靜態大小:" + staticData.length);
//一次性寫入AudioTrack
staticAudioTrack.write(staticData, 0, staticData.length);
//播放
staticAudioTrack.play();
}
流式播放
先開始播放,再不斷的讀取
//這裏以讀取file文件爲例,因爲文件較大,放在資源文件中不太合理。
//流式初始化
audioTrack = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(44100)//根據實際項目而定
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build())
.setTransferMode(AudioTrack.MODE_STREAM)//流式模式
.setSessionId(0)
.build();
//計算一次讀取的大小
int minBufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
//這裏以讀取一個長文件爲例
file = new File("文件路徑");
fileInputStream = new FileInputStream(file);
dataInputStream = new DataInputStream(new BufferedInputStream(fileInputStream))
byte[] bytes = new byte[minBufferSize];//(這裏的bytes就是上面通過getMinBufferSize計算出來的長度值)
int len;
audioTrack.play();
while ((len = dataInputStream.read(bytes)) != -1) {
//每次讀取minBufferSize的長度
audioTrack.write(bytes, 0, len);
}
audioTrack.stop();//這裏在播放完成時調用stop
audioTrack.release()
釋放資源。
當不需要或者使用完audioTrack時,可調用此方法釋放掉資源和佔用你的內存。
因爲這個方法調用後,audioTrack狀態將回到 未初始化 狀態,需要重新調用構造函數。
所以,還需要使用時,無需調用此方法。只需stop停止即可。當確定不再使用(比如退出當前頁面,這個音頻只是播放一次)時,可調用此方法釋放掉。
調用此方法後最好再置空。
audioTrack = null//將audioTrak置空