Android 音頻播放

Android平臺播放音頻的方式一般有3種。1.利用系統內置的應用程序播放音頻    2.利用AudioTrack播放原始音頻   3.使用MediaPlayer播放。此3種音頻播放方式,以第三種MediaPlayer播放這種方式使用的最多,必須掌握!

一、使用系統內置的程序。

Google想的“周到”,一般都給我們提供了一些內置程序,然而這些內置程序的UI效果,那真是感人啊!一般內置程序,我們就是看看而已。

Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(url,type); //eg:intent.setDataAndType(url,“audio/mp3”);  url音頻文件路徑
startActivity(intent);

二、使用AudioTrack播放音頻

AudioTrack只能用來播放原始音頻(PCM)

//播放音頻(PCM)
	public void play()
	{   
		DataInputStream dis=null;
		try {
			 //從音頻文件中讀取聲音
		     dis=new DataInputStream(new BufferedInputStream(new FileInputStream(recordingFile)));
		    } catch (FileNotFoundException e) {
			  e.printStackTrace();
		    }
		//最小緩存區
		int bufferSizeInBytes=AudioTrack.getMinBufferSize(sampleRateInHz,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT);
		//創建AudioTrack對象   依次傳入 :流類型、採樣率(與採集的要一致)、音頻通道(採集是IN 播放時OUT)、量化位數、最小緩衝區、模式
	    player=new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes, AudioTrack.MODE_STREAM);
		
		byte[] data =new byte [bufferSizeInBytes];
		player.play();//開始播放
		while(true)
		{
			int i=0;
	    	try {
				while(dis.available()>0&&i<data.length)
				{
				  data[i]=dis.readByte();//錄音時write Byte 那麼讀取時就該爲readByte要相互對應
				  i++;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	    	player.write(data,0,data.length);
	    	
	    	if(i!=bufferSizeInBytes) //表示讀取完了
	    	{
	    		player.stop();//停止播放
	    		player.release();//釋放資源
	    		break;
	    	}
		}
		
	}
這裏是播放PCM的關鍵代碼,完整Demo在本文的末尾會給出相應的鏈接!主要是從文件中讀取數據到數組中,然後寫到AudiotTrack之中,然後AudioTrack就會將其播放,利i!=bufferSizeInBytes 判斷其讀取完了(文件的末尾)。


三、使用MediaPlayer進行音頻播放。

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

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

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

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


佈局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/background"
    tools:context="${relativePackage}.${activityClass}" >

    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="天空之城主題曲(宮崎駿)"
        android:textColor="#F8F8F8"
        android:textSize="18sp"
        android:ellipsize="marquee"
        android:layout_centerInParent="true"
        />
    
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginBottom="5dp"
        android:layout_above="@+id/bottom"
        android:gravity="center_vertical"
        >
   
    <TextView
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="00:00"
        android:textColor="#F8F8F8"
        />
    
    <SeekBar 
        android:id="@+id/seek"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:enabled="false"
        />
   
    <TextView
        android:id="@+id/right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="00:00"
        android:textColor="#F8F8F8"
        />
    
    </LinearLayout>
    
    <LinearLayout
        android:id="@+id/bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="5dp"
        android:orientation="horizontal" >
        
        <Button
            android:id="@+id/start" 
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="播放"
            android:textColor="#F8F8F8"
            android:textSize="14sp"
            android:enabled="false"
            />
        
        <Button
            android:id="@+id/stop" 
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="暫停"
            android:textSize="14sp"
            android:textColor="#F8F8F8"
            android:enabled="false"
            />
        
    </LinearLayout>
    
</RelativeLayout>

初始化MediaPlayer

        mAssetManager=getAssets();
		try {
			afd=mAssetManager.openFd("sky.mp3");// 創建天空之城的AssetFileDescriptor文件
		    } catch (IOException e) {
			e.printStackTrace();
			Log.e(TAG,"創建AssetFileDescriptor 異常 ,請查看根福是否存在");
		}
		mMediaPlayer=new MediaPlayer();
		try {
			//設置播放源 ,當然還有其他的重載方法 eg:setDataSource(String path) path可以使網絡路徑也可以是本地路徑,網絡的記得加權限
			mMediaPlayer.setDataSource(afd.getFileDescriptor()); 
		} catch (Exception e) {
			e.printStackTrace();
			Log.e(TAG,"設置播放源異常" );
		}
		mMediaPlayer.prepareAsync(); //MediaPlayer 開始準備  異步的, 還有prepare()這個是同步的

MediaPlayer設置相應的監聽器

     seek.setMax(100);//設置長度100
	    seek.setOnSeekBarChangeListener(this);//設置Seekbar的滑動監聽器
	    mMediaPlayer.setOnPreparedListener(this);//設置準備就緒監聽
	    mMediaPlayer.setOnCompletionListener(this);//設置播放完成
 //結束滑動時   
	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {
		  int a=(int)((sum/100.0)*(seekBar.getProgress()));
		  mMediaPlayer.seekTo(a); //seekTo方法接收的單位是:毫秒
		  handler.sendEmptyMessage(START); //更新seekBar
	}
       @Override
       public void onPrepared(MediaPlayer mp) {
		            //準備就緒完成
               start.setEnabled(true);
               stop.setEnabled(true);
               seek.setEnabled(true);
               sum=mMediaPlayer.getDuration();
               right.setText(FormatTime(sum/1000));  }
 //播放完成
	@Override
	public void onCompletion(MediaPlayer mp) {
		start.setText("播放");
		seek.setProgress(0);
		mMediaPlayer.seekTo(0);
	}
Handler 更新SeekBar的狀態:
 private Handler handler=new Handler()
    {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			    case 1:{
			    	int current=mMediaPlayer.getCurrentPosition();// 得到數值的單位是毫秒
			    	int prass=(int)(current/(sum*1.0)*100);
			    	left.setText(FormatTime(current/1000));
			    	seek.setProgress(prass);
			    	if(!pause)
			    	{
			    	  handler.sendEmptyMessageDelayed(1,1000);//1 秒後繼續更新
			    	}
			    	break;
			    }
			    
			    case 0:{
			    	//停止更新
			    	pause=true;
			    	break;
			    }
			default:
				break;
			}
		}
    	
    };

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播放音頻)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章