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

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

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

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

贴上部分类代码片段:

[java] view plain copy
  1. publicclassAudioParam{
  2. intmFrequency;//采样率
  3. intmChannel;//声道
  4. intmSampBit;//采样精度
  5. }



[java] view plain copy
  1. publicinterfacePlayState{
  2. publicstaticfinalintMPS_UNINIT=0;//未就绪
  3. publicstaticfinalintMPS_PREPARE=1;//准备就绪(停止)
  4. publicstaticfinalintMPS_PLAYING=2;//播放中
  5. publicstaticfinalintMPS_PAUSE=3;//暂停
  6. }

AudioPlayer代码片段如下:

[java] view plain copy
  1. publicclassAudioPlayerimplementsIPlayComplete{
  2. privatefinalstaticStringTAG="AudioPlayer";
  3. publicfinalstaticintSTATE_MSG_ID=0x0010;
  4. privateHandlermHandler;
  5. privateAudioParammAudioParam;//音频参数
  6. privatebyte[]mData;//音频数据
  7. privateAudioTrackmAudioTrack;//AudioTrack对象
  8. privatebooleanmBReady=false;//播放源是否就绪
  9. privatePlayAudioThreadmPlayAudioThread;//播放线程
  10. publicAudioPlayer(Handlerhandler)
  11. {
  12. mHandler=handler;
  13. }
  14. publicAudioPlayer(Handlerhandler,AudioParamaudioParam)
  15. {
  16. mHandler=handler;
  17. setAudioParam(audioParam);
  18. }
  19. /*
  20. *设置音频参数
  21. */
  22. publicvoidsetAudioParam(AudioParamaudioParam)
  23. {
  24. mAudioParam=audioParam;
  25. }
  26. /*
  27. *设置音频源
  28. */
  29. publicvoidsetDataSource(byte[]data)
  30. {
  31. mData=data;
  32. }
  33. /*
  34. *就绪播放源
  35. */
  36. publicbooleanprepare()
  37. {
  38. if(mData==null||mAudioParam==null)
  39. {
  40. returnfalse;
  41. }
  42. if(mBReady==true)
  43. {
  44. returntrue;
  45. }
  46. try{
  47. createAudioTrack();
  48. }catch(Exceptione){
  49. //TODOAuto-generatedcatchblock
  50. e.printStackTrace();
  51. returnfalse;
  52. }
  53. mBReady=true;
  54. setPlayState(PlayState.MPS_PREPARE);
  55. returntrue;
  56. }

[java] view plain copy
  1. privatebooleanmThreadExitFlag=false;//线程退出标志
  2. privateintmPrimePlaySize=0;//较优播放块大小
  3. privateintmPlayOffset=0;//当前播放位置
  4. privateintmPlayState=0;//当前播放状态
  5. /*
  6. *播放音频的线程
  7. */
  8. classPlayAudioThreadextendsThread
  9. {
  10. @Override
  11. publicvoidrun(){
  12. //TODOAuto-generatedmethodstub
  13. Log.d(TAG,"PlayAudioThreadrunmPlayOffset="+mPlayOffset);
  14. mAudioTrack.play();
  15. while(true)
  16. {
  17. if(mThreadExitFlag==true)
  18. {
  19. break;
  20. }
  21. try{
  22. intsize=mAudioTrack.write(mData,mPlayOffset,mPrimePlaySize);
  23. mPlayOffset+=mPrimePlaySize;
  24. }catch(Exceptione){
  25. //TODO:handleexception
  26. e.printStackTrace();
  27. AudioPlayer.this.onPlayComplete();
  28. break;
  29. }
  30. if(mPlayOffset>=mData.length)
  31. {
  32. AudioPlayer.this.onPlayComplete();
  33. break;
  34. }
  35. }
  36. mAudioTrack.stop();
  37. Log.d(TAG,"PlayAudioThreadcomplete...");
  38. }
  39. }

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

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

[java] view plain copy
  1. privatevoidcreateAudioTrack()throwsException
  2. {
  3. //获得构建对象的最小缓冲区大小
  4. intminBufSize=AudioTrack.getMinBufferSize(mAudioParam.mFrequency,
  5. mAudioParam.mChannel,
  6. mAudioParam.mSampBit);
  7. mPrimePlaySize=minBufSize*2;
  8. Log.d(TAG,"mPrimePlaySize="+mPrimePlaySize);
  9. //STREAM_ALARM:警告声
  10. //STREAM_MUSCI:音乐声,例如music等
  11. //STREAM_RING:铃声
  12. //STREAM_SYSTEM:系统声音
  13. //STREAM_VOCIE_CALL:电话声音
  14. mAudioTrack=newAudioTrack(AudioManager.STREAM_MUSIC,
  15. mAudioParam.mFrequency,
  16. mAudioParam.mChannel,
  17. mAudioParam.mSampBit,
  18. minBufSize,
  19. AudioTrack.MODE_STREAM);
  20. //AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。
  21. //STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。
  22. //这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
  23. //这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
  24. //而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,
  25. //后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
  26. //这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
  27. }

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

[java] view plain copy
  1. privatevoidstartThread()
  2. {
  3. if(mPlayAudioThread==null)
  4. {
  5. mThreadExitFlag=false;
  6. mPlayAudioThread=newPlayAudioThread();
  7. mPlayAudioThread.start();
  8. }
  9. }

AudioTrack里有三个重要方法:

voidplay()

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

voidstop()

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

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

代码链接如下:

https://github.com/dongweiq/study/AudioPlayerDemo

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

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

我的github地址:https://github.com/dongweiq/study

欢迎关注,欢迎star o(∩_∩)o 。有什么问题请邮箱联系 dongweiqmail@gmail.com qq714094450

更多相关文章

  1. Android官方架构组件ViewModel:从前世今生到追本溯源
  2. Android自带音频均衡器MusicFx分析
  3. Android(安卓)IPC机制(四)用ContentProvider进行进程间通信
  4. iOS程序员必须知道的Android要点
  5. Android本地数据存储之SQLite
  6. 使用AudioTrack播放PCM音频数据(android)
  7. Android官方DataBinding(三):RecyclerView 使用ViewDataBinding更新
  8. android sqlite 数据类型
  9. Android(安卓)Xml文件生成,Xml数据格式写入

随机推荐

  1. jni开发之一
  2. Android进阶自定义控件之滑动开关
  3. Handler消息机制的原理及应用场景
  4. Android开发学习之一——Android全景概述
  5. [Android] 图像各种处理系列文章合集
  6. 在线免费考一个Android资格认证证书吧
  7. android中传感器的应用
  8. Android 的singleLine废弃解决
  9. Android:Thread & Handler 线程 消息循环
  10. android 下载管理器