AudioTrack的使用
-
- 简介
- 使用
- 测试
简介
AudioTrack类为Java应用程序管理和播放单个音频资源。它允许PCM音频缓冲区流到音频接收器进行播放。
AudioTrack有两个模式
模式 | 解释 | 作用范围 |
---|---|---|
静态模式(MODE_STATIC) | 这种模式下,在play之前只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区 | 在处理适合内存的短声音以及需要以尽可能小的延迟播放时,应选择静态模式 |
流模式(MODE_STREAM) | 以流的形式持续把音频数据写到AudioTrack内部的Buffer中 | 由于音频数据的特性(高采样率、每采样位数…)太大而无法放入内存的 等等 |
使用
1、加上读取文件的权限
<uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE" />
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;
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;
}
}
private class AsyncStatic extends AsyncTask{
@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;
}
@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();//开始播放
}
}
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();
}
}
}
测试
界面只有两个简单的按钮进行测试,均能播放。