Android 音频播放——AudioTrack直接播PCMMediaPlayer播媒体文件可以是audio

历览千载书,时时见遗烈。这篇文章主要讲述Android 音频播放——AudioTrack直接播PCMMediaPlayer播媒体文件可以是audio相关的知识,希望能为你提供帮助。
http://blog.csdn.net/java_android_c/article/details/52678265
 
Android平台播放音频的方式一般有3种。1.利用系统内置的应用程序播放音频     2.利用AudioTrack播放原始音频   3.使用MediaPlayer播放。此3种音频播放方式,以第三种MediaPlayer播放这种方式使用的最多,必须掌握!
一、使用系统内置的程序。
Google想的“周到”,一般都给我们提供了一些内置程序,然而这些内置程序的UI效果,那真是感人啊!一般内置程序,我们就是看看而已。
 
[java]  view plain  copy  

  1. Intent  intent=new  Intent(Intent.ACTION_VIEW);    
  2. intent.setDataAndType(url,type);   //eg:intent.setDataAndType(url,“audio/mp3”);     url音频文件路径   
  3. startActivity(intent);    

二、使用AudioTrack播放音频:只能播放原始的PCM数据,通过callback函数一直写入HW。实际上MediaPlayer也是通过AudioTrack来播放音频的
  在NuPlayer有一个mAudiosink,实际是AudioOutput类型,内部有一个AudioTrack,用来播放解码后的PCM数据
AudioTrack只能用来播放原始音频(PCM)
[java]  view plain  copy  
  1. //播放音频(PCM)   
  2.         public  void  play()   
  3.         {         
  4.                 DataInputStream  dis=null;    
  5.                 try  {   
  6.                           //从音频文件中读取声音   
  7.                           dis=new  DataInputStream(new  BufferedInputStream(new  FileInputStream(recordingFile)));    
  8.                         }  catch  (FileNotFoundException  e)  {   
  9.                             e.printStackTrace();    
  10.                         }   
  11.                 //最小缓存区   
  12.                 int  bufferSizeInBytes=AudioTrack.getMinBufferSize(sampleRateInHz,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT);    
  13.                 //创建AudioTrack对象      依次传入  :流类型、采样率(与采集的要一致)、音频通道(采集是IN  播放时OUT)、量化位数、最小缓冲区、模式   
  14.                 player=new  AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT,  bufferSizeInBytes,  AudioTrack.MODE_STREAM);    
  15.                    
  16.                 byte[]  data  =new  byte  [bufferSizeInBytes];    
  17.                 player.play(); //开始播放   
  18.                 while(true)   
  19.                 {   
  20.                         int  i=0;    
  21.                         try  {   
  22.                                 while(dis.available()> 0& & i< data.length)   
  23.                                 {   
  24.                                     data[i]=dis.readByte(); //录音时write  Byte  那么读取时就该为readByte要相互对应   
  25.                                     i++;    
  26.                                 }   
  27.                         }  catch  (IOException  e)  {   
  28.                                 //  TODO  Auto-generated  catch  block   
  29.                                 e.printStackTrace();    
  30.                         }   
  31.                         player.write(data,0,data.length);    
  32.                            
  33.                         if(i!=bufferSizeInBytes)  //表示读取完了   
  34.                         {   
  35.                                 player.stop(); //停止播放   
  36.                                 player.release(); //释放资源   
  37.                                 break;    
  38.                         }   
  39.                 }   
  40.                    
  41.         }   
这里是播放PCM的关键代码,完整Demo在本文的末尾会给出相应的链接!主要是从文件中读取数据到数组中,然后写到AudiotTrack之中,然后AudioTrack就会将其播放,利i!=bufferSizeInBytes  判断其读取完了(文件的末尾)。
 
三、使用MediaPlayer进行音频播放。——这个是播放多媒体的,包括audio和 video
MediaPlayer是很强大的一个Android系统内置的类,它不仅可以播放音频同时还可以播放视频。最常见的方法有:
start()开始播放         pause()暂停播放     stop()停止播放         prepareAsync() /prepare()   开始准备      
getCurrentPosition() 当前播放的位置           getDuration()文件总的时长       seekTo (int position)定位播放
示例代码 演示利用MediaPlayer和Seekbar进行音频 的播放 、暂停、拖动快进播放等 。效果如图:(具体完整代码在备注里面可下载MediaPlayerDemo)
Android 音频播放——AudioTrack直接播PCMMediaPlayer播媒体文件可以是audio

