1. 程式人生 > >Android 音頻播放——AudioTrack直接播PCM、MediaPlayer播媒體文件可以是audio

Android 音頻播放——AudioTrack直接播PCM、MediaPlayer播媒體文件可以是audio

@override ger ntp audio mon extc exc getc views

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. byte[] data =new byte [bufferSizeInBytes];
  16. player.play();//開始播放
  17. while(true)
  18. {
  19. int i=0;
  20. try {
  21. while(dis.available()>0&&i<data.length)
  22. {
  23. data[i]=dis.readByte();//錄音時write Byte 那麽讀取時就該為readByte要相互對應
  24. i++;
  25. }
  26. } catch (IOException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. player.write(data,0,data.length);
  31. if(i!=bufferSizeInBytes) //表示讀取完了
  32. {
  33. player.stop();//停止播放
  34. player.release();//釋放資源
  35. break;
  36. }
  37. }
  38. }

這裏是播放PCM的關鍵代碼,完整Demo在本文的末尾會給出相應的鏈接!主要是從文件中讀取數據到數組中,然後寫到AudiotTrack之中,然後AudioTrack就會將其播放,利i!=bufferSizeInBytes 判斷其讀取完了(文件的末尾)。

三、使用MediaPlayer進行音頻播放。——這個是播放多媒體的,包括audio和 video

MediaPlayer是很強大的一個Android系統內置的類,它不僅可以播放音頻同時還可以播放視頻。最常見的方法有:

start()開始播放 pause()暫停播放 stop()停止播放 prepareAsync() /prepare() 開始準備

getCurrentPosition() 當前播放的位置 getDuration()文件總的時長 seekTo (int position)定位播放

示例代碼 演示利用MediaPlayer和Seekbar進行音頻 的播放 、暫停、拖動快進播放等 。效果如圖:(具體完整代碼在備註裏面可下載MediaPlayerDemo)

技術分享

布局文件:

[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. <TextView
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:text="天空之城主題曲(宮崎駿)"
  11. android:textColor="#F8F8F8"
  12. android:textSize="18sp"
  13. android:ellipsize="marquee"
  14. android:layout_centerInParent="true"
  15. />
  16. <LinearLayout
  17. android:layout_width="match_parent"
  18. android:layout_height="wrap_content"
  19. android:layout_marginLeft="5dp"
  20. android:layout_marginRight="5dp"
  21. android:layout_marginBottom="5dp"
  22. android:layout_above="@+id/bottom"
  23. android:gravity="center_vertical"
  24. >
  25. <TextView
  26. android:id="@+id/left"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:text="00:00"
  30. android:textColor="#F8F8F8"
  31. />
  32. <SeekBar
  33. android:id="@+id/seek"
  34. android:layout_width="match_parent"
  35. android:layout_height="wrap_content"
  36. android:layout_weight="1"
  37. android:enabled="false"
  38. />
  39. <TextView
  40. android:id="@+id/right"
  41. android:layout_width="wrap_content"
  42. android:layout_height="wrap_content"
  43. android:text="00:00"
  44. android:textColor="#F8F8F8"
  45. />
  46. </LinearLayout>
  47. <LinearLayout
  48. android:id="@+id/bottom"
  49. android:layout_width="match_parent"
  50. android:layout_height="wrap_content"
  51. android:layout_alignParentBottom="true"
  52. android:layout_marginBottom="5dp"
  53. android:orientation="horizontal" >
  54. <Button
  55. android:id="@+id/start"
  56. android:layout_width="0dp"
  57. android:layout_height="wrap_content"
  58. android:layout_weight="1"
  59. android:text="播放"
  60. android:textColor="#F8F8F8"
  61. android:textSize="14sp"
  62. android:enabled="false"
  63. />
  64. <Button
  65. android:id="@+id/stop"
  66. android:layout_width="0dp"
  67. android:layout_height="wrap_content"
  68. android:layout_weight="1"
  69. android:text="暫停"
  70. android:textSize="14sp"
  71. android:textColor="#F8F8F8"
  72. android:enabled="false"
  73. />
  74. </LinearLayout>
  75. </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. case 0:{
  18. //停止更新
  19. pause=true;
  20. break;
  21. }
  22. default:
  23. break;
  24. }
  25. }
  26. };

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狀態及方法流程圖:

技術分享

四、備註:

AudioTrack播放PCMDemo 如果對Demo中音頻采集不熟悉,可以查閱 Android 音頻采集

MediaPlayerDemo(利用MediaPlayer播放音頻)

Android 音頻播放——AudioTrack直接播PCM、MediaPlayer播媒體文件可以是audio