众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的。MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制。但是该类只能对完整的音频文件进行操作,而不能直接对纯PCM音频数据操作。假如我们通过解码得到PCM数据源,又当如何将它们播放?没错,就是用AudioTrack这个类(MediaPlayer内部也是调用该类进行真正的播放音频流操作)下面这个DEMO演示了如何使用AudioTrack来播放PCM音频数据

废话不多说,先上效果图:

工程代码结构也较为简单:

简单说下思路,先把PCM音频数据从指定的路径文件读到内存,然后给AudioPlayer设置数据源,音频参数等,最后执行播放,暂停,停止等操作

贴上部分类代码片段:

public class AudioParam {int mFrequency;// 采样率int mChannel;// 声道int mSampBit;// 采样精度}



public interface PlayState {public static final int MPS_UNINIT = 0;// 未就绪public static final int MPS_PREPARE = 1;// 准备就绪(停止)public static final int MPS_PLAYING = 2;// 播放中public static final int MPS_PAUSE = 3;// 暂停}


AudioPlayer代码片段如下:

public class AudioPlayer implements IPlayComplete{private final static String TAG = "AudioPlayer";public final static int    STATE_MSG_ID = 0x0010;private Handler    mHandler;private AudioParam mAudioParam;// 音频参数private byte[]    mData;// 音频数据private AudioTrack mAudioTrack;// AudioTrack对象private boolean    mBReady = false;// 播放源是否就绪private PlayAudioThread mPlayAudioThread;// 播放线程public AudioPlayer(Handler handler){mHandler = handler;}public AudioPlayer(Handler handler,AudioParam audioParam){mHandler = handler;setAudioParam(audioParam);}/* * 设置音频参数 */public void setAudioParam(AudioParam audioParam){mAudioParam = audioParam;}/* * 设置音频源 */public void setDataSource(byte[] data){mData = data;}/* *  就绪播放源 */public boolean prepare(){if (mData == null || mAudioParam == null){return false;}if (mBReady == true){return true;}try {createAudioTrack();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();return false;}mBReady = true;setPlayState(PlayState.MPS_PREPARE);return true;}

private boolean mThreadExitFlag = false;// 线程退出标志private int     mPrimePlaySize = 0;// 较优播放块大小private int mPlayOffset = 0;// 当前播放位置private int     mPlayState = 0;// 当前播放状态/* *  播放音频的线程 */class PlayAudioThread extends Thread{@Overridepublic void run() {// TODO Auto-generated method stubLog.d(TAG, "PlayAudioThread run mPlayOffset = " + mPlayOffset);mAudioTrack.play();while(true){if (mThreadExitFlag == true){break;}try {int size = mAudioTrack.write(mData, mPlayOffset, mPrimePlaySize);mPlayOffset += mPrimePlaySize;} catch (Exception e) {// TODO: handle exceptione.printStackTrace();AudioPlayer.this.onPlayComplete();break;}if (mPlayOffset >= mData.length){AudioPlayer.this.onPlayComplete();break;}}mAudioTrack.stop();Log.d(TAG, "PlayAudioThread complete...");}}

下面来剖析以下如何使用AudioTrack来播放PCM音频数据

首先要构建一个AudioTrack对象:(需要采样率,声道,采样精度参数)

private void createAudioTrack() throws Exception{// 获得构建对象的最小缓冲区大小int minBufSize = AudioTrack.getMinBufferSize(mAudioParam.mFrequency, mAudioParam.mChannel,mAudioParam.mSampBit);mPrimePlaySize = minBufSize * 2;Log.d(TAG, "mPrimePlaySize = " + mPrimePlaySize);//         STREAM_ALARM:警告声//         STREAM_MUSCI:音乐声,例如music等//         STREAM_RING:铃声//         STREAM_SYSTEM:系统声音//         STREAM_VOCIE_CALL:电话声音mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,mAudioParam.mFrequency, mAudioParam.mChannel, mAudioParam.mSampBit, minBufSize,AudioTrack.MODE_STREAM);//AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。//      STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。//这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。//这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。//而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,//后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。//这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。}

然后开一个子线程从缓存区里分块取数据然后写入硬件设备进行播放

private void startThread(){if (mPlayAudioThread == null){mThreadExitFlag = false;mPlayAudioThread = new PlayAudioThread();mPlayAudioThread.start();}}

AudioTrack里有三个重要方法:

voidplay()

intwrite(byte[]audioData,intoffsetInBytes,intsizeInBytes)(该方法是阻塞的)

voidstop()

从前面那个线程代码可以看出,我们在写数据之前需要先执行play(),然后才能进行write操作,当数据播放完毕或是线程被外部终止的时候最后调用stop()停止写数据;若执行了play操作但后面却没有执行write操作的话,或是write操作结束后没有调用stop,观察logcat会不断打印提示信息,这是提示我们对以上三个方法的调用要规范

只要大家设置的音频参数和音频数据都是正确的,就能顺畅的播放出声音,本例已经附带了用于测试的音频文件以及参数说明(已测试通过),具体看工程里音频数据这个文件夹下的readme.txt即可.网上有些童鞋反应说audiotrack播放音频不顺畅,如果数据源没问题的话估计是他们的demo里没有连续地执行write操作而导致的,其它的不多说了,觉得有用的童鞋自己写代码看吧。。。喜欢就顶一下吧!

代码链接如下:

http://download.csdn.net/detail/geniuseoe2012/4378393

本文着重介绍audiotrack的使用,关于其底层原理,且看这位仁兄的文章:

http://www.cnblogs.com/innost/archive/2011/01/09/1931457.html

更多相关文章

  1. Android官方DataBinding(三):RecyclerView 使用ViewDataBinding更新
  2. android sqlite 数据类型
  3. Android(安卓)Xml文件生成,Xml数据格式写入
  4. 视频专辑: 善知堂android 4.0.3 就业视频教程
  5. Android(安卓)Intent 深入理解
  6. Android大数据、断点续传、耗时下载之DownloadManager开发简介(1
  7. Android(安卓)RakNet 系列之三 移植Portaudio
  8. Android处理屏幕旋转时的解决方案
  9. Android(安卓)SQLite使用方法

随机推荐

  1. LINQ to XML 编程基础的图文代码详细介绍
  2. 详细介绍使用XML资源文件来定义颜色,样式
  3. 使用xmldom在服务器端生成静态html页面
  4. C#-XML操作类的代码实例详解
  5. 详细介绍Web安全之XML注入的示例代码
  6. 详细解析包含中文字符的URL编码问题
  7. 详细介绍XML技术上传文件
  8. 详细介绍如何使用javascript+xml实现分页
  9. 如何使用DOM创建XML的详细介绍
  10. 教你怎样快速从一个XML文件中查找信息的