资料
音频播放AudioTrack之入门篇
配置基本参数
StreamType 音频流类型
最主要的几种STREAM
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)的音频流。
为什么分那么多种类型,其实原因很简单,比如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。
其实系统将这几种声音的数据分开管理,STREAM参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。
MODE模式(static和stream两种)
AudioTrack.MODE_STREAM
STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到AudioTrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到AudioTrack。这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
AudioTrack.MODE_STATIC
STATIC就是数据一次性交付给接收方。好处是简单高效,只需要进行一次操作就完成了数据的传递;缺点当然也很明显,对于数据量较大的音频回放,显然它是无法胜任的,因而通常只用于播放铃声、系统提醒等对内存小的操作
采样率:mSampleRateInHz
采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
通道数目:mChannelConfig
首先得出声道数,目前最多只支持双声道。为什么最多只支持双声道?看下面的源码
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch(channelConfig) {
case AudioFormat.CHANNEL_OUT_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;
break;
default:
if (!isMultichannelConfigSupported(channelConfig)) {
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
} else {
channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
}
}
.......
}
音频量化位数:mAudioFormat(只支持8bit和16bit两种)
if ((audioFormat !=AudioFormat.ENCODING_PCM_16BIT) && (audioFormat !=AudioFormat.ENCODING_PCM_8BIT)) {
return AudioTrack.ERROR_BAD_VALUE;
}
源码
public class AudioTrackManager {
private AudioTrack mAudioTrack;
private DataInputStream mDis; //播放文件的数据流
private Thread mRecordThread;
private boolean isStart = false;
private volatile static AudioTrackManager mInstance;
// 音频流类型
private static final int mStreamType = AudioManager.STREAM_MUSIC;
// 指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
private static final int mSampleRateInHz = 44100;
// 指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量
private static final int mChannelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;//单声道
// 指定音频量化位数 ,在AudioFormat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。
// 因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。
private static final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
// 指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。
private int mMinBufferSize;
// STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,
// 应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
private static final int mMode = AudioTrack.MODE_STREAM;
public AudioTrackManager() {
initData();
}
private void initData() {
// 根据采样率,采样精度,单双声道得到frame的大小。
mMinBufferSize = AudioTrack.getMinBufferSize(mSampleRateInHz, mChannelConfig, mAudioFormat);
// 注意,按照数字音频的知识,这个算出来得失一秒钟的大小.
// 创建AudioTrack
mAudioTrack = new AudioTrack(mStreamType, mSampleRateInHz, mChannelConfig, mAudioFormat, mMinBufferSize, mMode);
}
// 获取单例引用
public static AudioTrackManager getInstance() {
if (mInstance == null) {
synchronized (AudioTrackManager.class) {
if (mInstance == null) {
mInstance = new AudioTrackManager();
}
}
}
return mInstance;
}
// 销毁线程方法
private void destroyThread() {
try {
isStart = false;
if (null != mRecordThread && Thread.State.RUNNABLE == mRecordThread.getState()) {
try {
Thread.sleep(500);
mRecordThread.interrupt();
} catch (Exception e) {
mRecordThread = null;
}
}
mRecordThread = null;
} catch (Exception e) {
e.printStackTrace();
} finally {
mRecordThread = null;
}
}
// 启动播放线程
private void startThread() {
destroyThread();
isStart = true;
if (mRecordThread == null) {
mRecordThread = new Thread(recordRunnable);
mRecordThread.start();
}
}
// 播放线程
Runnable recordRunnable = new Runnable() {
@Override
public void run() {
try {
// 设置线程的优先级
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
byte[] tempBuffer = new byte[mMinBufferSize];
int readCount = 0;
while (mDis.available() > 0) {
readCount = mDis.read(tempBuffer);
if (readCount == AudioTrack.ERROR_INVALID_OPERATION || readCount == AudioTrack.ERROR_BAD_VALUE) {
continue;
}
if (readCount != 0 && readCount != -1) {
// 判断AudioTrack未初始化,停止播放的时候释放了,状态就为STATE_UNINITIALIZED
if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {
initData();
}
mAudioTrack.play();
mAudioTrack.write(tempBuffer, 0, readCount);
}
}
stopPlay();
} catch (Exception e) {
e.printStackTrace();
}
}
};
// 播放文件
private void setPath(String path) throws Exception {
File file = new File(path);
mDis = new DataInputStream(new FileInputStream(file));
}
// 设置音频流
private void setInputStream(InputStream is) {
mDis = new DataInputStream(is);
}
// 启动播放
public void startPlay(InputStream is) {
try {
// AudioTrack未初始化
// if(mAudioTrack.getState() == AudioTrack.STATE_UNINNITIALZED) {
// throw new RuntimeException("The AudioTrack is not uninitialized");
// } // AudioRecord.getMiniBufferSize的参数是否支持当前的硬件设备
// else if(AudioTrack.ERROR_BAD_VALUE == mMinBufferSize || AudioTrack.ERROR == mMinBufferSize) {
// throw new RuntimeException("AudioTrack unable to getMinBufferSize()");
// } else {
if (is != null) { // 输入音频输入流
setInputStream(is);
startThread();
}
// }
} catch (Exception e) {
e.printStackTrace();
}
}
// 停止播放
public void stopPlay() {
try {
destroyThread(); //销毁线程
if (mAudioTrack != null) {
if (mAudioTrack.getState() == AudioRecord.STATE_INITIALIZED) {
mAudioTrack.stop(); // 停止播放
}
mAudioTrack.release(); // 释放audioTrack资源
}
if (mDis != null) {
mDis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 暂停播放
public void pause() {
if (mAudioTrack != null && mAudioTrack.getState() == AudioRecord.STATE_INITIALIZED) {
mAudioTrack.pause();
}
}
}