使用AudioTrack播放PCM
为什么只播放PCM不播放Mp3呢?因为AudioTrack是基础API,没有解码功能,而MediaPlayer是有解码功能的。所以要播放Mp3,还需要自行添加解码功能,把Mp3解码为PCM再播放。
只要做好两件事情:
- AudioTrack的初始化参数的含义
- AudioTrack的播放流程:play->write->stop
1. 初始化AudioTrack
//获取最小缓冲区大小 int minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, //采样率 AudioFormat.CHANNEL_OUT_STEREO, //双声道 AudioFormat.ENCODING_PCM_16BIT //采样格式 ); //初始化AudioTrack对象 AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, //媒体类型 SAMPLE_RATE, //采样率 AudioFormat.CHANNEL_IN_STEREO, //双声道 AudioFormat.ENCODING_PCM_16BIT, //采样格式 minBufferSize, //缓冲区大小 AudioTrack.MODE_STREAM //流式加载 );
参数详解:
- 参数一:streamType
Android手机上提供了多重音频管理策略(例如按一下手机侧面音量键,会出现多个音量管理,这其实就是不同音频策略的音频控制展示),当系统有多个进程需要播放音频的时候,管理策略会决定最终的呈现效果,该参数的可选值将以常量的形式定义在类AudioManager中,主要包括以下内容:
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_ public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM; public static final int STREAM_RING = AudioSystem.STREAM_RING; public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC; public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
- 参数二:sampleRateInHz
采样率,即播放的音频每秒钟会有多少次采样,可选用的采样频率列表为:8000、16000、22050、24000、32000、44100、48000等,要根据播放的PCM的采样率来决定初始化AudioTrack时的具体值,否则可能导致声音变快或者变慢 - 参数三:channelConfig
声道数(通道数)配置,可选值以常量形式配置在类AudioFormat中,常用的是CHANNEL_IN_MONO(输入单声道),CHANNEL_IN_STEREO(输入双声道)。(因为现在大多数手机的麦克风都是伪立体声采集,为了性能考虑,建议使用单声道进行音频采集,而转变为立体声的过程可以在声音的特效处理阶段来完成)。 - 参数四:audioFormat
该参数是用来配置数据位宽,表示了采样精度,可选值以常量的形式定义在类AudioFormat中,分别为ENCODING_PCM_16BIT和ENCODING_PCM_8BIT,注意,前者是可以兼容安卓所有手机的。 - 参数五:bufferSizeInBytes
它配置的是AudioTrack内部缓冲区的大小,AudioTrack类提供了一个静态方法getMinBufferSize
来帮忙开发者确定缓冲区的大小,在实际开发中,强烈建议使用该方法,而不是自己手动计算。 - 参数六:mode
AudioTrack提供了两种播放模式,可选的值以常量的形式定义在类AudioTrack中,分别为:
- MODE_STREAM 按照一定的时间间隔不间断地定入音频数据,理论上它可以应用于任何音频播放的场景
- MODE_STATIC 需要一次性将所有的数据都写入播放缓冲区中,简单高效,通常用于播放铃声、系统提醒的音频片段。
2. 启动播放
//先启动播放 audioTrack.play();
3. write音频数据
//分配缓冲区 byte[] buffer = new byte[minBufferSize * 3]; is = getAssets().open(pcmFilePath); dis = new DataInputStream(is); int readCount = 0; while (dis.available() > 0) { //读取PCM readCount = dis.read(buffer); Log.d(MainActivity.class.getSimpleName(), "readCount=" + readCount); if (readCount == AudioTrack.ERROR_INVALID_OPERATION || readCount == AudioTrack.ERROR_BAD_VALUE) { continue; } if (readCount != 0 && readCount != -1) { //写入AudioTrack audioTrack.write(buffer, 0, readCount); } } ... audioTrack.stop();