简介
AudioTrack是Android系统中管理和播放单一音频资源的类,相对来说比较简单,但需要注意的是,它仅能播放已经解码出来的PCM数据。
对一个音频文件(如MP3文件),如何使用FFmpeg进行解码获取到PCM,之前的文章已经有相应的说明:
https://blog.csdn.net/myvest/article/details/89254452
使用AudioTrack来播放PCM数据之前,我们先对解码出来的PCM数据进行重采样,也即是转换为指定的格式,这样我们用AudioTrack播放时就固定格式即可。重采样可以参考:
https://blog.csdn.net/myvest/article/details/89442000
使用方法及API简介
AudioTrack使用方法如下:
1、创建:
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode);
参数说明:
1)int streamType:指定即将播放的声音类型,对于不同类型,Android的audio系统会有不同处理(如音量等级不同,音量控制不同等),一些常见类型如下,对于音乐文件,我们使用STREAM_MUSIC
- STREAM_ALARM:警告声
- STREAM_MUSIC:音乐声,例如music等
- STREAM_RING:铃声
- STREAM_SYSTEM:系统声音,例如低电提示音,锁屏音等
- STREAM_VOCIE_CALL:通话声
AudioTrack有两种数据加载模式(MODE_STREAM和MODE_STATIC),对应的是数据加载模式和音频流类型, 对应着两种完全不同的使用场景。
2)int sampleRateInHz:采样率
3)int channelConfig:音频声道对应的layout,如立体声是AudioFormat.CHANNEL_OUT_STEREO
4)int audioFormat:音频格式
5)int bufferSizeInBytes:缓冲区大小
缓冲区大小可以通过函数getMinBufferSize获取,传入采样率、声道layout、音频格式即可,如下:
public AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
6)int mode:数据加载模式(MODE_STREAM和MODE_STATIC),两种模式对应着两种不同的使用场景
- MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中。这和平时通过write系统调用往文件中写数据类似,但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。为解决这一问题,AudioTrack就引入了第二种模式。
- MODE_STATIC:这种模式下,在play之前只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。但它也有一个缺点,就是一次write的数据不能太多,否则系统无法分配足够的内存来存储全部数据。
2、启动:
public AudioTrack.play();
3、数据注入:
public int write(byte[] audioData, int offsetInBytes, int sizeInBytes);
参数比较简单,数据、偏移、size
4、停止:
public AudioTrack.stop();
5、释放:
public AudioTrack.release();
6、获取状态:
STATE_INITIALIZED和STATE_UNINITIALIZED就不用说明了。STATE_NO_STATIC_DATA是个中间状态,当使用MODE_STATIC模式时,创建AudioTrack ,首先会进入改状态,需要write数据后,才会变成STATE_INITIALIZED状态。非STATE_INITIALIZED状态下去进行play的话,会抛出异常,所以MODE_STATIC模式如果没有write就去play是不行的。
/**
* Returns the state of the AudioTrack instance. This is useful after the
* AudioTrack instance has been created to check if it was initialized
* properly. This ensures that the appropriate resources have been acquired.
* @see #STATE_INITIALIZED
* @see #STATE_NO_STATIC_DATA
* @see #STATE_UNINITIALIZED
*/
public int getState() {
return mState;
}
示例
private AudioTrack audioTrack = null;
private static int sampleRateInHz = 44100;
private static int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private int bufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
class processThread implements Runnable {
......省略
public void run() {
byte[] outBuf = new byte[DECODE_BUFFER_SIZE];
if(audioTrack == null){
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, bufferSize,
AudioTrack.MODE_STREAM);
if(audioTrack == null){
mFFdec.decodeDeInit();
return ;
}
}
try {
if ( audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED) {
audioTrack.play();
}
while(true){
int size = mFFdec.decodeFrame(outBuf);
if(size > 0){
if(mFFdec.getMediaType() == mFFdec.MEDIA_TYPE_AUDIO){//audio
audioTrack.write(outBuf, 0, size);
}
}else{
break;
}
}
if (audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED) {
audioTrack.stop();
audioTrack.release();
}
}catch (Exception ex) {
ex.printStackTrace();
} catch (Throwable t) {
t.printStackTrace();
}
audioTrack = null;
mFFdec.decodeDeInit();
}
}