文章图片

布局文件:
[html]  view plain  copy  
  1. < RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"   
  2.         xmlns:tools="http://schemas.android.com/tools"   
  3.         android:layout_width="match_parent"   
  4.         android:layout_height="match_parent"   
  5.         android:background="@drawable/background"   
  6.         tools:context="${relativePackage}.${activityClass}"  >    
  7.    
  8.         < TextView     
  9.                 android:layout_width="wrap_content"   
  10.                 android:layout_height="wrap_content"   
  11.                 android:text="天空之城主题曲(宫崎骏)"   
  12.                 android:textColor="#F8F8F8"   
  13.                 android:textSize="18sp"   
  14.                 android:ellipsize="marquee"   
  15.                 android:layout_centerInParent="true"   
  16.                 />    
  17.            
  18.         < LinearLayout     
  19.                 android:layout_width="match_parent"   
  20.                 android:layout_height="wrap_content"   
  21.                 android:layout_marginLeft="5dp"   
  22.                 android:layout_marginRight="5dp"   
  23.                 android:layout_marginBottom="5dp"   
  24.                 android:layout_above="@+id/bottom"   
  25.                 android:gravity="center_vertical"   
  26.                 >    
  27.          
  28.         < TextView   
  29.                 android:id="@+id/left"   
  30.                 android:layout_width="wrap_content"   
  31.                 android:layout_height="wrap_content"   
  32.                 android:text="00:00"   
  33.                 android:textColor="#F8F8F8"   
  34.                 />    
  35.            
  36.         < SeekBar     
  37.                 android:id="@+id/seek"   
  38.                 android:layout_width="match_parent"   
  39.                 android:layout_height="wrap_content"   
  40.                 android:layout_weight="1"   
  41.                 android:enabled="false"   
  42.                 />    
  43.          
  44.         < TextView   
  45.                 android:id="@+id/right"   
  46.                 android:layout_width="wrap_content"   
  47.                 android:layout_height="wrap_content"   
  48.                 android:text="00:00"   
  49.                 android:textColor="#F8F8F8"   
  50.                 />    
  51.            
  52.         < /LinearLayout>    
  53.            
  54.         < LinearLayout   
  55.                 android:id="@+id/bottom"   
  56.                 android:layout_width="match_parent"   
  57.                 android:layout_height="wrap_content"   
  58.                 android:layout_alignParentBottom="true"   
  59.                 android:layout_marginBottom="5dp"   
  60.                 android:orientation="horizontal"  >    
  61.                    
  62.                 < Button   
  63.                         android:id="@+id/start"     
  64.                         android:layout_width="0dp"   
  65.                         android:layout_height="wrap_content"   
  66.                         android:layout_weight="1"   
  67.                         android:text="播放"   
  68.                         android:textColor="#F8F8F8"   
  69.                         android:textSize="14sp"   
  70.                         android:enabled="false"   
  71.                         />    
  72.                    
  73.                 < Button   
  74.                         android:id="@+id/stop"     
  75.                         android:layout_width="0dp"   
  76.                         android:layout_height="wrap_content"   
  77.                         android:layout_weight="1"   
  78.                         android:text="暂停"   
  79.                         android:textSize="14sp"   
  80.                         android:textColor="#F8F8F8"   
  81.                         android:enabled="false"   
  82.                         />    
  83.                    
  84.         < /LinearLayout>    
  85.            
  86. < /RelativeLayout>    
 
初始化MediaPlayer
 
[java]  view plain  copy  
  1.             mAssetManager=getAssets();    
  2. try  {   
  3.         afd=mAssetManager.openFd("sky.mp3"); //  创建天空之城的AssetFileDescriptor文件   
  4.         }  catch  (IOException  e)  {   
  5.         e.printStackTrace();    
  6.         Log.e(TAG,"创建AssetFileDescriptor  异常  ,请查看根福是否存在");    
  7. }   
  8. mMediaPlayer=new  MediaPlayer();    
  9. try  {   
  10.         //设置播放源  ,当然还有其他的重载方法  eg:setDataSource(String  path)  path可以使网络路径也可以是本地路径,网络的记得加权限   
  11.         mMediaPlayer.setDataSource(afd.getFileDescriptor());      
  12. }  catch  (Exception  e)  {   
  13.         e.printStackTrace();    
  14.         Log.e(TAG,"设置播放源异常"  );    
  15. }   
  16. mMediaPlayer.prepareAsync();   //MediaPlayer  开始准备    异步的,  还有prepare()这个是同步的   
