AudioTrack
之前我们讲过MediaPlayer,MediaPlayer就相当于AudioTrack的包装层,它可以播放MP3,WAV,OGG,AAC,MIDI等,而AudioTrack只能播放PCM数据流
AudioTrack的构造方法
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) { ... }
streamType 音频流类型
AudioManager.STREAM_MUSIC:用于音乐播放的音频流。
AudioManager.STREAM_SYSTEM:用于系统声音的音频流。
AudioManager.STREAM_RING:用于电话铃声的音频流。
AudioManager.STREAM_VOICE_CALL:用于电话通话的音频流。
AudioManager.STREAM_ALARM:用于警报的音频流。
AudioManager.STREAM_NOTIFICATION:用于通知的音频流。
AudioManager.STREAM_BLUETOOTH_SCO:用于连接到蓝牙电话时的手机音频流。
AudioManager.STREAM_SYSTEM_ENFORCED:在某些国家实施的系统声音的音频流。
AudioManager.STREAM_DTMF:DTMF音调的音频流。
AudioManager.STREAM_TTS:文本到语音转换(TTS)的音频流。
sampleRateInHz 采样率
播放的音频每秒钟会有多少次采样,MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率
channelConfig 声道数(通道数)
一般可选的就两种,单声道CHANNEL_IN_MONO,双声道CHANNEL_IN_STEREO,建议选择单声道
audioFormat 数据位宽
只支持AudioFormat.ENCODING_PCM_8BIT(8bit)和AudioFormat.ENCODING_PCM_16BIT(16bit)两种,后者支持所有Android手机
bufferSizeInBytes 音频缓冲区大小
建议使用AudioTrack.getMinBufferSize()这个方法获取,参数如上
mode 播放模式
Android 提供了两种播放模式:
MODE_STATIC,一次性将所有数据都写入播放缓冲区中,简单高效,一般用于铃声,系统提醒音,内存比较小的。
MODE_STREAM,需要按照一定的时间间隔,不断的写入音频数据,理论上它可以应用于任何音频播放的场景。
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int mode, int sessionId) { ... }
静态播放
// ************ 静态播放模式 ************
// 直接获取文件大小
InputStream in = getResources().openRawResource(R.raw.ding);
try {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int b; (b = in.read()) != -1; ) {
out.write(b);
}
audioData = out.toByteArray();
} finally {
in.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
Log.d(TAG, "读取数据失败!");
}
//构造AudioTrack对象,写入数据并播放
audioTrack = new AudioTrack(
new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build(),
new AudioFormat.Builder().setSampleRate(22050)
.setEncoding(AudioFormat.ENCODING_PCM_8BIT)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build(),
audioData.length,
AudioTrack.MODE_STATIC,
AudioManager.AUDIO_SESSION_ID_GENERATE);
audioTrack.write(audioData, 0, audioData.length);
if(audioTrack.getState() == AudioTrack.STATE_UNINITIALIZED){
Toast.makeText(this,"AudioTrack初始化失败!",Toast.LENGTH_SHORT).show();
return;
}
audioTrack.play();
流播放模式
// ************ 流播放 ************
final int minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE_INHZ, AudioFormat.CHANNEL_OUT_MONO, AUDIO_FORMAT);
// 创建 AudioTrack 对象
audioTrack = new AudioTrack(
new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build(),
new AudioFormat.Builder().setSampleRate(SAMPLE_RATE_INHZ)
.setEncoding(AUDIO_FORMAT)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build(),
minBufferSize,
AudioTrack.MODE_STREAM,
AudioManager.AUDIO_SESSION_ID_GENERATE
);
// 检查初始化是否成功
if(audioTrack.getState() == AudioTrack.STATE_UNINITIALIZED){
Toast.makeText(this,"AudioTrack初始化失败!",Toast.LENGTH_SHORT).show();
return;
}
// 播放
audioTrack.play();
//子线程中文件流写入
workHandler.post(new Runnable() {
@Override
public void run() {
try {
final File file = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), "test.pcm");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] tempBuffer = new byte[minBufferSize];
while (fileInputStream.available() > 0) {
int readCount = fileInputStream.read(tempBuffer);
if (readCount == AudioTrack.ERROR_INVALID_OPERATION ||
readCount == AudioTrack.ERROR_BAD_VALUE) {
continue;
}
if (readCount != 0 && readCount != -1) {
audioTrack.write(tempBuffer, 0, readCount);
}
}
fileInputStream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
});
停止播放,销毁资源
// 停止线程
handlerThread.quit();
workHandler.removeCallbacksAndMessages(null);
if(audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED){
audioTrack.stop();
audioTrack.release();
}