Android音视频开发-AudioTrack
本篇文章我们主要介绍下AudioTrack.
1: 简介
AudioTrack是Android平台上的一个类,用于播放音频数据.
它允许PCM音频缓冲区流式传输到音频接收器进行播放.
- 创建AudioTrack对象:可以通过构造函数创建AudioTrack对象,需要指定音频流类型、采样率、音频通道配置和音频格式等参数。
- 写入音频数据:可以使用write()方法将音频数据写入AudioTrack对象。写入的数据可以是PCM格式的原始音频数据,也可以是压缩格式的音频数据(如MP3、AAC等)。
- 播放音频数据:在写入音频数据后,可以调用play()方法开始播放音频数据。可以使用pause()方法暂停播放,使用stop()方法停止播放。
- 设置音量:可以使用setVolume()方法设置音量大小,范围为0.0到1.0之间。
- 设置播放模式:可以使用setPlaybackRate()方法设置播放速率,使用setLoopPoints()方法设置循环播放的起始点和结束点。
- 监听播放状态:可以使用OnPlaybackPositionUpdateListener接口监听播放进度和播放完成事件。
- 释放资源:在不再需要使用AudioTrack对象时,应该调用release()方法释放资源。
2: 创建AudioTrack对象
java
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
this(streamType, sampleRateInHz, channelConfig, audioFormat,
bufferSizeInBytes, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);
}
参数配置:
1: streamType
- AudioManager.STREAM_VOICE_CALL:用于语音通话的音频流类型。
- AudioManager.STREAM_SYSTEM:用于系统声音的音频流类型,例如按键声音、提示音等。
- AudioManager.STREAM_RING: 用于电话铃声的音频流类型。
- AudioManager.STREAM_MUSIC:用于音乐播放的音频流类型。
- AudioManager.STREAM_ALARM:用于闹钟的音频流类型。
- AudioManager.STREAM_NOTIFICATION:用于通知的音频流类型。
- AudioManager.STREAM_DTMF:用于双音多频信号的音频流类型
2: sampleRateInHz
采样率的大小限制是4000~192000; 可以在源码中查看:
java
/** Minimum value for sample rate,
* assuming AudioTrack and AudioRecord share the same limitations.
* @hide
*/
// never unhide
public static final int SAMPLE_RATE_HZ_MIN = 4000;
/** Maximum value for sample rate,
* assuming AudioTrack and AudioRecord share the same limitations.
* @hide
*/
// never unhide
public static final int SAMPLE_RATE_HZ_MAX = 192000;
/** Sample rate will be a route-dependent value.
* For AudioTrack, it is usually the sink sample rate,
* and for AudioRecord it is usually the source sample rate.
*/
public static final int SAMPLE_RATE_UNSPECIFIED = 0;
如果不在这个区间,系统则抛出异常:
调用setSampleRate指定sampleRate时,如果不在4000~192000区间则怕出new IllegalArgumentException("Invalid sample rate " + sampleRate).
java
public Builder setSampleRate(int sampleRate) throws IllegalArgumentException {
// TODO Consider whether to keep the MIN and MAX range checks here.
// It is not necessary and poses the problem of defining the limits independently from
// native implementation or platform capabilities.
if (((sampleRate < SAMPLE_RATE_HZ_MIN) || (sampleRate > SAMPLE_RATE_HZ_MAX)) &&
sampleRate != SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException("Invalid sample rate " + sampleRate);
}
mSampleRate = sampleRate;
mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE;
return this;
}
至于初始化时,校验的方法在audioParamCheck方法中:
java
// sample rate, note these values are subject to change
if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
throw new IllegalArgumentException(sampleRateInHz
+ "Hz is not a supported sample rate.");
}
3: channelConfig
声道配置同样在AudioFormat中定义,常用的有:
AudioFormat.CHANNEL_OUT_MONO:单声道 AudioFormat.CHANNEL_OUT_STEREO:双声道
4:audioFormat
该参数定义音频格式:
- AudioFormat.ENCODING_PCM_8BIT:8位PCM编码
- AudioFormat.ENCODING_PCM_16BIT:16位PCM编码
- AudioFormat.ENCODING_PCM_FLOAT:浮点型PCM编码
5:bufferSizeInBytes
音频缓冲区的大小,以字节为单位.我们可以通过getMinBufferSize()来获取最小缓冲区大小.
6: mode
指定音频输出模式.
Android 系统提供了两种模式如下:
java
/**
* Creation mode where audio data is transferred from Java to the native layer
* only once before the audio starts playing.
*/
public static final int MODE_STATIC = 0;
/**
* Creation mode where audio data is streamed from Java to the native layer
* as the audio is playing.
*/
public static final int MODE_STREAM = 1;
- MODE_STATIC:静态模式,适用于一次性播放完整音频数据的场景
- MODE_STREAM:流模式,不间断地写入音频数据.
创建代码如下:
java
int streamType = AudioManager.STREAM_MUSIC; // 音频流类型
int sampleRateInHz = 44100; // 采样率
int channelConfig = AudioFormat.CHANNEL_OUT_MONO; // 声道配置
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 音频格式
int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); // 缓冲区大小
audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
3: 写入音频数据+播放
java
/**
* 播放录音
*/
private void playAudio() {
stopPlay();
File file = new File("sdcard/audioRecord.pcm");
if (!file.exists()) return;
int streamType = AudioManager.STREAM_MUSIC; // 音频流类型
int sampleRateInHz = 44100; // 采样率
int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; // 声道配置
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 音频格式
int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); // 缓冲区大小
audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
// if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
// /**
// * 设置音频信息属性
// * 1.设置支持多媒体属性,比如audio,video
// * 2.设置音频格式,比如 music
// */
// AudioAttributes attributes = new AudioAttributes.Builder()
// .setUsage(AudioAttributes.USAGE_MEDIA)
// .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
// .build();
// /**
// * 设置音频格式
// * 1. 设置采样率
// * 2. 设置采样位数
// * 3. 设置声道
// */
// AudioFormat format = new AudioFormat.Builder()
// .setSampleRate(sampleRateInHz)
// .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
// .setChannelMask(channelConfig)
// .build();
// audioTrack = new AudioTrack(attributes,format,bufferSizeInBytes,AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE);
//
// }
audioTrack.play();
new Thread(() -> {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
byte[] buffer = new byte[bufferSizeInBytes];
Log.i(TAG, "playAudio: "+bufferSizeInBytes);
int read = 0;
while (read != -1) {
read = fileInputStream.read(buffer);
//将缓冲区buffer写入audioTrack进行播放
audioTrack.write(buffer, 0, buffer.length);
}
audioTrack.stop();
audioTrack.release();
} catch (Throwable e) {
}
}).start();
}
4: 释放
java
/**
* 停止播放录音,并释放资源
*/
private void stopPlay() {
if (audioTrack != null) {
audioTrack.release();
}
}
这里注意下,release方法内部实现了stop,所以我们不需要额外的调用stop停止播放.
另外,如果调用停止播放, 内部会判断当前状态,如果非STATE_INITIALIZED下调用,则会抛出异常.
java
public void stop()
throws IllegalStateException {
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("stop() called on uninitialized AudioTrack.");
}
// stop playing
synchronized(mPlayStateLock) {
native_stop();
baseStop();
mPlayState = PLAYSTATE_STOPPED;
mAvSyncHeader = null;
mAvSyncBytesRemaining = 0;
}
}