AudioTrack的使用
-
- 簡介
- 使用
- 測試
簡介
AudioTrack類為Java應用程式管理和播放單個音頻資源。它允許PCM音頻緩沖區流到音頻接收器進行播放。
AudioTrack有兩個模式
模式 | 解釋 | 作用範圍 |
---|---|---|
靜态模式(MODE_STATIC) | 這種模式下,在play之前隻需要把所有資料通過一次write調用傳遞到AudioTrack中的内部緩沖區 | 在處理适合記憶體的短聲音以及需要以盡可能小的延遲播放時,應選擇靜态模式 |
流模式(MODE_STREAM) | 以流的形式持續把音頻資料寫到AudioTrack内部的Buffer中 | 由于音頻資料的特性(高采樣率、每采樣位數…)太大而無法放入記憶體的 等等 |
使用
1、加上讀取檔案的權限
2、因用到系統的檔案做
耗時操作
是以需要用到AsyncTask
3、最主要的是
public int write
(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes)方法
第一個參數是 儲存要播放的資料的數組
第二個參數是 在音頻資料中,以位元組表示的偏移量,其中要寫入的資料 (我也不是清楚,希望有大佬能解釋一下,建議寫0就好)
第三個參數是 寫入音頻資料的位元組數
4、具體解釋請看下面的代碼
package com.audioandvideo.two.Activity;
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.audioandvideo.R;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* MODE_STATIC:這種模式下,在play之前隻需要把所有資料通過一次write調用傳遞到AudioTrack中的内部緩沖區
* MODE_STREAM:以流的形式持續把音頻資料寫到AudioTrack内部的Buffer中
*/
public class AudioTrackActivity extends AppCompatActivity implements View.OnClickListener{
private Button btnStatic;
private Button btnStream;
private AudioTrack audioTrack;
private byte[] audioData;
// 采樣率:音頻的采樣頻率,每秒鐘能夠采樣的次數,采樣率越高,音質越高
// 44100是目前的标準,但是某些裝置仍然支援22050,16000,11025
// 采樣頻率一般共分為22.05KHz、44.1KHz、48KHz三個等級
private final int AUDIO_SAMPLE_RATE = 44100;
// 聲道設定:android支援雙聲道立體聲和單聲道。MONO單聲道,STEREO立體聲
private final int AUDIO_CHANNEL = AudioFormat.CHANNEL_OUT_STEREO;
// 編碼制式和采樣大小:采集來的資料當然使用PCM編碼
// (脈沖代碼調制編碼,即PCM編碼。PCM通過抽樣、量化、編碼三個步驟将連續變化的模拟信号轉換為數字編碼。)
// android支援的采樣大小16bit 或者8bit。當然采樣大小越大,那麼資訊量越多,音質也越高,現在主流的采樣
// 大小都是16bit,在低品質的語音傳輸的時候8bit 足夠了。
private final int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
//要播放的pcm檔案的儲存位置及檔案名
private final String pcmFileName = Environment.getExternalStorageDirectory() + "/Download/record.pcm";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_track);
btnStatic = findViewById(R.id.btn_static);
btnStream = findViewById(R.id.btn_stream);
btnStatic.setOnClickListener(this);
btnStream.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_static:
new AsyncStatic().execute();
break;
case R.id.btn_stream:
new AsyncStream().execute();
break;
}
}
/**
*MODE_STATIC:這種模式下,在play之前隻需要把所有資料通過一次write調用傳遞到AudioTrack中的内部緩沖區
*/
private class AsyncStatic extends AsyncTask{
/**
* 背景執行,比較耗時的操作都可以放在這裡。注意這裡不能直接操作UI。
* 此方法在背景線程執行,完成任務的主要工作,通常需要較長的時間。
* @param objects
* @return
*/
@Override
protected Object doInBackground(Object[] objects) {
try {
InputStream in = new FileInputStream(new File(pcmFileName));
try {
//位元組數組輸出流在記憶體中建立一個位元組數組緩沖區,所有發送到輸出流的資料儲存在該位元組數組緩沖區中。
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int b; (b = in.read()) != -1; ) {
out.write(b);
}
//建立一個新配置設定的位元組數組。數組的大小和目前輸出流的大小,内容是目前輸出流的拷貝。
audioData = out.toByteArray();
} finally {
//最後關閉檔案
in.close();
}
} catch (IOException e) {
}
return null;
}
/**
* 相當于Handler 處理UI的方式,在這裡面可以使用在doInBackground
* 得到的結果處理操作UI。 此方法在主線程執行,任務執行的結果作為此方法的參數傳回
* @param o
*/
@Override
protected void onPostExecute(Object o) {
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, AUDIO_SAMPLE_RATE,
AUDIO_CHANNEL, AUDIO_ENCODING, audioData.length,AudioTrack.MODE_STATIC);
audioTrack.write(audioData, 0, audioData.length); //将音頻資料寫入音頻接收器以便播放
audioTrack.play();//開始播放
}
}
/**
* MODE_STREAM:以流的形式持續把音頻資料寫到AudioTrack内部的Buffer中
*/
private class AsyncStream extends AsyncTask{
@Override
protected Object doInBackground(Object[] objects) {
final int minBufferSize = AudioTrack.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, AUDIO_SAMPLE_RATE,
AUDIO_CHANNEL, AUDIO_ENCODING, minBufferSize, AudioTrack.MODE_STREAM);//最後一個參數為audioTrack的模式
audioTrack.play();
File file = new File(pcmFileName);
//從檔案系統中的某個檔案中獲得輸入位元組
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] tempBuffer = new byte[minBufferSize];
while (true) {
try {
if (!(fileInputStream.available() > 0)) {
stop(); //停止播放
break;
}
// fileInputStream.read(tempBuffer)傳回讀入緩沖區的總位元組數;如果因為已經到達流末尾而不再有資料可用,則傳回 -1。
int readCount = fileInputStream.read(tempBuffer);
Log.d("TAG", "doInBackground:"+String.valueOf(readCount));
if (readCount == AudioTrack.ERROR_INVALID_OPERATION ||
readCount == AudioTrack.ERROR_BAD_VALUE) {
continue;
}
if (readCount != 0 && readCount != -1) {
//将音頻資料寫入音頻接收器以便播放
audioTrack.write(tempBuffer, 0, readCount);
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
//停止播放
private void stop(){
if(audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED){
audioTrack.stop();
audioTrack.release();
}
}
}
測試
界面隻有兩個簡單的按鈕進行測試,均能播放。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPn1kMZR0TxMGROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwMDOzEzMyEjM3IjMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)