MediaPlayer设置相应的监听器
 
[java]  view plain  copy  
  1. seek.setMax(100); //设置长度100   
  2. seek.setOnSeekBarChangeListener(this); //设置Seekbar的滑动监听器   
  3. mMediaPlayer.setOnPreparedListener(this); //设置准备就绪监听   
  4. mMediaPlayer.setOnCompletionListener(this); //设置播放完成   
[java]  view plain  copy  
  1. //结束滑动时         
  2. @Override   
  3. public  void  onStopTrackingTouch(SeekBar  seekBar)  {   
  4.             int  a=(int)((sum/100.0)*(seekBar.getProgress()));    
  5.             mMediaPlayer.seekTo(a);   //seekTo方法接收的单位是:毫秒   
  6.             handler.sendEmptyMessage(START);   //更新seekBar   
  7. }   
[java]  view plain  copy  
  1. @Override   
  2. public  void  onPrepared(MediaPlayer  mp)  {   
  3.               //准备就绪完成   
  4.                 start.setEnabled(true);    
  5.                 stop.setEnabled(true);    
  6.                 seek.setEnabled(true);    
  7.                 sum=mMediaPlayer.getDuration();    
  8.                 right.setText(FormatTime(sum/1000));     }   
[java]  view plain  copy  
  1. //播放完成   
  2. @Override   
  3. public  void  onCompletion(MediaPlayer  mp)  {   
  4.         start.setText("播放");    
  5.         seek.setProgress(0);    
  6.         mMediaPlayer.seekTo(0);    
  7. }   
Handler 更新SeekBar的状态:
[java]  view plain  copy  
  1. private  Handler  handler=new  Handler()   
  2.       {   
  3.         @Override   
  4.         public  void  handleMessage(Message  msg)  {   
  5.                 switch  (msg.what)  {   
  6.                         case  1:{   
  7.                                 int  current=mMediaPlayer.getCurrentPosition(); //  得到数值的单位是毫秒   
  8.                                 int  prass=(int)(current/(sum*1.0)*100);    
  9.                                 left.setText(FormatTime(current/1000));    
  10.                                 seek.setProgress(prass);    
  11.                                 if(!pause)   
  12.                                 {   
  13.                                     handler.sendEmptyMessageDelayed(1,1000); //1  秒后继续更新   
  14.                                 }   
  15.                                 break;    
  16.                         }   
  17.                            
  18.                         case  0:{   
  19.                                 //停止更新   
  20.                                 pause=true;    
  21.                                 break;    
  22.                         }   
  23.                 default:   
  24.                         break;    
  25.                 }   
  26.         }   
  27.            
  28.       };    
1.比较容易让人混淆的是pause方法和stop方法的区别:2个方法都可以让音频停止。
调用Pause方法后想再次听见声音直接调用start方法之后即可。调用stop方法停止音频之后,再次调用start方法之后不会播放,要先调用prepareAsync或者prepare方法,之后在public void onPrepared(MediaPlayer mp)回调方法里面调用start方法才会播放。
2.start方法要在准备就绪,即在public void onPrepared(MediaPlayer mp)里面回调。
3.不使用MediaPlayer时记得stop,然后release 释放相关的资源。(本例在Activity的OnDestroy方法中调用的)
下图是MediaPlayer状态及方法流程图:
Android 音频播放——AudioTrack直接播PCMMediaPlayer播媒体文件可以是audio

文章图片

 
 
 
四、备注:
AudioTrack播放PCMDemo  如果对Demo中音频采集不熟悉,可以查阅  Android 音频采集
MediaPlayerDemo(利用MediaPlayer播放音频)
【Android 音频播放——AudioTrack直接播PCMMediaPlayer播媒体文件可以是audio】 


    推荐阅读